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 46namespace blink { 47 48using namespace HTMLNames; 49 50namespace { 51 52const char kXSSDetected[] = "Token contains a reflected XSS vector"; 53 54} // namespace 55 56HTMLViewSourceDocument::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 67PassRefPtrWillBeRawPtr<DocumentParser> HTMLViewSourceDocument::createParser() 68{ 69 return HTMLViewSourceParser::create(*this, m_type); 70} 71 72void 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, "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 95void 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 123void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&) 124{ 125 m_current = addSpanWithClassName("html-doctype"); 126 addText(source, "html-doctype"); 127 m_current = m_td; 128} 129 130void HTMLViewSourceDocument::processEndOfFileToken(const String& source, HTMLToken&) 131{ 132 m_current = addSpanWithClassName("html-end-of-file"); 133 addText(source, "html-end-of-file"); 134 m_current = m_td; 135} 136 137void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token, SourceAnnotation annotation) 138{ 139 maybeAddSpanForAnnotation(annotation); 140 m_current = addSpanWithClassName("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(), "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(), "html-attribute-value", isLink, tagName == aTag, value); 167 168 ++iter; 169 } 170 m_current = m_td; 171} 172 173void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&) 174{ 175 m_current = addSpanWithClassName("html-comment"); 176 addText(source, "html-comment"); 177 m_current = m_td; 178} 179 180void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&, SourceAnnotation annotation) 181{ 182 addText(source, "", annotation); 183} 184 185PassRefPtrWillBeRawPtr<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 198void 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, "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, "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 == "html-attribute-name" || className == "html-attribute-value") 219 m_current = addSpanWithClassName("html-tag"); 220 m_current = addSpanWithClassName(className); 221 } 222} 223 224void 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 233void 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 261int 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 280PassRefPtrWillBeRawPtr<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 288PassRefPtrWillBeRawPtr<Element> HTMLViewSourceDocument::addLink(const AtomicString& url, bool isAnchor) 289{ 290 if (m_current == m_tbody) 291 addLine("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 = "html-attribute-value html-external-link"; 298 else 299 classValue = "html-attribute-value 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 307void HTMLViewSourceDocument::maybeAddSpanForAnnotation(SourceAnnotation annotation) 308{ 309 if (annotation == AnnotateSourceAsXSS) { 310 m_current = addSpanWithClassName("highlight"); 311 m_current->setAttribute(titleAttr, kXSSDetected); 312 } 313} 314 315void 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