1/*
2 * Copyright (C) 2010. Adam Barth. 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 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "DocumentWriter.h"
31
32#include "DOMImplementation.h"
33#include "DOMWindow.h"
34#include "Frame.h"
35#include "FrameLoader.h"
36#include "FrameLoaderClient.h"
37#include "FrameLoaderStateMachine.h"
38#include "FrameView.h"
39#include "PlaceholderDocument.h"
40#include "PluginDocument.h"
41#include "RawDataDocumentParser.h"
42#include "ScriptController.h"
43#include "ScriptableDocumentParser.h"
44#include "SecurityOrigin.h"
45#include "SegmentedString.h"
46#include "Settings.h"
47#include "SinkDocument.h"
48#include "TextResourceDecoder.h"
49
50namespace WebCore {
51
52static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame)
53{
54    return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin());
55}
56
57DocumentWriter::DocumentWriter(Frame* frame)
58    : m_frame(frame)
59    , m_hasReceivedSomeData(false)
60    , m_encodingWasChosenByUser(false)
61    , m_state(NotStartedWritingState)
62{
63}
64
65// This is only called by ScriptController::executeIfJavaScriptURL
66// and always contains the result of evaluating a javascript: url.
67// This is the <iframe src="javascript:'html'"> case.
68void DocumentWriter::replaceDocument(const String& source, Document* ownerDocument)
69{
70    m_frame->loader()->stopAllLoaders();
71    begin(m_frame->document()->url(), true, ownerDocument);
72
73    if (!source.isNull()) {
74        if (!m_hasReceivedSomeData) {
75            m_hasReceivedSomeData = true;
76            m_frame->document()->setCompatibilityMode(Document::NoQuirksMode);
77        }
78
79        // FIXME: This should call DocumentParser::appendBytes instead of append
80        // to support RawDataDocumentParsers.
81        if (DocumentParser* parser = m_frame->document()->parser()) {
82            parser->pinToMainThread();
83            // Because we're pinned to the main thread we don't need to worry about
84            // passing ownership of the source string.
85            parser->append(source.impl());
86        }
87    }
88
89    end();
90}
91
92void DocumentWriter::clear()
93{
94    m_decoder = 0;
95    m_hasReceivedSomeData = false;
96    if (!m_encodingWasChosenByUser)
97        m_encoding = String();
98}
99
100void DocumentWriter::begin()
101{
102    begin(KURL());
103}
104
105PassRefPtr<Document> DocumentWriter::createDocument(const KURL& url)
106{
107    if (!m_frame->loader()->stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->loader()->client()->shouldAlwaysUsePluginDocument(m_mimeType))
108        return PluginDocument::create(m_frame, url);
109    if (!m_frame->loader()->client()->hasHTMLView())
110        return PlaceholderDocument::create(m_frame, url);
111    return DOMImplementation::createDocument(m_mimeType, m_frame, url, m_frame->inViewSourceMode());
112}
113
114void DocumentWriter::begin(const KURL& urlReference, bool dispatch, Document* ownerDocument)
115{
116    // We grab a local copy of the URL because it's easy for callers to supply
117    // a URL that will be deallocated during the execution of this function.
118    // For example, see <https://bugs.webkit.org/show_bug.cgi?id=66360>.
119    KURL url = urlReference;
120
121    // Create a new document before clearing the frame, because it may need to
122    // inherit an aliased security context.
123    RefPtr<Document> document = createDocument(url);
124
125    // If the new document is for a Plugin but we're supposed to be sandboxed from Plugins,
126    // then replace the document with one whose parser will ignore the incoming data (bug 39323)
127    if (document->isPluginDocument() && document->isSandboxed(SandboxPlugins))
128        document = SinkDocument::create(m_frame, url);
129
130    // FIXME: Do we need to consult the content security policy here about blocked plug-ins?
131
132    bool shouldReuseDefaultView = m_frame->loader()->stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->document()->isSecureTransitionTo(url);
133    if (shouldReuseDefaultView)
134        document->takeDOMWindowFrom(m_frame->document());
135    else
136        document->createDOMWindow();
137
138    m_frame->loader()->clear(document.get(), !shouldReuseDefaultView, !shouldReuseDefaultView);
139    clear();
140
141    if (!shouldReuseDefaultView)
142        m_frame->script()->updatePlatformScriptObjects();
143
144    m_frame->loader()->setOutgoingReferrer(url);
145    m_frame->setDocument(document);
146
147    if (m_decoder)
148        document->setDecoder(m_decoder.get());
149    if (ownerDocument) {
150        document->setCookieURL(ownerDocument->cookieURL());
151        document->setSecurityOrigin(ownerDocument->securityOrigin());
152    }
153
154    m_frame->loader()->didBeginDocument(dispatch);
155
156    document->implicitOpen();
157
158    // We grab a reference to the parser so that we'll always send data to the
159    // original parser, even if the document acquires a new parser (e.g., via
160    // document.open).
161    m_parser = document->parser();
162
163    if (m_frame->view() && m_frame->loader()->client()->hasHTMLView())
164        m_frame->view()->setContentsSize(IntSize());
165
166    m_state = StartedWritingState;
167}
168
169TextResourceDecoder* DocumentWriter::createDecoderIfNeeded()
170{
171    if (!m_decoder) {
172        if (Settings* settings = m_frame->settings()) {
173            m_decoder = TextResourceDecoder::create(m_mimeType,
174                settings->defaultTextEncodingName(),
175                settings->usesEncodingDetector());
176            Frame* parentFrame = m_frame->tree()->parent();
177            // Set the hint encoding to the parent frame encoding only if
178            // the parent and the current frames share the security origin.
179            // We impose this condition because somebody can make a child frame
180            // containing a carefully crafted html/javascript in one encoding
181            // that can be mistaken for hintEncoding (or related encoding) by
182            // an auto detector. When interpreted in the latter, it could be
183            // an attack vector.
184            // FIXME: This might be too cautious for non-7bit-encodings and
185            // we may consider relaxing this later after testing.
186            if (canReferToParentFrameEncoding(m_frame, parentFrame))
187                m_decoder->setHintEncoding(parentFrame->document()->decoder());
188        } else
189            m_decoder = TextResourceDecoder::create(m_mimeType, String());
190        Frame* parentFrame = m_frame->tree()->parent();
191        if (m_encoding.isEmpty()) {
192            if (canReferToParentFrameEncoding(m_frame, parentFrame))
193                m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame);
194        } else {
195            m_decoder->setEncoding(m_encoding,
196                m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
197        }
198        m_frame->document()->setDecoder(m_decoder.get());
199    }
200    return m_decoder.get();
201}
202
203void DocumentWriter::reportDataReceived()
204{
205    ASSERT(m_decoder);
206    if (m_hasReceivedSomeData)
207        return;
208    m_hasReceivedSomeData = true;
209    if (m_decoder->encoding().usesVisualOrdering())
210        m_frame->document()->setVisuallyOrdered();
211    m_frame->document()->recalcStyle(Node::Force);
212}
213
214void DocumentWriter::addData(const char* bytes, size_t length)
215{
216    // Check that we're inside begin()/end().
217    // FIXME: Change these to ASSERT once https://bugs.webkit.org/show_bug.cgi?id=80427 has
218    // been resolved.
219    if (m_state == NotStartedWritingState)
220        CRASH();
221    if (m_state == FinishedWritingState)
222        CRASH();
223
224    ASSERT(m_parser);
225    m_parser->appendBytes(this, bytes, length);
226}
227
228void DocumentWriter::end()
229{
230    ASSERT(m_frame->page());
231    ASSERT(m_frame->document());
232
233    // The parser is guaranteed to be released after this point. begin() would
234    // have to be called again before we can start writing more data.
235    m_state = FinishedWritingState;
236
237    // http://bugs.webkit.org/show_bug.cgi?id=10854
238    // The frame's last ref may be removed and it can be deleted by checkCompleted(),
239    // so we'll add a protective refcount
240    RefPtr<Frame> protector(m_frame);
241
242    if (!m_parser)
243        return;
244    // FIXME: m_parser->finish() should imply m_parser->flush().
245    m_parser->flush(this);
246    if (!m_parser)
247        return;
248    m_parser->finish();
249    m_parser = 0;
250}
251
252void DocumentWriter::setEncoding(const String& name, bool userChosen)
253{
254    m_encoding = name;
255    m_encodingWasChosenByUser = userChosen;
256}
257
258void DocumentWriter::setDocumentWasLoadedAsPartOfNavigation()
259{
260    ASSERT(m_parser && !m_parser->isStopped());
261    m_parser->setDocumentWasLoadedAsPartOfNavigation();
262}
263
264} // namespace WebCore
265