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