#include "config.h"
#include "CachedFrame.h"
#include "BackForwardCache.h"
#include "CSSAnimationController.h"
#include "CachedFramePlatformData.h"
#include "CachedPage.h"
#include "CustomHeaderFields.h"
#include "DOMWindow.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "DocumentTimeline.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameView.h"
#include "Logging.h"
#include "NavigationDisabler.h"
#include "Page.h"
#include "RenderWidget.h"
#include "SVGDocumentExtensions.h"
#include "ScriptController.h"
#include "SerializedScriptValue.h"
#include "StyleTreeResolver.h"
#include "WindowEventLoop.h"
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/text/CString.h>
#if PLATFORM(IOS_FAMILY) || 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);
auto frame = makeRef(m_view->frame());
{
Style::PostResolutionCallbackDisabler disabler(*m_document);
WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
NavigationDisabler disableNavigation { nullptr };
m_cachedFrameScriptData->restore(frame.get());
if (m_document->svgExtensions())
m_document->accessSVGExtensions().unpauseAnimations();
m_document->resume(ReasonForSuspension::BackForwardCache);
frame->script().updatePlatformScriptObjects();
frame->loader().client().didRestoreFromBackForwardCache();
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_FAMILY)
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()->didRestoreFromBackForwardCache();
}
CachedFrame::CachedFrame(Frame& frame)
: CachedFrameBase(frame)
{
#ifndef NDEBUG
cachedFrameCounter.increment();
#endif
ASSERT(m_document);
ASSERT(m_documentLoader);
ASSERT(m_view);
ASSERT(m_document->backForwardCacheState() == Document::InBackForwardCache);
RELEASE_ASSERT(m_document->domWindow());
RELEASE_ASSERT(m_document->frame());
RELEASE_ASSERT(m_document->domWindow()->frame());
m_document->setMayBeDetachedFromFrame(false);
for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling())
m_childFrames.append(makeUnique<CachedFrame>(*child));
RELEASE_ASSERT(m_document->domWindow());
RELEASE_ASSERT(m_document->frame());
RELEASE_ASSERT(m_document->domWindow()->frame());
m_document->suspend(ReasonForSuspension::BackForwardCache);
m_cachedFrameScriptData = makeUnique<ScriptCachedFrameData>(frame);
m_document->domWindow()->suspendForBackForwardCache();
m_view->resetLayoutMilestones();
if (!frame.isMainFrame())
frame.loader().detachFromAllOpenedFrames();
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();
#ifndef NDEBUG
if (m_isMainFrame)
LOG(BackForwardCache, "Finished creating CachedFrame for main frame url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get());
else
LOG(BackForwardCache, "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_FAMILY)
if (m_isMainFrame) {
if (DOMWindow* domWindow = m_document->domWindow()) {
if (domWindow->scrollEventListenerCount() && frame.page())
frame.page()->chrome().client().setNeedsScrollNotifications(frame, false);
}
}
#endif
m_document->setMayBeDetachedFromFrame(true);
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_view->frame().loader().open(*this);
}
void CachedFrame::clear()
{
if (!m_document)
return;
ASSERT(m_document->backForwardCacheState() == Document::NotInBackForwardCache);
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->backForwardCacheState() == Document::InBackForwardCache);
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->setBackForwardCacheState(Document::NotInBackForwardCache);
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, UsedLegacyTLS usedLegacyTLS)
{
m_hasInsecureContent = hasInsecureContent;
m_usedLegacyTLS = usedLegacyTLS;
}
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;
}
}