Index: third_party/WebKit/Source/core/loader/DocumentLoader.cpp |
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp |
index 2cd5e93762e8d441f37b8bb9d0067302a94e46d3..48d9f34287f4c2959075a846262aed344466280d 100644 |
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp |
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp |
@@ -31,6 +31,7 @@ |
#include <memory> |
#include "core/dom/Document.h" |
+#include "core/dom/DocumentParser.h" |
#include "core/dom/WeakIdentifierMap.h" |
#include "core/events/Event.h" |
#include "core/frame/Deprecation.h" |
@@ -158,6 +159,7 @@ DEFINE_TRACE(DocumentLoader) { |
visitor->trace(m_frame); |
visitor->trace(m_fetcher); |
visitor->trace(m_mainResource); |
+ visitor->trace(m_historyItem); |
visitor->trace(m_writer); |
visitor->trace(m_subresourceFilter); |
visitor->trace(m_documentLoadTiming); |
@@ -273,12 +275,38 @@ void DocumentLoader::didObserveLoadingBehavior( |
} |
} |
+static HistoryCommitType loadTypeToCommitType(FrameLoadType type) { |
+ switch (type) { |
+ case FrameLoadTypeStandard: |
+ return StandardCommit; |
+ case FrameLoadTypeInitialInChildFrame: |
+ case FrameLoadTypeInitialHistoryLoad: |
+ return InitialCommitInChildFrame; |
+ case FrameLoadTypeBackForward: |
+ return BackForwardCommit; |
+ default: |
+ break; |
+ } |
+ return HistoryInertCommit; |
+} |
+ |
void DocumentLoader::updateForSameDocumentNavigation( |
const KURL& newURL, |
- SameDocumentNavigationSource sameDocumentNavigationSource) { |
+ SameDocumentNavigationSource sameDocumentNavigationSource, |
+ PassRefPtr<SerializedScriptValue> data, |
+ HistoryScrollRestorationType scrollRestorationType, |
+ FrameLoadType type, |
+ Document* initiatingDocument) { |
+ if (m_frame->settings()->getHistoryEntryRequiresUserGesture() && |
+ initiatingDocument && |
+ !initiatingDocument->frame()->hasReceivedUserGesture()) { |
+ type = FrameLoadTypeReplaceCurrentItem; |
+ } |
+ |
KURL oldURL = m_request.url(); |
m_originalRequest.setURL(newURL); |
m_request.setURL(newURL); |
+ setReplacesCurrentHistoryItem(type != FrameLoadTypeStandard); |
if (sameDocumentNavigationSource == SameDocumentNavigationHistoryApi) { |
m_request.setHTTPMethod(HTTPNames::GET); |
m_request.setHTTPBody(nullptr); |
@@ -287,16 +315,71 @@ void DocumentLoader::updateForSameDocumentNavigation( |
if (m_isClientRedirect) |
appendRedirect(oldURL); |
appendRedirect(newURL); |
+ |
+ setHistoryItemStateForCommit( |
+ m_historyItem.get(), type, |
+ sameDocumentNavigationSource == SameDocumentNavigationHistoryApi |
+ ? HistoryNavigationType::HistoryApi |
+ : HistoryNavigationType::Fragment); |
+ m_historyItem->setDocumentState(m_frame->document()->formElementsState()); |
Nate Chapin
2017/03/27 21:02:01
This was in FrameLoader::setHistoryItemStateForCom
|
+ if (sameDocumentNavigationSource == SameDocumentNavigationHistoryApi) { |
+ m_historyItem->setStateObject(std::move(data)); |
+ m_historyItem->setScrollRestorationType(scrollRestorationType); |
+ } |
+ localFrameClient().dispatchDidNavigateWithinPage( |
+ m_historyItem.get(), loadTypeToCommitType(type), initiatingDocument); |
} |
const KURL& DocumentLoader::urlForHistory() const { |
return unreachableURL().isEmpty() ? url() : unreachableURL(); |
} |
-void DocumentLoader::commitIfReady() { |
- if (m_state < Committed) { |
- m_state = Committed; |
- frameLoader().commitProvisionalLoad(); |
+void DocumentLoader::setHistoryItemStateForCommit( |
+ HistoryItem* oldItem, |
+ FrameLoadType loadType, |
+ HistoryNavigationType navigationType) { |
+ if (!m_historyItem || !isBackForwardLoadType(loadType)) |
+ m_historyItem = HistoryItem::create(); |
+ |
+ m_historyItem->setURL(urlForHistory()); |
+ m_historyItem->setReferrer(SecurityPolicy::generateReferrer( |
+ m_request.getReferrerPolicy(), m_historyItem->url(), |
+ m_request.httpReferrer())); |
+ m_historyItem->setFormInfoFromRequest(m_request); |
+ |
+ // Don't propagate state from the old item to the new item if there isn't an |
+ // old item (obviously), or if this is a back/forward navigation, since we |
+ // explicitly want to restore the state we just committed. |
+ if (!oldItem || isBackForwardLoadType(loadType)) |
+ return; |
+ // Don't propagate state from the old item if this is a different-document |
+ // navigation, unless the before and after pages are logically related. This |
+ // means they have the same url (ignoring fragment) and the new item was |
+ // loaded via reload or client redirect. |
+ HistoryCommitType historyCommitType = loadTypeToCommitType(loadType); |
+ if (navigationType == HistoryNavigationType::DifferentDocument && |
+ (historyCommitType != HistoryInertCommit || |
+ !equalIgnoringFragmentIdentifier(oldItem->url(), m_historyItem->url()))) |
+ return; |
+ m_historyItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber()); |
+ m_historyItem->setScrollOffset(oldItem->getScrollOffset()); |
+ m_historyItem->setDidSaveScrollOrScaleState( |
+ oldItem->didSaveScrollOrScaleState()); |
+ m_historyItem->setVisualViewportScrollOffset( |
+ oldItem->visualViewportScrollOffset()); |
+ m_historyItem->setPageScaleFactor(oldItem->pageScaleFactor()); |
+ m_historyItem->setScrollRestorationType(oldItem->scrollRestorationType()); |
+ |
+ // The item sequence number determines whether items are "the same", such |
+ // back/forward navigation between items with the same item sequence number is |
+ // a no-op. Only treat this as identical if the navigation did not create a |
+ // back/forward entry and the url is identical or it was loaded via |
+ // history.replaceState(). |
+ if (historyCommitType == HistoryInertCommit && |
+ (navigationType == HistoryNavigationType::HistoryApi || |
+ oldItem->url() == m_historyItem->url())) { |
+ m_historyItem->setStateObject(oldItem->stateObject()); |
+ m_historyItem->setItemSequenceNumber(oldItem->itemSequenceNumber()); |
} |
} |
@@ -318,10 +401,36 @@ void DocumentLoader::notifyFinished(Resource* resource) { |
m_mainResource.get()); |
} |
- frameLoader().loadFailed(this, m_mainResource->resourceError()); |
+ loadFailed(m_mainResource->resourceError()); |
clearMainResourceHandle(); |
} |
+void DocumentLoader::loadFailed(const ResourceError& error) { |
+ if (!error.isCancellation() && m_frame->owner()) { |
+ // FIXME: For now, fallback content doesn't work cross process. |
+ if (m_frame->owner()->isLocal()) |
+ m_frame->deprecatedLocalOwner()->renderFallbackContent(); |
+ } |
+ |
+ HistoryCommitType historyCommitType = loadTypeToCommitType(m_loadType); |
+ FrameLoader& loader = frameLoader(); |
+ if (m_state < Committed) { |
+ if (m_state == NotStarted) |
+ probe::frameClearedScheduledClientNavigation(m_frame); |
+ m_state = SentDidFinishLoad; |
+ localFrameClient().dispatchDidFailProvisionalLoad(error, historyCommitType); |
+ if (!m_frame) |
+ return; |
+ loader.detachProvisionalDocumentLoader(this); |
+ } else if (m_state == Committed) { |
+ if (m_frame->document()->parser()) |
+ m_frame->document()->parser()->stopParsing(); |
+ m_state = SentDidFinishLoad; |
+ localFrameClient().dispatchDidFailLoad(error, historyCommitType); |
+ } |
+ loader.checkCompleted(); |
+} |
+ |
void DocumentLoader::finishedLoading(double finishTime) { |
DCHECK(m_frame->loader().stateMachine()->creatingInitialEmptyDocument() || |
!m_frame->page()->suspended() || |
@@ -333,11 +442,6 @@ void DocumentLoader::finishedLoading(double finishTime) { |
if (!responseEndTime) |
responseEndTime = monotonicallyIncreasingTime(); |
timing().setResponseEnd(responseEndTime); |
- |
- commitIfReady(); |
Nate Chapin
2017/03/27 21:02:01
The equivalent of commitIfReady() now happens in e
|
- if (!m_frame) |
- 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 |
@@ -401,7 +505,7 @@ bool DocumentLoader::redirectReceived( |
// If a redirection happens during a back/forward navigation, don't restore |
yhirano
2017/03/29 09:38:13
Is this comment still valid?
Nate Chapin
2017/03/29 18:23:06
Yes, this is the same behavior as before.
|
// 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. |
- frameLoader().clearProvisionalHistoryItem(); |
+ m_historyItem.clear(); |
localFrameClient().dispatchDidReceiveServerRedirectForProvisionalLoad(); |
@@ -547,6 +651,20 @@ void DocumentLoader::ensureWriter(const AtomicString& mimeType, |
if (m_writer) |
return; |
+ // Set history state before commitProvisionalLoad() so that we still have |
+ // access to the previous committed DocumentLoader's HistoryItem, in case we |
+ // need to copy state from it. |
+ if (!frameLoader().stateMachine()->creatingInitialEmptyDocument()) { |
+ setHistoryItemStateForCommit(frameLoader().documentLoader()->historyItem(), |
+ m_loadType, |
+ HistoryNavigationType::DifferentDocument); |
+ } |
+ |
+ DCHECK_EQ(m_state, Provisional); |
+ frameLoader().commitProvisionalLoad(); |
+ if (!m_frame) |
+ return; |
+ |
const AtomicString& encoding = response().textEncodingName(); |
// Prepare a DocumentInit before clearing the frame, because it may need to |
@@ -581,12 +699,12 @@ void DocumentLoader::ensureWriter(const AtomicString& mimeType, |
} |
void DocumentLoader::commitData(const char* bytes, size_t length) { |
- DCHECK_EQ(m_state, Committed); |
ensureWriter(m_response.mimeType()); |
+ DCHECK_GE(m_state, Committed); |
yhirano
2017/03/29 09:38:13
There are several early returns in FrameLoader::pr
Nate Chapin
2017/03/29 18:23:06
The early exits are the reason it's DCHECK_GE() in
|
// 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()) |
+ if (!m_frame || !m_frame->document()->parsing()) |
return; |
if (length) |
@@ -637,9 +755,6 @@ void DocumentLoader::processData(const char* data, size_t length) { |
if (isArchiveMIMEType(response().mimeType())) |
return; |
- commitIfReady(); |
- if (!m_frame) |
- return; |
commitData(data, length); |
// If we are sending data to MediaDocument, we should stop here and cancel the |
@@ -664,7 +779,7 @@ void DocumentLoader::detachFromFrame() { |
m_fetcher->stopFetching(); |
if (m_frame && !sentDidFinishLoad()) |
- frameLoader().loadFailed(this, ResourceError::cancelledError(url())); |
+ loadFailed(ResourceError::cancelledError(url())); |
// If that load cancellation triggered another detach, leave. |
// (fast/frames/detach-frame-nested-no-crash.html is an example of this.) |
@@ -701,6 +816,8 @@ bool DocumentLoader::maybeCreateArchive() { |
// 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()); |
+ if (!m_frame) |
+ return false; |
// The Document has now been created. |
m_frame->document()->enforceSandboxFlags(SandboxAll); |
@@ -788,7 +905,8 @@ void DocumentLoader::didInstallNewDocument(Document* document) { |
document->setReadyState(Document::Loading); |
document->initContentSecurityPolicy(m_contentSecurityPolicy.release()); |
- frameLoader().didInstallNewDocument(); |
+ if (m_historyItem && isBackForwardLoadType(m_loadType)) |
+ document->setStateForNewFormElements(m_historyItem->getDocumentState()); |
String suboriginHeader = m_response.httpHeaderField(HTTPNames::Suborigin); |
if (!suboriginHeader.isNull()) { |
@@ -843,7 +961,22 @@ void DocumentLoader::didInstallNewDocument(Document* document) { |
void DocumentLoader::didCommitNavigation() { |
if (frameLoader().stateMachine()->creatingInitialEmptyDocument()) |
return; |
- frameLoader().receivedFirstData(); |
+ |
+ localFrameClient().dispatchDidCommitLoad(m_historyItem.get(), |
+ loadTypeToCommitType(m_loadType)); |
+ |
+ // When the embedder gets notified (above) that the new navigation has |
+ // committed, the embedder will drop the old Content Security Policy and |
+ // therefore now is a good time to report to the embedder the Content |
+ // Security Policies that have accumulated so far for the new navigation. |
+ m_frame->securityContext()->contentSecurityPolicy()->reportAccumulatedHeaders( |
+ &localFrameClient()); |
+ |
+ if (!m_frame->loader().stateMachine()->committedMultipleRealLoads() && |
yhirano
2017/03/29 09:38:13
Can you tell my why you changed the calling order
Nate Chapin
2017/03/29 18:23:06
It was accidental, but when we update FrameLoaderS
|
+ m_loadType == FrameLoadTypeStandard) { |
+ m_frame->loader().stateMachine()->advanceTo( |
+ FrameLoaderStateMachine::CommittedMultipleRealLoads); |
+ } |
// didObserveLoadingBehavior() must be called after dispatchDidCommitLoad() is |
// called for the metrics tracking logic to handle it properly. |