| Index: Source/core/loader/HistoryController.cpp
|
| diff --git a/Source/core/loader/HistoryController.cpp b/Source/core/loader/HistoryController.cpp
|
| index 23e1b4e2c7f42217e3b4d4beb02f92d9888686c9..4f5f0f48953c49ddaa37a58ad47d516b594a6d16 100644
|
| --- a/Source/core/loader/HistoryController.cpp
|
| +++ b/Source/core/loader/HistoryController.cpp
|
| @@ -45,12 +45,86 @@
|
| #include "core/page/Page.h"
|
| #include "core/page/scrolling/ScrollingCoordinator.h"
|
| #include "platform/Logging.h"
|
| +#include "wtf/Deque.h"
|
| #include "wtf/text/CString.h"
|
|
|
| namespace WebCore {
|
|
|
| -HistoryController::HistoryController(Frame* frame)
|
| - : m_frame(frame)
|
| +PassOwnPtr<HistoryNode> HistoryNode::create(HistoryEntry* entry, HistoryItem* value)
|
| +{
|
| + return adoptPtr(new HistoryNode(entry, value));
|
| +}
|
| +
|
| +HistoryNode* HistoryNode::addChild(PassRefPtr<HistoryItem> item)
|
| +{
|
| + m_children.append(HistoryNode::create(m_entry, item.get()));
|
| + return m_children.last().get();
|
| +}
|
| +
|
| +PassOwnPtr<HistoryNode> HistoryNode::cloneAndReplace(HistoryEntry* newEntry, HistoryItem* newItem, HistoryItem* oldItem, bool clipAtTarget, Frame* frame)
|
| +{
|
| + bool isNodeBeingNavigated = m_value == oldItem;
|
| + HistoryItem* itemForCreate = isNodeBeingNavigated ? newItem : m_value.get();
|
| + OwnPtr<HistoryNode> newHistoryNode = create(newEntry, itemForCreate);
|
| +
|
| + if (!clipAtTarget || !isNodeBeingNavigated) {
|
| + for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
|
| + HistoryNode* childHistoryNode = m_entry->m_framesToItems.get(child->frameID());
|
| + if (!childHistoryNode)
|
| + continue;
|
| + newHistoryNode->m_children.append(childHistoryNode->cloneAndReplace(newEntry, newItem, oldItem, clipAtTarget, child));
|
| + }
|
| + }
|
| + return newHistoryNode.release();
|
| +}
|
| +
|
| +HistoryNode::HistoryNode(HistoryEntry* entry, HistoryItem* value)
|
| + : m_entry(entry)
|
| + , m_value(value)
|
| +{
|
| + m_entry->m_framesToItems.add(value->targetFrameID(), this);
|
| + String target = value->target();
|
| + if (target.isNull())
|
| + target = emptyString();
|
| + m_entry->m_uniqueNamesToItems.add(target, this);
|
| +}
|
| +
|
| +HistoryEntry::HistoryEntry(HistoryItem* root)
|
| +{
|
| + m_root = HistoryNode::create(this, root);
|
| +}
|
| +
|
| +PassOwnPtr<HistoryEntry> HistoryEntry::create(HistoryItem* root)
|
| +{
|
| + return adoptPtr(new HistoryEntry(root));
|
| +}
|
| +
|
| +PassOwnPtr<HistoryEntry> HistoryEntry::cloneAndReplace(HistoryItem* newItem, HistoryItem* oldItem, bool clipAtTarget, Page* page)
|
| +{
|
| + OwnPtr<HistoryEntry> newEntry = adoptPtr(new HistoryEntry());
|
| + newEntry->m_root = m_root->cloneAndReplace(newEntry.get(), newItem, oldItem, clipAtTarget, page->mainFrame());
|
| + return newEntry.release();
|
| +}
|
| +
|
| +HistoryNode* HistoryEntry::historyNodeForFrame(Frame* frame)
|
| +{
|
| + if (HistoryNode* historyNode = m_framesToItems.get(frame->frameID()))
|
| + return historyNode;
|
| + String target = frame->tree().uniqueName();
|
| + if (target.isNull())
|
| + target = emptyString();
|
| + return m_uniqueNamesToItems.get(target);
|
| +}
|
| +
|
| +HistoryItem* HistoryEntry::itemForFrame(Frame* frame)
|
| +{
|
| + if (HistoryNode* historyNode = historyNodeForFrame(frame))
|
| + return historyNode->value();
|
| + return 0;
|
| +}
|
| +
|
| +HistoryController::HistoryController(Page* page)
|
| + : m_page(page)
|
| , m_defersLoading(false)
|
| {
|
| }
|
| @@ -61,11 +135,11 @@ HistoryController::~HistoryController()
|
|
|
| void HistoryController::clearScrollPositionAndViewState()
|
| {
|
| - if (!m_currentItem)
|
| + if (!m_currentEntry->root())
|
| return;
|
|
|
| - m_currentItem->clearScrollPoint();
|
| - m_currentItem->setPageScaleFactor(0);
|
| + m_currentEntry->root()->clearScrollPoint();
|
| + m_currentEntry->root()->setPageScaleFactor(0);
|
| }
|
|
|
| /*
|
| @@ -79,95 +153,123 @@ void HistoryController::clearScrollPositionAndViewState()
|
| 2) If the layout happens after the load completes, the attempt to restore at load completion time silently
|
| fails. We then successfully restore it when the layout happens.
|
| */
|
| -void HistoryController::restoreScrollPositionAndViewState()
|
| +void HistoryController::restoreScrollPositionAndViewState(Frame* frame)
|
| {
|
| - if (!m_currentItem || !m_frame->loader().stateMachine()->committedFirstRealDocumentLoad())
|
| + if (!m_currentEntry || !frame->loader().stateMachine()->committedFirstRealDocumentLoad())
|
| return;
|
|
|
| - if (FrameView* view = m_frame->view()) {
|
| - if (m_frame->isMainFrame()) {
|
| - if (ScrollingCoordinator* scrollingCoordinator = m_frame->page()->scrollingCoordinator())
|
| + if (FrameView* view = frame->view()) {
|
| + if (frame->isMainFrame()) {
|
| + if (ScrollingCoordinator* scrollingCoordinator = m_page->scrollingCoordinator())
|
| scrollingCoordinator->frameViewRootLayerDidChange(view);
|
| }
|
|
|
| if (!view->wasScrolledByUser()) {
|
| - if (m_frame->isMainFrame() && m_currentItem->pageScaleFactor())
|
| - m_frame->page()->setPageScaleFactor(m_currentItem->pageScaleFactor(), m_currentItem->scrollPoint());
|
| + if (frame->isMainFrame() && m_currentEntry->root()->pageScaleFactor())
|
| + m_page->setPageScaleFactor(m_currentEntry->root()->pageScaleFactor(), m_currentEntry->root()->scrollPoint());
|
| else
|
| - view->setScrollPositionNonProgrammatically(m_currentItem->scrollPoint());
|
| + view->setScrollPositionNonProgrammatically(m_currentEntry->itemForFrame(frame)->scrollPoint());
|
| }
|
| }
|
| }
|
|
|
| -void HistoryController::updateBackForwardListForFragmentScroll()
|
| +void HistoryController::updateBackForwardListForFragmentScroll(Frame* frame)
|
| {
|
| - createNewBackForwardItem(false);
|
| + createNewBackForwardItem(frame, false);
|
| }
|
|
|
| -void HistoryController::saveDocumentAndScrollState()
|
| +void HistoryController::saveDocumentAndScrollState(Frame* frame)
|
| {
|
| - if (!m_currentItem)
|
| + if (!m_currentEntry || !m_currentEntry->itemForFrame(frame))
|
| return;
|
|
|
| - Document* document = m_frame->document();
|
| + Document* document = frame->document();
|
| ASSERT(document);
|
| + HistoryItem* item = m_currentEntry->itemForFrame(frame);
|
|
|
| - if (m_currentItem->isCurrentDocument(document) && document->isActive()) {
|
| - LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree().uniqueName().string().utf8().data(), m_currentItem.get());
|
| - m_currentItem->setDocumentState(document->formElementsState());
|
| - }
|
| + if (item->isCurrentDocument(document) && document->isActive())
|
| + item->setDocumentState(document->formElementsState());
|
|
|
| - if (!m_frame->view())
|
| + if (!frame->view())
|
| return;
|
|
|
| - m_currentItem->setScrollPoint(m_frame->view()->scrollPosition());
|
| -
|
| - Page* page = m_frame->page();
|
| - if (m_frame->isMainFrame() && !page->inspectorController().deviceEmulationEnabled())
|
| - m_currentItem->setPageScaleFactor(page->pageScaleFactor());
|
| + item->setScrollPoint(frame->view()->scrollPosition());
|
| + if (frame->isMainFrame() && !m_page->inspectorController().deviceEmulationEnabled())
|
| + item->setPageScaleFactor(m_page->pageScaleFactor());
|
| }
|
|
|
| -void HistoryController::restoreDocumentState()
|
| +void HistoryController::restoreDocumentState(Frame* frame)
|
| {
|
| - if (m_currentItem && m_frame->loader().loadType() == FrameLoadTypeBackForward)
|
| - m_frame->document()->setStateForNewFormElements(m_currentItem->documentState());
|
| + HistoryItem* item = m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0;
|
| + if (item && frame->loader().loadType() == FrameLoadTypeBackForward)
|
| + frame->document()->setStateForNewFormElements(item->documentState());
|
| }
|
|
|
| -bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem* targetItem) const
|
| +void HistoryController::goToEntry(PassOwnPtr<HistoryEntry> targetEntry)
|
| {
|
| - if (!m_currentItem)
|
| - return false;
|
| - // Don't abort the current load if we're navigating within the current document.
|
| - return !m_currentItem->shouldDoSameDocumentNavigationTo(targetItem);
|
| + ASSERT(m_sameDocumentLoadsInProgress.isEmpty());
|
| + ASSERT(m_differentDocumentLoadsInProgress.isEmpty());
|
| +
|
| + m_provisionalEntry = targetEntry;
|
| + recursiveGoToEntry(m_page->mainFrame());
|
| +
|
| + if (m_differentDocumentLoadsInProgress.isEmpty()) {
|
| + m_previousEntry = m_currentEntry.release();
|
| + m_currentEntry = m_provisionalEntry.release();
|
| + } else {
|
| + m_page->mainFrame()->loader().stopAllLoaders();
|
| + }
|
| +
|
| + for (HistoryFrameLoadSet::iterator it = m_sameDocumentLoadsInProgress.begin(); it != m_sameDocumentLoadsInProgress.end(); ++it)
|
| + it->key->loader().loadHistoryItem(it->value, HistorySameDocumentLoad);
|
| + for (HistoryFrameLoadSet::iterator it = m_differentDocumentLoadsInProgress.begin(); it != m_differentDocumentLoadsInProgress.end(); ++it)
|
| + it->key->loader().loadHistoryItem(it->value, HistoryDifferentDocumentLoad);
|
| + m_sameDocumentLoadsInProgress.clear();
|
| + m_differentDocumentLoadsInProgress.clear();
|
| }
|
|
|
| -// Main funnel for navigating to a previous location (back/forward, non-search snap-back)
|
| -// This includes recursion to handle loading into framesets properly
|
| -void HistoryController::goToItem(HistoryItem* targetItem)
|
| +void HistoryController::recursiveGoToEntry(Frame* frame)
|
| {
|
| - ASSERT(!m_frame->tree().parent());
|
| + HistoryItem* newItem = m_provisionalEntry->itemForFrame(frame);
|
| + HistoryItem* oldItem = m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0;
|
| + if (!newItem)
|
| + return;
|
|
|
| - // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
|
| - // <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
|
| - // Ultimately, history item navigations should go through the policy delegate. That's covered in:
|
| - // <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
|
| - Page* page = m_frame->page();
|
| - if (!page)
|
| + if (!oldItem || (newItem != oldItem && newItem->itemSequenceNumber() != oldItem->itemSequenceNumber())) {
|
| + if (oldItem && newItem->documentSequenceNumber() == oldItem->documentSequenceNumber())
|
| + m_sameDocumentLoadsInProgress.set(frame, newItem);
|
| + else
|
| + m_differentDocumentLoadsInProgress.set(frame, newItem);
|
| return;
|
| + }
|
| +
|
| + for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling())
|
| + recursiveGoToEntry(child);
|
| +}
|
| +
|
| +void HistoryController::goToItem(HistoryItem* targetItem)
|
| +{
|
| if (m_defersLoading) {
|
| m_deferredItem = targetItem;
|
| return;
|
| }
|
|
|
| - clearProvisionalItemsInAllFrames();
|
| -
|
| - // First set the provisional item of any frames that are not actually navigating.
|
| - // This must be done before trying to navigate the desired frame, because some
|
| - // navigations can commit immediately (such as about:blank). We must be sure that
|
| - // all frames have provisional items set before the commit.
|
| - recursiveSetProvisionalItem(targetItem, m_currentItem.get());
|
| - // Now that all other frames have provisional items, do the actual navigation.
|
| - recursiveGoToItem(targetItem, m_currentItem.get());
|
| + OwnPtr<HistoryEntry> newEntry = HistoryEntry::create(targetItem);
|
| + Deque<HistoryNode*> historyNodes;
|
| + historyNodes.append(newEntry->rootHistoryNode());
|
| + while (!historyNodes.isEmpty()) {
|
| + // For each item, read the children (if any) off the HistoryItem,
|
| + // create a new HistoryNode for each child and attach it,
|
| + // then clear the children on the HistoryItem.
|
| + HistoryNode* historyNode = historyNodes.takeFirst();
|
| + const HistoryItemVector& children = historyNode->value()->children();
|
| + for (size_t i = 0; i < children.size(); i++) {
|
| + HistoryNode* childHistoryNode = historyNode->addChild(children[i].get());
|
| + historyNodes.append(childHistoryNode);
|
| + }
|
| + historyNode->value()->clearChildren();
|
| + }
|
| + goToEntry(newEntry.release());
|
| }
|
|
|
| void HistoryController::setDefersLoading(bool defer)
|
| @@ -179,154 +281,125 @@ void HistoryController::setDefersLoading(bool defer)
|
| }
|
| }
|
|
|
| -void HistoryController::clearProvisionalItemsInAllFrames()
|
| -{
|
| - for (RefPtr<Frame> frame = m_frame->page()->mainFrame(); frame; frame = frame->tree().traverseNext())
|
| - frame->loader().history()->m_provisionalItem = 0;
|
| -}
|
| -
|
| // There are 2 things you might think of as "history", all of which are handled by these functions.
|
| //
|
| // 1) Back/forward: The m_currentItem is part of this mechanism.
|
| // 2) Global history: Handled by the client.
|
| //
|
| -void HistoryController::updateForStandardLoad()
|
| +void HistoryController::updateForStandardLoad(Frame* frame)
|
| {
|
| - LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", m_frame->loader().documentLoader()->url().string().ascii().data());
|
| - createNewBackForwardItem(true);
|
| + LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", frame->loader().documentLoader()->url().string().ascii().data());
|
| + createNewBackForwardItem(frame, true);
|
| }
|
|
|
| -void HistoryController::updateForInitialLoadInChildFrame()
|
| +void HistoryController::updateForInitialLoadInChildFrame(Frame* frame)
|
| {
|
| - Frame* parentFrame = m_frame->tree().parent();
|
| - if (parentFrame && parentFrame->loader().history()->m_currentItem)
|
| - parentFrame->loader().history()->m_currentItem->setChildItem(createItem());
|
| + ASSERT(frame->tree().parent());
|
| + if (!m_currentEntry)
|
| + return;
|
| + if (HistoryNode* existingChildHistoryNode = m_currentEntry->historyNodeForFrame(frame))
|
| + existingChildHistoryNode->updateValue(createItem(frame));
|
| + else if (HistoryNode* parentHistoryNode = m_currentEntry->historyNodeForFrame(frame->tree().parent()))
|
| + parentHistoryNode->addChild(createItem(frame));
|
| }
|
|
|
| -void HistoryController::updateForCommit()
|
| +void HistoryController::updateForCommit(Frame* frame)
|
| {
|
| - FrameLoader& frameLoader = m_frame->loader();
|
| #if !LOG_DISABLED
|
| - if (m_frame->document())
|
| - LOG(History, "WebCoreHistory: Updating History for commit in frame %s", m_frame->document()->title().utf8().data());
|
| + if (frame->document())
|
| + LOG(History, "WebCoreHistory: Updating History for commit in frame %s", frame->document()->title().utf8().data());
|
| #endif
|
| - FrameLoadType type = frameLoader.loadType();
|
| - if (isBackForwardLoadType(type)) {
|
| + FrameLoadType type = frame->loader().loadType();
|
| + if (isBackForwardLoadType(type) && m_provisionalEntry) {
|
| // Once committed, we want to use current item for saving DocState, and
|
| // the provisional item for restoring state.
|
| // Note previousItem must be set before we close the URL, which will
|
| // happen when the data source is made non-provisional below
|
| - m_previousItem = m_currentItem;
|
| - ASSERT(m_provisionalItem);
|
| - m_currentItem = m_provisionalItem;
|
| - m_provisionalItem = 0;
|
| -
|
| - // Tell all other frames in the tree to commit their provisional items and
|
| - // restore their scroll position. We'll avoid this frame (which has already
|
| - // committed) and its children (which will be replaced).
|
| - Page* page = m_frame->page();
|
| - ASSERT(page);
|
| - page->mainFrame()->loader().history()->recursiveUpdateForCommit();
|
| + m_previousEntry = m_currentEntry.release();
|
| + ASSERT(m_provisionalEntry);
|
| + m_currentEntry = m_provisionalEntry.release();
|
| } else if (type != FrameLoadTypeRedirectWithLockedBackForwardList) {
|
| - m_provisionalItem = 0;
|
| + m_provisionalEntry.clear();
|
| }
|
|
|
| if (type == FrameLoadTypeStandard)
|
| - updateForStandardLoad();
|
| + updateForStandardLoad(frame);
|
| else if (type == FrameLoadTypeInitialInChildFrame)
|
| - updateForInitialLoadInChildFrame();
|
| + updateForInitialLoadInChildFrame(frame);
|
| else
|
| - updateWithoutCreatingNewBackForwardItem();
|
| + updateWithoutCreatingNewBackForwardItem(frame);
|
| }
|
|
|
| -void HistoryController::recursiveUpdateForCommit()
|
| +void HistoryController::updateForSameDocumentNavigation(Frame* frame)
|
| {
|
| - // The frame that navigated will now have a null provisional item.
|
| - // Ignore it and its children.
|
| - if (!m_provisionalItem)
|
| + if (frame->document()->url().isEmpty())
|
| return;
|
| -
|
| - // For each frame that already had the content the item requested (based on
|
| - // (a matching URL and frame tree snapshot), just restore the scroll position.
|
| - // Save form state
|
| - if (m_currentItem && itemsAreClones(m_currentItem.get(), m_provisionalItem.get())) {
|
| - if (FrameView* view = m_frame->view())
|
| - view->setWasScrolledByUser(false);
|
| -
|
| - // Now commit the provisional item
|
| - m_previousItem = m_currentItem;
|
| - m_currentItem = m_provisionalItem;
|
| - m_provisionalItem = 0;
|
| -
|
| - // Restore the scroll position (we choose to do this rather than going back to the anchor point)
|
| - restoreScrollPositionAndViewState();
|
| - }
|
| -
|
| - // Iterate over the rest of the tree
|
| - for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling())
|
| - child->loader().history()->recursiveUpdateForCommit();
|
| + if (HistoryItem* item = m_currentEntry->itemForFrame(frame))
|
| + item->setURL(frame->document()->url());
|
| }
|
|
|
| -void HistoryController::updateForSameDocumentNavigation()
|
| +static PassRefPtr<HistoryItem> itemForExport(HistoryNode* historyNode)
|
| {
|
| - if (m_frame->document()->url().isEmpty())
|
| - return;
|
| -
|
| - Page* page = m_frame->page();
|
| - if (!page)
|
| - return;
|
| -
|
| - page->mainFrame()->loader().history()->recursiveUpdateForSameDocumentNavigation();
|
| -
|
| - if (m_currentItem)
|
| - m_currentItem->setURL(m_frame->document()->url());
|
| + RefPtr<HistoryItem> item = historyNode->value()->copy();
|
| + const Vector<OwnPtr<HistoryNode> >& childEntries = historyNode->children();
|
| + for (size_t i = 0; i < childEntries.size(); i++)
|
| + item->addChildItem(itemForExport(childEntries[i].get()));
|
| + return item;
|
| }
|
|
|
| -void HistoryController::recursiveUpdateForSameDocumentNavigation()
|
| +PassRefPtr<HistoryItem> HistoryController::currentItemForExport(Frame* frame)
|
| {
|
| - // The frame that navigated will now have a null provisional item.
|
| - // Ignore it and its children.
|
| - if (!m_provisionalItem)
|
| - return;
|
| -
|
| - // The provisional item may represent a different pending navigation.
|
| - // Don't commit it if it isn't a same document navigation.
|
| - if (m_currentItem && !m_currentItem->shouldDoSameDocumentNavigationTo(m_provisionalItem.get()))
|
| - return;
|
| + if (!m_currentEntry)
|
| + return 0;
|
| + HistoryNode* historyNode = m_currentEntry->historyNodeForFrame(frame);
|
| + return historyNode ? itemForExport(historyNode) : 0;
|
| +}
|
|
|
| - // Commit the provisional item.
|
| - m_previousItem = m_currentItem;
|
| - m_currentItem = m_provisionalItem;
|
| - m_provisionalItem = 0;
|
| +PassRefPtr<HistoryItem> HistoryController::previousItemForExport(Frame* frame)
|
| +{
|
| + if (!m_previousEntry)
|
| + return 0;
|
| + HistoryNode* historyNode = m_previousEntry->historyNodeForFrame(frame);
|
| + return historyNode ? itemForExport(historyNode) : 0;
|
| +}
|
|
|
| - // Iterate over the rest of the tree.
|
| - for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling())
|
| - child->loader().history()->recursiveUpdateForSameDocumentNavigation();
|
| +PassRefPtr<HistoryItem> HistoryController::provisionalItemForExport(Frame* frame)
|
| +{
|
| + if (!m_provisionalEntry)
|
| + return 0;
|
| + HistoryNode* historyNode = m_provisionalEntry->historyNodeForFrame(frame);
|
| + return historyNode ? itemForExport(historyNode) : 0;
|
| }
|
|
|
| -void HistoryController::setCurrentItem(HistoryItem* item)
|
| +HistoryItem* HistoryController::currentItem(Frame* frame) const
|
| {
|
| - m_previousItem = m_currentItem;
|
| - m_currentItem = item;
|
| + return m_currentEntry ? m_currentEntry->itemForFrame(frame) : 0;
|
| }
|
|
|
| -bool HistoryController::currentItemShouldBeReplaced() const
|
| +bool HistoryController::currentItemShouldBeReplaced(Frame* frame) const
|
| {
|
| // From the HTML5 spec for location.assign():
|
| // "If the browsing context's session history contains only one Document,
|
| // and that was the about:blank Document created when the browsing context
|
| // was created, then the navigation must be done with replacement enabled."
|
| - return m_currentItem && !m_previousItem && equalIgnoringCase(m_currentItem->urlString(), blankURL());
|
| + if (m_previousEntry && m_previousEntry->itemForFrame(frame))
|
| + return false;
|
| + return equalIgnoringCase(frame->document()->url(), blankURL());
|
| }
|
|
|
| -void HistoryController::setProvisionalItem(HistoryItem* item)
|
| +HistoryItem* HistoryController::previousItem(Frame* frame) const
|
| {
|
| - m_provisionalItem = item;
|
| + return m_previousEntry ? m_previousEntry->itemForFrame(frame) : 0;
|
| }
|
|
|
| -void HistoryController::initializeItem(HistoryItem* item)
|
| +void HistoryController::clearProvisionalEntry()
|
| {
|
| - DocumentLoader* documentLoader = m_frame->loader().documentLoader();
|
| + m_provisionalEntry.clear();
|
| +}
|
| +
|
| +void HistoryController::initializeItem(HistoryItem* item, Frame* frame)
|
| +{
|
| + DocumentLoader* documentLoader = frame->loader().documentLoader();
|
| ASSERT(documentLoader);
|
|
|
| KURL unreachableURL = documentLoader->unreachableURL();
|
| @@ -352,213 +425,98 @@ void HistoryController::initializeItem(HistoryItem* item)
|
| if (originalURL.isEmpty())
|
| originalURL = blankURL();
|
|
|
| - Frame* parentFrame = m_frame->tree().parent();
|
| - String parent = parentFrame ? parentFrame->tree().uniqueName() : "";
|
| -
|
| item->setURL(url);
|
| - item->setTarget(m_frame->tree().uniqueName());
|
| - item->setTargetFrameID(m_frame->frameID());
|
| + item->setTarget(frame->tree().uniqueName());
|
| + item->setTargetFrameID(frame->frameID());
|
| item->setOriginalURLString(originalURL.string());
|
|
|
| // Save form state if this is a POST
|
| item->setFormInfoFromRequest(documentLoader->request());
|
| }
|
|
|
| -PassRefPtr<HistoryItem> HistoryController::createItem()
|
| +PassRefPtr<HistoryItem> HistoryController::createItem(Frame* frame)
|
| {
|
| RefPtr<HistoryItem> item = HistoryItem::create();
|
| - initializeItem(item.get());
|
| -
|
| - // Set the item for which we will save document state
|
| - m_previousItem = m_currentItem;
|
| - m_currentItem = item;
|
| -
|
| + initializeItem(item.get(), frame);
|
| return item.release();
|
| }
|
|
|
| -PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame* targetFrame, bool clipAtTarget)
|
| +void HistoryController::createItemTree(Frame* targetFrame, bool clipAtTarget)
|
| {
|
| - RefPtr<HistoryItem> bfItem = createItem();
|
| -
|
| - if (!clipAtTarget || m_frame != targetFrame) {
|
| - // clipAtTarget is false for navigations within the same document, so
|
| - // we should copy the documentSequenceNumber over to the newly create
|
| - // item. Non-target items are just clones, and they should therefore
|
| - // preserve the same itemSequenceNumber.
|
| - if (m_previousItem) {
|
| - if (m_frame != targetFrame)
|
| - bfItem->setItemSequenceNumber(m_previousItem->itemSequenceNumber());
|
| - bfItem->setDocumentSequenceNumber(m_previousItem->documentSequenceNumber());
|
| - }
|
| -
|
| - for (Frame* child = m_frame->tree().firstChild(); child; child = child->tree().nextSibling()) {
|
| - // If the child is a frame corresponding to an <object> element that never loaded,
|
| - // we don't want to create a history item, because that causes fallback content
|
| - // to be ignored on reload.
|
| - FrameLoader& childLoader = child->loader();
|
| - if (childLoader.stateMachine()->startedFirstRealLoad() || !child->ownerElement()->isObjectElement())
|
| - bfItem->addChildItem(childLoader.history()->createItemTree(targetFrame, clipAtTarget));
|
| - }
|
| - }
|
| - return bfItem;
|
| -}
|
| -
|
| -// The general idea here is to traverse the frame tree and the item tree in parallel,
|
| -// tracking whether each frame already has the content the item requests. If there is
|
| -// a match, we set the provisional item and recurse. Otherwise we will reload that
|
| -// frame and all its kids in recursiveGoToItem.
|
| -void HistoryController::recursiveSetProvisionalItem(HistoryItem* item, HistoryItem* fromItem)
|
| -{
|
| - ASSERT(item);
|
| -
|
| - if (itemsAreClones(item, fromItem)) {
|
| - // Set provisional item, which will be committed in recursiveUpdateForCommit.
|
| - m_provisionalItem = item;
|
| -
|
| - const HistoryItemVector& childItems = item->children();
|
| -
|
| - int size = childItems.size();
|
| -
|
| - for (int i = 0; i < size; ++i) {
|
| - String childFrameName = childItems[i]->target();
|
| - HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName);
|
| - ASSERT(fromChildItem);
|
| - Frame* childFrame = m_frame->tree().child(childFrameName);
|
| - ASSERT(childFrame);
|
| - childFrame->loader().history()->recursiveSetProvisionalItem(childItems[i].get(), fromChildItem);
|
| - }
|
| - }
|
| -}
|
| -
|
| -// We now traverse the frame tree and item tree a second time, loading frames that
|
| -// do have the content the item requests.
|
| -void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem)
|
| -{
|
| - ASSERT(item);
|
| -
|
| - if (itemsAreClones(item, fromItem)) {
|
| - // Just iterate over the rest, looking for frames to navigate.
|
| - const HistoryItemVector& childItems = item->children();
|
| -
|
| - int size = childItems.size();
|
| - for (int i = 0; i < size; ++i) {
|
| - String childFrameName = childItems[i]->target();
|
| - HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName);
|
| - ASSERT(fromChildItem);
|
| - Frame* childFrame = m_frame->tree().child(childFrameName);
|
| - ASSERT(childFrame);
|
| - childFrame->loader().history()->recursiveGoToItem(childItems[i].get(), fromChildItem);
|
| - }
|
| + RefPtr<HistoryItem> newItem = createItem(targetFrame);
|
| + if (!m_currentEntry) {
|
| + m_currentEntry = HistoryEntry::create(newItem.get());
|
| } else {
|
| - m_frame->loader().loadHistoryItem(item);
|
| + HistoryItem* oldItem = m_currentEntry->itemForFrame(targetFrame);
|
| + if (!clipAtTarget)
|
| + newItem->setDocumentSequenceNumber(oldItem->documentSequenceNumber());
|
| + m_previousEntry = m_currentEntry.release();
|
| + m_currentEntry = m_previousEntry->cloneAndReplace(newItem.get(), oldItem, clipAtTarget, m_page);
|
| }
|
| }
|
|
|
| -bool HistoryController::itemsAreClones(HistoryItem* item1, HistoryItem* item2) const
|
| -{
|
| - // If the item we're going to is a clone of the item we're at, then we do
|
| - // not need to load it again. The current frame tree and the frame tree
|
| - // snapshot in the item have to match.
|
| - // Note: Some clients treat a navigation to the current history item as
|
| - // a reload. Thus, if item1 and item2 are the same, we need to create a
|
| - // new document and should not consider them clones.
|
| - // (See http://webkit.org/b/35532 for details.)
|
| - return item1
|
| - && item2
|
| - && item1 != item2
|
| - && item1->itemSequenceNumber() == item2->itemSequenceNumber()
|
| - && currentFramesMatchItem(item1)
|
| - && item2->hasSameFrames(item1);
|
| -}
|
| -
|
| -// Helper method that determines whether the current frame tree matches given history item's.
|
| -bool HistoryController::currentFramesMatchItem(HistoryItem* item) const
|
| -{
|
| - if ((!m_frame->tree().uniqueName().isEmpty() || !item->target().isEmpty()) && m_frame->tree().uniqueName() != item->target())
|
| - return false;
|
| -
|
| - const HistoryItemVector& childItems = item->children();
|
| - if (childItems.size() != m_frame->tree().childCount())
|
| - return false;
|
| -
|
| - unsigned size = childItems.size();
|
| - for (unsigned i = 0; i < size; ++i) {
|
| - if (!m_frame->tree().child(childItems[i]->target()))
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void HistoryController::createNewBackForwardItem(bool doClip)
|
| +void HistoryController::createNewBackForwardItem(Frame* frame, bool doClip)
|
| {
|
| // In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.
|
| // The item that was the target of the user's navigation is designated as the "targetItem".
|
| // When this function is called with doClip=true we're able to create the whole tree except for the target's children,
|
| // which will be loaded in the future. That part of the tree will be filled out as the child loads are committed.
|
| -
|
| - Page* page = m_frame->page();
|
| - if (!page)
|
| + if (!frame->loader().documentLoader()->isURLValidForNewHistoryEntry())
|
| return;
|
| -
|
| - if (!m_frame->loader().documentLoader()->isURLValidForNewHistoryEntry())
|
| - return;
|
| -
|
| - Frame* mainFrame = page->mainFrame();
|
| - ASSERT(mainFrame);
|
| -
|
| - RefPtr<HistoryItem> topItem = mainFrame->loader().history()->createItemTree(m_frame, doClip);
|
| - LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", topItem.get(), m_frame->loader().documentLoader()->url().string().ascii().data());
|
| + createItemTree(frame, doClip);
|
| }
|
|
|
| -void HistoryController::updateWithoutCreatingNewBackForwardItem()
|
| +void HistoryController::updateWithoutCreatingNewBackForwardItem(Frame* frame)
|
| {
|
| - if (!m_currentItem)
|
| + if (!m_currentEntry || !m_currentEntry->itemForFrame(frame))
|
| return;
|
|
|
| - DocumentLoader* documentLoader = m_frame->loader().documentLoader();
|
| + DocumentLoader* documentLoader = frame->loader().documentLoader();
|
|
|
| if (!documentLoader->unreachableURL().isEmpty())
|
| return;
|
|
|
| - if (m_currentItem->url() != documentLoader->url()) {
|
| - m_currentItem->reset();
|
| - initializeItem(m_currentItem.get());
|
| + HistoryItem* item = m_currentEntry->itemForFrame(frame);
|
| + if (item->url() != documentLoader->url()) {
|
| + item->reset();
|
| + initializeItem(item, frame);
|
| } else {
|
| // Even if the final URL didn't change, the form data may have changed.
|
| - m_currentItem->setFormInfoFromRequest(documentLoader->request());
|
| + item->setFormInfoFromRequest(documentLoader->request());
|
| }
|
| }
|
|
|
| -void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject, const String& urlString)
|
| +void HistoryController::pushState(Frame* frame, PassRefPtr<SerializedScriptValue> stateObject, const String& urlString)
|
| {
|
| - if (!m_currentItem)
|
| + if (!m_currentEntry)
|
| return;
|
|
|
| - Page* page = m_frame->page();
|
| - ASSERT(page);
|
| -
|
| - // Get a HistoryItem tree for the current frame tree.
|
| - RefPtr<HistoryItem> topItem = page->mainFrame()->loader().history()->createItemTree(m_frame, false);
|
| -
|
| - // Override data in the current item (created by createItemTree) to reflect
|
| + // Get a HistoryItem tree for the current frame tree, then override data to reflect
|
| // the pushState() arguments.
|
| - m_currentItem->setStateObject(stateObject);
|
| - m_currentItem->setURLString(urlString);
|
| + createItemTree(frame, false);
|
| + HistoryItem* item = m_currentEntry->itemForFrame(frame);
|
| + if (!item) {
|
| + updateForInitialLoadInChildFrame(frame);
|
| + item = m_currentEntry->itemForFrame(frame);
|
| + }
|
| + item->setStateObject(stateObject);
|
| + item->setURLString(urlString);
|
| }
|
|
|
| -void HistoryController::replaceState(PassRefPtr<SerializedScriptValue> stateObject, const String& urlString)
|
| +void HistoryController::replaceState(Frame* frame, PassRefPtr<SerializedScriptValue> stateObject, const String& urlString)
|
| {
|
| - if (!m_currentItem)
|
| + if (!m_currentEntry)
|
| return;
|
|
|
| - if (!urlString.isEmpty())
|
| - m_currentItem->setURLString(urlString);
|
| - m_currentItem->setStateObject(stateObject);
|
| - m_currentItem->setFormData(0);
|
| - m_currentItem->setFormContentType(String());
|
| + HistoryItem* item = m_currentEntry->itemForFrame(frame);
|
| + if (!item)
|
| + return;
|
|
|
| - ASSERT(m_frame->page());
|
| + if (!urlString.isEmpty())
|
| + item->setURLString(urlString);
|
| + item->setStateObject(stateObject);
|
| + item->setFormData(0);
|
| + item->setFormContentType(String());
|
| }
|
|
|
| } // namespace WebCore
|
|
|