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 "HTMLNames.h" 29 #include "core/dom/DocumentStyleSheetCollection.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/HTMLHtmlElement.h" 37 #include "core/html/HTMLTableCellElement.h" 38 #include "core/html/HTMLTableElement.h" 39 #include "core/html/HTMLTableRowElement.h" 40 #include "core/html/HTMLTableSectionElement.h" 41 #include "core/html/parser/HTMLToken.h" 42 #include "core/html/parser/HTMLViewSourceParser.h" 43 44 namespace WebCore { 45 46 using namespace HTMLNames; 47 48 HTMLViewSourceDocument::HTMLViewSourceDocument(const DocumentInit& initializer, const String& mimeType) 49 : HTMLDocument(initializer) 50 , m_type(mimeType) 51 { 52 styleSheetCollection()->setUsesBeforeAfterRulesOverride(true); 53 setIsViewSource(true); 54 55 // FIXME: Why do view-source pages need to load in quirks mode? 56 setCompatibilityMode(QuirksMode); 57 lockCompatibilityMode(); 58 } 59 60 PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser() 61 { 62 return HTMLViewSourceParser::create(this, m_type); 63 } 64 65 void HTMLViewSourceDocument::createContainingTable() 66 { 67 RefPtr<HTMLHtmlElement> html = HTMLHtmlElement::create(this); 68 parserAppendChild(html); 69 html->lazyAttach(); 70 RefPtr<HTMLBodyElement> body = HTMLBodyElement::create(this); 71 html->parserAppendChild(body); 72 body->lazyAttach(); 73 74 // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole 75 // document. 76 RefPtr<HTMLDivElement> div = HTMLDivElement::create(this); 77 div->setAttribute(classAttr, "webkit-line-gutter-backdrop"); 78 body->parserAppendChild(div); 79 div->lazyAttach(); 80 81 RefPtr<HTMLTableElement> table = HTMLTableElement::create(this); 82 body->parserAppendChild(table); 83 table->lazyAttach(); 84 m_tbody = HTMLTableSectionElement::create(tbodyTag, this); 85 table->parserAppendChild(m_tbody); 86 m_tbody->lazyAttach(); 87 m_current = m_tbody; 88 m_lineNumber = 0; 89 } 90 91 void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token) 92 { 93 if (!m_current) 94 createContainingTable(); 95 96 switch (token.type()) { 97 case HTMLToken::Uninitialized: 98 ASSERT_NOT_REACHED(); 99 break; 100 case HTMLToken::DOCTYPE: 101 processDoctypeToken(source, token); 102 break; 103 case HTMLToken::EndOfFile: 104 if (!m_tbody->hasChildNodes()) 105 addLine(String()); 106 break; 107 case HTMLToken::StartTag: 108 case HTMLToken::EndTag: 109 processTagToken(source, token); 110 break; 111 case HTMLToken::Comment: 112 processCommentToken(source, token); 113 break; 114 case HTMLToken::Character: 115 processCharacterToken(source, token); 116 break; 117 } 118 } 119 120 void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&) 121 { 122 m_current = addSpanWithClassName("webkit-html-doctype"); 123 addText(source, "webkit-html-doctype"); 124 m_current = m_td; 125 } 126 127 void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token) 128 { 129 m_current = addSpanWithClassName("webkit-html-tag"); 130 131 AtomicString tagName(token.name()); 132 133 unsigned index = 0; 134 HTMLToken::AttributeList::const_iterator iter = token.attributes().begin(); 135 while (index < source.length()) { 136 if (iter == token.attributes().end()) { 137 // We want to show the remaining characters in the token. 138 index = addRange(source, index, source.length(), ""); 139 ASSERT(index == source.length()); 140 break; 141 } 142 143 AtomicString name(iter->name); 144 String value = StringImpl::create8BitIfPossible(iter->value); 145 146 index = addRange(source, index, iter->nameRange.start - token.startIndex(), ""); 147 index = addRange(source, index, iter->nameRange.end - token.startIndex(), "webkit-html-attribute-name"); 148 149 if (tagName == baseTag && name == hrefAttr) 150 m_current = addBase(value); 151 152 index = addRange(source, index, iter->valueRange.start - token.startIndex(), ""); 153 154 bool isLink = name == srcAttr || name == hrefAttr; 155 index = addRange(source, index, iter->valueRange.end - token.startIndex(), "webkit-html-attribute-value", isLink, tagName == aTag, value); 156 157 ++iter; 158 } 159 m_current = m_td; 160 } 161 162 void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&) 163 { 164 m_current = addSpanWithClassName("webkit-html-comment"); 165 addText(source, "webkit-html-comment"); 166 m_current = m_td; 167 } 168 169 void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&) 170 { 171 addText(source, ""); 172 } 173 174 PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const AtomicString& className) 175 { 176 if (m_current == m_tbody) { 177 addLine(className); 178 return m_current; 179 } 180 181 RefPtr<HTMLElement> span = HTMLElement::create(spanTag, this); 182 span->setAttribute(classAttr, className); 183 m_current->parserAppendChild(span); 184 span->lazyAttach(); 185 return span.release(); 186 } 187 188 void HTMLViewSourceDocument::addLine(const AtomicString& className) 189 { 190 // Create a table row. 191 RefPtr<HTMLTableRowElement> trow = HTMLTableRowElement::create(this); 192 m_tbody->parserAppendChild(trow); 193 trow->lazyAttach(); 194 195 // Create a cell that will hold the line number (it is generated in the stylesheet using counters). 196 RefPtr<HTMLTableCellElement> td = HTMLTableCellElement::create(tdTag, this); 197 td->setAttribute(classAttr, "webkit-line-number"); 198 td->setAttribute(valueAttr, String::number(++m_lineNumber)); 199 trow->parserAppendChild(td); 200 td->lazyAttach(); 201 202 // Create a second cell for the line contents 203 td = HTMLTableCellElement::create(tdTag, this); 204 td->setAttribute(classAttr, "webkit-line-content"); 205 trow->parserAppendChild(td); 206 td->lazyAttach(); 207 m_current = m_td = td; 208 209 #ifdef DEBUG_LINE_NUMBERS 210 RefPtr<Text> lineNumberText = Text::create(this, String::number(parser()->lineNumber() + 1) + " "); 211 td->addChild(lineNumberText); 212 lineNumberText->lazyAttach(); 213 #endif 214 215 // Open up the needed spans. 216 if (!className.isEmpty()) { 217 if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value") 218 m_current = addSpanWithClassName("webkit-html-tag"); 219 m_current = addSpanWithClassName(className); 220 } 221 } 222 223 void HTMLViewSourceDocument::finishLine() 224 { 225 if (!m_current->hasChildNodes()) { 226 RefPtr<HTMLBRElement> br = HTMLBRElement::create(this); 227 m_current->parserAppendChild(br); 228 br->lazyAttach(); 229 } 230 m_current = m_tbody; 231 } 232 233 void HTMLViewSourceDocument::addText(const String& text, const AtomicString& className) 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 RefPtr<Text> t = Text::create(this, substring); 253 m_current->parserAppendChild(t); 254 t->lazyAttach(); 255 if (i < size - 1) 256 finishLine(); 257 } 258 } 259 260 int HTMLViewSourceDocument::addRange(const String& source, int start, int end, const String& className, bool isLink, bool isAnchor, const String& link) 261 { 262 ASSERT(start <= end); 263 if (start == end) 264 return start; 265 266 String text = source.substring(start, end - start); 267 if (!className.isEmpty()) { 268 if (isLink) 269 m_current = addLink(link, isAnchor); 270 else 271 m_current = addSpanWithClassName(className); 272 } 273 addText(text, className); 274 if (!className.isEmpty() && m_current != m_tbody) 275 m_current = toElement(m_current->parentNode()); 276 return end; 277 } 278 279 PassRefPtr<Element> HTMLViewSourceDocument::addBase(const AtomicString& href) 280 { 281 RefPtr<HTMLBaseElement> base = HTMLBaseElement::create(baseTag, this); 282 base->setAttribute(hrefAttr, href); 283 m_current->parserAppendChild(base); 284 base->lazyAttach(); 285 return base.release(); 286 } 287 288 PassRefPtr<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 RefPtr<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 anchor->lazyAttach(); 305 return anchor.release(); 306 } 307 308 } 309