diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch) | |
tree | 5ac38a06f3dde268dc7927dc155896926aaf7012 /khtml/html | |
download | tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'khtml/html')
34 files changed, 18585 insertions, 0 deletions
diff --git a/khtml/html/Makefile.am b/khtml/html/Makefile.am new file mode 100644 index 000000000..ed7722517 --- /dev/null +++ b/khtml/html/Makefile.am @@ -0,0 +1,54 @@ +# This file is part of the KDE libraries +# Copyright (C) 1997 Martin Jones (mjones@kde.org) +# (C) 1997 Torben Weis (weis@kde.org) + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public License +# along with this library; see the file COPYING.LIB. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +KDE_CXXFLAGS = $(WOVERLOADED_VIRTUAL) + +noinst_LTLIBRARIES = libkhtmlhtml.la +libkhtmlhtml_la_SOURCES = \ + htmlparser.cpp htmltokenizer.cpp \ + dtd.cpp html_headimpl.cpp html_blockimpl.cpp \ + html_elementimpl.cpp html_inlineimpl.cpp html_documentimpl.cpp \ + html_baseimpl.cpp html_imageimpl.cpp html_listimpl.cpp \ + html_miscimpl.cpp html_formimpl.cpp html_objectimpl.cpp \ + html_tableimpl.cpp + +libkhtmlhtml_la_METASOURCES = AUTO + + +noinst_HEADERS = \ + dtd.h html_headimpl.h html_tableimpl.h \ + html_baseimpl.h html_imageimpl.h htmlparser.h \ + html_blockimpl.h html_inlineimpl.h \ + html_documentimpl.h html_listimpl.h htmltokenizer.h \ + html_elementimpl.h html_miscimpl.h \ + html_formimpl.h html_objectimpl.h + +INCLUDES = -I$(top_srcdir)/kimgio -I$(top_srcdir)/dcop \ + -I$(top_srcdir)/kio/kssl \ + -I$(top_srcdir)/kjs -I$(top_srcdir)/khtml -I$(top_srcdir) \ + -I$(top_srcdir)/kwallet/client -I$(top_srcdir)/kutils \ + $(all_includes) + +# Use "make doctypes" to regenerate doctypes.cpp from doctypes.gperf +doctypes: $(srcdir)/doctypes.gperf $(srcdir)/Makefile.am + gperf -CEot -L "ANSI-C" -k "*" -N findDoctypeEntry -F ,PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards $(srcdir)/doctypes.gperf > $(srcdir)/doctypes.cpp + +#html_documentimpl.lo: doctypes.cpp +.PHONY: doctypes + diff --git a/khtml/html/doctypes.cpp b/khtml/html/doctypes.cpp new file mode 100644 index 000000000..e79a56f34 --- /dev/null +++ b/khtml/html/doctypes.cpp @@ -0,0 +1,1174 @@ +/* ANSI-C code produced by gperf version 3.0.1 */ +/* Command-line: gperf -CEot -L ANSI-C -k '*' -N findDoctypeEntry -F ,PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards /opt/src/kde/kdelibs/khtml/html/doctypes.gperf */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>." +#endif + +#line 1 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" +struct PubIDInfo { + enum eMode { + eQuirks, /* always quirks mode, unless there's an internal subset */ + eQuirks3, /* ditto, but but pre-HTML4 (no tbody) */ + eAlmostStandards, + eFullStandards + }; + + const char* name; + eMode mode_if_no_sysid; + eMode mode_if_sysid; +}; +/* maximum key range = 727, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register unsigned int len) +{ + static const unsigned short asso_values[] = + { + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 0, 731, 731, 731, 731, 731, 731, 0, + 731, 731, 731, 0, 731, 0, 15, 0, 10, 25, + 5, 0, 5, 15, 5, 5, 731, 5, 0, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 10, 5, 0, + 40, 0, 20, 10, 0, 0, 0, 731, 0, 0, + 10, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 0, 5, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731, 731, 731, 731, 731, + 731, 731, 731, 731, 731, 731 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[79]]; + /*FALLTHROUGH*/ + case 79: + hval += asso_values[(unsigned char)str[78]]; + /*FALLTHROUGH*/ + case 78: + hval += asso_values[(unsigned char)str[77]]; + /*FALLTHROUGH*/ + case 77: + hval += asso_values[(unsigned char)str[76]]; + /*FALLTHROUGH*/ + case 76: + hval += asso_values[(unsigned char)str[75]]; + /*FALLTHROUGH*/ + case 75: + hval += asso_values[(unsigned char)str[74]]; + /*FALLTHROUGH*/ + case 74: + hval += asso_values[(unsigned char)str[73]]; + /*FALLTHROUGH*/ + case 73: + hval += asso_values[(unsigned char)str[72]]; + /*FALLTHROUGH*/ + case 72: + hval += asso_values[(unsigned char)str[71]]; + /*FALLTHROUGH*/ + case 71: + hval += asso_values[(unsigned char)str[70]]; + /*FALLTHROUGH*/ + case 70: + hval += asso_values[(unsigned char)str[69]]; + /*FALLTHROUGH*/ + case 69: + hval += asso_values[(unsigned char)str[68]]; + /*FALLTHROUGH*/ + case 68: + hval += asso_values[(unsigned char)str[67]]; + /*FALLTHROUGH*/ + case 67: + hval += asso_values[(unsigned char)str[66]]; + /*FALLTHROUGH*/ + case 66: + hval += asso_values[(unsigned char)str[65]]; + /*FALLTHROUGH*/ + case 65: + hval += asso_values[(unsigned char)str[64]]; + /*FALLTHROUGH*/ + case 64: + hval += asso_values[(unsigned char)str[63]]; + /*FALLTHROUGH*/ + case 63: + hval += asso_values[(unsigned char)str[62]]; + /*FALLTHROUGH*/ + case 62: + hval += asso_values[(unsigned char)str[61]]; + /*FALLTHROUGH*/ + case 61: + hval += asso_values[(unsigned char)str[60]]; + /*FALLTHROUGH*/ + case 60: + hval += asso_values[(unsigned char)str[59]]; + /*FALLTHROUGH*/ + case 59: + hval += asso_values[(unsigned char)str[58]]; + /*FALLTHROUGH*/ + case 58: + hval += asso_values[(unsigned char)str[57]]; + /*FALLTHROUGH*/ + case 57: + hval += asso_values[(unsigned char)str[56]]; + /*FALLTHROUGH*/ + case 56: + hval += asso_values[(unsigned char)str[55]]; + /*FALLTHROUGH*/ + case 55: + hval += asso_values[(unsigned char)str[54]]; + /*FALLTHROUGH*/ + case 54: + hval += asso_values[(unsigned char)str[53]]; + /*FALLTHROUGH*/ + case 53: + hval += asso_values[(unsigned char)str[52]]; + /*FALLTHROUGH*/ + case 52: + hval += asso_values[(unsigned char)str[51]]; + /*FALLTHROUGH*/ + case 51: + hval += asso_values[(unsigned char)str[50]]; + /*FALLTHROUGH*/ + case 50: + hval += asso_values[(unsigned char)str[49]]; + /*FALLTHROUGH*/ + case 49: + hval += asso_values[(unsigned char)str[48]]; + /*FALLTHROUGH*/ + case 48: + hval += asso_values[(unsigned char)str[47]]; + /*FALLTHROUGH*/ + case 47: + hval += asso_values[(unsigned char)str[46]]; + /*FALLTHROUGH*/ + case 46: + hval += asso_values[(unsigned char)str[45]]; + /*FALLTHROUGH*/ + case 45: + hval += asso_values[(unsigned char)str[44]]; + /*FALLTHROUGH*/ + case 44: + hval += asso_values[(unsigned char)str[43]]; + /*FALLTHROUGH*/ + case 43: + hval += asso_values[(unsigned char)str[42]]; + /*FALLTHROUGH*/ + case 42: + hval += asso_values[(unsigned char)str[41]]; + /*FALLTHROUGH*/ + case 41: + hval += asso_values[(unsigned char)str[40]]; + /*FALLTHROUGH*/ + case 40: + hval += asso_values[(unsigned char)str[39]]; + /*FALLTHROUGH*/ + case 39: + hval += asso_values[(unsigned char)str[38]]; + /*FALLTHROUGH*/ + case 38: + hval += asso_values[(unsigned char)str[37]]; + /*FALLTHROUGH*/ + case 37: + hval += asso_values[(unsigned char)str[36]]; + /*FALLTHROUGH*/ + case 36: + hval += asso_values[(unsigned char)str[35]]; + /*FALLTHROUGH*/ + case 35: + hval += asso_values[(unsigned char)str[34]]; + /*FALLTHROUGH*/ + case 34: + hval += asso_values[(unsigned char)str[33]]; + /*FALLTHROUGH*/ + case 33: + hval += asso_values[(unsigned char)str[32]]; + /*FALLTHROUGH*/ + case 32: + hval += asso_values[(unsigned char)str[31]]; + /*FALLTHROUGH*/ + case 31: + hval += asso_values[(unsigned char)str[30]]; + /*FALLTHROUGH*/ + case 30: + hval += asso_values[(unsigned char)str[29]]; + /*FALLTHROUGH*/ + case 29: + hval += asso_values[(unsigned char)str[28]]; + /*FALLTHROUGH*/ + case 28: + hval += asso_values[(unsigned char)str[27]]; + /*FALLTHROUGH*/ + case 27: + hval += asso_values[(unsigned char)str[26]]; + /*FALLTHROUGH*/ + case 26: + hval += asso_values[(unsigned char)str[25]]; + /*FALLTHROUGH*/ + case 25: + hval += asso_values[(unsigned char)str[24]]; + /*FALLTHROUGH*/ + case 24: + hval += asso_values[(unsigned char)str[23]]; + /*FALLTHROUGH*/ + case 23: + hval += asso_values[(unsigned char)str[22]]; + /*FALLTHROUGH*/ + case 22: + hval += asso_values[(unsigned char)str[21]]; + /*FALLTHROUGH*/ + case 21: + hval += asso_values[(unsigned char)str[20]]; + /*FALLTHROUGH*/ + case 20: + hval += asso_values[(unsigned char)str[19]]; + /*FALLTHROUGH*/ + case 19: + hval += asso_values[(unsigned char)str[18]]; + /*FALLTHROUGH*/ + case 18: + hval += asso_values[(unsigned char)str[17]]; + /*FALLTHROUGH*/ + case 17: + hval += asso_values[(unsigned char)str[16]]; + /*FALLTHROUGH*/ + case 16: + hval += asso_values[(unsigned char)str[15]]; + /*FALLTHROUGH*/ + case 15: + hval += asso_values[(unsigned char)str[14]]; + /*FALLTHROUGH*/ + case 14: + hval += asso_values[(unsigned char)str[13]]; + /*FALLTHROUGH*/ + case 13: + hval += asso_values[(unsigned char)str[12]]; + /*FALLTHROUGH*/ + case 12: + hval += asso_values[(unsigned char)str[11]]; + /*FALLTHROUGH*/ + case 11: + hval += asso_values[(unsigned char)str[10]]; + /*FALLTHROUGH*/ + case 10: + hval += asso_values[(unsigned char)str[9]]; + /*FALLTHROUGH*/ + case 9: + hval += asso_values[(unsigned char)str[8]]; + /*FALLTHROUGH*/ + case 8: + hval += asso_values[(unsigned char)str[7]]; + /*FALLTHROUGH*/ + case 7: + hval += asso_values[(unsigned char)str[6]]; + /*FALLTHROUGH*/ + case 6: + hval += asso_values[(unsigned char)str[5]]; + /*FALLTHROUGH*/ + case 5: + hval += asso_values[(unsigned char)str[4]]; + /*FALLTHROUGH*/ + case 4: + hval += asso_values[(unsigned char)str[3]]; + /*FALLTHROUGH*/ + case 3: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + hval += asso_values[(unsigned char)str[1]]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +#ifdef __GNUC__ +__inline +#endif +const struct PubIDInfo * +findDoctypeEntry (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 78, + MIN_WORD_LENGTH = 4, + MAX_WORD_LENGTH = 80, + MIN_HASH_VALUE = 4, + MAX_HASH_VALUE = 730 + }; + + static const struct PubIDInfo wordlist[] = + { + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 91 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"html", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 81 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd w3 html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 48 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 28 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 3//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 73 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 3.2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 45 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 35 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 3//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 33 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 43 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 3//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 29 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 41 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 27 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 3.2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 37 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 69 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w30//dtd w3 html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 24 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 3.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 50 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 25 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 3.0//en//", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 31 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 22 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 49 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 47 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 36 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 3//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 39 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 21 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 2.0 strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 46 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 44 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 3//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 18 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 2.0 level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 34 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 2//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 72 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 3.2 final//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 23 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 2.1e//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 75 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 4.0 frameset//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks}, +#line 30 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 0//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 20 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 2.0 strict level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 42 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 2//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 85 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3o//dtd w3 html 3.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 89 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//webtechs//dtd mozilla html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 86 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3o//dtd w3 html 3.0//en//", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 84 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd xhtml 1.1//en", PubIDInfo::eAlmostStandards, PubIDInfo::eAlmostStandards}, +#line 38 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 0//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 70 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 3 1995-03-24//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 87 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3o//dtd w3 html strict 3.0//en//", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 17 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 2.0 level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 32 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html level 1//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 26 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 3.2 final//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 19 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html 2.0 strict level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 40 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//ietf//dtd html strict level 1//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 77 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 4.01 frameset//en", PubIDInfo::eQuirks, PubIDInfo::eAlmostStandards}, +#line 71 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 3.2 draft//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 74 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 3.2s draft//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 82 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd xhtml 1.0 frameset//en", PubIDInfo::eAlmostStandards, PubIDInfo::eAlmostStandards}, +#line 80 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html experimental 970421//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 51 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//metrius//dtd metrius presentational//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 88 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//webtechs//dtd mozilla html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 90 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-/w3c/dtd html 4.0 transitional/en", PubIDInfo::eQuirks, PubIDInfo::eQuirks}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 76 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 4.0 transitional//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 79 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html experimental 19960712//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 58 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//netscape comm. corp.//dtd html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 78 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd html 4.01 transitional//en", PubIDInfo::eQuirks, PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 83 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//w3c//dtd xhtml 1.0 transitional//en", PubIDInfo::eAlmostStandards, PubIDInfo::eAlmostStandards}, +#line 59 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//netscape comm. corp.//dtd strict html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 65 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//spyglass//dtd html 2.0 extended//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 16 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//as//dtd html 3.0 aswedit + extensions//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 66 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//sq//dtd html 2.0 hotmetal + extensions//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 67 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//sun microsystems corp.//dtd hotjava html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 68 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//sun microsystems corp.//dtd hotjava strict html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 60 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//o'reilly and associates//dtd html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 56 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//microsoft//dtd internet explorer 3.0 html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 53 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//microsoft//dtd internet explorer 2.0 html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 55 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//microsoft//dtd internet explorer 3.0 html strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 14 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"+//silmaril//dtd html pro v0r11 19970101//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, +#line 52 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//microsoft//dtd internet explorer 2.0 html strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 57 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//microsoft//dtd internet explorer 3.0 tables//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 54 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//microsoft//dtd internet explorer 2.0 tables//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 15 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//advasoft ltd//dtd html 3.0 aswedit + extensions//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 61 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//o'reilly and associates//dtd html extended 1.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 62 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//o'reilly and associates//dtd html extended relaxed 1.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 64 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, + {"",PubIDInfo::eAlmostStandards,PubIDInfo::eAlmostStandards}, +#line 63 "/opt/src/kde/kdelibs/khtml/html/doctypes.gperf" + {"-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/khtml/html/doctypes.gperf b/khtml/html/doctypes.gperf new file mode 100644 index 000000000..97b10e258 --- /dev/null +++ b/khtml/html/doctypes.gperf @@ -0,0 +1,91 @@ +struct PubIDInfo { + enum eMode { + eQuirks, /* always quirks mode, unless there's an internal subset */ + eQuirks3, /* ditto, but but pre-HTML4 (no tbody) */ + eAlmostStandards, + eFullStandards + }; + + const char* name; + eMode mode_if_no_sysid; + eMode mode_if_sysid; +} +%% +"+//silmaril//dtd html pro v0r11 19970101//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//advasoft ltd//dtd html 3.0 aswedit + extensions//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//as//dtd html 3.0 aswedit + extensions//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 2.0 level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 2.0 level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 2.0 strict level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 2.0 strict level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 2.0 strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 2.1e//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 3.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 3.0//en//", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 3.2 final//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 3.2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html 3//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 0//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 1//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 2//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 3//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html level 3//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 0//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 1//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 1//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 2//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 3//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict level 3//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html strict//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html//en//2.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//ietf//dtd html//en//3.0", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//metrius//dtd metrius presentational//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks +"-//microsoft//dtd internet explorer 2.0 html strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//microsoft//dtd internet explorer 2.0 html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//microsoft//dtd internet explorer 2.0 tables//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//microsoft//dtd internet explorer 3.0 html strict//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//microsoft//dtd internet explorer 3.0 html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//microsoft//dtd internet explorer 3.0 tables//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//netscape comm. corp.//dtd html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//netscape comm. corp.//dtd strict html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//o'reilly and associates//dtd html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//o'reilly and associates//dtd html extended 1.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//o'reilly and associates//dtd html extended relaxed 1.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks +"-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks +"-//spyglass//dtd html 2.0 extended//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//sq//dtd html 2.0 hotmetal + extensions//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//sun microsystems corp.//dtd hotjava html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//sun microsystems corp.//dtd hotjava strict html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w30//dtd w3 html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd html 3 1995-03-24//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd html 3.2 draft//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd html 3.2 final//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd html 3.2//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd html 3.2s draft//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd html 4.0 frameset//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks +"-//w3c//dtd html 4.0 transitional//en", PubIDInfo::eQuirks, PubIDInfo::eQuirks +"-//w3c//dtd html 4.01 frameset//en", PubIDInfo::eQuirks, PubIDInfo::eAlmostStandards +"-//w3c//dtd html 4.01 transitional//en", PubIDInfo::eQuirks, PubIDInfo::eAlmostStandards +"-//w3c//dtd html experimental 19960712//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd html experimental 970421//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd w3 html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3c//dtd xhtml 1.0 frameset//en", PubIDInfo::eAlmostStandards, PubIDInfo::eAlmostStandards +"-//w3c//dtd xhtml 1.0 transitional//en", PubIDInfo::eAlmostStandards, PubIDInfo::eAlmostStandards +"-//w3c//dtd xhtml 1.1//en", PubIDInfo::eAlmostStandards, PubIDInfo::eAlmostStandards +"-//w3o//dtd w3 html 3.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3o//dtd w3 html 3.0//en//", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//w3o//dtd w3 html strict 3.0//en//", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//webtechs//dtd mozilla html 2.0//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-//webtechs//dtd mozilla html//en", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3 +"-/w3c/dtd html 4.0 transitional/en", PubIDInfo::eQuirks, PubIDInfo::eQuirks +"html", PubIDInfo::eQuirks3, PubIDInfo::eQuirks3
\ No newline at end of file diff --git a/khtml/html/dtd.cpp b/khtml/html/dtd.cpp new file mode 100644 index 000000000..accc8b855 --- /dev/null +++ b/khtml/html/dtd.cpp @@ -0,0 +1,918 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "html/dtd.h" +#include "misc/htmlhashes.h" + +using namespace DOM; + +#include <kdebug.h> +#include <kglobal.h> + +// priority of tags. Closing tags of higher priority close tags of lower +// priority. +// Update this list, whenever you change htmltags.* +// +// 0 elements with forbidden close tag and text. They don't get pushed +// to the stack. +// 1 inline elements +// 2 form elements +// 3 regular block level elements +// 4 lists (OL UL DIR MENU) +// 5 TD TH SELECT +// 6 TR +// 7 tbody thead tfoot caption object +// 8 table +// 9 body frameset +// 10 html + +const unsigned short KDE_NO_EXPORT DOM::tagPriority[] = { + 0, // 0 + 1, // ID_A == 1 + 1, // ID_ABBR + 1, // ID_ACRONYM + 3, // ID_ADDRESS + 1, // ID_APPLET + 0, // ID_AREA + 1, // ID_B + 0, // ID_BASE + 0, // ID_BASEFONT + 1, // ID_BDO + 1, // ID_BIG + 5, // ID_BLOCKQUOTE + 10, // ID_BODY + 0, // ID_BR + 1, // ID_BUTTON + 5, // ID_CAPTION + 5, // ID_CENTER + 1, // ID_CITE + 1, // ID_CODE + 0, // ID_COL + 1, // ID_COLGROUP + 3, // ID_DD + 1, // ID_DEL + 1, // ID_DFN + 5, // ID_DIR + 5, // ID_DIV + 5, // ID_DL + 3, // ID_DT + 1, // ID_EM + 0, // ID_EMBED + 3, // ID_FIELDSET + 1, // ID_FONT + 3, // ID_FORM + 0, // ID_FRAME + 10,// ID_FRAMESET + 5, // ID_H1 + 5, // ID_H2 + 5, // ID_H3 + 5, // ID_H4 + 5, // ID_H5 + 5, // ID_H6 + 10,// ID_HEAD + 0, // ID_HR + 11,// ID_HTML + 1, // ID_I + 1, // ID_IFRAME + 1, // ID_ILAYER + 0, // ID_IMG + 0, // ID_INPUT + 1, // ID_INS + 0, // ID_ISINDEX + 1, // ID_KBD + 0, // ID_KEYGEN + 1, // ID_LABEL + 1, // ID_LAYER + 1, // ID_LEGEND + 3, // ID_LI + 0, // ID_LINK + 1, // ID_MAP + 3, // ID_MARQUEE + 5, // ID_MENU + 0, // ID_META + 5, // ID_NOBR + 10,// ID_NOEMBED + 10,// ID_NOFRAMES + 3, // ID_NOSCRIPT + 1, // ID_NOLAYER + 5, // ID_OBJECT + 5, // ID_OL + 1, // ID_OPTGROUP + 2, // ID_OPTION + 3, // ID_P + 0, // ID_PARAM + 5, // ID_PLAINTEXT + 5, // ID_PRE + 1, // ID_Q + 1, // ID_S + 1, // ID_SAMP + 1, // ID_SCRIPT + 6, // ID_SELECT + 1, // ID_SMALL + 1, // ID_SPAN + 1, // ID_STRIKE + 1, // ID_STRONG + 1, // ID_STYLE + 1, // ID_SUB + 1, // ID_SUP + 9,// ID_TABLE + 8, // ID_TBODY + 6, // ID_TD + 1, // ID_TEXTAREA + 8, // ID_TFOOT + 6, // ID_TH + 8, // ID_THEAD + 1, // ID_TITLE + 7, // ID_TR + 1, // ID_TT + 1, // ID_U + 5, // ID_UL + 1, // ID_VAR + 1, // ID_WBR + 5, // ID_XMP + 0, // ID_TEXT +}; + +const tagStatus DOM::endTag[] = { + REQUIRED, // 0 + REQUIRED, // ID_A == 1 + REQUIRED, // ID_ABBR + REQUIRED, // ID_ACRONYM + REQUIRED, // ID_ADDRESS + REQUIRED, // ID_APPLET + FORBIDDEN, // ID_AREA + REQUIRED, // ID_B + FORBIDDEN, // ID_BASE + FORBIDDEN, // ID_BASEFONT + REQUIRED, // ID_BDO + REQUIRED, // ID_BIG + REQUIRED, // ID_BLOCKQUOTE + REQUIRED, // ID_BODY + FORBIDDEN, // ID_BR + REQUIRED, // ID_BUTTON + REQUIRED, // ID_CAPTION + REQUIRED, // ID_CENTER + REQUIRED, // ID_CITE + REQUIRED, // ID_CODE + FORBIDDEN, // ID_COL + OPTIONAL, // ID_COLGROUP + OPTIONAL, // ID_DD + REQUIRED, // ID_DEL + REQUIRED, // ID_DFN + REQUIRED, // ID_DIR + REQUIRED, // ID_DIV + REQUIRED, // ID_DL + OPTIONAL, // ID_DT + REQUIRED, // ID_EM + REQUIRED, // ID_EMBED + REQUIRED, // ID_FIELDSET + REQUIRED, // ID_FONT + REQUIRED, // ID_FORM + FORBIDDEN, // ID_FRAME + REQUIRED, // ID_FRAMESET + REQUIRED, // ID_H1 + REQUIRED, // ID_H2 + REQUIRED, // ID_H3 + REQUIRED, // ID_H4 + REQUIRED, // ID_H5 + REQUIRED, // ID_H6 + OPTIONAL, // ID_HEAD + FORBIDDEN, // ID_HR + REQUIRED, // ID_HTML + REQUIRED, // ID_I + REQUIRED, // ID_IFRAME + REQUIRED, // ID_ILAYER + FORBIDDEN, // ID_IMG + FORBIDDEN, // ID_INPUT + REQUIRED, // ID_INS + FORBIDDEN, // ID_ISINDEX + REQUIRED, // ID_KBD + REQUIRED, // ID_KEYGEN + REQUIRED, // ID_LABEL + REQUIRED, // ID_LAYER + REQUIRED, // ID_LEGEND + OPTIONAL, // ID_LI + FORBIDDEN, // ID_LINK + REQUIRED, // ID_MAP + REQUIRED, // ID_MARQUEE + REQUIRED, // ID_MENU + FORBIDDEN, // ID_META + REQUIRED, // ID_NOBR + REQUIRED, // ID_NOEMBED + REQUIRED, // ID_NOFRAMES + REQUIRED, // ID_NOSCRIPT + REQUIRED, // ID_NOLAYER + REQUIRED, // ID_OBJECT + REQUIRED, // ID_OL + REQUIRED, // ID_OPTGROUP + OPTIONAL, // ID_OPTION + OPTIONAL, // ID_P + FORBIDDEN, // ID_PARAM + REQUIRED, // ID_PLAINTEXT + REQUIRED, // ID_PRE + REQUIRED, // ID_Q + REQUIRED, // ID_S + REQUIRED, // ID_SAMP + REQUIRED, // ID_SCRIPT + REQUIRED, // ID_SELECT + REQUIRED, // ID_SMALL + REQUIRED, // ID_SPAN + REQUIRED, // ID_STRIKE + REQUIRED, // ID_STRONG + REQUIRED, // ID_STYLE + REQUIRED, // ID_SUB + REQUIRED, // ID_SUP + REQUIRED, // ID_TABLE + OPTIONAL, // ID_TBODY + OPTIONAL, // ID_TD + REQUIRED, // ID_TEXTAREA + OPTIONAL, // ID_TFOOT + OPTIONAL, // ID_TH + OPTIONAL, // ID_THEAD + REQUIRED, // ID_TITLE + OPTIONAL, // ID_TR + REQUIRED, // ID_TT + REQUIRED, // ID_U + REQUIRED, // ID_UL + REQUIRED, // ID_VAR + OPTIONAL, // ID_WBR + REQUIRED, // ID_XMP + REQUIRED // ID_TEXT +}; + + +static const ushort tag_list_0[] = { + ID_TEXT, + ID_TT, + ID_I, + ID_B, + ID_U, + ID_S, + ID_STRIKE, + ID_BIG, + ID_SMALL, + ID_EM, + ID_STRONG, + ID_DFN, + ID_CODE, + ID_SAMP, + ID_KBD, + ID_VAR, + ID_CITE, + ID_ABBR, + ID_ACRONYM, + ID_A, + ID_IMG, + ID_APPLET, + ID_OBJECT, + ID_EMBED, + ID_FONT, + ID_BASEFONT, + ID_BR, + ID_SCRIPT, + ID_MAP, + ID_Q, + ID_SUB, + ID_SUP, + ID_SPAN, + ID_BDO, + ID_IFRAME, + ID_INPUT, + ID_SELECT, + ID_TEXTAREA, + ID_LABEL, + ID_BUTTON, + ID_INS, + ID_DEL, + ID_COMMENT, + ID_NOBR, + ID_WBR, + 0 +}; + +static const ushort tag_list_1[] = { + ID_TEXT, + ID_P, + ID_H1, + ID_H2, + ID_H3, + ID_H4, + ID_H5, + ID_H6, + ID_UL, + ID_OL, + ID_DIR, + ID_MENU, + ID_PRE, + ID_PLAINTEXT, + ID_DL, + ID_DIV, + ID_ILAYER, + ID_LAYER, + ID_CENTER, + ID_NOSCRIPT, + ID_NOFRAMES, + ID_BLOCKQUOTE, + ID_FORM, + ID_ISINDEX, + ID_HR, + ID_TABLE, + ID_FIELDSET, + ID_ADDRESS, + ID_TT, + ID_I, + ID_B, + ID_U, + ID_S, + ID_STRIKE, + ID_BIG, + ID_SMALL, + ID_EM, + ID_STRONG, + ID_DFN, + ID_CODE, + ID_SAMP, + ID_KBD, + ID_VAR, + ID_CITE, + ID_ABBR, + ID_ACRONYM, + ID_A, + ID_IMG, + ID_APPLET, + ID_OBJECT, + ID_EMBED, + ID_FONT, + ID_BASEFONT, + ID_BR, + ID_SCRIPT, + ID_MAP, + ID_Q, + ID_SUB, + ID_SUP, + ID_SPAN, + ID_BDO, + ID_IFRAME, + ID_INPUT, + ID_KEYGEN, + ID_SELECT, + ID_TEXTAREA, + ID_LABEL, + ID_BUTTON, + ID_COMMENT, + ID_LI, + ID_DD, + ID_XMP, + ID_INS, + ID_DEL, + ID_NOBR, + ID_WBR, + ID_MARQUEE, + 0 +}; + +static const ushort tag_list_2[] = { + ID_COMMENT, + 0 +}; + +static const ushort tag_list_3[] = { + ID_TEXT, + ID_P, + ID_H1, + ID_H2, + ID_H3, + ID_H4, + ID_H5, + ID_H6, + ID_UL, + ID_OL, + ID_DIR, + ID_MENU, + ID_PRE, + ID_PLAINTEXT, + ID_DL, + ID_DIV, + ID_ILAYER, + ID_LAYER, + ID_CENTER, + ID_NOSCRIPT, + ID_NOFRAMES, + ID_BLOCKQUOTE, + ID_FORM, + ID_ISINDEX, + ID_HR, + ID_TABLE, + ID_FIELDSET, + ID_ADDRESS, + ID_COMMENT, + ID_LI, + ID_DD, + ID_XMP, + ID_MARQUEE, + 0 +}; + +static const ushort tag_list_4[] = { + ID_TEXT, + ID_PARAM, + ID_P, + ID_H1, + ID_H2, + ID_H3, + ID_H4, + ID_H5, + ID_H6, + ID_UL, + ID_OL, + ID_DIR, + ID_MENU, + ID_PRE, + ID_PLAINTEXT, + ID_DL, + ID_DIV, + ID_ILAYER, + ID_LAYER, + ID_CENTER, + ID_NOSCRIPT, + ID_NOFRAMES, + ID_BLOCKQUOTE, + ID_FORM, + ID_ISINDEX, + ID_HR, + ID_TABLE, + ID_FIELDSET, + ID_ADDRESS, + ID_TEXT, + ID_TT, + ID_I, + ID_B, + ID_U, + ID_S, + ID_STRIKE, + ID_BIG, + ID_SMALL, + ID_EM, + ID_STRONG, + ID_DFN, + ID_CODE, + ID_SAMP, + ID_KBD, + ID_VAR, + ID_CITE, + ID_ABBR, + ID_ACRONYM, + ID_A, + ID_IMG, + ID_APPLET, + ID_OBJECT, + ID_EMBED, + ID_FONT, + ID_BASEFONT, + ID_BR, + ID_SCRIPT, + ID_MAP, + ID_Q, + ID_SUB, + ID_SUP, + ID_SPAN, + ID_BDO, + ID_IFRAME, + ID_INPUT, + ID_SELECT, + ID_TEXTAREA, + ID_LABEL, + ID_BUTTON, + ID_COMMENT, + ID_LI, + ID_DD, + ID_XMP, + ID_MARQUEE, + 0 +}; + +static const ushort tag_list_6[] = { + ID_DT, + ID_DD, + ID_COMMENT, + 0 +}; + +static const ushort tag_list_7[] = { + ID_TEXT, + ID_OPTGROUP, + ID_OPTION, + ID_COMMENT, + ID_SCRIPT, + 0 +}; + +static const ushort tag_list_10[] = { + ID_FRAMESET, + ID_FRAME, + ID_NOFRAMES, + ID_COMMENT, + 0 +}; + +static const ushort tag_list_11[] = { + ID_SCRIPT, + ID_STYLE, + ID_META, + ID_LINK, + ID_TITLE, + ID_ISINDEX, + ID_BASE, + ID_COMMENT, + 0 +}; + +static bool check_array(ushort child, const ushort *tagList) +{ + int i = 0; + while(tagList[i] != 0) + { + if(tagList[i] == child) return true; + i++; + } + return false; +} + + +bool DOM::checkChild(ushort tagID, ushort childID, bool strict) +{ + //kdDebug( 6030 ) << "checkChild: " << tagID << "/" << childID << endl; + + // ### allow comments inside ANY node that can contain children + + if (tagID >= 1000 || childID >= 1000) + return true; // one or both of the elements in an XML element; just allow for now + + switch(tagID) + { + case ID_TT: + case ID_I: + case ID_B: + case ID_U: + case ID_S: + case ID_STRIKE: + case ID_BIG: + case ID_SMALL: + case ID_EM: + case ID_STRONG: + case ID_DFN: + case ID_CODE: + case ID_SAMP: + case ID_KBD: + case ID_VAR: + case ID_CITE: + case ID_ABBR: + case ID_ACRONYM: + case ID_SUB: + case ID_SUP: + case ID_BDO: + case ID_FONT: + case ID_LEGEND: + case ID_Q: + case ID_A: + case ID_NOBR: + case ID_WBR: + // _1 * + return check_array(childID, tag_list_1) || check_array(childID, tag_list_6); + case ID_P: + // P: ( _0 | TABLE | NOSCRIPT) * + return check_array(childID, tag_list_0) || (!strict && (childID == ID_TABLE || childID == ID_NOSCRIPT)); + case ID_H1: + case ID_H2: + case ID_H3: + case ID_H4: + case ID_H5: + case ID_H6: + // _0 * + return check_array(childID, tag_list_0) || + (!strict && check_array(childID, tag_list_3) && (childID < ID_H1 || childID > ID_H6)); + case ID_BASEFONT: + case ID_BR: + case ID_AREA: + case ID_LINK: + case ID_IMG: + case ID_PARAM: + case ID_HR: + case ID_INPUT: + case ID_COL: + case ID_FRAME: + case ID_ISINDEX: + case ID_BASE: + case ID_META: + case ID_COMMENT: + // BASEFONT: EMPTY + return false; + case ID_BODY: + // BODY: _1 * + _2 + return check_array(childID, tag_list_1) || check_array(childID, tag_list_2); + case ID_ADDRESS: + // ADDRESS: ( _0 | P ) * + return check_array(childID, tag_list_0) || childID == ID_P; + case ID_DT: + if ( childID == ID_DL ) return false; + case ID_LI: + case ID_DIV: + case ID_SPAN: + case ID_ILAYER: + case ID_LAYER: + case ID_CENTER: + case ID_BLOCKQUOTE: + case ID_INS: + case ID_DEL: + case ID_DD: + case ID_TH: + case ID_TD: + case ID_IFRAME: + case ID_NOFRAMES: + case ID_NOSCRIPT: + case ID_CAPTION: + case ID_MARQUEE: + // DIV: _1 * + return check_array(childID, tag_list_1); + case ID_MAP: + // We accept SCRIPT in client-side image maps as an extension to the DTD. + // MAP: ( _3 + | AREA + | SCRIPT + ) + return check_array(childID, tag_list_3) || + childID == ID_AREA || + childID == ID_SCRIPT; + case ID_OBJECT: + case ID_EMBED: + case ID_APPLET: + // OBJECT: _4 * + return check_array(childID, tag_list_4); + case ID_PRE: + case ID_XMP: + case ID_PLAINTEXT: + // PRE: _0 * - _5 + return check_array(childID, tag_list_1); + case ID_DL: + // DL: _6 + + return check_array(childID, tag_list_6) || check_array(childID, tag_list_1); + case ID_OL: + case ID_UL: + // OL: LI + + return check_array(childID, tag_list_1); + case ID_DIR: + case ID_MENU: + // (DIR|MENU): LI + - _3 + if(childID == ID_LI) return true; + return false; + case ID_FORM: + // FORM: _1 * - FORM + return check_array(childID, tag_list_1); + case ID_LABEL: + // LABEL: _1 * - LABEL + return check_array(childID, tag_list_1); + // KEYGEN does not really allow any children + // from outside, just need this to be able + // to add the keylengths ourself + // Yes, consider it a hack (Dirk) + case ID_KEYGEN: + case ID_SELECT: + // SELECT: _7 + + return check_array(childID, tag_list_7); + case ID_OPTGROUP: + // OPTGROUP: OPTION + + if(childID == ID_OPTION) return true; + return false; + case ID_OPTION: + case ID_TEXTAREA: + case ID_TITLE: + case ID_STYLE: + case ID_SCRIPT: + // OPTION: TEXT + if(childID == ID_TEXT) return true; + return false; + case ID_FIELDSET: + // FIELDSET: ( TEXT , LEGEND , _1 * ) + if(childID == ID_TEXT) return true; + if(childID == ID_LEGEND) return true; + return check_array(childID, tag_list_1); + case ID_BUTTON: + // BUTTON: _1 * - _8 + return check_array(childID, tag_list_1); + case ID_TABLE: + // TABLE: ( CAPTION ? , ( COL * | COLGROUP * ) , THEAD ? , TFOOT ? , TBODY + ) + switch(childID) + { + case ID_CAPTION: + case ID_COL: + case ID_COLGROUP: + case ID_THEAD: + case ID_TFOOT: + case ID_TBODY: + case ID_FORM: + case ID_SCRIPT: + return true; + default: + return false; + } + case ID_THEAD: + case ID_TFOOT: + case ID_TBODY: + // THEAD: TR + + if(childID == ID_TR || childID == ID_SCRIPT) return true; + return false; + case ID_COLGROUP: + // COLGROUP: COL * + if(childID == ID_COL) return true; + return false; + case ID_TR: + // TR: (TD, TH) + return (childID == ID_TH || childID == ID_TD || childID == ID_SCRIPT); + case ID_FRAMESET: + // FRAMESET: _10 + return check_array(childID, tag_list_10); + case ID_HEAD: + // HEAD: _11 + return check_array(childID, tag_list_11); + case ID_HTML: + // HTML: ( HEAD , COMMENT, ( BODY | ( FRAMESET & NOFRAMES ? ) ) ) + switch(childID) + { + case ID_HEAD: + case ID_COMMENT: + case ID_BODY: + case ID_FRAMESET: + case ID_NOFRAMES: + case ID_SCRIPT: + return true; + default: + return false; + } + default: + kdDebug( 6030 ) << "unhandled tag in dtd.cpp:checkChild(): tagID=" << tagID << "!" << endl; + return false; + } +} + +void DOM::addForbidden(int tagId, ushort *forbiddenTags) +{ + switch(tagId) + { + case ID_A: + // we allow nested anchors. The innermost one wil be taken... + //forbiddenTags[ID_A]++; + break; + case ID_NOBR: + forbiddenTags[ID_PRE]++; + // fall through + case ID_PRE: + case ID_PLAINTEXT: + case ID_XMP: + //forbiddenTags[ID_IMG]++; + forbiddenTags[ID_OBJECT]++; + forbiddenTags[ID_EMBED]++; + forbiddenTags[ID_APPLET]++; + // why forbid them. We can deal with them in PRE + //forbiddenTags[ID_BIG]++; + //forbiddenTags[ID_SMALL]++; + //forbiddenTags[ID_SUB]++; + //forbiddenTags[ID_SUP]++; + forbiddenTags[ID_BASEFONT]++; + break; + case ID_DIR: + case ID_MENU: + forbiddenTags[ID_P]++; + forbiddenTags[ID_H1]++; + forbiddenTags[ID_H2]++; + forbiddenTags[ID_H3]++; + forbiddenTags[ID_H4]++; + forbiddenTags[ID_H5]++; + forbiddenTags[ID_H6]++; + forbiddenTags[ID_UL]++; + forbiddenTags[ID_OL]++; + forbiddenTags[ID_DIR]++; + forbiddenTags[ID_MENU]++; + forbiddenTags[ID_PRE]++; + forbiddenTags[ID_PLAINTEXT]++; + forbiddenTags[ID_XMP]++; + forbiddenTags[ID_DL]++; + forbiddenTags[ID_DIV]++; + forbiddenTags[ID_CENTER]++; + forbiddenTags[ID_NOSCRIPT]++; + forbiddenTags[ID_NOFRAMES]++; + forbiddenTags[ID_BLOCKQUOTE]++; + forbiddenTags[ID_FORM]++; + forbiddenTags[ID_ISINDEX]++; + forbiddenTags[ID_HR]++; + forbiddenTags[ID_TABLE]++; + forbiddenTags[ID_FIELDSET]++; + forbiddenTags[ID_ADDRESS]++; + break; + case ID_LABEL: + forbiddenTags[ID_LABEL]++; + break; + case ID_BUTTON: + forbiddenTags[ID_A]++; + forbiddenTags[ID_INPUT]++; + forbiddenTags[ID_SELECT]++; + forbiddenTags[ID_TEXTAREA]++; + forbiddenTags[ID_LABEL]++; + forbiddenTags[ID_BUTTON]++; + forbiddenTags[ID_FORM]++; + forbiddenTags[ID_ISINDEX]++; + forbiddenTags[ID_FIELDSET]++; + forbiddenTags[ID_IFRAME]++; + break; + default: + break; + } +} + +void DOM::removeForbidden(int tagId, ushort *forbiddenTags) +{ + switch(tagId) + { + case ID_A: + //forbiddenTags[ID_A]--; + break; + case ID_NOBR: + forbiddenTags[ID_PRE]--; + // fall through + case ID_PRE: + case ID_XMP: + case ID_PLAINTEXT: + //forbiddenTags[ID_IMG]--; + forbiddenTags[ID_OBJECT]--; + forbiddenTags[ID_EMBED]--; + forbiddenTags[ID_APPLET]--; + //forbiddenTags[ID_BIG]--; + //forbiddenTags[ID_SMALL]--; + //forbiddenTags[ID_SUB]--; + //forbiddenTags[ID_SUP]--; + forbiddenTags[ID_BASEFONT]--; + break; + case ID_DIR: + case ID_MENU: + forbiddenTags[ID_P]--; + forbiddenTags[ID_H1]--; + forbiddenTags[ID_H2]--; + forbiddenTags[ID_H3]--; + forbiddenTags[ID_H4]--; + forbiddenTags[ID_H5]--; + forbiddenTags[ID_H6]--; + forbiddenTags[ID_UL]--; + forbiddenTags[ID_OL]--; + forbiddenTags[ID_DIR]--; + forbiddenTags[ID_MENU]--; + forbiddenTags[ID_PRE]--; + forbiddenTags[ID_PLAINTEXT]--; + forbiddenTags[ID_XMP]--; + forbiddenTags[ID_DL]--; + forbiddenTags[ID_DIV]--; + forbiddenTags[ID_CENTER]--; + forbiddenTags[ID_NOSCRIPT]--; + forbiddenTags[ID_NOFRAMES]--; + forbiddenTags[ID_BLOCKQUOTE]--; + forbiddenTags[ID_FORM]--; + forbiddenTags[ID_ISINDEX]--; + forbiddenTags[ID_HR]--; + forbiddenTags[ID_TABLE]--; + forbiddenTags[ID_FIELDSET]--; + forbiddenTags[ID_ADDRESS]--; + break; + case ID_LABEL: + forbiddenTags[ID_LABEL]--; + break; + case ID_BUTTON: + forbiddenTags[ID_A]--; + forbiddenTags[ID_INPUT]--; + forbiddenTags[ID_SELECT]--; + forbiddenTags[ID_TEXTAREA]--; + forbiddenTags[ID_LABEL]--; + forbiddenTags[ID_BUTTON]--; + forbiddenTags[ID_FORM]--; + forbiddenTags[ID_ISINDEX]--; + forbiddenTags[ID_FIELDSET]--; + forbiddenTags[ID_IFRAME]--; + break; + default: + break; + } +} + diff --git a/khtml/html/dtd.dtd b/khtml/html/dtd.dtd new file mode 100644 index 000000000..d6a5ac8f0 --- /dev/null +++ b/khtml/html/dtd.dtd @@ -0,0 +1,1072 @@ +<!-- + This is the HTML 4.0 Transitional DTD, which includes + presentation attributes and elements that W3C expects to phase out + as support for style sheets matures. Authors should use the Strict + DTD when possible, but may use the Transitional DTD when support + for presentation attribute and elements is required. + + HTML 4.0 includes mechanisms for style sheets, scripting, + embedding objects, improved support for right to left and mixed + direction text, and enhancements to forms for improved + accessibility for people with disabilities. + + Draft: $Date$ + + Authors: + Dave Raggett <dsr@w3.org> + Arnaud Le Hors <lehors@w3.org> + Ian Jacobs <ij@w3.org> + + Further information about HTML 4.0 is available at: + + http://www.w3.org/TR/REC-html40 +--> +<!ENTITY % HTML.Version "-//W3C//DTD HTML 4.0 Transitional//EN" + -- Typical usage: + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" + "http://www.w3.org/TR/REC-html40/loose.dtd"> + <html> + <head> + ... + </head> + <body> + ... + </body> + </html> + + The URI used as a system identifier with the public identifier allows + the user agent to download the DTD and entity sets as needed. + + The FPI for the Strict HTML 4.0 DTD is: + + "-//W3C//DTD HTML 4.0//EN" + + and its URI is: + + http://www.w3.org/TR/REC-html40/strict.dtd + + Authors should use the Strict DTD unless they need the + presentation control for user agents that don't (adequately) + support style sheets. + + If you are writing a document that includes frames, use + the following FPI: + + "-//W3C//DTD HTML 4.0 Frameset//EN" + + with the URI: + + http://www.w3.org/TR/REC-html40/frameset.dtd + + The following URIs are supported in relation to HTML 4.0 + + "http://www.w3.org/TR/REC-html40/strict.dtd" (Strict DTD) + "http://www.w3.org/TR/REC-html40/loose.dtd" (Loose DTD) + "http://www.w3.org/TR/REC-html40/frameset.dtd" (Frameset DTD) + "http://www.w3.org/TR/REC-html40/HTMLlat1.ent" (Latin-1 entities) + "http://www.w3.org/TR/REC-html40/HTMLsymbol.ent" (Symbol entities) + "http://www.w3.org/TR/REC-html40/HTMLspecial.ent" (Special entities) + + These URIs point to the latest version of each file. To reference + this specific revision use the following URIs: + + "http://www.w3.org/TR/1998/REC-html40-19980424/strict.dtd" + "http://www.w3.org/TR/1998/REC-html40-19980424/loose.dtd" + "http://www.w3.org/TR/1998/REC-html40-19980424/frameset.dtd" + "http://www.w3.org/TR/1998/REC-html40-19980424/HTMLlat1.ent" + "http://www.w3.org/TR/1998/REC-html40-19980424/HTMLsymbol.ent" + "http://www.w3.org/TR/1998/REC-html40-19980424/HTMLspecial.ent" + +--> + +<!--================== Imported Names ====================================--> + +<!ENTITY % ContentType "CDATA" + -- media type, as per [RFC2045] + --> + +<!ENTITY % ContentTypes "CDATA" + -- comma-separated list of media types, as per [RFC2045] + --> + +<!ENTITY % Charset "CDATA" + -- a character encoding, as per [RFC2045] + --> + +<!ENTITY % Charsets "CDATA" + -- a space separated list of character encodings, as per [RFC2045] + --> + +<!ENTITY % LanguageCode "NAME" + -- a language code, as per [RFC1766] + --> + +<!ENTITY % Character "CDATA" + -- a single character from [ISO10646] + --> + +<!ENTITY % LinkTypes "CDATA" + -- space-separated list of link types + --> + +<!ENTITY % MediaDesc "CDATA" + -- single or comma-separated list of media descriptors + --> + +<!ENTITY % URI "CDATA" + -- a Uniform Resource Identifier, + see [URI] + --> + +<!ENTITY % Datetime "CDATA" -- date and time information. ISO date format --> + + +<!ENTITY % Script "CDATA" -- script expression --> + +<!ENTITY % StyleSheet "CDATA" -- style sheet data --> + +<!ENTITY % FrameTarget "CDATA" -- render in this frame --> + + +<!ENTITY % Text "CDATA"> + + +<!-- Parameter Entities --> + +<!ENTITY % head.misc "SCRIPT|STYLE|META|LINK|OBJECT" -- repeatable head elements --> + +<!ENTITY % heading "H1|H2|H3|H4|H5|H6"> + +<!ENTITY % list "UL | OL | DIR | MENU"> + +<!ENTITY % preformatted "PRE"> + +<!ENTITY % Color "CDATA" -- a color using sRGB: #RRGGBB as Hex values --> + +<!-- There are also 16 widely known color names with their sRGB values: + + Black = #000000 Green = #008000 + Silver = #C0C0C0 Lime = #00FF00 + Gray = #808080 Olive = #808000 + White = #FFFFFF Yellow = #FFFF00 + Maroon = #800000 Navy = #000080 + Red = #FF0000 Blue = #0000FF + Purple = #800080 Teal = #008080 + Fuchsia= #FF00FF Aqua = #00FFFF + --> + +<!ENTITY % bodycolors " + bgcolor %Color; #IMPLIED -- document background color -- + text %Color; #IMPLIED -- document text color -- + link %Color; #IMPLIED -- color of links -- + vlink %Color; #IMPLIED -- color of visited links -- + alink %Color; #IMPLIED -- color of selected links -- + "> + +<!--=================== Generic Attributes ===============================--> + +<!ENTITY % coreattrs + "id ID #IMPLIED -- document-wide unique id -- + class CDATA #IMPLIED -- space separated list of classes -- + style %StyleSheet; #IMPLIED -- associated style info -- + title %Text; #IMPLIED -- advisory title/amplification --" + > + +<!ENTITY % i18n + "lang %LanguageCode; #IMPLIED -- language code -- + dir (ltr|rtl) #IMPLIED -- direction for weak/neutral text --" + > + +<!ENTITY % events + "onclick %Script; #IMPLIED -- a pointer button was clicked -- + ondblclick %Script; #IMPLIED -- a pointer button was double clicked-- + onmousedown %Script; #IMPLIED -- a pointer button was pressed down -- + onmouseup %Script; #IMPLIED -- a pointer button was released -- + onmouseover %Script; #IMPLIED -- a pointer was moved onto -- + onmousemove %Script; #IMPLIED -- a pointer was moved within -- + onmouseout %Script; #IMPLIED -- a pointer was moved away -- + onkeypress %Script; #IMPLIED -- a key was pressed and released -- + onkeydown %Script; #IMPLIED -- a key was pressed down -- + onkeyup %Script; #IMPLIED -- a key was released --" + > + +<!-- Reserved Feature Switch --> +<!ENTITY % HTML.Reserved "IGNORE"> + +<!-- The following attributes are reserved for possible future use --> +<![ %HTML.Reserved; [ +<!ENTITY % reserved + "datasrc %URI; #IMPLIED -- a single or tabular Data Source -- + datafld CDATA #IMPLIED -- the property or column name -- + dataformatas (plaintext|html) plaintext -- text or html --" + > +]]> + +<!ENTITY % reserved ""> + +<!ENTITY % attrs "%coreattrs; %i18n; %events;"> + +<!ENTITY % align "align (left|center|right|justify) #IMPLIED" + -- default is left for ltr paragraphs, right for rtl -- + > + +<!--=================== Text Markup ======================================--> + +<!ENTITY % fontstyle + "TT | I | B | U | S | STRIKE | BIG | SMALL"> + +<!ENTITY % phrase "EM | STRONG | DFN | CODE | + SAMP | KBD | VAR | CITE | ABBR | ACRONYM" > + +<!ENTITY % special + "A | IMG | APPLET | OBJECT | FONT | BASEFONT | BR | SCRIPT | + MAP | Q | SUB | SUP | SPAN | BDO | IFRAME"> + +<!ENTITY % formctrl "INPUT | SELECT | TEXTAREA | LABEL | BUTTON"> + +<!-- %inline; covers inline or "text-level" elements --> +<!ENTITY % inline "#PCDATA | %fontstyle; | %phrase; | %special; | %formctrl;"> + +<!ELEMENT (%fontstyle;|%phrase;) - - (%inline;)*> +<!ATTLIST (%fontstyle;|%phrase;) + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!ELEMENT (SUB|SUP) - - (%inline;)* -- subscript, superscript --> +<!ATTLIST (SUB|SUP) + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!ELEMENT SPAN - - (%inline;)* -- generic language/style container --> +<!ATTLIST SPAN + %attrs; -- %coreattrs, %i18n, %events -- + %reserved; -- reserved for possible future use -- + > + +<!ELEMENT BDO - - (%inline;)* -- I18N BiDi over-ride --> +<!ATTLIST BDO + %coreattrs; -- id, class, style, title -- + lang %LanguageCode; #IMPLIED -- language code -- + dir (ltr|rtl) #REQUIRED -- directionality -- + > + +<!ELEMENT BASEFONT - O EMPTY -- base font size --> +<!ATTLIST BASEFONT + id ID #IMPLIED -- document-wide unique id -- + size CDATA #REQUIRED -- base font size for FONT elements -- + color %Color; #IMPLIED -- text color -- + face CDATA #IMPLIED -- comma separated list of font names -- + > + +<!ELEMENT FONT - - (%inline;)* -- local change to font --> +<!ATTLIST FONT + %coreattrs; -- id, class, style, title -- + %i18n; -- lang, dir -- + size CDATA #IMPLIED -- [+|-]nn e.g. size="+1", size="4" -- + color %Color; #IMPLIED -- text color -- + face CDATA #IMPLIED -- comma separated list of font names -- + > + +<!ELEMENT BR - O EMPTY -- forced line break --> +<!ATTLIST BR + %coreattrs; -- id, class, style, title -- + clear (left|all|right|none) none -- control of text flow -- + > + +<!--================== HTML content models ===============================--> + +<!-- + HTML has two basic content models: + + %inline; character level elements and text strings + %block; block-like elements e.g. paragraphs and lists +--> + +<!ENTITY % block + "P | %heading; | %list; | %preformatted; | DL | DIV | CENTER | + NOSCRIPT | NOFRAMES | BLOCKQUOTE | FORM | ISINDEX | HR | + TABLE | FIELDSET | ADDRESS"> + +<!ENTITY % flow "%block; | %inline;"> + +<!--=================== Document Body ====================================--> + +<!ELEMENT BODY O O (%flow;)* +(INS|DEL) -- document body --> +<!ATTLIST BODY + %attrs; -- %coreattrs, %i18n, %events -- + onload %Script; #IMPLIED -- the document has been loaded -- + onunload %Script; #IMPLIED -- the document has been removed -- + background %URI; #IMPLIED -- texture tile for document + background -- + %bodycolors; -- bgcolor, text, link, vlink, alink -- + > + +<!ELEMENT ADDRESS - - ((%inline;)|P)* -- information on author --> +<!ATTLIST ADDRESS + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!ELEMENT DIV - - (%flow;)* -- generic language/style container --> +<!ATTLIST DIV + %attrs; -- %coreattrs, %i18n, %events -- + %align; -- align, text alignment -- + %reserved; -- reserved for possible future use -- + > + +<!ELEMENT CENTER - - (%flow;)* -- shorthand for DIV align=center --> +<!ATTLIST CENTER + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!--================== The Anchor Element ================================--> + +<!ENTITY % Shape "(rect|circle|poly|default)"> +<!ENTITY % Coords "CDATA" -- comma separated list of lengths --> + +<!ELEMENT A - - (%inline;)* -(A) -- anchor --> +<!ATTLIST A + %attrs; -- %coreattrs, %i18n, %events -- + charset %Charset; #IMPLIED -- char encoding of linked resource -- + type %ContentType; #IMPLIED -- advisory content type -- + name CDATA #IMPLIED -- named link end -- + href %URI; #IMPLIED -- URI for linked resource -- + hreflang %LanguageCode; #IMPLIED -- language code -- + target %FrameTarget; #IMPLIED -- render in this frame -- + rel %LinkTypes; #IMPLIED -- forward link types -- + rev %LinkTypes; #IMPLIED -- reverse link types -- + accesskey %Character; #IMPLIED -- accessibility key character -- + shape %Shape; rect -- for use with client-side image maps -- + coords %Coords; #IMPLIED -- for use with client-side image maps -- + tabindex NUMBER #IMPLIED -- position in tabbing order -- + onfocus %Script; #IMPLIED -- the element got the focus -- + onblur %Script; #IMPLIED -- the element lost the focus -- + > + +<!--================== Client-side image maps ============================--> + +<!-- These can be placed in the same document or grouped in a + separate document although this isn't yet widely supported --> + +<!ELEMENT MAP - - ((%block;)+ | AREA+) -- client-side image map --> +<!ATTLIST MAP + %attrs; -- %coreattrs, %i18n, %events -- + name CDATA #REQUIRED -- for reference by usemap -- + > + +<!ELEMENT AREA - O EMPTY -- client-side image map area --> +<!ATTLIST AREA + %attrs; -- %coreattrs, %i18n, %events -- + shape %Shape; rect -- controls interpretation of coords -- + coords %Coords; #IMPLIED -- comma separated list of lengths -- + href %URI; #IMPLIED -- URI for linked resource -- + target %FrameTarget; #IMPLIED -- render in this frame -- + nohref (nohref) #IMPLIED -- this region has no action -- + alt %Text; #REQUIRED -- short description -- + tabindex NUMBER #IMPLIED -- position in tabbing order -- + accesskey %Character; #IMPLIED -- accessibility key character -- + onfocus %Script; #IMPLIED -- the element got the focus -- + onblur %Script; #IMPLIED -- the element lost the focus -- + > + +<!--================== The LINK Element ==================================--> + +<!-- + Relationship values can be used in principle: + + a) for document specific toolbars/menus when used + with the LINK element in document head e.g. + start, contents, previous, next, index, end, help + b) to link to a separate style sheet (rel=stylesheet) + c) to make a link to a script (rel=script) + d) by stylesheets to control how collections of + html nodes are rendered into printed documents + e) to make a link to a printable version of this document + e.g. a postscript or pdf version (rel=alternate media=print) +--> + +<!ELEMENT LINK - O EMPTY -- a media-independent link --> +<!ATTLIST LINK + %attrs; -- %coreattrs, %i18n, %events -- + charset %Charset; #IMPLIED -- char encoding of linked resource -- + href %URI; #IMPLIED -- URI for linked resource -- + hreflang %LanguageCode; #IMPLIED -- language code -- + type %ContentType; #IMPLIED -- advisory content type -- + rel %LinkTypes; #IMPLIED -- forward link types -- + rev %LinkTypes; #IMPLIED -- reverse link types -- + media %MediaDesc; #IMPLIED -- for rendering on these media -- + target %FrameTarget; #IMPLIED -- render in this frame -- + > + +<!--=================== Images ===========================================--> + +<!-- Length defined in strict DTD for cellpadding/cellspacing --> +<!ENTITY % Length "CDATA" -- nn for pixels or nn% for percentage length --> +<!ENTITY % MultiLength "CDATA" -- pixel, percentage, or relative --> + +<!ENTITY % MultiLengths "CDATA" -- comma-separated list of MultiLength --> + +<!ENTITY % Pixels "CDATA" -- integer representing length in pixels --> + +<!ENTITY % IAlign "(top|middle|bottom|left|right)" -- center? --> + +<!-- To avoid problems with text-only UAs as well as + to make image content understandable and navigable + to users of non-visual UAs, you need to provide + a description with ALT, and avoid server-side image maps --> +<!ELEMENT IMG - O EMPTY -- Embedded image --> +<!ATTLIST IMG + %attrs; -- %coreattrs, %i18n, %events -- + src %URI; #REQUIRED -- URI of image to embed -- + alt %Text; #REQUIRED -- short description -- + longdesc %URI; #IMPLIED -- link to long description + (complements alt) -- + height %Length; #IMPLIED -- override height -- + width %Length; #IMPLIED -- override width -- + usemap %URI; #IMPLIED -- use client-side image map -- + ismap (ismap) #IMPLIED -- use server-side image map -- + align %IAlign; #IMPLIED -- vertical or horizontal alignment -- + border %Length; #IMPLIED -- link border width -- + hspace %Pixels; #IMPLIED -- horizontal gutter -- + vspace %Pixels; #IMPLIED -- vertical gutter -- + > + +<!-- USEMAP points to a MAP element which may be in this document + or an external document, although the latter is not widely supported --> + +<!--==================== OBJECT ======================================--> +<!-- + OBJECT is used to embed objects as part of HTML pages + PARAM elements should precede other content. SGML mixed content + model technicality precludes specifying this formally ... +--> + +<!ELEMENT OBJECT - - (PARAM | %flow;)* + -- generic embedded object --> +<!ATTLIST OBJECT + %attrs; -- %coreattrs, %i18n, %events -- + declare (declare) #IMPLIED -- declare but don't instantiate flag -- + classid %URI; #IMPLIED -- identifies an implementation -- + codebase %URI; #IMPLIED -- base URI for classid, data, archive-- + data %URI; #IMPLIED -- reference to object's data -- + type %ContentType; #IMPLIED -- content type for data -- + codetype %ContentType; #IMPLIED -- content type for code -- + archive %URI; #IMPLIED -- space separated archive list -- + standby %Text; #IMPLIED -- message to show while loading -- + height %Length; #IMPLIED -- override height -- + width %Length; #IMPLIED -- override width -- + usemap %URI; #IMPLIED -- use client-side image map -- + name CDATA #IMPLIED -- submit as part of form -- + tabindex NUMBER #IMPLIED -- position in tabbing order -- + align %IAlign; #IMPLIED -- vertical or horizontal alignment -- + border %Length; #IMPLIED -- link border width -- + hspace %Pixels; #IMPLIED -- horizontal gutter -- + vspace %Pixels; #IMPLIED -- vertical gutter -- + %reserved; -- reserved for possible future use -- + > + +<!ELEMENT PARAM - O EMPTY -- named property value --> +<!ATTLIST PARAM + id ID #IMPLIED -- document-wide unique id -- + name CDATA #REQUIRED -- property name -- + value CDATA #IMPLIED -- property value -- + valuetype (DATA|REF|OBJECT) DATA -- How to interpret value -- + type %ContentType; #IMPLIED -- content type for value + when valuetype=ref -- + > + +<!--=================== Java APPLET ==================================--> +<!-- + One of code or object attributes must be present. + Place PARAM elements before other content. +--> +<!ELEMENT APPLET - - (PARAM | %flow;)* -- Java applet --> +<!ATTLIST APPLET + %coreattrs; -- id, class, style, title -- + codebase %URI; #IMPLIED -- optional base URI for applet -- + archive CDATA #IMPLIED -- comma separated archive list -- + code CDATA #IMPLIED -- applet class file -- + object CDATA #IMPLIED -- serialized applet file -- + alt %Text; #IMPLIED -- short description -- + name CDATA #IMPLIED -- allows applets to find each other -- + width %Length; #REQUIRED -- initial width -- + height %Length; #REQUIRED -- initial height -- + align %IAlign; #IMPLIED -- vertical or horizontal alignment -- + hspace %Pixels; #IMPLIED -- horizontal gutter -- + vspace %Pixels; #IMPLIED -- vertical gutter -- + > + +<!--=================== Horizontal Rule ==================================--> + +<!ELEMENT HR - O EMPTY -- horizontal rule --> +<!ATTLIST HR + %coreattrs; -- id, class, style, title -- + %events; + align (left|center|right) #IMPLIED + noshade (noshade) #IMPLIED + size %Pixels; #IMPLIED + width %Length; #IMPLIED + > + +<!--=================== Paragraphs =======================================--> + +<!ELEMENT P - O (%inline;)* -- paragraph --> +<!ATTLIST P + %attrs; -- %coreattrs, %i18n, %events -- + %align; -- align, text alignment -- + > + +<!--=================== Headings =========================================--> + +<!-- + There are six levels of headings from H1 (the most important) + to H6 (the least important). +--> + +<!ELEMENT (%heading;) - - (%inline;)* -- heading --> +<!ATTLIST (%heading;) + %attrs; -- %coreattrs, %i18n, %events -- + %align; -- align, text alignment -- + > + +<!--=================== Preformatted Text ================================--> + +<!-- excludes markup for images and changes in font size --> +<!ENTITY % pre.exclusion "IMG|OBJECT|APPLET|BIG|SMALL|SUB|SUP|FONT|BASEFONT"> + +<!ELEMENT PRE - - (%inline;)* -(%pre.exclusion;) -- preformatted text --> +<!ATTLIST PRE + %attrs; -- %coreattrs, %i18n, %events -- + width NUMBER #IMPLIED + > + +<!--===================== Inline Quotes ==================================--> + +<!ELEMENT Q - - (%inline;)* -- short inline quotation --> +<!ATTLIST Q + %attrs; -- %coreattrs, %i18n, %events -- + cite %URI; #IMPLIED -- URI for source document or msg -- + > + +<!--=================== Block-like Quotes ================================--> + +<!ELEMENT BLOCKQUOTE - - (%flow;)* -- long quotation --> +<!ATTLIST BLOCKQUOTE + %attrs; -- %coreattrs, %i18n, %events -- + cite %URI; #IMPLIED -- URI for source document or msg -- + > + +<!--=================== Inserted/Deleted Text ============================--> + + +<!-- INS/DEL are handled by inclusion on BODY --> +<!ELEMENT (INS|DEL) - - (%flow;)* -- inserted text, deleted text --> +<!ATTLIST (INS|DEL) + %attrs; -- %coreattrs, %i18n, %events -- + cite %URI; #IMPLIED -- info on reason for change -- + datetime %Datetime; #IMPLIED -- date and time of change -- + > + +<!--=================== Lists ============================================--> + +<!-- definition lists - DT for term, DD for its definition --> + +<!ELEMENT DL - - (DT|DD)+ -- definition list --> +<!ATTLIST DL + %attrs; -- %coreattrs, %i18n, %events -- + compact (compact) #IMPLIED -- reduced interitem spacing -- + > + +<!ELEMENT DT - O (%inline;)* -- definition term --> +<!ELEMENT DD - O (%flow;)* -- definition description --> +<!ATTLIST (DT|DD) + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!-- Ordered lists (OL) Numbering style + + 1 arablic numbers 1, 2, 3, ... + a lower alpha a, b, c, ... + A upper alpha A, B, C, ... + i lower roman i, ii, iii, ... + I upper roman I, II, III, ... + + The style is applied to the sequence number which by default + is reset to 1 for the first list item in an ordered list. + + This can't be expressed directly in SGML due to case folding. +--> + +<!ENTITY % OLStyle "CDATA" -- constrained to: "(1|a|A|i|I)" --> + +<!ELEMENT OL - - (LI)+ -- ordered list --> +<!ATTLIST OL + %attrs; -- %coreattrs, %i18n, %events -- + type %OLStyle; #IMPLIED -- numbering style -- + compact (compact) #IMPLIED -- reduced interitem spacing -- + start NUMBER #IMPLIED -- starting sequence number -- + > + +<!-- Unordered Lists (UL) bullet styles --> +<!ENTITY % ULStyle "(disc|square|circle)"> + +<!ELEMENT UL - - (LI)+ -- unordered list --> +<!ATTLIST UL + %attrs; -- %coreattrs, %i18n, %events -- + type %ULStyle; #IMPLIED -- bullet style -- + compact (compact) #IMPLIED -- reduced interitem spacing -- + > + +<!ELEMENT (DIR|MENU) - - (LI)+ -(%block;) -- directory list, menu list --> +<!ATTLIST DIR + %attrs; -- %coreattrs, %i18n, %events -- + compact (compact) #IMPLIED + > +<!ATTLIST MENU + %attrs; -- %coreattrs, %i18n, %events -- + compact (compact) #IMPLIED + > + +<!ENTITY % LIStyle "CDATA" -- constrained to: "(%ULStyle;|%OLStyle;)" --> + +<!ELEMENT LI - O (%flow;)* -- list item --> +<!ATTLIST LI + %attrs; -- %coreattrs, %i18n, %events -- + type %LIStyle; #IMPLIED -- list item style -- + value NUMBER #IMPLIED -- reset sequence number -- + > + +<!--================ Forms ===============================================--> +<!ELEMENT FORM - - (%flow;)* -(FORM) -- interactive form --> +<!ATTLIST FORM + %attrs; -- %coreattrs, %i18n, %events -- + action %URI; #REQUIRED -- server-side form handler -- + method (GET|POST) GET -- HTTP method used to submit the form-- + enctype %ContentType; "application/x-www-form-urlencoded" + onsubmit %Script; #IMPLIED -- the form was submitted -- + onreset %Script; #IMPLIED -- the form was reset -- + target %FrameTarget; #IMPLIED -- render in this frame -- + accept-charset %Charsets; #IMPLIED -- list of supported charsets -- + > + +<!-- Each label must not contain more than ONE field --> +<!ELEMENT LABEL - - (%inline;)* -(LABEL) -- form field label text --> +<!ATTLIST LABEL + %attrs; -- %coreattrs, %i18n, %events -- + for IDREF #IMPLIED -- matches field ID value -- + accesskey %Character; #IMPLIED -- accessibility key character -- + onfocus %Script; #IMPLIED -- the element got the focus -- + onblur %Script; #IMPLIED -- the element lost the focus -- + > + +<!ENTITY % InputType + "(TEXT | PASSWORD | CHECKBOX | + RADIO | SUBMIT | RESET | + FILE | HIDDEN | IMAGE | BUTTON)" + > + +<!-- attribute name required for all but submit & reset --> +<!ELEMENT INPUT - O EMPTY -- form control --> +<!ATTLIST INPUT + %attrs; -- %coreattrs, %i18n, %events -- + type %InputType; TEXT -- what kind of widget is needed -- + name CDATA #IMPLIED -- submit as part of form -- + value CDATA #IMPLIED -- required for radio and checkboxes -- + checked (checked) #IMPLIED -- for radio buttons and check boxes -- + disabled (disabled) #IMPLIED -- unavailable in this context -- + readonly (readonly) #IMPLIED -- for text and passwd -- + size CDATA #IMPLIED -- specific to each type of field -- + maxlength NUMBER #IMPLIED -- max chars for text fields -- + src %URI; #IMPLIED -- for fields with images -- + alt CDATA #IMPLIED -- short description -- + usemap %URI; #IMPLIED -- use client-side image map -- + tabindex NUMBER #IMPLIED -- position in tabbing order -- + accesskey %Character; #IMPLIED -- accessibility key character -- + onfocus %Script; #IMPLIED -- the element got the focus -- + onblur %Script; #IMPLIED -- the element lost the focus -- + onselect %Script; #IMPLIED -- some text was selected -- + onchange %Script; #IMPLIED -- the element value was changed -- + accept %ContentTypes; #IMPLIED -- list of MIME types for file upload -- + align %IAlign; #IMPLIED -- vertical or horizontal alignment -- + %reserved; -- reserved for possible future use -- + > + +<!ELEMENT SELECT - - (OPTGROUP|OPTION)+ -- option selector --> +<!ATTLIST SELECT + %attrs; -- %coreattrs, %i18n, %events -- + name CDATA #IMPLIED -- field name -- + size NUMBER #IMPLIED -- rows visible -- + multiple (multiple) #IMPLIED -- default is single selection -- + disabled (disabled) #IMPLIED -- unavailable in this context -- + tabindex NUMBER #IMPLIED -- position in tabbing order -- + onfocus %Script; #IMPLIED -- the element got the focus -- + onblur %Script; #IMPLIED -- the element lost the focus -- + onchange %Script; #IMPLIED -- the element value was changed -- + %reserved; -- reserved for possible future use -- + > + +<!ELEMENT OPTGROUP - - (OPTION)+ -- option group --> +<!ATTLIST OPTGROUP + %attrs; -- %coreattrs, %i18n, %events -- + disabled (disabled) #IMPLIED -- unavailable in this context -- + label %Text; #REQUIRED -- for use in hierarchical menus -- + > + +<!ELEMENT OPTION - O (#PCDATA) -- selectable choice --> +<!ATTLIST OPTION + %attrs; -- %coreattrs, %i18n, %events -- + selected (selected) #IMPLIED + disabled (disabled) #IMPLIED -- unavailable in this context -- + label %Text; #IMPLIED -- for use in hierarchical menus -- + value CDATA #IMPLIED -- defaults to element content -- + > + +<!ELEMENT TEXTAREA - - (#PCDATA) -- multi-line text field --> +<!ATTLIST TEXTAREA + %attrs; -- %coreattrs, %i18n, %events -- + name CDATA #IMPLIED + rows NUMBER #REQUIRED + cols NUMBER #REQUIRED + disabled (disabled) #IMPLIED -- unavailable in this context -- + readonly (readonly) #IMPLIED + tabindex NUMBER #IMPLIED -- position in tabbing order -- + accesskey %Character; #IMPLIED -- accessibility key character -- + onfocus %Script; #IMPLIED -- the element got the focus -- + onblur %Script; #IMPLIED -- the element lost the focus -- + onselect %Script; #IMPLIED -- some text was selected -- + onchange %Script; #IMPLIED -- the element value was changed -- + %reserved; -- reserved for possible future use -- + > + +<!-- + #PCDATA is to solve the mixed content problem, + per specification only whitespace is allowed there! + --> +<!ELEMENT FIELDSET - - (#PCDATA,LEGEND,(%flow;)*) -- form control group --> +<!ATTLIST FIELDSET + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!ELEMENT LEGEND - - (%inline;)* -- fieldset legend --> +<!ENTITY % LAlign "(top|bottom|left|right)"> + +<!ATTLIST LEGEND + %attrs; -- %coreattrs, %i18n, %events -- + accesskey %Character; #IMPLIED -- accessibility key character -- + align %LAlign; #IMPLIED -- relative to fieldset -- + > + +<!ELEMENT BUTTON - - + (%flow;)* -(A|%formctrl;|FORM|ISINDEX|FIELDSET|IFRAME) + -- push button --> +<!ATTLIST BUTTON + %attrs; -- %coreattrs, %i18n, %events -- + name CDATA #IMPLIED + value CDATA #IMPLIED -- sent to server when submitted -- + type (button|submit|reset) submit -- for use as form button -- + disabled (disabled) #IMPLIED -- unavailable in this context -- + tabindex NUMBER #IMPLIED -- position in tabbing order -- + accesskey %Character; #IMPLIED -- accessibility key character -- + onfocus %Script; #IMPLIED -- the element got the focus -- + onblur %Script; #IMPLIED -- the element lost the focus -- + %reserved; -- reserved for possible future use -- + > + +<!--======================= Tables =======================================--> + +<!-- IETF HTML table standard, see [RFC1942] --> + +<!-- + The BORDER attribute sets the thickness of the frame around the + table. The default units are screen pixels. + + The FRAME attribute specifies which parts of the frame around + the table should be rendered. The values are not the same as + CALS to avoid a name clash with the VALIGN attribute. + + The value "border" is included for backwards compatibility with + <TABLE BORDER> which yields frame=border and border=implied + For <TABLE BORDER=1> you get border=1 and frame=implied. In this + case, it is appropriate to treat this as frame=border for backwards + compatibility with deployed browsers. +--> +<!ENTITY % TFrame "(void|above|below|hsides|lhs|rhs|vsides|box|border)"> + +<!-- + The RULES attribute defines which rules to draw between cells: + + If RULES is absent then assume: + "none" if BORDER is absent or BORDER=0 otherwise "all" +--> + +<!ENTITY % TRules "(none | groups | rows | cols | all)"> + +<!-- horizontal placement of table relative to document --> +<!ENTITY % TAlign "(left|center|right)"> + +<!-- horizontal alignment attributes for cell contents --> +<!ENTITY % cellhalign + "align (left|center|right|justify|char) #IMPLIED + char %Character; #IMPLIED -- alignment char, e.g. char=':' -- + charoff %Length; #IMPLIED -- offset for alignment char --" + > + +<!-- vertical alignment attributes for cell contents --> +<!ENTITY % cellvalign + "valign (top|middle|bottom|baseline) #IMPLIED" + > + +<!ELEMENT TABLE - - + (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)> +<!ELEMENT CAPTION - - (%inline;)* -- table caption --> +<!ELEMENT THEAD - O (TR)+ -- table header --> +<!ELEMENT TFOOT - O (TR)+ -- table footer --> +<!ELEMENT TBODY O O (TR)+ -- table body --> +<!ELEMENT COLGROUP - O (col)* -- table column group --> +<!ELEMENT COL - O EMPTY -- table column --> +<!ELEMENT TR - O (TH|TD)+ -- table row --> +<!ELEMENT (TH|TD) - O (%flow;)* -- table header cell, table data cell--> + +<!ATTLIST TABLE -- table element -- + %attrs; -- %coreattrs, %i18n, %events -- + summary %Text; #IMPLIED -- purpose/structure for speech output-- + width %Length; #IMPLIED -- table width -- + border %Pixels; #IMPLIED -- controls frame width around table -- + frame %TFrame; #IMPLIED -- which parts of frame to render -- + rules %TRules; #IMPLIED -- rulings between rows and cols -- + cellspacing %Length; #IMPLIED -- spacing between cells -- + cellpadding %Length; #IMPLIED -- spacing within cells -- + align %TAlign; #IMPLIED -- table position relative to window -- + bgcolor %Color; #IMPLIED -- background color for cells -- + %reserved; -- reserved for possible future use -- + datapagesize CDATA #IMPLIED -- reserved for possible future use -- + > + +<!ENTITY % CAlign "(top|bottom|left|right)"> + +<!ATTLIST CAPTION + %attrs; -- %coreattrs, %i18n, %events -- + align %CAlign; #IMPLIED -- relative to table -- + > + +<!-- +COLGROUP groups a set of COL elements. It allows you to group +several semantically related columns together. +--> +<!ATTLIST COLGROUP + %attrs; -- %coreattrs, %i18n, %events -- + span NUMBER 1 -- default number of columns in group -- + width %MultiLength; #IMPLIED -- default width for enclosed COLs -- + %cellhalign; -- horizontal alignment in cells -- + %cellvalign; -- vertical alignment in cells -- + > + +<!-- + COL elements define the alignment properties for cells in + one or more columns. + + The WIDTH attribute specifies the width of the columns, e.g. + + width=64 width in screen pixels + width=0.5* relative width of 0.5 + + The SPAN attribute causes the attributes of one + COL element to apply to more than one column. +--> +<!ATTLIST COL -- column groups and properties -- + %attrs; -- %coreattrs, %i18n, %events -- + span NUMBER 1 -- COL attributes affect N columns -- + width %MultiLength; #IMPLIED -- column width specification -- + %cellhalign; -- horizontal alignment in cells -- + %cellvalign; -- vertical alignment in cells -- + > + +<!-- + Use THEAD to duplicate headers when breaking table + across page boundaries, or for static headers when + TBODY sections are rendered in scrolling panel. + + Use TFOOT to duplicate footers when breaking table + across page boundaries, or for static footers when + TBODY sections are rendered in scrolling panel. + + Use multiple TBODY sections when rules are needed + between groups of table rows. +--> +<!ATTLIST (THEAD|TBODY|TFOOT) -- table section -- + %attrs; -- %coreattrs, %i18n, %events -- + %cellhalign; -- horizontal alignment in cells -- + %cellvalign; -- vertical alignment in cells -- + > + +<!ATTLIST TR -- table row -- + %attrs; -- %coreattrs, %i18n, %events -- + %cellhalign; -- horizontal alignment in cells -- + %cellvalign; -- vertical alignment in cells -- + bgcolor %Color; #IMPLIED -- background color for row -- + > + + +<!-- Scope is simpler than axes attribute for common tables --> +<!ENTITY % Scope "(row|col|rowgroup|colgroup)"> + +<!-- TH is for headers, TD for data, but for cells acting as both use TD --> +<!ATTLIST (TH|TD) -- header or data cell -- + %attrs; -- %coreattrs, %i18n, %events -- + abbr %Text; #IMPLIED -- abbreviation for header cell -- + axis CDATA #IMPLIED -- names groups of related headers-- + headers IDREFS #IMPLIED -- list of id's for header cells -- + scope %Scope; #IMPLIED -- scope covered by header cells -- + rowspan NUMBER 1 -- number of rows spanned by cell -- + colspan NUMBER 1 -- number of cols spanned by cell -- + %cellhalign; -- horizontal alignment in cells -- + %cellvalign; -- vertical alignment in cells -- + nowrap (nowrap) #IMPLIED -- suppress word wrap -- + bgcolor %Color; #IMPLIED -- cell background color -- + width %Pixels; #IMPLIED -- width for cell -- + height %Pixels; #IMPLIED -- height for cell -- + > + +<!--================== Document Frames ===================================--> + +<!-- + The content model for HTML documents depends on whether the HEAD is + followed by a FRAMESET or BODY element. The widespread omission of + the BODY start tag makes it impractical to define the content model + without the use of a marked section. +--> + +<!ELEMENT FRAMESET - - ((FRAMESET|FRAME)+ & NOFRAMES?) -- window subdivision--> +<!ATTLIST FRAMESET + %coreattrs; -- id, class, style, title -- + rows %MultiLengths; #IMPLIED -- list of lengths, + default: 100% (1 row) -- + cols %MultiLengths; #IMPLIED -- list of lengths, + default: 100% (1 col) -- + onload %Script; #IMPLIED -- all the frames have been loaded -- + onunload %Script; #IMPLIED -- all the frames have been removed -- + > + +<!-- reserved frame names start with "_" otherwise starts with letter --> +<!ELEMENT FRAME - O EMPTY -- subwindow --> +<!ATTLIST FRAME + %coreattrs; -- id, class, style, title -- + longdesc %URI; #IMPLIED -- link to long description + (complements title) -- + name CDATA #IMPLIED -- name of frame for targetting -- + src %URI; #IMPLIED -- source of frame content -- + frameborder (1|0) 1 -- request frame borders? -- + marginwidth %Pixels; #IMPLIED -- margin widths in pixels -- + marginheight %Pixels; #IMPLIED -- margin height in pixels -- + noresize (noresize) #IMPLIED -- allow users to resize frames? -- + scrolling (yes|no|auto) auto -- scrollbar or none -- + > + +<!ELEMENT IFRAME - - (%flow;)* -- inline subwindow --> +<!ATTLIST IFRAME + %coreattrs; -- id, class, style, title -- + longdesc %URI; #IMPLIED -- link to long description + (complements title) -- + name CDATA #IMPLIED -- name of frame for targetting -- + src %URI; #IMPLIED -- source of frame content -- + frameborder (1|0) 1 -- request frame borders? -- + marginwidth %Pixels; #IMPLIED -- margin widths in pixels -- + marginheight %Pixels; #IMPLIED -- margin height in pixels -- + scrolling (yes|no|auto) auto -- scrollbar or none -- + align %IAlign; #IMPLIED -- vertical or horizontal alignment -- + height %Length; #IMPLIED -- frame height -- + width %Length; #IMPLIED -- frame width -- + > + +<![ %HTML.Frameset; [ +<!ENTITY % noframes.content "(BODY) -(NOFRAMES)"> +]]> + +<!ENTITY % noframes.content "(%flow;)*"> + +<!ELEMENT NOFRAMES - - %noframes.content; + -- alternate content container for non frame-based rendering --> +<!ATTLIST NOFRAMES + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!--================ Document Head =======================================--> +<!-- %head.misc; defined earlier on as "SCRIPT|STYLE|META|LINK|OBJECT" --> +<!ENTITY % head.content "TITLE & ISINDEX? & BASE?"> + +<!ELEMENT HEAD O O (%head.content;) +(%head.misc;) -- document head --> +<!ATTLIST HEAD + %i18n; -- lang, dir -- + profile %URI; #IMPLIED -- named dictionary of meta info -- + > + +<!-- The TITLE element is not considered part of the flow of text. + It should be displayed, for example as the page header or + window title. Exactly one title is required per document. + --> +<!-- Lars: small correction here... --> +<!--ELEMENT TITLE - - (#PCDATA) -(%head.misc;) -- document title -- --> +<!ELEMENT TITLE - - (#PCDATA) -- document title --> +<!ATTLIST TITLE %i18n> + +<!ELEMENT ISINDEX - O EMPTY -- single line prompt --> +<!ATTLIST ISINDEX + %coreattrs; -- id, class, style, title -- + %i18n; -- lang, dir -- + prompt %Text; #IMPLIED -- prompt message --> + +<!ELEMENT BASE - O EMPTY -- document base URI --> +<!ATTLIST BASE + href %URI; #IMPLIED -- URI that acts as base URI -- + target %FrameTarget; #IMPLIED -- render in this frame -- + > + +<!ELEMENT META - O EMPTY -- generic metainformation --> +<!ATTLIST META + %i18n; -- lang, dir, for use with content -- + http-equiv NAME #IMPLIED -- HTTP response header name -- + name NAME #IMPLIED -- metainformation name -- + content CDATA #REQUIRED -- associated information -- + scheme CDATA #IMPLIED -- select form of content -- + > + +<!ELEMENT STYLE - - %StyleSheet -- style info --> +<!ATTLIST STYLE + %i18n; -- lang, dir, for use with title -- + type %ContentType; #REQUIRED -- content type of style language -- + media %MediaDesc; #IMPLIED -- designed for use with these media -- + title %Text; #IMPLIED -- advisory title -- + > + +<!ELEMENT SCRIPT - - %Script; -- script statements --> +<!ATTLIST SCRIPT + charset %Charset; #IMPLIED -- char encoding of linked resource -- + type %ContentType; #REQUIRED -- content type of script language -- + language CDATA #IMPLIED -- predefined script language name -- + src %URI; #IMPLIED -- URI for an external script -- + defer (defer) #IMPLIED -- UA may defer execution of script -- + event CDATA #IMPLIED -- reserved for possible future use -- + for %URI; #IMPLIED -- reserved for possible future use -- + > + +<!ELEMENT NOSCRIPT - - (%flow;)* + -- alternate content container for non script-based rendering --> +<!ATTLIST NOSCRIPT + %attrs; -- %coreattrs, %i18n, %events -- + > + +<!--================ Document Structure ==================================--> +<!ENTITY % version "version CDATA %HTML.Version;"> + +<!--[ %HTML.Frameset; [ +<!ENTITY % html.content "HEAD, FRAMESET"> +]]--> + +<!-- Lars: slight correction here: include FRAMESET --> +<!ENTITY % html.content "HEAD, (BODY|(FRAMESET&NOFRAMES?))"> + +<!ELEMENT HTML O O (%html.content;) -- document root element --> +<!ATTLIST HTML + %i18n; -- lang, dir -- + %version; + > diff --git a/khtml/html/dtd.h b/khtml/html/dtd.h new file mode 100644 index 000000000..4192ce51c --- /dev/null +++ b/khtml/html/dtd.h @@ -0,0 +1,41 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef _DOM_dtd_h_ +#define _DOM_dtd_h_ + +#include "dom/dom_string.h" + +namespace DOM +{ + +void addForbidden(int tagId, ushort *forbiddenTags); +void removeForbidden(int tagId, ushort *forbiddenTags); + +enum tagStatus { OPTIONAL, REQUIRED, FORBIDDEN }; + +bool checkChild(ushort tagID, ushort childID, bool strict = false); + +extern const unsigned short tagPriority[]; +extern const tagStatus endTag[]; + +} //namespace DOM +#endif diff --git a/khtml/html/html_baseimpl.cpp b/khtml/html/html_baseimpl.cpp new file mode 100644 index 000000000..babbac76d --- /dev/null +++ b/khtml/html/html_baseimpl.cpp @@ -0,0 +1,708 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001-2003 Dirk Mueller (mueller@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +// ------------------------------------------------------------------------- + +#include "html/html_baseimpl.h" +#include "html/html_documentimpl.h" + +#include "khtmlview.h" +#include "khtml_part.h" + +#include "rendering/render_frames.h" +#include "rendering/render_body.h" +#include "css/cssstyleselector.h" +#include "css/css_stylesheetimpl.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "css/csshelper.h" +#include "misc/loader.h" +#include "misc/htmlhashes.h" +#include "dom/dom_string.h" +#include "dom/dom_doc.h" +#include "xml/dom2_eventsimpl.h" + +#include <kurl.h> +#include <kdebug.h> + +using namespace DOM; +using namespace khtml; + +HTMLBodyElementImpl::HTMLBodyElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc), + m_bgSet( false ), m_fgSet( false ) +{ + m_styleSheet = 0; +} + +HTMLBodyElementImpl::~HTMLBodyElementImpl() +{ + if(m_styleSheet) m_styleSheet->deref(); +} + +NodeImpl::Id HTMLBodyElementImpl::id() const +{ + return ID_BODY; +} + +void HTMLBodyElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + + case ATTR_BACKGROUND: + { + QString url = khtml::parseURL( attr->value() ).string(); + if (!url.isEmpty()) { + url = getDocument()->completeURL( url ); + addCSSProperty(CSS_PROP_BACKGROUND_IMAGE, "url('"+url+"')" ); + m_bgSet = true; + } + else { + removeCSSProperty(CSS_PROP_BACKGROUND_IMAGE); + m_bgSet = false; + } + break; + } + case ATTR_MARGINWIDTH: { + KHTMLView* w = getDocument()->view(); + if (w) + w->setMarginWidth( -1 ); // unset this, so it doesn't override the setting here + addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value() ); + } + /* nobreak; */ + case ATTR_LEFTMARGIN: + addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value() ); + break; + case ATTR_MARGINHEIGHT: { + KHTMLView* w = getDocument()->view(); + if (w) + w->setMarginHeight( -1 ); // unset this, so it doesn't override the setting here + addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value()); + } + /* nobreak */ + case ATTR_TOPMARGIN: + addCSSLength(CSS_PROP_MARGIN_TOP, attr->value()); + break; + case ATTR_BGCOLOR: + addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value()); + m_bgSet = !attr->value().isNull(); + break; + case ATTR_TEXT: + addHTMLColor(CSS_PROP_COLOR, attr->value()); + m_fgSet = !attr->value().isNull(); + break; + case ATTR_BGPROPERTIES: + if ( strcasecmp( attr->value(), "fixed" ) == 0) + addCSSProperty(CSS_PROP_BACKGROUND_ATTACHMENT, CSS_VAL_FIXED); + break; + case ATTR_VLINK: + case ATTR_ALINK: + case ATTR_LINK: + { + if(!m_styleSheet) { + m_styleSheet = new CSSStyleSheetImpl(this,DOMString(),true); + m_styleSheet->ref(); + } + QString aStr; + if ( attr->id() == ATTR_LINK ) + aStr = "a:link"; + else if ( attr->id() == ATTR_VLINK ) + aStr = "a:visited"; + else if ( attr->id() == ATTR_ALINK ) + aStr = "a:active"; + aStr += " { color: " + attr->value().string() + "; }"; + m_styleSheet->parseString(aStr, !getDocument()->inCompatMode()); + m_styleSheet->setNonCSSHints(); + if (attached()) + getDocument()->updateStyleSelector(); + break; + } + case ATTR_ONLOAD: + getDocument()->setHTMLWindowEventListener(EventImpl::LOAD_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onload", NULL)); + break; + case ATTR_ONUNLOAD: + getDocument()->setHTMLWindowEventListener(EventImpl::UNLOAD_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onunload", NULL)); + break; + case ATTR_ONBLUR: + getDocument()->setHTMLWindowEventListener(EventImpl::BLUR_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onblur", NULL)); + break; + case ATTR_ONFOCUS: + getDocument()->setHTMLWindowEventListener(EventImpl::FOCUS_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onfocus", NULL)); + break; + case ATTR_ONRESIZE: + getDocument()->setHTMLWindowEventListener(EventImpl::RESIZE_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onresize", NULL)); + break; + case ATTR_ONKEYUP: + getDocument()->setHTMLWindowEventListener(EventImpl::KEYUP_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onkeyup", NULL)); + break; + case ATTR_ONKEYDOWN: + getDocument()->setHTMLWindowEventListener(EventImpl::KEYDOWN_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onkeydown", NULL)); + break; + case ATTR_ONKEYPRESS: + getDocument()->setHTMLWindowEventListener(EventImpl::KEYPRESS_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onkeypress", NULL)); + break; + case ATTR_ONSCROLL: + getDocument()->setHTMLWindowEventListener(EventImpl::SCROLL_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onscroll", NULL)); + break; + case ATTR_NOSAVE: + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLBodyElementImpl::insertedIntoDocument() +{ + HTMLElementImpl::insertedIntoDocument(); + + KHTMLView* w = getDocument()->view(); + if(w && w->marginWidth() != -1) { + QString s; + s.sprintf( "%d", w->marginWidth() ); + addCSSLength(CSS_PROP_MARGIN_LEFT, s); + addCSSLength(CSS_PROP_MARGIN_RIGHT, s); + } + if(w && w->marginHeight() != -1) { + QString s; + s.sprintf( "%d", w->marginHeight() ); + addCSSLength(CSS_PROP_MARGIN_TOP, s); + addCSSLength(CSS_PROP_MARGIN_BOTTOM, s); + } + + if ( m_bgSet && !m_fgSet ) + addCSSProperty(CSS_PROP_COLOR, CSS_VAL_BLACK); + + if (m_styleSheet) + getDocument()->updateStyleSelector(); +} + +void HTMLBodyElementImpl::removedFromDocument() +{ + HTMLElementImpl::removedFromDocument(); + + if (m_styleSheet) + getDocument()->updateStyleSelector(); +} + +void HTMLBodyElementImpl::attach() +{ + assert(!m_render); + assert(parentNode()); + + RenderStyle* style = getDocument()->styleSelector()->styleForElement(this); + style->ref(); + if (parentNode()->renderer() && style->display() != NONE) { + if (style->display() == BLOCK) + // only use the quirky class for block display + m_render = new (getDocument()->renderArena()) RenderBody(this); + else + m_render = RenderObject::createObject(this, style); + m_render->setStyle(style); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + } + style->deref(); + + NodeBaseImpl::attach(); +} + +// ------------------------------------------------------------------------- + +HTMLFrameElementImpl::HTMLFrameElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) +{ + frameBorder = true; + frameBorderSet = false; + marginWidth = -1; + marginHeight = -1; + scrolling = QScrollView::Auto; + noresize = false; + url = "about:blank"; +} + +HTMLFrameElementImpl::~HTMLFrameElementImpl() +{ +} + +NodeImpl::Id HTMLFrameElementImpl::id() const +{ + return ID_FRAME; +} + +void HTMLFrameElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_SRC: + setLocation(khtml::parseURL(attr->val())); + break; + case ATTR_FRAMEBORDER: + { + frameBorder = attr->value().toInt(); + frameBorderSet = ( attr->val() != 0 ); + // FIXME: when attached, has no effect + } + break; + case ATTR_MARGINWIDTH: + marginWidth = attr->val()->toInt(); + // FIXME: when attached, has no effect + break; + case ATTR_MARGINHEIGHT: + marginHeight = attr->val()->toInt(); + // FIXME: when attached, has no effect + break; + case ATTR_NORESIZE: + noresize = true; + // FIXME: when attached, has no effect + break; + case ATTR_SCROLLING: + if( strcasecmp( attr->value(), "auto" ) == 0 ) + scrolling = QScrollView::Auto; + else if( strcasecmp( attr->value(), "yes" ) == 0 ) + scrolling = QScrollView::AlwaysOn; + else if( strcasecmp( attr->value(), "no" ) == 0 ) + scrolling = QScrollView::AlwaysOff; + // when attached, has no effect + break; + case ATTR_ONLOAD: + setHTMLEventListener(EventImpl::LOAD_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onload", this)); + break; + case ATTR_ONUNLOAD: + setHTMLEventListener(EventImpl::UNLOAD_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onunload", this)); + break; + case ATTR_ID: + case ATTR_NAME: + // FIXME: if already attached, doesn't change the frame name + // FIXME: frame name conflicts, no unique frame name anymore + name = attr->value(); + //fallthrough intentional, let the base handle it + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLFrameElementImpl::attach() +{ + assert(!attached()); + assert(parentNode()); + + name = getAttribute(ATTR_NAME); + if (name.isNull()) + name = getAttribute(ATTR_ID); + + // inherit default settings from parent frameset + HTMLElementImpl* node = static_cast<HTMLElementImpl*>(parentNode()); + while(node) + { + if(node->id() == ID_FRAMESET) + { + HTMLFrameSetElementImpl* frameset = static_cast<HTMLFrameSetElementImpl*>(node); + if(!frameBorderSet) frameBorder = frameset->frameBorder(); + if(!noresize) noresize = frameset->noResize(); + break; + } + node = static_cast<HTMLElementImpl*>(node->parentNode()); + } + + if (parentNode()->renderer() && getDocument()->isURLAllowed(url.string())) { + RenderStyle* _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + if ( _style->display() != NONE ) { + m_render = new (getDocument()->renderArena()) RenderFrame(this); + m_render->setStyle(_style); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + } + _style->deref(); + } + + NodeBaseImpl::attach(); + + if (!m_render) + return; + + KHTMLView* w = getDocument()->view(); + if (w) { + // we need a unique name for every frame in the frameset. Hope that's unique enough. + if(name.isEmpty() || w->part()->frameExists( name.string() ) ) + name = DOMString(w->part()->requestFrameName()); + + // load the frame contents + w->part()->requestFrame( static_cast<RenderFrame*>(m_render), url.string(), name.string() ); + } +} + +void HTMLFrameElementImpl::setLocation( const DOMString& str ) +{ + + url = str; + + if( !attached() ) + return; + + if( !m_render ) { + detach(); + attach(); + return; + } + + if( !getDocument()->isURLAllowed(url.string()) ) + return; + + // load the frame contents + KHTMLView *w = getDocument()->view(); + if (w) { + KHTMLPart *part = w->part()->findFrame( name.string() ); + if ( part ) { + part->openURL( KURL( getDocument()->completeURL( url.string() ) ) ); + } else { + w->part()->requestFrame( static_cast<RenderFrame*>( m_render ), url.string(), name.string() ); + } + } +} + +bool HTMLFrameElementImpl::isFocusable() const +{ + return m_render!=0; +} + +void HTMLFrameElementImpl::setFocus(bool received) +{ + HTMLElementImpl::setFocus(received); + khtml::RenderFrame *renderFrame = static_cast<khtml::RenderFrame *>(m_render); + if (!renderFrame || !renderFrame->widget()) + return; + if (received) + renderFrame->widget()->setFocus(); + else + renderFrame->widget()->clearFocus(); +} + +DocumentImpl* HTMLFrameElementImpl::contentDocument() const +{ + if ( !m_render ) return 0; + + RenderPart* render = static_cast<RenderPart*>( m_render ); + + if(render->widget() && ::qt_cast<KHTMLView*>( render->widget()) ) + return static_cast<KHTMLView*>( render->widget() )->part()->xmlDocImpl(); + + return 0; +} + +KHTMLPart* HTMLFrameElementImpl::contentPart() const +{ + if ( !m_render ) return 0; + + RenderPart* render = static_cast<RenderPart*>( m_render ); + + if(render->widget() && ::qt_cast<KHTMLView*>( render->widget()) ) + return static_cast<KHTMLView*>( render->widget() )->part(); + + return 0; +} + +// ------------------------------------------------------------------------- + +HTMLFrameSetElementImpl::HTMLFrameSetElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) +{ + // default value for rows and cols... + m_totalRows = 1; + m_totalCols = 1; + + m_rows = m_cols = 0; + + frameborder = true; + frameBorderSet = false; + m_border = 4; + noresize = false; + + m_resizing = false; + + m_onLoad = m_onUnLoad = 0; +} + +HTMLFrameSetElementImpl::~HTMLFrameSetElementImpl() +{ + //### this is likely not quite right since we may be effectively "overriding" some old value, + //which needs to be recomputed, but this is better than crashing... + if (getDocument()) { + if (m_onLoad && getDocument()->getHTMLEventListener(EventImpl::LOAD_EVENT) == m_onLoad) + getDocument()->setHTMLEventListener(EventImpl::LOAD_EVENT, 0); + + if (m_onUnLoad && getDocument()->getHTMLEventListener(EventImpl::UNLOAD_EVENT) == m_onUnLoad) + getDocument()->setHTMLEventListener(EventImpl::UNLOAD_EVENT, 0); + } + + delete [] m_rows; + delete [] m_cols; +} + +NodeImpl::Id HTMLFrameSetElementImpl::id() const +{ + return ID_FRAMESET; +} + +void HTMLFrameSetElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_ROWS: + if (!attr->val()) break; + delete [] m_rows; + m_rows = attr->val()->toLengthArray(m_totalRows); + setChanged(); + break; + case ATTR_COLS: + if (!attr->val()) break; + delete [] m_cols; + m_cols = attr->val()->toLengthArray(m_totalCols); + setChanged(); + break; + case ATTR_FRAMEBORDER: + // false or "no" or "0".. + if ( attr->value().toInt() == 0 ) { + frameborder = false; + m_border = 0; + } + frameBorderSet = true; + break; + case ATTR_NORESIZE: + noresize = true; + break; + case ATTR_BORDER: + m_border = attr->val()->toInt(); + if(!m_border) + frameborder = false; + break; + case ATTR_ONLOAD: + m_onLoad = getDocument()->createHTMLEventListener(attr->value().string(), "onload", this); + getDocument()->setHTMLEventListener(EventImpl::LOAD_EVENT, m_onLoad); + break; + case ATTR_ONUNLOAD: + m_onUnLoad = getDocument()->createHTMLEventListener(attr->value().string(), "onunload", this); + getDocument()->setHTMLEventListener(EventImpl::UNLOAD_EVENT, m_onUnLoad); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLFrameSetElementImpl::attach() +{ + assert(!attached() ); + assert(parentNode()); + + // inherit default settings from parent frameset + HTMLElementImpl* node = static_cast<HTMLElementImpl*>(parentNode()); + while(node) + { + if(node->id() == ID_FRAMESET) + { + HTMLFrameSetElementImpl* frameset = static_cast<HTMLFrameSetElementImpl*>(node); + if(!frameBorderSet) frameborder = frameset->frameBorder(); + if(!noresize) noresize = frameset->noResize(); + break; + } + node = static_cast<HTMLElementImpl*>(node->parentNode()); + } + + // ignore display: none + if ( parentNode()->renderer() ) { + m_render = new (getDocument()->renderArena()) RenderFrameSet(this); + m_render->setStyle(getDocument()->styleSelector()->styleForElement(this)); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + } + + NodeBaseImpl::attach(); +} + +void HTMLFrameSetElementImpl::defaultEventHandler(EventImpl *evt) +{ + if (evt->isMouseEvent() && !noresize && m_render) + static_cast<khtml::RenderFrameSet *>(m_render)->userResize(static_cast<MouseEventImpl*>(evt)); + + evt->setDefaultHandled(); + HTMLElementImpl::defaultEventHandler(evt); +} + +void HTMLFrameSetElementImpl::detach() +{ + if(attached()) + // ### send the event when we actually get removed from the doc instead of here + getDocument()->dispatchHTMLEvent(EventImpl::UNLOAD_EVENT,false,false); + + HTMLElementImpl::detach(); +} + +void HTMLFrameSetElementImpl::recalcStyle( StyleChange ch ) +{ + if (changed() && m_render) { + m_render->setNeedsLayout(true); +// m_render->layout(); + setChanged(false); + } + HTMLElementImpl::recalcStyle( ch ); +} + + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLHeadElementImpl::id() const +{ + return ID_HEAD; +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLHtmlElementImpl::id() const +{ + return ID_HTML; +} + + +// ------------------------------------------------------------------------- + +HTMLIFrameElementImpl::HTMLIFrameElementImpl(DocumentImpl *doc) : HTMLFrameElementImpl(doc) +{ + frameBorder = false; + marginWidth = 0; + marginHeight = 0; + needWidgetUpdate = false; + m_frame = true; +} + +HTMLIFrameElementImpl::~HTMLIFrameElementImpl() +{ +} + +NodeImpl::Id HTMLIFrameElementImpl::id() const +{ + return ID_IFRAME; +} + +void HTMLIFrameElementImpl::parseAttribute(AttributeImpl *attr ) +{ + switch ( attr->id() ) + { + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_WIDTH, attr->value()); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_HEIGHT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_HEIGHT, attr->value()); + else + removeCSSProperty(CSS_PROP_HEIGHT); + break; + case ATTR_ALIGN: + addHTMLAlignment( attr->value() ); + break; + case ATTR_SRC: + needWidgetUpdate = true; // ### do this for scrolling, margins etc? + HTMLFrameElementImpl::parseAttribute( attr ); + break; + case ATTR_FRAMEBORDER: + { + m_frame = (!attr->val() || attr->value().toInt() > 0); + if (attached()) updateFrame(); + } + default: + HTMLFrameElementImpl::parseAttribute( attr ); + } +} + +void HTMLIFrameElementImpl::updateFrame() +{ + if (m_frame) { + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_OUTSET); + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_OUTSET); + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_OUTSET); + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_OUTSET); + addCSSLength(CSS_PROP_BORDER_WIDTH, "2"); + } else { + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_NONE); + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_NONE); + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_NONE); + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_NONE); + removeCSSProperty(CSS_PROP_BORDER_WIDTH); + } + +} + +void HTMLIFrameElementImpl::attach() +{ + assert(!attached()); + assert(!m_render); + assert(parentNode()); + + updateFrame(); + name = getAttribute(ATTR_NAME); + if (name.isNull()) + name = getAttribute(ATTR_ID); + + RenderStyle* style = getDocument()->styleSelector()->styleForElement(this); + style->ref(); + if (getDocument()->isURLAllowed(url.string()) && + parentNode()->renderer() && style->display() != NONE) { + m_render = new (getDocument()->renderArena()) RenderPartObject(this); + m_render->setStyle(style); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + } + style->deref(); + + NodeBaseImpl::attach(); + + if (m_render) { + // we need a unique name for every frame in the frameset. Hope that's unique enough. + KHTMLView* w = getDocument()->view(); + if(w && (name.isEmpty() || w->part()->frameExists( name.string() ))) + name = DOMString(w->part()->requestFrameName()); + + needWidgetUpdate = false; + static_cast<RenderPartObject*>(m_render)->updateWidget(); + } +} + +void HTMLIFrameElementImpl::recalcStyle( StyleChange ch ) +{ + if (needWidgetUpdate) { + needWidgetUpdate = false; + if(m_render) static_cast<RenderPartObject*>(m_render)->updateWidget(); + } + HTMLElementImpl::recalcStyle( ch ); +} + diff --git a/khtml/html/html_baseimpl.h b/khtml/html/html_baseimpl.h new file mode 100644 index 000000000..f30dc81f6 --- /dev/null +++ b/khtml/html/html_baseimpl.h @@ -0,0 +1,207 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 2000-2003 Dirk Mueller (mueller@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2002 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef HTML_BASEIMPL_H +#define HTML_BASEIMPL_H + +#include "html/dtd.h" +#include "html/html_elementimpl.h" +#include "misc/khtmllayout.h" + +#include <qscrollview.h> + +class KHTMLView; +class KHTMLPart; + +namespace khtml { + class RenderFrameSet; + class RenderFrame; + class RenderPartObject; +} + +namespace DOM { + +class DOMString; +class CSSStyleSheetImpl; +class HTMLFrameElement; + +// ------------------------------------------------------------------------- + +class HTMLBodyElementImpl : public HTMLElementImpl +{ +public: + HTMLBodyElementImpl(DocumentImpl *doc); + ~HTMLBodyElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *); + virtual void attach(); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + CSSStyleSheetImpl *sheet() const { return m_styleSheet; } + +protected: + CSSStyleSheetImpl *m_styleSheet; + bool m_bgSet; + bool m_fgSet; +}; + +// ------------------------------------------------------------------------- + +class HTMLFrameElementImpl : public HTMLElementImpl +{ + friend class khtml::RenderFrame; + friend class khtml::RenderPartObject; + +public: + HTMLFrameElementImpl(DocumentImpl *doc); + + ~HTMLFrameElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *); + virtual void attach(); + + bool noResize() { return noresize; } + void setLocation( const DOMString& str ); + + virtual bool isFocusable() const; + virtual void setFocus(bool); + + DocumentImpl* contentDocument() const; + KHTMLPart* contentPart() const; + + DOMString url; + DOMString name; + + int marginWidth; + int marginHeight; + QScrollView::ScrollBarMode scrolling; + + bool frameBorder : 1; + bool frameBorderSet : 1; + bool noresize : 1; +}; + +// ------------------------------------------------------------------------- + +class HTMLFrameSetElementImpl : public HTMLElementImpl +{ + friend class khtml::RenderFrameSet; +public: + HTMLFrameSetElementImpl(DocumentImpl *doc); + + ~HTMLFrameSetElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *); + virtual void attach(); + + virtual void defaultEventHandler(EventImpl *evt); + + bool frameBorder() { return frameborder; } + bool noResize() { return noresize; } + + int totalRows() const { return m_totalRows; } + int totalCols() const { return m_totalCols; } + int border() const { return m_border; } + virtual void detach(); + + virtual void recalcStyle( StyleChange ch ); + +protected: + khtml::Length* m_rows; + khtml::Length* m_cols; + + int m_totalRows; + int m_totalCols; + int m_border; + + bool frameborder : 1; + bool frameBorderSet : 1; + bool noresize : 1; + bool m_resizing : 1; // is the user resizing currently + + EventListener* m_onLoad; + EventListener* m_onUnLoad; +}; + +// ------------------------------------------------------------------------- + +class HTMLHeadElementImpl : public HTMLElementImpl +{ +public: + HTMLHeadElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + virtual Id id() const; +}; + +// ------------------------------------------------------------------------- + +class HTMLHtmlElementImpl : public HTMLElementImpl +{ +public: + HTMLHtmlElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + virtual Id id() const; +}; + + +// ------------------------------------------------------------------------- + +class HTMLIFrameElementImpl : public HTMLFrameElementImpl +{ +public: + HTMLIFrameElementImpl(DocumentImpl *doc); + + ~HTMLIFrameElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); + virtual void recalcStyle( StyleChange ch ); + +protected: + + void updateFrame(); + + bool needWidgetUpdate; + bool m_frame; +}; + + +} //namespace + +#endif + diff --git a/khtml/html/html_blockimpl.cpp b/khtml/html/html_blockimpl.cpp new file mode 100644 index 000000000..ac6d6436c --- /dev/null +++ b/khtml/html/html_blockimpl.cpp @@ -0,0 +1,371 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2003 Apple Computer, Inc. + * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +// ------------------------------------------------------------------------- +//#define DEBUG +#include "html_blockimpl.h" +#include "html_documentimpl.h" +#include "css/cssstyleselector.h" + +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "misc/htmlhashes.h" + +#include <kdebug.h> + +using namespace khtml; +using namespace DOM; + +void HTMLDivElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_ALIGN: + { + DOMString v = attr->value().lower(); + if ( strcmp( v, "middle" ) == 0 || strcmp( v, "center" ) == 0 ) + addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_CENTER); + else if (strcmp(v, "left") == 0) + addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_LEFT); + else if (strcmp(v, "right") == 0) + addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_RIGHT); + else + addCSSProperty(CSS_PROP_TEXT_ALIGN, v); + break; + } + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLHRElementImpl::id() const +{ + return ID_HR; +} + +void HTMLHRElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch( attr->id() ) + { + case ATTR_ALIGN: { + if (strcasecmp(attr->value(), "left") == 0) { + addCSSProperty(CSS_PROP_MARGIN_LEFT, "0"); + addCSSProperty(CSS_PROP_MARGIN_RIGHT, CSS_VAL_AUTO); + } + else if (strcasecmp(attr->value(), "right") == 0) { + addCSSProperty(CSS_PROP_MARGIN_LEFT, CSS_VAL_AUTO); + addCSSProperty(CSS_PROP_MARGIN_RIGHT, "0"); + } + else { + addCSSProperty(CSS_PROP_MARGIN_LEFT, CSS_VAL_AUTO); + addCSSProperty(CSS_PROP_MARGIN_RIGHT, CSS_VAL_AUTO); + } + break; + } + case ATTR_WIDTH: + { + if(!attr->val()) break; + // cheap hack to cause linebreaks + // khtmltests/html/strange_hr.html + bool ok; + int v = attr->val()->toInt(&ok); + if(ok && !v) + addCSSLength(CSS_PROP_WIDTH, "1"); + else + addCSSLength(CSS_PROP_WIDTH, attr->value()); + } + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ### make sure we undo what we did during detach +void HTMLHRElementImpl::attach() +{ + if (attributes(true /* readonly */)) { + // there are some attributes, lets check + DOMString color = getAttribute(ATTR_COLOR); + DOMStringImpl* si = getAttribute(ATTR_SIZE).implementation(); + int _s = si ? si->toInt() : -1; + DOMString n("1"); + if (!color.isNull()) { + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID); + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID); + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID); + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID); + addCSSProperty(CSS_PROP_BORDER_TOP_WIDTH, DOMString("0")); + addCSSLength(CSS_PROP_BORDER_BOTTOM_WIDTH, DOMString(si)); + addHTMLColor(CSS_PROP_BORDER_COLOR, color); + } + else { + if (_s > 1 && getAttribute(ATTR_NOSHADE).isNull()) { + addCSSProperty(CSS_PROP_BORDER_BOTTOM_WIDTH, n); + addCSSProperty(CSS_PROP_BORDER_TOP_WIDTH, n); + addCSSProperty(CSS_PROP_BORDER_LEFT_WIDTH, n); + addCSSProperty(CSS_PROP_BORDER_RIGHT_WIDTH, n); + addCSSLength(CSS_PROP_HEIGHT, DOMString(QString::number(_s-2))); + } + else if (_s >= 0) { + addCSSProperty(CSS_PROP_BORDER_TOP_WIDTH, DOMString(QString::number(_s))); + addCSSProperty(CSS_PROP_BORDER_BOTTOM_WIDTH, DOMString("0")); + } + } + if (_s == 0) + addCSSProperty(CSS_PROP_MARGIN_BOTTOM, n); + } + + HTMLElementImpl::attach(); +} + +// ------------------------------------------------------------------------- + +long HTMLPreElementImpl::width() const +{ + // ### + return 0; +} + +void HTMLPreElementImpl::setWidth( long /*w*/ ) +{ + // ### +} + +// ------------------------------------------------------------------------- + + // WinIE uses 60ms as the minimum delay by default. +const int defaultMinimumDelay = 60; + +HTMLMarqueeElementImpl::HTMLMarqueeElementImpl(DocumentImpl *doc) +: HTMLElementImpl(doc), + m_minimumDelay(defaultMinimumDelay) +{ +} + +NodeImpl::Id HTMLMarqueeElementImpl::id() const +{ + return ID_MARQUEE; +} + +void HTMLMarqueeElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_WIDTH, attr->value()); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_HEIGHT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_HEIGHT, attr->value()); + else + removeCSSProperty(CSS_PROP_HEIGHT); + break; + case ATTR_BGCOLOR: + if (!attr->value().isEmpty()) + addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value()); + else + removeCSSProperty(CSS_PROP_BACKGROUND_COLOR); + break; + case ATTR_VSPACE: + if (!attr->value().isEmpty()) { + addCSSLength(CSS_PROP_MARGIN_TOP, attr->value()); + addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value()); + } + else { + removeCSSProperty(CSS_PROP_MARGIN_TOP); + removeCSSProperty(CSS_PROP_MARGIN_BOTTOM); + } + break; + case ATTR_HSPACE: + if (!attr->value().isEmpty()) { + addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value()); + addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value()); + } + else { + removeCSSProperty(CSS_PROP_MARGIN_LEFT); + removeCSSProperty(CSS_PROP_MARGIN_RIGHT); + } + break; + case ATTR_SCROLLAMOUNT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP__KHTML_MARQUEE_INCREMENT, attr->value()); + else + removeCSSProperty(CSS_PROP__KHTML_MARQUEE_INCREMENT); + break; + case ATTR_SCROLLDELAY: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP__KHTML_MARQUEE_SPEED, attr->value(), true); + else + removeCSSProperty(CSS_PROP__KHTML_MARQUEE_SPEED); + break; + case ATTR_LOOP: + if (!attr->value().isEmpty()) { + if (attr->value() == "-1" || strcasecmp(attr->value(), "infinite") == 0) + addCSSProperty(CSS_PROP__KHTML_MARQUEE_REPETITION, CSS_VAL_INFINITE); + else + addCSSLength(CSS_PROP__KHTML_MARQUEE_REPETITION, attr->value().lower(), true); + } + else + removeCSSProperty(CSS_PROP__KHTML_MARQUEE_REPETITION); + break; + case ATTR_BEHAVIOR: + if (!attr->value().isEmpty()) + addCSSProperty(CSS_PROP__KHTML_MARQUEE_STYLE, attr->value().lower()); + else + removeCSSProperty(CSS_PROP__KHTML_MARQUEE_STYLE); + break; + case ATTR_DIRECTION: + if (!attr->value().isEmpty()) + addCSSProperty(CSS_PROP__KHTML_MARQUEE_DIRECTION, attr->value().lower()); + else + removeCSSProperty(CSS_PROP__KHTML_MARQUEE_DIRECTION); + break; + case ATTR_TRUESPEED: + m_minimumDelay = attr->val() ? 0 : defaultMinimumDelay; + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------ + +HTMLLayerElementImpl::HTMLLayerElementImpl(DocumentImpl *doc, ushort _tagid) + : HTMLDivElementImpl( doc, _tagid ) +{ + transparent = fixed = false; +} + +void HTMLLayerElementImpl::parseAttribute(AttributeImpl *attr) +{ + // Layers are evil + // They are mainly implemented here to correctly parse the hidden attribute + switch(attr->id()) { + case ATTR_LEFT: + addCSSProperty(CSS_PROP_LEFT, attr->value()); + break; + case ATTR_TOP: + addCSSProperty(CSS_PROP_TOP, attr->value()); + break; + case ATTR_PAGEX: + if (!transparent && !fixed) { + addCSSProperty(CSS_PROP_POSITION, CSS_VAL_FIXED); + fixed = true; + } + addCSSProperty(CSS_PROP_LEFT, attr->value()); + break; + case ATTR_PAGEY: + if (!transparent && !fixed) { + addCSSProperty(CSS_PROP_POSITION, CSS_VAL_FIXED); + fixed = true; + } + addCSSProperty(CSS_PROP_TOP, attr->value()); + break; + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_WIDTH, attr->value()); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_HEIGHT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_HEIGHT, attr->value()); + else + removeCSSProperty(CSS_PROP_HEIGHT); + break; + case ATTR_BGCOLOR: + if (!attr->value().isEmpty()) + addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value()); + else + removeCSSProperty(CSS_PROP_BACKGROUND_COLOR); + break; + case ATTR_Z_INDEX: + if (!attr->value().isEmpty()) + addCSSProperty(CSS_PROP_Z_INDEX, attr->value()); + else + removeCSSProperty(CSS_PROP_Z_INDEX); + break; + case ATTR_VISIBILITY: + if (attr->value().lower() == "show") + addCSSProperty(CSS_PROP_VISIBILITY, CSS_VAL_VISIBLE); + else if (attr->value().lower() == "hide") + addCSSProperty(CSS_PROP_VISIBILITY, CSS_VAL_HIDDEN); + else if (attr->value().lower() == "inherit") + addCSSProperty(CSS_PROP_VISIBILITY, CSS_VAL_INHERIT); + break; + case ATTR_NAME: + if (id() == ID_LAYER && inDocument() && m_name != attr->value()) { + getDocument()->underDocNamedCache().remove(m_name.string(), this); + getDocument()->underDocNamedCache().add (attr->value().string(), this); + } + //fallthrough + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLLayerElementImpl::removedFromDocument() +{ + if (id() == ID_LAYER) + getDocument()->underDocNamedCache().remove(m_name.string(), this); + HTMLDivElementImpl::removedFromDocument(); +} + +void HTMLLayerElementImpl::insertedIntoDocument() +{ + if (id() == ID_LAYER) + getDocument()->underDocNamedCache().add(m_name.string(), this); + HTMLDivElementImpl::insertedIntoDocument(); +} + +void HTMLLayerElementImpl::removeId(const QString& id) +{ + getDocument()->underDocNamedCache().remove(id, this); + HTMLDivElementImpl::removeId(id); +} + +void HTMLLayerElementImpl::addId (const QString& id) +{ + getDocument()->underDocNamedCache().add(id, this); + HTMLDivElementImpl::addId(id); +} + + + +NodeImpl *HTMLLayerElementImpl::addChild(NodeImpl *child) +{ + NodeImpl *retval = HTMLDivElementImpl::addChild(child); + // When someone adds standard layers, we make sure not to interfere + if (retval && retval->id() == ID_DIV) { + if (!transparent) + addCSSProperty(CSS_PROP_POSITION, CSS_VAL_STATIC); + transparent = true; + } + return retval; +} diff --git a/khtml/html/html_blockimpl.h b/khtml/html/html_blockimpl.h new file mode 100644 index 000000000..a3a1b8994 --- /dev/null +++ b/khtml/html/html_blockimpl.h @@ -0,0 +1,108 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2003 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +// ------------------------------------------------------------------------- +#ifndef HTML_BLOCKIMPL_H +#define HTML_BLOCKIMPL_H + +#include "html_elementimpl.h" +#include "dtd.h" + +namespace DOM { + +// ------------------------------------------------------------------------- + +class HTMLDivElementImpl : public HTMLGenericElementImpl +{ +public: + HTMLDivElementImpl(DocumentImpl *doc, ushort _tagid) + : HTMLGenericElementImpl(doc, _tagid) {} + + virtual void parseAttribute(AttributeImpl *token); +}; + +// ------------------------------------------------------------------------- + +class HTMLHRElementImpl : public HTMLElementImpl +{ +public: + HTMLHRElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + virtual NodeImpl::Id id() const; + virtual void parseAttribute(AttributeImpl *); + virtual void attach(); +}; + +// ------------------------------------------------------------------------- + +class HTMLPreElementImpl : public HTMLGenericElementImpl +{ +public: + HTMLPreElementImpl(DocumentImpl *doc, ushort _tagid) + : HTMLGenericElementImpl(doc, _tagid) {} + + long width() const; + void setWidth( long w ); +}; + +// ------------------------------------------------------------------------- + +class HTMLMarqueeElementImpl : public HTMLElementImpl +{ +public: + HTMLMarqueeElementImpl(DocumentImpl *doc); + + virtual NodeImpl::Id id() const; + virtual void parseAttribute(AttributeImpl *token); + + int minimumDelay() const { return m_minimumDelay; } + +private: + int m_minimumDelay; +}; + +// ------------------------------------------------------------------------- + +class HTMLLayerElementImpl : public HTMLDivElementImpl +{ +public: + HTMLLayerElementImpl( DocumentImpl *doc, ushort _tagid ); + + virtual void parseAttribute(AttributeImpl *); + virtual NodeImpl *addChild(NodeImpl *child); + + virtual void removedFromDocument(); + virtual void insertedIntoDocument(); + virtual void addId(const QString& id); + virtual void removeId(const QString& id); +private: + DOMString m_name; + bool fixed; + bool transparent; +}; + +} //namespace +#endif + diff --git a/khtml/html/html_documentimpl.cpp b/khtml/html/html_documentimpl.cpp new file mode 100644 index 000000000..59ec6859f --- /dev/null +++ b/khtml/html/html_documentimpl.cpp @@ -0,0 +1,537 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "html/html_documentimpl.h" +#include "html/html_imageimpl.h" +#include "html/html_headimpl.h" +#include "html/html_baseimpl.h" +#include "html/htmltokenizer.h" +#include "html/html_miscimpl.h" +#include "html/html_formimpl.h" + +#include "khtmlview.h" +#include "khtml_part.h" +#include "khtmlpart_p.h" +#include "khtml_settings.h" +#include "misc/htmlattrs.h" +#include "misc/htmlhashes.h" + +#include "xml/xml_tokenizer.h" +#include "xml/dom2_eventsimpl.h" + +#include "khtml_factory.h" +#include "rendering/render_object.h" +#include "dom/dom_exception.h" + +#include <dcopclient.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kurl.h> +#include <kglobal.h> +#include <kcharsets.h> +#include <kglobalsettings.h> + +#include "css/cssproperties.h" +#include "css/cssstyleselector.h" +#include "css/css_stylesheetimpl.h" +#include <stdlib.h> +#include <qptrstack.h> + +// Turn off inlining to avoid warning with newer gcc. +#undef __inline +#define __inline +#include "doctypes.cpp" +#undef __inline + +template class QPtrStack<DOM::NodeImpl>; + +using namespace DOM; +using namespace khtml; + + +HTMLDocumentImpl::HTMLDocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v) + : DocumentImpl(_implementation, v) +{ +// kdDebug( 6090 ) << "HTMLDocumentImpl constructor this = " << this << endl; + htmlElement = 0; + + m_doAutoFill = false; + +/* dynamic history stuff to be fixed later (pfeiffer) + connect( KHTMLFactory::vLinks(), SIGNAL( removed( const QString& )), + SLOT( slotHistoryChanged() )); +*/ + connect( KHTMLFactory::vLinks(), SIGNAL( inserted( const QString& ) ), + SLOT( slotHistoryChanged() )); + connect( KHTMLFactory::vLinks(), SIGNAL( cleared()), + SLOT( slotHistoryChanged() )); +} + +HTMLDocumentImpl::~HTMLDocumentImpl() +{ +} + +DOMString HTMLDocumentImpl::referrer() const +{ + if ( view() ) + return view()->part()->pageReferrer(); + return DOMString(); +} + +DOMString HTMLDocumentImpl::lastModified() const +{ + if ( view() ) + return view()->part()->lastModified(); + return DOMString(); +} + +DOMString HTMLDocumentImpl::cookie() const +{ + long windowId = 0; + KHTMLView *v = view (); + + if ( v && v->topLevelWidget() ) + windowId = v->topLevelWidget()->winId(); + + QCString replyType; + QByteArray params, reply; + QDataStream stream(params, IO_WriteOnly); + stream << URL().url() << windowId; + if (!kapp->dcopClient()->call("kcookiejar", "kcookiejar", + "findDOMCookies(QString,long int)", params, + replyType, reply)) + { + kdWarning(6010) << "Can't communicate with cookiejar!" << endl; + return DOMString(); + } + + QDataStream stream2(reply, IO_ReadOnly); + if(replyType != "QString") { + kdError(6010) << "DCOP function findDOMCookies(...) returns " + << replyType << ", expected QString" << endl; + return DOMString(); + } + + QString result; + stream2 >> result; + return DOMString(result); +} + +void HTMLDocumentImpl::setCookie( const DOMString & value ) +{ + long windowId = 0; + KHTMLView *v = view (); + + if ( v && v->topLevelWidget() ) + windowId = v->topLevelWidget()->winId(); + + QByteArray params; + QDataStream stream(params, IO_WriteOnly); + QCString fake_header("Set-Cookie: "); + fake_header.append(value.string().latin1()); + fake_header.append("\n"); + stream << URL().url() << fake_header << windowId; + if (!kapp->dcopClient()->send("kcookiejar", "kcookiejar", + "addCookies(QString,QCString,long int)", params)) + { + // Maybe it wasn't running (e.g. we're opening local html files) + KApplication::startServiceByDesktopName( "kcookiejar"); + if (!kapp->dcopClient()->send("kcookiejar", "kcookiejar", + "addCookies(QString,QCString,long int)", params)) + kdWarning(6010) << "Can't communicate with cookiejar!" << endl; + } +} + + + +HTMLElementImpl *HTMLDocumentImpl::body() +{ + NodeImpl *de = documentElement(); + if (!de) + return 0; + + // try to prefer a FRAMESET element over BODY + NodeImpl* body = 0; + for (NodeImpl* i = de->firstChild(); i; i = i->nextSibling()) { + if (i->id() == ID_FRAMESET) + return static_cast<HTMLElementImpl*>(i); + + if (i->id() == ID_BODY) + body = i; + } + return static_cast<HTMLElementImpl *>(body); +} + +void HTMLDocumentImpl::setBody(HTMLElementImpl *_body, int& exceptioncode) +{ + HTMLElementImpl *b = body(); + if ( !_body ) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + if ( !b ) + documentElement()->appendChild( _body, exceptioncode ); + else + documentElement()->replaceChild( _body, b, exceptioncode ); +} + +Tokenizer *HTMLDocumentImpl::createTokenizer() +{ + return new HTMLTokenizer(docPtr(),m_view); +} + +// -------------------------------------------------------------------------- +// not part of the DOM +// -------------------------------------------------------------------------- + +bool HTMLDocumentImpl::childAllowed( NodeImpl *newChild ) +{ + // ### support comments. etc as a child + return (newChild->id() == ID_HTML || newChild->id() == ID_COMMENT); +} + +ElementImpl *HTMLDocumentImpl::createElement( const DOMString &name, int* pExceptioncode ) +{ + ElementImpl *e = createHTMLElement(name); + if ( e ) { + e->setHTMLCompat( htmlMode() != XHtml ); + return e; + } + return DocumentImpl::createElement(name, pExceptioncode); +} + +void HTMLDocumentImpl::slotHistoryChanged() +{ + if ( true || !m_render ) + return; + + recalcStyle( Force ); + m_render->repaint(); +} + +HTMLMapElementImpl* HTMLDocumentImpl::getMap(const DOMString& _url) +{ + QString url = _url.string(); + QString s; + int pos = url.find('#'); + //kdDebug(0) << "map pos of #:" << pos << endl; + s = QString(_url.unicode() + pos + 1, _url.length() - pos - 1); + + QMapConstIterator<QString,HTMLMapElementImpl*> it = mapMap.find(s); + + if (it != mapMap.end()) + return *it; + else + return 0; +} + +void HTMLDocumentImpl::close() +{ + bool doload = !parsing() && m_tokenizer; + + DocumentImpl::close(); + + if (doload) { + + if (title().isEmpty()) // ensure setTitle is called at least once + setTitle( DOMString() ); + + // auto fill: walk the tree and try to fill in login credentials + if (view() && m_doAutoFill) { + for (NodeImpl* n = this; n; n = n->traverseNextNode()) + if (n->id() == ID_FORM) + static_cast<HTMLFormElementImpl*>(n)->doAutoFill(); + m_doAutoFill = false; + } + + // According to dom the load event must not bubble + // but other browsers execute in a frameset document + // the first(IE)/last(Moz/Konq) registered onload on a <frame> and the + // first(IE)/last(Moz/Konq) registered onload on a <frameset>. + + //kdDebug() << "dispatching LOAD_EVENT on document " << getDocument() << " " << (view()?view()->part()->name():0) << endl; + + //Make sure to flush any pending image events now, as we want them out before the document's load event + dispatchImageLoadEventsNow(); + getDocument()->dispatchWindowEvent(EventImpl::LOAD_EVENT, false, false); + + // don't update rendering if we're going to redirect anyway + if ( view() && ( view()->part()->d->m_redirectURL.isNull() || + view()->part()->d->m_delayRedirect > 1 ) ) + updateRendering(); + } +} + + +const int PARSEMODE_HAVE_DOCTYPE = (1<<0); +const int PARSEMODE_HAVE_PUBLIC_ID = (1<<1); +const int PARSEMODE_HAVE_SYSTEM_ID = (1<<2); +const int PARSEMODE_HAVE_INTERNAL = (1<<3); + +static int parseDocTypePart(const QString& buffer, int index) +{ + while (true) { + QChar ch = buffer[index]; + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') + ++index; + else if (ch == '-') { + int tmpIndex=index; + if (buffer[index+1] == '-' && + ((tmpIndex=buffer.find("--", index+2)) != -1)) + index = tmpIndex+2; + else + return index; + } + else + return index; + } +} + +static bool containsString(const char* str, const QString& buffer, int offset) +{ + QString startString(str); + if (offset + startString.length() > buffer.length()) + return false; + + QString bufferString = buffer.mid(offset, startString.length()).lower(); + QString lowerStart = startString.lower(); + + return bufferString.startsWith(lowerStart); +} + +static bool parseDocTypeDeclaration(const QString& buffer, + int* resultFlags, + QString& publicID, + QString& systemID) +{ + bool haveDocType = false; + *resultFlags = 0; + + // Skip through any comments and processing instructions. + int index = 0; + do { + index = buffer.find('<', index); + if (index == -1) break; + QChar nextChar = buffer[index+1]; + if (nextChar == '!') { + if (containsString("doctype", buffer, index+2)) { + haveDocType = true; + index += 9; // Skip "<!DOCTYPE" + break; + } + index = parseDocTypePart(buffer,index); + index = buffer.find('>', index); + } + else if (nextChar == '?') + index = buffer.find('>', index); + else + break; + } while (index != -1); + + if (!haveDocType) + return true; + *resultFlags |= PARSEMODE_HAVE_DOCTYPE; + + index = parseDocTypePart(buffer, index); + if (!containsString("html", buffer, index)) + return false; + + index = parseDocTypePart(buffer, index+4); + bool hasPublic = containsString("public", buffer, index); + if (hasPublic) { + index = parseDocTypePart(buffer, index+6); + + // We've read <!DOCTYPE HTML PUBLIC (not case sensitive). + // Now we find the beginning and end of the public identifers + // and system identifiers (assuming they're even present). + QChar theChar = buffer[index]; + if (theChar != '\"' && theChar != '\'') + return false; + + // |start| is the first character (after the quote) and |end| + // is the final quote, so there are |end|-|start| characters. + int publicIDStart = index+1; + int publicIDEnd = buffer.find(theChar, publicIDStart); + if (publicIDEnd == -1) + return false; + index = parseDocTypePart(buffer, publicIDEnd+1); + QChar next = buffer[index]; + if (next == '>') { + // Public identifier present, but no system identifier. + // Do nothing. Note that this is the most common + // case. + } + else if (next == '\"' || next == '\'') { + // We have a system identifier. + *resultFlags |= PARSEMODE_HAVE_SYSTEM_ID; + int systemIDStart = index+1; + int systemIDEnd = buffer.find(next, systemIDStart); + if (systemIDEnd == -1) + return false; + systemID = buffer.mid(systemIDStart, systemIDEnd - systemIDStart); + } + else if (next == '[') { + // We found an internal subset. + *resultFlags |= PARSEMODE_HAVE_INTERNAL; + } + else + return false; // Something's wrong. + + // We need to trim whitespace off the public identifier. + publicID = buffer.mid(publicIDStart, publicIDEnd - publicIDStart); + publicID = publicID.simplifyWhiteSpace(); + *resultFlags |= PARSEMODE_HAVE_PUBLIC_ID; + } else { + if (containsString("system", buffer, index)) { + // Doctype has a system ID but no public ID + *resultFlags |= PARSEMODE_HAVE_SYSTEM_ID; + index = parseDocTypePart(buffer, index+6); + QChar next = buffer[index]; + if (next != '\"' && next != '\'') + return false; + int systemIDStart = index+1; + int systemIDEnd = buffer.find(next, systemIDStart); + if (systemIDEnd == -1) + return false; + systemID = buffer.mid(systemIDStart, systemIDEnd - systemIDStart); + index = parseDocTypePart(buffer, systemIDEnd+1); + } + + QChar nextChar = buffer[index]; + if (nextChar == '[') + *resultFlags |= PARSEMODE_HAVE_INTERNAL; + else if (nextChar != '>') + return false; + } + + return true; +} + +void HTMLDocumentImpl::determineParseMode( const QString &str ) +{ + //kdDebug() << "DocumentImpl::determineParseMode str=" << str<< endl; + int oldPMode = pMode; + + // This code more or less mimics Mozilla's implementation (specifically the + // doctype parsing implemented by David Baron in Mozilla's nsParser.cpp). + // + // There are three possible parse modes: + // COMPAT - quirks mode emulates WinIE + // and NS4. CSS parsing is also relaxed in this mode, e.g., unit types can + // be omitted from numbers. + // ALMOST STRICT - This mode is identical to strict mode + // except for its treatment of line-height in the inline box model. For + // now (until the inline box model is re-written), this mode is identical + // to STANDARDS mode. + // STRICT - no quirks apply. Web pages will obey the specifications to + // the letter. + + QString systemID, publicID; + int resultFlags = 0; + if (parseDocTypeDeclaration(str, &resultFlags, publicID, systemID)) { + if (resultFlags & PARSEMODE_HAVE_DOCTYPE) { + m_doctype->setName("HTML"); + m_doctype->setPublicId(publicID); + m_doctype->setSystemId(systemID); + } + if (!(resultFlags & PARSEMODE_HAVE_DOCTYPE)) { + // No doctype found at all. Default to quirks mode and Html4. + pMode = Compat; + hMode = Html4; + } + else if ((resultFlags & PARSEMODE_HAVE_INTERNAL) || + !(resultFlags & PARSEMODE_HAVE_PUBLIC_ID)) { + // Internal subsets always denote full standards, as does + // a doctype without a public ID. + pMode = Strict; + hMode = Html4; + } + else { + // We have to check a list of public IDs to see what we + // should do. + QString lowerPubID = publicID.lower(); + const char* pubIDStr = lowerPubID.latin1(); + + // Look up the entry in our gperf-generated table. + const PubIDInfo* doctypeEntry = findDoctypeEntry(pubIDStr, publicID.length()); + if (!doctypeEntry) { + // The DOCTYPE is not in the list. Assume strict mode. + // ### Doesn't make any sense, but it's what Mozilla does. + pMode = Strict; + hMode = Html4; + return; + } + + switch ((resultFlags & PARSEMODE_HAVE_SYSTEM_ID) ? + doctypeEntry->mode_if_sysid : + doctypeEntry->mode_if_no_sysid) + { + case PubIDInfo::eQuirks3: + pMode = Compat; + hMode = Html3; + break; + case PubIDInfo::eQuirks: + pMode = Compat; + hMode = Html4; + break; + case PubIDInfo::eAlmostStandards: + pMode = Transitional; + hMode = Html4; + break; + default: + assert(false); + } + } + } + else { + // Malformed doctype implies quirks mode. + pMode = Compat; + hMode = Html3; + } + + // This needs to be done last, see tests/parser/compatmode_xhtml_mixed.html + if ( hMode == Html4 && !m_htmlRequested ) { + // this part is still debatable and possibly UA dependent + hMode = XHtml; + pMode = Transitional; + } + + m_styleSelector->strictParsing = !inCompatMode(); + + // kdDebug() << "DocumentImpl::determineParseMode: publicId =" << publicID << " systemId = " << systemID << endl; + // kdDebug() << "DocumentImpl::determineParseMode: htmlMode = " << hMode<< endl; + if( pMode == Strict ) + kdDebug(6030) << " using strict parseMode" << endl; + else if (pMode == Compat ) + kdDebug(6030) << " using compatibility parseMode" << endl; + else + kdDebug(6030) << " using transitional parseMode" << endl; + + // not sure this is needed + if ( pMode != oldPMode && styleSelector() ) + recalcStyleSelector(); + +} + + +#include "html_documentimpl.moc" diff --git a/khtml/html/html_documentimpl.h b/khtml/html/html_documentimpl.h new file mode 100644 index 000000000..20f9ddb2f --- /dev/null +++ b/khtml/html/html_documentimpl.h @@ -0,0 +1,95 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef HTML_DOCUMENTIMPL_H +#define HTML_DOCUMENTIMPL_H + +#include "xml/dom_docimpl.h" +#include "html/html_miscimpl.h" + +#include <qmap.h> + +class KHTMLView; +class QString; + +namespace DOM { + + class Element; + class HTMLElement; + class HTMLElementImpl; + class DOMString; + class CSSStyleSheetImpl; + class HTMLMapElementImpl; + +class HTMLDocumentImpl : public DOM::DocumentImpl +{ + Q_OBJECT +public: + HTMLDocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v = 0); + ~HTMLDocumentImpl(); + + virtual bool isHTMLDocument() const { return true; } + + DOMString referrer() const; + DOMString lastModified() const; + DOMString cookie() const; + void setCookie( const DOMString &); + + HTMLElementImpl *body(); + void setBody(HTMLElementImpl *_body, int& exceptioncode); + + virtual khtml::Tokenizer *createTokenizer(); + + virtual bool childAllowed( NodeImpl *newChild ); + + virtual ElementImpl *createElement ( const DOMString &tagName, int* pExceptioncode ); + + HTMLMapElementImpl* getMap(const DOMString& url_); + + virtual void determineParseMode( const QString &str ); + virtual void close(); + + void setAutoFill() { m_doAutoFill = true; } + + // If true, HTML was requested by mimetype (e.g. HTTP Content-Type). Otherwise XHTML was requested. + // This is independent of the actual doctype, of course. (#86446) + void setHTMLRequested( bool html ) { m_htmlRequested = html; } + +protected: + HTMLElementImpl *htmlElement; + friend class HTMLMapElementImpl; + friend class HTMLImageElementImpl; + QMap<QString,HTMLMapElementImpl*> mapMap; + bool m_doAutoFill; + bool m_htmlRequested; + +protected slots: + /** + * Repaints, so that all links get the proper color + */ + void slotHistoryChanged(); +}; + +} //namespace + +#endif diff --git a/khtml/html/html_elementimpl.cpp b/khtml/html/html_elementimpl.cpp new file mode 100644 index 000000000..9c0e58335 --- /dev/null +++ b/khtml/html/html_elementimpl.cpp @@ -0,0 +1,678 @@ +// -*- c-basic-offset: 4; -*- +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2003 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2002 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +// ------------------------------------------------------------------------- +//#define DEBUG +//#define DEBUG_LAYOUT +//#define PAR_DEBUG +//#define EVENT_DEBUG +//#define UNSUPPORTED_ATTR + +#include "html/dtd.h" +#include "html/html_elementimpl.h" +#include "html/html_documentimpl.h" +#include "html/htmltokenizer.h" + +#include "misc/htmlhashes.h" + +#include "khtmlview.h" +#include "khtml_part.h" + +#include "rendering/render_object.h" +#include "rendering/render_replaced.h" +#include "css/css_valueimpl.h" +#include "css/css_stylesheetimpl.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "xml/dom_textimpl.h" +#include "xml/dom2_eventsimpl.h" + +#include <kdebug.h> +#include <kglobal.h> +#include "html_elementimpl.h" + +using namespace DOM; +using namespace khtml; + +HTMLElementImpl::HTMLElementImpl(DocumentImpl *doc) + : ElementImpl( doc ) +{ + m_htmlCompat = doc && doc->htmlMode() != DocumentImpl::XHtml; +} + +HTMLElementImpl::~HTMLElementImpl() +{ +} + +bool HTMLElementImpl::isInline() const +{ + if (renderer()) + return ElementImpl::isInline(); + + switch(id()) { + case ID_A: + case ID_FONT: + case ID_TT: + case ID_U: + case ID_B: + case ID_I: + case ID_S: + case ID_STRIKE: + case ID_BIG: + case ID_SMALL: + + // %phrase + case ID_EM: + case ID_STRONG: + case ID_DFN: + case ID_CODE: + case ID_SAMP: + case ID_KBD: + case ID_VAR: + case ID_CITE: + case ID_ABBR: + case ID_ACRONYM: + + // %special + case ID_SUB: + case ID_SUP: + case ID_SPAN: + case ID_NOBR: + case ID_WBR: + return true; + + default: + return ElementImpl::isInline(); + } +} + +DOMString HTMLElementImpl::namespaceURI() const +{ + return (!m_htmlCompat) ? + DOMString(XHTML_NAMESPACE) : DOMString(); +} + + +DOMString HTMLElementImpl::localName() const +{ + // We only have a localName if we were created by createElementNS(), in which + // case we are an XHTML element. This also means we have a lowercase name. + if (!m_htmlCompat) // XHTML == not HTMLCompat + { + NodeImpl::Id _id = id(); + DOMString tn; + if ( _id >= ID_LAST_TAG ) + tn = getDocument()->getName(ElementId, _id); + else // HTML tag + tn = getTagName( _id ); + return tn; // lowercase already + } + // createElement() always returns elements with a null localName. + else + return DOMString(); +} + +DOMString HTMLElementImpl::tagName() const +{ + DOMString tn; + NodeImpl::Id _id = id(); + if ( _id >= ID_LAST_TAG ) + tn = getDocument()->getName(ElementId, _id); + else // HTML tag + tn = getTagName( _id ); + + if ( m_htmlCompat ) + tn = tn.upper(); + + if (m_prefix) + return DOMString(m_prefix) + ":" + tn; + + return tn; +} + +void HTMLElementImpl::parseAttribute(AttributeImpl *attr) +{ + DOMString indexstring; + switch( attr->id() ) + { + case ATTR_ALIGN: + if (attr->val()) { + if ( strcasecmp(attr->value(), "middle" ) == 0 ) + addCSSProperty( CSS_PROP_TEXT_ALIGN, CSS_VAL_CENTER ); + else + addCSSProperty(CSS_PROP_TEXT_ALIGN, attr->value().lower()); + } + else + removeCSSProperty(CSS_PROP_TEXT_ALIGN); + break; +// the core attributes... + case ATTR_ID: + // unique id + setHasID(); + getDocument()->incDOMTreeVersion(); + break; + case ATTR_CLASS: + if (attr->val()) { + DOMString v = attr->value(); + const QChar* s = v.unicode(); + int l = v.length(); + while( l && !s->isSpace() ) + l--,s++; + setHasClassList(l); + setHasClass(true); + } else { + setHasClassList(false); + setHasClass(false); + } + break; + case ATTR_NAME: + getDocument()->incDOMTreeVersion(); + break; + case ATTR_STYLE: + if (m_styleDecls) + m_styleDecls->removeCSSHints(); + else + createDecl(); + m_styleDecls->setProperty(attr->value()); + setChanged(); + break; + case ATTR_TABINDEX: + indexstring=getAttribute(ATTR_TABINDEX); + if (indexstring.length()) + setTabIndex(indexstring.toInt()); + break; +// i18n attributes + case ATTR_LANG: + break; + case ATTR_DIR: + addCSSProperty(CSS_PROP_DIRECTION, attr->value().lower()); + addCSSProperty(CSS_PROP_UNICODE_BIDI, CSS_VAL_EMBED); + break; +// standard events + case ATTR_ONCLICK: + setHTMLEventListener(EventImpl::KHTML_ECMA_CLICK_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onclick", this)); + break; + case ATTR_ONDBLCLICK: + setHTMLEventListener(EventImpl::KHTML_ECMA_DBLCLICK_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "ondblclick", this)); + break; + case ATTR_ONMOUSEDOWN: + setHTMLEventListener(EventImpl::MOUSEDOWN_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onmousedown", this)); + break; + case ATTR_ONMOUSEMOVE: + setHTMLEventListener(EventImpl::MOUSEMOVE_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onmousemove", this)); + break; + case ATTR_ONMOUSEOUT: + setHTMLEventListener(EventImpl::MOUSEOUT_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onmouseout", this)); + break; + case ATTR_ONMOUSEOVER: + setHTMLEventListener(EventImpl::MOUSEOVER_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onmouseover", this)); + break; + case ATTR_ONMOUSEUP: + setHTMLEventListener(EventImpl::MOUSEUP_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onmouseup", this)); + break; + case ATTR_ONKEYDOWN: + setHTMLEventListener(EventImpl::KEYDOWN_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onkeydown", this)); + break; + case ATTR_ONKEYPRESS: + setHTMLEventListener(EventImpl::KEYPRESS_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onkeypress", this)); + break; + case ATTR_ONKEYUP: + setHTMLEventListener(EventImpl::KEYUP_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onkeyup", this)); + break; + case ATTR_ONFOCUS: + setHTMLEventListener(EventImpl::FOCUS_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onfocus", this)); + break; + case ATTR_ONBLUR: + setHTMLEventListener(EventImpl::BLUR_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onblur", this)); + break; + case ATTR_ONSCROLL: + setHTMLEventListener(EventImpl::SCROLL_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onscroll", this)); + break; +// other misc attributes + default: +#ifdef UNSUPPORTED_ATTR + kdDebug(6030) << "UATTR: <" << this->nodeName().string() << "> [" + << attr->name().string() << "]=[" << attr->value().string() << "]" << endl; +#endif + break; + } +} + +void HTMLElementImpl::recalcStyle( StyleChange ch ) +{ + ElementImpl::recalcStyle( ch ); + + if (m_render /*&& changed*/) + m_render->updateFromElement(); +} + +void HTMLElementImpl::addCSSProperty(int id, const DOMString &value) +{ + if(!m_styleDecls) createDecl(); + m_styleDecls->setProperty(id, value, false, true); + setChanged(); +} + +void HTMLElementImpl::addCSSProperty(int id, int value) +{ + if(!m_styleDecls) createDecl(); + m_styleDecls->setProperty(id, value, false, true); + setChanged(); +} + +void HTMLElementImpl::addCSSLength(int id, const DOMString &value, bool numOnly, bool multiLength) +{ + if(!m_styleDecls) createDecl(); + + // strip attribute garbage to avoid CSS parsing errors + // ### create specialized hook that avoids parsing every + // value twice! + if ( value.implementation() ) { + // match \s*[+-]?\d*(\.\d*)?[%\*]? + unsigned i = 0, j = 0; + QChar* s = value.implementation()->s; + unsigned l = value.implementation()->l; + + while (i < l && s[i].isSpace()) + ++i; + if (i < l && (s[i] == '+' || s[i] == '-')) + ++i; + while (i < l && s[i].isDigit()) + ++i,++j; + + // no digits! + if (j == 0) return; + + int v = kClamp( QConstString(s, i).string().toInt(), -8192, 8191 ) ; + const char* suffix = "px"; + if (!numOnly || multiLength) { + // look if we find a % or * + while (i < l) { + if (multiLength && s[i] == '*') { + suffix = ""; + break; + } + if (s[i] == '%') { + suffix = "%"; + break; + } + ++i; + } + } + if (numOnly) suffix = ""; + + QString ns = QString::number(v) + suffix; + m_styleDecls->setLengthProperty( id, DOMString( ns ), false, true, multiLength ); + setChanged(); + return; + } + + m_styleDecls->setLengthProperty(id, value, false, true, multiLength); + setChanged(); +} + +static inline bool isHexDigit( const QChar &c ) { + return ( c >= '0' && c <= '9' ) || + ( c >= 'a' && c <= 'f' ) || + ( c >= 'A' && c <= 'F' ); +} + +static inline int toHex( const QChar &c ) { + return ( (c >= '0' && c <= '9') + ? (c.unicode() - '0') + : ( ( c >= 'a' && c <= 'f' ) + ? (c.unicode() - 'a' + 10) + : ( ( c >= 'A' && c <= 'F' ) + ? (c.unicode() - 'A' + 10) + : -1 ) ) ); +} + +/* color parsing that tries to match as close as possible IE 6. */ +void HTMLElementImpl::addHTMLColor( int id, const DOMString &c ) +{ + if(!m_styleDecls) createDecl(); + + // this is the only case no color gets applied in IE. + if ( !c.length() ) { + removeCSSProperty(id); + return; + } + + if ( m_styleDecls->setProperty(id, c, false, true) ) + return; + + QString color = c.string(); + // not something that fits the specs. + + // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value + // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have. + + // the length of the color value is rounded up to the next + // multiple of 3. each part of the rgb triple then gets one third + // of the length. + // + // Each triplet is parsed byte by byte, mapping + // each number to a hex value (0-9a-fA-F to their values + // everything else to 0). + // + // The highest non zero digit in all triplets is remembered, and + // used as a normalization point to normalize to values between 0 + // and 255. + + if ( color.lower() != "transparent" ) { + if ( color[0] == '#' ) + color.remove( 0, 1 ); + int basicLength = (color.length() + 2) / 3; + if ( basicLength > 1 ) { + // IE ignores colors with three digits or less +// qDebug("trying to fix up color '%s'. basicLength=%d, length=%d", +// color.latin1(), basicLength, color.length() ); + int colors[3] = { 0, 0, 0 }; + int component = 0; + int pos = 0; + int maxDigit = basicLength-1; + while ( component < 3 ) { + // search forward for digits in the string + int numDigits = 0; + while ( pos < (int)color.length() && numDigits < basicLength ) { + int hex = toHex( color[pos] ); + colors[component] = (colors[component] << 4); + if ( hex > 0 ) { + colors[component] += hex; + maxDigit = kMin( maxDigit, numDigits ); + } + numDigits++; + pos++; + } + while ( numDigits++ < basicLength ) + colors[component] <<= 4; + component++; + } + maxDigit = basicLength - maxDigit; +// qDebug("color is %x %x %x, maxDigit=%d", colors[0], colors[1], colors[2], maxDigit ); + + // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits + maxDigit -= 2; + colors[0] >>= 4*maxDigit; + colors[1] >>= 4*maxDigit; + colors[2] >>= 4*maxDigit; +// qDebug("normalized color is %x %x %x", colors[0], colors[1], colors[2] ); + // assert( colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100 ); + + color.sprintf("#%02x%02x%02x", colors[0], colors[1], colors[2] ); +// qDebug( "trying to add fixed color string '%s'", color.latin1() ); + if ( m_styleDecls->setProperty(id, DOMString(color), false, true) ) + return; + } + } + m_styleDecls->setProperty(id, CSS_VAL_BLACK, false, true); +} + +void HTMLElementImpl::removeCSSProperty(int id) +{ + if(!m_styleDecls) + return; + m_styleDecls->setParent(getDocument()->elementSheet()); + m_styleDecls->removeProperty(id, true /*nonCSSHint */); + setChanged(); +} + +DOMString HTMLElementImpl::innerHTML() const +{ + QString result; //Use QString to accumulate since DOMString is poor for appends + for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) { + DOMString kid = child->toString(); + result += QConstString(kid.unicode(), kid.length()).string(); + } + return result; +} + +DOMString HTMLElementImpl::innerText() const +{ + QString text = ""; + if(!firstChild()) + return text; + + const NodeImpl *n = this; + // find the next text/image after the anchor, to get a position + while(n) { + if(n->firstChild()) + n = n->firstChild(); + else if(n->nextSibling()) + n = n->nextSibling(); + else { + NodeImpl *next = 0; + while(!next) { + n = n->parentNode(); + if(!n || n == (NodeImpl *)this ) goto end; + next = n->nextSibling(); + } + n = next; + } + if(n->isTextNode() ) { + DOMStringImpl* data = static_cast<const TextImpl *>(n)->string(); + text += QConstString(data->s, data->l).string(); + } + } + end: + return text; +} + +DocumentFragment HTMLElementImpl::createContextualFragment( const DOMString &html ) +{ + // the following is in accordance with the definition as used by IE + if( endTag[id()] == FORBIDDEN ) + return DocumentFragment(); + // IE disallows innerHTML on inline elements. + // I don't see why we should have this restriction, as our + // dhtml engine can cope with it. Lars + //if ( isInline() ) return false; + switch( id() ) { + case ID_COL: + case ID_COLGROUP: + case ID_FRAMESET: + case ID_HEAD: + case ID_TABLE: + case ID_TBODY: + case ID_TFOOT: + case ID_THEAD: + case ID_TITLE: + return DocumentFragment(); + default: + break; + } + if ( !getDocument()->isHTMLDocument() ) + return DocumentFragment(); + + DocumentFragmentImpl* fragment = new DocumentFragmentImpl( docPtr() ); + DocumentFragment f( fragment ); + { + HTMLTokenizer tok( docPtr(), fragment ); + tok.begin(); + tok.write( html.string(), true ); + tok.end(); + } + + // Exceptions are ignored because none ought to happen here. + int ignoredExceptionCode; + + // we need to pop <html> and <body> elements and remove <head> to + // accomadate folks passing complete HTML documents to make the + // child of an element. + for ( NodeImpl* node = fragment->firstChild(); node; ) { + if (node->id() == ID_HTML || node->id() == ID_BODY) { + NodeImpl* firstChild = node->firstChild(); + NodeImpl* child = firstChild; + while ( child ) { + NodeImpl *nextChild = child->nextSibling(); + fragment->insertBefore(child, node, ignoredExceptionCode); + child = nextChild; + } + if ( !firstChild ) { + NodeImpl *nextNode = node->nextSibling(); + fragment->removeChild(node, ignoredExceptionCode); + node = nextNode; + } else { + fragment->removeChild(node, ignoredExceptionCode); + node = firstChild; + } + } else if (node->id() == ID_HEAD) { + NodeImpl *nextNode = node->nextSibling(); + fragment->removeChild(node, ignoredExceptionCode); + node = nextNode; + } else { + node = node->nextSibling(); + } + } + + return f; +} + +void HTMLElementImpl::setInnerHTML( const DOMString &html, int &exceptioncode ) +{ + DocumentFragment fragment = createContextualFragment( html ); + if ( fragment.isNull() ) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // Make sure adding the new child is ok, before removing all children (#96187) + checkAddChild( fragment.handle(), exceptioncode ); + if ( exceptioncode ) + return; + + removeChildren(); + appendChild( fragment.handle(), exceptioncode ); +} + +void HTMLElementImpl::setInnerText( const DOMString &text, int& exceptioncode ) +{ + // following the IE specs. + if( endTag[id()] == FORBIDDEN ) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + // IE disallows innerHTML on inline elements. I don't see why we should have this restriction, as our + // dhtml engine can cope with it. Lars + //if ( isInline() ) return false; + switch( id() ) { + case ID_COL: + case ID_COLGROUP: + case ID_FRAMESET: + case ID_HEAD: + case ID_HTML: + case ID_TABLE: + case ID_TBODY: + case ID_TFOOT: + case ID_THEAD: + case ID_TR: + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + default: + break; + } + + removeChildren(); + + TextImpl *t = new TextImpl( docPtr(), text.implementation() ); + appendChild( t, exceptioncode ); +} + +void HTMLElementImpl::addHTMLAlignment( DOMString alignment ) +{ + //qDebug("alignment is %s", alignment.string().latin1() ); + // vertical alignment with respect to the current baseline of the text + // right or left means floating images + int propfloat = -1; + int propvalign = -1; + if ( strcasecmp( alignment, "absmiddle" ) == 0 ) { + propvalign = CSS_VAL_MIDDLE; + } else if ( strcasecmp( alignment, "absbottom" ) == 0 ) { + propvalign = CSS_VAL_BOTTOM; + } else if ( strcasecmp( alignment, "left" ) == 0 ) { + propfloat = CSS_VAL_LEFT; + propvalign = CSS_VAL_TOP; + } else if ( strcasecmp( alignment, "right" ) == 0 ) { + propfloat = CSS_VAL_RIGHT; + propvalign = CSS_VAL_TOP; + } else if ( strcasecmp( alignment, "top" ) == 0 ) { + propvalign = CSS_VAL_TOP; + } else if ( strcasecmp( alignment, "middle" ) == 0 ) { + propvalign = CSS_VAL__KHTML_BASELINE_MIDDLE; + } else if ( strcasecmp( alignment, "center" ) == 0 ) { + propvalign = CSS_VAL_MIDDLE; + } else if ( strcasecmp( alignment, "bottom" ) == 0 ) { + propvalign = CSS_VAL_BASELINE; + } else if ( strcasecmp ( alignment, "texttop") == 0 ) { + propvalign = CSS_VAL_TEXT_TOP; + } + + if ( propfloat != -1 ) + addCSSProperty( CSS_PROP_FLOAT, propfloat ); + if ( propvalign != -1 ) + addCSSProperty( CSS_PROP_VERTICAL_ALIGN, propvalign ); +} + +DOMString HTMLElementImpl::toString() const +{ + if (!hasChildNodes()) { + DOMString result = openTagStartToString(); + result += ">"; + + if (endTag[id()] == REQUIRED) { + result += "</"; + result += tagName(); + result += ">"; + } + + return result; + } + + return ElementImpl::toString(); +} + +// ------------------------------------------------------------------------- +HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentImpl *doc, ushort i) + : HTMLElementImpl(doc) +{ + _id = i; +} + +HTMLGenericElementImpl::~HTMLGenericElementImpl() +{ +} diff --git a/khtml/html/html_elementimpl.h b/khtml/html/html_elementimpl.h new file mode 100644 index 000000000..f71ae9b23 --- /dev/null +++ b/khtml/html/html_elementimpl.h @@ -0,0 +1,89 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_ELEMENTIMPL_H +#define HTML_ELEMENTIMPL_H + +#include "xml/dom_elementimpl.h" + +namespace DOM { + +class DOMString; +class CSSStyleDeclarationImpl; +class HTMLFormElementImpl; +class DocumentFragment; + +class HTMLElementImpl : public ElementImpl +{ +public: + HTMLElementImpl(DocumentImpl *doc); + + virtual ~HTMLElementImpl(); + + virtual bool isHTMLElement() const { return true; } + + virtual bool isInline() const; + + virtual Id id() const = 0; + virtual DOMString tagName() const; + virtual DOMString localName() const; + virtual DOMString namespaceURI() const; + + virtual void parseAttribute(AttributeImpl *token); + + void addCSSLength(int id, const DOMString &value, bool numOnly = false, bool multiLength = false); + void addCSSProperty(int id, const DOMString &value); + void addCSSProperty(int id, int value); + void addHTMLColor( int id, const DOMString &c ); + void removeCSSProperty(int id); + + virtual void recalcStyle( StyleChange ); + + DOMString innerHTML() const; + DOMString innerText() const; + DocumentFragment createContextualFragment( const DOMString &html ); + void setInnerHTML( const DOMString &html, int& exceptioncode ); + void setInnerText( const DOMString &text, int& exceptioncode ); + + virtual DOMString toString() const; + +protected: + // for IMG, OBJECT and APPLET + void addHTMLAlignment( DOMString alignment ); +}; + +class HTMLGenericElementImpl : public HTMLElementImpl +{ +public: + HTMLGenericElementImpl(DocumentImpl *doc, ushort i); + + virtual ~HTMLGenericElementImpl(); + + virtual Id id() const { return _id; }; + +protected: + ushort _id; +}; + +} //namespace + +#endif diff --git a/khtml/html/html_formimpl.cpp b/khtml/html/html_formimpl.cpp new file mode 100644 index 000000000..e59098499 --- /dev/null +++ b/khtml/html/html_formimpl.cpp @@ -0,0 +1,2984 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#undef FORMS_DEBUG +//#define FORMS_DEBUG + +#include "html/html_formimpl.h" + +#include "khtmlview.h" +#include "khtml_part.h" +#include "html/html_documentimpl.h" +#include "khtml_settings.h" +#include "misc/htmlhashes.h" + +#include "css/cssstyleselector.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "css/csshelper.h" +#include "xml/dom_textimpl.h" +#include "xml/dom_docimpl.h" +#include "xml/dom2_eventsimpl.h" +#include "xml/dom_restyler.h" +#include "khtml_ext.h" + +#include "rendering/render_form.h" + +#include <kcharsets.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kmimetype.h> +#include <kmessagebox.h> +#include <kapplication.h> +#include <klocale.h> +#ifndef KHTML_NO_WALLET +#include <kwallet.h> +#endif +#include <netaccess.h> +#include <kfileitem.h> +#include <qfile.h> +#include <qdir.h> +#include <qtextcodec.h> + +// for keygen +#include <qstring.h> +#include <ksslkeygen.h> + +#include <assert.h> + + +using namespace DOM; +using namespace khtml; + +HTMLFormElementImpl::HTMLFormElementImpl(DocumentImpl *doc, bool implicit) + : HTMLElementImpl(doc) +{ + m_implicit = implicit; + m_post = false; + m_multipart = false; + m_autocomplete = true; + m_insubmit = false; + m_doingsubmit = false; + m_inreset = false; + m_enctype = "application/x-www-form-urlencoded"; + m_boundary = "----------" + KApplication::randomString( 42 + 13 ); + m_acceptcharset = "UNKNOWN"; + m_malformed = false; +} + +HTMLFormElementImpl::~HTMLFormElementImpl() +{ + if (getDocument() && getDocument()->view() && getDocument()->view()->part()) { + getDocument()->view()->part()->dequeueWallet(this); + } + QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); + for (; it.current(); ++it) + it.current()->m_form = 0; + QPtrListIterator<HTMLImageElementImpl> it2(imgElements); + for (; it2.current(); ++it2) + it2.current()->m_form = 0; +} + +NodeImpl::Id HTMLFormElementImpl::id() const +{ + return ID_FORM; +} + +long HTMLFormElementImpl::length() const +{ + int len = 0; + QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); + for (; it.current(); ++it) + if (it.current()->isEnumeratable()) + ++len; + + return len; +} + +static QCString encodeCString(const QCString& e) +{ + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 + // safe characters like NS handles them for compatibility + static const char *safe = "-._*"; + QCString encoded(( e.length()+e.contains( '\n' ) )*3 + +e.contains('\r') * 3 + 1); + int enclen = 0; + bool crmissing = false; + unsigned char oldc; + unsigned char c ='\0'; + + //QCString orig(e.data(), e.size()); + + unsigned len = e.length(); + for(unsigned pos = 0; pos < len; pos++) { + oldc = c; + c = e[pos]; + + if (crmissing && c != '\n') { + encoded[enclen++] = '%'; + encoded[enclen++] = '0'; + encoded[enclen++] = 'D'; + crmissing = false; + } + + if ( (( c >= 'A') && ( c <= 'Z')) || + (( c >= 'a') && ( c <= 'z')) || + (( c >= '0') && ( c <= '9')) || + (strchr(safe, c)) + ) + encoded[enclen++] = c; + else if ( c == ' ' ) + encoded[enclen++] = '+'; + else if ( c == '\n' ) + { + encoded[enclen++] = '%'; + encoded[enclen++] = '0'; + encoded[enclen++] = 'D'; + encoded[enclen++] = '%'; + encoded[enclen++] = '0'; + encoded[enclen++] = 'A'; + crmissing = false; + } + else if (c == '\r' && oldc != '\n') { + crmissing = true; + } + else if ( c != '\r' ) + { + encoded[enclen++] = '%'; + unsigned int h = c / 16; + h += (h > 9) ? ('A' - 10) : '0'; + encoded[enclen++] = h; + + unsigned int l = c % 16; + l += (l > 9) ? ('A' - 10) : '0'; + encoded[enclen++] = l; + } + } + encoded[enclen++] = '\0'; + encoded.truncate(enclen); + + return encoded; +} + +// ### This function only encodes to numeric ampersand escapes, +// ### we could use standard ampersand values as well. +inline static QString escapeUnencodeable(const QTextCodec* codec, const QString& s) { + QString enc_string; + const int len = s.length(); + for(int i=0; i <len; ++i) { + const QChar c = s[i]; + if (codec->canEncode(c)) + enc_string.append(c); + else { + QString ampersandEscape; + ampersandEscape.sprintf("&#%u;", c.unicode()); + enc_string.append(ampersandEscape); + } + } + return enc_string; +} + +inline static QCString fixUpfromUnicode(const QTextCodec* codec, const QString& s) +{ + QCString str = codec->fromUnicode(escapeUnencodeable(codec,s)); + str.truncate(str.length()); + return str; +} + +QByteArray HTMLFormElementImpl::formData(bool& ok) +{ +#ifdef FORMS_DEBUG + kdDebug( 6030 ) << "form: formData()" << endl; +#endif + + QByteArray form_data(0); + QCString enc_string = ""; // used for non-multipart data + + // find out the QTextcodec to use + const QString str = m_acceptcharset.string(); + const QChar space(' '); + const unsigned int strLength = str.length(); + for(unsigned int i=0; i < strLength; ++i) if(str[i].latin1() == ',') str[i] = space; + const QStringList charsets = QStringList::split(' ', str); + QTextCodec* codec = 0; + KHTMLView *view = getDocument()->view(); + { + QStringList::ConstIterator it = charsets.begin(); + const QStringList::ConstIterator itEnd = charsets.end(); + + for ( ; it != itEnd; ++it ) + { + QString enc = (*it); + if(enc.contains("UNKNOWN")) + { + // use standard document encoding + enc = "ISO 8859-1"; + if(view && view->part()) + enc = view->part()->encoding(); + } + if((codec = KGlobal::charsets()->codecForName(enc.latin1()))) + break; + } + } + if(!codec) + codec = QTextCodec::codecForLocale(); + + // we need to map visual hebrew to logical hebrew, as the web + // server alsways expects responses in logical ordering + if ( codec->mibEnum() == 11 ) + codec = QTextCodec::codecForMib( 85 ); + + m_encCharset = codec->name(); + const unsigned int m_encCharsetLength = m_encCharset.length(); + for(unsigned int i=0; i < m_encCharsetLength; ++i) + m_encCharset[i] = m_encCharset[i].latin1() == ' ' ? QChar('-') : m_encCharset[i].lower(); + + QStringList fileUploads, fileNotUploads; + + for (QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); it.current(); ++it) { + HTMLGenericFormElementImpl* const current = it.current(); + khtml::encodingList lst; + + if (!current->disabled() && current->encoding(codec, lst, m_multipart)) + { + //kdDebug(6030) << "adding name '" << current->name().string() << "'" << endl; + khtml::encodingList::ConstIterator it = lst.begin(); + const khtml::encodingList::ConstIterator itEnd = lst.end(); + for( it = lst.begin(); it != itEnd; ++it ) + { + if (!m_multipart) + { + // handle ISINDEX / <input name=isindex> special + // but only if its the first entry + if ( enc_string.isEmpty() && *it == "isindex" ) { + ++it; + enc_string += encodeCString( *it ); + } + else { + if(!enc_string.isEmpty()) + enc_string += '&'; + + enc_string += encodeCString(*it); + enc_string += "="; + ++it; + enc_string += encodeCString(*it); + } + } + else + { + QCString hstr("--"); + hstr += m_boundary.latin1(); + hstr += "\r\n"; + hstr += "Content-Disposition: form-data; name=\""; + hstr += (*it).data(); + hstr += "\""; + + // if the current type is FILE, then we also need to + // include the filename + if (current->id() == ID_INPUT && + static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::FILE && + current->renderer()) + { + KURL path; + QString val = static_cast<HTMLInputElementImpl*>(current)->value().string().stripWhiteSpace(); + if (!val.isEmpty() && + QDir::isRelativePath(val) && + QFile::exists(KGlobalSettings::documentPath() + val)) { + path.setPath(KGlobalSettings::documentPath() + val); + } else { + path = KURL::fromPathOrURL(val); + } + + hstr += fixUpfromUnicode(codec, "; filename=\"" + path.fileName() + "\""); + if (path.isValid()) { + fileUploads << path.prettyURL(0, KURL::StripFileProtocol); + const KMimeType::Ptr ptr = KMimeType::findByURL(path); + if (!ptr->name().isEmpty()) { + hstr += "\r\nContent-Type: "; + hstr += ptr->name().ascii(); + } + } else if (!val.isEmpty()) { + fileNotUploads << path.prettyURL(0, KURL::StripFileProtocol); + } + } + + hstr += "\r\n\r\n"; + ++it; + + // append body + const unsigned int old_size = form_data.size(); + form_data.resize( old_size + hstr.length() + (*it).size() + 1); + memcpy(form_data.data() + old_size, hstr.data(), hstr.length()); + memcpy(form_data.data() + old_size + hstr.length(), *it, (*it).size()); + form_data[form_data.size()-2] = '\r'; + form_data[form_data.size()-1] = '\n'; + + // reset unsubmittedFormChange flag + if (current->id() == ID_INPUT && + static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::TEXT) + static_cast<HTMLInputElementImpl*>(current)->setUnsubmittedFormChange(false); + + if (current->id() == ID_TEXTAREA) + static_cast<HTMLTextAreaElementImpl*>(current)->setUnsubmittedFormChange(false); + + } + } + } + } + + if (fileNotUploads.count()) { + const int result = KMessageBox::warningContinueCancelList( 0, + i18n("The following files will not be uploaded" + " because they could not be found.\n" + "Do you want to continue?"), + fileNotUploads, + i18n("Submit Confirmation"),KGuiItem(i18n("&Submit Anyway"))); + + + if (result == KMessageBox::Cancel) { + ok = false; + return QByteArray(); + } + } + + if (fileUploads.count()) { + const int result = KMessageBox::warningContinueCancelList( 0, + i18n("You're about to transfer the following files from " + "your local computer to the Internet.\n" + "Do you really want to continue?"), + fileUploads, + i18n("Send Confirmation"),KGuiItem(i18n("&Send Files"))); + + + if (result == KMessageBox::Cancel) { + ok = false; + return QByteArray(); + } + } + + if (m_multipart) + enc_string = ("--" + m_boundary + "--\r\n").ascii(); + + const int old_size = form_data.size(); + form_data.resize( form_data.size() + enc_string.length() ); + memcpy(form_data.data() + old_size, enc_string.data(), enc_string.length() ); + + ok = true; + return form_data; +} + +void HTMLFormElementImpl::setEnctype( const DOMString& type ) +{ + if(type.string().find("multipart", 0, false) != -1 || type.string().find("form-data", 0, false) != -1) + { + m_enctype = "multipart/form-data"; + m_multipart = true; + m_post = true; + } else if (type.string().find("text", 0, false) != -1 || type.string().find("plain", 0, false) != -1) + { + m_enctype = "text/plain"; + m_multipart = false; + } + else + { + m_enctype = "application/x-www-form-urlencoded"; + m_multipart = false; + } + m_encCharset = QString::null; +} + +static QString calculateAutoFillKey(const HTMLFormElementImpl& e) +{ + KURL k(e.getDocument()->URL()); + k.setRef(QString::null); + k.setQuery(QString::null); + // ensure that we have the user / password inside the url + // otherwise we might have a potential security problem + // by saving passwords under wrong lookup key. + const QString name = e.getAttribute(ATTR_NAME).string().stripWhiteSpace(); + const QRegExp re("[;,!]"); + const QStringList url = QStringList::split(re, k.url()); + return url[0] + '#' + name; +} + +void HTMLFormElementImpl::doAutoFill() +{ +#ifndef KHTML_NO_WALLET + const QString key = calculateAutoFillKey(*this); + + if (KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), + KWallet::Wallet::FormDataFolder(), + key)) + return; + + // assert(view()) + getDocument()->view()->part()->openWallet(this); +#endif // KHTML_NO_WALLET +} + + +void HTMLFormElementImpl::walletOpened(KWallet::Wallet *w) { +#ifndef KHTML_NO_WALLET + assert(w); + const QString key = calculateAutoFillKey(*this); + if (!w->hasFolder(KWallet::Wallet::FormDataFolder())) { + return; // failed + } + w->setFolder(KWallet::Wallet::FormDataFolder()); + QMap<QString, QString> map; + if (w->readMap(key, map)) + return; // failed, abort + + for (QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); it.current(); ++it) { + if (it.current()->id() == ID_INPUT) { + HTMLInputElementImpl* const current = static_cast<HTMLInputElementImpl*>(it.current()); + if ((current->inputType() == HTMLInputElementImpl::PASSWORD || + current->inputType() == HTMLInputElementImpl::TEXT) && + !current->readOnly() && + map.contains(current->name().string())) { + getDocument()->setFocusNode(current); + current->setValue(map[current->name().string()]); + } + } + } +#endif // KHTML_NO_WALLET +} + +void HTMLFormElementImpl::submitFromKeyboard() +{ + // Activate the first nondisabled submit button + // if there is none, do a submit anyway if not more + // than one <input type=text> or <input type=password> + unsigned int inputtext = 0; + for (QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); it.current(); ++it) { + if (it.current()->id() == ID_BUTTON) { + HTMLButtonElementImpl* const current = static_cast<HTMLButtonElementImpl *>(it.current()); + if (current->buttonType() == HTMLButtonElementImpl::SUBMIT && !current->disabled()) { + current->click(); + return; + } + } else if (it.current()->id() == ID_INPUT) { + HTMLInputElementImpl* const current = static_cast<HTMLInputElementImpl *>(it.current()); + switch(current->inputType()) { + case HTMLInputElementImpl::SUBMIT: + case HTMLInputElementImpl::IMAGE: + if(!current->disabled()) { + current->click(); + return; + } + break; + case HTMLInputElementImpl::TEXT: + case HTMLInputElementImpl::PASSWORD: + ++inputtext; + default: + break; + } + } + } + + if (inputtext <= 1) + prepareSubmit(); +} + + +void HTMLFormElementImpl::gatherWalletData() +{ +#ifndef KHTML_NO_WALLET + KHTMLView* const view = getDocument()->view(); + // check if we have any password input's + m_walletMap.clear(); + m_havePassword = false; + m_haveTextarea = false; + const KURL formUrl(getDocument()->URL()); + if (view && !view->nonPasswordStorableSite(formUrl.host())) { + for (QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); it.current(); ++it) { + if (it.current()->id() == ID_INPUT) { + HTMLInputElementImpl* const c = static_cast<HTMLInputElementImpl*> (it.current()); + if ((c->inputType() == HTMLInputElementImpl::TEXT || + c->inputType() == HTMLInputElementImpl::PASSWORD) && + !c->readOnly()) { + m_walletMap.insert(c->name().string(), c->value().string()); + if (c->inputType() == HTMLInputElementImpl::PASSWORD && + !c->value().isEmpty()) + m_havePassword = true; + } + } + else if (it.current()->id() == ID_TEXTAREA) + m_haveTextarea = true; + } + } +#endif // KHTML_NO_WALLET +} + + +bool HTMLFormElementImpl::prepareSubmit() +{ + KHTMLView* const view = getDocument()->view(); + if(m_insubmit || !view || !view->part() || view->part()->onlyLocalReferences()) + return m_insubmit; + + gatherWalletData(); + + m_insubmit = true; + m_doingsubmit = false; + + if ( dispatchHTMLEvent(EventImpl::SUBMIT_EVENT,true,true) && !m_doingsubmit ) + m_doingsubmit = true; + + m_insubmit = false; + + if ( m_doingsubmit ) + submit(); + + return m_doingsubmit; +} + +void HTMLFormElementImpl::submit( ) +{ + if ( m_insubmit ) { + m_doingsubmit = true; + return; + } + + m_insubmit = true; + +#ifdef FORMS_DEBUG + kdDebug( 6030 ) << "submitting!" << endl; +#endif + + bool ok; + KHTMLView* const view = getDocument()->view(); + const QByteArray form_data = formData(ok); + const KURL formUrl(getDocument()->URL()); + + if (ok && view) { + if (m_walletMap.isEmpty()) { + gatherWalletData(); + } +#ifndef KHTML_NO_WALLET + if (m_havePassword && !m_haveTextarea && KWallet::Wallet::isEnabled()) { + const QString key = calculateAutoFillKey(*this); + const bool doesnotexist = KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::FormDataFolder(), key); + KWallet::Wallet* const w = view->part()->wallet(); + bool login_changed = false; + + if (!doesnotexist && w) { + // check if the login information changed from what + // we had so far. + if (w->hasFolder(KWallet::Wallet::FormDataFolder())) { + w->setFolder(KWallet::Wallet::FormDataFolder()); + QMap<QString, QString> map; + if (!w->readMap(key, map)) { + QMapConstIterator<QString, QString> it = map.begin(); + const QMapConstIterator<QString, QString> itEnd = map.end(); + for ( ; it != itEnd; ++it ) + if ( map[it.key()] != m_walletMap[it.key()] ) { + login_changed = true; + break; + } + } else { + login_changed = true; + } + } + } + + if ( doesnotexist || !w || login_changed ) { + // TODO use KMessageBox::questionYesNoCancel() again, if you can pass a KGuiItem for Cancel + KDialogBase* const dialog = new KDialogBase(i18n("Save Login Information"), + KDialogBase::Yes | KDialogBase::No | KDialogBase::Cancel, + KDialogBase::Yes, KDialogBase::Cancel, + 0, "questionYesNoCancel", true, true, + i18n("Store"), KGuiItem(i18n("Ne&ver for This Site")), i18n("Do Not Store")); + + bool checkboxResult = false; + const int savePassword = KMessageBox::createKMessageBox(dialog, QMessageBox::Information, + formUrl.host().isEmpty() ? // e.g. local file + i18n("Konqueror has the ability to store the password " + "in an encrypted wallet. When the wallet is unlocked, it " + "can then automatically restore the login information " + "next time you submit this form. Do you want to store " + "the information now?") : + i18n("Konqueror has the ability to store the password " + "in an encrypted wallet. When the wallet is unlocked, it " + "can then automatically restore the login information " + "next time you visit %1. Do you want to store " + "the information now?").arg(formUrl.host()), + QStringList(), QString::null, &checkboxResult, KMessageBox::Notify); + + if ( savePassword == KDialogBase::Yes ) { + // ensure that we have the user / password inside the url + // otherwise we might have a potential security problem + // by saving passwords under wrong lookup key. + + if (view->part()) { + view->part()->saveToWallet(key, m_walletMap); + } + } else if ( savePassword == KDialogBase::No ) { + view->addNonPasswordStorableSite(formUrl.host()); + } + } + } +#endif // KHTML_NO_WALLET + + const DOMString url(khtml::parseURL(getAttribute(ATTR_ACTION))); + if(m_post) { + view->part()->submitForm( "post", url.string(), form_data, + m_target.string(), + enctype().string(), + m_boundary ); + } + else { + view->part()->submitForm( "get", url.string(), form_data, + m_target.string() ); + } + } + + m_walletMap.clear(); // done with it + m_havePassword = m_haveTextarea= false; + m_doingsubmit = m_insubmit = false; +} + +void HTMLFormElementImpl::reset( ) +{ + KHTMLView* const view = getDocument()->view(); + if(m_inreset || !view || !view->part()) return; + + m_inreset = true; + +#ifdef FORMS_DEBUG + kdDebug( 6030 ) << "reset pressed!" << endl; +#endif + + // ### DOM2 labels this event as not cancelable, however + // common browsers( sick! ) allow it be cancelled. + if ( !dispatchHTMLEvent(EventImpl::RESET_EVENT,true, true) ) { + m_inreset = false; + return; + } + + for (QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); it.current(); ++it) + it.current()->reset(); + + m_inreset = false; +} + +void HTMLFormElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_ACTION: + break; + case ATTR_TARGET: + m_target = attr->value(); + break; + case ATTR_METHOD: + m_post = ( strcasecmp( attr->value(), "post" ) == 0 ); + break; + case ATTR_ENCTYPE: + setEnctype( attr->value() ); + break; + case ATTR_ACCEPT_CHARSET: + // space separated list of charsets the server + // accepts - see rfc2045 + m_acceptcharset = attr->value(); + break; + case ATTR_ACCEPT: + // ignore this one for the moment... + break; + case ATTR_AUTOCOMPLETE: + m_autocomplete = strcasecmp( attr->value(), "off" ); + break; + case ATTR_ONSUBMIT: + setHTMLEventListener(EventImpl::SUBMIT_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onsubmit", this)); + break; + case ATTR_ONRESET: + setHTMLEventListener(EventImpl::RESET_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onreset", this)); + break; + case ATTR_NAME: + if (inDocument() && m_name != attr->value()) { + getDocument()->underDocNamedCache().remove(m_name.string(), this); + getDocument()->underDocNamedCache().add (attr->value().string(), this); + } + m_name = attr->value(); + //Fallthrough intentional + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLFormElementImpl::removedFromDocument() +{ + getDocument()->underDocNamedCache().remove(m_name.string(), this); + HTMLElementImpl::removedFromDocument(); +} + +void HTMLFormElementImpl::insertedIntoDocument() +{ + getDocument()->underDocNamedCache().add(m_name.string(), this); + HTMLElementImpl::insertedIntoDocument(); +} + +void HTMLFormElementImpl::removeId(const QString& id) +{ + getDocument()->underDocNamedCache().remove(id, this); + HTMLElementImpl::removeId(id); +} + +void HTMLFormElementImpl::addId (const QString& id) +{ + getDocument()->underDocNamedCache().add(id, this); + HTMLElementImpl::addId(id); +} + + +void HTMLFormElementImpl::radioClicked( HTMLGenericFormElementImpl *caller ) +{ + for (QPtrListIterator<HTMLGenericFormElementImpl> it(formElements); it.current(); ++it) { + HTMLGenericFormElementImpl* const current = it.current(); + if (current->id() == ID_INPUT && + static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::RADIO && + current != caller && current->form() == caller->form() && current->name() == caller->name()) + static_cast<HTMLInputElementImpl*>(current)->setChecked(false); + } +} + +void HTMLFormElementImpl::registerFormElement(HTMLGenericFormElementImpl *e) +{ + formElements.append(e); +} + +void HTMLFormElementImpl::removeFormElement(HTMLGenericFormElementImpl *e) +{ + formElements.remove(e); +} + +void HTMLFormElementImpl::registerImgElement(HTMLImageElementImpl *e) +{ + imgElements.append(e); +} + +void HTMLFormElementImpl::removeImgElement(HTMLImageElementImpl *e) +{ + imgElements.remove(e); +} + +// ------------------------------------------------------------------------- + +HTMLGenericFormElementImpl::HTMLGenericFormElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLElementImpl(doc) +{ + m_disabled = m_readOnly = false; + m_name = 0; + + if (f) + m_form = f; + else + m_form = getForm(); + if (m_form) + m_form->registerFormElement(this); +} + +void HTMLGenericFormElementImpl::insertedIntoDocument() +{ + HTMLElementImpl::insertedIntoDocument(); + + if (!m_form) { + HTMLFormElementImpl* const newform = getForm(); + if (newform) { + m_form = newform; + m_form->registerFormElement(this); + } + } +} + +void HTMLGenericFormElementImpl::removedFromDocument() +{ + HTMLElementImpl::removedFromDocument(); + + if (m_form) + m_form->removeFormElement(this); + + m_form = 0; +} + +HTMLGenericFormElementImpl::~HTMLGenericFormElementImpl() +{ + if (m_form) + m_form->removeFormElement(this); + if (m_name) m_name->deref(); +} + +void HTMLGenericFormElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_DISABLED: + setDisabled( attr->val() != 0 ); + break; + case ATTR_READONLY: + { + const bool m_oldreadOnly = m_readOnly; + m_readOnly = attr->val() != 0; + if (m_oldreadOnly != m_readOnly) setChanged(); + break; + } + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLGenericFormElementImpl::attach() +{ + assert(!attached()); + + if (m_render) { + assert(m_render->style()); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + } + + // FIXME: This handles the case of a new form element being created by + // JavaScript and inserted inside a form. What it does not handle is + // a form element being moved from inside a form to outside, or from one + // inside one form to another. The reason this other case is hard to fix + // is that during parsing, we may have been passed a form that we are not + // inside, DOM-tree-wise. If so, it's hard for us to know when we should + // be removed from that form's element list. + if (!m_form) { + m_form = getForm(); + if (m_form) + m_form->registerFormElement(this); + } + + NodeBaseImpl::attach(); + + // The call to updateFromElement() needs to go after the call through + // to the base class's attach() because that can sometimes do a close + // on the renderer. + if (m_render) + m_render->updateFromElement(); + +} + +HTMLFormElementImpl *HTMLGenericFormElementImpl::getForm() const +{ + NodeImpl *p = parentNode(); + while(p) + { + if( p->id() == ID_FORM ) + return static_cast<HTMLFormElementImpl *>(p); + if( p->parentNode() && p->parentNode()->id() == ID_TABLE && p->previousSibling() ) + { + p = p->previousSibling(); + continue; + } + p = p->parentNode(); + } +#ifdef FORMS_DEBUG + kdDebug( 6030 ) << "couldn't find form!" << endl; + kdDebug( 6030 ) << kdBacktrace() << endl; +#endif + return 0; +} + +DOMString HTMLGenericFormElementImpl::name() const +{ + if (m_name) return m_name; + +// ### +// DOMString n = getDocument()->htmlMode() != DocumentImpl::XHtml ? +// getAttribute(ATTR_NAME) : getAttribute(ATTR_ID); + const DOMString n = getAttribute(ATTR_NAME); + if (n.isNull()) + return new DOMStringImpl(""); + + return n; +} + +void HTMLGenericFormElementImpl::setName(const DOMString& name) +{ + if (m_name) m_name->deref(); + m_name = name.implementation(); + setAttribute( ATTR_NAME, name ); + if (m_name) m_name->ref(); +} + +void HTMLGenericFormElementImpl::onSelect() +{ + // ### make this work with new form events architecture + dispatchHTMLEvent(EventImpl::SELECT_EVENT,true,false); +} + +void HTMLGenericFormElementImpl::onChange() +{ + // ### make this work with new form events architecture + dispatchHTMLEvent(EventImpl::CHANGE_EVENT,true,false); +} + +void HTMLGenericFormElementImpl::setDisabled( bool _disabled ) +{ + if ( m_disabled != _disabled ) { + m_disabled = _disabled; + // Trigger dynamic restyles + getDocument()->dynamicDomRestyler().restyleDepedent(this, OtherStateDependency); + // We need to update rendering under all circumstances + if (!changed() && m_render) { + m_render->updateFromElement(); + } + } +} + +bool HTMLGenericFormElementImpl::isFocusable() const +{ + if (disabled()) + return false; + + //Non-widget INPUT TYPE="image" and <BUTTON> support focus, too. + if (id() == ID_INPUT && static_cast<const HTMLInputElementImpl *>(this)->inputType() == HTMLInputElementImpl::IMAGE) + return true; + + if (id() == ID_BUTTON) + return true; + + if (!m_render || !m_render->isWidget()) + return false; + + QWidget* widget = static_cast<RenderWidget*>(m_render)->widget(); + return widget && widget->focusPolicy() >= QWidget::TabFocus; +} + +class FocusHandleWidget : public QWidget +{ +public: + void focusNextPrev(bool n) { + if (!focusNextPrevChild(n) && inherits("QTextEdit")) + QWidget::focusNextPrevChild(n); + } +}; + +void HTMLGenericFormElementImpl::defaultEventHandler(EventImpl *evt) +{ + if (evt->target() == this && renderer() && renderer()->isWidget()) { + switch(evt->id()) { + case EventImpl::MOUSEDOWN_EVENT: + case EventImpl::MOUSEUP_EVENT: + case EventImpl::MOUSEMOVE_EVENT: + case EventImpl::MOUSEOUT_EVENT: + case EventImpl::MOUSEOVER_EVENT: + case EventImpl::KEYDOWN_EVENT: + case EventImpl::KEYUP_EVENT: + case EventImpl::KEYPRESS_EVENT: + if (static_cast<RenderWidget*>(renderer())->handleEvent(*evt)) + evt->setDefaultHandled(); + default: + break; + } + } + + if (evt->target()==this && !m_disabled) + { + // Report focus in/out changes to the browser extension (editable widgets only) + KHTMLView* const view = getDocument()->view(); + if (view && evt->id() == EventImpl::DOMFOCUSIN_EVENT && isEditable() && m_render && m_render->isWidget()) { + KHTMLPartBrowserExtension *ext = static_cast<KHTMLPartBrowserExtension *>(view->part()->browserExtension()); + QWidget* const widget = static_cast<RenderWidget*>(m_render)->widget(); + if (ext) + ext->editableWidgetFocused(widget); + } + if (evt->id()==EventImpl::MOUSEDOWN_EVENT || evt->id()==EventImpl::KEYDOWN_EVENT) + { + setActive(); + } + else if (evt->id() == EventImpl::MOUSEUP_EVENT || evt->id()==EventImpl::KEYUP_EVENT) + { + if (m_active) + { + setActive(false); + setFocus(); + } + else { + setActive(false); + } + } + + if (!evt->defaultHandled() && m_render && m_render->isWidget()) { + // handle tabbing out, either from a single or repeated key event. + if ( evt->id() == EventImpl::KEYPRESS_EVENT && evt->isKeyRelatedEvent() ) { + QKeyEvent* const k = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent(); + if ( k && (k->key() == Qt::Key_Tab || k->key() == Qt::Key_BackTab) ) { + QWidget* const widget = static_cast<RenderWidget*>(m_render)->widget(); + QFocusEvent::setReason( k->key() == Qt::Key_Tab ? QFocusEvent::Tab : QFocusEvent::Backtab ); + if (widget) + static_cast<FocusHandleWidget *>(widget) + ->focusNextPrev(k->key() == Qt::Key_Tab); + QFocusEvent::resetReason(); + evt->setDefaultHandled(); + } + } + } + + + if (view && evt->id() == EventImpl::DOMFOCUSOUT_EVENT && isEditable() && m_render && m_render->isWidget()) { + KHTMLPartBrowserExtension* const ext = static_cast<KHTMLPartBrowserExtension *>(view->part()->browserExtension()); + QWidget* const widget = static_cast<RenderWidget*>(m_render)->widget(); + if (ext) + ext->editableWidgetBlurred(widget); + + // ### Don't count popup as a valid reason for losing the focus (example: opening the options of a select + // combobox shouldn't emit onblur) + } + } + if (evt->target() == this && evt->isMouseEvent() && renderer()) + evt->setDefaultHandled(); + + HTMLElementImpl::defaultEventHandler(evt); +} + +bool HTMLGenericFormElementImpl::isEditable() +{ + return false; +} + +// ------------------------------------------------------------------------- + +HTMLButtonElementImpl::HTMLButtonElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLGenericFormElementImpl(doc, f) +{ + m_clicked = false; + m_type = SUBMIT; + m_dirty = true; + m_activeSubmit = false; +} + +HTMLButtonElementImpl::~HTMLButtonElementImpl() +{ +} + +NodeImpl::Id HTMLButtonElementImpl::id() const +{ + return ID_BUTTON; +} + +DOMString HTMLButtonElementImpl::type() const +{ + return getAttribute(ATTR_TYPE); +} + +void HTMLButtonElementImpl::blur() +{ + if(getDocument()->focusNode() == this) + getDocument()->setFocusNode(0); +} + +void HTMLButtonElementImpl::focus() +{ + getDocument()->setFocusNode(this); +} + +void HTMLButtonElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_TYPE: + if ( strcasecmp( attr->value(), "submit" ) == 0 ) + m_type = SUBMIT; + else if ( strcasecmp( attr->value(), "reset" ) == 0 ) + m_type = RESET; + else if ( strcasecmp( attr->value(), "button" ) == 0 ) + m_type = BUTTON; + break; + case ATTR_VALUE: + m_value = attr->value(); + m_currValue = m_value.string(); + break; + case ATTR_ACCESSKEY: + break; + case ATTR_ALIGN: + break; + default: + HTMLGenericFormElementImpl::parseAttribute(attr); + } +} + +void HTMLButtonElementImpl::defaultEventHandler(EventImpl *evt) +{ + if (m_type != BUTTON && !m_disabled) { + bool act = (evt->id() == EventImpl::DOMACTIVATE_EVENT); + if (!act && evt->id()==EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) { + QKeyEvent* const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent(); + if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space)) + act = true; + } + if (act) + activate(); + } + HTMLGenericFormElementImpl::defaultEventHandler(evt); +} + +void HTMLButtonElementImpl::activate() +{ + m_clicked = true; + + if(m_form && m_type == SUBMIT) { + m_activeSubmit = true; + m_form->prepareSubmit(); + m_activeSubmit = false; // in case we were canceled + } + if(m_form && m_type == RESET) + m_form->reset(); +} + +void HTMLButtonElementImpl::click() +{ + QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0,0),Qt::LeftButton, 0); + dispatchMouseEvent(&me,EventImpl::CLICK_EVENT, 1); +} + +bool HTMLButtonElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoding, bool /*multipart*/) +{ + if (m_type != SUBMIT || name().isEmpty() || !m_activeSubmit) + return false; + + encoding += fixUpfromUnicode(codec, name().string()); + const QString enc_str = m_currValue.isNull() ? QString("") : m_currValue; + encoding += fixUpfromUnicode(codec, enc_str); + + return true; +} + +void HTMLButtonElementImpl::attach() +{ + // skip the generic handler + HTMLElementImpl::attach(); +} + +// ------------------------------------------------------------------------- + +HTMLFieldSetElementImpl::HTMLFieldSetElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLGenericFormElementImpl(doc, f) +{ +} + +HTMLFieldSetElementImpl::~HTMLFieldSetElementImpl() +{ +} + +NodeImpl::Id HTMLFieldSetElementImpl::id() const +{ + return ID_FIELDSET; +} + +void HTMLFieldSetElementImpl::attach() +{ + assert(!attached()); + assert(!m_render); + assert(parentNode()); + + RenderStyle* const _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + if (parentNode()->renderer() && _style->display() != NONE) { + m_render = new (getDocument()->renderArena()) RenderFieldset(this); + m_render->setStyle(_style); + } + HTMLGenericFormElementImpl::attach(); + _style->deref(); +} + +void HTMLFieldSetElementImpl::parseAttribute(AttributeImpl *attr) +{ + HTMLElementImpl::parseAttribute(attr); +} + +// ------------------------------------------------------------------------- + +HTMLInputElementImpl::HTMLInputElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLGenericFormElementImpl(doc, f) +{ + m_type = TEXT; + m_maxLen = -1; + m_size = 20; + m_clicked = false; + m_checked = false; + m_defaultChecked = false; + m_useDefaultChecked = true; + m_indeterminate = false; + + m_haveType = false; + m_activeSubmit = false; + m_autocomplete = true; + m_inited = false; + m_unsubmittedFormChange = false; + + xPos = 0; + yPos = 0; + + if ( m_form ) + m_autocomplete = f->autoComplete(); +} + +HTMLInputElementImpl::~HTMLInputElementImpl() +{ + if (getDocument()) getDocument()->deregisterMaintainsState(this); +} + +NodeImpl::Id HTMLInputElementImpl::id() const +{ + return ID_INPUT; +} + +// Called from JS. Can't merge with parseType since we +// also need to actually set ATTR_TYPE, which can't be done there. +void HTMLInputElementImpl::setType(const DOMString& t) +{ + setAttribute(ATTR_TYPE, t); +} + +void HTMLInputElementImpl::parseType(const DOMString& t) +{ + typeEnum newType; + + if ( strcasecmp( t, "password" ) == 0 ) + newType = PASSWORD; + else if ( strcasecmp( t, "checkbox" ) == 0 ) + newType = CHECKBOX; + else if ( strcasecmp( t, "radio" ) == 0 ) + newType = RADIO; + else if ( strcasecmp( t, "submit" ) == 0 ) + newType = SUBMIT; + else if ( strcasecmp( t, "reset" ) == 0 ) + newType = RESET; + else if ( strcasecmp( t, "file" ) == 0 ) + newType = FILE; + else if ( strcasecmp( t, "hidden" ) == 0 ) + newType = HIDDEN; + else if ( strcasecmp( t, "image" ) == 0 ) + newType = IMAGE; + else if ( strcasecmp( t, "button" ) == 0 ) + newType = BUTTON; + else if ( strcasecmp( t, "khtml_isindex" ) == 0 ) + newType = ISINDEX; + else + newType = TEXT; + + // ### IMPORTANT: Don't allow the type to be changed to FILE after the first + // type change, otherwise a JavaScript programmer would be able to set a text + // field's value to something like /etc/passwd and then change it to a file field. + if (m_type != newType) { + if (newType == FILE && m_haveType) { + // Set the attribute back to the old value. + // Note that this calls parseAttribute again. + setAttribute(ATTR_TYPE, type()); + } else { + m_type = newType; + + // force reattach if need be. + if (attached()) { + detach(); + attach(); + } + } + } + m_haveType = true; +} + +DOMString HTMLInputElementImpl::type() const +{ + // needs to be lowercase according to DOM spec + switch (m_type) { + case TEXT: return "text"; + case PASSWORD: return "password"; + case CHECKBOX: return "checkbox"; + case RADIO: return "radio"; + case SUBMIT: return "submit"; + case RESET: return "reset"; + case FILE: return "file"; + case HIDDEN: return "hidden"; + case IMAGE: return "image"; + case BUTTON: return "button"; + default: return ""; + } +} + +QString HTMLInputElementImpl::state( ) +{ + switch (m_type) { + case PASSWORD: + return QString::fromLatin1("."); // empty string, avoid restoring + case CHECKBOX: + case RADIO: + return QString::fromLatin1(checked() ? "on" : "off"); + case TEXT: + if (autoComplete() && value() != getAttribute(ATTR_VALUE) && getDocument()->view()) + getDocument()->view()->addFormCompletionItem(name().string(), value().string()); + /* nobreak */ + default: + return value().string() + (m_unsubmittedFormChange ? 'M' : '.'); + } +} + +void HTMLInputElementImpl::restoreState(const QString &state) +{ + switch (m_type) { + case CHECKBOX: + case RADIO: + setChecked((state == QString::fromLatin1("on"))); + break; + case FILE: + m_value = DOMString(state.left(state.length()-1)); + setChanged(); + break; + default: + setValue(DOMString(state.left(state.length()-1))); + m_unsubmittedFormChange = state.endsWith("M"); + break; + } +} + +void HTMLInputElementImpl::select( ) +{ + if(!m_render) return; + + if (m_type == TEXT || m_type == PASSWORD) + static_cast<RenderLineEdit*>(m_render)->select(); + else if (m_type == FILE) + static_cast<RenderFileButton*>(m_render)->select(); +} + +void HTMLInputElementImpl::click() +{ + QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0,0),Qt::LeftButton, 0); + dispatchMouseEvent(&me,0, 1); + dispatchMouseEvent(&me,EventImpl::CLICK_EVENT, 1); +} + +void HTMLInputElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_AUTOCOMPLETE: + m_autocomplete = strcasecmp( attr->value(), "off" ); + break; + case ATTR_TYPE: + parseType(attr->value()); + break; + case ATTR_VALUE: + if (m_value.isNull()) {// We only need to setChanged if the form is looking + setChanged(); // at the default value right now. + if (m_type == TEXT && m_render) + m_render->updateFromElement(); + } + break; + case ATTR_CHECKED: + m_defaultChecked = attr->val(); + if (m_useDefaultChecked) // We only need to setChanged if the form is looking + setChanged(); // at the default checked state right now. + break; + case ATTR_MAXLENGTH: + { + m_maxLen = -1; + if (!attr->val()) break; + bool ok; + const int ml = attr->val()->toInt(&ok); + if (ml > 0 && ml < 32767) + m_maxLen = ml; + else if (ok && ml <= 0) + m_maxLen = 0; + setChanged(); + } + break; + case ATTR_SIZE: + m_size = attr->val() ? attr->val()->toInt() : 20; + break; + case ATTR_ALT: + case ATTR_SRC: + if (m_type == IMAGE) + setChanged(); + break; + case ATTR_USEMAP: + // ### ignore for the moment + break; + case ATTR_ALIGN: + if ( m_inited && m_type == IMAGE ) + addHTMLAlignment( attr->value() ); + break; + case ATTR_ACCESSKEY: + break; + case ATTR_WIDTH: + if ( m_type == IMAGE ) + addCSSLength(CSS_PROP_WIDTH, attr->value() ); + break; + case ATTR_HEIGHT: + if ( m_type == IMAGE ) + addCSSLength(CSS_PROP_HEIGHT, attr->value() ); + break; + case ATTR_ONSELECT: + setHTMLEventListener(EventImpl::SELECT_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onselect", this)); + break; + case ATTR_ONCHANGE: + setHTMLEventListener(EventImpl::CHANGE_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onchange", this)); + break; + default: + HTMLGenericFormElementImpl::parseAttribute(attr); + } +} + +void HTMLInputElementImpl::attach() +{ + assert(!attached()); + assert(!m_render); + assert(parentNode()); + + if (!m_inited) { + // FIXME: This needs to be dynamic, doesn't it, since someone could set this + // after attachment? + if ((uint) m_type <= ISINDEX && !m_value.isEmpty()) { + const QString value = m_value.string(); + // remove newline stuff.. + QString nvalue; + unsigned int valueLength = value.length(); + for (unsigned int i = 0; i < valueLength; ++i) + if (value[i] >= ' ') + nvalue += value[i]; + m_value = nvalue; + } + m_defaultChecked = (getAttribute(ATTR_CHECKED) != 0); + if ( m_type == IMAGE ) + addHTMLAlignment( getAttribute( ATTR_ALIGN ) ); + m_inited = true; + } + + switch( m_type ) { + case PASSWORD: + if (getDocument()->isHTMLDocument()) + static_cast<HTMLDocumentImpl*>(getDocument())->setAutoFill(); + break; + case HIDDEN: + case IMAGE: + if (!getAttribute(ATTR_WIDTH).isNull()) + addCSSLength(CSS_PROP_WIDTH, getAttribute(ATTR_WIDTH)); + if (!getAttribute(ATTR_HEIGHT).isNull()) + addCSSLength(CSS_PROP_HEIGHT, getAttribute(ATTR_HEIGHT)); + default: + break; + }; + + RenderStyle* const _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + if (parentNode()->renderer() && _style->display() != NONE) { + switch(m_type) + { + case TEXT: + case PASSWORD: + case ISINDEX: m_render = new (getDocument()->renderArena()) RenderLineEdit(this); break; + case CHECKBOX: m_render = new (getDocument()->renderArena()) RenderCheckBox(this); break; + case RADIO: m_render = new (getDocument()->renderArena()) RenderRadioButton(this); break; + case SUBMIT: m_render = new (getDocument()->renderArena()) RenderSubmitButton(this); break; + case IMAGE: m_render = new (getDocument()->renderArena()) RenderImageButton(this); break; + case RESET: m_render = new (getDocument()->renderArena()) RenderResetButton(this); break; + case FILE: m_render = new (getDocument()->renderArena()) RenderFileButton(this); break; + case BUTTON: m_render = new (getDocument()->renderArena()) RenderPushButton(this); + case HIDDEN: break; + } + } + + // Let check and radio boxes start indeterminate + setIndeterminate(true); + + if (m_render) + m_render->setStyle(_style); + + HTMLGenericFormElementImpl::attach(); + _style->deref(); +} + +DOMString HTMLInputElementImpl::altText() const +{ + // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen + // also heavily discussed by Hixie on bugzilla + // note this is intentionally different to HTMLImageElementImpl::altText() + DOMString alt = getAttribute( ATTR_ALT ); + // fall back to title attribute + if ( alt.isNull() ) + alt = getAttribute( ATTR_TITLE ); + if ( alt.isNull() ) + alt = getAttribute( ATTR_VALUE ); + if ( alt.isEmpty() ) + alt = i18n( "Submit" ); + + return alt; +} + +bool HTMLInputElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoding, bool multipart) +{ + const QString nme = name().string(); + + // image generates its own name's + if (nme.isEmpty() && m_type != IMAGE) return false; + + // IMAGE needs special handling later + if(m_type != IMAGE) encoding += fixUpfromUnicode(codec, nme); + + switch (m_type) { + case CHECKBOX: + + if( checked() ) { + encoding += fixUpfromUnicode(codec, value().string()); + return true; + } + break; + + case RADIO: + + if( checked() ) { + encoding += fixUpfromUnicode(codec, value().string()); + return true; + } + break; + + case BUTTON: + case RESET: + // those buttons are never successful + return false; + + case IMAGE: + + if(m_clicked) + { + m_clicked = false; + QString astr(nme.isEmpty() ? QString::fromLatin1("x") : nme + ".x"); + + encoding += fixUpfromUnicode(codec, astr); + astr.setNum(KMAX( clickX(), 0 )); + encoding += fixUpfromUnicode(codec, astr); + astr = nme.isEmpty() ? QString::fromLatin1("y") : nme + ".y"; + encoding += fixUpfromUnicode(codec, astr); + astr.setNum(KMAX( clickY(), 0 ) ); + encoding += fixUpfromUnicode(codec, astr); + astr = value().string(); + if(astr.length() > 0) { + encoding += fixUpfromUnicode(codec, nme); + encoding += fixUpfromUnicode(codec, astr); + } + + return true; + } + break; + + case SUBMIT: + + if (m_activeSubmit) + { + QString enc_str = valueWithDefault().string(); + if(!enc_str.isEmpty()) + { + encoding += fixUpfromUnicode(codec, enc_str); + return true; + } + } + break; + + case FILE: // hmm, we have the type FILE also. bad choice here... + { + // don't submit if display: none or display: hidden + if(!renderer() || renderer()->style()->visibility() != khtml::VISIBLE) + return false; + + QString local; + KURL fileurl; + QString val = value().string(); + if (!val.isEmpty() && + QDir::isRelativePath(val) && + QFile::exists(KGlobalSettings::documentPath() + val)) { + fileurl.setPath(KGlobalSettings::documentPath() + val); + } else { + fileurl = KURL::fromPathOrURL(val); + } + + KIO::UDSEntry filestat; + + // can't submit file in www-url-form encoded + QWidget* const toplevel = static_cast<RenderSubmitButton*>(m_render)->widget()->topLevelWidget(); + if (multipart) { + QCString filearray( "" ); + if ( KIO::NetAccess::stat(fileurl, filestat, toplevel)) { + const KFileItem fileitem(filestat, fileurl, true, false); + if ( fileitem.isFile() && + KIO::NetAccess::download(fileurl, local, toplevel) ) { + QFile file(local); + filearray.resize(file.size()+1); + if ( file.open( IO_ReadOnly ) ) { + const int readbytes = file.readBlock( filearray.data(), file.size()); + if ( readbytes >= 0 ) + filearray[readbytes] = '\0'; + file.close(); + } + KIO::NetAccess::removeTempFile( local ); + } + } + encoding += filearray; + return true; + } + // else fall through + } + case HIDDEN: + case TEXT: + case PASSWORD: + // always successful + encoding += fixUpfromUnicode(codec, value().string()); + return true; + case ISINDEX: + encoding += fixUpfromUnicode(codec, value().string()); + return true; + } + return false; +} + +void HTMLInputElementImpl::reset() +{ + if (m_type == FILE) { + // set directly to bypass security check. emptying the value + // should mean no risk. + if (!m_value.isEmpty()) { + m_value = DOMString(); + setChanged(); + } + } else { + setValue(getAttribute(ATTR_VALUE)); + } + m_useDefaultChecked = true; + m_checked = m_defaultChecked; + setIndeterminate(true); +} + +void HTMLInputElementImpl::setChecked(bool _checked) +{ + if (m_form && m_type == RADIO && _checked && !name().isEmpty()) + m_form->radioClicked(this); + + if (checked() == _checked) return; + m_useDefaultChecked = false; + m_checked = _checked; + +// setIndeterminate(false); + + // Trigger dynamic restyles + getDocument()->dynamicDomRestyler().restyleDepedent(this, OtherStateDependency); + // We need to update rendering under all circumstances + if (!changed() && m_render) { + m_render->updateFromElement(); + } +} + +void HTMLInputElementImpl::setIndeterminate(bool _indeterminate) +{ + // Only checkboxes and radio-boxes honor indeterminate. + if (inputType() != CHECKBOX || inputType() != RADIO || indeterminate() == _indeterminate) + return; + + m_indeterminate = _indeterminate; + + // Trigger dynamic restyles +// getDocument()->dynamicDomRestyler().restyleDepedent(this, OtherStateDependency); + // We need to update rendering under all circumstances + if (!changed() && m_render) { + m_render->updateFromElement(); + } +} + +DOMString HTMLInputElementImpl::value() const +{ + if (m_type == CHECKBOX || m_type == RADIO) { + const DOMString val = getAttribute(ATTR_VALUE); + // If no attribute exists, then we'll just return "on" as + // other browsers strangely seem to do without respecting the + // checked() state of the control. + if (val.isNull()) + return DOMString("on"); + return val; + } + + DOMString val = m_value; + // It's important *not* to fall back to the value attribute for file inputs, + // because that would allow a malicious web page to upload files by setting the + // value attribute in markup. + if (val.isNull() && m_type != FILE) + val = getAttribute(ATTR_VALUE); + + return val.isNull() ? DOMString("") : val; +} + + +void HTMLInputElementImpl::setValue(DOMString val) +{ + if (m_type == FILE) return; + + m_value = (val.isNull() ? DOMString("") : val); + // ### set attribute for other types, too. no need for m_value + // ### in those cases. + if (m_type == RADIO || m_type == CHECKBOX) + setAttribute(ATTR_VALUE, m_value); + if (m_type == TEXT && m_render) + m_render->updateFromElement(); + setChanged(); +} + +void HTMLInputElementImpl::blur() +{ + if(getDocument()->focusNode() == this) + getDocument()->setFocusNode(0); +} + +void HTMLInputElementImpl::focus() +{ + getDocument()->setFocusNode(this); +} + +void HTMLInputElementImpl::defaultEventHandler(EventImpl *evt) +{ + if ( !m_disabled ) + { + + if (evt->isMouseEvent()) { + MouseEventImpl* const me = static_cast<MouseEventImpl*>(evt); + if ((m_type == RADIO || m_type == CHECKBOX) + && me->id() == EventImpl::MOUSEUP_EVENT && me->detail() > 0) { + // click will follow + setChecked(m_type == RADIO ? true : !checked()); + } + if (evt->id() == EventImpl::CLICK_EVENT && m_type == IMAGE && m_render) { + // record the mouse position for when we get the DOMActivate event + int offsetX, offsetY; + m_render->absolutePosition(offsetX,offsetY); + xPos = me->clientX()-offsetX; + yPos = me->clientY()-offsetY; + KHTMLView* v = getDocument()->view(); + if ( v ) { + xPos += v->contentsX(); + yPos += v->contentsY(); + } + } + } + + if (m_type == RADIO || m_type == CHECKBOX || m_type == SUBMIT || m_type == RESET || m_type == BUTTON ) { + bool check = false; + if (active() && ( evt->id() == EventImpl::KEYUP_EVENT || + evt->id() == EventImpl::KEYPRESS_EVENT ) ) { + TextEventImpl* const te = static_cast<TextEventImpl *>(evt); + if (te->keyVal() == ' ') + check = true; + else if (te->keyVal() == '\r' && (m_type == BUTTON || m_type == RESET || m_type == SUBMIT)) + check = true; + } + if (check) { + if (evt->id() == EventImpl::KEYUP_EVENT) + click(); + // Tell the parent that we handle this key (keyup and keydown), even though only keyup activates (#70478) + evt->setDefaultHandled(); + } + } + + + // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means + // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks + // on the element, or presses enter while it is the active element. Javascript code wishing to activate the element + // must dispatch a DOMActivate event - a click event will not do the job. + if (m_type == IMAGE || m_type == SUBMIT || m_type == RESET) { + bool act = (evt->id() == EventImpl::DOMACTIVATE_EVENT); + if (!act && evt->id() == EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) { + QKeyEvent* const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent(); + if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space)) + act = true; + } + if (act) + activate(); + } + } + HTMLGenericFormElementImpl::defaultEventHandler(evt); +} + +void HTMLInputElementImpl::activate() +{ + if (!m_form) + return; + + m_clicked = true; + if (m_type == RESET) { + m_form->reset(); + } + else { + m_activeSubmit = true; + if (!m_form->prepareSubmit()) { + xPos = 0; + yPos = 0; + } + m_activeSubmit = false; + } +} + +bool HTMLInputElementImpl::isEditable() +{ + return ((m_type == TEXT) || (m_type == PASSWORD) || (m_type == ISINDEX) || (m_type == FILE)); +} + +long HTMLInputElementImpl::selectionStart() +{ + if (m_type != TEXT || !m_render) return -1; + return static_cast<RenderLineEdit*>(m_render)->selectionStart(); +} + +long HTMLInputElementImpl::selectionEnd() +{ + if (m_type != TEXT || !m_render) return -1; + return static_cast<RenderLineEdit*>(m_render)->selectionEnd(); +} + +void HTMLInputElementImpl::setSelectionStart(long pos) +{ + if (m_type != TEXT || !m_render) return; + static_cast<RenderLineEdit*>(m_render)->setSelectionStart(pos); +} + +void HTMLInputElementImpl::setSelectionEnd (long pos) +{ + if (m_type != TEXT || !m_render) return; + static_cast<RenderLineEdit*>(m_render)->setSelectionEnd(pos); +} + +void HTMLInputElementImpl::setSelectionRange(long start, long end) +{ + if (m_type != TEXT || !m_render) return; + static_cast<RenderLineEdit*>(m_render)->setSelectionRange(start, end); +} + +// ------------------------------------------------------------------------- + +HTMLLabelElementImpl::HTMLLabelElementImpl(DocumentImpl *doc) + : HTMLGenericFormElementImpl(doc) +{ +} + +HTMLLabelElementImpl::~HTMLLabelElementImpl() +{ +} + +NodeImpl::Id HTMLLabelElementImpl::id() const +{ + return ID_LABEL; +} + +void HTMLLabelElementImpl::attach() +{ + // skip the generic handler + HTMLElementImpl::attach(); +} + +NodeImpl* HTMLLabelElementImpl::getFormElement() +{ + const DOMString formElementId = getAttribute(ATTR_FOR); + NodeImpl *newNode=0L; + if (!formElementId.isEmpty()) + newNode=getDocument()->getElementById(formElementId); + if (!newNode){ + const uint children=childNodeCount(); + if (children>1) + for (unsigned int i=0;i<children;++i){ + const uint nodeId=childNode(i)->id(); + if (nodeId==ID_INPUT || nodeId==ID_SELECT || nodeId==ID_TEXTAREA){ + newNode=childNode(i); + break; + } + } + } +return newNode; +} + +void HTMLLabelElementImpl::defaultEventHandler(EventImpl *evt) +{ + if ( !m_disabled ) { + bool act = false; + if ( evt->id() == EventImpl::CLICK_EVENT ) { + act = true; + } + else if ( evt->isKeyRelatedEvent() && ( evt->id() == EventImpl::KEYUP_EVENT || + evt->id() == EventImpl::KEYPRESS_EVENT ) ) { + QKeyEvent* const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent(); + if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space)) + act = true; + } + + if (act) { + NodeImpl* const formNode=getFormElement(); + if (formNode && evt->target() != formNode) { + getDocument()->setFocusNode(formNode); + if (formNode->id()==ID_INPUT) + static_cast<DOM::HTMLInputElementImpl*>(formNode)->click(); + evt->setDefaultHandled(); + } + } + } + HTMLGenericFormElementImpl::defaultEventHandler(evt); +} + +// ------------------------------------------------------------------------- + +HTMLLegendElementImpl::HTMLLegendElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLGenericFormElementImpl(doc, f) +{ +} + +HTMLLegendElementImpl::~HTMLLegendElementImpl() +{ +} + +NodeImpl::Id HTMLLegendElementImpl::id() const +{ + return ID_LEGEND; +} + +void HTMLLegendElementImpl::attach() +{ + assert(!attached()); + assert(!m_render); + assert(parentNode()); + RenderStyle* const _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + if (parentNode()->renderer() && _style->display() != NONE) { + m_render = new (getDocument()->renderArena()) RenderLegend(this); + m_render->setStyle(_style); + } + HTMLGenericFormElementImpl::attach(); + _style->deref(); +} + +void HTMLLegendElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_ACCESSKEY: + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------- + +HTMLSelectElementImpl::HTMLSelectElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLGenericFormElementImpl(doc, f) +{ + m_multiple = false; + m_recalcListItems = false; + // 0 means invalid (i.e. not set) + m_size = 0; + m_minwidth = 0; + m_length = 0; +} + +HTMLSelectElementImpl::~HTMLSelectElementImpl() +{ + if (getDocument()) getDocument()->deregisterMaintainsState(this); +} + +NodeImpl::Id HTMLSelectElementImpl::id() const +{ + return ID_SELECT; +} + +DOMString HTMLSelectElementImpl::type() const +{ + return (m_multiple ? "select-multiple" : "select-one"); +} + +long HTMLSelectElementImpl::selectedIndex() const +{ + // return the number of the first option selected + uint o = 0; + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + const unsigned int itemsSize = items.size(); + for (unsigned int i = 0; i < itemsSize; ++i) { + if (items[i]->id() == ID_OPTION) { + if (static_cast<HTMLOptionElementImpl*>(items[i])->selected()) + return o; + o++; + } + } + Q_ASSERT(m_multiple || items.isEmpty()); + return -1; +} + +void HTMLSelectElementImpl::setSelectedIndex( long index ) +{ + // deselect all other options and select only the new one + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + int listIndex; + const int itemsSize = int(items.size()); + for (listIndex = 0; listIndex < itemsSize; ++listIndex) { + if (items[listIndex]->id() == ID_OPTION) + static_cast<HTMLOptionElementImpl*>(items[listIndex])->setSelected(false); + } + listIndex = optionToListIndex(index); + if (listIndex >= 0) + static_cast<HTMLOptionElementImpl*>(items[listIndex])->setSelected(true); + + setChanged(true); +} + +long HTMLSelectElementImpl::length() const +{ + if (m_recalcListItems) + recalcListItems(); + return m_length; +} + +void HTMLSelectElementImpl::add( const HTMLElement &element, const HTMLElement &before, int& exceptioncode ) +{ + if(element.isNull() || element.handle()->id() != ID_OPTION) + return; + + HTMLOptionElementImpl* option = static_cast<HTMLOptionElementImpl*>(element.handle());; + //Fast path for appending an item. Can't be done if it is selected and + //we're single-select, since we may need to drop an implicitly-selected item + bool fastAppendLast = false; + if (before.handle() == 0 && (m_multiple || !option->selected()) && !m_recalcListItems) + fastAppendLast = true; + + insertBefore(option, before.handle(), exceptioncode ); + + if (fastAppendLast) { + m_listItems.resize(m_listItems.size() + 1); + m_listItems[m_listItems.size() - 1] = option; + ++m_length; + m_recalcListItems = false; + } else if (!exceptioncode) + setRecalcListItems(); +} + +void HTMLSelectElementImpl::remove( long index ) +{ + int exceptioncode = 0; + const int listIndex = optionToListIndex(index); + + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + if(listIndex < 0 || index >= int(items.size())) + return; // ### what should we do ? remove the last item? + + //Fast path for last element, for e.g. clearing the box + //Note that if this is a single-select, we may have to recompute + //anyway if the item was selected, since we may want to set + //a different one + bool fastRemoveLast = false; + if ((listIndex == (signed)items.size() - 1) && !m_recalcListItems && + (m_multiple || !static_cast<HTMLOptionElementImpl*>(items[listIndex])->selected())) + fastRemoveLast = true; + + removeChild(items[listIndex], exceptioncode); + + if (fastRemoveLast) { + m_listItems.resize(m_listItems.size() - 1); + --m_length; + m_recalcListItems = false; + } else if( !exceptioncode) + setRecalcListItems(); +} + +void HTMLSelectElementImpl::blur() +{ + if(getDocument()->focusNode() == this) + getDocument()->setFocusNode(0); +} + +void HTMLSelectElementImpl::focus() +{ + getDocument()->setFocusNode(this); +} + +DOMString HTMLInputElementImpl::valueWithDefault() const +{ + DOMString v = value(); + if (v.isEmpty()) { + switch (m_type) { + case RESET: +#ifdef APPLE_CHANGES + v = resetButtonDefaultLabel(); +#else + v = i18n("Reset"); +#endif + break; + + case SUBMIT: +#ifdef APPLE_CHANGES + v = submitButtonDefaultLabel(); +#else + v = i18n("Submit"); +#endif + break; + + case BUTTON: + case CHECKBOX: + case FILE: + case HIDDEN: + case IMAGE: + case ISINDEX: + case PASSWORD: + case RADIO: + #ifdef APPLE_CHANGES + case RANGE: + case SEARCH: + #endif + case TEXT: + break; + } + } + return v; +} + +DOMString HTMLSelectElementImpl::value( ) const +{ + uint i; + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + const uint itemsSize = items.size(); + for (i = 0; i < itemsSize; ++i) { + if ( items[i]->id() == ID_OPTION + && static_cast<HTMLOptionElementImpl*>(items[i])->selected()) + return static_cast<HTMLOptionElementImpl*>(items[i])->value(); + } + return DOMString(""); +} + +void HTMLSelectElementImpl::setValue(DOMStringImpl* value) +{ + // find the option with value() matching the given parameter + // and make it the current selection. + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + for (unsigned i = 0; i < items.size(); i++) + if (items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl*>(items[i])->value() == value) { + static_cast<HTMLOptionElementImpl*>(items[i])->setSelected(true); + return; + } +} + +QString HTMLSelectElementImpl::state( ) +{ + QString state; + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + + const int l = items.count(); + + state.fill('.', l); + for(int i = 0; i < l; ++i) + if(items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl*>(items[i])->selected()) + state[i] = 'X'; + + return state; +} + +void HTMLSelectElementImpl::restoreState(const QString &_state) +{ + recalcListItems(); + + QString state = _state; + if(!state.isEmpty() && !state.contains('X') && !m_multiple && m_size <= 1) { + qWarning("should not happen in restoreState!"); + state[0] = 'X'; + } + + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + + const int l = items.count(); + for(int i = 0; i < l; ++i) { + if(items[i]->id() == ID_OPTION) { + HTMLOptionElementImpl* const oe = static_cast<HTMLOptionElementImpl*>(items[i]); + oe->setSelected(state[i] == 'X'); + } + } + setChanged(true); +} + +NodeImpl *HTMLSelectElementImpl::insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode ) +{ + NodeImpl* const result = HTMLGenericFormElementImpl::insertBefore(newChild,refChild, exceptioncode ); + if (!exceptioncode) + setRecalcListItems(); + return result; +} + +void HTMLSelectElementImpl::replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode ) +{ + HTMLGenericFormElementImpl::replaceChild(newChild,oldChild, exceptioncode); + if( !exceptioncode ) + setRecalcListItems(); +} + +void HTMLSelectElementImpl::removeChild ( NodeImpl *oldChild, int &exceptioncode ) +{ + HTMLGenericFormElementImpl::removeChild(oldChild, exceptioncode); + if( !exceptioncode ) + setRecalcListItems(); +} + +NodeImpl *HTMLSelectElementImpl::appendChild ( NodeImpl *newChild, int &exceptioncode ) +{ + NodeImpl* const result = HTMLGenericFormElementImpl::appendChild(newChild, exceptioncode); + if( !exceptioncode ) + setRecalcListItems(); + setChanged(true); + return result; +} + +NodeImpl* HTMLSelectElementImpl::addChild(NodeImpl* newChild) +{ + setRecalcListItems(); + return HTMLGenericFormElementImpl::addChild(newChild); +} + +void HTMLSelectElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_SIZE: + m_size = kMax( attr->val()->toInt(), 1 ); + setChanged(); + break; + case ATTR_WIDTH: + m_minwidth = kMax( attr->val()->toInt(), 0 ); + break; + case ATTR_MULTIPLE: + m_multiple = (attr->val() != 0); + break; + case ATTR_ACCESSKEY: + break; + case ATTR_ALIGN: + addHTMLAlignment( attr->value() ); + break; + case ATTR_ONCHANGE: + setHTMLEventListener(EventImpl::CHANGE_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onchange", this)); + break; + default: + HTMLGenericFormElementImpl::parseAttribute(attr); + } +} + +void HTMLSelectElementImpl::attach() +{ + assert(!attached()); + assert(parentNode()); + assert(!renderer()); + + RenderStyle* const _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + if (parentNode()->renderer() && _style->display() != NONE) { + m_render = new (getDocument()->renderArena()) RenderSelect(this); + m_render->setStyle(_style); + } + + HTMLGenericFormElementImpl::attach(); + _style->deref(); +} + +bool HTMLSelectElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoded_values, bool) +{ + bool successful = false; + const QCString enc_name = fixUpfromUnicode(codec, name().string()); + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + + uint i; + const uint itemsSize = items.size(); + for (i = 0; i < itemsSize; ++i) { + if (items[i]->id() == ID_OPTION) { + HTMLOptionElementImpl* const option = static_cast<HTMLOptionElementImpl*>(items[i]); + if (option->selected()) { + encoded_values += enc_name; + encoded_values += fixUpfromUnicode(codec, option->value().string()); + successful = true; + } + } + } + + // ### this case should not happen. make sure that we select the first option + // in any case. otherwise we have no consistency with the DOM interface. FIXME! + // we return the first one if it was a combobox select + if (!successful && !m_multiple && m_size <= 1 && itemsSize && + (items[0]->id() == ID_OPTION) ) { + HTMLOptionElementImpl* const option = static_cast<HTMLOptionElementImpl*>(items[0]); + encoded_values += enc_name; + if (option->value().isNull()) + encoded_values += fixUpfromUnicode(codec, option->text().string().stripWhiteSpace()); + else + encoded_values += fixUpfromUnicode(codec, option->value().string()); + successful = true; + } + + return successful; +} + +int HTMLSelectElementImpl::optionToListIndex(int optionIndex) const +{ + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + const int itemsSize = int(items.size()); + if (optionIndex < 0 || optionIndex >= itemsSize) + return -1; + + //See if we're asked for the very last item, and check whether it's an <option> + //to fastpath clear + if ((unsigned int)optionIndex == (m_length - 1) && items[itemsSize - 1]->id() == ID_OPTION) + return itemsSize - 1; + + int listIndex = 0; + int optionIndex2 = 0; + for (; + optionIndex2 < itemsSize && optionIndex2 <= optionIndex; + ++listIndex) { // not a typo! + if (items[listIndex]->id() == ID_OPTION) + ++optionIndex2; + } + --listIndex; + return listIndex; +} + +int HTMLSelectElementImpl::listToOptionIndex(int listIndex) const +{ + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + if (listIndex < 0 || listIndex >= int(items.size()) || + items[listIndex]->id() != ID_OPTION) + return -1; + + int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list + int i; + for (i = 0; i < listIndex; i++) + if (items[i]->id() == ID_OPTION) + optionIndex++; + return optionIndex; +} + +void HTMLSelectElementImpl::recalcListItems() const +{ + NodeImpl* current = firstChild(); + m_listItems.resize(0); + HTMLOptionElementImpl* foundSelected = 0; + m_length = 0; + while(current) { + if (current->id() == ID_OPTGROUP && current->firstChild()) { + // ### what if optgroup contains just comments? don't want one of no options in it... + m_listItems.resize(m_listItems.size()+1); + m_listItems[m_listItems.size()-1] = static_cast<HTMLGenericFormElementImpl*>(current); + current = current->firstChild(); + } + if (current->id() == ID_OPTION) { + ++m_length; + m_listItems.resize(m_listItems.size()+1); + m_listItems[m_listItems.size()-1] = static_cast<HTMLGenericFormElementImpl*>(current); + if (!foundSelected && !m_multiple && m_size <= 1) { + foundSelected = static_cast<HTMLOptionElementImpl*>(current); + foundSelected->m_selected = true; + } + else if (foundSelected && !m_multiple && static_cast<HTMLOptionElementImpl*>(current)->selected()) { + foundSelected->m_selected = false; + foundSelected = static_cast<HTMLOptionElementImpl*>(current); + } + } + NodeImpl* const parent = current->parentNode(); + current = current->nextSibling(); + if (!current) { + if (static_cast<const NodeImpl *>(parent) != this) + current = parent->nextSibling(); + } + } + m_recalcListItems = false; +} + +void HTMLSelectElementImpl::childrenChanged() +{ + setRecalcListItems(); + + HTMLGenericFormElementImpl::childrenChanged(); +} + +void HTMLSelectElementImpl::setRecalcListItems() +{ + m_recalcListItems = true; + if (m_render) + static_cast<khtml::RenderSelect*>(m_render)->setOptionsChanged(true); + setChanged(); +} + +void HTMLSelectElementImpl::reset() +{ + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + uint i; + const uint itemsSize = items.size(); + bool anySelected = false; + for (i = 0; i < itemsSize; ++i) { + if (items[i]->id() == ID_OPTION) { + HTMLOptionElementImpl* const option = static_cast<HTMLOptionElementImpl*>(items[i]); + const bool selected = (!option->getAttribute(ATTR_SELECTED).isNull()); + option->setSelected(selected); + if (selected) + anySelected = true; + } + } + // If this is a single-row SELECT and there is no default selection, jump to first option. + if ( !anySelected && m_size <= 1 ) { + for (i = 0; i < itemsSize; ++i) { + if (items[i]->id() == ID_OPTION) { + static_cast<HTMLOptionElementImpl*>(items[i])->setSelected(true); + break; + } + } + } + if ( m_render ) + static_cast<RenderSelect*>(m_render)->setSelectionChanged(true); + setChanged( true ); +} + +void HTMLSelectElementImpl::notifyOptionSelected(HTMLOptionElementImpl *selectedOption, bool selected) +{ + if (selected && !m_multiple) { + // deselect all other options + QMemArray<HTMLGenericFormElementImpl*> items = listItems(); + uint i; + const uint itemsSize = items.size(); + for (i = 0; i < itemsSize; ++i) { + if (items[i]->id() == ID_OPTION) + static_cast<HTMLOptionElementImpl*>(items[i])->m_selected = (items[i] == selectedOption); + } + } + if (m_render) + static_cast<RenderSelect*>(m_render)->setSelectionChanged(true); + + setChanged(true); +} + +// ------------------------------------------------------------------------- + +HTMLKeygenElementImpl::HTMLKeygenElementImpl(DocumentImpl* doc, HTMLFormElementImpl* f) + : HTMLSelectElementImpl(doc, f) +{ + const QStringList keys = KSSLKeyGen::supportedKeySizes(); + QStringList::ConstIterator i = keys.begin(); + const QStringList::ConstIterator iEnd = keys.end(); + for ( ; i != iEnd; ++i) { + HTMLOptionElementImpl* const o = new HTMLOptionElementImpl(doc, form()); + addChild(o); + o->addChild(doc->createTextNode(DOMString(*i).implementation())); + } +} + +NodeImpl::Id HTMLKeygenElementImpl::id() const +{ + return ID_KEYGEN; +} + +void HTMLKeygenElementImpl::parseAttribute(AttributeImpl* attr) +{ + switch(attr->id()) + { + case ATTR_CHALLENGE: + break; + default: + // skip HTMLSelectElementImpl parsing! + HTMLGenericFormElementImpl::parseAttribute(attr); + } +} + +bool HTMLKeygenElementImpl::encoding(const QTextCodec* codec, khtml::encodingList& encoded_values, bool) +{ + bool successful = false; + const QCString enc_name = fixUpfromUnicode(codec, name().string()); + + encoded_values += enc_name; + + // pop up the fancy certificate creation dialog here + KSSLKeyGen* const kg = new KSSLKeyGen(getDocument()->view(), "Key Generator", true); + + kg->setKeySize(0); + successful = (QDialog::Accepted == kg->exec()); + + delete kg; + + encoded_values += "deadbeef"; + + return successful; +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLOptGroupElementImpl::id() const +{ + return ID_OPTGROUP; +} + +// ------------------------------------------------------------------------- + +HTMLOptionElementImpl::HTMLOptionElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLGenericFormElementImpl(doc, f) +{ + m_selected = false; +} + +NodeImpl::Id HTMLOptionElementImpl::id() const +{ + return ID_OPTION; +} + +DOMString HTMLOptionElementImpl::text() const +{ + if (firstChild() && firstChild()->nodeType() == Node::TEXT_NODE) { + if (firstChild()->nextSibling()) { + DOMString ret = ""; + NodeImpl *n = firstChild(); + for (; n; n = n->nextSibling()) { + if (n->nodeType() == Node::TEXT_NODE || + n->nodeType() == Node::CDATA_SECTION_NODE) + ret += n->nodeValue(); + } + return ret; + } + else + return firstChild()->nodeValue(); + } + return ""; +} + +long HTMLOptionElementImpl::index() const +{ + // Let's do this dynamically. Might be a bit slow, but we're sure + // we won't forget to update a member variable in some cases... + QMemArray<HTMLGenericFormElementImpl*> items = getSelect()->listItems(); + const int l = items.count(); + int optionIndex = 0; + for(int i = 0; i < l; ++i) { + if(items[i]->id() == ID_OPTION) + { + if (static_cast<HTMLOptionElementImpl*>(items[i]) == this) + return optionIndex; + ++optionIndex; + } + } + kdWarning() << "HTMLOptionElementImpl::index(): option not found!" << endl; + return 0; +} + +void HTMLOptionElementImpl::setIndex( long ) +{ + kdWarning() << "Unimplemented HTMLOptionElementImpl::setIndex(long) called" << endl; + // ### +} + +void HTMLOptionElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_SELECTED: + m_selected = (attr->val() != 0); + break; + case ATTR_VALUE: + m_value = attr->value(); + break; + default: + HTMLGenericFormElementImpl::parseAttribute(attr); + } +} + +DOMString HTMLOptionElementImpl::value() const +{ + if ( !m_value.isNull() ) + return m_value; + // Use the text if the value wasn't set. + return text().string().simplifyWhiteSpace(); +} + +void HTMLOptionElementImpl::setValue(DOMStringImpl* value) +{ + setAttribute(ATTR_VALUE, value); +} + +void HTMLOptionElementImpl::setSelected(bool _selected) +{ + if(m_selected == _selected) + return; + m_selected = _selected; + HTMLSelectElementImpl* const select = getSelect(); + if (select) + select->notifyOptionSelected(this,_selected); +} + +HTMLSelectElementImpl *HTMLOptionElementImpl::getSelect() const +{ + NodeImpl *select = parentNode(); + while (select && select->id() != ID_SELECT) + select = select->parentNode(); + return static_cast<HTMLSelectElementImpl*>(select); +} + +// ------------------------------------------------------------------------- + +HTMLTextAreaElementImpl::HTMLTextAreaElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLGenericFormElementImpl(doc, f) +{ + // DTD requires rows & cols be specified, but we will provide reasonable defaults + m_rows = 2; + m_cols = 20; + m_wrap = ta_Virtual; + m_changed = false; + m_dirtyvalue = true; + m_initialized = false; + m_unsubmittedFormChange = false; +} + +HTMLTextAreaElementImpl::~HTMLTextAreaElementImpl() +{ + if (getDocument()) getDocument()->deregisterMaintainsState(this); +} + +NodeImpl::Id HTMLTextAreaElementImpl::id() const +{ + return ID_TEXTAREA; +} + +DOMString HTMLTextAreaElementImpl::type() const +{ + return "textarea"; +} + +QString HTMLTextAreaElementImpl::state( ) +{ + return value().string() + (m_unsubmittedFormChange ? 'M' : '.'); +} + +void HTMLTextAreaElementImpl::restoreState(const QString &state) +{ + setDefaultValue(state.left(state.length()-1)); + m_unsubmittedFormChange = state.endsWith("M"); + // the close() in the rendertree will take care of transferring defaultvalue to 'value' +} + +void HTMLTextAreaElementImpl::select( ) +{ + if (m_render) + static_cast<RenderTextArea*>(m_render)->select(); + onSelect(); +} + +void HTMLTextAreaElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_ROWS: + m_rows = 0; + if (attr->val()) + m_rows = DOMString(attr->val()).string().toInt(); + if (!m_rows) m_rows = 2; + if (renderer()) + renderer()->setNeedsLayoutAndMinMaxRecalc(); + break; + case ATTR_COLS: + m_cols = 0; + if (attr->val()) + m_cols = DOMString(attr->val()).string().toInt(); + if (!m_cols) m_cols = 20; + if (renderer()) + renderer()->setNeedsLayoutAndMinMaxRecalc(); + break; + case ATTR_WRAP: + // virtual / physical is Netscape extension of HTML 3.0, now deprecated + // soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4 + if ( strcasecmp( attr->value(), "virtual" ) == 0 || strcasecmp( attr->value(), "soft") == 0) + m_wrap = ta_Virtual; + else if ( strcasecmp ( attr->value(), "physical" ) == 0 || strcasecmp( attr->value(), "hard") == 0) + m_wrap = ta_Physical; + else if(strcasecmp( attr->value(), "on" ) == 0) + m_wrap = ta_Physical; + else if(strcasecmp( attr->value(), "off") == 0) + m_wrap = ta_NoWrap; + break; + case ATTR_ACCESSKEY: + break; + case ATTR_ALIGN: + break; + case ATTR_ONSELECT: + setHTMLEventListener(EventImpl::SELECT_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onselect", this)); + break; + case ATTR_ONCHANGE: + setHTMLEventListener(EventImpl::CHANGE_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onchange", this)); + break; + default: + HTMLGenericFormElementImpl::parseAttribute(attr); + } +} + +void HTMLTextAreaElementImpl::attach() +{ + assert(!attached()); + assert(!m_render); + assert(parentNode()); + + RenderStyle* const _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + if (parentNode()->renderer() && _style->display() != NONE) { + m_render = new (getDocument()->renderArena()) RenderTextArea(this); + m_render->setStyle(_style); + } + + HTMLGenericFormElementImpl::attach(); + _style->deref(); +} + + +static QString expandLF(const QString& s) +{ + // LF -> CRLF + unsigned crs = s.contains( '\n' ); + if (crs == 0) + return s; + unsigned len = s.length(); + + QString r; + r.reserve(len + crs + 1); + unsigned pos2 = 0; + for(unsigned pos = 0; pos < len; pos++) + { + QChar c = s.at(pos); + switch(c.unicode()) + { + case '\n': + r[pos2++] = '\r'; + r[pos2++] = '\n'; + break; + + case '\r': + break; + + default: + r[pos2++]= c; + break; + } + } + r.squeeze(); + return r; +} + + +bool HTMLTextAreaElementImpl::encoding(const QTextCodec* codec, encodingList& encoding, bool) +{ + if (name().isEmpty()) return false; + + encoding += fixUpfromUnicode(codec, name().string()); + encoding += fixUpfromUnicode(codec, expandLF(value().string())); + + return true; +} + +void HTMLTextAreaElementImpl::reset() +{ + setValue(defaultValue()); +} + + +DOMString HTMLTextAreaElementImpl::value() +{ + if ( m_dirtyvalue) { + if ( m_render && m_initialized ) { + RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render ); + m_value = renderArea->text(); + m_dirtyvalue = false; + } else { + m_value = defaultValue().string(); + m_initialized = true; + m_dirtyvalue = false; + } + } + + if ( m_value.isNull() ) return ""; + + return m_value; +} + +void HTMLTextAreaElementImpl::setValue(DOMString _value) +{ + // \r\n -> \n, \r -> \n + QString str = _value.string().replace( "\r\n", "\n" ); + m_value = str.replace( '\r', '\n' ); + m_dirtyvalue = false; + m_initialized = true; + setChanged(true); +} + +DOMString HTMLTextAreaElementImpl::defaultValue() +{ + DOMString val = ""; + // there may be comments - just grab the text nodes + NodeImpl *n; + for (n = firstChild(); n; n = n->nextSibling()) + if (n->isTextNode()) + val += static_cast<TextImpl*>(n)->data(); + + if (val[0] == '\r' && val[1] == '\n') { + val = val.copy(); + val.remove(0,2); + } + else if (val[0] == '\r' || val[0] == '\n') { + val = val.copy(); + val.remove(0,1); + } + + return val; +} + +void HTMLTextAreaElementImpl::setDefaultValue(DOMString _defaultValue) +{ + // there may be comments - remove all the text nodes and replace them with one + QPtrList<NodeImpl> toRemove; + NodeImpl *n; + for (n = firstChild(); n; n = n->nextSibling()) + if (n->isTextNode()) + toRemove.append(n); + QPtrListIterator<NodeImpl> it(toRemove); + int exceptioncode = 0; + for (; it.current(); ++it) { + removeChild(it.current(), exceptioncode); + } + insertBefore(getDocument()->createTextNode(_defaultValue.implementation()),firstChild(), exceptioncode); + setValue(_defaultValue); +} + +void HTMLTextAreaElementImpl::blur() +{ + if(getDocument()->focusNode() == this) + getDocument()->setFocusNode(0); +} + +void HTMLTextAreaElementImpl::focus() +{ + getDocument()->setFocusNode(this); +} + +bool HTMLTextAreaElementImpl::isEditable() +{ + return true; +} + +//Mozilla extensions. +long HTMLTextAreaElementImpl::selectionStart() +{ + if (m_render) { + RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render ); + return renderArea->selectionStart(); + } + + return 0; +} + +long HTMLTextAreaElementImpl::selectionEnd() +{ + if (m_render) { + RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render ); + return renderArea->selectionEnd(); + } + + return 0; +} + +void HTMLTextAreaElementImpl::setSelectionStart(long pos) +{ + if (m_render) { + RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render ); + renderArea->setSelectionStart( pos ); + } +} + +void HTMLTextAreaElementImpl::setSelectionEnd(long pos) +{ + if (m_render) { + RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render ); + renderArea->setSelectionEnd( pos ); + } +} + +void HTMLTextAreaElementImpl::setSelectionRange(long start, long end) +{ + if (m_render) { + RenderTextArea* renderArea = static_cast<RenderTextArea*>( m_render ); + renderArea->setSelectionRange( start, end ); + } +} + +long HTMLTextAreaElementImpl::textLength() +{ + return value().length(); +} + +// ------------------------------------------------------------------------- + +HTMLIsIndexElementImpl::HTMLIsIndexElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLInputElementImpl(doc, f) +{ + m_type = TEXT; + setName("isindex"); +} + +HTMLIsIndexElementImpl::~HTMLIsIndexElementImpl() +{ +} + +NodeImpl::Id HTMLIsIndexElementImpl::id() const +{ + return ID_ISINDEX; +} + +void HTMLIsIndexElementImpl::parseAttribute(AttributeImpl* attr) +{ + // don't call HTMLInputElement::parseAttribute here, as it would + // accept attributes this element does not support + HTMLGenericFormElementImpl::parseAttribute(attr); +} + +DOMString HTMLIsIndexElementImpl::prompt() const +{ + // When IsIndex is parsed, <HR/>Prompt: <ISINDEX/><HR/> is created. + // So we have to look at the previous sibling to find the prompt text + DOM::NodeImpl* const prev = previousSibling(); + if ( prev && prev->nodeType() == DOM::Node::TEXT_NODE) + return prev->nodeValue(); + return ""; +} + +void HTMLIsIndexElementImpl::setPrompt(const DOMString& str) +{ + // When IsIndex is parsed, <HR/>Prompt: <ISINDEX/><HR/> is created. + // So we have to look at the previous sibling to find the prompt text + int exceptioncode = 0; + DOM::NodeImpl* const prev = previousSibling(); + if ( prev && prev->nodeType() == DOM::Node::TEXT_NODE) + static_cast<DOM::TextImpl *>(prev)->setData(str, exceptioncode); +} + +// ------------------------------------------------------------------------- + diff --git a/khtml/html/html_formimpl.h b/khtml/html/html_formimpl.h new file mode 100644 index 000000000..f12821340 --- /dev/null +++ b/khtml/html/html_formimpl.h @@ -0,0 +1,606 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_FORMIMPL_H +#define HTML_FORMIMPL_H + +#include "html/html_elementimpl.h" +#include "html/html_imageimpl.h" +#include "dom/html_element.h" + +#include <qvaluelist.h> +#include <qptrlist.h> +#include <qcstring.h> +#include <qmemarray.h> + +class KHTMLView; +class QTextCodec; + +namespace khtml +{ + class RenderFormElement; + class RenderTextArea; + class RenderSelect; + class RenderLineEdit; + class RenderRadioButton; + class RenderFileButton; + + typedef QValueList<QCString> encodingList; +} + +namespace KWallet { + class Wallet; +} + +namespace DOM { + +class HTMLFormElement; +class DOMString; +class HTMLGenericFormElementImpl; +class HTMLOptionElementImpl; + +// ------------------------------------------------------------------------- + +class HTMLFormElementImpl : public HTMLElementImpl +{ +public: + HTMLFormElementImpl(DocumentImpl *doc, bool implicit); + virtual ~HTMLFormElementImpl(); + + virtual Id id() const; + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void addId(const QString& id); + virtual void removeId(const QString& id); + + + long length() const; + + QByteArray formData(bool& ok); + + DOMString enctype() const { return m_enctype; } + void setEnctype( const DOMString & ); + + bool autoComplete() const { return m_autocomplete; } + void doAutoFill(); + void walletOpened(KWallet::Wallet *w); + + virtual void parseAttribute(AttributeImpl *attr); + + void radioClicked( HTMLGenericFormElementImpl *caller ); + + void registerFormElement(HTMLGenericFormElementImpl *); + void removeFormElement(HTMLGenericFormElementImpl *); + void registerImgElement(HTMLImageElementImpl *); + void removeImgElement(HTMLImageElementImpl *); + + void submitFromKeyboard(); + bool prepareSubmit(); + void submit(); + void reset(); + + void setMalformed(bool malformed) { m_malformed = malformed; } + bool isMalformed() const { return m_malformed; } + + friend class HTMLFormElement; + friend class HTMLFormCollectionImpl; + +private: + void gatherWalletData(); + QPtrList<HTMLGenericFormElementImpl> formElements; + QPtrList<HTMLImageElementImpl> imgElements; + DOMString m_target; + DOMString m_enctype; + QString m_boundary; + DOMString m_acceptcharset; + QString m_encCharset; + bool m_post : 1; + bool m_multipart : 1; + bool m_autocomplete : 1; + bool m_insubmit : 1; + bool m_doingsubmit : 1; + bool m_inreset : 1; + bool m_malformed : 1; + bool m_haveTextarea : 1; // for wallet storage + bool m_havePassword : 1; // for wallet storage + DOMString m_name; // our name + QMap<QString, QString> m_walletMap; // for wallet storage +}; + +// ------------------------------------------------------------------------- + +class HTMLGenericFormElementImpl : public HTMLElementImpl +{ + friend class HTMLFormElementImpl; + friend class khtml::RenderFormElement; + +public: + HTMLGenericFormElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + virtual ~HTMLGenericFormElementImpl(); + + HTMLFormElementImpl *form() { return m_form; } + + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); + virtual void reset() {} + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + void onSelect(); + void onChange(); + + bool disabled() const { return m_disabled; } + void setDisabled(bool _disabled); + + virtual bool isFocusable() const; + virtual bool isEnumeratable() const { return false; } + + bool readOnly() const { return m_readOnly; } + void setReadOnly(bool _readOnly) { m_readOnly = _readOnly; } + + DOMString name() const; + void setName(const DOMString& name); + + virtual bool isGenericFormElement() const { return true; } + + /* + * override in derived classes to get the encoded name=value pair + * for submitting + * return true for a successful control (see HTML4-17.13.2) + */ + virtual bool encoding(const QTextCodec*, khtml::encodingList&, bool) { return false; } + + virtual void defaultEventHandler(EventImpl *evt); + virtual bool isEditable(); + +protected: + HTMLFormElementImpl *getForm() const; + + DOMStringImpl* m_name; + HTMLFormElementImpl *m_form; + bool m_disabled, m_readOnly; +}; + +// ------------------------------------------------------------------------- + +class HTMLButtonElementImpl : public HTMLGenericFormElementImpl +{ +public: + HTMLButtonElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + + virtual ~HTMLButtonElementImpl(); + + enum typeEnum { + SUBMIT, + RESET, + BUTTON + }; + + virtual Id id() const; + virtual bool isEnumeratable() const { return true; } + + DOMString type() const; + typeEnum buttonType() const { return m_type; } + virtual void parseAttribute(AttributeImpl *attr); + virtual void defaultEventHandler(EventImpl *evt); + virtual bool encoding(const QTextCodec*, khtml::encodingList&, bool); + void activate(); + virtual void attach(); + void click(); + void blur(); + void focus(); + +protected: + DOMString m_value; + QString m_currValue; + typeEnum m_type : 2; + bool m_dirty : 1; + bool m_clicked : 1; + bool m_activeSubmit : 1; +}; + +// ------------------------------------------------------------------------- + +class HTMLFieldSetElementImpl : public HTMLGenericFormElementImpl +{ +public: + HTMLFieldSetElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + + virtual ~HTMLFieldSetElementImpl(); + + virtual Id id() const; + virtual void attach(); + virtual void parseAttribute(AttributeImpl *attr); + +}; + +// ------------------------------------------------------------------------- + +class HTMLInputElementImpl : public HTMLGenericFormElementImpl +{ + friend class khtml::RenderLineEdit; + friend class khtml::RenderRadioButton; + friend class khtml::RenderFileButton; + +public: + // do not change the order! + enum typeEnum { + TEXT, + PASSWORD, + ISINDEX, + CHECKBOX, + RADIO, + SUBMIT, + RESET, + FILE, + HIDDEN, + IMAGE, + BUTTON + }; + + HTMLInputElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + virtual ~HTMLInputElementImpl(); + + virtual Id id() const; + + virtual bool isEnumeratable() const { return inputType() != IMAGE; } + + bool autoComplete() const { return m_autocomplete; } + + bool checked() const { return m_useDefaultChecked ? m_defaultChecked : m_checked; } + void setChecked(bool); + bool indeterminate() const { return m_indeterminate; } + void setIndeterminate(bool); + long maxLength() const { return m_maxLen; } + int size() const { return m_size; } + DOMString type() const; + void setType(const DOMString& t); + + DOMString value() const; + void setValue(DOMString val); + + DOMString valueWithDefault() const; + + void blur(); + void focus(); + + virtual bool maintainsState() { return true; } + virtual QString state(); + virtual void restoreState(const QString &state); + + void select(); + void click(); + + virtual void parseAttribute(AttributeImpl *attr); + + virtual void attach(); + virtual bool encoding(const QTextCodec*, khtml::encodingList&, bool); + + typeEnum inputType() const { return m_type; } + virtual void reset(); + + // used in case input type=image was clicked. + int clickX() const { return xPos; } + int clickY() const { return yPos; } + + virtual void defaultEventHandler(EventImpl *evt); + virtual bool isEditable(); + + DOMString altText() const; + void activate(); + + void setUnsubmittedFormChange(bool unsubmitted) { m_unsubmittedFormChange = unsubmitted; } + + //Mozilla extensions. + long selectionStart(); + long selectionEnd(); + void setSelectionStart(long pos); + void setSelectionEnd (long pos); + void setSelectionRange(long start, long end); +protected: + void parseType(const DOMString& t); + + DOMString m_value; + int xPos; + short m_maxLen; + short m_size; + short yPos; + + typeEnum m_type : 4; + bool m_clicked : 1 ; + bool m_checked : 1; + bool m_defaultChecked : 1; // could do without by checking ATTR_CHECKED + bool m_useDefaultChecked : 1; + bool m_indeterminate : 1; + bool m_haveType : 1; + bool m_activeSubmit : 1; + bool m_autocomplete : 1; + bool m_inited : 1; + bool m_unsubmittedFormChange : 1; +}; + +// ------------------------------------------------------------------------- + +class HTMLLabelElementImpl : public HTMLGenericFormElementImpl +{ +public: + HTMLLabelElementImpl(DocumentImpl *doc); + virtual ~HTMLLabelElementImpl(); + + virtual Id id() const; + virtual void attach(); + virtual void defaultEventHandler(EventImpl *evt); + virtual bool isFocusable() const { return true; }; + virtual bool isTabFocusable() const { return false; }; + NodeImpl* getFormElement(); + + private: + DOMString m_formElementID; +}; + +// ------------------------------------------------------------------------- + +class HTMLLegendElementImpl : public HTMLGenericFormElementImpl +{ +public: + HTMLLegendElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + virtual ~HTMLLegendElementImpl(); + + virtual Id id() const; + virtual void attach(); + virtual void parseAttribute(AttributeImpl *attr); +}; + + +// ------------------------------------------------------------------------- + +class HTMLSelectElementImpl : public HTMLGenericFormElementImpl +{ + friend class khtml::RenderSelect; + +public: + HTMLSelectElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + ~HTMLSelectElementImpl(); + + virtual Id id() const; + + DOMString type() const; + + long selectedIndex() const; + void setSelectedIndex( long index ); + + virtual bool isEnumeratable() const { return true; } + + long length() const; + + long minWidth() const { return m_minwidth; } + + long size() const { return m_size; } + + bool multiple() const { return m_multiple; } + + void add ( const HTMLElement &element, const HTMLElement &before, int& exceptioncode ); + void remove ( long index ); + void blur(); + void focus(); + + DOMString value() const; + void setValue(DOMStringImpl* value); + + virtual bool maintainsState() { return true; } + virtual QString state(); + virtual void restoreState(const QString &state); + + virtual NodeImpl *insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode ); + virtual void replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode ); + virtual void removeChild ( NodeImpl *oldChild, int &exceptioncode ); + virtual NodeImpl *appendChild ( NodeImpl *newChild, int &exceptioncode ); + virtual NodeImpl *addChild( NodeImpl* newChild ); + + virtual void childrenChanged(); + + virtual void parseAttribute(AttributeImpl *attr); + + virtual void attach(); + virtual bool encoding(const QTextCodec*, khtml::encodingList&, bool); + + // get the actual listbox index of the optionIndexth option + int optionToListIndex(int optionIndex) const; + // reverse of optionToListIndex - get optionIndex from listboxIndex + int listToOptionIndex(int listIndex) const; + + void setRecalcListItems(); + + QMemArray<HTMLGenericFormElementImpl*> listItems() const + { + if (m_recalcListItems) const_cast<HTMLSelectElementImpl*>(this)->recalcListItems(); + return m_listItems; + } + virtual void reset(); + void notifyOptionSelected(HTMLOptionElementImpl *selectedOption, bool selected); + +private: + void recalcListItems() const; + +protected: + mutable QMemArray<HTMLGenericFormElementImpl*> m_listItems; + short m_minwidth; + signed short m_size : 15; + bool m_multiple : 1; + mutable bool m_recalcListItems :1; + mutable unsigned int m_length:31; +}; + +// ------------------------------------------------------------------------- + +class HTMLKeygenElementImpl : public HTMLSelectElementImpl +{ +public: + HTMLKeygenElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + + virtual Id id() const; + + DOMString type() const; + + long selectedIndex() const; + void setSelectedIndex( long index ); + + // ### this is just a rough guess + virtual bool isEnumeratable() const { return false; } + + virtual void parseAttribute(AttributeImpl *attr); + virtual bool encoding(const QTextCodec*, khtml::encodingList&, bool); + +}; + +// ------------------------------------------------------------------------- + +class HTMLOptGroupElementImpl : public HTMLGenericFormElementImpl +{ +public: + HTMLOptGroupElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0) + : HTMLGenericFormElementImpl(doc, f) {} + + virtual Id id() const; +}; + + +// --------------------------------------------------------------------------- + +class HTMLOptionElementImpl : public HTMLGenericFormElementImpl +{ + friend class khtml::RenderSelect; + friend class DOM::HTMLSelectElementImpl; + +public: + HTMLOptionElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + + virtual Id id() const; + + DOMString text() const; + + long index() const; + void setIndex( long ); + virtual void parseAttribute(AttributeImpl *attr); + DOMString value() const; + void setValue(DOMStringImpl* value); + + bool selected() const { return m_selected; } + void setSelected(bool _selected); + + HTMLSelectElementImpl *getSelect() const; + +protected: + DOMString m_value; + bool m_selected; +}; + + +// ------------------------------------------------------------------------- + +class HTMLTextAreaElementImpl : public HTMLGenericFormElementImpl +{ + friend class khtml::RenderTextArea; + +public: + enum WrapMethod { + ta_NoWrap, + ta_Virtual, + ta_Physical + }; + + HTMLTextAreaElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + ~HTMLTextAreaElementImpl(); + + virtual Id id() const; + + long cols() const { return m_cols; } + + long rows() const { return m_rows; } + + WrapMethod wrap() const { return m_wrap; } + + virtual bool isEnumeratable() const { return true; } + + DOMString type() const; + + virtual bool maintainsState() { return true; } + virtual QString state(); + virtual void restoreState(const QString &state); + + void select ( ); + + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); + virtual bool encoding(const QTextCodec*, khtml::encodingList&, bool); + virtual void reset(); + DOMString value(); + void setValue(DOMString _value); + DOMString defaultValue(); + void setDefaultValue(DOMString _defaultValue); + void blur(); + void focus(); + + virtual bool isEditable(); + void setUnsubmittedFormChange(bool unsubmitted) { m_unsubmittedFormChange = unsubmitted; } + + //Mozilla extensions. + long selectionStart(); + long selectionEnd(); + void setSelectionStart(long pos); + void setSelectionEnd (long pos); + void setSelectionRange(long start, long end); + long textLength(); +protected: + int m_rows; + int m_cols; + WrapMethod m_wrap; + QString m_value; + bool m_changed: 1; //States whether the contents has been editted + bool m_dirtyvalue: 1; //States whether m_value is out-of-date compared to the renderer or default + bool m_unsubmittedFormChange: 1; + bool m_initialized: 1; +}; + +// ------------------------------------------------------------------------- + +class HTMLIsIndexElementImpl : public HTMLInputElementImpl +{ +public: + HTMLIsIndexElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + ~HTMLIsIndexElementImpl(); + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); + + DOMString prompt() const; + void setPrompt(const DOMString& _value); +}; + + +} //namespace + +#endif diff --git a/khtml/html/html_headimpl.cpp b/khtml/html/html_headimpl.cpp new file mode 100644 index 000000000..6808f27bf --- /dev/null +++ b/khtml/html/html_headimpl.cpp @@ -0,0 +1,649 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2002-2003 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +// ------------------------------------------------------------------------- + +#include "html/html_headimpl.h" +#include "html/html_documentimpl.h" +#include "xml/dom_textimpl.h" + +#include "khtmlview.h" +#include "khtml_part.h" + +#include "misc/htmlhashes.h" +#include "misc/loader.h" +#include "misc/helper.h" + +#include "css/cssstyleselector.h" +#include "css/css_stylesheetimpl.h" +#include "css/csshelper.h" + +#include "ecma/kjs_proxy.h" + +#include <kurl.h> +#include <kdebug.h> + +using namespace khtml; + +NodeImpl::Id HTMLBaseElementImpl::id() const +{ + return ID_BASE; +} + +void HTMLBaseElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_HREF: + m_href = khtml::parseURL(attr->value()); + process(); + break; + case ATTR_TARGET: + m_target = attr->value(); + process(); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLBaseElementImpl::insertedIntoDocument() +{ + HTMLElementImpl::insertedIntoDocument(); + process(); +} + +void HTMLBaseElementImpl::removedFromDocument() +{ + HTMLElementImpl::removedFromDocument(); + + // Since the document doesn't have a base element... + // (This will break in the case of multiple base elements, but that's not valid anyway (?)) + getDocument()->setBaseURL( KURL() ); + getDocument()->setBaseTarget( QString::null ); +} + +void HTMLBaseElementImpl::process() +{ + if (!inDocument()) + return; + + if(!m_href.isEmpty() && getDocument()->view()) + getDocument()->setBaseURL( KURL( getDocument()->view()->part()->url(), m_href.string() ) ); + + if(!m_target.isEmpty()) + getDocument()->setBaseTarget( m_target.string() ); + + // ### should changing a document's base URL dynamically automatically update all images, stylesheets etc? +} + +// ------------------------------------------------------------------------- + + +HTMLLinkElementImpl::~HTMLLinkElementImpl() +{ + if(m_sheet) m_sheet->deref(); + if(m_cachedSheet) m_cachedSheet->deref(this); +} + +NodeImpl::Id HTMLLinkElementImpl::id() const +{ + return ID_LINK; +} + +void HTMLLinkElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch (attr->id()) + { + case ATTR_HREF: + m_url = getDocument()->completeURL( khtml::parseURL(attr->value()).string() ); + process(); + break; + case ATTR_REL: + case ATTR_TYPE: + process(); + break; + case ATTR_TITLE: + // ### when title changes we have to reconsider our alternative + // stylesheet choice + break; + case ATTR_MEDIA: + m_media = attr->value().string().lower(); + process(); + break; + case ATTR_DISABLED: { + bool m_oldisDisabled = m_isDisabled; + m_isDisabled = attr->val(); + if (m_oldisDisabled != m_isDisabled) { + if (isLoading()) { + if (m_oldisDisabled) + getDocument()->addPendingSheet(); + else if (!m_alternate) + getDocument()->styleSheetLoaded(); + } + if (m_oldisDisabled) { + // enabling: if it's an alternate sheet, pretend it's not. + m_alternate = false; + } else if (!m_alternate) { + // disabling: recheck alternate status + QString rel = getAttribute(ATTR_REL).string().lower(); + QString type = getAttribute(ATTR_TYPE).string().lower(); + m_alternate = (type.contains("text/css") || rel.contains("stylesheet")) && rel.contains("alternate"); + } + if (isLoading()) + break; + if (!m_sheet && !m_isDisabled) { + process(); + if (isLoading() && m_alternate) + getDocument()->addPendingSheet(); + m_alternate = false; + } else + getDocument()->updateStyleSelector(); // Update the style selector. + } + break; + } + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLLinkElementImpl::process() +{ + if (!inDocument()) + return; + + QString type = getAttribute(ATTR_TYPE).string().lower(); + QString rel = getAttribute(ATTR_REL).string().lower(); + + KHTMLPart* part = getDocument()->view() ? getDocument()->view()->part() : 0; + + // IE extension: location of small icon for locationbar / bookmarks + // Uses both "shortcut icon" and "icon" + if ( part && rel.contains("icon") && !m_url.isEmpty() && !part->parentPart()) + part->browserExtension()->setIconURL( KURL(m_url.string()) ); + + // Stylesheet + else if (!m_isDisabled && + (type.contains("text/css") || rel.contains("stylesheet"))) { + // no need to load style sheets which aren't for the screen output + // ### there may be in some situations e.g. for an editor or script to manipulate + if( m_media.isNull() || m_media.contains("screen") || m_media.contains("all") || m_media.contains("print") ) { + m_loading = true; + // Add ourselves as a pending sheet, but only if we aren't an alternate + // stylesheet. Alternate stylesheets don't hold up render tree construction. + m_alternate = rel.contains("alternate"); + if (!isAlternate()) + getDocument()->addPendingSheet(); + + QString chset = getAttribute( ATTR_CHARSET ).string(); + // set chset to charset of referring document when attribute CHARSET is absent. + // http://www.w3.org/TR/CSS21/syndata.html(4.4) + if (chset.isEmpty() && part) chset = part->encoding(); + if (m_cachedSheet) + m_cachedSheet->deref(this); + m_cachedSheet = getDocument()->docLoader()->requestStyleSheet(m_url, chset); + if (m_cachedSheet) { + m_isCSSSheet = true; + m_cachedSheet->ref(this); + } + else if (!isAlternate()) { + // Error requesting sheet; decrement pending sheet count + getDocument()->styleSheetLoaded(); + } + } + } + else if (m_sheet) { + // we no longer contain a stylesheet, e.g. perhaps rel or type was changed + m_sheet->deref(); + m_sheet = 0; + m_isCSSSheet = false; + getDocument()->updateStyleSelector(); + } +} + +void HTMLLinkElementImpl::insertedIntoDocument() +{ + HTMLElementImpl::insertedIntoDocument(); + process(); +} + +void HTMLLinkElementImpl::removedFromDocument() +{ + HTMLElementImpl::removedFromDocument(); + getDocument()->updateStyleSelector(); +} + +void HTMLLinkElementImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheetStr, const DOM::DOMString &charset) +{ + if (m_sheet) + m_sheet->deref(); + m_sheet = new CSSStyleSheetImpl(this, url); + m_sheet->ref(); + m_sheet->setCharset(charset); + m_sheet->parseString( sheetStr, !getDocument()->inCompatMode() ); + + MediaListImpl *media = new MediaListImpl( m_sheet, m_media ); + m_sheet->setMedia( media ); + + finished(); +} + +void HTMLLinkElementImpl::finished() +{ + m_loading = false; + + // Tell the doc about the sheet. + if (!isLoading() && !isDisabled() && !isAlternate()) + getDocument()->styleSheetLoaded(); + + // ### major inefficiency, but currently necessary for proper + // alternate styles support. don't recalc the styleselector + // when nothing actually changed! + if ( isAlternate() && m_sheet && !isDisabled()) + getDocument()->updateStyleSelector(); +} + +void HTMLLinkElementImpl::error( int, const QString& ) +{ + finished(); +} + +bool HTMLLinkElementImpl::isLoading() const +{ +// kdDebug( 6030 ) << "link: checking if loading!" << endl; + if(m_loading) return true; + if(!m_sheet) return false; + //if(!m_sheet->isCSSStyleSheet()) return false; + return static_cast<CSSStyleSheetImpl *>(m_sheet)->isLoading(); +} + +void HTMLLinkElementImpl::sheetLoaded() +{ + if (!isLoading() && !isDisabled() && !isAlternate()) + getDocument()->styleSheetLoaded(); +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLMetaElementImpl::id() const +{ + return ID_META; +} + +void HTMLMetaElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_HTTP_EQUIV: + m_equiv = attr->value(); + process(); + break; + case ATTR_CONTENT: + m_content = attr->value(); + process(); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLMetaElementImpl::insertedIntoDocument() +{ + HTMLElementImpl::insertedIntoDocument(); + process(); +} + +void HTMLMetaElementImpl::process() +{ + // Get the document to process the tag, but only if we're actually part of DOM tree (changing a meta tag while + // it's not in the tree shouldn't have any effect on the document) + if (inDocument() && !m_equiv.isNull() && !m_content.isNull()) + getDocument()->processHttpEquiv(m_equiv,m_content); +} + +// ------------------------------------------------------------------------- + +HTMLScriptElementImpl::HTMLScriptElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc), m_cachedScript(0), m_createdByParser(false), m_evaluated(false) +{ +} + +HTMLScriptElementImpl::~HTMLScriptElementImpl() +{ + if (m_cachedScript) + m_cachedScript->deref(this); +} + +NodeImpl::Id HTMLScriptElementImpl::id() const +{ + return ID_SCRIPT; +} + +bool HTMLScriptElementImpl::isURLAttribute(AttributeImpl *attr) const +{ + return attr->id() == ATTR_SRC; +} + +void HTMLScriptElementImpl::childrenChanged() +{ + // If a node is inserted as a child of the script element + // and the script element has been inserted in the document + // we evaluate the script. + if (!m_createdByParser && inDocument() && firstChild()) + evaluateScript(getDocument()->URL().url(), text()); +} + +void HTMLScriptElementImpl::insertedIntoDocument() +{ + HTMLElementImpl::insertedIntoDocument(); + + assert(!m_cachedScript); + + if (m_createdByParser) + return; + + QString url = getAttribute(ATTR_SRC).string(); + if (!url.isEmpty()) { + QString charset = getAttribute(ATTR_CHARSET).string(); + m_cachedScript = getDocument()->docLoader()->requestScript(DOMString(url), charset); + if (m_cachedScript) + m_cachedScript->ref(this); + return; + } + + // If there's an empty script node, we shouldn't evaluate the script + // because if a script is inserted afterwards (by setting text or innerText) + // it should be evaluated, and evaluateScript only evaluates a script once. + DOMString scriptString = text(); + if (!scriptString.isEmpty()) + evaluateScript(getDocument()->URL().url(), scriptString); +} + +void HTMLScriptElementImpl::removedFromDocument() +{ + HTMLElementImpl::removedFromDocument(); + + if (m_cachedScript) { + m_cachedScript->deref(this); + m_cachedScript = 0; + } +} + +void HTMLScriptElementImpl::notifyFinished(CachedObject* o) +{ + CachedScript *cs = static_cast<CachedScript *>(o); + + assert(cs == m_cachedScript); + + QString URL = cs->url().string(); + DOMString script = cs->script(); + cs->deref(this); + m_cachedScript = 0; + + evaluateScript(URL, script); +} + +void HTMLScriptElementImpl::evaluateScript(const QString &URL, const DOMString &script) +{ + if (m_evaluated) + return; + + KHTMLPart *part = getDocument()->part(); + if (part) { + KJSProxy *proxy = KJSProxy::proxy(part); + if (proxy) { + m_evaluated = true; + proxy->evaluate(URL, 0, script.string(), 0); + DocumentImpl::updateDocumentsRendering(); + } + } +} + +DOMString HTMLScriptElementImpl::text() const +{ + DOMString val = ""; + + for (NodeImpl *n = firstChild(); n; n = n->nextSibling()) { + if (n->isTextNode()) + val += static_cast<TextImpl *>(n)->data(); + } + + return val; +} + +void HTMLScriptElementImpl::setText(const DOMString &value) +{ + int exceptioncode = 0; + int numChildren = childNodeCount(); + + if (numChildren == 1 && firstChild()->isTextNode()) { + static_cast<DOM::TextImpl *>(firstChild())->setData(value, exceptioncode); + return; + } + + if (numChildren > 0) { + removeChildren(); + } + + appendChild(getDocument()->createTextNode(value.implementation()), exceptioncode); +} + +DOMString HTMLScriptElementImpl::htmlFor() const +{ + // DOM Level 1 says: reserved for future use. + return DOMString(); +} + +void HTMLScriptElementImpl::setHtmlFor(const DOMString &/*value*/) +{ + // DOM Level 1 says: reserved for future use. +} + +DOMString HTMLScriptElementImpl::event() const +{ + // DOM Level 1 says: reserved for future use. + return DOMString(); +} + +void HTMLScriptElementImpl::setEvent(const DOMString &/*value*/) +{ + // DOM Level 1 says: reserved for future use. +} + +DOMString HTMLScriptElementImpl::charset() const +{ + return getAttribute(ATTR_CHARSET); +} + +void HTMLScriptElementImpl::setCharset(const DOMString &value) +{ + setAttribute(ATTR_CHARSET, value); +} + +bool HTMLScriptElementImpl::defer() const +{ + return !getAttribute(ATTR_DEFER).isNull(); +} + +void HTMLScriptElementImpl::setDefer(bool defer) +{ + setAttribute(ATTR_DEFER, defer ? "" : 0); +} + +DOMString HTMLScriptElementImpl::src() const +{ + return getDocument()->completeURL(getAttribute(ATTR_SRC).string()); +} + +void HTMLScriptElementImpl::setSrc(const DOMString &value) +{ + setAttribute(ATTR_SRC, value); +} + +DOMString HTMLScriptElementImpl::type() const +{ + return getAttribute(ATTR_TYPE); +} + +void HTMLScriptElementImpl::setType(const DOMString &value) +{ + setAttribute(ATTR_TYPE, value); +} + +// ------------------------------------------------------------------------- + +HTMLStyleElementImpl::~HTMLStyleElementImpl() +{ + if(m_sheet) m_sheet->deref(); +} + +NodeImpl::Id HTMLStyleElementImpl::id() const +{ + return ID_STYLE; +} + +// other stuff... +void HTMLStyleElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch (attr->id()) + { + case ATTR_TYPE: + m_type = attr->value().lower(); + break; + case ATTR_MEDIA: + m_media = attr->value().string().lower(); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLStyleElementImpl::insertedIntoDocument() +{ + HTMLElementImpl::insertedIntoDocument(); + if (m_sheet) + getDocument()->updateStyleSelector(); +} + +void HTMLStyleElementImpl::removedFromDocument() +{ + HTMLElementImpl::removedFromDocument(); + if (m_sheet) + getDocument()->updateStyleSelector(); +} + +void HTMLStyleElementImpl::childrenChanged() +{ + HTMLElementImpl::childrenChanged(); + + DOMString text = ""; + + for (NodeImpl *c = firstChild(); c != 0; c = c->nextSibling()) { + if ((c->nodeType() == Node::TEXT_NODE) || + (c->nodeType() == Node::CDATA_SECTION_NODE) || + (c->nodeType() == Node::COMMENT_NODE)) + text += c->nodeValue(); + } + + if (m_sheet) { + m_sheet->deref(); + m_sheet = 0; + } + + m_loading = false; + if ((m_type.isEmpty() || m_type == "text/css") // Type must be empty or CSS + && (m_media.isNull() || m_media.contains("screen") || m_media.contains("all") || m_media.contains("print"))) { + getDocument()->addPendingSheet(); + m_loading = true; + m_sheet = new CSSStyleSheetImpl(this); + m_sheet->ref(); + m_sheet->parseString( text, !getDocument()->inCompatMode() ); + MediaListImpl* media = new MediaListImpl( m_sheet, DOMString( m_media ) ); + m_sheet->setMedia( media ); + m_loading = false; + } + + if (!isLoading() && m_sheet) + getDocument()->styleSheetLoaded(); +} + +bool HTMLStyleElementImpl::isLoading() const +{ + if (m_loading) return true; + if(!m_sheet) return false; + return static_cast<CSSStyleSheetImpl *>(m_sheet)->isLoading(); +} + +void HTMLStyleElementImpl::sheetLoaded() +{ + if (!isLoading()) + getDocument()->styleSheetLoaded(); +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLTitleElementImpl::id() const +{ + return ID_TITLE; +} + +void HTMLTitleElementImpl::childrenChanged() +{ + HTMLElementImpl::childrenChanged(); + + m_title = ""; + for (NodeImpl *c = firstChild(); c != 0; c = c->nextSibling()) { + if ((c->nodeType() == Node::TEXT_NODE) || (c->nodeType() == Node::CDATA_SECTION_NODE)) + m_title += c->nodeValue(); + } + if ( !m_title.isEmpty() && inDocument()) + getDocument()->setTitle(m_title); +} + +DOMString HTMLTitleElementImpl::text() +{ + if (firstChild() && firstChild()->nodeType() == Node::TEXT_NODE) { + return firstChild()->nodeValue(); + } + return ""; +} + +void HTMLTitleElementImpl::setText( const DOMString& str ) +{ + int exceptioncode = 0; + // Look for an existing text child node + DOM::NodeListImpl* nl(childNodes()); + if (nl) + { + for (unsigned int i = 0; i < nl->length(); i++) { + if (nl->item(i)->nodeType() == DOM::Node::TEXT_NODE) { + static_cast<DOM::TextImpl *>(nl->item(i))->setData(str, exceptioncode); + return; + } + } + delete nl; + } + // No child text node found, creating one + DOM::TextImpl* t = getDocument()->createTextNode(str.implementation()); + appendChild(t, exceptioncode); +} diff --git a/khtml/html/html_headimpl.h b/khtml/html/html_headimpl.h new file mode 100644 index 000000000..3fa72c928 --- /dev/null +++ b/khtml/html/html_headimpl.h @@ -0,0 +1,233 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2002-2003 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_HEADIMPL_H +#define HTML_HEADIMPL_H + +#include "html/html_elementimpl.h" +#include "misc/loader_client.h" +#include "css/css_stylesheetimpl.h" + +class KHTMLView; + +namespace khtml { + class CachedCSSStyleSheet; + class CachedObject; +} + + +namespace DOM { + +class DOMString; +class HTMLFormElementImpl; +class StyleSheetImpl; +class CSSStyleSheetImpl; + +class HTMLBaseElementImpl : public HTMLElementImpl +{ +public: + HTMLBaseElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + DOMString href() const { return m_href; } + DOMString target() const { return m_target; } + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + void process(); + +protected: + DOMString m_href; + DOMString m_target; +}; + + + +// ------------------------------------------------------------------------- + +class HTMLLinkElementImpl : public khtml::CachedObjectClient, public HTMLElementImpl +{ +public: + HTMLLinkElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc), m_cachedSheet(0), m_sheet(0), m_isDisabled(false), + m_loading(false), m_alternate(false), m_isCSSSheet(false) {} + + ~HTMLLinkElementImpl(); + + virtual Id id() const; + + const StyleSheetImpl* sheet() const { return m_sheet; } + StyleSheetImpl* sheet() { return m_sheet; } + + // overload from HTMLElementImpl + virtual void parseAttribute(AttributeImpl *attr); + + void process(); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + // from CachedObjectClient + virtual void setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet, const DOM::DOMString &charset); + virtual void error(int err, const QString &text); + bool isLoading() const; + void sheetLoaded(); + + bool isAlternate() const { return m_alternate; } + bool isCSSStyleSheet() const { return m_isCSSSheet; } + bool isDisabled() const { return m_isDisabled; } + void setDisabled(bool disabled) { m_isDisabled = disabled; } + +protected: + void finished(); + + khtml::CachedCSSStyleSheet *m_cachedSheet; + CSSStyleSheetImpl *m_sheet; + DOMString m_url; + QString m_media; + bool m_isDisabled : 1; + bool m_loading : 1; + bool m_alternate : 1; + bool m_isCSSSheet : 1; +}; + +// ------------------------------------------------------------------------- + +class HTMLMetaElementImpl : public HTMLElementImpl +{ +public: + HTMLMetaElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); + virtual void insertedIntoDocument(); + + void process(); + +protected: + DOMString m_equiv; + DOMString m_content; +}; + +// ------------------------------------------------------------------------- + +class HTMLScriptElementImpl : public HTMLElementImpl, public khtml::CachedObjectClient +{ +public: + HTMLScriptElementImpl(DocumentImpl *doc); + ~HTMLScriptElementImpl(); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void notifyFinished(khtml::CachedObject *finishedObj); + virtual void childrenChanged(); + + virtual Id id() const; + virtual bool isURLAttribute(AttributeImpl *attr) const; + + void setCreatedByParser(bool createdByParser) { m_createdByParser = createdByParser; } + + void evaluateScript(const QString &, const DOMString &); + + DOMString text() const; + void setText( const DOMString& str ); + + DOMString htmlFor() const; + void setHtmlFor(const DOMString &); + + DOMString event() const; + void setEvent(const DOMString &); + + DOMString charset() const; + void setCharset(const DOMString &); + + bool defer() const; + void setDefer(bool); + + DOMString src() const; + void setSrc(const DOMString &); + + DOMString type() const; + void setType(const DOMString &); + +private: + khtml::CachedScript *m_cachedScript; + bool m_createdByParser; + bool m_evaluated; +}; + +// ------------------------------------------------------------------------- + +class HTMLStyleElementImpl : public HTMLElementImpl +{ +public: + HTMLStyleElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc), m_sheet(0), m_loading(false) {} + ~HTMLStyleElementImpl(); + + virtual Id id() const; + + CSSStyleSheetImpl *sheet() { return m_sheet; } + + // overload from HTMLElementImpl + virtual void parseAttribute(AttributeImpl *attr); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void childrenChanged(); + + bool isLoading() const; + void sheetLoaded(); + +protected: + CSSStyleSheetImpl *m_sheet; + DOMString m_type; + QString m_media; + bool m_loading; +}; + +// ------------------------------------------------------------------------- + +class HTMLTitleElementImpl : public HTMLElementImpl +{ +public: + HTMLTitleElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + DOMString text(); + void setText( const DOMString& str ); + + virtual Id id() const; + + virtual void childrenChanged(); + +protected: + DOMString m_title; +}; + +} //namespace + +#endif diff --git a/khtml/html/html_imageimpl.cpp b/khtml/html/html_imageimpl.cpp new file mode 100644 index 000000000..1c755920b --- /dev/null +++ b/khtml/html/html_imageimpl.cpp @@ -0,0 +1,560 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "html/html_imageimpl.h" +#include "html/html_formimpl.h" +#include "html/html_documentimpl.h" + +#include "misc/htmlhashes.h" +#include "khtmlview.h" +#include "khtml_part.h" + +#include <kstringhandler.h> +#include <kglobal.h> +#include <kdebug.h> + +#include "rendering/render_image.h" +#include "rendering/render_flow.h" +#include "css/cssstyleselector.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "css/csshelper.h" +#include "xml/dom2_eventsimpl.h" + +#include <qstring.h> +#include <qpoint.h> +#include <qregion.h> +#include <qptrstack.h> +#include <qimage.h> +#include <qpointarray.h> + +using namespace DOM; +using namespace khtml; + +// ------------------------------------------------------------------------- + +HTMLImageElementImpl::HTMLImageElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f) + : HTMLElementImpl(doc), ismap(false), loadEventSent(true), m_image(0), m_form(f) +{ + if (m_form) + m_form->registerImgElement(this); +} + +HTMLImageElementImpl::~HTMLImageElementImpl() +{ + if (getDocument()) + getDocument()->removeImage(this); + + if (m_image) + m_image->deref(this); + + if (m_form) + m_form->removeImgElement(this); +} + +NodeImpl::Id HTMLImageElementImpl::id() const +{ + return ID_IMG; +} + +void HTMLImageElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch (attr->id()) + { + case ATTR_ALT: + setChanged(); + break; + case ATTR_SRC: { + setChanged(); + + //Start loading the image already, to generate events + DOMString url = attr->value(); + if (!url.isEmpty()) { //### why do we not hide or something when setting this? + CachedImage* newImage = getDocument()->docLoader()->requestImage(khtml::parseURL(url)); + if (newImage && newImage != m_image) { + CachedImage* oldImage = m_image; + loadEventSent = false; + m_image = newImage; + m_image->ref(this); + if (oldImage) + oldImage->deref(this); + } + } + } + break; + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_WIDTH, attr->value()); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_HEIGHT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_HEIGHT, attr->value()); + else + removeCSSProperty(CSS_PROP_HEIGHT); + break; + case ATTR_BORDER: + // border="noborder" -> border="0" + if(attr->value().toInt()) { + addCSSLength(CSS_PROP_BORDER_WIDTH, attr->value()); + addCSSProperty( CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID ); + addCSSProperty( CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID ); + addCSSProperty( CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID ); + addCSSProperty( CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID ); + } else { + removeCSSProperty( CSS_PROP_BORDER_WIDTH ); + removeCSSProperty( CSS_PROP_BORDER_TOP_STYLE ); + removeCSSProperty( CSS_PROP_BORDER_RIGHT_STYLE ); + removeCSSProperty( CSS_PROP_BORDER_BOTTOM_STYLE ); + removeCSSProperty( CSS_PROP_BORDER_LEFT_STYLE ); + } + break; + case ATTR_VSPACE: + addCSSLength(CSS_PROP_MARGIN_TOP, attr->value()); + addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value()); + break; + case ATTR_HSPACE: + addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value()); + addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value()); + break; + case ATTR_ALIGN: + addHTMLAlignment( attr->value() ); + break; + case ATTR_VALIGN: + addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower()); + break; + case ATTR_USEMAP: + if ( attr->value()[0] == '#' ) + usemap = attr->value().lower(); + else { + QString url = getDocument()->completeURL( khtml::parseURL( attr->value() ).string() ); + // ### we remove the part before the anchor and hope + // the map is on the same html page.... + usemap = url; + } + m_hasAnchor = attr->val() != 0; + case ATTR_ISMAP: + ismap = true; + break; + case ATTR_ONABORT: // ### add support for this + setHTMLEventListener(EventImpl::ABORT_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onabort", this)); + break; + case ATTR_ONERROR: + setHTMLEventListener(EventImpl::ERROR_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onerror", this)); + break; + case ATTR_ONLOAD: + setHTMLEventListener(EventImpl::LOAD_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onload", this)); + break; + case ATTR_NOSAVE: + break; + case ATTR_NAME: + if (inDocument() && m_name != attr->value()) { + getDocument()->underDocNamedCache().remove(m_name.string(), this); + getDocument()->underDocNamedCache().add (attr->value().string(), this); + } + m_name = attr->value(); + //fallthrough + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLImageElementImpl::notifyFinished(CachedObject *finishedObj) +{ + if (m_image == finishedObj) { + getDocument()->dispatchImageLoadEventSoon(this); + } +} + +void HTMLImageElementImpl::dispatchLoadEvent() +{ + if (!loadEventSent) { + loadEventSent = true; + if (m_image->isErrorImage()) { + dispatchHTMLEvent(EventImpl::ERROR_EVENT, false, false); + } else { + dispatchHTMLEvent(EventImpl::LOAD_EVENT, false, false); + } + } +} + +DOMString HTMLImageElementImpl::altText() const +{ + // lets figure out the alt text.. magic stuff + // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen + // also heavily discussed by Hixie on bugzilla + DOMString alt( getAttribute( ATTR_ALT ) ); + // fall back to title attribute + if ( alt.isNull() ) + alt = getAttribute( ATTR_TITLE ); +#if 0 + if ( alt.isNull() ) { + QString p = KURL( getDocument()->completeURL( getAttribute(ATTR_SRC).string() ) ).prettyURL(); + int pos; + if ( ( pos = p.findRev( '.' ) ) > 0 ) + p.truncate( pos ); + alt = DOMString( KStringHandler::csqueeze( p ) ); + } +#endif + + return alt; +} + +void HTMLImageElementImpl::attach() +{ + assert(!attached()); + assert(!m_render); + assert(parentNode()); + + RenderStyle* _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() && + _style->display() != NONE) + { + m_render = new (getDocument()->renderArena()) RenderImage(this); + m_render->setStyle(_style); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + } + _style->deref(); + + NodeBaseImpl::attach(); + if (m_render) + m_render->updateFromElement(); +} + +void HTMLImageElementImpl::removedFromDocument() +{ + getDocument()->underDocNamedCache().remove(m_name.string(), this); + HTMLElementImpl::removedFromDocument(); +} + +void HTMLImageElementImpl::insertedIntoDocument() +{ + getDocument()->underDocNamedCache().add(m_name.string(), this); + HTMLElementImpl::insertedIntoDocument(); +} + +void HTMLImageElementImpl::removeId(const QString& id) +{ + getDocument()->underDocNamedCache().remove(id, this); + HTMLElementImpl::removeId(id); +} + +void HTMLImageElementImpl::addId (const QString& id) +{ + getDocument()->underDocNamedCache().add(id, this); + HTMLElementImpl::addId(id); +} + + +long HTMLImageElementImpl::width() const +{ + if (!m_render) { + DOMString widthAttr = getAttribute(ATTR_WIDTH); + if (!widthAttr.isNull()) + return widthAttr.toInt(); + else if (m_image && m_image->pixmap_size().isValid()) + return m_image->pixmap_size().width(); + else + return 0; + } + + // ### make a unified call for this + if (changed()) { + getDocument()->updateRendering(); + if (getDocument()->view()) + getDocument()->view()->layout(); + } + + return m_render ? m_render->contentWidth() : + getAttribute(ATTR_WIDTH).toInt(); +} + +long HTMLImageElementImpl::height() const +{ + if (!m_render) { + DOMString heightAttr = getAttribute(ATTR_HEIGHT); + if (!heightAttr.isNull()) + return heightAttr.toInt(); + else if (m_image && m_image->pixmap_size().isValid()) + return m_image->pixmap_size().height(); + else + return 0; + } + + // ### make a unified call for this + if (changed()) { + getDocument()->updateRendering(); + if (getDocument()->view()) + getDocument()->view()->layout(); + } + + return m_render ? m_render->contentHeight() : + getAttribute(ATTR_HEIGHT).toInt(); +} + +QImage HTMLImageElementImpl::currentImage() const +{ + RenderImage *r = static_cast<RenderImage*>(renderer()); + + if(r) + return r->pixmap().convertToImage(); + + return QImage(); +} + +QPixmap HTMLImageElementImpl::currentPixmap() const +{ + RenderImage *r = static_cast<RenderImage*>(renderer()); + + if(r) + return r->pixmap(); + + return QPixmap(); +} + +bool HTMLImageElementImpl::complete() const +{ + return m_image && m_image->valid_rect().size() == m_image->pixmap_size(); +} + +// ------------------------------------------------------------------------- + +HTMLMapElementImpl::HTMLMapElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) +{ +} + +HTMLMapElementImpl::~HTMLMapElementImpl() +{ + if(getDocument() && getDocument()->isHTMLDocument()) + static_cast<HTMLDocumentImpl*>(getDocument())->mapMap.remove(name); +} + +NodeImpl::Id HTMLMapElementImpl::id() const +{ + return ID_MAP; +} + +bool +HTMLMapElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_, + RenderObject::NodeInfo& info) +{ + //cout << "map:mapMouseEvent " << endl; + //cout << x_ << " " << y_ <<" "<< width_ <<" "<< height_ << endl; + QPtrStack<NodeImpl> nodeStack; + + NodeImpl *current = firstChild(); + while(1) + { + if(!current) + { + if(nodeStack.isEmpty()) break; + current = nodeStack.pop(); + current = current->nextSibling(); + continue; + } + if(current->id()==ID_AREA) + { + //cout << "area found " << endl; + HTMLAreaElementImpl* area=static_cast<HTMLAreaElementImpl*>(current); + if (area->mapMouseEvent(x_,y_,width_,height_, info)) + return true; + } + NodeImpl *child = current->firstChild(); + if(child) + { + nodeStack.push(current); + current = child; + } + else + { + current = current->nextSibling(); + } + } + + return false; +} + +void HTMLMapElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch (attr->id()) + { + case ATTR_ID: + if (getDocument()->htmlMode() != DocumentImpl::XHtml) { + HTMLElementImpl::parseAttribute(attr); + break; + } + else { + // add name with full url: + QString url = getDocument()->completeURL( khtml::parseURL( attr->value() ).string() ); + if(getDocument()->isHTMLDocument()) + static_cast<HTMLDocumentImpl*>(getDocument())->mapMap[url] = this; + } + // fall through + case ATTR_NAME: + { + DOMString s = attr->value(); + if(*s.unicode() == '#') + name = QString(s.unicode()+1, s.length()-1).lower(); + else + name = s.string().lower(); + // ### make this work for XML documents, e.g. in case of <html:map...> + if(getDocument()->isHTMLDocument()) + static_cast<HTMLDocumentImpl*>(getDocument())->mapMap[name] = this; + + //fallthrough + } + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------- + +HTMLAreaElementImpl::HTMLAreaElementImpl(DocumentImpl *doc) + : HTMLAnchorElementImpl(doc) +{ + m_coords=0; + m_coordsLen = 0; + nohref = false; + shape = Unknown; + lasth = lastw = -1; +} + +HTMLAreaElementImpl::~HTMLAreaElementImpl() +{ + delete [] m_coords; +} + +NodeImpl::Id HTMLAreaElementImpl::id() const +{ + return ID_AREA; +} + +void HTMLAreaElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch (attr->id()) + { + case ATTR_SHAPE: + if ( strcasecmp( attr->value(), "default" ) == 0 ) + shape = Default; + else if ( strcasecmp( attr->value(), "circle" ) == 0 ) + shape = Circle; + else if ( strcasecmp( attr->value(), "poly" ) == 0 || strcasecmp( attr->value(), "polygon" ) == 0 ) + shape = Poly; + else if ( strcasecmp( attr->value(), "rect" ) == 0 ) + shape = Rect; + break; + case ATTR_COORDS: + delete [] m_coords; + m_coords = attr->val()->toCoordsArray(m_coordsLen); + break; + case ATTR_NOHREF: + nohref = attr->val() != 0; + break; + case ATTR_TARGET: + m_hasTarget = attr->val() != 0; + break; + case ATTR_ALT: + break; + case ATTR_ACCESSKEY: + break; + default: + HTMLAnchorElementImpl::parseAttribute(attr); + } +} + +bool HTMLAreaElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_, + RenderObject::NodeInfo& info) +{ + bool inside = false; + if (width_ != lastw || height_ != lasth) + { + region=getRegion(width_, height_); + lastw=width_; lasth=height_; + } + if (region.contains(QPoint(x_,y_))) + { + inside = true; + info.setInnerNode(this); + info.setURLElement(this); + } + + return inside; +} + +QRect HTMLAreaElementImpl::getRect() const +{ + if (parentNode()->renderer()==0) + return QRect(); + int dx, dy; + if (!parentNode()->renderer()->absolutePosition(dx, dy)) + return QRect(); + QRegion region = getRegion(lastw,lasth); + region.translate(dx, dy); + return region.boundingRect(); +} + +QRegion HTMLAreaElementImpl::getRegion(int width_, int height_) const +{ + QRegion region; + if (!m_coords) + return region; + + // added broken HTML support (Dirk): some pages omit the SHAPE + // attribute, so we try to guess by looking at the coords count + // what the HTML author tried to tell us. + + // a Poly needs at least 3 points (6 coords), so this is correct + if ((shape==Poly || shape==Unknown) && m_coordsLen > 5) { + // make sure its even + int len = m_coordsLen >> 1; + QPointArray points(len); + for (int i = 0; i < len; ++i) + points.setPoint(i, m_coords[(i<<1)].minWidth(width_), + m_coords[(i<<1)+1].minWidth(height_)); + region = QRegion(points); + } + else if (shape==Circle && m_coordsLen>=3 || shape==Unknown && m_coordsLen == 3) { + int r = kMin(m_coords[2].minWidth(width_), m_coords[2].minWidth(height_)); + region = QRegion(m_coords[0].minWidth(width_)-r, + m_coords[1].minWidth(height_)-r, 2*r, 2*r,QRegion::Ellipse); + } + else if (shape==Rect && m_coordsLen>=4 || shape==Unknown && m_coordsLen == 4) { + int x0 = m_coords[0].minWidth(width_); + int y0 = m_coords[1].minWidth(height_); + int x1 = m_coords[2].minWidth(width_); + int y1 = m_coords[3].minWidth(height_); + region = QRegion(x0,y0,x1-x0,y1-y0); + } + else if (shape==Default) + region = QRegion(0,0,width_,height_); + // else + // return null region + + return region; +} diff --git a/khtml/html/html_imageimpl.h b/khtml/html/html_imageimpl.h new file mode 100644 index 000000000..8fc00b027 --- /dev/null +++ b/khtml/html/html_imageimpl.h @@ -0,0 +1,150 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_IMAGEIMPL_H +#define HTML_IMAGEIMPL_H + +#include "html/html_inlineimpl.h" +#include "misc/khtmllayout.h" +#include "misc/loader_client.h" +#include "rendering/render_object.h" + +#include <qregion.h> + +namespace DOM { + +class DOMString; +class HTMLFormElementImpl; + +class HTMLImageElementImpl + : public HTMLElementImpl, public khtml::CachedObjectClient +{ + friend class HTMLFormElementImpl; +public: + HTMLImageElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f = 0); + ~HTMLImageElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *); + + virtual void attach(); + virtual void removedFromDocument(); + virtual void insertedIntoDocument(); + virtual void addId(const QString& id); + virtual void removeId(const QString& id); + + + long width() const; + long height() const; + + bool isServerMap() const { return ( ismap && !usemap.length() ); } + /** Return the image for this element. + * This has to convert the pixmap into an image first. + * This will return undefined results if complete() is not true. + */ + QImage currentImage() const; + /** Return the pixmap for this element. + * This will return undefined results if complete() is not true. + */ + QPixmap currentPixmap() const; + + DOMString altText() const; + + DOMString imageMap() const { return usemap; } + /** See if the image has been completely downloaded. + * @return True if and only if the image is completely downloaded yet*/ + bool complete() const; + + virtual void notifyFinished(khtml::CachedObject *finishedObj); + void dispatchLoadEvent(); +protected: + DOMString usemap; + bool ismap : 1; + bool loadEventSent : 1; + khtml::CachedImage *m_image; + HTMLFormElementImpl *m_form; + DOMString m_name; +}; + + +//------------------------------------------------------------------ + +class HTMLAreaElementImpl : public HTMLAnchorElementImpl +{ +public: + + enum Shape { Default, Poly, Rect, Circle, Unknown }; + + HTMLAreaElementImpl(DocumentImpl *doc); + ~HTMLAreaElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *attr); + + bool isDefault() const { return shape==Default; } + + bool mapMouseEvent(int x_, int y_, int width_, int height_, + khtml::RenderObject::NodeInfo& info); + + virtual QRect getRect() const; + + QRegion cachedRegion() const { return region; } + +protected: + QRegion getRegion(int width_, int height) const; + QRegion region; + khtml::Length* m_coords; + int m_coordsLen; + int lastw, lasth; + Shape shape : 3; + bool nohref : 1; +}; + + +// ------------------------------------------------------------------------- + +class HTMLMapElementImpl : public HTMLElementImpl +{ +public: + HTMLMapElementImpl(DocumentImpl *doc); + + ~HTMLMapElementImpl(); + + virtual Id id() const; + + virtual DOMString getName() const { return name; } + + virtual void parseAttribute(AttributeImpl *attr); + + bool mapMouseEvent(int x_, int y_, int width_, int height_, + khtml::RenderObject::NodeInfo& info); +private: + + QString name; +}; + + +} //namespace + +#endif diff --git a/khtml/html/html_inlineimpl.cpp b/khtml/html/html_inlineimpl.cpp new file mode 100644 index 000000000..08bbbb177 --- /dev/null +++ b/khtml/html/html_inlineimpl.cpp @@ -0,0 +1,298 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * (C) 2001-2003 Dirk Mueller (mueller@kde.org) + * (C) 2002 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +// ------------------------------------------------------------------------- + +#include "html/html_inlineimpl.h" +#include "html/html_imageimpl.h" +#include "html/html_documentimpl.h" + +#include "misc/htmlhashes.h" +#include "khtmlview.h" +#include "khtml_part.h" +#include "css/csshelper.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "css/cssstyleselector.h" +#include "xml/dom2_eventsimpl.h" +#include "rendering/render_br.h" +#include "rendering/render_image.h" + +#include <kdebug.h> + +using namespace khtml; +using namespace DOM; + +NodeImpl::Id HTMLAnchorElementImpl::id() const +{ + return ID_A; +} + +void HTMLAnchorElementImpl::defaultEventHandler(EventImpl *evt) +{ + bool keydown = evt->id() == EventImpl::KEYDOWN_EVENT && evt->isKeyRelatedEvent(); + + // React on clicks and on keypresses. + // Don't make this KEYUP_EVENT again, it makes khtml follow links + // it shouldn't, when pressing Enter in the combo. + if ( ( (evt->id() == EventImpl::CLICK_EVENT && !static_cast<MouseEventImpl*>(evt)->isDoubleClick()) || + ( keydown && m_focused)) && m_hasAnchor) { + + MouseEventImpl *e = 0; + if ( evt->id() == EventImpl::CLICK_EVENT ) + e = static_cast<MouseEventImpl*>( evt ); + + KeyEventBaseImpl *k = 0; + if (keydown) + k = static_cast<KeyEventBaseImpl *>( evt ); + + QString utarget; + QString url; + if ( e && e->button() == 2 ) { + HTMLElementImpl::defaultEventHandler(evt); + return; + } + + if ( k ) { + if (k->virtKeyVal() != KeyEventBaseImpl::DOM_VK_ENTER) { + if (k->qKeyEvent()) + k->qKeyEvent()->ignore(); + HTMLElementImpl::defaultEventHandler(evt); + return; + } + if (k->qKeyEvent()) k->qKeyEvent()->accept(); + } + + url = khtml::parseURL(getAttribute(ATTR_HREF)).string(); + + utarget = getAttribute(ATTR_TARGET).string(); + + if ( e && e->button() == 1 ) + utarget = "_blank"; + + if ( evt->target()->id() == ID_IMG ) { + HTMLImageElementImpl* img = static_cast<HTMLImageElementImpl*>( evt->target() ); + if ( img && img->isServerMap() ) + { + khtml::RenderImage *r = static_cast<khtml::RenderImage *>(img->renderer()); + if(r && e) + { + KHTMLView* v = getDocument()->view(); + int x = e->clientX(); + int y = e->clientY(); + int absx = 0; + int absy = 0; + if ( v ) { + x += v->contentsX(); + y += v->contentsY(); + } + r->absolutePosition(absx, absy); + url += QString("?%1,%2").arg( x - absx ).arg( y - absy ); + } + else { + evt->setDefaultHandled(); + HTMLElementImpl::defaultEventHandler(evt); + return; + } + } + } + if ( !evt->defaultPrevented() ) { + int state = 0; + int button = 0; + + if ( e ) { + if ( e->ctrlKey() ) + state |= Qt::ControlButton; + if ( e->shiftKey() ) + state |= Qt::ShiftButton; + if ( e->altKey() ) + state |= Qt::AltButton; + if ( e->metaKey() ) + state |= Qt::MetaButton; + + if ( e->button() == 0 ) + button = Qt::LeftButton; + else if ( e->button() == 1 ) + button = Qt::MidButton; + else if ( e->button() == 2 ) + button = Qt::RightButton; + } + else if ( k ) + { + if ( k->checkModifier(Qt::ShiftButton) ) + state |= Qt::ShiftButton; + if ( k->checkModifier(Qt::AltButton) ) + state |= Qt::AltButton; + if ( k->checkModifier(Qt::ControlButton) ) + state |= Qt::ControlButton; + } + + // ### also check if focused node is editable if not in designmode, + // and prevent link loading then (LS) + if (getDocument()->view() && !getDocument()->designMode()) { + if (k) + click(); + else + getDocument()->view()->part()-> + urlSelected( url, button, state, utarget ); + } + } + evt->setDefaultHandled(); + } + HTMLElementImpl::defaultEventHandler(evt); +} + + +void HTMLAnchorElementImpl::click() +{ + QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0,0),Qt::LeftButton, 0); + dispatchMouseEvent(&me,EventImpl::CLICK_EVENT, 1); +} + +void HTMLAnchorElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_HREF: + { + bool hadAnchor = m_hasAnchor; + m_hasAnchor = attr->val() != 0; + getDocument()->incDOMTreeVersion(); + if (hadAnchor != m_hasAnchor) + setChanged(); + } + break; + case ATTR_TARGET: + m_hasTarget = attr->val() != 0; + break; + case ATTR_TITLE: + case ATTR_REL: + break; + case ATTR_ACCESSKEY: + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLBRElementImpl::id() const +{ + return ID_BR; +} + +void HTMLBRElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_CLEAR: + { + DOMString str = attr->value().lower(); + if( str.isEmpty() ) str = "none"; + else if( strcmp (str,"all")==0 ) str = "both"; + addCSSProperty(CSS_PROP_CLEAR, str); + break; + } + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLBRElementImpl::attach() +{ + assert(!attached()); + assert(!m_render); + assert(parentNode()); + + if (parentNode()->renderer()) { + RenderStyle* style = getDocument()->styleSelector()->styleForElement( this ); + style->ref(); + if( style->display() != NONE ) { + m_render = new (getDocument()->renderArena()) RenderBR(this); + m_render->setStyle(style); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + } + style->deref(); + } + NodeImpl::attach(); +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLFontElementImpl::id() const +{ + return ID_FONT; +} + +void HTMLFontElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_SIZE: + { + DOMStringImpl* v = attr->val(); + if(v) { + const QChar* s = v->s; + int num = v->toInt(); + int len = v->l; + while( len && s->isSpace() ) + len--,s++; + if ( len && *s == '+' ) + num += 3; + int size; + switch (num) + { + case -2: + case 1: size = CSS_VAL_XX_SMALL; break; + case -1: + case 2: size = CSS_VAL_SMALL; break; + case 0: // treat 0 the same as 3, because people + // expect it to be between -1 and +1 + case 3: size = CSS_VAL_MEDIUM; break; + case 4: size = CSS_VAL_LARGE; break; + case 5: size = CSS_VAL_X_LARGE; break; + case 6: size = CSS_VAL_XX_LARGE; break; + default: + if (num > 6) + size = CSS_VAL__KHTML_XXX_LARGE; + else + size = CSS_VAL_XX_SMALL; + } + addCSSProperty(CSS_PROP_FONT_SIZE, size); + } + break; + } + case ATTR_COLOR: + addHTMLColor(CSS_PROP_COLOR, attr->value()); + break; + case ATTR_FACE: + addCSSProperty(CSS_PROP_FONT_FAMILY, attr->value()); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + + diff --git a/khtml/html/html_inlineimpl.h b/khtml/html/html_inlineimpl.h new file mode 100644 index 000000000..252e64e41 --- /dev/null +++ b/khtml/html/html_inlineimpl.h @@ -0,0 +1,75 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_INLINEIMPL_H +#define HTML_INLINEIMPL_H + +#include "html_elementimpl.h" + +namespace DOM { + +class DOMString; + +class HTMLAnchorElementImpl : public HTMLElementImpl +{ +public: + HTMLAnchorElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc), m_hasTarget(false) {} + + virtual bool isFocusable() const { return m_hasAnchor; } + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); + virtual void defaultEventHandler(EventImpl *evt); + void click(); +protected: + bool m_hasTarget : 1; +}; + +// ------------------------------------------------------------------------- + +class HTMLBRElementImpl : public HTMLElementImpl +{ +public: + HTMLBRElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); +}; + +// ------------------------------------------------------------------------- + +class HTMLFontElementImpl : public HTMLElementImpl +{ +public: + HTMLFontElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); +}; + +} //namespace + +#endif diff --git a/khtml/html/html_listimpl.cpp b/khtml/html/html_listimpl.cpp new file mode 100644 index 000000000..e1650af46 --- /dev/null +++ b/khtml/html/html_listimpl.cpp @@ -0,0 +1,169 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#include "html_listimpl.h" + +using namespace DOM; + +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "rendering/render_list.h" +#include "misc/htmlhashes.h" +#include "xml/dom_docimpl.h" + +using namespace khtml; + +NodeImpl::Id HTMLUListElementImpl::id() const +{ + return ID_UL; +} + +void HTMLUListElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_TYPE: + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, attr->value()); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLDirectoryElementImpl::id() const +{ + return ID_DIR; +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLMenuElementImpl::id() const +{ + return ID_MENU; +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLOListElementImpl::id() const +{ + return ID_OL; +} + +void HTMLOListElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_TYPE: + if ( strcmp( attr->value(), "a" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_LOWER_ALPHA); + else if ( strcmp( attr->value(), "A" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_UPPER_ALPHA); + else if ( strcmp( attr->value(), "i" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_LOWER_ROMAN); + else if ( strcmp( attr->value(), "I" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_UPPER_ROMAN); + else if ( strcmp( attr->value(), "1" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_DECIMAL); + break; + case ATTR_START: + _start = attr->val() ? attr->val()->toInt() : 1; + break; + default: + HTMLUListElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLLIElementImpl::id() const +{ + return ID_LI; +} + +void HTMLLIElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_VALUE: + if(m_render && m_render->isListItem() && m_render->style()->display() == LIST_ITEM) + static_cast<RenderListItem*>(m_render)->setValue(attr->value().toInt()); + break; + case ATTR_TYPE: + if ( strcmp( attr->value(), "a" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_LOWER_ALPHA); + else if ( strcmp( attr->value(), "A" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_UPPER_ALPHA); + else if ( strcmp( attr->value(), "i" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_LOWER_ROMAN); + else if ( strcmp( attr->value(), "I" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_UPPER_ROMAN); + else if ( strcmp( attr->value(), "1" ) == 0 ) + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_DECIMAL); + else + addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, attr->value()); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLLIElementImpl::attach() +{ + assert(!attached()); + + HTMLElementImpl::attach(); + + if ( m_render && m_render->style()->display() == LIST_ITEM ) { + RenderListItem* render = static_cast<RenderListItem*>( renderer() ); + NodeImpl* listNode = 0; + NodeImpl* n = parentNode(); + while ( !listNode && n ) { + switch( n->id() ) { + case ID_UL: + case ID_OL: + listNode = n; + break; + } + n = n->parentNode(); + } + + // if we are not in a list, then position us inside + // can't use addCSSProperty cause its inherited attribute + render->setInsideList( listNode ); + + DOMString v = getAttribute(ATTR_VALUE); + if ( !v.isEmpty() ) + render->setValue( v.implementation()->toInt() ); + } +} + + +// ------------------------------------------------------------------------- + + +NodeImpl::Id HTMLDListElementImpl::id() const +{ + return ID_DL; +} + diff --git a/khtml/html/html_listimpl.h b/khtml/html/html_listimpl.h new file mode 100644 index 000000000..d4a9f4436 --- /dev/null +++ b/khtml/html/html_listimpl.h @@ -0,0 +1,112 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_LISTIMPL_H +#define HTML_LISTIMPL_H + +/* + * we ignore the deprecated compact attribute. Netscape does so too... + */ + +#include "html_elementimpl.h" + +namespace DOM +{ + +class DOMString; + +class HTMLUListElementImpl : public HTMLElementImpl +{ +public: + HTMLUListElementImpl(DocumentImpl *doc) : HTMLElementImpl(doc) {} + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *); + + virtual int start() const { return 1; } +}; + +// ------------------------------------------------------------------------- + +class HTMLDirectoryElementImpl : public HTMLElementImpl +{ +public: + HTMLDirectoryElementImpl(DocumentImpl *doc) : HTMLElementImpl(doc) {} + + virtual Id id() const; +}; + +// ------------------------------------------------------------------------- + +class HTMLMenuElementImpl : public HTMLElementImpl +{ +public: + HTMLMenuElementImpl(DocumentImpl *doc) : HTMLElementImpl(doc) {} + virtual ~HTMLMenuElementImpl() {} + + virtual Id id() const; +}; + +// ------------------------------------------------------------------------- + +class HTMLOListElementImpl : public HTMLUListElementImpl +{ +public: + HTMLOListElementImpl(DocumentImpl *doc) + : HTMLUListElementImpl(doc), _start(1) {} + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *); + + int start() const { return _start; } +private: + int _start; +}; + +// ------------------------------------------------------------------------- + +class HTMLLIElementImpl : public HTMLElementImpl +{ +public: + HTMLLIElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) {} + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); +}; + +// ------------------------------------------------------------------------- + +class HTMLDListElementImpl : public HTMLElementImpl +{ +public: + HTMLDListElementImpl(DocumentImpl *doc) : HTMLElementImpl(doc) {} + virtual ~HTMLDListElementImpl() {} + + virtual Id id() const; +}; + +} //namespace + +#endif diff --git a/khtml/html/html_miscimpl.cpp b/khtml/html/html_miscimpl.cpp new file mode 100644 index 000000000..bf0c56e00 --- /dev/null +++ b/khtml/html/html_miscimpl.cpp @@ -0,0 +1,443 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2005 Maksim Orlovich (maksim@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +// ------------------------------------------------------------------------- +#include "html/html_tableimpl.h" +#include "html/html_miscimpl.h" +#include "html/html_formimpl.h" +#include "html/html_documentimpl.h" + +#include "misc/htmlhashes.h" +#include "dom/dom_node.h" + +using namespace DOM; + +#include <kdebug.h> + +HTMLBaseFontElementImpl::HTMLBaseFontElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) +{ +} + +HTMLBaseFontElementImpl::~HTMLBaseFontElementImpl() +{ +} + +NodeImpl::Id HTMLBaseFontElementImpl::id() const +{ + return ID_BASEFONT; +} + +// ------------------------------------------------------------------------- + +struct CollectionCache: public NodeListImpl::Cache +{ + static Cache* make() { return new CollectionCache; } + + QDict<QValueList<NodeImpl*> > nameCache; + + CollectionCache(): nameCache(127) + { + nameCache.setAutoDelete(true); + } + + virtual void clear(DocumentImpl* doc) + { + Cache::clear(doc); + //qDeletaAll here in Qt4 + nameCache.clear(); + } +}; + +HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type): + NodeListImpl(_base, _type, CollectionCache::make) +{ + type = _type; +} + +bool HTMLCollectionImpl::nodeMatches(NodeImpl *current, bool& deep) const +{ + if ( current->nodeType() != Node::ELEMENT_NODE ) + { + deep = false; + return false; + } + + bool check = false; + HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current); + switch(type) + { + case DOC_IMAGES: + if(e->id() == ID_IMG) + check = true; + break; + case DOC_SCRIPTS: + if(e->id() == ID_SCRIPT) + check = true; + break; + case DOC_FORMS: + if(e->id() == ID_FORM) + check = true; + break; + case DOC_LAYERS: + if(e->id() == ID_LAYER || e->id() == ID_ILAYER) + check = true; + break; + case TABLE_TBODIES: + if(e->id() == ID_TBODY) + check = true; + else if(e->id() == ID_TABLE) + deep = false; + break; + case TR_CELLS: + if(e->id() == ID_TD || e->id() == ID_TH) + check = true; + else if(e->id() == ID_TABLE) + deep = false; + break; + case TABLE_ROWS: + case TSECTION_ROWS: + if(e->id() == ID_TR) + check = true; + else if(e->id() == ID_TABLE) + deep = false; + break; + case SELECT_OPTIONS: + if(e->id() == ID_OPTION) + check = true; + break; + case MAP_AREAS: + if(e->id() == ID_AREA) + check = true; + break; + case DOC_APPLETS: // all OBJECT and APPLET elements + if(e->id() == ID_OBJECT || e->id() == ID_APPLET || e->id() == ID_EMBED) + check = true; + break; + case DOC_LINKS: // all A _and_ AREA elements with a value for href + if(e->id() == ID_A || e->id() == ID_AREA) + if(!e->getAttribute(ATTR_HREF).isNull()) + check = true; + break; + case DOC_ANCHORS: // all A elements with a value for name and/or id + if(e->id() == ID_A) { + if(e->hasID() || !e->getAttribute(ATTR_NAME).isNull()) + check = true; + } + break; + case DOC_ALL: // "all" elements + check = true; + break; + case NODE_CHILDREN: // first-level children + check = true; + deep = false; + break; + default: + kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl; + } + + return check; +} + +bool HTMLCollectionImpl::checkForNameMatch(NodeImpl *node, const DOMString &name) const +{ + if ( node->nodeType() != Node::ELEMENT_NODE ) + return false; + + HTMLElementImpl *e = static_cast<HTMLElementImpl *>(node); + + //If ID matches, this is definitely a match + if (e->getAttribute(ATTR_ID) == name) + return true; + + //Despite what the DOM spec says, neither IE nor Gecko actually + //care to prefer IDs. Instead, they just match everything + //that has ID or a name for nodes that have a name. + //Except for the form elements collection, Gecko always returns + //just one item. IE is more complex: its namedItem + //and call notation access return everything that matches, + //but the subscript notation is sometimes different. + //For now, we try to match IE, but without the subscript + //oddness, which I don't understand -- Maks. + + bool checkName; + switch (e->id()) + { + case ID_A: + case ID_APPLET: + case ID_BUTTON: + case ID_EMBED: + case ID_FORM: + case ID_IMG: + case ID_INPUT: + case ID_MAP: + case ID_META: + case ID_OBJECT: + case ID_SELECT: + case ID_TEXTAREA: + case ID_FRAME: + case ID_IFRAME: + case ID_FRAMESET: + checkName = true; + break; + default: + checkName = false; + } + + if (checkName) + return e->getAttribute(ATTR_NAME) == name; + else + return false; +} + +NodeImpl *HTMLCollectionImpl::item ( unsigned long index ) const +{ + //Most of the time, we just go in normal document order + if (type != TABLE_ROWS) + return NodeListImpl::item(index); + + //For table.rows, we first need to check header, then bodies, then footer. + //we pack any extra headers/footer with bodies. This matches IE, and + //means doing the usual thing with length is right + const HTMLTableElementImpl* table = static_cast<const HTMLTableElementImpl*>(m_refNode); + + long sectionIndex; + HTMLTableSectionElementImpl* section; + + NodeImpl* found = 0; + if (table->findRowSection(index, section, sectionIndex)) { + HTMLCollectionImpl rows(section, TSECTION_ROWS); + found = rows.item(sectionIndex); + } + + m_cache->current.node = found; //namedItem needs this. + m_cache->position = index; + return found; +} + +unsigned long HTMLCollectionImpl::calcLength(NodeImpl *start) const +{ + if (type != TABLE_ROWS) + return NodeListImpl::calcLength(start); + + unsigned length = 0; + const HTMLTableElementImpl* table = static_cast<const HTMLTableElementImpl*>(m_refNode); + for (NodeImpl* kid = table->firstChild(); kid; kid = kid->nextSibling()) { + HTMLCollectionImpl rows(kid, TSECTION_ROWS); + length += rows.length(); + } + return length; +} + +NodeImpl *HTMLCollectionImpl::firstItem() const +{ + return item(0); +} + +NodeImpl *HTMLCollectionImpl::nextItem() const +{ + //### this assumes this is called immediately after nextItem -- + //it this sane? + return item(m_cache->position + 1); +} + +NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name ) const +{ + //Reset the position. The invariant is that nextNamedItem will start looking + //from the current position. + firstItem(); + + return nextNamedItem(name); +} + +NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const +{ + while (NodeImpl* candidate = m_cache->current.node) + { + //Always advance, for next call + nextItem(); + if (checkForNameMatch(candidate, name)) + return candidate; + } + return 0; +} + +QValueList<NodeImpl*> HTMLCollectionImpl::namedItems( const DOMString &name ) const +{ + QString key = name.string(); + + //We use a work-conserving design for the name cache presently -- only + //remember stuff about elements we were asked for. + m_cache->updateNodeListInfo(m_refNode->getDocument()); + CollectionCache* cache = static_cast<CollectionCache*>(m_cache); + if (QValueList<NodeImpl*>* info = cache->nameCache.find(key)) { + return *info; + } + else { + QValueList<NodeImpl*>* newInfo = new QValueList<NodeImpl*>; + + NodeImpl* match = namedItem(name); + while (match) { + newInfo->append(match); + match = nextNamedItem(name); + } + + cache->nameCache.insert(key, newInfo); + return *newInfo; + } +} + +// ----------------------------------------------------------------------------- + +HTMLFormCollectionImpl::HTMLFormCollectionImpl(NodeImpl* _base) + : HTMLCollectionImpl(_base, FORM_ELEMENTS), currentNamePos(0), currentNameImgPos(0) +{} + +NodeImpl *HTMLFormCollectionImpl::item( unsigned long index ) const +{ + m_cache->updateNodeListInfo(m_refNode->getDocument()); + + unsigned int dist = index; + unsigned int strt = 0; + if (m_cache->current.index && m_cache->position <= index) + { + dist = index - m_cache->position; + strt = m_cache->current.index; + } + + QPtrList<HTMLGenericFormElementImpl>& l = static_cast<HTMLFormElementImpl*>( m_refNode )->formElements; + for (unsigned i = strt; i < l.count(); i++) + { + if (l.at( i )->isEnumeratable()) + { + if (dist == 0) + { + //Found it! + m_cache->position = index; + m_cache->current.index = i; + return l.at( i ); + } + else + --dist; + } + } + return 0; +} + +unsigned long HTMLFormCollectionImpl::calcLength(NodeImpl *start) const +{ + unsigned length = 0; + QPtrList<HTMLGenericFormElementImpl> l = static_cast<HTMLFormElementImpl*>( start )->formElements; + for ( unsigned i = 0; i < l.count(); i++ ) + if ( l.at( i )->isEnumeratable() ) + ++length; + return length; +} + +NodeImpl *HTMLFormCollectionImpl::namedItem( const DOMString &name ) const +{ + currentNamePos = 0; + currentNameImgPos = 0; + foundInput = false; + return nextNamedItem(name); +} + +NodeImpl *HTMLFormCollectionImpl::nextNamedItem( const DOMString &name ) const +{ + QPtrList<HTMLGenericFormElementImpl>& l = static_cast<HTMLFormElementImpl*>( m_refNode )->formElements; + + //Go through the list, trying to find the appropriate named form element. + for ( ; currentNamePos < l.count(); ++currentNamePos ) + { + HTMLGenericFormElementImpl* el = l.at(currentNamePos); + if (el->isEnumeratable() && + ((el->getAttribute(ATTR_ID) == name) || + (el->getAttribute(ATTR_NAME) == name))) + { + ++currentNamePos; //Make next call start after this + foundInput = true;//No need to look for img + return el; + } + } + + //If we got this far, we may need to start looking through the images, + //but only if no input tags were matched + if (foundInput) return 0; + + QPtrList<HTMLImageElementImpl>& il = static_cast<HTMLFormElementImpl*>( m_refNode )->imgElements; + for ( ; currentNameImgPos < il.count(); ++currentNameImgPos ) + { + HTMLImageElementImpl* el = il.at(currentNameImgPos); + if ((el->getAttribute(ATTR_ID) == name) || + (el->getAttribute(ATTR_NAME) == name)) + { + ++currentNameImgPos; //Make next call start after this + return el; + } + } + + return 0; +} + +// ------------------------------------------------------------------------- +HTMLMappedNameCollectionImpl::HTMLMappedNameCollectionImpl(NodeImpl* _base, int _type, const DOMString& _name): + HTMLCollectionImpl(_base, NodeListImpl::UNCACHEABLE), name(_name) +{ + type = _type; //We pass uncacheable to collection, but need our own type internally. +} + +bool HTMLMappedNameCollectionImpl::nodeMatches(NodeImpl *current, bool& deep) const +{ + if ( current->nodeType() != Node::ELEMENT_NODE ) + { + deep = false; + return false; + } + + HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current); + + return matchesName(e, type, name); +} + +bool HTMLMappedNameCollectionImpl::matchesName( ElementImpl* el, int type, const DOMString& name ) +{ + switch (el->id()) + { + case ID_IMG: + case ID_FORM: + //Under document. these require non-empty name to see the element + if (type == DOCUMENT_NAMED_ITEMS && el->getAttribute(ATTR_NAME).isNull()) + return false; + //Otherwise, fallthrough + case ID_OBJECT: + case ID_EMBED: + case ID_APPLET: + case ID_LAYER: + if (el->getAttribute(ATTR_NAME) == name || el->getAttribute(ATTR_ID) == name) + return true; + else + return false; + default: + return false; + } +} diff --git a/khtml/html/html_miscimpl.h b/khtml/html/html_miscimpl.h new file mode 100644 index 000000000..beff1fe7d --- /dev/null +++ b/khtml/html/html_miscimpl.h @@ -0,0 +1,150 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_MISCIMPL_H +#define HTML_MISCIMPL_H + +#include <qvaluelist.h> +#include "html_elementimpl.h" +#include "misc/shared.h" + +namespace DOM { + +class Node; +class DOMString; +class HTMLCollection; + +class HTMLBaseFontElementImpl : public HTMLElementImpl +{ +public: + HTMLBaseFontElementImpl(DocumentImpl *doc); + + ~HTMLBaseFontElementImpl(); + + virtual Id id() const; +}; + +// ------------------------------------------------------------------------- + +class HTMLCollectionImpl : public NodeListImpl +{ + friend class DOM::HTMLCollection; +public: + enum Type { + // from HTMLDocument + DOC_IMAGES = LAST_NODE_LIST + 1, // all IMG elements in the document + DOC_APPLETS, // all OBJECT and APPLET elements + DOC_FORMS, // all FORMS + DOC_LAYERS, // all LAYERS + DOC_LINKS, // all A _and_ AREA elements with a value for href + DOC_ANCHORS, // all A elements with a value for name + DOC_SCRIPTS, // all SCRIPT elements + // from HTMLTable, HTMLTableSection, HTMLTableRow + TABLE_ROWS, // all rows in this table + TABLE_TBODIES, // all TBODY elements in this table + TSECTION_ROWS, // all rows elements in this table section + TR_CELLS, // all CELLS in this row + // from SELECT + SELECT_OPTIONS, + // from HTMLMap + MAP_AREAS, + DOC_ALL, // "all" elements (IE) + NODE_CHILDREN, // first-level children (IE) + FORM_ELEMENTS, // input elements in a form + WINDOW_NAMED_ITEMS, + DOCUMENT_NAMED_ITEMS, + LAST_TYPE + }; + + HTMLCollectionImpl(NodeImpl *_base, int _tagId); + + virtual NodeImpl *item ( unsigned long index ) const; + + // obsolete and not domtree changes save + virtual NodeImpl *firstItem() const; + virtual NodeImpl *nextItem() const; + + virtual NodeImpl *namedItem ( const DOMString &name ) const; + // In case of multiple items named the same way + virtual NodeImpl *nextNamedItem( const DOMString &name ) const; + + QValueList<NodeImpl*> namedItems( const DOMString &name ) const; + + int getType() const { + return type; + } +protected: + virtual unsigned long calcLength(NodeImpl *start) const; + + // The collection list the following elements + int type:8; + + // Reimplemented from NodeListImpl + virtual bool nodeMatches( NodeImpl *testNode, bool& doRecurse ) const; + + // Helper for name iteration: checks whether ID matches, + // and inserts any name-matching things into namedItemsWithName + bool checkForNameMatch(NodeImpl *node, const DOMString &name) const; +}; + +// this whole class is just a big hack to find form elements even in +// malformed HTML elements +// the famous <table><tr><form><td> problem +class HTMLFormCollectionImpl : public HTMLCollectionImpl +{ +public: + // base must inherit HTMLGenericFormElementImpl or this won't work + HTMLFormCollectionImpl(NodeImpl* _base); + ~HTMLFormCollectionImpl() { } + + virtual NodeImpl *item ( unsigned long index ) const; + + virtual NodeImpl *namedItem ( const DOMString &name ) const; + // In case of multiple items named the same way + virtual NodeImpl *nextNamedItem( const DOMString &name ) const; +protected: + virtual unsigned long calcLength( NodeImpl *start ) const; + +private: + mutable unsigned currentNamePos; + mutable unsigned currentNameImgPos; + mutable bool foundInput; +}; + +/* + Special collection for items of given name/id under document. or window.; but using + iteration interface +*/ +class HTMLMappedNameCollectionImpl : public HTMLCollectionImpl +{ +public: + HTMLMappedNameCollectionImpl(NodeImpl* _base, int type, const DOMString& name); + virtual bool nodeMatches( NodeImpl *testNode, bool& doRecurse ) const; + + static bool matchesName( ElementImpl* el, int type, const DOMString& name); +private: + DOMString name; +}; + +} //namespace + +#endif diff --git a/khtml/html/html_objectimpl.cpp b/khtml/html/html_objectimpl.cpp new file mode 100644 index 000000000..8a60ba41f --- /dev/null +++ b/khtml/html/html_objectimpl.cpp @@ -0,0 +1,465 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "html/html_objectimpl.h" + +#include "khtml_part.h" +#include "dom/dom_string.h" +#include "misc/htmlhashes.h" +#include "khtmlview.h" +#include <qstring.h> +#include <qvariant.h> +#include <qmap.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <kimageio.h> + +#include "xml/dom_docimpl.h" +#include "css/cssstyleselector.h" +#include "css/csshelper.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "rendering/render_applet.h" +#include "rendering/render_frames.h" +#include "rendering/render_image.h" +#include "xml/dom2_eventsimpl.h" + +using namespace DOM; +using namespace khtml; + +// ------------------------------------------------------------------------- +HTMLObjectBaseElementImpl::HTMLObjectBaseElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) +{ + needWidgetUpdate = false; + m_renderAlternative = false; +} + +void HTMLObjectBaseElementImpl::setServiceType(const QString & val) { + serviceType = val.lower(); + int pos = serviceType.find( ";" ); + if ( pos!=-1 ) + serviceType.truncate( pos ); +} + +void HTMLObjectBaseElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch ( attr->id() ) + { + case ATTR_TYPE: + case ATTR_CODETYPE: + if (attr->val()) { + DOM::DOMStringImpl *stringImpl = attr->val(); + QString val = QConstString( stringImpl->s, stringImpl->l ).string(); + setServiceType( val ); + needWidgetUpdate = true; + } + break; + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_WIDTH, attr->value()); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_HEIGHT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_HEIGHT, attr->value()); + else + removeCSSProperty(CSS_PROP_HEIGHT); + break; + case ATTR_NAME: + if (inDocument() && m_name != attr->value()) { + getDocument()->underDocNamedCache().remove(m_name.string(), this); + getDocument()->underDocNamedCache().add (attr->value().string(), this); + } + m_name = attr->value(); + //fallthrough + default: + HTMLElementImpl::parseAttribute( attr ); + } +} + +void HTMLObjectBaseElementImpl::removedFromDocument() +{ + getDocument()->underDocNamedCache().remove(m_name.string(), this); + HTMLElementImpl::removedFromDocument(); +} + +void HTMLObjectBaseElementImpl::insertedIntoDocument() +{ + getDocument()->underDocNamedCache().add(m_name.string(), this); + HTMLElementImpl::insertedIntoDocument(); +} + +void HTMLObjectBaseElementImpl::removeId(const QString& id) +{ + getDocument()->underDocNamedCache().remove(id, this); + HTMLElementImpl::removeId(id); +} + +void HTMLObjectBaseElementImpl::addId (const QString& id) +{ + getDocument()->underDocNamedCache().add(id, this); + HTMLElementImpl::addId(id); +} + +void HTMLObjectBaseElementImpl::recalcStyle( StyleChange ch ) +{ + if (needWidgetUpdate) { + if(m_render && strcmp( m_render->renderName(), "RenderPartObject" ) == 0 ) + static_cast<RenderPartObject*>(m_render)->updateWidget(); + needWidgetUpdate = false; + } + HTMLElementImpl::recalcStyle( ch ); +} + +void HTMLObjectBaseElementImpl::renderAlternative() +{ + if ( m_renderAlternative ) return; + QTimer::singleShot( 0, this, SLOT( slotRenderAlternative() ) ); +} + +void HTMLObjectBaseElementImpl::slotRenderAlternative() +{ + // the singleshot timer might have fired after we're removed + // from the document, but not yet deleted due to references + if ( !inDocument() || m_renderAlternative ) return; + + // ### there can be a m_render if this is called from our attach indirectly + if ( attached() || m_render) + detach(); + + m_renderAlternative = true; + + attach(); +} + +void HTMLObjectBaseElementImpl::attach() { + assert(!attached()); + assert(!m_render); + + if (serviceType.isEmpty() && url.startsWith("data:")) { + // Extract the MIME type from the data URL. + int index = url.find(';'); + if (index == -1) + index = url.find(','); + if (index != -1) { + int len = index - 5; + if (len > 0) + serviceType = url.mid(5, len); + else + serviceType = "text/plain"; // Data URLs with no MIME type are considered text/plain. + } + } + + bool imagelike = serviceType.startsWith("image/") && + !KImageIO::typeForMime(serviceType).isNull(); + + if (m_renderAlternative && !imagelike) { + // render alternative content + ElementImpl::attach(); + return; + } + + if (!parentNode()->renderer()) { + NodeBaseImpl::attach(); + return; + } + + RenderStyle* _style = getDocument()->styleSelector()->styleForElement(this); + _style->ref(); + + if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() && + _style->display() != NONE) + { + needWidgetUpdate = false; + + if (imagelike) { + m_render = new (getDocument()->renderArena()) RenderImage(this); + // make sure we don't attach the inner contents + addCSSProperty(CSS_PROP_DISPLAY, CSS_VAL_NONE); + } + else + m_render = new (getDocument()->renderArena())RenderPartObject(this); + + m_render->setStyle(_style); + parentNode()->renderer()->addChild(m_render, nextRenderer()); + if (imagelike) + m_render->updateFromElement(); + } + + _style->deref(); + NodeBaseImpl::attach(); + + // ### do this when we are actually finished loading instead + if (m_render) dispatchHTMLEvent(EventImpl::LOAD_EVENT, false, false); +} + + +// ------------------------------------------------------------------------- + +HTMLAppletElementImpl::HTMLAppletElementImpl(DocumentImpl *doc) + : HTMLObjectBaseElementImpl(doc) +{ + serviceType = "application/x-java-applet"; + needWidgetUpdate = true; +} + +HTMLAppletElementImpl::~HTMLAppletElementImpl() +{ +} + +NodeImpl::Id HTMLAppletElementImpl::id() const +{ + return ID_APPLET; +} + +void HTMLAppletElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch( attr->id() ) + { + case ATTR_CODEBASE: + case ATTR_ARCHIVE: + case ATTR_CODE: + case ATTR_OBJECT: + case ATTR_ALT: + break; + case ATTR_ALIGN: + addHTMLAlignment( attr->value() ); + break; + case ATTR_VSPACE: + addCSSLength(CSS_PROP_MARGIN_TOP, attr->value()); + addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value()); + break; + case ATTR_HSPACE: + addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value()); + addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value()); + break; + case ATTR_VALIGN: + addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower() ); + break; + default: + HTMLObjectBaseElementImpl::parseAttribute(attr); + } +} + +void HTMLAppletElementImpl::attach() +{ + KHTMLView* w = getDocument()->view(); + +#ifndef Q_WS_QWS // FIXME? + DOMString codeBase = getAttribute( ATTR_CODEBASE ); + DOMString code = getAttribute( ATTR_CODE ); + if ( !codeBase.isEmpty() ) + url = codeBase.string(); + if ( !code.isEmpty() ) + url = code.string(); + + if (!w || !w->part()->javaEnabled()) +#endif + m_renderAlternative = true; + + HTMLObjectBaseElementImpl::attach(); +} + +// ------------------------------------------------------------------------- + +HTMLEmbedElementImpl::HTMLEmbedElementImpl(DocumentImpl *doc) + : HTMLObjectBaseElementImpl(doc) +{ +} + +HTMLEmbedElementImpl::~HTMLEmbedElementImpl() +{ +} + +NodeImpl::Id HTMLEmbedElementImpl::id() const +{ + return ID_EMBED; +} + +void HTMLEmbedElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch ( attr->id() ) + { + case ATTR_CODE: + case ATTR_SRC: + url = khtml::parseURL(attr->val()).string(); + needWidgetUpdate = true; + break; + case ATTR_BORDER: + addCSSLength(CSS_PROP_BORDER_WIDTH, attr->value()); + addCSSProperty( CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID ); + addCSSProperty( CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID ); + addCSSProperty( CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID ); + addCSSProperty( CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID ); + break; + case ATTR_VSPACE: + addCSSLength(CSS_PROP_MARGIN_TOP, attr->value()); + addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value()); + break; + case ATTR_HSPACE: + addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value()); + addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value()); + break; + case ATTR_ALIGN: + addHTMLAlignment( attr->value() ); + break; + case ATTR_VALIGN: + addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower() ); + break; + case ATTR_PLUGINPAGE: + case ATTR_PLUGINSPAGE: { + pluginPage = attr->value().string(); + break; + } + case ATTR_HIDDEN: + if (strcasecmp( attr->value(), "yes" ) == 0 || strcasecmp( attr->value() , "true") == 0 ) + hidden = true; + else + hidden = false; + break; + default: + HTMLObjectBaseElementImpl::parseAttribute( attr ); + } +} + +void HTMLEmbedElementImpl::attach() +{ + KHTMLView* w = getDocument()->view(); + + if (!w || !w->part()->pluginsEnabled()) + m_renderAlternative = true; + + if (parentNode()->id() == ID_OBJECT) + NodeBaseImpl::attach(); + else + HTMLObjectBaseElementImpl::attach(); +} + +// ------------------------------------------------------------------------- + +HTMLObjectElementImpl::HTMLObjectElementImpl(DocumentImpl *doc) + : HTMLObjectBaseElementImpl(doc) +{ +} + +HTMLObjectElementImpl::~HTMLObjectElementImpl() +{ +} + +NodeImpl::Id HTMLObjectElementImpl::id() const +{ + return ID_OBJECT; +} + +HTMLFormElementImpl *HTMLObjectElementImpl::form() const +{ + return 0; +} + +void HTMLObjectElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch ( attr->id() ) + { + case ATTR_DATA: + url = khtml::parseURL( attr->val() ).string(); + needWidgetUpdate = true; + break; + case ATTR_CLASSID: + classId = attr->value().string(); + needWidgetUpdate = true; + break; + case ATTR_ONLOAD: // ### support load/unload on object elements + setHTMLEventListener(EventImpl::LOAD_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onload", this)); + break; + case ATTR_ONUNLOAD: + setHTMLEventListener(EventImpl::UNLOAD_EVENT, + getDocument()->createHTMLEventListener(attr->value().string(), "onunload", this)); + break; + case ATTR_VSPACE: + addCSSLength(CSS_PROP_MARGIN_TOP, attr->value()); + addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value()); + break; + case ATTR_HSPACE: + addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value()); + addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value()); + break; + case ATTR_ALIGN: + addHTMLAlignment( attr->value() ); + break; + case ATTR_VALIGN: + addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower() ); + break; + default: + HTMLObjectBaseElementImpl::parseAttribute( attr ); + } +} + +DocumentImpl* HTMLObjectElementImpl::contentDocument() const +{ + if ( !m_render ) return 0; + if ( !m_render->isWidget() ) return 0; + QWidget* widget = static_cast<RenderWidget*>( m_render )->widget(); + if( widget && ::qt_cast<KHTMLView*>( widget ) ) + return static_cast<KHTMLView*>( widget )->part()->xmlDocImpl(); + return 0; +} + +void HTMLObjectElementImpl::attach() +{ + KHTMLView* w = getDocument()->view(); + + if (!w || !w->part()->pluginsEnabled()) + m_renderAlternative = true; + + HTMLObjectBaseElementImpl::attach(); +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLParamElementImpl::id() const +{ + return ID_PARAM; +} + +void HTMLParamElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch( attr->id() ) + { + case ATTR_VALUE: + m_value = attr->value().string(); + break; + case ATTR_ID: + if (getDocument()->htmlMode() != DocumentImpl::XHtml) break; + // fall through + case ATTR_NAME: + m_name = attr->value().string(); + // fall through + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +#include "html_objectimpl.moc" diff --git a/khtml/html/html_objectimpl.h b/khtml/html/html_objectimpl.h new file mode 100644 index 000000000..a33786442 --- /dev/null +++ b/khtml/html/html_objectimpl.h @@ -0,0 +1,145 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_OBJECTIMPL_H +#define HTML_OBJECTIMPL_H + +#include "html_elementimpl.h" +#include "xml/dom_stringimpl.h" +#include <qobject.h> +#include <qstringlist.h> + +class KHTMLView; + +// ------------------------------------------------------------------------- +namespace DOM { + +class HTMLFormElementImpl; +class DOMStringImpl; + +class HTMLObjectBaseElementImpl : public QObject, public HTMLElementImpl +{ + Q_OBJECT +public: + HTMLObjectBaseElementImpl(DocumentImpl *doc); + + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); + + virtual void recalcStyle( StyleChange ch ); + + void renderAlternative(); + + void setServiceType(const QString &); + + QString url; + QString classId; + QString serviceType; + bool needWidgetUpdate; + bool m_renderAlternative; + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void addId(const QString& id); + virtual void removeId(const QString& id); +protected slots: + void slotRenderAlternative(); +protected: + DOMString m_name; +}; + +// ------------------------------------------------------------------------- + +class HTMLAppletElementImpl : public HTMLObjectBaseElementImpl +{ +public: + HTMLAppletElementImpl(DocumentImpl *doc); + + ~HTMLAppletElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *token); + virtual void attach(); +protected: + khtml::VAlign valign; +}; + +// ------------------------------------------------------------------------- + +class HTMLEmbedElementImpl : public HTMLObjectBaseElementImpl +{ +public: + HTMLEmbedElementImpl(DocumentImpl *doc); + ~HTMLEmbedElementImpl(); + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); + + QString pluginPage; + bool hidden; +}; + +// ------------------------------------------------------------------------- + +class HTMLObjectElementImpl : public HTMLObjectBaseElementImpl +{ +public: + HTMLObjectElementImpl(DocumentImpl *doc); + + ~HTMLObjectElementImpl(); + + virtual Id id() const; + + HTMLFormElementImpl *form() const; + + virtual void parseAttribute(AttributeImpl *token); + + virtual void attach(); + + DocumentImpl* contentDocument() const; +}; + +// ------------------------------------------------------------------------- + +class HTMLParamElementImpl : public HTMLElementImpl +{ + friend class HTMLAppletElementImpl; +public: + HTMLParamElementImpl(DocumentImpl* _doc) : HTMLElementImpl(_doc) {} + + virtual Id id() const; + + virtual void parseAttribute(AttributeImpl *token); + + QString name() const { return m_name; } + QString value() const { return m_value; } + + protected: + QString m_name; + QString m_value; +}; + +} // namespace +#endif diff --git a/khtml/html/html_tableimpl.cpp b/khtml/html/html_tableimpl.cpp new file mode 100644 index 000000000..3d11c5e98 --- /dev/null +++ b/khtml/html/html_tableimpl.cpp @@ -0,0 +1,994 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2003 Apple Computer, Inc. + * (C) 2006 Maksim Orlovich (maksim@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "html/html_documentimpl.h" +#include "html/html_tableimpl.h" + +#include "dom/dom_exception.h" +#include "dom/dom_node.h" + +#include "misc/htmlhashes.h" +#include "khtmlview.h" +#include "khtml_part.h" + +#include "css/cssstyleselector.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "css/csshelper.h" + +#include "rendering/render_table.h" + +#include <kdebug.h> +#include <kglobal.h> + +using namespace khtml; +using namespace DOM; + +HTMLTableElementImpl::HTMLTableElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) +{ + rules = None; + frame = Void; + + padding = 1; + + m_solid = false; + + // reset font color and sizes here, if we don't have strict parse mode. + // this is 90% compatible to ie and mozilla, and the by way easiest solution... + // only difference to 100% correct is that in strict mode <font> elements are propagated into tables. + if ( getDocument()->parseMode() < DocumentImpl::Transitional ) { + addCSSProperty( CSS_PROP_FONT_SIZE, CSS_VAL_MEDIUM ); + addCSSProperty( CSS_PROP_COLOR, CSS_VAL__KHTML_TEXT ); + } +} + +HTMLTableElementImpl::~HTMLTableElementImpl() +{ +} + +NodeImpl::Id HTMLTableElementImpl::id() const +{ + return ID_TABLE; +} + +NodeImpl* HTMLTableElementImpl::setCaption( HTMLTableCaptionElementImpl *c ) +{ + int exceptioncode = 0; + NodeImpl* r; + if(ElementImpl* cap = caption()) { + replaceChild ( c, cap, exceptioncode ); + r = c; + } + else + r = insertBefore( c, firstChild(), exceptioncode ); + tCaption = c; + return r; +} + +NodeImpl* HTMLTableElementImpl::setTHead( HTMLTableSectionElementImpl *s ) +{ + int exceptioncode = 0; + NodeImpl* r; + if( ElementImpl* head = tHead() ) { + replaceChild( s, head, exceptioncode ); + r = s; + } + else if(ElementImpl* foot = tFoot()) + r = insertBefore( s, foot, exceptioncode ); + else if(ElementImpl* firstBody = tFirstBody()) + r = insertBefore( s, firstBody, exceptioncode ); + else + r = appendChild( s, exceptioncode ); + + head = s; + return r; +} + +NodeImpl* HTMLTableElementImpl::setTFoot( HTMLTableSectionElementImpl *s ) +{ + int exceptioncode = 0; + NodeImpl* r; + if(ElementImpl* foot = tFoot()) { + replaceChild ( s, foot, exceptioncode ); + r = s; + } else if(ElementImpl* firstBody = tFirstBody()) + r = insertBefore( s, firstBody, exceptioncode ); + else + r = appendChild( s, exceptioncode ); + foot = s; + return r; +} + +NodeImpl* HTMLTableElementImpl::setTBody( HTMLTableSectionElementImpl *s ) +{ + int exceptioncode = 0; + NodeImpl* r; + + if(ElementImpl* firstBody = tFirstBody()) { + replaceChild ( s, firstBody, exceptioncode ); + r = s; + } else + r = appendChild( s, exceptioncode ); + firstBody = s; + + return r; +} + +HTMLElementImpl *HTMLTableElementImpl::createTHead( ) +{ + if(!tHead()) + { + int exceptioncode = 0; + ElementImpl* head = new HTMLTableSectionElementImpl(docPtr(), ID_THEAD, true /* implicit */); + if(ElementImpl* foot = tFoot()) + insertBefore( head, foot, exceptioncode ); + else if(ElementImpl* firstBody = tFirstBody()) + insertBefore( head, firstBody, exceptioncode); + else + appendChild(head, exceptioncode); + } + return tHead(); +} + +void HTMLTableElementImpl::deleteTHead( ) +{ + if(ElementImpl* head = tHead()) { + int exceptioncode = 0; + removeChild(head, exceptioncode); + } +} + +HTMLElementImpl *HTMLTableElementImpl::createTFoot( ) +{ + if(!tFoot()) + { + int exceptioncode = 0; + ElementImpl* foot = new HTMLTableSectionElementImpl(docPtr(), ID_TFOOT, true /*implicit */); + if(ElementImpl* firstBody = tFirstBody()) + insertBefore( foot, firstBody, exceptioncode ); + else + appendChild(foot, exceptioncode); + } + return tFoot(); +} + +void HTMLTableElementImpl::deleteTFoot( ) +{ + if(ElementImpl* foot = tFoot()) { + int exceptioncode = 0; + removeChild(foot, exceptioncode); + } +} + +HTMLElementImpl *HTMLTableElementImpl::createCaption( ) +{ + if(!caption()) + { + int exceptioncode = 0; + ElementImpl* tCaption = new HTMLTableCaptionElementImpl(docPtr()); + insertBefore( tCaption, firstChild(), exceptioncode ); + } + return caption(); +} + +void HTMLTableElementImpl::deleteCaption( ) +{ + if(ElementImpl* tCaption = caption()) { + int exceptioncode = 0; + removeChild(tCaption, exceptioncode); + } +} + +/** + Helper. This checks whether the section contains the desired index, and if so, + returns the section. Otherwise, it adjust the index, and returns 0. + indeces < 0 are considered to be infinite. + + lastSection is adjusted to reflect the parameter passed in. +*/ +static inline HTMLTableSectionElementImpl* processSection(HTMLTableSectionElementImpl* section, + HTMLTableSectionElementImpl* &lastSection, long& index) +{ + lastSection = section; + if ( index < 0 ) //append/last mode + return 0; + + long rows = section->numRows(); + if ( index >= rows ) { + section = 0; + index -= rows; + } + return section; +} + + +bool HTMLTableElementImpl::findRowSection(long index, + HTMLTableSectionElementImpl*& outSection, + long& outIndex) const +{ + HTMLTableSectionElementImpl* foot = tFoot(); + HTMLTableSectionElementImpl* head = tHead(); + + HTMLTableSectionElementImpl* section = 0L; + HTMLTableSectionElementImpl* lastSection = 0L; + + if ( head ) + section = processSection( head, lastSection, index ); + + if ( !section ) { + for ( NodeImpl *node = firstChild(); node; node = node->nextSibling() ) { + if ( ( node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY ) && + node != foot && node != head ) { + section = processSection( static_cast<HTMLTableSectionElementImpl *>(node), + lastSection, index ); + if ( section ) + break; + } + } + } + + if ( !section && foot ) + section = processSection( foot, lastSection, index ); + + outIndex = index; + if ( section ) { + outSection = section; + return true; + } else { + outSection = lastSection; + return false; + } +} + +HTMLElementImpl *HTMLTableElementImpl::insertRow( long index, int &exceptioncode ) +{ + // The DOM requires that we create a tbody if the table is empty + // (cf DOM2TS HTMLTableElement31 test). This includes even the cases where + // there are <tr>'s immediately under the table, as they're essentially + // ignored by these functions. + HTMLTableSectionElementImpl* foot = tFoot(); + HTMLTableSectionElementImpl* head = tHead(); + if(!tFirstBody() && !foot && !head) + setTBody( new HTMLTableSectionElementImpl(docPtr(), ID_TBODY, true /* implicit */) ); + + //kdDebug(6030) << k_funcinfo << index << endl; + + long sectionIndex; + HTMLTableSectionElementImpl* section; + if ( findRowSection( index, section, sectionIndex ) ) + return section->insertRow( sectionIndex, exceptioncode ); + else if ( index == -1 || sectionIndex == 0 ) + return section->insertRow( section->numRows(), exceptioncode ); + + // The index is too big. + exceptioncode = DOMException::INDEX_SIZE_ERR; + return 0L; +} + +void HTMLTableElementImpl::deleteRow( long index, int &exceptioncode ) +{ + long sectionIndex; + HTMLTableSectionElementImpl* section; + if ( findRowSection( index, section, sectionIndex ) ) + section->deleteRow( sectionIndex, exceptioncode ); + else if ( section && index == -1 ) + section->deleteRow( -1, exceptioncode ); + else + exceptioncode = DOMException::INDEX_SIZE_ERR; +} + +NodeImpl *HTMLTableElementImpl::appendChild(NodeImpl *child, int &exceptioncode) +{ + NodeImpl* retval = HTMLElementImpl::appendChild( child, exceptioncode ); + if(retval) + handleChildAppend( child ); + return retval; +} + +void HTMLTableElementImpl::handleChildAdd( NodeImpl *child ) +{ + if (!child) return; + switch(child->id()) { + case ID_CAPTION: + tCaption.childAdded(this, child); + break; + case ID_THEAD: + head.childAdded(this, child); + break; + case ID_TFOOT: + foot.childAdded(this, child); + break; + case ID_TBODY: + firstBody.childAdded(this, child); + break; + } +} + +void HTMLTableElementImpl::handleChildAppend( NodeImpl *child ) +{ + if (!child) return; + switch(child->id()) { + case ID_CAPTION: + tCaption.childAppended(child); + break; + case ID_THEAD: + head.childAppended(child); + break; + case ID_TFOOT: + foot.childAppended(child); + break; + case ID_TBODY: + firstBody.childAppended(child); + break; + } +} + +void HTMLTableElementImpl::handleChildRemove( NodeImpl *child ) +{ + if (!child) return; + switch(child->id()) { + case ID_CAPTION: + tCaption.childRemoved(this, child); + break; + case ID_THEAD: + head.childRemoved(this, child); + break; + case ID_TFOOT: + foot.childRemoved(this, child); + break; + case ID_TBODY: + firstBody.childRemoved(this, child); + break; + } +} + +NodeImpl *HTMLTableElementImpl::addChild(NodeImpl *child) +{ +#ifdef DEBUG_LAYOUT + kdDebug( 6030 ) << nodeName().string() << "(Table)::addChild( " << child->nodeName().string() << " )" << endl; +#endif + + NodeImpl *retval = HTMLElementImpl::addChild( child ); + if ( retval ) + handleChildAppend( child ); + + return retval; +} + +NodeImpl *HTMLTableElementImpl::insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode ) +{ + NodeImpl* retval = HTMLElementImpl::insertBefore( newChild, refChild, exceptioncode); + if (retval) + handleChildAdd( newChild ); + + return retval; +} + +void HTMLTableElementImpl::replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode ) +{ + handleChildRemove( oldChild ); //Always safe. + HTMLElementImpl::replaceChild( newChild, oldChild, exceptioncode ); + if ( !exceptioncode ) + handleChildAdd( newChild ); +} + +void HTMLTableElementImpl::removeChild ( NodeImpl *oldChild, int &exceptioncode ) +{ + handleChildRemove( oldChild ); + HTMLElementImpl::removeChild( oldChild, exceptioncode); +} + +void HTMLTableElementImpl::parseAttribute(AttributeImpl *attr) +{ + // ### to CSS!! + switch(attr->id()) + { + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength( CSS_PROP_WIDTH, attr->value() ); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_HEIGHT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_HEIGHT, attr->value()); + else + removeCSSProperty(CSS_PROP_HEIGHT); + break; + case ATTR_BORDER: + { + int border; + // ### this needs more work, as the border value is not only + // the border of the box, but also between the cells + if(!attr->val()) + border = 0; + else if(attr->val()->l == 0) + border = 1; + else + border = attr->val()->toInt(); +#ifdef DEBUG_DRAW_BORDER + border=1; +#endif + DOMString v = QString::number( border ); + addCSSLength(CSS_PROP_BORDER_WIDTH, v ); + + // wanted by HTML4 specs + if(!border) + frame = Void, rules = None; + else + frame = Box, rules = All; + + + if (attached()) updateFrame(); + break; + } + case ATTR_BGCOLOR: + if (!attr->value().isEmpty()) + addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value()); + else + removeCSSProperty(CSS_PROP_BACKGROUND_COLOR); + break; + case ATTR_BORDERCOLOR: + if(!attr->value().isEmpty()) { + addHTMLColor(CSS_PROP_BORDER_COLOR, attr->value()); + m_solid = true; + } + + if (attached()) updateFrame(); + break; + case ATTR_BACKGROUND: + { + if (!attr->value().isEmpty()) { + QString url = khtml::parseURL( attr->value() ).string(); + url = getDocument()->completeURL( url ); + addCSSProperty(CSS_PROP_BACKGROUND_IMAGE, "url('"+url+"')" ); + } + else + removeCSSProperty(CSS_PROP_BACKGROUND_IMAGE); + break; + } + case ATTR_FRAME: + + if ( strcasecmp( attr->value(), "void" ) == 0 ) + frame = Void; + else if ( strcasecmp( attr->value(), "border" ) == 0 ) + frame = Box; + else if ( strcasecmp( attr->value(), "box" ) == 0 ) + frame = Box; + else if ( strcasecmp( attr->value(), "hsides" ) == 0 ) + frame = Hsides; + else if ( strcasecmp( attr->value(), "vsides" ) == 0 ) + frame = Vsides; + else if ( strcasecmp( attr->value(), "above" ) == 0 ) + frame = Above; + else if ( strcasecmp( attr->value(), "below" ) == 0 ) + frame = Below; + else if ( strcasecmp( attr->value(), "lhs" ) == 0 ) + frame = Lhs; + else if ( strcasecmp( attr->value(), "rhs" ) == 0 ) + frame = Rhs; + + if (attached()) updateFrame(); + break; + case ATTR_RULES: + if ( strcasecmp( attr->value(), "none" ) == 0 ) + rules = None; + else if ( strcasecmp( attr->value(), "groups" ) == 0 ) + rules = Groups; + else if ( strcasecmp( attr->value(), "rows" ) == 0 ) + rules = Rows; + else if ( strcasecmp( attr->value(), "cols" ) == 0 ) + rules = Cols; + else if ( strcasecmp( attr->value(), "all" ) == 0 ) + rules = All; + if (attached()) updateFrame(); + break; + case ATTR_CELLSPACING: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_BORDER_SPACING, attr->value(), true); + else + removeCSSProperty(CSS_PROP_BORDER_SPACING); + break; + case ATTR_CELLPADDING: + if (!attr->value().isEmpty()) + padding = kMax( 0, attr->value().toInt() ); + else + padding = 1; + if (m_render && m_render->isTable()) { + static_cast<RenderTable *>(m_render)->setCellPadding(padding); + if (!m_render->needsLayout()) + m_render->setNeedsLayout(true); + } + break; + case ATTR_COLS: + { + // ### +#if 0 + int c; + c = attr->val()->toInt(); + addColumns(c-totalCols); +#endif + break; + + } + case ATTR_ALIGN: + setChanged(); + break; + case ATTR_VALIGN: + if (!attr->value().isEmpty()) + addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower()); + else + removeCSSProperty(CSS_PROP_VERTICAL_ALIGN); + break; + case ATTR_NOSAVE: + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +void HTMLTableElementImpl::attach() +{ + updateFrame(); + HTMLElementImpl::attach(); + if ( m_render && m_render->isTable() ) + static_cast<RenderTable *>(m_render)->setCellPadding( padding ); +} + +void HTMLTableElementImpl::close() +{ + ElementImpl* firstBody = tFirstBody(); + if (firstBody && !firstBody->closed()) + firstBody->close(); + HTMLElementImpl::close(); +} + +void HTMLTableElementImpl::updateFrame() +{ + int v = m_solid ? CSS_VAL_SOLID : CSS_VAL_OUTSET; + if ( frame & Above ) + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, v); + else + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_NONE); + if ( frame & Below ) + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, v); + else + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_NONE); + if ( frame & Lhs ) + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, v); + else + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_NONE); + if ( frame & Rhs ) + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, v); + else + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_NONE); +} + +// -------------------------------------------------------------------------- + +void HTMLTablePartElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_BGCOLOR: + if (attr->val()) + addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value() ); + else + removeCSSProperty(CSS_PROP_BACKGROUND_COLOR); + break; + case ATTR_BACKGROUND: + { + if (attr->val()) { + QString url = khtml::parseURL( attr->value() ).string(); + url = getDocument()->completeURL( url ); + addCSSProperty(CSS_PROP_BACKGROUND_IMAGE, "url('"+url+"')" ); + } + else + removeCSSProperty(CSS_PROP_BACKGROUND_IMAGE); + break; + } + case ATTR_BORDERCOLOR: + { + if(!attr->value().isEmpty()) { + addHTMLColor(CSS_PROP_BORDER_COLOR, attr->value()); + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID); + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID); + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID); + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID); + } + break; + } + case ATTR_ALIGN: + { + DOMString v = attr->value(); + if ( strcasecmp( attr->value(), "middle" ) == 0 || strcasecmp( attr->value(), "center" ) == 0 ) + addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_CENTER); + else if (strcasecmp(attr->value(), "absmiddle") == 0) + addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL_CENTER); + else if (strcasecmp(attr->value(), "left") == 0) + addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_LEFT); + else if (strcasecmp(attr->value(), "right") == 0) + addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_RIGHT); + else + addCSSProperty(CSS_PROP_TEXT_ALIGN, v); + break; + } + case ATTR_VALIGN: + { + if (!attr->value().isEmpty()) + addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower()); + else + removeCSSProperty(CSS_PROP_VERTICAL_ALIGN); + break; + } + case ATTR_HEIGHT: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_HEIGHT, attr->value()); + else + removeCSSProperty(CSS_PROP_HEIGHT); + break; + case ATTR_NOSAVE: + break; + default: + HTMLElementImpl::parseAttribute(attr); + } +} + +// ------------------------------------------------------------------------- + +HTMLTableSectionElementImpl::HTMLTableSectionElementImpl(DocumentImpl *doc, + ushort tagid, bool implicit) + : HTMLTablePartElementImpl(doc) +{ + _id = tagid; + m_implicit = implicit; +} + +HTMLTableSectionElementImpl::~HTMLTableSectionElementImpl() +{ +} + +NodeImpl::Id HTMLTableSectionElementImpl::id() const +{ + return _id; +} + +// these functions are rather slow, since we need to get the row at +// the index... but they aren't used during usual HTML parsing anyway +HTMLElementImpl *HTMLTableSectionElementImpl::insertRow( long index, int& exceptioncode ) +{ + HTMLTableRowElementImpl *r = 0L; + HTMLCollectionImpl rows(const_cast<HTMLTableSectionElementImpl*>(this), HTMLCollectionImpl::TSECTION_ROWS); + int numRows = rows.length(); + //kdDebug(6030) << k_funcinfo << "index=" << index << " numRows=" << numRows << endl; + if ( index < -1 || index > numRows ) { + exceptioncode = DOMException::INDEX_SIZE_ERR; // per the DOM + } + else + { + r = new HTMLTableRowElementImpl(docPtr()); + if ( numRows == index || index == -1 ) + appendChild(r, exceptioncode); + else { + NodeImpl *n; + if(index < 1) + n = firstChild(); + else + n = rows.item(index); + insertBefore(r, n, exceptioncode ); + } + } + return r; +} + +void HTMLTableSectionElementImpl::deleteRow( long index, int &exceptioncode ) +{ + HTMLCollectionImpl rows(const_cast<HTMLTableSectionElementImpl*>(this), HTMLCollectionImpl::TSECTION_ROWS); + int numRows = rows.length(); + if ( index == -1 ) index = numRows - 1; + if( index >= 0 && index < numRows ) + HTMLElementImpl::removeChild(rows.item(index), exceptioncode); + else + exceptioncode = DOMException::INDEX_SIZE_ERR; +} + + +int HTMLTableSectionElementImpl::numRows() const +{ + HTMLCollectionImpl rows(const_cast<HTMLTableSectionElementImpl*>(this), HTMLCollectionImpl::TSECTION_ROWS); + return rows.length(); +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLTableRowElementImpl::id() const +{ + return ID_TR; +} + +long HTMLTableRowElementImpl::rowIndex() const +{ + int rIndex = 0; + + NodeImpl *table = parentNode(); + if ( !table ) + return -1; + table = table->parentNode(); + if ( !table || table->id() != ID_TABLE ) + return -1; + + HTMLTableSectionElementImpl *head = static_cast<HTMLTableElementImpl *>(table)->tHead(); + HTMLTableSectionElementImpl *foot = static_cast<HTMLTableElementImpl *>(table)->tFoot(); + + if ( head ) { + const NodeImpl *row = head->firstChild(); + while ( row ) { + if ( row == this ) + return rIndex; + rIndex++; + row = row->nextSibling(); + } + } + + NodeImpl *node = table->firstChild(); + while ( node ) { + if ( node != foot && node != head && (node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY) ) { + HTMLTableSectionElementImpl* section = static_cast<HTMLTableSectionElementImpl *>(node); + const NodeImpl *row = section->firstChild(); + while ( row ) { + if ( row == this ) + return rIndex; + rIndex++; + row = row->nextSibling(); + } + } + node = node->nextSibling(); + } + const NodeImpl *row = foot->firstChild(); + while ( row ) { + if ( row == this ) + return rIndex; + rIndex++; + row = row->nextSibling(); + } + // should never happen + return -1; +} + +long HTMLTableRowElementImpl::sectionRowIndex() const +{ + int rIndex = 0; + const NodeImpl *n = this; + do { + n = n->previousSibling(); + if (n && n->id() == ID_TR) + rIndex++; + } + while (n); + + return rIndex; +} + +HTMLElementImpl *HTMLTableRowElementImpl::insertCell( long index, int &exceptioncode ) +{ + HTMLTableCellElementImpl *c = 0L; + NodeListImpl *children = childNodes(); + int numCells = children ? children->length() : 0; + if ( index < -1 || index > numCells ) + exceptioncode = DOMException::INDEX_SIZE_ERR; // per the DOM + else + { + c = new HTMLTableCellElementImpl(docPtr(), ID_TD); + if(numCells == index || index == -1) + appendChild(c, exceptioncode); + else { + NodeImpl *n; + if(index < 1) + n = firstChild(); + else + n = children->item(index); + insertBefore(c, n, exceptioncode); + } + } + delete children; + return c; +} + +void HTMLTableRowElementImpl::deleteCell( long index, int &exceptioncode ) +{ + NodeListImpl *children = childNodes(); + int numCells = children ? children->length() : 0; + if ( index == -1 ) index = numCells-1; + if( index >= 0 && index < numCells ) + HTMLElementImpl::removeChild(children->item(index), exceptioncode); + else + exceptioncode = DOMException::INDEX_SIZE_ERR; + delete children; +} + +// ------------------------------------------------------------------------- + +HTMLTableCellElementImpl::HTMLTableCellElementImpl(DocumentImpl *doc, int tag) + : HTMLTablePartElementImpl(doc) +{ + _col = -1; + _row = -1; + cSpan = rSpan = 1; + _id = tag; + rowHeight = 0; + m_solid = false; +} + +HTMLTableCellElementImpl::~HTMLTableCellElementImpl() +{ +} + +long HTMLTableCellElementImpl::cellIndex() const +{ + int index = 0; + for (const NodeImpl * node = previousSibling(); node; node = node->previousSibling()) { + if (node->id() == ID_TD || node->id() == ID_TH) + index++; + } + + return index; +} + +void HTMLTableCellElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_BORDER: + // euhm? not supported by other browsers as far as I can see (Dirk) + //addCSSLength(CSS_PROP_BORDER_WIDTH, attr->value()); + break; + case ATTR_ROWSPAN: + // ### + rSpan = attr->val() ? attr->val()->toInt() : 1; + // limit this to something not causing an overflow with short int + if(rSpan < 1 || rSpan > 1024) rSpan = 1; + break; + case ATTR_COLSPAN: + // ### + cSpan = attr->val() ? attr->val()->toInt() : 1; + // limit this to something not causing an overflow with short int + if(cSpan < 1 || cSpan > 1024) cSpan = 1; + break; + case ATTR_NOWRAP: + if (attr->val() != 0) + addCSSProperty(CSS_PROP_WHITE_SPACE, CSS_VAL__KHTML_NOWRAP); + else + removeCSSProperty(CSS_PROP_WHITE_SPACE); + break; + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength( CSS_PROP_WIDTH, attr->value() ); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_NOSAVE: + break; + default: + HTMLTablePartElementImpl::parseAttribute(attr); + } +} + +void HTMLTableCellElementImpl::attach() +{ + HTMLElementImpl* p = static_cast<HTMLElementImpl*>(parentNode()); + while(p && p->id() != ID_TABLE) + p = static_cast<HTMLElementImpl*>(p->parentNode()); + + if(p) { + HTMLTableElementImpl* table = static_cast<HTMLTableElementImpl*>(p); + if (table->rules == HTMLTableElementImpl::None) { + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_NONE); + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_NONE); + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_NONE); + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_NONE); + } + else { + addCSSProperty(CSS_PROP_BORDER_WIDTH, "1px"); + int v = (table->m_solid || m_solid) ? CSS_VAL_SOLID : CSS_VAL_INSET; + addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, v); + addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, v); + addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, v); + addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, v); + + if (!m_solid) + addCSSProperty(CSS_PROP_BORDER_COLOR, CSS_VAL_INHERIT); + } + } + + HTMLTablePartElementImpl::attach(); +} + +// ------------------------------------------------------------------------- + +HTMLTableColElementImpl::HTMLTableColElementImpl(DocumentImpl *doc, ushort i) + : HTMLTablePartElementImpl(doc) +{ + _id = i; + _span = 1; +} + +NodeImpl::Id HTMLTableColElementImpl::id() const +{ + return _id; +} + + +void HTMLTableColElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_SPAN: + _span = attr->val() ? attr->val()->toInt() : 1; + if (_span < 1) _span = 1; + break; + case ATTR_WIDTH: + if (!attr->value().isEmpty()) + addCSSLength(CSS_PROP_WIDTH, attr->value(), false, true ); + else + removeCSSProperty(CSS_PROP_WIDTH); + break; + case ATTR_VALIGN: + if (!attr->value().isEmpty()) + addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower()); + else + removeCSSProperty(CSS_PROP_VERTICAL_ALIGN); + break; + default: + HTMLTablePartElementImpl::parseAttribute(attr); + } + +} + +// ------------------------------------------------------------------------- + +NodeImpl::Id HTMLTableCaptionElementImpl::id() const +{ + return ID_CAPTION; +} + + +void HTMLTableCaptionElementImpl::parseAttribute(AttributeImpl *attr) +{ + switch(attr->id()) + { + case ATTR_ALIGN: + if (!attr->value().isEmpty()) + addCSSProperty(CSS_PROP_CAPTION_SIDE, attr->value().lower()); + else + removeCSSProperty(CSS_PROP_CAPTION_SIDE); + break; + default: + HTMLElementImpl::parseAttribute(attr); + } + +} diff --git a/khtml/html/html_tableimpl.h b/khtml/html/html_tableimpl.h new file mode 100644 index 000000000..76d81ce50 --- /dev/null +++ b/khtml/html/html_tableimpl.h @@ -0,0 +1,329 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2006 Maksim Orlovich (maksim@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#ifndef HTML_TABLEIMPL_H +#define HTML_TABLEIMPL_H + +#include "misc/htmltags.h" +#include "html/html_elementimpl.h" + +namespace DOM { + +class DOMString; +class HTMLTableElementImpl; +class HTMLTableSectionElementImpl; +class HTMLTableSectionElement; +class HTMLTableRowElementImpl; +class HTMLTableRowElement; +class HTMLTableCellElementImpl; +class HTMLTableCellElement; +class HTMLTableColElementImpl; +class HTMLTableColElement; +class HTMLTableCaptionElementImpl; +class HTMLTableCaptionElement; +class HTMLElement; +class HTMLCollection; + +// ------------------------------------------------------------------------- + +class HTMLTablePartElementImpl : public HTMLElementImpl + +{ +public: + HTMLTablePartElementImpl(DocumentImpl *doc) + : HTMLElementImpl(doc) + { } + + virtual void parseAttribute(AttributeImpl *attr); +}; + +// ------------------------------------------------------------------------- + +class HTMLTableSectionElementImpl : public HTMLTablePartElementImpl +{ +public: + HTMLTableSectionElementImpl(DocumentImpl *doc, ushort tagid, bool implicit); + + ~HTMLTableSectionElementImpl(); + + virtual Id id() const; + + HTMLElementImpl *insertRow ( long index, int& exceptioncode ); + void deleteRow ( long index, int& exceptioncode ); + + int numRows() const; + +protected: + ushort _id; +}; + +// ------------------------------------------------------------------------- + +class HTMLTableRowElementImpl : public HTMLTablePartElementImpl +{ +public: + HTMLTableRowElementImpl(DocumentImpl *doc) + : HTMLTablePartElementImpl(doc) {} + + virtual Id id() const; + + long rowIndex() const; + long sectionRowIndex() const; + + HTMLElementImpl *insertCell ( long index, int &exceptioncode ); + void deleteCell ( long index, int &exceptioncode ); + +protected: + int ncols; +}; + +// ------------------------------------------------------------------------- + +class HTMLTableCellElementImpl : public HTMLTablePartElementImpl +{ +public: + HTMLTableCellElementImpl(DocumentImpl *doc, int tagId); + ~HTMLTableCellElementImpl(); + + long cellIndex() const; + + int col() const { return _col; } + void setCol(int col) { _col = col; } + int row() const { return _row; } + void setRow(int r) { _row = r; } + + int colSpan() const { return cSpan; } + int rowSpan() const { return rSpan; } + + virtual Id id() const { return _id; } + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); + +protected: + int _row; + int _col; + int rSpan; + int cSpan; + int _id; + int rowHeight; + bool m_solid : 1; + bool m_nowrap : 1; +}; + +// ------------------------------------------------------------------------- + +class HTMLTableColElementImpl : public HTMLTablePartElementImpl +{ +public: + HTMLTableColElementImpl(DocumentImpl *doc, ushort i); + + virtual Id id() const; + + void setTable(HTMLTableElementImpl *t) { table = t; } + + // overrides + virtual void parseAttribute(AttributeImpl *attr); + + int span() const { return _span; } + +protected: + // could be ID_COL or ID_COLGROUP ... The DOM is not quite clear on + // this, but since both elements work quite similar, we use one + // DOMElement for them... + ushort _id; + int _span; + HTMLTableElementImpl *table; +}; + +// ------------------------------------------------------------------------- + +class HTMLTableCaptionElementImpl : public HTMLTablePartElementImpl +{ +public: + HTMLTableCaptionElementImpl(DocumentImpl *doc) + : HTMLTablePartElementImpl(doc) {} + + virtual Id id() const; + virtual void parseAttribute(AttributeImpl *attr); +}; + +// ------------------------------------------------------------------------- + +/* +This class helps memorize pointers to child objects that may be +yanked around via the DOM. It always picks the first pointer of the +given type. + +The pointer it stores can have 3 meanings: +0 -- no child +parent -- no idea about the state +other -- pointer to the child +*/ +template<typename ChildType, int ChildId> class ChildHolder +{ +public: + ChildHolder():ptr(0) {} + + ChildType* get(const ElementImpl* parent) const { + if (static_cast<const NodeImpl *>(ptr) == parent) { + //Do lookup. + ptr = 0; + for (NodeImpl* child = parent->firstChild(); child; child = child->nextSibling()) + if (child->id() == ChildId) { + ptr = static_cast<ElementImpl*>(child); + break; + } + } + return static_cast<ChildType*>(ptr); + } + + void childAdded(ElementImpl* parent, NodeImpl* child) { + if (ptr) + ptr = parent; //No clue now.. + else + ptr = child; + } + + void childAppended(NodeImpl* child) { + if (!ptr) + ptr = child; + } + + void childRemoved(ElementImpl* parent, NodeImpl* child) { + if (child == ptr) + ptr = parent; //We removed what was pointing - no clue now.. + //else things are unchanged. + } + + void operator =(ChildType* child) { + ptr = child; + } +private: + mutable NodeImpl* ptr; +}; + +// ------------------------------------------------------------------------- +class HTMLTableElementImpl : public HTMLElementImpl +{ +public: + enum Rules { + None = 0x00, + RGroups = 0x01, + CGroups = 0x02, + Groups = 0x03, + Rows = 0x05, + Cols = 0x0a, + All = 0x0f + }; + enum Frame { + Void = 0x00, + Above = 0x01, + Below = 0x02, + Lhs = 0x04, + Rhs = 0x08, + Hsides = 0x03, + Vsides = 0x0c, + Box = 0x0f + }; + + HTMLTableElementImpl(DocumentImpl *doc); + ~HTMLTableElementImpl(); + + virtual Id id() const; + + HTMLTableCaptionElementImpl *caption() const { return tCaption.get(this); } + NodeImpl *setCaption( HTMLTableCaptionElementImpl * ); + + HTMLTableSectionElementImpl *tHead() const { return head.get(this); } + NodeImpl *setTHead( HTMLTableSectionElementImpl * ); + + HTMLTableSectionElementImpl *tFoot() const { return foot.get(this); } + NodeImpl *setTFoot( HTMLTableSectionElementImpl * ); + + NodeImpl *setTBody( HTMLTableSectionElementImpl * ); + + HTMLElementImpl *createTHead ( ); + void deleteTHead ( ); + HTMLElementImpl *createTFoot ( ); + void deleteTFoot ( ); + HTMLElementImpl *createCaption ( ); + void deleteCaption ( ); + HTMLElementImpl *insertRow ( long index, int &exceptioncode ); + void deleteRow ( long index, int &exceptioncode ); + + // overrides + virtual NodeImpl *addChild(NodeImpl *child); + virtual NodeImpl *insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode ); + virtual void replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode ); + virtual void removeChild ( NodeImpl *oldChild, int &exceptioncode ); + virtual NodeImpl *appendChild ( NodeImpl *newChild, int &exceptioncode ); + + virtual void parseAttribute(AttributeImpl *attr); + virtual void attach(); + virtual void close(); + + /* Tries to find the section containing row number outIndex. + Returns whether it succeeded or not. negative outIndex values + are interpreted as being infinite. + + On success, outSection, outIndex points to section, and index in that + section. + + On failure, outSection points to the last section of the table, and + index is the offset the row would have if there was an additional section. + */ + bool findRowSection(long inIndex, + HTMLTableSectionElementImpl*& outSection, + long& outIndex) const; +protected: + //Actual implementations of keeping things in place. + void handleChildAdd ( NodeImpl *newChild ); + void handleChildAppend( NodeImpl *newChild ); + void handleChildRemove( NodeImpl *oldChild ); + + void updateFrame(); + + ChildHolder<HTMLTableSectionElementImpl, ID_THEAD> head; + ChildHolder<HTMLTableSectionElementImpl, ID_TFOOT> foot; + ChildHolder<HTMLTableSectionElementImpl, ID_TBODY> firstBody; + ChildHolder<HTMLTableCaptionElementImpl, ID_CAPTION> tCaption; + + HTMLTableSectionElementImpl *tFirstBody() const { return firstBody.get(this); } + + Frame frame : 4; + Rules rules : 4; + + bool m_solid : 1; + uint unused : 7; + ushort padding : 16; + friend class HTMLTableCellElementImpl; +}; + + +} //namespace + +#endif + diff --git a/khtml/html/htmlparser.cpp b/khtml/html/htmlparser.cpp new file mode 100644 index 000000000..1fe79f9e0 --- /dev/null +++ b/khtml/html/htmlparser.cpp @@ -0,0 +1,1733 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 1997 Martin Jones (mjones@kde.org) + (C) 1997 Torben Weis (weis@kde.org) + (C) 1999,2001 Lars Knoll (knoll@kde.org) + (C) 2000,2001 Dirk Mueller (mueller@kde.org) + (C) 2003 Apple Computer, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +//---------------------------------------------------------------------------- +// +// KDE HTML Widget -- HTML Parser +// #define PARSER_DEBUG + +#include "dom/dom_exception.h" + +#include "html/html_baseimpl.h" +#include "html/html_blockimpl.h" +#include "html/html_documentimpl.h" +#include "html/html_elementimpl.h" +#include "html/html_formimpl.h" +#include "html/html_headimpl.h" +#include "html/html_imageimpl.h" +#include "html/html_inlineimpl.h" +#include "html/html_listimpl.h" +#include "html/html_miscimpl.h" +#include "html/html_tableimpl.h" +#include "html/html_objectimpl.h" +#include "xml/dom_textimpl.h" +#include "xml/dom_nodeimpl.h" +#include "misc/htmlhashes.h" +#include "html/htmltokenizer.h" +#include "khtmlview.h" +#include "khtml_part.h" +#include "khtml_factory.h" +#include "css/cssproperties.h" +#include "css/cssvalues.h" +#include "css/csshelper.h" + +#include "rendering/render_object.h" + +#include "html/htmlparser.h" +#include <kdebug.h> +#include <klocale.h> + +using namespace DOM; +using namespace khtml; + +//---------------------------------------------------------------------------- + +/** + * @internal + */ +class HTMLStackElem +{ +public: + HTMLStackElem( int _id, + int _level, + DOM::NodeImpl *_node, + bool _inline, + HTMLStackElem * _next ) + : + id(_id), + level(_level), + strayTableContent(false), + m_inline(_inline), + node(_node), + next(_next) + { node->ref(); } + + ~HTMLStackElem() + { node->deref(); } + + void setNode(NodeImpl* newNode) + { + newNode->ref(); + node->deref(); + node = newNode; + } + + int id; + int level; + bool strayTableContent; + bool m_inline; + NodeImpl *node; + HTMLStackElem *next; +}; + +/** + * @internal + * + * The parser parses tokenized input into the document, building up the + * document tree. If the document is wellformed, parsing it is + * straightforward. + * Unfortunately, people can't write wellformed HTML documents, so the parser + * has to be tolerant about errors. + * + * We have to take care of the following error conditions: + * 1. The element being added is explicitly forbidden inside some outer tag. + * In this case we should close all tags up to the one, which forbids + * the element, and add it afterwards. + * 2. We are not allowed to add the element directly. It could be, that + * the person writing the document forgot some tag inbetween (or that the + * tag inbetween is optional...) This could be the case with the following + * tags: HTML HEAD BODY TBODY TR TD LI (did I forget any?) + * 3. We wan't to add a block element inside to an inline element. Close all + * inline elements up to the next higher block element. + * 4. If this doesn't help close elements, until we are allowed to add the + * element or ignore the tag. + * + */ + +KHTMLParser::KHTMLParser( KHTMLView *_parent, DocumentImpl *doc) +{ + //kdDebug( 6035 ) << "parser constructor" << endl; +#if SPEED_DEBUG > 0 + qt.start(); +#endif + + HTMLWidget = _parent; + document = doc; + + blockStack = 0; + current = 0; + + // ID_CLOSE_TAG == Num of tags + forbiddenTag = new ushort[ID_CLOSE_TAG+1]; + + reset(); +} + +KHTMLParser::KHTMLParser( DOM::DocumentFragmentImpl *i, DocumentImpl *doc ) +{ + HTMLWidget = 0; + document = doc; + + forbiddenTag = new ushort[ID_CLOSE_TAG+1]; + + blockStack = 0; + current = 0; + + reset(); + + setCurrent(i); + + inBody = true; +} + +KHTMLParser::~KHTMLParser() +{ +#if SPEED_DEBUG > 0 + kdDebug( ) << "TIME: parsing time was = " << qt.elapsed() << endl; +#endif + + freeBlock(); + + if (current) current->deref(); + + delete [] forbiddenTag; + delete isindex; +} + +void KHTMLParser::reset() +{ + setCurrent ( document ); + + freeBlock(); + + // before parsing no tags are forbidden... + memset(forbiddenTag, 0, (ID_CLOSE_TAG+1)*sizeof(ushort)); + + inBody = false; + haveFrameSet = false; + haveContent = false; + haveBody = false; + haveTitle = false; + inSelect = false; + inStrayTableContent = 0; + m_inline = false; + + form = 0; + map = 0; + head = 0; + end = false; + isindex = 0; + + discard_until = 0; +} + +void KHTMLParser::parseToken(Token *t) +{ + if (t->tid > 2*ID_CLOSE_TAG) + { + kdDebug( 6035 ) << "Unknown tag!! tagID = " << t->tid << endl; + return; + } + if(discard_until) { + if(t->tid == discard_until) + discard_until = 0; + + // do not skip </iframe> + if ( discard_until || current->id() + ID_CLOSE_TAG != t->tid ) + return; + } + +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "\n\n==> parser: processing token " << getTagName(t->tid) << "(" << t->tid << ")" + << " current = " << getTagName(current->id()) << "(" << current->id() << ")" << endl; + kdDebug(6035) << "inline=" << m_inline << " inBody=" << inBody << " haveFrameSet=" << haveFrameSet << " haveContent=" << haveContent << endl; +#endif + + // holy shit. apparently some sites use </br> instead of <br> + // be compatible with IE and NS + if(t->tid == ID_BR+ID_CLOSE_TAG && document->inCompatMode()) + t->tid -= ID_CLOSE_TAG; + + if(t->tid > ID_CLOSE_TAG) + { + processCloseTag(t); + return; + } + + // ignore spaces, if we're not inside a paragraph or other inline code + if( t->tid == ID_TEXT && t->text ) { + if(inBody && !skipMode() && + current->id() != ID_STYLE && current->id() != ID_TITLE && + current->id() != ID_SCRIPT && + !t->text->containsOnlyWhitespace()) haveContent = true; +#ifdef PARSER_DEBUG + kdDebug(6035) << "length="<< t->text->l << " text='" << QConstString(t->text->s, t->text->l).string() << "'" << endl; +#endif + } + + NodeImpl *n = getElement(t); + // just to be sure, and to catch currently unimplemented stuff + if(!n) + return; + + // set attributes + if(n->isElementNode() && t->tid != ID_ISINDEX) + { + ElementImpl *e = static_cast<ElementImpl *>(n); + e->setAttributeMap(t->attrs); + + // take care of optional close tags + if(endTag[e->id()] == DOM::OPTIONAL) + popBlock(t->tid); + } + + // if this tag is forbidden inside the current context, pop + // blocks until we are allowed to add it... + while(blockStack && forbiddenTag[t->tid]) { +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "t->id: " << t->tid << " is forbidden :-( " << endl; +#endif + popOneBlock(); + } + + // sometimes flat doesn't make sense + switch(t->tid) { + case ID_SELECT: + case ID_OPTION: + t->flat = false; + } + + // the tokenizer needs the feedback for space discarding + if ( tagPriority[t->tid] == 0 ) + t->flat = true; + + if ( !insertNode(n, t->flat) ) { + // we couldn't insert the node... +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "insertNode failed current=" << current->id() << ", new=" << n->id() << "!" << endl; +#endif + if (map == n) + { +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << " --> resetting map!" << endl; +#endif + map = 0; + } + if (form == n) + { +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << " --> resetting form!" << endl; +#endif + form = 0; + } + delete n; + } +} + +static bool isTableRelatedTag(int id) +{ + return (id == ID_TR || id == ID_TD || id == ID_TABLE || id == ID_TBODY || id == ID_TFOOT || id == ID_THEAD || + id == ID_TH); +} + +bool KHTMLParser::insertNode(NodeImpl *n, bool flat) +{ + int id = n->id(); + + // let's be stupid and just try to insert it. + // this should work if the document is wellformed +#ifdef PARSER_DEBUG + NodeImpl *tmp = current; +#endif + NodeImpl *newNode = current->addChild(n); + if ( newNode ) { +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "added " << n->nodeName().string() << " to " << tmp->nodeName().string() << ", new current=" << newNode->nodeName().string() << endl; +#endif + // We allow TABLE > FORM in dtd.cpp, but do not allow the form have children in this case + if (current->id() == ID_TABLE && id == ID_FORM) { + flat = true; + static_cast<HTMLFormElementImpl*>(n)->setMalformed(true); + } + + // don't push elements without end tag on the stack + if(tagPriority[id] != 0 && !flat) { +#if SPEED_DEBUG < 2 + if(!n->attached() && HTMLWidget ) + n->attach(); +#endif + if(n->isInline()) m_inline = true; + pushBlock(id, tagPriority[id]); + setCurrent( newNode ); + } else { +#if SPEED_DEBUG < 2 + if(!n->attached() && HTMLWidget) + n->attach(); + if (n->maintainsState()) { + document->registerMaintainsState(n); + QString state(document->nextState()); + if (!state.isNull()) n->restoreState(state); + } + n->close(); +#endif + if(n->isInline()) m_inline = true; + } + + +#if SPEED_DEBUG < 1 + if(tagPriority[id] == 0 && n->renderer()) + n->renderer()->calcMinMaxWidth(); +#endif + return true; + } else { +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "ADDING NODE FAILED!!!! current = " << current->nodeName().string() << ", new = " << n->nodeName().string() << endl; +#endif + // error handling... + HTMLElementImpl *e; + bool handled = false; + + // first switch on current element for elements with optional end-tag and inline-only content + switch(current->id()) + { + case ID_P: + case ID_DT: + if(!n->isInline()) + { + popBlock(current->id()); + return insertNode(n); + } + break; + default: + break; + } + + // switch according to the element to insert + switch(id) + { + case ID_TR: + case ID_TH: + case ID_TD: + if (inStrayTableContent && !isTableRelatedTag(current->id())) { + // pop out to the nearest enclosing table-related tag. + while (blockStack && !isTableRelatedTag(current->id())) + popOneBlock(); + return insertNode(n); + } + break; + case ID_COMMENT: + break; + case ID_HEAD: + // ### allow not having <HTML> in at all, as per HTML spec + if (!current->isDocumentNode() && current->id() != ID_HTML ) + return false; + break; + case ID_META: + case ID_LINK: + case ID_ISINDEX: + case ID_BASE: + if( !head ) + createHead(); + if( head ) { + if ( head->addChild(n) ) { +#if SPEED_DEBUG < 2 + if(!n->attached() && HTMLWidget) + n->attach(); +#endif + } + + return true; + } + + break; + case ID_HTML: + if (!current->isDocumentNode() ) { + if ( doc()->firstChild()->id() == ID_HTML) { + // we have another <HTML> element.... apply attributes to existing one + // make sure we don't overwrite already existing attributes + NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true); + NamedAttrMapImpl *bmap = static_cast<ElementImpl*>(doc()->firstChild())->attributes(false); + bool changed = false; + for (unsigned long l = 0; map && l < map->length(); ++l) { + NodeImpl::Id attrId = map->idAt(l); + DOMStringImpl *attrValue = map->valueAt(l); + changed = !bmap->getValue(attrId); + bmap->setValue(attrId,attrValue); + } + if ( changed ) + doc()->recalcStyle( NodeImpl::Inherit ); + } + return false; + } + break; + case ID_TITLE: + case ID_STYLE: + if ( !head ) + createHead(); + if ( head ) { + DOM::NodeImpl *newNode = head->addChild(n); + if ( newNode ) { + pushBlock(id, tagPriority[id]); + setCurrent ( newNode ); +#if SPEED_DEBUG < 2 + if(!n->attached() && HTMLWidget) + n->attach(); +#endif + } else { +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "adding style before to body failed!!!!" << endl; +#endif + discard_until = ID_STYLE + ID_CLOSE_TAG; + return false; + } + return true; + } else if(inBody) { + discard_until = id + ID_CLOSE_TAG; + return false; + } + break; + case ID_SCRIPT: + // if we failed to insert it, go into skip mode + discard_until = id + ID_CLOSE_TAG; + break; + case ID_BODY: + if(inBody && doc()->body()) { + // we have another <BODY> element.... apply attributes to existing one + // make sure we don't overwrite already existing attributes + // some sites use <body bgcolor=rightcolor>...<body bgcolor=wrongcolor> + NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true); + NamedAttrMapImpl *bmap = doc()->body()->attributes(false); + bool changed = false; + for (unsigned long l = 0; map && l < map->length(); ++l) { + NodeImpl::Id attrId = map->idAt(l); + DOMStringImpl *attrValue = map->valueAt(l); + if ( !bmap->getValue(attrId) ) { + bmap->setValue(attrId,attrValue); + changed = true; + } + } + if ( changed ) + doc()->recalcStyle( NodeImpl::Inherit ); + } else if ( current->isDocumentNode() ) + break; + return false; + break; + + // the following is a hack to move non rendered elements + // outside of tables. + // needed for broken constructs like <table><form ...><tr>.... + case ID_INPUT: + { + ElementImpl *e = static_cast<ElementImpl *>(n); + DOMString type = e->getAttribute(ATTR_TYPE); + + if ( strcasecmp( type, "hidden" ) != 0 ) + break; + // Fall through! + } + case ID_TEXT: + { + // Don't try to fit random white-space anywhere + TextImpl *t = static_cast<TextImpl *>(n); + if (t->containsOnlyWhitespace()) + return false; + // ignore text inside the following elements. + switch(current->id()) + { + case ID_SELECT: + return false; + default: + ; + // fall through!! + }; + break; + } + case ID_DL: + popBlock( ID_DT ); + if ( current->id() == ID_DL ) { + e = new HTMLGenericElementImpl( document, ID_DD ); + insertNode( e ); + handled = true; + } + break; + case ID_DT: + e = new HTMLDListElementImpl(document); + if ( insertNode(e) ) { + insertNode(n); + return true; + } + break; + case ID_AREA: + { + if(map) + { + map->addChild(n); +#if SPEED_DEBUG < 2 + if(!n->attached() && HTMLWidget) + n->attach(); +#endif + handled = true; + return true; + } + else + return false; + } + case ID_CAPTION: { + switch (current->id()) { + case ID_THEAD: + case ID_TBODY: + case ID_TFOOT: + case ID_TR: + case ID_TH: + case ID_TD: { + NodeImpl* tsection = current; + if (current->id() == ID_TR) + tsection = current->parent(); + else if (current->id() == ID_TD || current->id() == ID_TH) + tsection = current->parent()->parent(); + NodeImpl* table = tsection->parent(); + int exceptioncode = 0; + table->insertBefore(n, tsection, exceptioncode); + pushBlock(id, tagPriority[id]); + setCurrent(n); + inStrayTableContent++; + blockStack->strayTableContent = true; + return true; + } + default: + break; + } + break; + } + + case ID_THEAD: + case ID_TBODY: + case ID_TFOOT: + case ID_COLGROUP: { + if (isTableRelatedTag(current->id())) { + while (blockStack && current->id() != ID_TABLE && isTableRelatedTag(current->id())) + popOneBlock(); + return insertNode(n); + } + } + default: + break; + } + + // switch on the currently active element + switch(current->id()) + { + case ID_HTML: + switch(id) + { + case ID_SCRIPT: + case ID_STYLE: + case ID_META: + case ID_LINK: + case ID_OBJECT: + case ID_EMBED: + case ID_TITLE: + case ID_ISINDEX: + case ID_BASE: + if(!head) { + head = new HTMLHeadElementImpl(document); + e = head; + insertNode(e); + handled = true; + } + break; + case ID_TEXT: { + TextImpl *t = static_cast<TextImpl *>(n); + if (t->containsOnlyWhitespace()) + return false; + /* Fall through to default */ + } + default: + if ( haveFrameSet ) break; + e = new HTMLBodyElementImpl(document); + startBody(); + insertNode(e); + handled = true; + break; + } + break; + case ID_HEAD: + // we can get here only if the element is not allowed in head. + if (id == ID_HTML) + return false; + else { + // This means the body starts here... + if ( haveFrameSet ) break; + popBlock(ID_HEAD); + e = new HTMLBodyElementImpl(document); + startBody(); + insertNode(e); + handled = true; + } + break; + case ID_BODY: + break; + case ID_CAPTION: + // Illegal content in a caption. Close the caption and try again. + popBlock(ID_CAPTION); + switch( id ) { + case ID_THEAD: + case ID_TFOOT: + case ID_TBODY: + case ID_TR: + case ID_TD: + case ID_TH: + return insertNode(n, flat); + } + break; + case ID_TABLE: + case ID_THEAD: + case ID_TFOOT: + case ID_TBODY: + case ID_TR: + switch(id) + { + case ID_TABLE: + popBlock(ID_TABLE); // end the table + handled = checkChild( current->id(), id, !doc()->inCompatMode()); + break; + default: + { + NodeImpl *node = current; + NodeImpl *parent = node->parentNode(); + // A script may have removed the current node's parent from the DOM + // http://bugzilla.opendarwin.org/show_bug.cgi?id=7137 + // FIXME: we should do real recovery here and re-parent with the correct node. + if (!parent) + return false; + NodeImpl *parentparent = parent->parentNode(); + + if (n->isTextNode() || + ( node->id() == ID_TR && + ( parent->id() == ID_THEAD || + parent->id() == ID_TBODY || + parent->id() == ID_TFOOT ) && parentparent->id() == ID_TABLE ) || + ( !checkChild( ID_TR, id ) && ( node->id() == ID_THEAD || node->id() == ID_TBODY || node->id() == ID_TFOOT ) && + parent->id() == ID_TABLE ) ) + { + node = (node->id() == ID_TABLE) ? node : + ((node->id() == ID_TR ) ? parentparent : parent); + NodeImpl *parent = node->parentNode(); + if (!parent) + return false; + int exceptioncode = 0; +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "calling insertBefore(" << n->nodeName().string() << "," << node->nodeName().string() << ")" << endl; +#endif + parent->insertBefore(n, node, exceptioncode); + if (exceptioncode) { +#ifndef PARSER_DEBUG + if (!n->isTextNode()) +#endif + kdDebug(6035) << "adding content before table failed.." << endl; + break; + } + if ( n->isElementNode() && tagPriority[id] != 0 && + !flat && endTag[id] != DOM::FORBIDDEN ) { + + pushBlock(id, tagPriority[id]); + setCurrent ( n ); + inStrayTableContent++; + blockStack->strayTableContent = true; + } + return true; + } + + if ( current->id() == ID_TR ) + e = new HTMLTableCellElementImpl(document, ID_TD); + else if ( current->id() == ID_TABLE ) + e = new HTMLTableSectionElementImpl( document, ID_TBODY, true /* implicit */ ); + else + e = new HTMLTableRowElementImpl( document ); + + insertNode(e); + handled = true; + break; + } // end default + } // end switch + break; + case ID_OBJECT: + discard_until = id + ID_CLOSE_TAG; + return false; + case ID_UL: + case ID_OL: + case ID_DIR: + case ID_MENU: + e = new HTMLLIElementImpl(document); + e->addCSSProperty(CSS_PROP_LIST_STYLE_TYPE, CSS_VAL_NONE); + insertNode(e); + handled = true; + break; + case ID_DL: + popBlock(ID_DL); + handled = true; + break; + case ID_DT: + popBlock(ID_DT); + handled = true; + break; + case ID_FORM: + popBlock(ID_FORM); + handled = true; + break; + case ID_SELECT: + if( n->isInline() ) + return false; + break; + case ID_P: + case ID_H1: + case ID_H2: + case ID_H3: + case ID_H4: + case ID_H5: + case ID_H6: + if(!n->isInline()) + { + popBlock(current->id()); + handled = true; + } + break; + case ID_OPTION: + case ID_OPTGROUP: + if (id == ID_OPTGROUP) + { + popBlock(current->id()); + handled = true; + } + else if(id == ID_SELECT) + { + // IE treats a nested select as </select>. Let's do the same + popBlock( ID_SELECT ); + break; + } + break; + // head elements in the body should be ignored. + + case ID_ADDRESS: + case ID_COLGROUP: + case ID_FONT: + popBlock(current->id()); + handled = true; + break; + default: + if(current->isDocumentNode()) + { + if(current->firstChild() == 0) { + e = new HTMLHtmlElementImpl(document); + insertNode(e); + handled = true; + } + } + else if(current->isInline()) + { + popInlineBlocks(); + handled = true; + } + } + + // if we couldn't handle the error, just rethrow the exception... + if(!handled) + { + //kdDebug( 6035 ) << "Exception handler failed in HTMLPArser::insertNode()" << endl; + return false; + } + + return insertNode(n); + } +} + + +NodeImpl *KHTMLParser::getElement(Token* t) +{ + NodeImpl *n = 0; + + switch(t->tid) + { + case ID_HTML: + n = new HTMLHtmlElementImpl(document); + break; + case ID_HEAD: + if(!head && current->id() == ID_HTML) { + head = new HTMLHeadElementImpl(document); + n = head; + } + break; + case ID_BODY: + // body no longer allowed if we have a frameset + if(haveFrameSet) break; + popBlock(ID_HEAD); + n = new HTMLBodyElementImpl(document); + haveBody = true; + startBody(); + break; + +// head elements + case ID_BASE: + n = new HTMLBaseElementImpl(document); + break; + case ID_LINK: + n = new HTMLLinkElementImpl(document); + break; + case ID_META: + n = new HTMLMetaElementImpl(document); + break; + case ID_STYLE: + n = new HTMLStyleElementImpl(document); + break; + case ID_TITLE: + // only one non-empty <title> allowed + if (haveTitle) { + discard_until = ID_TITLE+ID_CLOSE_TAG; + break; + } + n = new HTMLTitleElementImpl(document); + // we'll set haveTitle when closing the tag + break; + +// frames + case ID_FRAME: + n = new HTMLFrameElementImpl(document); + break; + case ID_FRAMESET: + popBlock(ID_HEAD); + if ( inBody && !haveFrameSet && !haveContent && !haveBody) { + popBlock( ID_BODY ); + // ### actually for IE document.body returns the now hidden "body" element + // we can't implement that behavior now because it could cause too many + // regressions and the headaches are not worth the work as long as there is + // no site actually relying on that detail (Dirk) + if (static_cast<HTMLDocumentImpl*>(document)->body()) + static_cast<HTMLDocumentImpl*>(document)->body() + ->addCSSProperty(CSS_PROP_DISPLAY, CSS_VAL_NONE); + inBody = false; + } + if ( (haveBody || haveContent || haveFrameSet) && current->id() == ID_HTML) + break; + n = new HTMLFrameSetElementImpl(document); + haveFrameSet = true; + startBody(); + break; + // a bit a special case, since the frame is inlined... + case ID_IFRAME: + n = new HTMLIFrameElementImpl(document); + if (!t->flat) discard_until = ID_IFRAME+ID_CLOSE_TAG; + break; + +// form elements + case ID_FORM: + // thou shall not nest <form> - NS/IE quirk + if (form) break; + n = form = new HTMLFormElementImpl(document, false); + break; + case ID_BUTTON: + n = new HTMLButtonElementImpl(document, form); + break; + case ID_FIELDSET: + n = new HTMLFieldSetElementImpl(document, form); + break; + case ID_INPUT: + if ( t->attrs && + KHTMLFactory::defaultHTMLSettings()->isAdFilterEnabled() && + KHTMLFactory::defaultHTMLSettings()->isHideAdsEnabled() && + !strcasecmp( t->attrs->getValue( ATTR_TYPE ), "image" ) ) + { + if (KHTMLFactory::defaultHTMLSettings()->isAdFiltered( doc()->completeURL( khtml::parseURL(t->attrs->getValue(ATTR_SRC)).string() ) )) + return 0; + } + n = new HTMLInputElementImpl(document, form); + break; + case ID_ISINDEX: + n = handleIsindex(t); + if( !inBody ) { + isindex = n; + n = 0; + } else + t->flat = true; + break; + case ID_KEYGEN: + n = new HTMLKeygenElementImpl(document, form); + break; + case ID_LABEL: + n = new HTMLLabelElementImpl(document); + break; + case ID_LEGEND: + n = new HTMLLegendElementImpl(document, form); + break; + case ID_OPTGROUP: + n = new HTMLOptGroupElementImpl(document, form); + break; + case ID_OPTION: + n = new HTMLOptionElementImpl(document, form); + break; + case ID_SELECT: + inSelect = true; + n = new HTMLSelectElementImpl(document, form); + break; + case ID_TEXTAREA: + n = new HTMLTextAreaElementImpl(document, form); + break; + +// lists + case ID_DL: + n = new HTMLDListElementImpl(document); + break; + case ID_DD: + n = new HTMLGenericElementImpl(document, t->tid); + popBlock(ID_DT); + popBlock(ID_DD); + break; + case ID_DT: + n = new HTMLGenericElementImpl(document, t->tid); + popBlock(ID_DD); + popBlock(ID_DT); + break; + case ID_UL: + { + n = new HTMLUListElementImpl(document); + break; + } + case ID_OL: + { + n = new HTMLOListElementImpl(document); + break; + } + case ID_DIR: + n = new HTMLDirectoryElementImpl(document); + break; + case ID_MENU: + n = new HTMLMenuElementImpl(document); + break; + case ID_LI: + popBlock(ID_LI); + n = new HTMLLIElementImpl(document); + break; +// formatting elements (block) + case ID_BLOCKQUOTE: + n = new HTMLGenericElementImpl(document, t->tid); + break; + case ID_LAYER: + case ID_ILAYER: + n = new HTMLLayerElementImpl(document, t->tid); + break; + case ID_P: + case ID_DIV: + n = new HTMLDivElementImpl(document, t->tid); + break; + case ID_H1: + case ID_H2: + case ID_H3: + case ID_H4: + case ID_H5: + case ID_H6: + n = new HTMLGenericElementImpl(document, t->tid); + break; + case ID_HR: + n = new HTMLHRElementImpl(document); + break; + case ID_PRE: + case ID_XMP: + case ID_PLAINTEXT: + n = new HTMLPreElementImpl(document, t->tid); + break; + +// font stuff + case ID_BASEFONT: + n = new HTMLBaseFontElementImpl(document); + break; + case ID_FONT: + n = new HTMLFontElementImpl(document); + break; + +// ins/del + case ID_DEL: + case ID_INS: + n = new HTMLGenericElementImpl(document, t->tid); + break; + +// anchor + case ID_A: + popBlock(ID_A); + + n = new HTMLAnchorElementImpl(document); + break; + +// images + case ID_IMG: + if (t->attrs&& + KHTMLFactory::defaultHTMLSettings()->isAdFilterEnabled()&& + KHTMLFactory::defaultHTMLSettings()->isHideAdsEnabled()) + { + QString url = doc()->completeURL( khtml::parseURL(t->attrs->getValue(ATTR_SRC)).string() ); + if (KHTMLFactory::defaultHTMLSettings()->isAdFiltered(url)) + return 0; + } + n = new HTMLImageElementImpl(document, form); + break; + + case ID_MAP: + map = new HTMLMapElementImpl(document); + n = map; + break; + case ID_AREA: + n = new HTMLAreaElementImpl(document); + break; + +// objects, applets and scripts + case ID_APPLET: + n = new HTMLAppletElementImpl(document); + break; + case ID_EMBED: + n = new HTMLEmbedElementImpl(document); + break; + case ID_OBJECT: + n = new HTMLObjectElementImpl(document); + break; + case ID_PARAM: + n = new HTMLParamElementImpl(document); + break; + case ID_SCRIPT: + { + HTMLScriptElementImpl *scriptElement = new HTMLScriptElementImpl(document); + scriptElement->setCreatedByParser(true); + n = scriptElement; + break; + } + +// tables + case ID_TABLE: + n = new HTMLTableElementImpl(document); + break; + case ID_CAPTION: + n = new HTMLTableCaptionElementImpl(document); + break; + case ID_COLGROUP: + case ID_COL: + n = new HTMLTableColElementImpl(document, t->tid); + break; + case ID_TR: + popBlock(ID_TR); + n = new HTMLTableRowElementImpl(document); + break; + case ID_TD: + case ID_TH: + popBlock(ID_TH); + popBlock(ID_TD); + n = new HTMLTableCellElementImpl(document, t->tid); + break; + case ID_TBODY: + case ID_THEAD: + case ID_TFOOT: + popBlock( ID_THEAD ); + popBlock( ID_TBODY ); + popBlock( ID_TFOOT ); + n = new HTMLTableSectionElementImpl(document, t->tid, false); + break; + +// inline elements + case ID_BR: + n = new HTMLBRElementImpl(document); + break; + case ID_Q: + n = new HTMLGenericElementImpl(document, t->tid); + break; + +// elements with no special representation in the DOM + +// block: + case ID_ADDRESS: + case ID_CENTER: + n = new HTMLGenericElementImpl(document, t->tid); + break; +// inline + // %fontstyle + case ID_TT: + case ID_U: + case ID_B: + case ID_I: + case ID_S: + case ID_STRIKE: + case ID_BIG: + case ID_SMALL: + + // %phrase + case ID_EM: + case ID_STRONG: + case ID_DFN: + case ID_CODE: + case ID_SAMP: + case ID_KBD: + case ID_VAR: + case ID_CITE: + case ID_ABBR: + case ID_ACRONYM: + + // %special + case ID_SUB: + case ID_SUP: + case ID_SPAN: + case ID_WBR: + case ID_NOBR: + if ( t->tid == ID_NOBR || t->tid == ID_WBR ) + popBlock( t->tid ); + case ID_BDO: + n = new HTMLGenericElementImpl(document, t->tid); + break; + + // these are special, and normally not rendered + case ID_NOEMBED: + if (!t->flat) { + n = new HTMLGenericElementImpl(document, t->tid); + discard_until = ID_NOEMBED + ID_CLOSE_TAG; + } + return n; + case ID_NOFRAMES: + if (!t->flat) { + n = new HTMLGenericElementImpl(document, t->tid); + discard_until = ID_NOFRAMES + ID_CLOSE_TAG; + } + return n; + case ID_NOSCRIPT: + if (!t->flat) { + n = new HTMLGenericElementImpl(document, t->tid); + if(HTMLWidget && HTMLWidget->part()->jScriptEnabled()) + discard_until = ID_NOSCRIPT + ID_CLOSE_TAG; + } + return n; + case ID_NOLAYER: +// discard_until = ID_NOLAYER + ID_CLOSE_TAG; + return 0; + break; + case ID_MARQUEE: + n = new HTMLMarqueeElementImpl(document); + break; +// text + case ID_TEXT: +// kdDebug(6035) << "ID_TEXT: \"" << DOMString(t->text).string() << "\"" << endl; + n = new TextImpl(document, t->text); + break; + case ID_COMMENT: +#ifdef COMMENTS_IN_DOM + n = new CommentImpl(document, t->text); +#endif + break; + default: + kdDebug( 6035 ) << "Unknown tag " << t->tid << "!" << endl; + } + return n; +} + +void KHTMLParser::processCloseTag(Token *t) +{ + // support for really broken html. Can't believe I'm supporting such crap (lars) + switch(t->tid) + { + case ID_HTML+ID_CLOSE_TAG: + case ID_BODY+ID_CLOSE_TAG: + // we never trust those close tags, since stupid webpages close + // them prematurely + return; + case ID_FORM+ID_CLOSE_TAG: + form = 0; + // this one is to get the right style on the body element + break; + case ID_MAP+ID_CLOSE_TAG: + map = 0; + break; + case ID_SELECT+ID_CLOSE_TAG: + inSelect = false; + break; + case ID_TITLE+ID_CLOSE_TAG: + // Set haveTitle only if <title> isn't empty + if ( current->firstChild() ) + haveTitle = true; + break; + default: + break; + } + +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "added the following children to " << current->nodeName().string() << endl; + NodeImpl *child = current->firstChild(); + while(child != 0) + { + kdDebug( 6035 ) << " " << child->nodeName().string() << endl; + child = child->nextSibling(); + } +#endif + popBlock(t->tid-ID_CLOSE_TAG); +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "closeTag --> current = " << current->nodeName().string() << endl; +#endif +} + +bool KHTMLParser::isResidualStyleTag(int _id) +{ + switch (_id) { + case ID_A: + case ID_FONT: + case ID_TT: + case ID_U: + case ID_B: + case ID_I: + case ID_S: + case ID_STRIKE: + case ID_BIG: + case ID_SMALL: + case ID_EM: + case ID_STRONG: + case ID_DFN: + case ID_CODE: + case ID_SAMP: + case ID_KBD: + case ID_VAR: + case ID_DEL: + case ID_INS: + case ID_WBR: + case ID_NOBR: + return true; + default: + return false; + } +} + +bool KHTMLParser::isAffectedByResidualStyle(int _id) +{ + if (isResidualStyleTag(_id)) + return true; + + switch (_id) { + case ID_P: + case ID_DIV: + case ID_BLOCKQUOTE: + case ID_ADDRESS: + case ID_H1: + case ID_H2: + case ID_H3: + case ID_H4: + case ID_H5: + case ID_H6: + case ID_CENTER: + case ID_UL: + case ID_OL: + case ID_LI: + case ID_DL: + case ID_DT: + case ID_DD: + case ID_PRE: + return true; + default: + return false; + } +} + +void KHTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem) +{ + // Find the element that crosses over to a higher level. + // ### For now, if there is more than one, we will only make sure we close the residual style. + int exceptionCode = 0; + HTMLStackElem* curr = blockStack; + HTMLStackElem* maxElem = 0; + HTMLStackElem* endElem = 0; + HTMLStackElem* prev = 0; + HTMLStackElem* prevMaxElem = 0; + bool advancedResidual = false; // ### if set we only close the residual style + while (curr && curr != elem) { + if (curr->level > elem->level) { + if (!isAffectedByResidualStyle(curr->id)) return; + if (maxElem) advancedResidual = true; + else + endElem = curr; + maxElem = curr; + prevMaxElem = prev; + } + + prev = curr; + curr = curr->next; + } + + if (!curr || !maxElem ) return; + + NodeImpl* residualElem = prev->node; + NodeImpl* blockElem = prevMaxElem ? prevMaxElem->node : current; + NodeImpl* parentElem = elem->node; + + // Check to see if the reparenting that is going to occur is allowed according to the DOM. + // FIXME: We should either always allow it or perform an additional fixup instead of + // just bailing here. + // Example: <p><font><center>blah</font></center></p> isn't doing a fixup right now. + if (!parentElem->childAllowed(blockElem)) + return; + + if (maxElem->node->parentNode() != elem->node && !advancedResidual) { + // Walk the stack and remove any elements that aren't residual style tags. These + // are basically just being closed up. Example: + // <font><span>Moo<p>Goo</font></p>. + // In the above example, the <span> doesn't need to be reopened. It can just close. + HTMLStackElem* currElem = maxElem->next; + HTMLStackElem* prevElem = maxElem; + while (currElem != elem) { + HTMLStackElem* nextElem = currElem->next; + if (!isResidualStyleTag(currElem->id)) { + prevElem->next = nextElem; + prevElem->setNode(currElem->node); + delete currElem; + } + else + prevElem = currElem; + currElem = nextElem; + } + + // We have to reopen residual tags in between maxElem and elem. An example of this case s: + // <font><i>Moo<p>Foo</font>. + // In this case, we need to transform the part before the <p> into: + // <font><i>Moo</i></font><i> + // so that the <i> will remain open. This involves the modification of elements + // in the block stack. + // This will also affect how we ultimately reparent the block, since we want it to end up + // under the reopened residual tags (e.g., the <i> in the above example.) + NodeImpl* prevNode = 0; + NodeImpl* currNode = 0; + currElem = maxElem; + while (currElem->node != residualElem) { + if (isResidualStyleTag(currElem->node->id())) { + // Create a clone of this element. + currNode = currElem->node->cloneNode(false); + currElem->node->close(); + removeForbidden(currElem->id, forbiddenTag); + + // Change the stack element's node to point to the clone. + currElem->setNode(currNode); + + // Attach the previous node as a child of this new node. + if (prevNode) + currNode->appendChild(prevNode, exceptionCode); + else // The new parent for the block element is going to be the innermost clone. + parentElem = currNode; + + prevNode = currNode; + } + + currElem = currElem->next; + } + + // Now append the chain of new residual style elements if one exists. + if (prevNode) + elem->node->appendChild(prevNode, exceptionCode); + } + + // We need to make a clone of |residualElem| and place it just inside |blockElem|. + // All content of |blockElem| is reparented to be under this clone. We then + // reparent |blockElem| using real DOM calls so that attachment/detachment will + // be performed to fix up the rendering tree. + // So for this example: <b>...<p>Foo</b>Goo</p> + // The end result will be: <b>...</b><p><b>Foo</b>Goo</p> + // + // Step 1: Remove |blockElem| from its parent, doing a batch detach of all the kids. + SharedPtr<NodeImpl> guard(blockElem); + blockElem->parentNode()->removeChild(blockElem, exceptionCode); + + if (!advancedResidual) { + // Step 2: Clone |residualElem|. + NodeImpl* newNode = residualElem->cloneNode(false); // Shallow clone. We don't pick up the same kids. + + // Step 3: Place |blockElem|'s children under |newNode|. Remove all of the children of |blockElem| + // before we've put |newElem| into the document. That way we'll only do one attachment of all + // the new content (instead of a bunch of individual attachments). + NodeImpl* currNode = blockElem->firstChild(); + while (currNode) { + NodeImpl* nextNode = currNode->nextSibling(); + SharedPtr<NodeImpl> guard(currNode); //Protect from deletion while moving + blockElem->removeChild(currNode, exceptionCode); + newNode->appendChild(currNode, exceptionCode); + currNode = nextNode; + + // TODO - To be replaced. + // Re-register form elements with currently active form, step 1 will have removed them + if (form && currNode && currNode->isGenericFormElement()) + { + HTMLGenericFormElementImpl *e = static_cast<HTMLGenericFormElementImpl *>(currNode); + form->registerFormElement(e); + } + } + + // Step 4: Place |newNode| under |blockElem|. |blockElem| is still out of the document, so no + // attachment can occur yet. + blockElem->appendChild(newNode, exceptionCode); + } + + // Step 5: Reparent |blockElem|. Now the full attachment of the fixed up tree takes place. + parentElem->appendChild(blockElem, exceptionCode); + + // Step 6: Elide |elem|, since it is effectively no longer open. Also update + // the node associated with the previous stack element so that when it gets popped, + // it doesn't make the residual element the next current node. + HTMLStackElem* currElem = maxElem; + HTMLStackElem* prevElem = 0; + while (currElem != elem) { + prevElem = currElem; + currElem = currElem->next; + } + prevElem->next = elem->next; + prevElem->setNode(elem->node); + delete elem; + + // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>. + // In the above example, Goo should stay italic. + curr = blockStack; + HTMLStackElem* residualStyleStack = 0; + while (curr && curr != endElem) { + // We will actually schedule this tag for reopening + // after we complete the close of this entire block. + NodeImpl* currNode = current; + if (isResidualStyleTag(curr->id)) { + // We've overloaded the use of stack elements and are just reusing the + // struct with a slightly different meaning to the variables. Instead of chaining + // from innermost to outermost, we build up a list of all the tags we need to reopen + // from the outermost to the innermost, i.e., residualStyleStack will end up pointing + // to the outermost tag we need to reopen. + // We also set curr->node to be the actual element that corresponds to the ID stored in + // curr->id rather than the node that you should pop to when the element gets pulled off + // the stack. + popOneBlock(false); + curr->setNode(currNode); + curr->next = residualStyleStack; + residualStyleStack = curr; + } + else + popOneBlock(); + + curr = blockStack; + } + + reopenResidualStyleTags(residualStyleStack, 0); // FIXME: Deal with stray table content some day + // if it becomes necessary to do so. +} + +void KHTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, DOM::NodeImpl* malformedTableParent) +{ + // Loop for each tag that needs to be reopened. + while (elem) { + // Create a shallow clone of the DOM node for this element. + NodeImpl* newNode = elem->node->cloneNode(false); + + // Append the new node. In the malformed table case, we need to insert before the table, + // which will be the last child. + int exceptionCode = 0; + if (malformedTableParent) + malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), exceptionCode); + else + current->appendChild(newNode, exceptionCode); + // FIXME: Is it really OK to ignore the exceptions here? + + // Now push a new stack element for this node we just created. + pushBlock(elem->id, elem->level); + + // Set our strayTableContent boolean if needed, so that the reopened tag also knows + // that it is inside a malformed table. + blockStack->strayTableContent = malformedTableParent != 0; + if (blockStack->strayTableContent) + inStrayTableContent++; + + // Clear our malformed table parent variable. + malformedTableParent = 0; + + // Update |current| manually to point to the new node. + setCurrent(newNode); + + // Advance to the next tag that needs to be reopened. + HTMLStackElem* next = elem->next; + delete elem; + elem = next; + } +} + +void KHTMLParser::pushBlock(int _id, int _level) +{ + HTMLStackElem *Elem = new HTMLStackElem(_id, _level, current, m_inline, blockStack); + + blockStack = Elem; + addForbidden(_id, forbiddenTag); +} + +void KHTMLParser::popBlock( int _id ) +{ + HTMLStackElem *Elem = blockStack; + int maxLevel = 0; + +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "popBlock(" << getTagName(_id) << ")" << endl; + while(Elem) { + kdDebug( 6035) << " > " << getTagName(Elem->id) << endl; + Elem = Elem->next; + } + Elem = blockStack; +#endif + + while( Elem && (Elem->id != _id)) + { + if (maxLevel < Elem->level) + { + maxLevel = Elem->level; + } + Elem = Elem->next; + } + if (!Elem) + return; + + if (maxLevel > Elem->level) { + // We didn't match because the tag is in a different scope, e.g., + // <b><p>Foo</b>. Try to correct the problem. + if (!isResidualStyleTag(_id)) + return; + return handleResidualStyleCloseTagAcrossBlocks(Elem); + } + + bool isAffectedByStyle = isAffectedByResidualStyle(Elem->id); + HTMLStackElem* residualStyleStack = 0; + NodeImpl* malformedTableParent = 0; + + Elem = blockStack; + + while (Elem) + { + if (Elem->id == _id) + { + int strayTable = inStrayTableContent; + popOneBlock(); + Elem = 0; + + // This element was the root of some malformed content just inside an implicit or + // explicit <tbody> or <tr>. + // If we end up needing to reopen residual style tags, the root of the reopened chain + // must also know that it is the root of malformed content inside a <tbody>/<tr>. + if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) { + NodeImpl* curr = current; + while (curr && curr->id() != ID_TABLE) + curr = curr->parentNode(); + malformedTableParent = curr ? curr->parentNode() : 0; + } + } + else + { + // Schedule this tag for reopening + // after we complete the close of this entire block. + NodeImpl* currNode = current; + if (isAffectedByStyle && isResidualStyleTag(Elem->id)) { + // We've overloaded the use of stack elements and are just reusing the + // struct with a slightly different meaning to the variables. Instead of chaining + // from innermost to outermost, we build up a list of all the tags we need to reopen + // from the outermost to the innermost, i.e., residualStyleStack will end up pointing + // to the outermost tag we need to reopen. + // We also set Elem->node to be the actual element that corresponds to the ID stored in + // Elem->id rather than the node that you should pop to when the element gets pulled off + // the stack. + popOneBlock(false); + Elem->next = residualStyleStack; + Elem->setNode(currNode); + residualStyleStack = Elem; + } + else + popOneBlock(); + Elem = blockStack; + } + } + + reopenResidualStyleTags(residualStyleStack, malformedTableParent); +} + +void KHTMLParser::popOneBlock(bool delBlock) +{ + HTMLStackElem *Elem = blockStack; + + // we should never get here, but some bad html might cause it. +#ifndef PARSER_DEBUG + if(!Elem) return; +#else + kdDebug( 6035 ) << "popping block: " << getTagName(Elem->id) << "(" << Elem->id << ")" << endl; +#endif + +#if SPEED_DEBUG < 1 + if((Elem->node != current)) { + if (current->maintainsState() && document){ + document->registerMaintainsState(current); + QString state(document->nextState()); + if (!state.isNull()) current->restoreState(state); + } + current->close(); + } +#endif + + removeForbidden(Elem->id, forbiddenTag); + + blockStack = Elem->next; + // we only set inline to false, if the element we close is a block level element. + // This helps getting cases as <p><b>bla</b> <b>bla</b> right. + + m_inline = Elem->m_inline; + + if (current->id() == ID_FORM && form && inStrayTableContent) + form->setMalformed(true); + + setCurrent( Elem->node ); + + if (Elem->strayTableContent) + inStrayTableContent--; + + if (delBlock) + delete Elem; +} + +void KHTMLParser::popInlineBlocks() +{ + while(blockStack && current->isInline() && current->id() != ID_FONT) + popOneBlock(); +} + +void KHTMLParser::freeBlock() +{ + while (blockStack) + popOneBlock(); + blockStack = 0; +} + +void KHTMLParser::createHead() +{ + if(head || !doc()->firstChild()) + return; + + head = new HTMLHeadElementImpl(document); + HTMLElementImpl *body = doc()->body(); + int exceptioncode = 0; + doc()->firstChild()->insertBefore(head, body, exceptioncode); + if ( exceptioncode ) { +#ifdef PARSER_DEBUG + kdDebug( 6035 ) << "creation of head failed!!!!" << endl; +#endif + delete head; + head = 0; + } +} + +NodeImpl *KHTMLParser::handleIsindex( Token *t ) +{ + NodeImpl *n; + HTMLFormElementImpl *myform = form; + if ( !myform ) { + myform = new HTMLFormElementImpl(document, true); + n = myform; + } else + n = new HTMLDivElementImpl( document, ID_DIV ); + NodeImpl *child = new HTMLHRElementImpl( document ); + n->addChild( child ); + DOMStringImpl* a = t->attrs ? t->attrs->getValue(ATTR_PROMPT) : 0; + DOMString text = i18n("This is a searchable index. Enter search keywords: "); + if (a) + text = a; + child = new TextImpl(document, text.implementation()); + n->addChild( child ); + child = new HTMLIsIndexElementImpl(document, myform); + static_cast<ElementImpl *>(child)->setAttribute(ATTR_TYPE, "khtml_isindex"); + n->addChild( child ); + child = new HTMLHRElementImpl( document ); + n->addChild( child ); + + return n; +} + +void KHTMLParser::startBody() +{ + if(inBody) return; + + inBody = true; + + if( isindex ) { + insertNode( isindex, true /* don't decend into this node */ ); + isindex = 0; + } +} diff --git a/khtml/html/htmlparser.h b/khtml/html/htmlparser.h new file mode 100644 index 000000000..e09eefbae --- /dev/null +++ b/khtml/html/htmlparser.h @@ -0,0 +1,191 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 1997 Martin Jones (mjones@kde.org) + (C) 1997 Torben Weis (weis@kde.org) + (C) 1998 Waldo Bastian (bastian@kde.org) + (C) 1999 Lars Knoll (knoll@kde.org) + (C) 2003 Apple Computer, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +//---------------------------------------------------------------------------- +// +// KDE HTML Widget -- HTML Parser + +#ifndef HTMLPARSER_H +#define HTMLPARSER_H + +// 0 all +// 1 domtree + rendertree + styleForElement, no layouting +// 2 domtree only +#define SPEED_DEBUG 0 + +#ifdef SPEED_DEBUG +#include <qdatetime.h> +#endif + + +#include "dom/dom_string.h" +#include "xml/dom_nodeimpl.h" +#include "html/html_documentimpl.h" + +class KHTMLView; +class HTMLStackElem; + +namespace DOM { + class HTMLDocumentImpl; + class DocumentImpl; + class HTMLElementImpl; + class NodeImpl; + class HTMLFormElementImpl; + class HTMLMapElementImpl; + class HTMLHeadElementImpl; + class DocumentFragmentImpl; +} + +namespace khtml { + +class Token; + +/** + * The parser for html. It receives a stream of tokens from the HTMLTokenizer, and + * builds up the Document structure form it. + */ +class KHTMLParser +{ +public: + KHTMLParser( KHTMLView *w, DOM::DocumentImpl *i ); + KHTMLParser( DOM::DocumentFragmentImpl *frag, DOM::DocumentImpl *doc ); + virtual ~KHTMLParser(); + + /** + * parses one token delivered by the tokenizer + */ + void parseToken(Token *_t); + + /** + * resets the parser + */ + void reset(); + + bool skipMode() const { return (discard_until != 0); } + bool noSpaces() const { return (inSelect || !m_inline || !inBody); } + bool selectMode() const { return inSelect; } + + DOM::HTMLDocumentImpl *doc() const { return static_cast<DOM::HTMLDocumentImpl *>(document); } + DOM::DocumentImpl *docPtr() const { return document; } + +protected: + + KHTMLView *HTMLWidget; + DOM::DocumentImpl *document; + + /* + * generate an element from the token + */ + DOM::NodeImpl *getElement(Token *); + + void processCloseTag(Token *); + + bool insertNode(DOM::NodeImpl *n, bool flat = false); + + /* + * The currently active element (the one new elements will be added to) + */ + void setCurrent( DOM::NodeImpl* newNode ) + { + if ( newNode ) newNode->ref(); + if ( current ) current->deref(); + current = newNode; + } + +private: + DOM::NodeImpl *current; + + HTMLStackElem *blockStack; + + void pushBlock( int _id, int _level); + + void popBlock( int _id ); + void popOneBlock(bool delBlock = true); + void popInlineBlocks(); + + void freeBlock( void); + + void createHead(); + + bool isResidualStyleTag(int _id); + bool isAffectedByResidualStyle(int _id); + void handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem); + void reopenResidualStyleTags(HTMLStackElem* elem, DOM::NodeImpl* malformedTableParent); + + ushort *forbiddenTag; + + /* + * currently active form + */ + DOM::HTMLFormElementImpl *form; + + /* + * current map + */ + DOM::HTMLMapElementImpl *map; + + /* + * the head element. Needed for crappy html which defines <base> after </head> + */ + DOM::HTMLHeadElementImpl *head; + + /* + * a possible <isindex> element in the head. Compatibility hack for + * html from the stone age + */ + DOM::NodeImpl *isindex; + DOM::NodeImpl *handleIsindex( Token *t ); + + /* + * inserts the stupid isIndex element. + */ + void startBody(); + + bool inBody; + bool haveContent; + bool haveBody; + bool haveFrameSet; + bool haveTitle; + bool m_inline; + bool end; + bool inSelect; + + + /* + * tells the parser to discard all tags, until it reaches the one specified + */ + int discard_until; + + bool headLoaded; + int inStrayTableContent; + +#if SPEED_DEBUG > 0 + QTime qt; +#endif +}; + +} // namespace khtml + +#endif // HTMLPARSER_H + diff --git a/khtml/html/htmltokenizer.cpp b/khtml/html/htmltokenizer.cpp new file mode 100644 index 000000000..e0983582a --- /dev/null +++ b/khtml/html/htmltokenizer.cpp @@ -0,0 +1,1798 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 1997 Martin Jones (mjones@kde.org) + (C) 1997 Torben Weis (weis@kde.org) + (C) 1998 Waldo Bastian (bastian@kde.org) + (C) 1999 Lars Knoll (knoll@kde.org) + (C) 1999 Antti Koivisto (koivisto@kde.org) + (C) 2001-2003 Dirk Mueller (mueller@kde.org) + (C) 2004 Apple Computer, Inc. + (C) 2006 Germain Garand (germain@ebooksfrance.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +//---------------------------------------------------------------------------- +// +// KDE HTML Widget - Tokenizers + +//#define TOKEN_DEBUG 1 +//#define TOKEN_DEBUG 2 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "html/htmltokenizer.h" +#include "html/html_documentimpl.h" +#include "html/htmlparser.h" +#include "html/dtd.h" + +#include "misc/loader.h" +#include "misc/htmlhashes.h" + +#include "khtmlview.h" +#include "khtml_part.h" +#include "xml/dom_docimpl.h" +#include "css/csshelper.h" +#include "ecma/kjs_proxy.h" +#include <kcharsets.h> +#include <kglobal.h> +#include <ctype.h> +#include <assert.h> +#include <qvariant.h> +#include <kdebug.h> +#include <stdlib.h> + +#include "kentities.c" + +using namespace khtml; + +static const QChar commentStart [] = { '<','!','-','-', QChar::null }; + +static const char scriptEnd [] = "</script"; +static const char xmpEnd [] = "</xmp"; +static const char styleEnd [] = "</style"; +static const char textareaEnd [] = "</textarea"; +static const char titleEnd [] = "</title"; + +#define KHTML_ALLOC_QCHAR_VEC( N ) (QChar*) malloc( sizeof(QChar)*( N ) ) +#define KHTML_REALLOC_QCHAR_VEC(P, N ) (QChar*) realloc(P, sizeof(QChar)*( N )) +#define KHTML_DELETE_QCHAR_VEC( P ) free((char*)( P )) + +// Full support for MS Windows extensions to Latin-1. +// Technically these extensions should only be activated for pages +// marked "windows-1252" or "cp1252", but +// in the standard Microsoft way, these extensions infect hundreds of thousands +// of web pages. Note that people with non-latin-1 Microsoft extensions +// are SOL. +// +// See: http://www.microsoft.com/globaldev/reference/WinCP.asp +// http://www.bbsinc.com/iso8859.html +// http://www.obviously.com/ +// +// There may be better equivalents +#if 0 +#define fixUpChar(x) +#else +#define fixUpChar(x) \ + switch ((x).unicode()) \ + { \ + case 0x80: (x) = 0x20ac; break; \ + case 0x82: (x) = 0x201a; break; \ + case 0x83: (x) = 0x0192; break; \ + case 0x84: (x) = 0x201e; break; \ + case 0x85: (x) = 0x2026; break; \ + case 0x86: (x) = 0x2020; break; \ + case 0x87: (x) = 0x2021; break; \ + case 0x88: (x) = 0x02C6; break; \ + case 0x89: (x) = 0x2030; break; \ + case 0x8A: (x) = 0x0160; break; \ + case 0x8b: (x) = 0x2039; break; \ + case 0x8C: (x) = 0x0152; break; \ + case 0x8E: (x) = 0x017D; break; \ + case 0x91: (x) = 0x2018; break; \ + case 0x92: (x) = 0x2019; break; \ + case 0x93: (x) = 0x201C; break; \ + case 0x94: (x) = 0X201D; break; \ + case 0x95: (x) = 0x2022; break; \ + case 0x96: (x) = 0x2013; break; \ + case 0x97: (x) = 0x2014; break; \ + case 0x98: (x) = 0x02DC; break; \ + case 0x99: (x) = 0x2122; break; \ + case 0x9A: (x) = 0x0161; break; \ + case 0x9b: (x) = 0x203A; break; \ + case 0x9C: (x) = 0x0153; break; \ + case 0x9E: (x) = 0x017E; break; \ + case 0x9F: (x) = 0x0178; break; \ + default: break; \ + } +#endif +// ---------------------------------------------------------------------------- + +HTMLTokenizer::HTMLTokenizer(DOM::DocumentImpl *_doc, KHTMLView *_view) +{ + view = _view; + buffer = 0; + scriptCode = 0; + scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0; + charsets = KGlobal::charsets(); + parser = new KHTMLParser(_view, _doc); + m_executingScript = 0; + m_autoCloseTimer = 0; + onHold = false; + + reset(); +} + +HTMLTokenizer::HTMLTokenizer(DOM::DocumentImpl *_doc, DOM::DocumentFragmentImpl *i) +{ + view = 0; + buffer = 0; + scriptCode = 0; + scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0; + charsets = KGlobal::charsets(); + parser = new KHTMLParser( i, _doc ); + m_executingScript = 0; + m_autoCloseTimer = 0; + onHold = false; + + reset(); +} + +void HTMLTokenizer::reset() +{ + assert(m_executingScript == 0); + Q_ASSERT(onHold == false); + m_abort = false; + + while (!cachedScript.isEmpty()) + cachedScript.dequeue()->deref(this); + + if ( buffer ) + KHTML_DELETE_QCHAR_VEC(buffer); + buffer = dest = 0; + size = 0; + + if ( scriptCode ) + KHTML_DELETE_QCHAR_VEC(scriptCode); + scriptCode = 0; + scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0; + + if (m_autoCloseTimer) { + killTimer(m_autoCloseTimer); + m_autoCloseTimer = 0; + } + + currToken.reset(); +} + +void HTMLTokenizer::begin() +{ + m_executingScript = 0; + onHold = false; + reset(); + size = 254; + buffer = KHTML_ALLOC_QCHAR_VEC( 255 ); + dest = buffer; + tag = NoTag; + pending = NonePending; + discard = NoneDiscard; + pre = false; + prePos = 0; + plaintext = false; + xmp = false; + processingInstruction = false; + script = false; + escaped = false; + style = false; + skipLF = false; + select = false; + comment = false; + server = false; + textarea = false; + title = false; + startTag = false; + tquote = NoQuote; + searchCount = 0; + Entity = NoEntity; + noMoreData = false; + brokenComments = false; + brokenServer = false; + brokenScript = false; + lineno = 0; + scriptStartLineno = 0; + tagStartLineno = 0; +} + +void HTMLTokenizer::processListing(TokenizerString list) +{ + bool old_pre = pre; + + // This function adds the listing 'list' as + // preformatted text-tokens to the token-collection + // thereby converting TABs. + if(!style) pre = true; + prePos = 0; + + while ( !list.isEmpty() ) + { + checkBuffer(3*TAB_SIZE); + + if (skipLF && ( *list != '\n' )) + { + skipLF = false; + } + + if (skipLF) + { + skipLF = false; + ++list; + } + else if (( *list == '\n' ) || ( *list == '\r' )) + { + if (discard == LFDiscard) + { + // Ignore this LF + discard = NoneDiscard; // We have discarded 1 LF + } + else + { + // Process this LF + if (pending) + addPending(); + + // we used to do it not at all and we want to have + // it fixed for textarea. So here we are + if ( textarea ) { + prePos++; + *dest++ = *list; + } else + pending = LFPending; + } + /* Check for MS-DOS CRLF sequence */ + if (*list == '\r') + { + skipLF = true; + } + ++list; + } + else if (( *list == ' ' ) || ( *list == '\t')) + { + if (pending) + addPending(); + if (*list == ' ') + pending = SpacePending; + else + pending = TabPending; + + ++list; + } + else + { + discard = NoneDiscard; + if (pending) + addPending(); + + prePos++; + *dest++ = *list; + ++list; + } + + } + + if ((pending == SpacePending) || (pending == TabPending)) + addPending(); + else + pending = NonePending; + + prePos = 0; + pre = old_pre; +} + +void HTMLTokenizer::parseSpecial(TokenizerString &src) +{ + assert( textarea || title || !Entity ); + assert( !tag ); + assert( xmp+textarea+title+style+script == 1 ); + if (script) + scriptStartLineno = lineno+src.lineCount(); + + if ( comment ) parseComment( src ); + + while ( !src.isEmpty() ) { + checkScriptBuffer(); + unsigned char ch = src->latin1(); + if ( !scriptCodeResync && !brokenComments && !textarea && !xmp && ch == '-' && scriptCodeSize >= 3 && !src.escaped() && QConstString( scriptCode+scriptCodeSize-3, 3 ).string() == "<!-" ) { + comment = true; + scriptCode[ scriptCodeSize++ ] = ch; + ++src; + parseComment( src ); + continue; + } + if ( scriptCodeResync && !tquote && ( ch == '>' ) ) { + ++src; + scriptCodeSize = scriptCodeResync-1; + scriptCodeResync = 0; + scriptCode[ scriptCodeSize ] = scriptCode[ scriptCodeSize + 1 ] = 0; + if ( script ) + scriptHandler(); + else { + processListing(TokenizerString(scriptCode, scriptCodeSize)); + processToken(); + if ( style ) { currToken.tid = ID_STYLE + ID_CLOSE_TAG; } + else if ( textarea ) { currToken.tid = ID_TEXTAREA + ID_CLOSE_TAG; } + else if ( title ) { currToken.tid = ID_TITLE + ID_CLOSE_TAG; } + else if ( xmp ) { currToken.tid = ID_XMP + ID_CLOSE_TAG; } + processToken(); + script = style = textarea = title = xmp = false; + tquote = NoQuote; + scriptCodeSize = scriptCodeResync = 0; + } + return; + } + // possible end of tagname, lets check. + if ( !scriptCodeResync && !escaped && !src.escaped() && ( ch == '>' || ch == '/' || ch <= ' ' ) && ch && + scriptCodeSize >= searchStopperLen && + !QConstString( scriptCode+scriptCodeSize-searchStopperLen, searchStopperLen ).string().find( searchStopper, 0, false )) { + scriptCodeResync = scriptCodeSize-searchStopperLen+1; + tquote = NoQuote; + continue; + } + if ( scriptCodeResync && !escaped ) { + if(ch == '\"') + tquote = (tquote == NoQuote) ? DoubleQuote : ((tquote == SingleQuote) ? SingleQuote : NoQuote); + else if(ch == '\'') + tquote = (tquote == NoQuote) ? SingleQuote : (tquote == DoubleQuote) ? DoubleQuote : NoQuote; + else if (tquote != NoQuote && (ch == '\r' || ch == '\n')) + tquote = NoQuote; + } + escaped = ( !escaped && ch == '\\' ); + if (!scriptCodeResync && (textarea||title) && !src.escaped() && ch == '&') { + QChar *scriptCodeDest = scriptCode+scriptCodeSize; + ++src; + parseEntity(src,scriptCodeDest,true); + scriptCodeSize = scriptCodeDest-scriptCode; + } + else { + scriptCode[ scriptCodeSize++ ] = *src; + ++src; + } + } +} + +void HTMLTokenizer::scriptHandler() +{ + QString currentScriptSrc = scriptSrc; + scriptSrc = QString::null; + + processListing(TokenizerString(scriptCode, scriptCodeSize)); + QString exScript( buffer, dest-buffer ); + + processToken(); + currToken.tid = ID_SCRIPT + ID_CLOSE_TAG; + processToken(); + + // Scripts following a frameset element should not be executed or even loaded in the case of extern scripts. + bool followingFrameset = (parser->doc()->body() && parser->doc()->body()->id() == ID_FRAMESET); + bool effectiveScript = !parser->skipMode() && !followingFrameset; + bool deferredScript = false; + + if ( effectiveScript ) { + CachedScript* cs = 0; + + // forget what we just got, load from src url instead + if ( !currentScriptSrc.isEmpty() && javascript && + (cs = parser->doc()->docLoader()->requestScript(currentScriptSrc, scriptSrcCharset) )) { + cachedScript.enqueue(cs); + } + + if (cs) { + pendingQueue.push(src); + uint scriptCount = cachedScript.count(); + setSrc(TokenizerString()); + scriptCodeSize = scriptCodeResync = 0; + cs->ref(this); + if (cachedScript.count() == scriptCount) + deferredScript = true; + } + else if (currentScriptSrc.isEmpty() && view && javascript ) { + pendingQueue.push(src); + setSrc(TokenizerString()); + scriptCodeSize = scriptCodeResync = 0; + scriptExecution( exScript, QString::null, tagStartLineno /*scriptStartLineno*/ ); + } else { + // script was filtered or disallowed + effectiveScript = false; + } + } + + script = false; + scriptCodeSize = scriptCodeResync = 0; + + if ( !effectiveScript ) + return; + + if ( !m_executingScript && cachedScript.isEmpty() ) { + src.append(pendingQueue.pop()); + } else if ( cachedScript.isEmpty() ) { + write( pendingQueue.pop(), false ); + } else if ( !deferredScript && pendingQueue.count() > 1) { + TokenizerString t = pendingQueue.pop(); + pendingQueue.top().prepend( t ); + } +} + +void HTMLTokenizer::scriptExecution( const QString& str, const QString& scriptURL, + int baseLine) +{ + bool oldscript = script; + m_executingScript++; + script = false; + QString url; + if (scriptURL.isNull() && view) + url = static_cast<DocumentImpl*>(view->part()->document().handle())->URL().url(); + else + url = scriptURL; + + if (view) + view->part()->executeScript(url,baseLine+1,Node(),str); + m_executingScript--; + script = oldscript; +} + +void HTMLTokenizer::parseComment(TokenizerString &src) +{ + // SGML strict + bool strict = parser->doc()->inStrictMode() && parser->doc()->htmlMode() != DocumentImpl::XHtml && !script && !style; + int delimiterCount = 0; + bool canClose = false; + + checkScriptBuffer(src.length()); + while ( src.length() ) { + scriptCode[ scriptCodeSize++ ] = *src; + +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("comment is now: *%s*", src.toString().left(16).latin1()); +#endif + + if (strict) + { + if (src->unicode() == '-') { + delimiterCount++; + if (delimiterCount == 2) { + delimiterCount = 0; + canClose = !canClose; + } + } + else + delimiterCount = 0; + } + + if ((!strict || canClose) && src->unicode() == '>') + { + bool handleBrokenComments = brokenComments && !( script || style ); + bool scriptEnd=false; + if (!strict) + { + if ( scriptCodeSize > 2 && scriptCode[scriptCodeSize-3] == '-' && + scriptCode[scriptCodeSize-2] == '-' ) + scriptEnd=true; + } + + if (canClose || handleBrokenComments || scriptEnd ){ + ++src; + if ( !( title || script || xmp || textarea || style) ) { +#ifdef COMMENTS_IN_DOM + checkScriptBuffer(); + scriptCode[ scriptCodeSize ] = 0; + scriptCode[ scriptCodeSize + 1 ] = 0; + currToken.tid = ID_COMMENT; + processListing(DOMStringIt(scriptCode, scriptCodeSize - 2)); + processToken(); + currToken.tid = ID_COMMENT + ID_CLOSE_TAG; + processToken(); +#endif + scriptCodeSize = 0; + } + comment = false; + return; // Finished parsing comment + } + } + ++src; + } +} + +void HTMLTokenizer::parseServer(TokenizerString &src) +{ + checkScriptBuffer(src.length()); + while ( !src.isEmpty() ) { + scriptCode[ scriptCodeSize++ ] = *src; + if (src->unicode() == '>' && + scriptCodeSize > 1 && scriptCode[scriptCodeSize-2] == '%') { + ++src; + server = false; + scriptCodeSize = 0; + return; // Finished parsing server include + } + ++src; + } +} + +void HTMLTokenizer::parseProcessingInstruction(TokenizerString &src) +{ + char oldchar = 0; + while ( !src.isEmpty() ) + { + unsigned char chbegin = src->latin1(); + if(chbegin == '\'') { + tquote = tquote == SingleQuote ? NoQuote : SingleQuote; + } + else if(chbegin == '\"') { + tquote = tquote == DoubleQuote ? NoQuote : DoubleQuote; + } + // Look for '?>' + // some crappy sites omit the "?" before it, so + // we look for an unquoted '>' instead. (IE compatible) + else if ( chbegin == '>' && ( !tquote || oldchar == '?' ) ) + { + // We got a '?>' sequence + processingInstruction = false; + ++src; + discard=LFDiscard; + return; // Finished parsing comment! + } + ++src; + oldchar = chbegin; + } +} + +void HTMLTokenizer::parseText(TokenizerString &src) +{ + while ( !src.isEmpty() ) + { + // do we need to enlarge the buffer? + checkBuffer(); + + // ascii is okay because we only do ascii comparisons + unsigned char chbegin = src->latin1(); + + if (skipLF && ( chbegin != '\n' )) + { + skipLF = false; + } + + if (skipLF) + { + skipLF = false; + ++src; + } + else if (( chbegin == '\n' ) || ( chbegin == '\r' )) + { + if (chbegin == '\r') + skipLF = true; + + *dest++ = '\n'; + ++src; + } + else { + *dest++ = *src; + ++src; + } + } +} + + +void HTMLTokenizer::parseEntity(TokenizerString &src, QChar *&dest, bool start) +{ + if( start ) + { + cBufferPos = 0; + entityLen = 0; + Entity = SearchEntity; + } + + while( !src.isEmpty() ) + { + ushort cc = src->unicode(); + switch(Entity) { + case NoEntity: + return; + + break; + case SearchEntity: + if(cc == '#') { + cBuffer[cBufferPos++] = cc; + ++src; + Entity = NumericSearch; + } + else + Entity = EntityName; + + break; + + case NumericSearch: + if(cc == 'x' || cc == 'X') { + cBuffer[cBufferPos++] = cc; + ++src; + Entity = Hexadecimal; + } + else if(cc >= '0' && cc <= '9') + Entity = Decimal; + else + Entity = SearchSemicolon; + + break; + + case Hexadecimal: + { + int uc = EntityChar.unicode(); + int ll = kMin<uint>(src.length(), 8); + while(ll--) { + QChar csrc(src->lower()); + cc = csrc.cell(); + + if(csrc.row() || !((cc >= '0' && cc <= '9') || (cc >= 'a' && cc <= 'f'))) { + break; + } + uc = uc*16 + (cc - ( cc < 'a' ? '0' : 'a' - 10)); + cBuffer[cBufferPos++] = cc; + ++src; + } + EntityChar = QChar(uc); + Entity = SearchSemicolon; + break; + } + case Decimal: + { + int uc = EntityChar.unicode(); + int ll = kMin(src.length(), 9-cBufferPos); + while(ll--) { + cc = src->cell(); + + if(src->row() || !(cc >= '0' && cc <= '9')) { + Entity = SearchSemicolon; + break; + } + + uc = uc * 10 + (cc - '0'); + cBuffer[cBufferPos++] = cc; + ++src; + } + EntityChar = QChar(uc); + if(cBufferPos == 9) Entity = SearchSemicolon; + break; + } + case EntityName: + { + int ll = kMin(src.length(), 9-cBufferPos); + while(ll--) { + QChar csrc = *src; + cc = csrc.cell(); + + if(csrc.row() || !((cc >= 'a' && cc <= 'z') || + (cc >= '0' && cc <= '9') || (cc >= 'A' && cc <= 'Z'))) { + Entity = SearchSemicolon; + break; + } + + cBuffer[cBufferPos++] = cc; + ++src; + + // be IE compatible and interpret even unterminated entities + // outside tags. like "foo  stuff bla". + if ( tag == NoTag ) { + const entity* e = kde_findEntity(cBuffer, cBufferPos); + if ( e && e->code < 256 ) { + EntityChar = e->code; + entityLen = cBufferPos; + } + } + } + if(cBufferPos == 9) Entity = SearchSemicolon; + if(Entity == SearchSemicolon) { + if(cBufferPos > 1) { + const entity *e = kde_findEntity(cBuffer, cBufferPos); + // IE only accepts unterminated entities < 256, + // Gecko accepts them all, but only outside tags + if(e && ( tag == NoTag || e->code < 256 || *src == ';' )) { + EntityChar = e->code; + entityLen = cBufferPos; + } + } + } + break; + } + case SearchSemicolon: +#ifdef TOKEN_DEBUG + kdDebug( 6036 ) << "ENTITY " << EntityChar.unicode() << endl; +#endif + fixUpChar(EntityChar); + + if (*src == ';') + ++src; + + if ( !EntityChar.isNull() ) { + checkBuffer(); + if (entityLen > 0 && entityLen < cBufferPos) { + int rem = cBufferPos - entityLen; + src.prepend( TokenizerString(QString::fromAscii(cBuffer+entityLen, rem)) ); + } + src.push( EntityChar ); + } else { +#ifdef TOKEN_DEBUG + kdDebug( 6036 ) << "unknown entity!" << endl; +#endif + checkBuffer(11); + // ignore the sequence, add it to the buffer as plaintext + *dest++ = '&'; + for(unsigned int i = 0; i < cBufferPos; i++) + dest[i] = cBuffer[i]; + dest += cBufferPos; + if (pre) + prePos += cBufferPos+1; + } + + Entity = NoEntity; + EntityChar = QChar::null; + return; + }; + } +} + +void HTMLTokenizer::parseTag(TokenizerString &src) +{ + assert(!Entity ); + checkScriptBuffer( src.length() ); + + while ( !src.isEmpty() ) + { + checkBuffer(); +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + uint l = 0; + while(l < src.length() && (src.toString()[l]).latin1() != '>') + l++; + qDebug("src is now: *%s*, tquote: %d", + src.toString().left(l).latin1(), tquote); +#endif + switch(tag) { + case NoTag: + return; + case TagName: + { +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("TagName"); +#endif + if (searchCount > 0) + { + if (*src == commentStart[searchCount]) + { + searchCount++; + if (searchCount == 4) + { +#ifdef TOKEN_DEBUG + kdDebug( 6036 ) << "Found comment" << endl; +#endif + // Found '<!--' sequence + ++src; + dest = buffer; // ignore the previous part of this tag + tag = NoTag; + + comment = true; + parseComment(src); + return; // Finished parsing tag! + } + // cuts of high part, is okay + cBuffer[cBufferPos++] = src->cell(); + ++src; + break; + } + else + searchCount = 0; // Stop looking for '<!--' sequence + } + + bool finish = false; + unsigned int ll = kMin(src.length(), CBUFLEN-cBufferPos); + while(ll--) { + ushort curchar = *src; + if(curchar <= ' ' || curchar == '>' ) { + finish = true; + break; + } + // this is a nasty performance trick. will work for the A-Z + // characters, but not for others. if it contains one, + // we fail anyway + char cc = curchar; + cBuffer[cBufferPos++] = cc | 0x20; + ++src; + } + + // Disadvantage: we add the possible rest of the tag + // as attribute names. ### judge if this causes problems + if(finish || CBUFLEN == cBufferPos) { + bool beginTag; + char* ptr = cBuffer; + unsigned int len = cBufferPos; + cBuffer[cBufferPos] = '\0'; + if ((cBufferPos > 0) && (*ptr == '/')) + { + // End Tag + beginTag = false; + ptr++; + len--; + } + else + // Start Tag + beginTag = true; + // Accept empty xml tags like <br/> + if(len > 1 && ptr[len-1] == '/' ) { + ptr[--len] = '\0'; + // if its like <br/> and not like <input/ value=foo>, take it as flat + if (*src == '>') + currToken.flat = true; + } + + uint tagID = khtml::getTagID(ptr, len); + if (!tagID) { +#ifdef TOKEN_DEBUG + QCString tmp(ptr, len+1); + kdDebug( 6036 ) << "Unknown tag: \"" << tmp.data() << "\"" << endl; +#endif + dest = buffer; + } + else + { +#ifdef TOKEN_DEBUG + QCString tmp(ptr, len+1); + kdDebug( 6036 ) << "found tag id=" << tagID << ": " << tmp.data() << endl; +#endif + currToken.tid = beginTag ? tagID : tagID + ID_CLOSE_TAG; + dest = buffer; + } + tag = SearchAttribute; + cBufferPos = 0; + } + break; + } + case SearchAttribute: + { +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("SearchAttribute"); +#endif + bool atespace = false; + ushort curchar; + while(!src.isEmpty()) { + curchar = *src; + if(curchar > ' ') { + if(curchar == '<' || curchar == '>') + tag = SearchEnd; + else if(atespace && (curchar == '\'' || curchar == '"')) + { + tag = SearchValue; + *dest++ = 0; + attrName = QString::null; + } + else + tag = AttributeName; + + cBufferPos = 0; + break; + } + atespace = true; + ++src; + } + break; + } + case AttributeName: + { +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("AttributeName"); +#endif + ushort curchar; + int ll = kMin(src.length(), CBUFLEN-cBufferPos); + + while(ll--) { + curchar = *src; + if(curchar <= '>') { + if(curchar <= ' ' || curchar == '=' || curchar == '>') { + unsigned int a; + cBuffer[cBufferPos] = '\0'; + a = khtml::getAttrID(cBuffer, cBufferPos); + if ( !a ) { + // did we just get /> or e.g checked/> + if (curchar == '>' && cBufferPos >=1 && cBuffer[cBufferPos-1] == '/') { + currToken.flat = true; + if (cBufferPos>1) + a = khtml::getAttrID(cBuffer, cBufferPos-1); + } + if (!a) + attrName = QString::fromLatin1(QCString(cBuffer, cBufferPos+1).data()); + } + + dest = buffer; + *dest++ = a; +#ifdef TOKEN_DEBUG + if (!a || (cBufferPos && *cBuffer == '!')) + kdDebug( 6036 ) << "Unknown attribute: *" << QCString(cBuffer, cBufferPos+1).data() << "*" << endl; + else + kdDebug( 6036 ) << "Known attribute: " << QCString(cBuffer, cBufferPos+1).data() << endl; +#endif + + tag = SearchEqual; + break; + } + } + cBuffer[cBufferPos++] = + ( curchar >= 'A' && curchar <= 'Z' ) ? curchar | 0x20 : curchar; + ++src; + } + if ( cBufferPos == CBUFLEN ) { + cBuffer[cBufferPos] = '\0'; + attrName = QString::fromLatin1(QCString(cBuffer, cBufferPos+1).data()); + dest = buffer; + *dest++ = 0; + tag = SearchEqual; + } + break; + } + case SearchEqual: + { +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("SearchEqual"); +#endif + ushort curchar; + bool atespace = false; + while(!src.isEmpty()) { + curchar = src->unicode(); + if(curchar > ' ') { + if(curchar == '=') { +#ifdef TOKEN_DEBUG + kdDebug(6036) << "found equal" << endl; +#endif + tag = SearchValue; + ++src; + } + else if(atespace && (curchar == '\'' || curchar == '"')) + { + tag = SearchValue; + *dest++ = 0; + attrName = QString::null; + } + else { + DOMString v(""); + currToken.addAttribute(parser->docPtr(), buffer, attrName, v); + dest = buffer; + tag = SearchAttribute; + } + break; + } + atespace = true; + ++src; + } + break; + } + case SearchValue: + { + ushort curchar; + while(!src.isEmpty()) { + curchar = src->unicode(); + if(curchar > ' ') { + if(( curchar == '\'' || curchar == '\"' )) { + tquote = curchar == '\"' ? DoubleQuote : SingleQuote; + tag = QuotedValue; + ++src; + } else + tag = Value; + + break; + } + ++src; + } + break; + } + case QuotedValue: + { +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("QuotedValue"); +#endif + ushort curchar; + while(!src.isEmpty()) { + checkBuffer(); + + curchar = src->unicode(); + if(curchar <= '\'' && !src.escaped()) { + // ### attributes like '&{blaa....};' are supposed to be treated as jscript. + if ( curchar == '&' ) + { + ++src; + parseEntity(src, dest, true); + break; + } + else if ( (tquote == SingleQuote && curchar == '\'') || + (tquote == DoubleQuote && curchar == '\"') ) + { + // some <input type=hidden> rely on trailing spaces. argh + while(dest > buffer+1 && (*(dest-1) == '\n' || *(dest-1) == '\r')) + dest--; // remove trailing newlines + DOMString v(buffer+1, dest-buffer-1); + currToken.addAttribute(parser->docPtr(), buffer, attrName, v); + + dest = buffer; + tag = SearchAttribute; + tquote = NoQuote; + ++src; + break; + } + } + *dest++ = *src; + ++src; + } + break; + } + case Value: + { +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("Value"); +#endif + ushort curchar; + while(!src.isEmpty()) { + checkBuffer(); + curchar = src->unicode(); + if(curchar <= '>' && !src.escaped()) { + // parse Entities + if ( curchar == '&' ) + { + ++src; + parseEntity(src, dest, true); + break; + } + // no quotes. Every space means end of value + // '/' does not delimit in IE! + if ( curchar <= ' ' || curchar == '>' ) + { + DOMString v(buffer+1, dest-buffer-1); + currToken.addAttribute(parser->docPtr(), buffer, attrName, v); + dest = buffer; + tag = SearchAttribute; + break; + } + } + + *dest++ = *src; + ++src; + } + break; + } + case SearchEnd: + { +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 1 + qDebug("SearchEnd"); +#endif + while(!src.isEmpty()) { + if(*src == '<' || *src == '>') + break; + + if (*src == '/') + currToken.flat = true; + + ++src; + } + if(src.isEmpty() && *src != '<' && *src != '>') break; + + searchCount = 0; // Stop looking for '<!--' sequence + tag = NoTag; + tquote = NoQuote; + if ( *src == '>' ) + ++src; + + if ( !currToken.tid ) //stop if tag is unknown + return; + + uint tagID = currToken.tid; +#if defined(TOKEN_DEBUG) && TOKEN_DEBUG > 0 + kdDebug( 6036 ) << "appending Tag: " << tagID << endl; +#endif + // If the tag requires an end tag it cannot be flat, + // unless we are using the HTML parser to parse XHTML + // The only exception is SCRIPT and priority 0 tokens. + if (tagID < ID_CLOSE_TAG && tagID != ID_SCRIPT && + DOM::endTag[tagID] == DOM::REQUIRED && + parser->doc()->htmlMode() != DocumentImpl::XHtml) + currToken.flat = false; + + bool beginTag = !currToken.flat && (tagID < ID_CLOSE_TAG); + + if(tagID >= ID_CLOSE_TAG) + tagID -= ID_CLOSE_TAG; + else if ( !brokenScript && tagID == ID_SCRIPT ) { + DOMStringImpl* a = 0; + bool foundTypeAttribute = false; + scriptSrc = scriptSrcCharset = QString::null; + if ( currToken.attrs && /* potentially have a ATTR_SRC ? */ + view && /* are we a regular tokenizer or just for innerHTML ? */ + parser->doc()->view()->part()->jScriptEnabled() /* jscript allowed at all? */ + ) { + if ( ( a = currToken.attrs->getValue( ATTR_SRC ) ) ) + scriptSrc = parser->doc()->completeURL(khtml::parseURL( DOMString(a) ).string() ); + if ( ( a = currToken.attrs->getValue( ATTR_CHARSET ) ) ) + scriptSrcCharset = DOMString(a).string().stripWhiteSpace(); + if ( scriptSrcCharset.isEmpty() && view) + scriptSrcCharset = parser->doc()->view()->part()->encoding(); + /* Check type before language, since language is deprecated */ + if ((a = currToken.attrs->getValue(ATTR_TYPE)) != 0 && !DOMString(a).string().isEmpty()) + foundTypeAttribute = true; + else + a = currToken.attrs->getValue(ATTR_LANGUAGE); + } + javascript = true; + + if( foundTypeAttribute ) { + /* + Mozilla 1.5 doesn't accept the text/javascript1.x formats, but WinIE 6 does. + Mozilla 1.5 doesn't accept text/jscript, text/ecmascript, and text/livescript, but WinIE 6 does. + Mozilla 1.5 accepts application/x-javascript, WinIE 6 doesn't. + Mozilla 1.5 allows leading and trailing whitespace, but WinIE 6 doesn't. + Mozilla 1.5 and WinIE 6 both accept the empty string, but neither accept a whitespace-only string. + We want to accept all the values that either of these browsers accept, but not other values. + */ + QString type = DOMString(a).string().stripWhiteSpace().lower(); + if( type.compare("text/javascript") != 0 && + type.compare("text/javascript1.0") != 0 && + type.compare("text/javascript1.1") != 0 && + type.compare("text/javascript1.2") != 0 && + type.compare("text/javascript1.3") != 0 && + type.compare("text/javascript1.4") != 0 && + type.compare("text/javascript1.5") != 0 && + type.compare("text/jscript") != 0 && + type.compare("text/ecmascript") != 0 && + type.compare("text/livescript") != 0 && + type.compare("application/x-javascript") != 0 && + type.compare("application/x-ecmascript") != 0 && + type.compare("application/javascript") != 0 && + type.compare("application/ecmascript") != 0 ) + javascript = false; + } else if( a ) { + /* + Mozilla 1.5 doesn't accept jscript or ecmascript, but WinIE 6 does. + Mozilla 1.5 accepts javascript1.0, javascript1.4, and javascript1.5, but WinIE 6 accepts only 1.1 - 1.3. + Neither Mozilla 1.5 nor WinIE 6 accept leading or trailing whitespace. + We want to accept all the values that either of these browsers accept, but not other values. + */ + QString lang = DOMString(a).string(); + lang = lang.lower(); + if( lang.compare("") != 0 && + lang.compare("javascript") != 0 && + lang.compare("javascript1.0") != 0 && + lang.compare("javascript1.1") != 0 && + lang.compare("javascript1.2") != 0 && + lang.compare("javascript1.3") != 0 && + lang.compare("javascript1.4") != 0 && + lang.compare("javascript1.5") != 0 && + lang.compare("ecmascript") != 0 && + lang.compare("livescript") != 0 && + lang.compare("jscript") ) + javascript = false; + } + } + + processToken(); + + if ( parser->selectMode() && beginTag) + discard = AllDiscard; + + switch( tagID ) { + case ID_PRE: + pre = beginTag; + if (beginTag) + discard = LFDiscard; + prePos = 0; + break; + case ID_BR: + prePos = 0; + break; + case ID_SCRIPT: + if (beginTag) { + searchStopper = scriptEnd; + searchStopperLen = 8; + script = true; + parseSpecial(src); + } + else if (tagID < ID_CLOSE_TAG) // Handle <script src="foo"/> + scriptHandler(); + break; + case ID_STYLE: + if (beginTag) { + searchStopper = styleEnd; + searchStopperLen = 7; + style = true; + parseSpecial(src); + } + break; + case ID_TEXTAREA: + if(beginTag) { + searchStopper = textareaEnd; + searchStopperLen = 10; + textarea = true; + discard = NoneDiscard; + parseSpecial(src); + } + break; + case ID_TITLE: + if (beginTag) { + searchStopper = titleEnd; + searchStopperLen = 7; + title = true; + parseSpecial(src); + } + break; + case ID_XMP: + if (beginTag) { + searchStopper = xmpEnd; + searchStopperLen = 5; + xmp = true; + parseSpecial(src); + } + break; + case ID_SELECT: + select = beginTag; + break; + case ID_PLAINTEXT: + plaintext = beginTag; + break; + } + return; // Finished parsing tag! + } + } // end switch + } + return; +} + +void HTMLTokenizer::addPending() +{ + if ( select && !(comment || script)) + { + *dest++ = ' '; + } + else if ( textarea ) + { + switch(pending) { + case LFPending: *dest++ = '\n'; prePos = 0; break; + case SpacePending: *dest++ = ' '; ++prePos; break; + case TabPending: *dest++ = '\t'; prePos += TAB_SIZE - (prePos % TAB_SIZE); break; + case NonePending: + assert(0); + } + } + else + { + int p; + + switch (pending) + { + case SpacePending: + // Insert a breaking space + *dest++ = QChar(' '); + prePos++; + break; + + case LFPending: + *dest = '\n'; + dest++; + prePos = 0; + break; + + case TabPending: + p = TAB_SIZE - ( prePos % TAB_SIZE ); + for ( int x = 0; x < p; x++ ) + *dest++ = QChar(' '); + prePos += p; + break; + + case NonePending: + assert(0); + break; + } + } + + pending = NonePending; +} + +void HTMLTokenizer::write( const TokenizerString &str, bool appendData ) +{ +#ifdef TOKEN_DEBUG + kdDebug( 6036 ) << this << " Tokenizer::write(\"" << str.toString() << "\"," << appendData << ")" << endl; +#endif + + if ( !buffer ) + return; + + if ( ( m_executingScript && appendData ) || cachedScript.count() ) { + // don't parse; we will do this later + if (pendingQueue.isEmpty()) + pendingQueue.push(str); + else if (appendData) + pendingQueue.bottom().append(str); + else + pendingQueue.top().append(str); + return; + } + + if ( onHold ) { + src.append(str); + return; + } + + if (!src.isEmpty()) + src.append(str); + else + setSrc(str); + m_abort = false; + +// if (Entity) +// parseEntity(src, dest); + + while ( !src.isEmpty() ) + { + if ( m_abort ) + return; + // do we need to enlarge the buffer? + checkBuffer(); + + ushort cc = src->unicode(); + + if (skipLF && (cc != '\n')) + skipLF = false; + + if (skipLF) { + skipLF = false; + ++src; + } + else if ( Entity ) + parseEntity( src, dest ); + else if ( plaintext ) + parseText( src ); + else if (script) + parseSpecial(src); + else if (style) + parseSpecial(src); + else if (xmp) + parseSpecial(src); + else if (textarea) + parseSpecial(src); + else if (title) + parseSpecial(src); + else if (comment) + parseComment(src); + else if (server) + parseServer(src); + else if (processingInstruction) + parseProcessingInstruction(src); + else if (tag) + parseTag(src); + else if ( startTag ) + { + startTag = false; + bool endTag = false; + + switch(cc) { + case '/': + endTag = true; + break; + case '!': + { + // <!-- comment --> + searchCount = 1; // Look for '<!--' sequence to start comment + + break; + } + case '?': + { + // xml processing instruction + processingInstruction = true; + tquote = NoQuote; + parseProcessingInstruction(src); + continue; + + break; + } + case '%': + if (!brokenServer) { + // <% server stuff, handle as comment %> + server = true; + tquote = NoQuote; + parseServer(src); + continue; + } + // else fall through + default: + { + if( ((cc >= 'a') && (cc <= 'z')) || ((cc >= 'A') && (cc <= 'Z'))) + { + // Start of a Start-Tag + } + else + { + // Invalid tag + // Add as is + if (pending) + addPending(); + *dest = '<'; + dest++; + continue; + } + } + }; // end case + + // According to SGML any LF immediately after a starttag, or + // immediately before an endtag should be ignored. + // ### Gecko and MSIE though only ignores LF immediately after + // starttags and only for PRE elements -- asj (28/06-2005) + if ( pending ) + if (!select) + addPending(); + else + pending = NonePending; + + // Cancel unused discards + discard = NoneDiscard; + // if (!endTag) discard = LFDiscard; + + processToken(); + + cBufferPos = 0; + tag = TagName; + parseTag(src); + } + else if ( cc == '&' && !src.escaped()) + { + ++src; + if ( pending ) + addPending(); + discard = NoneDiscard; + parseEntity(src, dest, true); + } + else if ( cc == '<' && !src.escaped()) + { + tagStartLineno = lineno+src.lineCount(); + ++src; + discard = NoneDiscard; + startTag = true; + } + else if (( cc == '\n' ) || ( cc == '\r' )) + { + if (discard == SpaceDiscard) + discard = NoneDiscard; + + if (discard == LFDiscard) { + // Ignore one LF + discard = NoneDiscard; + } + else if (discard == AllDiscard) + { + // Ignore + } + else + { + if (select && !script) { + pending = LFPending; + } else { + if (pending) + addPending(); + pending = LFPending; + } + } + + /* Check for MS-DOS CRLF sequence */ + if (cc == '\r') + { + skipLF = true; + } + ++src; + } + else if (( cc == ' ' ) || ( cc == '\t' )) + { + if(discard == LFDiscard) + discard = NoneDiscard; + + if(discard == SpaceDiscard) { + // Ignore one space + discard = NoneDiscard; + } + else if(discard == AllDiscard) + { + // Ignore + } + else { + if (select && !script) { + if (!pending) + pending = SpacePending; + } else { + if (pending) + addPending(); + if (cc == ' ') + pending = SpacePending; + else + pending = TabPending; + } + } + + ++src; + } + else + { + if (pending) + addPending(); + + discard = NoneDiscard; + if ( pre ) + { + prePos++; + } + *dest = *src; + fixUpChar( *dest ); + ++dest; + ++src; + } + } + + if (noMoreData && cachedScript.isEmpty() && !m_executingScript) + end(); // this actually causes us to be deleted +} + +void HTMLTokenizer::timerEvent( QTimerEvent *e ) +{ + if ( e->timerId() == m_autoCloseTimer && cachedScript.isEmpty() ) { + finish(); + } +} + +void HTMLTokenizer::setAutoClose( bool b ) { + killTimer( m_autoCloseTimer ); + m_autoCloseTimer = 0; + if ( b ) + m_autoCloseTimer = startTimer(100); +} + +void HTMLTokenizer::end() +{ + if ( buffer == 0 ) { + emit finishedParsing(); + return; + } + + // parseTag is using the buffer for different matters + if ( !tag ) + processToken(); + + if(buffer) + KHTML_DELETE_QCHAR_VEC(buffer); + + if(scriptCode) + KHTML_DELETE_QCHAR_VEC(scriptCode); + + scriptCode = 0; + scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0; + buffer = 0; + emit finishedParsing(); +} + +void HTMLTokenizer::finish() +{ + if ( m_autoCloseTimer ) { + killTimer( m_autoCloseTimer ); + m_autoCloseTimer = 0; + } + // do this as long as we don't find matching comment ends + while((title || script || comment || server) && scriptCode && scriptCodeSize) + { + // we've found an unmatched comment start + if (comment) + brokenComments = true; + else if (server) + brokenServer = true; + else if (script) + brokenScript = true; + + checkScriptBuffer(); + scriptCode[ scriptCodeSize ] = 0; + scriptCode[ scriptCodeSize + 1 ] = 0; + int pos; + QString food; + if (title || style || script) + food.setUnicode(scriptCode, scriptCodeSize); + else if (server) { + food = "<"; + food += QString(scriptCode, scriptCodeSize); + } + else { + pos = QConstString(scriptCode, scriptCodeSize).string().find('>'); + food.setUnicode(scriptCode+pos+1, scriptCodeSize-pos-1); // deep copy + } + KHTML_DELETE_QCHAR_VEC(scriptCode); + scriptCode = 0; + scriptCodeSize = scriptCodeMaxSize = scriptCodeResync = 0; + if (script) + scriptHandler(); + + comment = title = server = script = false; + if ( !food.isEmpty() ) + write(food, true); + } + // this indicates we will not receive any more data... but if we are waiting on + // an external script to load, we can't finish parsing until that is done + noMoreData = true; + if (cachedScript.isEmpty() && !m_executingScript && !onHold) + end(); // this actually causes us to be deleted +} + +void HTMLTokenizer::processToken() +{ + KJSProxy *jsProxy = view ? view->part()->jScript() : 0L; + if (jsProxy) + jsProxy->setEventHandlerLineno(tagStartLineno+1); + if ( dest > buffer ) + { +#if 0 + if(currToken.tid) { + qDebug( "unexpected token id: %d, str: *%s*", currToken.tid,QConstString( buffer,dest-buffer ).string().latin1() ); + assert(0); + } + +#endif + currToken.text = new DOMStringImpl( buffer, dest - buffer ); + currToken.text->ref(); + currToken.tid = ID_TEXT; + } + else if(!currToken.tid) { + currToken.reset(); + if (jsProxy) + jsProxy->setEventHandlerLineno(lineno+src.lineCount()+1); + return; + } + + dest = buffer; + +#ifdef TOKEN_DEBUG + QString name = QString( getTagName(currToken.tid) ); + QString text; + if(currToken.text) + text = QConstString(currToken.text->s, currToken.text->l).string(); + + kdDebug( 6036 ) << "Token --> " << name << " id = " << currToken.tid << endl; + if (currToken.flat) + kdDebug( 6036 ) << "Token is FLAT!" << endl; + if(!text.isNull()) + kdDebug( 6036 ) << "text: \"" << text << "\"" << endl; + unsigned long l = currToken.attrs ? currToken.attrs->length() : 0; + if(l) { + kdDebug( 6036 ) << "Attributes: " << l << endl; + for (unsigned long i = 0; i < l; ++i) { + NodeImpl::Id tid = currToken.attrs->idAt(i); + DOMString value = currToken.attrs->valueAt(i); + kdDebug( 6036 ) << " " << tid << " " << parser->doc()->getDocument()->getName(NodeImpl::AttributeId, tid).string() + << "=\"" << value.string() << "\"" << endl; + } + } + kdDebug( 6036 ) << endl; +#endif + + // In some cases, parseToken() can cause javascript code to be executed + // (for example, when setting an attribute that causes an event handler + // to be created). So we need to protect against re-entrancy into the parser + m_executingScript++; + + // pass the token over to the parser, the parser DOES NOT delete the token + parser->parseToken(&currToken); + + m_executingScript--; + + if ( currToken.flat && currToken.tid != ID_TEXT && !parser->noSpaces() ) + discard = NoneDiscard; + + currToken.reset(); + if (jsProxy) + jsProxy->setEventHandlerLineno(1); +} + + +HTMLTokenizer::~HTMLTokenizer() +{ + reset(); + delete parser; +} + + +void HTMLTokenizer::enlargeBuffer(int len) +{ + int newsize = kMax(size*2, size+len); + int oldoffs = (dest - buffer); + + buffer = KHTML_REALLOC_QCHAR_VEC(buffer, newsize); + dest = buffer + oldoffs; + size = newsize; +} + +void HTMLTokenizer::enlargeScriptBuffer(int len) +{ + int newsize = kMax(scriptCodeMaxSize*2, scriptCodeMaxSize+len); + scriptCode = KHTML_REALLOC_QCHAR_VEC(scriptCode, newsize); + scriptCodeMaxSize = newsize; +} + +void HTMLTokenizer::notifyFinished(CachedObject* /*finishedObj*/) +{ + assert(!cachedScript.isEmpty()); + bool done = false; + while (!done && cachedScript.head()->isLoaded()) { + + kdDebug( 6036 ) << "Finished loading an external script" << endl; + + CachedScript* cs = cachedScript.dequeue(); + DOMString scriptSource = cs->script(); +#ifdef TOKEN_DEBUG + kdDebug( 6036 ) << "External script is:" << endl << scriptSource.string() << endl; +#endif + setSrc(TokenizerString()); + + // make sure we forget about the script before we execute the new one + // infinite recursion might happen otherwise + QString cachedScriptUrl( cs->url().string() ); + cs->deref(this); + + scriptExecution( scriptSource.string(), cachedScriptUrl ); + + done = cachedScript.isEmpty(); + + // 'script' is true when we are called synchronously from + // scriptHandler(). In that case scriptHandler() will take care + // of 'scriptOutput'. + if ( !script ) { + while (pendingQueue.count() > 1) { + TokenizerString t = pendingQueue.pop(); + pendingQueue.top().prepend( t ); + } + if (done) { + write(pendingQueue.pop(), false); + } + // we might be deleted at this point, do not + // access any members. + } + } +} + +bool HTMLTokenizer::isWaitingForScripts() const +{ + return cachedScript.count(); +} + +bool HTMLTokenizer::isExecutingScript() const +{ + return (m_executingScript > 0); +} + +void HTMLTokenizer::setSrc(const TokenizerString& source) +{ + lineno += src.lineCount(); + src = source; + src.resetLineCount(); +} + +void HTMLTokenizer::setOnHold(bool _onHold) +{ + if (onHold == _onHold) return; + onHold = _onHold; +} + diff --git a/khtml/html/htmltokenizer.h b/khtml/html/htmltokenizer.h new file mode 100644 index 000000000..5e4186d58 --- /dev/null +++ b/khtml/html/htmltokenizer.h @@ -0,0 +1,358 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 1997 Martin Jones (mjones@kde.org) + (C) 1997 Torben Weis (weis@kde.org) + (C) 1998 Waldo Bastian (bastian@kde.org) + (C) 2001 Dirk Mueller (mueller@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +//---------------------------------------------------------------------------- +// +// KDE HTML Widget -- Tokenizers + +#ifndef HTMLTOKENIZER_H +#define HTMLTOKENIZER_H + +#include <qstring.h> +#include <qobject.h> +#include <qptrqueue.h> + +#include "misc/loader_client.h" +#include "misc/htmltags.h" +#include "misc/stringit.h" +#include "xml/dom_stringimpl.h" +#include "xml/xml_tokenizer.h" +#include "xml/dom_elementimpl.h" +#include "xml/dom_docimpl.h" + +class KCharsets; +class KHTMLView; + +namespace DOM { + class DocumentImpl; + class DocumentFragmentImpl; +} + +namespace khtml { + class CachedScript; + class KHTMLParser; + + /** + * @internal + * represents one HTML tag. Consists of a numerical id, and the list + * of attributes. Can also represent text. In this case the id = 0 and + * text contains the text. + */ + class Token + { + public: + Token() { + tid = 0; + attrs = 0; + text = 0; + flat = false; + //qDebug("new token, creating %08lx", attrs); + } + ~Token() { + if(attrs) attrs->deref(); + if(text) text->deref(); + } + void addAttribute(DocumentImpl* doc, QChar* buffer, const QString& attrName, const DOMString& v) + { + DOMStringImpl *value = 0; + NodeImpl::Id tid = 0; + if(buffer->unicode()) { + tid = buffer->unicode(); + value = v.implementation(); + } + else if ( !attrName.isEmpty() && attrName != "/" ) { + tid = doc->getId(NodeImpl::AttributeId, DOMString(attrName).implementation(), false, true); + value = v.implementation(); + } + + if (value && tid) { + if(!attrs) { + attrs = new DOM::NamedAttrMapImpl(0); + attrs->ref(); + } + if (!attrs->getValue(tid)) + attrs->setValue(tid,value); + } + } + void reset() + { + if(attrs) { + attrs->deref(); + attrs = 0; + } + tid = 0; + if(text) { + text->deref(); + text = 0; + } + flat = false; + } + DOM::NamedAttrMapImpl* attrs; + DOMStringImpl* text; + ushort tid; + bool flat; + }; + +// The count of spaces used for each tab. +#define TAB_SIZE 8 + +//----------------------------------------------------------------------------- + +class HTMLTokenizer : public Tokenizer, public CachedObjectClient +{ + friend class KHTMLParser; +public: + HTMLTokenizer(DOM::DocumentImpl *, KHTMLView * = 0); + HTMLTokenizer(DOM::DocumentImpl *, DOM::DocumentFragmentImpl *frag); + virtual ~HTMLTokenizer(); + + void begin(); + void write( const khtml::TokenizerString &str, bool appendData ); + void end(); + void finish(); + void timerEvent( QTimerEvent *e ); + virtual void setOnHold(bool _onHold); + void abort() { m_abort = true; } + virtual void setAutoClose(bool b=true); + virtual bool isWaitingForScripts() const; + virtual bool isExecutingScript() const; + +protected: + void reset(); + void addPending(); + void processToken(); + void processListing(khtml::TokenizerString list); + + void parseComment(khtml::TokenizerString &str); + void parseServer(khtml::TokenizerString &str); + void parseText(khtml::TokenizerString &str); + void parseListing(khtml::TokenizerString &str); + void parseSpecial(khtml::TokenizerString &str); + void parseTag(khtml::TokenizerString &str); + void parseEntity(khtml::TokenizerString &str, QChar *&dest, bool start = false); + void parseProcessingInstruction(khtml::TokenizerString &str); + void scriptHandler(); + void scriptExecution(const QString& script, const QString& scriptURL = QString::null, int baseLine = 0); + void setSrc(const TokenizerString& source); + + // check if we have enough space in the buffer. + // if not enlarge it + inline void checkBuffer(int len = 10) + { + if ( (dest - buffer) > size-len ) + enlargeBuffer(len); + } + inline void checkScriptBuffer(int len = 10) + { + if ( scriptCodeSize + len >= scriptCodeMaxSize ) + enlargeScriptBuffer(len); + } + + void enlargeBuffer(int len); + void enlargeScriptBuffer(int len); + + // from CachedObjectClient + void notifyFinished(khtml::CachedObject *finishedObj); + +protected: + // Internal buffers + /////////////////// + QChar *buffer; + QChar *dest; + + khtml::Token currToken; + + // the size of buffer + int size; + + // Tokenizer flags + ////////////////// + // are we in quotes within a html tag + enum + { + NoQuote = 0, + SingleQuote, + DoubleQuote + } tquote; + + enum + { + NonePending = 0, + SpacePending, + LFPending, + TabPending + } pending; + + enum + { + NoneDiscard = 0, + SpaceDiscard, // Discard spaces after '=' within tags + LFDiscard, // Discard line breaks immediately after start-tags + AllDiscard // discard all spaces, LF's etc until next non white char + } discard; + + // Discard the LF part of CRLF sequence + bool skipLF; + + // Flag to say that we have the '<' but not the character following it. + bool startTag; + + // Flag to say, we are just parsing a tag, meaning, we are in the middle + // of <tag... + enum { + NoTag = 0, + TagName, + SearchAttribute, + AttributeName, + SearchEqual, + SearchValue, + QuotedValue, + Value, + SearchEnd + } tag; + + // Are we in a &... character entity description? + enum { + NoEntity = 0, + SearchEntity, + NumericSearch, + Hexadecimal, + Decimal, + EntityName, + SearchSemicolon + } Entity; + + // are we in a <script> ... </script> block + bool script; + + QChar EntityChar; + + // Are we in a <pre> ... </pre> block + bool pre; + + // if 'pre == true' we track in which column we are + int prePos; + + // Are we in a <style> ... </style> block + bool style; + + // Are we in a <select> ... </select> block + bool select; + + // Are we in a <xmp> ... </xmp> block + bool xmp; + + // Are we in a <title> ... </title> block + bool title; + + // Are we in plain textmode ? + bool plaintext; + + // XML processing instructions. Ignored at the moment + bool processingInstruction; + + // Area we in a <!-- comment --> block + bool comment; + + // Are we in a <textarea> ... </textarea> block + bool textarea; + + // was the previous character escaped ? + bool escaped; + + // are we in a server includes statement? + bool server; + + bool brokenServer; + + bool brokenScript; + + // name of an unknown attribute + QString attrName; + + // Used to store the code of a srcipting sequence + QChar *scriptCode; + // Size of the script sequenze stored in scriptCode + int scriptCodeSize; + // Maximal size that can be stored in scriptCode + int scriptCodeMaxSize; + // resync point of script code size + int scriptCodeResync; + + // Stores characters if we are scanning for a string like "</script>" + QChar searchBuffer[ 10 ]; + // Counts where we are in the string we are scanning for + int searchCount; + // The string we are searching for + const QChar *searchFor; + // the stopper string + const char* searchStopper; + // the stopper len + int searchStopperLen; + // if no more data is coming, just parse what we have (including ext scripts that + // may be still downloading) and finish + bool noMoreData; + // URL to get source code of script from + QString scriptSrc; + QString scriptSrcCharset; + bool javascript; + // the HTML code we will parse after the external script we are waiting for has loaded + TokenizerQueue pendingQueue; + // true if we are executing a script while parsing a document. This causes the parsing of + // the output of the script to be postponed until after the script has finished executing + int m_executingScript; + QPtrQueue<khtml::CachedScript> cachedScript; + // you can pause the tokenizer if you need to display a dialog or something + bool onHold; + // you can ask the tokenizer to abort the current write() call, e.g. to redirect somewhere else + bool m_abort; + + // if we found one broken comment, there are most likely others as well + // store a flag to get rid of the O(n^2) behavior in such a case. + bool brokenComments; + // current line number + int lineno; + // line number at which the current <script> started + int scriptStartLineno; + int tagStartLineno; + // autoClose mode is used when the tokenizer was created by a script document.writing + // on an already loaded document + int m_autoCloseTimer; + +#define CBUFLEN 1024 + char cBuffer[CBUFLEN+2]; + unsigned int cBufferPos; + unsigned int entityLen; + + khtml::TokenizerString src; + + KCharsets *charsets; + KHTMLParser *parser; + + KHTMLView *view; +}; + +} // namespace + +#endif // HTMLTOKENIZER + |