1/*
2 * Copyright (C) 2010 Google, 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 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 INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#ifndef HTMLInputStream_h
27#define HTMLInputStream_h
28
29#include "core/html/parser/InputStreamPreprocessor.h"
30#include "platform/text/SegmentedString.h"
31
32namespace blink {
33
34// The InputStream is made up of a sequence of SegmentedStrings:
35//
36// [--current--][--next--][--next--] ... [--next--]
37//            /\                         (also called m_last)
38//            L_ current insertion point
39//
40// The current segmented string is stored in InputStream.  Each of the
41// afterInsertionPoint buffers are stored in InsertionPointRecords on the
42// stack.
43//
44// We remove characters from the "current" string in the InputStream.
45// document.write() will add characters at the current insertion point,
46// which appends them to the "current" string.
47//
48// m_last is a pointer to the last of the afterInsertionPoint strings.
49// The network adds data at the end of the InputStream, which appends
50// them to the "last" string.
51class HTMLInputStream {
52    WTF_MAKE_NONCOPYABLE(HTMLInputStream);
53public:
54    HTMLInputStream()
55        : m_last(&m_first)
56    {
57    }
58
59    void appendToEnd(const SegmentedString& string)
60    {
61        m_last->append(string);
62    }
63
64    void insertAtCurrentInsertionPoint(const SegmentedString& string)
65    {
66        m_first.append(string);
67    }
68
69    bool hasInsertionPoint() const
70    {
71        return &m_first != m_last;
72    }
73
74    void markEndOfFile()
75    {
76        m_last->append(SegmentedString(String(&kEndOfFileMarker, 1)));
77        m_last->close();
78    }
79
80    void closeWithoutMarkingEndOfFile()
81    {
82        m_last->close();
83    }
84
85    bool haveSeenEndOfFile() const
86    {
87        return m_last->isClosed();
88    }
89
90    SegmentedString& current() { return m_first; }
91    const SegmentedString& current() const { return m_first; }
92
93    void splitInto(SegmentedString& next)
94    {
95        next = m_first;
96        m_first = SegmentedString();
97        if (m_last == &m_first) {
98            // We used to only have one SegmentedString in the InputStream
99            // but now we have two.  That means m_first is no longer also
100            // the m_last string, |next| is now the last one.
101            m_last = &next;
102        }
103    }
104
105    void mergeFrom(SegmentedString& next)
106    {
107        m_first.append(next);
108        if (m_last == &next) {
109            // The string |next| used to be the last SegmentedString in
110            // the InputStream.  Now that it's been merged into m_first,
111            // that makes m_first the last one.
112            m_last = &m_first;
113        }
114        if (next.isClosed()) {
115            // We also need to merge the "closed" state from next to
116            // m_first.  Arguably, this work could be done in append().
117            m_first.close();
118        }
119    }
120
121private:
122    SegmentedString m_first;
123    SegmentedString* m_last;
124};
125
126class InsertionPointRecord {
127    WTF_MAKE_NONCOPYABLE(InsertionPointRecord);
128public:
129    explicit InsertionPointRecord(HTMLInputStream& inputStream)
130        : m_inputStream(&inputStream)
131    {
132        m_line = m_inputStream->current().currentLine();
133        m_column = m_inputStream->current().currentColumn();
134        m_inputStream->splitInto(m_next);
135        // We 'fork' current position and use it for the generated script part.
136        // This is a bit weird, because generated part does not have positions within an HTML document.
137        m_inputStream->current().setCurrentPosition(m_line, m_column, 0);
138    }
139
140    ~InsertionPointRecord()
141    {
142        // Some inserted text may have remained in input stream. E.g. if script has written "&amp" or "<table",
143        // it stays in buffer because it cannot be properly tokenized before we see next part.
144        int unparsedRemainderLength = m_inputStream->current().length();
145        m_inputStream->mergeFrom(m_next);
146        // We restore position for the character that goes right after unparsed remainder.
147        m_inputStream->current().setCurrentPosition(m_line, m_column, unparsedRemainderLength);
148    }
149
150private:
151    HTMLInputStream* m_inputStream;
152    SegmentedString m_next;
153    OrdinalNumber m_line;
154    OrdinalNumber m_column;
155};
156
157}
158
159#endif
160