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 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 "MIMETypeRegistry.h"
40#include "MainFrame.h"
41#include "PluginDocument.h"
42#include "RawDataDocumentParser.h"
43#include "ScriptController.h"
44#include "ScriptableDocumentParser.h"
45#include "SecurityOrigin.h"
46#include "SegmentedString.h"
47#include "Settings.h"
48#include "SinkDocument.h"
49#include "TextResourceDecoder.h"
50#include <wtf/Ref.h>
51
52#if PLATFORM(IOS)
53#include "PDFDocument.h"
54#endif
55
56namespace WebCore {
57
58static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame)
59{
60    return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin());
61}
62
63DocumentWriter::DocumentWriter(Frame* frame)
64    : m_frame(frame)
65    , m_hasReceivedSomeData(false)
66    , m_encodingWasChosenByUser(false)
67    , m_state(NotStartedWritingState)
68{
69}
70
71// This is only called by ScriptController::executeIfJavaScriptURL
72// and always contains the result of evaluating a javascript: url.
73// This is the <iframe src="javascript:'html'"> case.
74void DocumentWriter::replaceDocument(const String& source, Document* ownerDocument)
75{
76    m_frame->loader().stopAllLoaders();
77    begin(m_frame->document()->url(), true, ownerDocument);
78
79    if (!source.isNull()) {
80        if (!m_hasReceivedSomeData) {
81            m_hasReceivedSomeData = true;
82            m_frame->document()->setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
83        }
84
85        // FIXME: This should call DocumentParser::appendBytes instead of append
86        // to support RawDataDocumentParsers.
87        if (DocumentParser* parser = m_frame->document()->parser())
88            parser->append(source.impl());
89    }
90
91    end();
92}
93
94void DocumentWriter::clear()
95{
96    m_decoder = 0;
97    m_hasReceivedSomeData = false;
98    if (!m_encodingWasChosenByUser)
99        m_encoding = String();
100}
101
102void DocumentWriter::begin()
103{
104    begin(URL());
105}
106
107PassRefPtr<Document> DocumentWriter::createDocument(const URL& url)
108{
109    if (!m_frame->loader().stateMachine().isDisplayingInitialEmptyDocument() && m_frame->loader().client().shouldAlwaysUsePluginDocument(m_mimeType))
110        return PluginDocument::create(m_frame, url);
111#if PLATFORM(IOS)
112    if (MIMETypeRegistry::isPDFMIMEType(m_mimeType) && (m_frame->isMainFrame() || !m_frame->settings().useImageDocumentForSubframePDF()))
113        return PDFDocument::create(m_frame, url);
114#endif
115    if (!m_frame->loader().client().hasHTMLView())
116        return Document::createNonRenderedPlaceholder(m_frame, url);
117    return DOMImplementation::createDocument(m_mimeType, m_frame, url);
118}
119
120void DocumentWriter::begin(const URL& urlReference, bool dispatch, Document* ownerDocument)
121{
122    // We grab a local copy of the URL because it's easy for callers to supply
123    // a URL that will be deallocated during the execution of this function.
124    // For example, see <https://bugs.webkit.org/show_bug.cgi?id=66360>.
125    URL url = urlReference;
126
127    // Create a new document before clearing the frame, because it may need to
128    // inherit an aliased security context.
129    RefPtr<Document> document = createDocument(url);
130
131    // If the new document is for a Plugin but we're supposed to be sandboxed from Plugins,
132    // then replace the document with one whose parser will ignore the incoming data (bug 39323)
133    if (document->isPluginDocument() && document->isSandboxed(SandboxPlugins))
134        document = SinkDocument::create(m_frame, url);
135
136    // FIXME: Do we need to consult the content security policy here about blocked plug-ins?
137
138    bool shouldReuseDefaultView = m_frame->loader().stateMachine().isDisplayingInitialEmptyDocument() && m_frame->document()->isSecureTransitionTo(url);
139    if (shouldReuseDefaultView)
140        document->takeDOMWindowFrom(m_frame->document());
141    else
142        document->createDOMWindow();
143
144    m_frame->loader().clear(document.get(), !shouldReuseDefaultView, !shouldReuseDefaultView);
145    clear();
146
147    if (!shouldReuseDefaultView)
148        m_frame->script().updatePlatformScriptObjects();
149
150    m_frame->loader().setOutgoingReferrer(url);
151    m_frame->setDocument(document);
152
153    if (m_decoder)
154        document->setDecoder(m_decoder.get());
155    if (ownerDocument) {
156        document->setCookieURL(ownerDocument->cookieURL());
157        document->setSecurityOrigin(ownerDocument->securityOrigin());
158    }
159
160    m_frame->loader().didBeginDocument(dispatch);
161
162    document->implicitOpen();
163
164    // We grab a reference to the parser so that we'll always send data to the
165    // original parser, even if the document acquires a new parser (e.g., via
166    // document.open).
167    m_parser = document->parser();
168
169    if (m_frame->view() && m_frame->loader().client().hasHTMLView())
170        m_frame->view()->setContentsSize(IntSize());
171
172    m_state = StartedWritingState;
173}
174
175TextResourceDecoder* DocumentWriter::createDecoderIfNeeded()
176{
177    if (!m_decoder) {
178        m_decoder = TextResourceDecoder::create(m_mimeType,
179            m_frame->settings().defaultTextEncodingName(),
180            m_frame->settings().usesEncodingDetector());
181        Frame* parentFrame = m_frame->tree().parent();
182        // Set the hint encoding to the parent frame encoding only if
183        // the parent and the current frames share the security origin.
184        // We impose this condition because somebody can make a child frame
185        // containing a carefully crafted html/javascript in one encoding
186        // that can be mistaken for hintEncoding (or related encoding) by
187        // an auto detector. When interpreted in the latter, it could be
188        // an attack vector.
189        // FIXME: This might be too cautious for non-7bit-encodings and
190        // we may consider relaxing this later after testing.
191        if (canReferToParentFrameEncoding(m_frame, parentFrame))
192            m_decoder->setHintEncoding(parentFrame->document()->decoder());
193        if (m_encoding.isEmpty()) {
194            if (canReferToParentFrameEncoding(m_frame, parentFrame))
195                m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame);
196        } else {
197            m_decoder->setEncoding(m_encoding,
198                m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
199        }
200        m_frame->document()->setDecoder(m_decoder.get());
201    }
202    return m_decoder.get();
203}
204
205void DocumentWriter::reportDataReceived()
206{
207    ASSERT(m_decoder);
208    if (m_hasReceivedSomeData)
209        return;
210    m_hasReceivedSomeData = true;
211    if (m_decoder->encoding().usesVisualOrdering())
212        m_frame->document()->setVisuallyOrdered();
213    m_frame->document()->recalcStyle(Style::Force);
214}
215
216void DocumentWriter::addData(const char* bytes, size_t length)
217{
218    // Check that we're inside begin()/end().
219    // FIXME: Change these to ASSERT once https://bugs.webkit.org/show_bug.cgi?id=80427 has
220    // been resolved.
221    if (m_state == NotStartedWritingState)
222        CRASH();
223    if (m_state == FinishedWritingState)
224        CRASH();
225
226    ASSERT(m_parser);
227    m_parser->appendBytes(*this, bytes, length);
228}
229
230void DocumentWriter::end()
231{
232    ASSERT(m_frame->page());
233    ASSERT(m_frame->document());
234
235    // The parser is guaranteed to be released after this point. begin() would
236    // have to be called again before we can start writing more data.
237    m_state = FinishedWritingState;
238
239    // http://bugs.webkit.org/show_bug.cgi?id=10854
240    // The frame's last ref may be removed and it can be deleted by checkCompleted(),
241    // so we'll add a protective refcount
242    Ref<Frame> protect(*m_frame);
243
244    if (!m_parser)
245        return;
246    // FIXME: m_parser->finish() should imply m_parser->flush().
247    m_parser->flush(*this);
248    if (!m_parser)
249        return;
250    m_parser->finish();
251    m_parser = 0;
252}
253
254void DocumentWriter::setEncoding(const String& name, bool userChosen)
255{
256    m_encoding = name;
257    m_encodingWasChosenByUser = userChosen;
258}
259
260void DocumentWriter::setDocumentWasLoadedAsPartOfNavigation()
261{
262    ASSERT(m_parser && !m_parser->isStopped());
263    m_parser->setDocumentWasLoadedAsPartOfNavigation();
264}
265
266} // namespace WebCore
267