fa6e44da5ea82949465acb3bec914848a97ab4cd
[WebKit-https.git] / Source / WebCore / loader / DocumentWriter.cpp
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
56 namespace WebCore {
57
58 static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame) 
59 {
60     return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin());
61 }
62     
63 DocumentWriter::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.
74 void 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
94 void DocumentWriter::clear()
95 {
96     m_decoder = 0;
97     m_hasReceivedSomeData = false;
98     if (!m_encodingWasChosenByUser)
99         m_encoding = String();
100 }
101
102 void DocumentWriter::begin()
103 {
104     begin(URL());
105 }
106
107 PassRefPtr<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
120 void 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
175 TextResourceDecoder* 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
205 void 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
216 void 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
230 void 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
254 void DocumentWriter::setEncoding(const String& name, bool userChosen)
255 {
256     m_encoding = name;
257     m_encodingWasChosenByUser = userChosen;
258 }
259
260 void DocumentWriter::setDocumentWasLoadedAsPartOfNavigation()
261 {
262     ASSERT(m_parser && !m_parser->isStopped());
263     m_parser->setDocumentWasLoadedAsPartOfNavigation();
264 }
265
266 } // namespace WebCore