#include "config.h"
#include "CachedPage.h"
#include "Document.h"
#include "Element.h"
#include "FocusController.h"
#include "FrameLoader.h"
#include "FrameView.h"
#include "HistoryController.h"
#include "HistoryItem.h"
#include "MainFrame.h"
#include "NoEventDispatchAssertion.h"
#include "Node.h"
#include "Page.h"
#include "PageTransitionEvent.h"
#include "Settings.h"
#include "VisitedLinkState.h"
#include <wtf/CurrentTime.h>
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/StdLibExtras.h>
#if PLATFORM(IOS)
#include "FrameSelection.h"
#endif
using namespace JSC;
namespace WebCore {
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedPageCounter, ("CachedPage"));
CachedPage::CachedPage(Page& page)
: m_page(page)
, m_expirationTime(monotonicallyIncreasingTime() + page.settings().backForwardCacheExpirationInterval())
, m_cachedMainFrame(std::make_unique<CachedFrame>(page.mainFrame()))
{
#ifndef NDEBUG
cachedPageCounter.increment();
#endif
}
CachedPage::~CachedPage()
{
#ifndef NDEBUG
cachedPageCounter.decrement();
#endif
if (m_cachedMainFrame)
m_cachedMainFrame->destroy();
}
static void firePageShowAndPopStateEvents(Page& page)
{
auto& mainFrame = page.mainFrame();
Vector<Ref<Frame>> childFrames;
for (auto* child = mainFrame.tree().traverseNextInPostOrder(CanWrap::Yes); child; child = child->tree().traverseNextInPostOrder(CanWrap::No))
childFrames.append(*child);
for (auto& child : childFrames) {
if (!child->tree().isDescendantOf(&mainFrame))
continue;
auto* document = child->document();
if (!document)
continue;
document->dispatchPageshowEvent(PageshowEventPersisted);
auto* historyItem = child->loader().history().currentItem();
if (historyItem && historyItem->stateObject())
document->dispatchPopstateEvent(historyItem->stateObject());
}
}
void CachedPage::restore(Page& page)
{
ASSERT(m_cachedMainFrame);
ASSERT(m_cachedMainFrame->view()->frame().isMainFrame());
ASSERT(!page.subframeCount());
{
NoEventDispatchAssertion noEventDispatchAssertion;
m_cachedMainFrame->open();
}
Document* focusedDocument = page.focusController().focusedOrMainFrame().document();
if (Element* element = focusedDocument->focusedElement()) {
#if PLATFORM(IOS)
page.mainFrame().selection().suppressScrolling();
bool hadProhibitsScrolling = false;
FrameView* frameView = page.mainFrame().view();
if (frameView) {
hadProhibitsScrolling = frameView->prohibitsScrolling();
frameView->setProhibitsScrolling(true);
}
#endif
element->updateFocusAppearance(SelectionRestorationMode::Restore);
#if PLATFORM(IOS)
if (frameView)
frameView->setProhibitsScrolling(hadProhibitsScrolling);
page.mainFrame().selection().restoreScrolling();
#endif
}
if (m_needsDeviceOrPageScaleChanged)
page.mainFrame().deviceOrPageScaleFactorChanged();
page.setNeedsRecalcStyleInAllFrames();
#if ENABLE(VIDEO_TRACK)
if (m_needsCaptionPreferencesChanged)
page.captionPreferencesChanged();
#endif
if (m_needsUpdateContentsSize) {
if (FrameView* frameView = page.mainFrame().view())
frameView->updateContentsSize();
}
firePageShowAndPopStateEvents(page);
clear();
}
void CachedPage::clear()
{
ASSERT(m_cachedMainFrame);
m_cachedMainFrame->clear();
m_cachedMainFrame = nullptr;
#if ENABLE(VIDEO_TRACK)
m_needsCaptionPreferencesChanged = false;
#endif
m_needsDeviceOrPageScaleChanged = false;
m_needsUpdateContentsSize = false;
}
bool CachedPage::hasExpired() const
{
return monotonicallyIncreasingTime() > m_expirationTime;
}
}