void RedirectScheduler::scheduleHistoryNavigation(int steps) { if (!m_frame->page()) return; // Invalid history navigations (such as history.forward() during a new load) have the side effect of cancelling any scheduled // redirects. We also avoid the possibility of cancelling the current load by avoiding the scheduled redirection altogether. HistoryItem* specifiedEntry = m_frame->page()->backForwardList()->itemAtIndex(steps); if (!specifiedEntry) { cancel(); return; } #if !ENABLE(HISTORY_ALWAYS_ASYNC) // If the specified entry and the current entry have the same document, this is either a state object traversal or a fragment // traversal (or both) and should be performed synchronously. HistoryItem* currentEntry = m_frame->loader()->history()->currentItem(); if (currentEntry != specifiedEntry && currentEntry->documentSequenceNumber() == specifiedEntry->documentSequenceNumber()) { m_frame->loader()->history()->goToItem(specifiedEntry, FrameLoadTypeIndexedBackForward); return; } #endif // In all other cases, schedule the history traversal to occur asynchronously. schedule(new ScheduledHistoryNavigation(steps)); }
// We do same-document navigation if going to a different item and if either of the following is true: // - The other item corresponds to the same document (for history entries created via pushState or fragment changes). // - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation) bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem& otherItem) const { if (this == &otherItem) return false; if (stateObject() || otherItem.stateObject()) return documentSequenceNumber() == otherItem.documentSequenceNumber(); if ((url().hasFragmentIdentifier() || otherItem.url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem.url())) return documentSequenceNumber() == otherItem.documentSequenceNumber(); return hasSameDocumentTree(otherItem); }
void Page::goToItem(HistoryItem* item, FrameLoadType type) { if (defersLoading()) return; // stopAllLoaders may end up running onload handlers, which could cause further history traversals that may lead to the passed in HistoryItem // being deref()-ed. Make sure we can still use it with HistoryController::goToItem later. RefPtr<HistoryItem> protector(item); // Abort any current load unless we're navigating the current document to a new state object HistoryItem* currentItem = m_mainFrame->loader()->history()->currentItem(); if (!item->stateObject() || !currentItem || item->documentSequenceNumber() != currentItem->documentSequenceNumber() || item == currentItem) { // Define what to do with any open database connections. By default we stop them and terminate the database thread. DatabasePolicy databasePolicy = DatabasePolicyStop; #if ENABLE(DATABASE) // If we're navigating the history via a fragment on the same document, then we do not want to stop databases. const KURL& currentURL = m_mainFrame->loader()->url(); const KURL& newURL = item->url(); if (newURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(currentURL, newURL)) databasePolicy = DatabasePolicyContinue; #endif m_mainFrame->loader()->stopAllLoaders(databasePolicy); } m_mainFrame->loader()->history()->goToItem(item, type); }
void HistoryController::recursiveGoToEntry(LocalFrame* frame, HistoryFrameLoadSet& sameDocumentLoads, HistoryFrameLoadSet& differentDocumentLoads) { ASSERT(m_provisionalEntry); ASSERT(m_currentEntry); HistoryItem* newItem = m_provisionalEntry->itemForFrame(frame); HistoryItem* oldItem = m_currentEntry->itemForFrame(frame); if (!newItem) return; if (!oldItem || (newItem != oldItem && newItem->itemSequenceNumber() != oldItem->itemSequenceNumber())) { if (oldItem && newItem->documentSequenceNumber() == oldItem->documentSequenceNumber()) sameDocumentLoads.set(frame, newItem); else differentDocumentLoads.set(frame, newItem); return; } for (LocalFrame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) recursiveGoToEntry(child, sameDocumentLoads, differentDocumentLoads); }
void HistoryController::createNewBackForwardItem(LocalFrame* targetFrame, HistoryItem* item, bool clipAtTarget) { RefPtr<HistoryItem> newItem = item; if (!m_currentEntry) { m_currentEntry = HistoryEntry::create(newItem.get(), targetFrame->frameID()); } else { HistoryItem* oldItem = m_currentEntry->itemForFrame(targetFrame); if (!clipAtTarget && oldItem) newItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber()); m_previousEntry = m_currentEntry.release(); m_currentEntry = m_previousEntry->cloneAndReplace(newItem.get(), clipAtTarget, targetFrame, m_page); } }
// Does a recursive check that this item and its descendants have the same // document sequence numbers as the other item. bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const { if (documentSequenceNumber() != otherItem->documentSequenceNumber()) return false; if (children().size() != otherItem->children().size()) return false; for (size_t i = 0; i < children().size(); i++) { HistoryItem* child = children()[i].get(); HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber()); if (!otherChild || !child->hasSameDocumentTree(otherChild)) return false; } return true; }
// Does a recursive check that this item and its descendants have the same // document sequence numbers as the other item. bool HistoryItem::hasSameDocumentTree(HistoryItem& otherItem) const { if (documentSequenceNumber() != otherItem.documentSequenceNumber()) return false; if (children().size() != otherItem.children().size()) return false; for (size_t i = 0; i < children().size(); i++) { auto& child = children()[i].get(); auto* otherChild = otherItem.childItemWithDocumentSequenceNumber(child.documentSequenceNumber()); if (!otherChild || !child.hasSameDocumentTree(*otherChild)) return false; } return true; }
static FrameState toFrameState(const HistoryItem& historyItem) { FrameState frameState; frameState.urlString = historyItem.urlString(); frameState.originalURLString = historyItem.originalURLString(); frameState.referrer = historyItem.referrer(); frameState.target = historyItem.target(); frameState.documentState = historyItem.documentState(); if (RefPtr<SerializedScriptValue> stateObject = historyItem.stateObject()) frameState.stateObjectData = stateObject->data(); frameState.documentSequenceNumber = historyItem.documentSequenceNumber(); frameState.itemSequenceNumber = historyItem.itemSequenceNumber(); frameState.scrollPoint = historyItem.scrollPoint(); frameState.pageScaleFactor = historyItem.pageScaleFactor(); if (FormData* formData = const_cast<HistoryItem&>(historyItem).formData()) { HTTPBody httpBody = toHTTPBody(*formData); httpBody.contentType = historyItem.formContentType(); frameState.httpBody = WTF::move(httpBody); } #if PLATFORM(IOS) frameState.exposedContentRect = historyItem.exposedContentRect(); frameState.unobscuredContentRect = historyItem.unobscuredContentRect(); frameState.minimumLayoutSizeInScrollViewCoordinates = historyItem.minimumLayoutSizeInScrollViewCoordinates(); frameState.contentSize = historyItem.contentSize(); frameState.scaleIsInitial = historyItem.scaleIsInitial(); #endif for (auto& childHistoryItem : historyItem.children()) { FrameState childFrameState = toFrameState(childHistoryItem); frameState.children.append(WTF::move(childFrameState)); } return frameState; }
void Page::goToItem(HistoryItem* item, FrameLoadType type) { // Abort any current load unless we're navigating the current document to a new state object HistoryItem* currentItem = m_mainFrame->loader()->history()->currentItem(); if (!item->stateObject() || !currentItem || item->documentSequenceNumber() != currentItem->documentSequenceNumber() || item == currentItem) { // Define what to do with any open database connections. By default we stop them and terminate the database thread. DatabasePolicy databasePolicy = DatabasePolicyStop; #if ENABLE(DATABASE) // If we're navigating the history via a fragment on the same document, then we do not want to stop databases. const KURL& currentURL = m_mainFrame->loader()->url(); const KURL& newURL = item->url(); if (newURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(currentURL, newURL)) databasePolicy = DatabasePolicyContinue; #endif m_mainFrame->loader()->stopAllLoaders(databasePolicy); } m_mainFrame->loader()->history()->goToItem(item, type); }