blob: 90c4187918135b4ca10e9970d0cc91b43fe0756c [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core/loader/DocumentLoader.h"
#include "core/dom/Document.h"
#include "core/dom/DocumentParser.h"
#include "core/dom/WeakIdentifierMap.h"
#include "core/events/Event.h"
#include "core/fetch/CSSStyleSheetResource.h"
#include "core/fetch/FetchInitiatorTypeNames.h"
#include "core/fetch/FetchRequest.h"
#include "core/fetch/FontResource.h"
#include "core/fetch/ImageResource.h"
#include "core/fetch/MemoryCache.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/frame/FrameHost.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/html/HTMLFrameOwnerElement.h"
#include "core/html/parser/HTMLDocumentParser.h"
#include "core/html/parser/TextResourceDecoder.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/loader/FrameFetchContext.h"
#include "core/loader/FrameLoader.h"
#include "core/loader/FrameLoaderClient.h"
#include "core/loader/LinkLoader.h"
#include "core/loader/NetworkHintsInterface.h"
#include "core/loader/ProgressTracker.h"
#include "core/loader/appcache/ApplicationCacheHost.h"
#include "core/loader/resource/ScriptResource.h"
#include "core/page/FrameTree.h"
#include "core/page/Page.h"
#include "platform/HTTPNames.h"
#include "platform/MIMETypeRegistry.h"
#include "platform/UserGestureIndicator.h"
#include "platform/mhtml/ArchiveResource.h"
#include "platform/network/ContentSecurityPolicyResponseHeaders.h"
#include "platform/network/HTTPParsers.h"
#include "platform/plugins/PluginData.h"
#include "platform/weborigin/SchemeRegistry.h"
#include "platform/weborigin/SecurityPolicy.h"
#include "public/platform/Platform.h"
#include "public/platform/WebDocumentSubresourceFilter.h"
#include "wtf/Assertions.h"
#include "wtf/AutoReset.h"
#include "wtf/text/WTFString.h"
#include <memory>
namespace blink {
static bool isArchiveMIMEType(const String& mimeType) {
return equalIgnoringCase("multipart/related", mimeType);
}
static bool shouldInheritSecurityOriginFromOwner(const KURL& url) {
// https://html.spec.whatwg.org/multipage/browsers.html#origin
//
// If a Document is the initial "about:blank" document The origin and
// effective script origin of the Document are those it was assigned when its
// browsing context was created.
//
// Note: We generalize this to all "blank" URLs and invalid URLs because we
// treat all of these URLs as about:blank.
return url.isEmpty() || url.protocolIsAbout();
}
DocumentLoader::DocumentLoader(LocalFrame* frame,
const ResourceRequest& req,
const SubstituteData& substituteData,
ClientRedirectPolicy clientRedirectPolicy)
: m_frame(frame),
m_fetcher(FrameFetchContext::createContextAndFetcher(this, nullptr)),
m_originalRequest(req),
m_substituteData(substituteData),
m_request(req),
m_isClientRedirect(clientRedirectPolicy ==
ClientRedirectPolicy::ClientRedirect),
m_replacesCurrentHistoryItem(false),
m_dataReceived(false),
m_navigationType(NavigationTypeOther),
m_documentLoadTiming(*this),
m_timeOfLastDataReceived(0.0),
m_applicationCacheHost(ApplicationCacheHost::create(this)),
m_wasBlockedAfterXFrameOptionsOrCSP(false),
m_state(NotStarted),
m_inDataReceived(false),
m_dataBuffer(SharedBuffer::create()) {
// The document URL needs to be added to the head of the list as that is
// where the redirects originated.
if (m_isClientRedirect)
appendRedirect(m_frame->document()->url());
}
FrameLoader* DocumentLoader::frameLoader() const {
if (!m_frame)
return nullptr;
return &m_frame->loader();
}
DocumentLoader::~DocumentLoader() {
DCHECK(!m_frame);
DCHECK(!m_mainResource);
DCHECK(!m_applicationCacheHost);
}
DEFINE_TRACE(DocumentLoader) {
visitor->trace(m_frame);
visitor->trace(m_fetcher);
visitor->trace(m_mainResource);
visitor->trace(m_writer);
visitor->trace(m_documentLoadTiming);
visitor->trace(m_applicationCacheHost);
visitor->trace(m_contentSecurityPolicy);
RawResourceClient::trace(visitor);
}
unsigned long DocumentLoader::mainResourceIdentifier() const {
return m_mainResource ? m_mainResource->identifier() : 0;
}
const ResourceRequest& DocumentLoader::originalRequest() const {
return m_originalRequest;
}
const ResourceRequest& DocumentLoader::request() const {
return m_request;
}
const KURL& DocumentLoader::url() const {
return m_request.url();
}
void DocumentLoader::setSubresourceFilter(
std::unique_ptr<WebDocumentSubresourceFilter> subresourceFilter) {
m_subresourceFilter = std::move(subresourceFilter);
}
Resource* DocumentLoader::startPreload(Resource::Type type,
FetchRequest& request) {
Resource* resource = nullptr;
switch (type) {
case Resource::Image:
if (m_frame && m_frame->settings() &&
m_frame->settings()->fetchImagePlaceholders()) {
request.setAllowImagePlaceholder();
}
resource = ImageResource::fetch(request, fetcher());
break;
case Resource::Script:
resource = ScriptResource::fetch(request, fetcher());
break;
case Resource::CSSStyleSheet:
resource = CSSStyleSheetResource::fetch(request, fetcher());
break;
case Resource::Font:
resource = FontResource::fetch(request, fetcher());
break;
case Resource::Media:
resource = RawResource::fetchMedia(request, fetcher());
break;
case Resource::TextTrack:
resource = RawResource::fetchTextTrack(request, fetcher());
break;
case Resource::ImportResource:
resource = RawResource::fetchImport(request, fetcher());
break;
case Resource::Raw:
resource = RawResource::fetch(request, fetcher());
break;
default:
NOTREACHED();
}
// CSP layout tests verify that preloads are subject to access checks by
// seeing if they are in the `preload started` list. Therefore do not add
// them to the list if the load is immediately denied.
if (resource && !resource->resourceError().isAccessCheck())
fetcher()->preloadStarted(resource);
return resource;
}
void DocumentLoader::didRedirect(const KURL& oldURL, const KURL& newURL) {
timing().addRedirect(oldURL, newURL);
// If a redirection happens during a back/forward navigation, don't restore
// any state from the old HistoryItem. There is a provisional history item for
// back/forward navigation only. In the other case, clearing it is a no-op.
DCHECK(frameLoader());
frameLoader()->clearProvisionalHistoryItem();
}
void DocumentLoader::dispatchLinkHeaderPreloads(
ViewportDescriptionWrapper* viewport,
LinkLoader::MediaPreloadPolicy mediaPolicy) {
LinkLoader::loadLinksFromHeader(
response().httpHeaderField(HTTPNames::Link), response().url(),
m_frame->document(), NetworkHintsInterfaceImpl(),
LinkLoader::OnlyLoadResources, mediaPolicy, viewport);
}
void DocumentLoader::didChangePerformanceTiming() {
if (frame() && frame()->isMainFrame() && m_state >= Committed) {
frameLoader()->client()->didChangePerformanceTiming();
}
}
void DocumentLoader::didObserveLoadingBehavior(
WebLoadingBehaviorFlag behavior) {
if (frame() && frame()->isMainFrame()) {
DCHECK_GE(m_state, Committed);
frameLoader()->client()->didObserveLoadingBehavior(behavior);
}
}
void DocumentLoader::updateForSameDocumentNavigation(
const KURL& newURL,
SameDocumentNavigationSource sameDocumentNavigationSource) {
KURL oldURL = m_request.url();
m_originalRequest.setURL(newURL);
m_request.setURL(newURL);
if (sameDocumentNavigationSource == SameDocumentNavigationHistoryApi) {
m_request.setHTTPMethod(HTTPNames::GET);
m_request.setHTTPBody(nullptr);
}
clearRedirectChain();
if (m_isClientRedirect)
appendRedirect(oldURL);
appendRedirect(newURL);
}
const KURL& DocumentLoader::urlForHistory() const {
return unreachableURL().isEmpty() ? url() : unreachableURL();
}
void DocumentLoader::commitIfReady() {
if (m_state < Committed) {
m_state = Committed;
frameLoader()->commitProvisionalLoad();
}
}
void DocumentLoader::notifyFinished(Resource* resource) {
DCHECK_EQ(m_mainResource, resource);
DCHECK(m_mainResource);
if (!m_mainResource->errorOccurred() && !m_mainResource->wasCanceled()) {
finishedLoading(m_mainResource->loadFinishTime());
return;
}
if (m_applicationCacheHost)
m_applicationCacheHost->failedLoadingMainResource();
m_state = MainResourceDone;
frameLoader()->loadFailed(this, m_mainResource->resourceError());
clearMainResourceHandle();
}
void DocumentLoader::finishedLoading(double finishTime) {
DCHECK(m_frame->loader().stateMachine()->creatingInitialEmptyDocument() ||
!m_frame->page()->suspended() ||
InspectorInstrumentation::isDebuggerPaused(m_frame));
double responseEndTime = finishTime;
if (!responseEndTime)
responseEndTime = m_timeOfLastDataReceived;
if (!responseEndTime)
responseEndTime = monotonicallyIncreasingTime();
timing().setResponseEnd(responseEndTime);
commitIfReady();
if (!frameLoader())
return;
if (!maybeCreateArchive()) {
// If this is an empty document, it will not have actually been created yet.
// Commit dummy data so that DocumentWriter::begin() gets called and creates
// the Document.
if (!m_writer)
commitData(0, 0);
}
if (!m_frame)
return;
m_applicationCacheHost->finishedLoadingMainResource();
endWriting();
if (m_state < MainResourceDone)
m_state = MainResourceDone;
clearMainResourceHandle();
}
bool DocumentLoader::redirectReceived(
Resource* resource,
const ResourceRequest& request,
const ResourceResponse& redirectResponse) {
DCHECK_EQ(resource, m_mainResource);
DCHECK(!redirectResponse.isNull());
m_request = request;
// If the redirecting url is not allowed to display content from the target
// origin, then block the redirect.
const KURL& requestURL = m_request.url();
RefPtr<SecurityOrigin> redirectingOrigin =
SecurityOrigin::create(redirectResponse.url());
if (!redirectingOrigin->canDisplay(requestURL)) {
FrameLoader::reportLocalLoadFailed(m_frame, requestURL.getString());
m_fetcher->stopFetching();
return false;
}
if (!frameLoader()->shouldContinueForNavigationPolicy(
m_request, SubstituteData(), this, CheckContentSecurityPolicy,
m_navigationType, NavigationPolicyCurrentTab,
replacesCurrentHistoryItem(), isClientRedirect(), nullptr)) {
m_fetcher->stopFetching();
return false;
}
DCHECK(timing().fetchStart());
appendRedirect(requestURL);
didRedirect(redirectResponse.url(), requestURL);
frameLoader()->client()->dispatchDidReceiveServerRedirectForProvisionalLoad();
return true;
}
static bool canShowMIMEType(const String& mimeType, LocalFrame* frame) {
if (MIMETypeRegistry::isSupportedMIMEType(mimeType))
return true;
PluginData* pluginData = frame->pluginData();
return !mimeType.isEmpty() && pluginData &&
pluginData->supportsMimeType(mimeType);
}
bool DocumentLoader::shouldContinueForResponse() const {
if (m_substituteData.isValid())
return true;
int statusCode = m_response.httpStatusCode();
if (statusCode == 204 || statusCode == 205) {
// The server does not want us to replace the page contents.
return false;
}
if (getContentDispositionType(m_response.httpHeaderField(
HTTPNames::Content_Disposition)) == ContentDispositionAttachment) {
// The server wants us to download instead of replacing the page contents.
// Downloading is handled by the embedder, but we still get the initial
// response so that we can ignore it and clean up properly.
return false;
}
if (!canShowMIMEType(m_response.mimeType(), m_frame))
return false;
return true;
}
void DocumentLoader::cancelLoadAfterXFrameOptionsOrCSPDenied(
const ResourceResponse& response) {
InspectorInstrumentation::continueAfterXFrameOptionsDenied(
m_frame, this, mainResourceIdentifier(), response, m_mainResource.get());
setWasBlockedAfterXFrameOptionsOrCSP();
// Pretend that this was an empty HTTP 200 response. Don't reuse the original
// URL for the empty page (https://crbug.com/622385).
//
// TODO(mkwst): Remove this once XFO moves to the browser.
// https://crbug.com/555418.
clearMainResourceHandle();
KURL blockedURL = SecurityOrigin::urlWithUniqueSecurityOrigin();
m_originalRequest.setURL(blockedURL);
m_request.setURL(blockedURL);
m_redirectChain.pop_back();
appendRedirect(blockedURL);
m_response = ResourceResponse(blockedURL, "text/html", 0, nullAtom, String());
finishedLoading(monotonicallyIncreasingTime());
return;
}
void DocumentLoader::responseReceived(
Resource* resource,
const ResourceResponse& response,
std::unique_ptr<WebDataConsumerHandle> handle) {
DCHECK_EQ(m_mainResource, resource);
DCHECK(!handle);
DCHECK(frame());
m_applicationCacheHost->didReceiveResponseForMainResource(response);
// The memory cache doesn't understand the application cache or its caching
// rules. So if a main resource is served from the application cache, ensure
// we don't save the result for future use. All responses loaded from appcache
// will have a non-zero appCacheID().
if (response.appCacheID())
memoryCache()->remove(m_mainResource.get());
m_contentSecurityPolicy = ContentSecurityPolicy::create();
m_contentSecurityPolicy->setOverrideURLForSelf(response.url());
m_contentSecurityPolicy->didReceiveHeaders(
ContentSecurityPolicyResponseHeaders(response));
if (!m_contentSecurityPolicy->allowAncestors(m_frame, response.url())) {
cancelLoadAfterXFrameOptionsOrCSPDenied(response);
return;
}
// 'frame-ancestors' obviates 'x-frame-options':
// https://w3c.github.io/webappsec/specs/content-security-policy/#frame-ancestors-and-frame-options
if (!m_contentSecurityPolicy->isFrameAncestorsEnforced()) {
HTTPHeaderMap::const_iterator it =
response.httpHeaderFields().find(HTTPNames::X_Frame_Options);
if (it != response.httpHeaderFields().end()) {
String content = it->value;
if (frameLoader()->shouldInterruptLoadForXFrameOptions(
content, response.url(), mainResourceIdentifier())) {
String message = "Refused to display '" +
response.url().elidedString() +
"' in a frame because it set 'X-Frame-Options' to '" +
content + "'.";
ConsoleMessage* consoleMessage = ConsoleMessage::createForRequest(
SecurityMessageSource, ErrorMessageLevel, message, response.url(),
mainResourceIdentifier());
frame()->document()->addConsoleMessage(consoleMessage);
cancelLoadAfterXFrameOptionsOrCSPDenied(response);
return;
}
}
}
if (RuntimeEnabledFeatures::embedderCSPEnforcementEnabled() &&
!frameLoader()->requiredCSP().isEmpty()) {
SecurityOrigin* parentSecurityOrigin =
frame()->tree().parent()->securityContext()->getSecurityOrigin();
if (ContentSecurityPolicy::shouldEnforceEmbeddersPolicy(
response, parentSecurityOrigin)) {
m_contentSecurityPolicy->addPolicyFromHeaderValue(
frameLoader()->requiredCSP(), ContentSecurityPolicyHeaderTypeEnforce,
ContentSecurityPolicyHeaderSourceHTTP);
} else {
String message = "Refused to display '" + response.url().elidedString() +
"' because it has not opted-into the following policy "
"required by its embedder: '" +
frameLoader()->requiredCSP() + "'.";
ConsoleMessage* consoleMessage = ConsoleMessage::createForRequest(
SecurityMessageSource, ErrorMessageLevel, message, response.url(),
mainResourceIdentifier());
frame()->document()->addConsoleMessage(consoleMessage);
cancelLoadAfterXFrameOptionsOrCSPDenied(response);
return;
}
}
DCHECK(!m_frame->page()->suspended());
m_response = response;
if (isArchiveMIMEType(m_response.mimeType()) &&
m_mainResource->getDataBufferingPolicy() != BufferData)
m_mainResource->setDataBufferingPolicy(BufferData);
if (!shouldContinueForResponse()) {
InspectorInstrumentation::continueWithPolicyIgnore(
m_frame, this, m_mainResource->identifier(), m_response,
m_mainResource.get());
m_fetcher->stopFetching();
return;
}
if (m_response.isHTTP()) {
int status = m_response.httpStatusCode();
if ((status < 200 || status >= 300) && m_frame->owner())
m_frame->owner()->renderFallbackContent();
}
}
void DocumentLoader::ensureWriter(const AtomicString& mimeType,
const KURL& overridingURL) {
if (m_writer)
return;
const AtomicString& encoding = response().textEncodingName();
// Prepare a DocumentInit before clearing the frame, because it may need to
// inherit an aliased security context.
Document* owner = nullptr;
// TODO(dcheng): This differs from the behavior of both IE and Firefox: the
// origin is inherited from the document that loaded the URL.
if (shouldInheritSecurityOriginFromOwner(url())) {
Frame* ownerFrame = m_frame->tree().parent();
if (!ownerFrame)
ownerFrame = m_frame->loader().opener();
if (ownerFrame && ownerFrame->isLocalFrame())
owner = toLocalFrame(ownerFrame)->document();
}
DocumentInit init(owner, url(), m_frame);
init.withNewRegistrationContext();
m_frame->loader().clear();
DCHECK(m_frame->page());
ParserSynchronizationPolicy parsingPolicy = AllowAsynchronousParsing;
if ((m_substituteData.isValid() && m_substituteData.forceSynchronousLoad()) ||
!Document::threadedParsingEnabledForTesting())
parsingPolicy = ForceSynchronousParsing;
m_writer = createWriterFor(init, mimeType, encoding, false, parsingPolicy,
overridingURL);
m_writer->setDocumentWasLoadedAsPartOfNavigation();
m_frame->document()->maybeHandleHttpRefresh(
m_response.httpHeaderField(HTTPNames::Refresh),
Document::HttpRefreshFromHeader);
}
void DocumentLoader::commitData(const char* bytes, size_t length) {
DCHECK_LT(m_state, MainResourceDone);
ensureWriter(m_response.mimeType());
// This can happen if document.close() is called by an event handler while
// there's still pending incoming data.
if (m_frame && !m_frame->document()->parsing())
return;
if (length)
m_dataReceived = true;
m_writer->addData(bytes, length);
}
void DocumentLoader::dataReceived(Resource* resource,
const char* data,
size_t length) {
DCHECK(data);
DCHECK(length);
DCHECK_EQ(resource, m_mainResource);
DCHECK(!m_response.isNull());
DCHECK(!m_frame->page()->suspended());
if (m_inDataReceived) {
// If this function is reentered, defer processing of the additional data to
// the top-level invocation. Reentrant calls can occur because of web
// platform (mis-)features that require running a nested message loop:
// - alert(), confirm(), prompt()
// - Detach of plugin elements.
// - Synchronous XMLHTTPRequest
m_dataBuffer->append(data, length);
return;
}
AutoReset<bool> reentrancyProtector(&m_inDataReceived, true);
processData(data, length);
// Process data received in reentrant invocations. Note that the invocations
// of processData() may queue more data in reentrant invocations, so iterate
// until it's empty.
const char* segment;
size_t pos = 0;
while (size_t length = m_dataBuffer->getSomeData(segment, pos)) {
processData(segment, length);
pos += length;
}
// All data has been consumed, so flush the buffer.
m_dataBuffer->clear();
}
void DocumentLoader::processData(const char* data, size_t length) {
m_applicationCacheHost->mainResourceDataReceived(data, length);
m_timeOfLastDataReceived = monotonicallyIncreasingTime();
if (isArchiveMIMEType(response().mimeType()))
return;
commitIfReady();
if (!frameLoader())
return;
commitData(data, length);
// If we are sending data to MediaDocument, we should stop here and cancel the
// request.
if (m_frame && m_frame->document()->isMediaDocument())
m_fetcher->stopFetching();
}
void DocumentLoader::clearRedirectChain() {
m_redirectChain.clear();
}
void DocumentLoader::appendRedirect(const KURL& url) {
m_redirectChain.append(url);
}
void DocumentLoader::detachFromFrame() {
DCHECK(m_frame);
// It never makes sense to have a document loader that is detached from its
// frame have any loads active, so go ahead and kill all the loads.
m_fetcher->stopFetching();
// If that load cancellation triggered another detach, leave.
// (fast/frames/detach-frame-nested-no-crash.html is an example of this.)
if (!m_frame)
return;
m_fetcher->clearContext();
m_applicationCacheHost->detachFromDocumentLoader();
m_applicationCacheHost.clear();
WeakIdentifierMap<DocumentLoader>::notifyObjectDestroyed(this);
clearMainResourceHandle();
m_frame = nullptr;
}
void DocumentLoader::clearMainResourceHandle() {
if (!m_mainResource)
return;
m_mainResource->removeClient(this);
m_mainResource = nullptr;
}
bool DocumentLoader::maybeCreateArchive() {
// Give the archive machinery a crack at this document. If the MIME type is
// not an archive type, it will return 0.
if (!isArchiveMIMEType(m_response.mimeType()))
return false;
DCHECK(m_mainResource);
ArchiveResource* mainResource =
m_fetcher->createArchive(m_mainResource.get());
if (!mainResource)
return false;
// The origin is the MHTML file, we need to set the base URL to the document
// encoded in the MHTML so relative URLs are resolved properly.
ensureWriter(mainResource->mimeType(), mainResource->url());
// The Document has now been created.
m_frame->document()->enforceSandboxFlags(SandboxAll);
commitData(mainResource->data()->data(), mainResource->data()->size());
return true;
}
const AtomicString& DocumentLoader::responseMIMEType() const {
return m_response.mimeType();
}
const KURL& DocumentLoader::unreachableURL() const {
return m_substituteData.failingURL();
}
bool DocumentLoader::maybeLoadEmpty() {
bool shouldLoadEmpty = !m_substituteData.isValid() &&
(m_request.url().isEmpty() ||
SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(
m_request.url().protocol()));
if (!shouldLoadEmpty)
return false;
if (m_request.url().isEmpty() &&
!frameLoader()->stateMachine()->creatingInitialEmptyDocument())
m_request.setURL(blankURL());
m_response =
ResourceResponse(m_request.url(), "text/html", 0, nullAtom, String());
finishedLoading(monotonicallyIncreasingTime());
return true;
}
void DocumentLoader::startLoadingMainResource() {
timing().markNavigationStart();
DCHECK(!m_mainResource);
DCHECK_EQ(m_state, NotStarted);
m_state = Provisional;
if (maybeLoadEmpty())
return;
DCHECK(timing().navigationStart());
// PlzNavigate:
// The fetch has already started in the browser. Don't mark it again.
if (!m_frame->settings()->browserSideNavigationEnabled()) {
DCHECK(!timing().fetchStart());
timing().markFetchStart();
}
DEFINE_STATIC_LOCAL(
ResourceLoaderOptions, mainResourceLoadOptions,
(DoNotBufferData, AllowStoredCredentials, ClientRequestedCredentials,
CheckContentSecurityPolicy, DocumentContext));
FetchRequest fetchRequest(m_request, FetchInitiatorTypeNames::document,
mainResourceLoadOptions);
m_mainResource =
RawResource::fetchMainResource(fetchRequest, fetcher(), m_substituteData);
// PlzNavigate:
// The final access checks are still performed here, potentially rejecting
// the "provisional" load, but the browser side already expects the renderer
// to be able to unconditionally commit.
if (!m_mainResource || (m_frame->settings()->browserSideNavigationEnabled() &&
m_mainResource->errorOccurred())) {
m_request = ResourceRequest(blankURL());
maybeLoadEmpty();
return;
}
// A bunch of headers are set when the underlying resource load begins, and
// m_request needs to include those. Even when using a cached resource, we may
// make some modification to the request, e.g. adding the referer header.
m_request = m_mainResource->isLoading() ? m_mainResource->resourceRequest()
: fetchRequest.resourceRequest();
m_mainResource->addClient(this);
}
void DocumentLoader::endWriting() {
m_writer->end();
m_writer.clear();
}
DocumentWriter* DocumentLoader::createWriterFor(
const DocumentInit& init,
const AtomicString& mimeType,
const AtomicString& encoding,
bool dispatchWindowObjectAvailable,
ParserSynchronizationPolicy parsingPolicy,
const KURL& overridingURL) {
LocalFrame* frame = init.frame();
DCHECK(!frame->document() || !frame->document()->isActive());
DCHECK_EQ(frame->tree().childCount(), 0u);
if (!init.shouldReuseDefaultView())
frame->setDOMWindow(LocalDOMWindow::create(*frame));
Document* document =
frame->localDOMWindow()->installNewDocument(mimeType, init);
if (!init.shouldReuseDefaultView())
frame->page()->chromeClient().installSupplements(*frame);
// This should be set before receivedFirstData().
if (!overridingURL.isEmpty())
frame->document()->setBaseURLOverride(overridingURL);
frame->loader().didInstallNewDocument(dispatchWindowObjectAvailable);
// This must be called before DocumentWriter is created, otherwise HTML parser
// will use stale values from HTMLParserOption.
if (!dispatchWindowObjectAvailable)
frame->loader().receivedFirstData();
frame->loader().didBeginDocument();
return DocumentWriter::create(document, parsingPolicy, mimeType, encoding);
}
const AtomicString& DocumentLoader::mimeType() const {
if (m_writer)
return m_writer->mimeType();
return m_response.mimeType();
}
// This is only called by
// FrameLoader::replaceDocumentWhileExecutingJavaScriptURL()
void DocumentLoader::replaceDocumentWhileExecutingJavaScriptURL(
const DocumentInit& init,
const String& source) {
m_writer = createWriterFor(init, mimeType(),
m_writer ? m_writer->encoding() : emptyAtom, true,
ForceSynchronousParsing);
if (!source.isNull())
m_writer->appendReplacingData(source);
endWriting();
}
DEFINE_WEAK_IDENTIFIER_MAP(DocumentLoader);
} // namespace blink