#include "config.h"
#include "CachedFrame.h"
#include "CSSAnimationController.h"
#include "CachedFramePlatformData.h"
#include "CachedPage.h"
#include "DOMWindow.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameView.h"
#include "Logging.h"
#include "MainFrame.h"
#include "Page.h"
#include "PageCache.h"
#include "SVGDocumentExtensions.h"
#include "ScriptController.h"
#include "SerializedScriptValue.h"
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/text/CString.h>
#if PLATFORM(IOS) || ENABLE(TOUCH_EVENTS)
#include "Chrome.h"
#include "ChromeClient.h"
#endif
namespace WebCore {
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedFrameCounter, ("CachedFrame"));
CachedFrameBase::CachedFrameBase(Frame& frame)
: m_document(frame.document())
, m_documentLoader(frame.loader().documentLoader())
, m_view(frame.view())
, m_url(frame.document()->url())
, m_isMainFrame(!frame.tree().parent())
{
}
CachedFrameBase::~CachedFrameBase()
{
#ifndef NDEBUG
cachedFrameCounter.decrement();
#endif
ASSERT(!m_document);
}
void CachedFrameBase::pruneDetachedChildFrames()
{
for (size_t i = m_childFrames.size(); i;) {
--i;
if (m_childFrames[i]->view()->frame().page())
continue;
m_childFrames[i]->destroy();
m_childFrames.remove(i);
}
}
void CachedFrameBase::restore()
{
ASSERT(m_document->view() == m_view);
if (m_isMainFrame)
m_view->setParentVisible(true);
Frame& frame = m_view->frame();
m_cachedFrameScriptData->restore(frame);
if (m_document->svgExtensions())
m_document->accessSVGExtensions().unpauseAnimations();
frame.animation().resumeAnimationsForDocument(m_document.get());
m_document->resume(ActiveDOMObject::PageCache);
frame.script().updatePlatformScriptObjects();
frame.loader().client().didRestoreFromPageCache();
pruneDetachedChildFrames();
for (auto& childFrame : m_childFrames) {
ASSERT(childFrame->view()->frame().page());
frame.tree().appendChild(childFrame->view()->frame());
childFrame->open();
ASSERT_WITH_SECURITY_IMPLICATION(m_document == frame.document());
}
#if PLATFORM(IOS)
if (m_isMainFrame) {
frame.loader().client().didRestoreFrameHierarchyForCachedFrame();
if (DOMWindow* domWindow = m_document->domWindow()) {
if (domWindow->scrollEventListenerCount() && frame.page())
frame.page()->chrome().client().setNeedsScrollNotifications(frame, true);
}
}
#endif
frame.view()->didRestoreFromPageCache();
}
CachedFrame::CachedFrame(Frame& frame)
: CachedFrameBase(frame)
{
#ifndef NDEBUG
cachedFrameCounter.increment();
#endif
ASSERT(m_document);
ASSERT(m_documentLoader);
ASSERT(m_view);
ASSERT(m_document->pageCacheState() == Document::InPageCache);
for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling())
m_childFrames.append(std::make_unique<CachedFrame>(*child));
m_document->suspend(ActiveDOMObject::PageCache);
m_cachedFrameScriptData = std::make_unique<ScriptCachedFrameData>(frame);
m_document->domWindow()->suspendForDocumentSuspension();
frame.loader().client().savePlatformDataToCachedFrame(this);
frame.clearTimers();
for (unsigned i = 0; i < m_childFrames.size(); ++i)
frame.tree().removeChild(m_childFrames[i]->view()->frame());
if (!m_isMainFrame)
frame.page()->decrementSubframeCount();
frame.loader().client().didSaveToPageCache();
#ifndef NDEBUG
if (m_isMainFrame)
LOG(PageCache, "Finished creating CachedFrame for main frame url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get());
else
LOG(PageCache, "Finished creating CachedFrame for child frame with url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get());
#endif
#if PLATFORM(IOS)
if (m_isMainFrame) {
if (DOMWindow* domWindow = m_document->domWindow()) {
if (domWindow->scrollEventListenerCount() && frame.page())
frame.page()->chrome().client().setNeedsScrollNotifications(frame, false);
}
}
#endif
m_document->detachFromCachedFrame(*this);
ASSERT_WITH_SECURITY_IMPLICATION(!m_documentLoader->isLoading());
}
void CachedFrame::open()
{
ASSERT(m_view);
ASSERT(m_document);
if (!m_isMainFrame)
m_view->frame().page()->incrementSubframeCount();
m_document->attachToCachedFrame(*this);
m_view->frame().loader().open(*this);
}
void CachedFrame::clear()
{
if (!m_document)
return;
ASSERT(m_document->pageCacheState() == Document::NotInPageCache);
ASSERT(m_view);
ASSERT(!m_document->frame() || m_document->frame() == &m_view->frame());
for (int i = m_childFrames.size() - 1; i >= 0; --i)
m_childFrames[i]->clear();
m_document = nullptr;
m_view = nullptr;
m_url = URL();
m_cachedFramePlatformData = nullptr;
m_cachedFrameScriptData = nullptr;
}
void CachedFrame::destroy()
{
if (!m_document)
return;
ASSERT(m_document->pageCacheState() == Document::InPageCache);
ASSERT(m_view);
ASSERT(!m_document->frame());
m_document->domWindow()->willDestroyCachedFrame();
if (!m_isMainFrame && m_view->frame().page()) {
m_view->frame().loader().detachViewsAndDocumentLoader();
m_view->frame().detachFromPage();
}
for (int i = m_childFrames.size() - 1; i >= 0; --i)
m_childFrames[i]->destroy();
if (m_cachedFramePlatformData)
m_cachedFramePlatformData->clear();
Frame::clearTimers(m_view.get(), m_document.get());
m_view->frame().animation().detachFromDocument(m_document.get());
m_document->removeAllEventListeners();
m_document->setPageCacheState(Document::NotInPageCache);
m_document->prepareForDestruction();
clear();
}
void CachedFrame::setCachedFramePlatformData(std::unique_ptr<CachedFramePlatformData> data)
{
m_cachedFramePlatformData = WTFMove(data);
}
CachedFramePlatformData* CachedFrame::cachedFramePlatformData()
{
return m_cachedFramePlatformData.get();
}
void CachedFrame::setHasInsecureContent(HasInsecureContent hasInsecureContent)
{
m_hasInsecureContent = hasInsecureContent;
}
int CachedFrame::descendantFrameCount() const
{
int count = m_childFrames.size();
for (size_t i = 0; i < m_childFrames.size(); ++i)
count += m_childFrames[i]->descendantFrameCount();
return count;
}
}