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