Chromium Code Reviews| 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. |