Home | History | Annotate | Download | only in html
      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