1 /* 2 * Copyright (C) 2006, 2008, 2009, 2010 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 "Attribute.h" 29 #include "DOMImplementation.h" 30 #include "HTMLAnchorElement.h" 31 #include "HTMLBaseElement.h" 32 #include "HTMLBodyElement.h" 33 #include "HTMLDivElement.h" 34 #include "HTMLHtmlElement.h" 35 #include "HTMLNames.h" 36 #include "HTMLTableCellElement.h" 37 #include "HTMLTableElement.h" 38 #include "HTMLTableRowElement.h" 39 #include "HTMLTableSectionElement.h" 40 #include "HTMLToken.h" 41 #include "HTMLViewSourceParser.h" 42 #include "SegmentedString.h" 43 #include "Text.h" 44 #include "TextViewSourceParser.h" 45 46 namespace WebCore { 47 48 using namespace HTMLNames; 49 50 HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, const String& mimeType) 51 : HTMLDocument(frame, url) 52 , m_type(mimeType) 53 { 54 setUsesBeforeAfterRules(true); 55 setUsesViewSourceStyles(true); 56 57 setCompatibilityMode(QuirksMode); 58 lockCompatibilityMode(); 59 } 60 61 PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser() 62 { 63 if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || DOMImplementation::isXMLMIMEType(m_type) 64 #if ENABLE(XHTMLMP) 65 || m_type == "application/vnd.wap.xhtml+xml" 66 #endif 67 ) 68 return HTMLViewSourceParser::create(this); 69 70 return TextViewSourceParser::create(this); 71 } 72 73 void HTMLViewSourceDocument::createContainingTable() 74 { 75 RefPtr<HTMLHtmlElement> html = HTMLHtmlElement::create(this); 76 parserAddChild(html); 77 html->attach(); 78 RefPtr<HTMLBodyElement> body = HTMLBodyElement::create(this); 79 html->parserAddChild(body); 80 body->attach(); 81 82 // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole 83 // document. 84 RefPtr<HTMLDivElement> div = HTMLDivElement::create(this); 85 RefPtr<NamedNodeMap> attrs = NamedNodeMap::create(); 86 attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-gutter-backdrop")); 87 div->setAttributeMap(attrs.release()); 88 body->parserAddChild(div); 89 div->attach(); 90 91 RefPtr<HTMLTableElement> table = HTMLTableElement::create(this); 92 body->parserAddChild(table); 93 table->attach(); 94 m_tbody = HTMLTableSectionElement::create(tbodyTag, this); 95 table->parserAddChild(m_tbody); 96 m_tbody->attach(); 97 m_current = m_tbody; 98 } 99 100 void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token) 101 { 102 if (!m_current) 103 createContainingTable(); 104 105 switch (token.type()) { 106 case HTMLToken::Uninitialized: 107 ASSERT_NOT_REACHED(); 108 break; 109 case HTMLToken::DOCTYPE: 110 processDoctypeToken(source, token); 111 break; 112 case HTMLToken::EndOfFile: 113 break; 114 case HTMLToken::StartTag: 115 case HTMLToken::EndTag: 116 processTagToken(source, token); 117 break; 118 case HTMLToken::Comment: 119 processCommentToken(source, token); 120 break; 121 case HTMLToken::Character: 122 processCharacterToken(source, token); 123 break; 124 } 125 } 126 127 void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&) 128 { 129 if (!m_current) 130 createContainingTable(); 131 m_current = addSpanWithClassName("webkit-html-doctype"); 132 addText(source, "webkit-html-doctype"); 133 m_current = m_td; 134 } 135 136 void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token) 137 { 138 m_current = addSpanWithClassName("webkit-html-tag"); 139 140 AtomicString tagName(token.name().data(), token.name().size()); 141 142 unsigned index = 0; 143 HTMLToken::AttributeList::const_iterator iter = token.attributes().begin(); 144 while (index < source.length()) { 145 if (iter == token.attributes().end()) { 146 // We want to show the remaining characters in the token. 147 index = addRange(source, index, source.length(), ""); 148 ASSERT(index == source.length()); 149 break; 150 } 151 152 AtomicString name(iter->m_name.data(), iter->m_name.size()); 153 String value(iter->m_value.data(), iter->m_value.size()); 154 155 index = addRange(source, index, iter->m_nameRange.m_start - token.startIndex(), ""); 156 index = addRange(source, index, iter->m_nameRange.m_end - token.startIndex(), "webkit-html-attribute-name"); 157 158 if (tagName == baseTag && name == hrefAttr) 159 m_current = addBase(value); 160 161 index = addRange(source, index, iter->m_valueRange.m_start - token.startIndex(), ""); 162 163 bool isLink = name == srcAttr || name == hrefAttr; 164 index = addRange(source, index, iter->m_valueRange.m_end - token.startIndex(), "webkit-html-attribute-value", isLink, tagName == aTag); 165 166 ++iter; 167 } 168 m_current = m_td; 169 } 170 171 void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&) 172 { 173 m_current = addSpanWithClassName("webkit-html-comment"); 174 addText(source, "webkit-html-comment"); 175 m_current = m_td; 176 } 177 178 void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&) 179 { 180 addText(source, ""); 181 } 182 183 PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const AtomicString& className) 184 { 185 if (m_current == m_tbody) { 186 addLine(className); 187 return m_current; 188 } 189 190 RefPtr<HTMLElement> span = HTMLElement::create(spanTag, this); 191 RefPtr<NamedNodeMap> attrs = NamedNodeMap::create(); 192 attrs->addAttribute(Attribute::createMapped(classAttr, className)); 193 span->setAttributeMap(attrs.release()); 194 m_current->parserAddChild(span); 195 span->attach(); 196 return span.release(); 197 } 198 199 void HTMLViewSourceDocument::addLine(const AtomicString& className) 200 { 201 // Create a table row. 202 RefPtr<HTMLTableRowElement> trow = HTMLTableRowElement::create(this); 203 m_tbody->parserAddChild(trow); 204 trow->attach(); 205 206 // Create a cell that will hold the line number (it is generated in the stylesheet using counters). 207 RefPtr<HTMLTableCellElement> td = HTMLTableCellElement::create(tdTag, this); 208 RefPtr<NamedNodeMap> attrs = NamedNodeMap::create(); 209 attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-number")); 210 td->setAttributeMap(attrs.release()); 211 trow->parserAddChild(td); 212 td->attach(); 213 214 // Create a second cell for the line contents 215 td = HTMLTableCellElement::create(tdTag, this); 216 attrs = NamedNodeMap::create(); 217 attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-content")); 218 td->setAttributeMap(attrs.release()); 219 trow->parserAddChild(td); 220 td->attach(); 221 m_current = m_td = td; 222 223 #ifdef DEBUG_LINE_NUMBERS 224 RefPtr<Text> lineNumberText = Text::create(this, String::number(parser()->lineNumber() + 1) + " "); 225 td->addChild(lineNumberText); 226 lineNumberText->attach(); 227 #endif 228 229 // Open up the needed spans. 230 if (!className.isEmpty()) { 231 if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value") 232 m_current = addSpanWithClassName("webkit-html-tag"); 233 m_current = addSpanWithClassName(className); 234 } 235 } 236 237 void HTMLViewSourceDocument::addText(const String& text, const AtomicString& className) 238 { 239 if (text.isEmpty()) 240 return; 241 242 // Add in the content, splitting on newlines. 243 Vector<String> lines; 244 text.split('\n', true, lines); 245 unsigned size = lines.size(); 246 for (unsigned i = 0; i < size; i++) { 247 String substring = lines[i]; 248 if (substring.isEmpty()) { 249 if (i == size - 1) 250 break; 251 substring = " "; 252 } 253 if (m_current == m_tbody) 254 addLine(className); 255 RefPtr<Text> t = Text::create(this, substring); 256 m_current->parserAddChild(t); 257 t->attach(); 258 if (i < size - 1) 259 m_current = m_tbody; 260 } 261 262 // Set current to m_tbody if the last character was a newline. 263 if (text[text.length() - 1] == '\n') 264 m_current = m_tbody; 265 } 266 267 int HTMLViewSourceDocument::addRange(const String& source, int start, int end, const String& className, bool isLink, bool isAnchor) 268 { 269 ASSERT(start <= end); 270 if (start == end) 271 return start; 272 273 String text = source.substring(start, end - start); 274 if (!className.isEmpty()) { 275 if (isLink) 276 m_current = addLink(text, isAnchor); 277 else 278 m_current = addSpanWithClassName(className); 279 } 280 addText(text, className); 281 if (!className.isEmpty() && m_current != m_tbody) 282 m_current = static_cast<Element*>(m_current->parentNode()); 283 return end; 284 } 285 286 PassRefPtr<Element> HTMLViewSourceDocument::addBase(const AtomicString& href) 287 { 288 RefPtr<HTMLBaseElement> base = HTMLBaseElement::create(baseTag, this); 289 RefPtr<NamedNodeMap> attributeMap = NamedNodeMap::create(); 290 attributeMap->addAttribute(Attribute::createMapped(hrefAttr, href)); 291 base->setAttributeMap(attributeMap.release()); 292 m_current->parserAddChild(base); 293 base->attach(); 294 return base.release(); 295 } 296 297 PassRefPtr<Element> HTMLViewSourceDocument::addLink(const AtomicString& url, bool isAnchor) 298 { 299 if (m_current == m_tbody) 300 addLine("webkit-html-tag"); 301 302 // Now create a link for the attribute value instead of a span. 303 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::create(this); 304 RefPtr<NamedNodeMap> attrs = NamedNodeMap::create(); 305 const char* classValue; 306 if (isAnchor) 307 classValue = "webkit-html-attribute-value webkit-html-external-link"; 308 else 309 classValue = "webkit-html-attribute-value webkit-html-resource-link"; 310 attrs->addAttribute(Attribute::createMapped(classAttr, classValue)); 311 attrs->addAttribute(Attribute::createMapped(targetAttr, "_blank")); 312 attrs->addAttribute(Attribute::createMapped(hrefAttr, url)); 313 anchor->setAttributeMap(attrs.release()); 314 m_current->parserAddChild(anchor); 315 anchor->attach(); 316 return anchor.release(); 317 } 318 319 } 320