blob: 2bd3bc8fdd794827427c8bf722ee463bf091a0b7 [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
*
* 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.
*/
#ifndef HistoryController_h
#define HistoryController_h
#include "core/history/HistoryItem.h"
#include "core/loader/FrameLoaderTypes.h"
#include "wtf/HashMap.h"
#include "wtf/Noncopyable.h"
#include "wtf/RefPtr.h"
#include "wtf/text/WTFString.h"
namespace WebCore {
class Frame;
class HistoryEntry;
class Page;
// A guide to history state in Blink:
//
// HistoryController: Owned by Page, is the entry point for interacting with history.
// Handles most of the operations to modify history state, navigate to an existing
// back/forward entry, etc.
// HistoryEntry: Represents a single entry in the back/forward list, encapsulating
// all frames in the page it represents. It provides access to each frame's
// state via lookups by frame id or frame name.
// HistoryNode: Represents a single frame in a HistoryEntry. Owned by a HistoryEntry. HistoryNodes
// form a tree that mirrors the FrameTree in the corresponding page. HistoryNodes represent
// the structure of the page, but don't hold any per-frame state except a list of child frames.
// HistoryItem (lives in a separate file): The state for a given frame. Can persist across
// navigations. HistoryItem is reference counted, and each HistoryNode holds a reference
// to its single corresponding HistoryItem. Can be referenced by multiple HistoryNodes and
// can therefore exist in multiple HistoryEntry instances.
//
// Suppose we have the following page, foo.com, which embeds foo.com/a in an iframe:
//
// HistoryEntry 0:
// HistoryNode 0_0 (HistoryItem A (url: foo.com))
// HistoryNode 0_1: (HistoryItem B (url: foo.com/a))
//
// Now we navigation the top frame to bar.com, which embeds bar.com/b and bar.com/c in iframes,
// and bar.com/b in turn embeds bar.com/d. We will create a new HistoryEntry with a tree
// containing 4 new HistoryNodes. The state will be:
//
// HistoryEntry 1:
// HistoryNode 1_0 (HistoryItem C (url: bar.com))
// HistoryNode 1_1: (HistoryItem D (url: bar.com/b))
// HistoryNode 1_3: (HistoryItem F (url: bar.com/d))
// HistoryNode 1_2: (HistoryItem E (url: bar.com/c))
//
//
// Finally, we navigate the first subframe from bar.com/b to bar.com/e, which embeds bar.com/f.
// We will create a new HistoryEntry and new HistoryNode for each frame. Any frame that
// navigates (bar.com/e and its child, bar.com/f) will receive a new HistoryItem. However,
// 2 frames were not navigated (bar.com and bar.com/c), so those two frames will reuse the
// existing HistoryItem:
//
// HistoryEntry 2:
// HistoryNode 2_0 (HistoryItem C (url: bar.com)) *REUSED*
// HistoryNode 2_1: (HistoryItem G (url: bar.com/e))
// HistoryNode 2_3: (HistoryItem H (url: bar.com/f))
// HistoryNode 2_2: (HistoryItem E (url: bar.com/c)) *REUSED*
//
class HistoryNode {
public:
static PassOwnPtr<HistoryNode> create(HistoryEntry*, HistoryItem*);
~HistoryNode() { }
HistoryNode* addChild(PassRefPtr<HistoryItem>);
PassOwnPtr<HistoryNode> cloneAndReplace(HistoryEntry*, HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Frame* currentFrame);
HistoryItem* value() { return m_value.get(); }
void updateValue(PassRefPtr<HistoryItem> item) { m_value = item; }
const Vector<OwnPtr<HistoryNode> >& children() const { return m_children; }
void removeChildren();
private:
HistoryNode(HistoryEntry*, HistoryItem*);
HistoryEntry* m_entry;
Vector<OwnPtr<HistoryNode> > m_children;
RefPtr<HistoryItem> m_value;
};
class HistoryEntry {
public:
static PassOwnPtr<HistoryEntry> create(HistoryItem* root);
PassOwnPtr<HistoryEntry> cloneAndReplace(HistoryItem* newItem, bool clipAtTarget, Frame* targetFrame, Page*);
HistoryNode* historyNodeForFrame(Frame*);
HistoryItem* itemForFrame(Frame*);
HistoryItem* root() const { return m_root->value(); }
HistoryNode* rootHistoryNode() const { return m_root.get(); }
private:
friend class HistoryNode;
HistoryEntry() { }
explicit HistoryEntry(HistoryItem* root);
OwnPtr<HistoryNode> m_root;
HashMap<uint64_t, HistoryNode*> m_framesToItems;
HashMap<String, HistoryNode*> m_uniqueNamesToItems;
};
class HistoryController {
WTF_MAKE_NONCOPYABLE(HistoryController);
public:
explicit HistoryController(Page*);
~HistoryController();
// Should only be called by embedder. To request a back/forward
// navigation, call FrameLoaderClient::navigateBackForward().
void goToItem(HistoryItem*);
void updateBackForwardListForFragmentScroll(Frame*, HistoryItem*);
void updateForCommit(Frame*, HistoryItem*);
PassRefPtr<HistoryItem> currentItemForExport();
PassRefPtr<HistoryItem> previousItemForExport();
PassRefPtr<HistoryItem> provisionalItemForExport();
HistoryItem* itemForNewChildFrame(Frame*) const;
void removeChildrenForRedirect(Frame*);
bool inSameDocumentLoad() const { return !m_sameDocumentLoadsInProgress.isEmpty() && m_differentDocumentLoadsInProgress.isEmpty(); }
void setDefersLoading(bool);
private:
void goToEntry(PassOwnPtr<HistoryEntry>);
void recursiveGoToEntry(Frame*);
void updateForInitialLoadInChildFrame(Frame*, HistoryItem*);
void createNewBackForwardItem(Frame*, HistoryItem*, bool doClip);
Page* m_page;
OwnPtr<HistoryEntry> m_currentEntry;
OwnPtr<HistoryEntry> m_previousEntry;
OwnPtr<HistoryEntry> m_provisionalEntry;
typedef HashMap<Frame*, HistoryItem*> HistoryFrameLoadSet;
HistoryFrameLoadSet m_sameDocumentLoadsInProgress;
HistoryFrameLoadSet m_differentDocumentLoadsInProgress;
bool m_defersLoading;
RefPtr<HistoryItem> m_deferredItem;
};
} // namespace WebCore
#endif // HistoryController_h