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