1 /* 2 * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include "config.h" 26 #include "HTMLViewSourceDocument.h" 27 28 #include "DOMImplementation.h" 29 #include "HTMLAnchorElement.h" 30 #include "HTMLBodyElement.h" 31 #include "HTMLDivElement.h" 32 #include "HTMLHtmlElement.h" 33 #include "HTMLNames.h" 34 #include "HTMLTableCellElement.h" 35 #include "HTMLTableElement.h" 36 #include "HTMLTableRowElement.h" 37 #include "HTMLTableSectionElement.h" 38 #include "HTMLTokenizer.h" 39 #include "MappedAttribute.h" 40 #include "Text.h" 41 #include "TextDocument.h" 42 43 namespace WebCore { 44 45 using namespace HTMLNames; 46 47 HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const String& mimeType) 48 : HTMLDocument(frame) 49 , m_type(mimeType) 50 { 51 setUsesBeforeAfterRules(true); 52 } 53 54 Tokenizer* HTMLViewSourceDocument::createTokenizer() 55 { 56 // Use HTMLTokenizer if applicable, otherwise use TextTokenizer. 57 if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || implementation()->isXMLMIMEType(m_type) 58 #if ENABLE(XHTMLMP) 59 || m_type == "application/vnd.wap.xhtml+xml" 60 #endif 61 ) { 62 return new HTMLTokenizer(this); 63 } 64 65 return createTextTokenizer(this); 66 } 67 68 void HTMLViewSourceDocument::createContainingTable() 69 { 70 RefPtr<HTMLHtmlElement> html = new HTMLHtmlElement(htmlTag, this); 71 addChild(html); 72 html->attach(); 73 RefPtr<HTMLBodyElement> body = new HTMLBodyElement(bodyTag, this); 74 html->addChild(body); 75 body->attach(); 76 77 // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole 78 // document. 79 RefPtr<HTMLDivElement> div = new HTMLDivElement(divTag, this); 80 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create(); 81 attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-gutter-backdrop")); 82 div->setAttributeMap(attrs.release()); 83 body->addChild(div); 84 div->attach(); 85 86 RefPtr<HTMLTableElement> table = new HTMLTableElement(tableTag, this); 87 body->addChild(table); 88 table->attach(); 89 m_tbody = new HTMLTableSectionElement(tbodyTag, this); 90 table->addChild(m_tbody); 91 m_tbody->attach(); 92 m_current = m_tbody; 93 } 94 95 void HTMLViewSourceDocument::addViewSourceText(const String& text) 96 { 97 if (!m_current) 98 createContainingTable(); 99 addText(text, ""); 100 } 101 102 void HTMLViewSourceDocument::addViewSourceToken(Token* token) 103 { 104 if (!m_current) 105 createContainingTable(); 106 107 if (token->tagName == textAtom) 108 addText(token->text.get(), ""); 109 else if (token->tagName == commentAtom) { 110 if (token->beginTag) { 111 m_current = addSpanWithClassName("webkit-html-comment"); 112 addText(String("<!--") + token->text.get() + "-->", "webkit-html-comment"); 113 } 114 } else { 115 // Handle the tag. 116 String classNameStr = "webkit-html-tag"; 117 m_current = addSpanWithClassName(classNameStr); 118 119 String text = "<"; 120 if (!token->beginTag) 121 text += "/"; 122 text += token->tagName; 123 Vector<UChar>* guide = token->m_sourceInfo.get(); 124 if (!guide || !guide->size()) 125 text += ">"; 126 127 addText(text, classNameStr); 128 129 // Walk our guide string that tells us where attribute names/values should go. 130 if (guide && guide->size()) { 131 unsigned size = guide->size(); 132 unsigned begin = 0; 133 unsigned currAttr = 0; 134 RefPtr<Attribute> attr = 0; 135 for (unsigned i = 0; i < size; i++) { 136 if (guide->at(i) == 'a' || guide->at(i) == 'x' || guide->at(i) == 'v') { 137 // Add in the string. 138 addText(String(static_cast<UChar*>(guide->data()) + begin, i - begin), classNameStr); 139 140 begin = i + 1; 141 142 if (guide->at(i) == 'a') { 143 if (token->attrs && currAttr < token->attrs->length()) 144 attr = token->attrs->attributeItem(currAttr++); 145 else 146 attr = 0; 147 } 148 if (attr) { 149 if (guide->at(i) == 'a') { 150 String name = attr->name().toString(); 151 152 m_current = addSpanWithClassName("webkit-html-attribute-name"); 153 addText(name, "webkit-html-attribute-name"); 154 if (m_current != m_tbody) 155 m_current = static_cast<Element*>(m_current->parent()); 156 } else { 157 const String& value = attr->value().string(); 158 159 // Compare ignoring case since HTMLTokenizer doesn't 160 // lower names when passing in tokens to 161 // HTMLViewSourceDocument. 162 if (equalIgnoringCase(token->tagName, "base") && equalIgnoringCase(attr->name().localName(), "href")) { 163 // Catch the href attribute in the base element. 164 // It will be used for rendering anchors created 165 // by addLink() below. 166 setBaseElementURL(KURL(url(), value)); 167 } 168 169 // FIXME: XML could use namespace prefixes and confuse us. 170 if (equalIgnoringCase(attr->name().localName(), "src") || equalIgnoringCase(attr->name().localName(), "href")) 171 m_current = addLink(value, equalIgnoringCase(token->tagName, "a")); 172 else 173 m_current = addSpanWithClassName("webkit-html-attribute-value"); 174 addText(value, "webkit-html-attribute-value"); 175 if (m_current != m_tbody) 176 m_current = static_cast<Element*>(m_current->parent()); 177 } 178 } 179 } 180 } 181 182 // Add in any string that might be left. 183 if (begin < size) 184 addText(String(static_cast<UChar*>(guide->data()) + begin, size - begin), classNameStr); 185 186 // Add in the end tag. 187 addText(">", classNameStr); 188 } 189 190 m_current = m_td; 191 } 192 } 193 194 void HTMLViewSourceDocument::addViewSourceDoctypeToken(DoctypeToken* doctypeToken) 195 { 196 if (!m_current) 197 createContainingTable(); 198 m_current = addSpanWithClassName("webkit-html-doctype"); 199 String text = "<"; 200 text += String::adopt(doctypeToken->m_source); 201 text += ">"; 202 addText(text, "webkit-html-doctype"); 203 } 204 205 PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const String& className) 206 { 207 if (m_current == m_tbody) { 208 addLine(className); 209 return m_current; 210 } 211 212 RefPtr<HTMLElement> span = HTMLElement::create(spanTag, this); 213 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create(); 214 attrs->addAttribute(MappedAttribute::create(classAttr, className)); 215 span->setAttributeMap(attrs.release()); 216 m_current->addChild(span); 217 span->attach(); 218 return span.release(); 219 } 220 221 void HTMLViewSourceDocument::addLine(const String& className) 222 { 223 // Create a table row. 224 RefPtr<HTMLTableRowElement> trow = new HTMLTableRowElement(trTag, this); 225 m_tbody->addChild(trow); 226 trow->attach(); 227 228 // Create a cell that will hold the line number (it is generated in the stylesheet using counters). 229 RefPtr<HTMLTableCellElement> td = new HTMLTableCellElement(tdTag, this); 230 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create(); 231 attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-number")); 232 td->setAttributeMap(attrs.release()); 233 trow->addChild(td); 234 td->attach(); 235 236 // Create a second cell for the line contents 237 td = new HTMLTableCellElement(tdTag, this); 238 attrs = NamedMappedAttrMap::create(); 239 attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-content")); 240 td->setAttributeMap(attrs.release()); 241 trow->addChild(td); 242 td->attach(); 243 m_current = m_td = td; 244 245 #ifdef DEBUG_LINE_NUMBERS 246 RefPtr<Text> lineNumberText = Text::create(this, String::number(tokenizer()->lineNumber() + 1) + " "); 247 td->addChild(lineNumberText); 248 lineNumberText->attach(); 249 #endif 250 251 // Open up the needed spans. 252 if (!className.isEmpty()) { 253 if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value") 254 m_current = addSpanWithClassName("webkit-html-tag"); 255 m_current = addSpanWithClassName(className); 256 } 257 } 258 259 void HTMLViewSourceDocument::addText(const String& text, const String& className) 260 { 261 if (text.isEmpty()) 262 return; 263 264 // Add in the content, splitting on newlines. 265 Vector<String> lines; 266 text.split('\n', true, lines); 267 unsigned size = lines.size(); 268 for (unsigned i = 0; i < size; i++) { 269 String substring = lines[i]; 270 if (substring.isEmpty()) { 271 if (i == size - 1) 272 break; 273 substring = " "; 274 } 275 if (m_current == m_tbody) 276 addLine(className); 277 RefPtr<Text> t = Text::create(this, substring); 278 m_current->addChild(t); 279 t->attach(); 280 if (i < size - 1) 281 m_current = m_tbody; 282 } 283 284 // Set current to m_tbody if the last character was a newline. 285 if (text[text.length() - 1] == '\n') 286 m_current = m_tbody; 287 } 288 289 PassRefPtr<Element> HTMLViewSourceDocument::addLink(const String& url, bool isAnchor) 290 { 291 if (m_current == m_tbody) 292 addLine("webkit-html-tag"); 293 294 // Now create a link for the attribute value instead of a span. 295 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::create(this); 296 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create(); 297 const char* classValue; 298 if (isAnchor) 299 classValue = "webkit-html-attribute-value webkit-html-external-link"; 300 else 301 classValue = "webkit-html-attribute-value webkit-html-resource-link"; 302 attrs->addAttribute(MappedAttribute::create(classAttr, classValue)); 303 attrs->addAttribute(MappedAttribute::create(targetAttr, "_blank")); 304 attrs->addAttribute(MappedAttribute::create(hrefAttr, url)); 305 anchor->setAttributeMap(attrs.release()); 306 m_current->addChild(anchor); 307 anchor->attach(); 308 return anchor.release(); 309 } 310 311 } 312