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