blob: f7bc5297352d876a36b2312736d524f382a75571 [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008, 2014 Apple Inc. All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
#include "config.h"
#include "CachedPage.h"
#include "Document.h"
#include "Element.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameView.h"
#include "HistoryController.h"
#include "HistoryItem.h"
#include "Node.h"
#include "Page.h"
#include "PageTransitionEvent.h"
#include "ScriptDisallowedScope.h"
#include "SelectionRestorationMode.h"
#include "Settings.h"
#include "VisitedLinkState.h"
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/StdLibExtras.h>
#if PLATFORM(IOS_FAMILY)
#include "FrameSelection.h"
#endif
namespace WebCore {
using namespace JSC;
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedPageCounter, ("CachedPage"));
CachedPage::CachedPage(Page& page)
: m_page(page)
, m_expirationTime(MonotonicTime::now() + page.settings().backForwardCacheExpirationInterval())
, m_cachedMainFrame(makeUnique<CachedFrame>(page.mainFrame()))
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
, m_loadedSubresourceDomains(page.mainFrame().loader().client().loadedSubresourceDomains())
#endif
{
#ifndef NDEBUG
cachedPageCounter.increment();
#endif
}
CachedPage::~CachedPage()
{
#ifndef NDEBUG
cachedPageCounter.decrement();
#endif
if (m_cachedMainFrame)
m_cachedMainFrame->destroy();
}
static void firePageShowAndPopStateEvents(Page& page)
{
// Dispatching JavaScript events can cause frame destruction.
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;
// This takes care of firing the visibilitychange event and making sure the document is reported as visible.
document->setVisibilityHiddenDueToDismissal(false);
document->dispatchPageshowEvent(PageshowEventPersisted);
auto* historyItem = child->loader().history().currentItem();
if (historyItem && historyItem->stateObject())
document->dispatchPopstateEvent(historyItem->stateObject());
}
}
class CachedPageRestorationScope {
public:
CachedPageRestorationScope(Page& page)
: m_page(page)
{
m_page.setIsRestoringCachedPage(true);
}
~CachedPageRestorationScope()
{
m_page.setIsRestoringCachedPage(false);
}
private:
Page& m_page;
};
void CachedPage::restore(Page& page)
{
ASSERT(m_cachedMainFrame);
ASSERT(m_cachedMainFrame->view()->frame().isMainFrame());
ASSERT(!page.subframeCount());
CachedPageRestorationScope restorationScope(page);
m_cachedMainFrame->open();
// Restore the focus appearance for the focused element.
// FIXME: Right now we don't support pages w/ frames in the b/f cache. This may need to be tweaked when we add support for that.
RefPtr focusedDocument = CheckedRef(page.focusController())->focusedOrMainFrame().document();
if (RefPtr element = focusedDocument->focusedElement()) {
#if PLATFORM(IOS_FAMILY)
// We don't want focused nodes changing scroll position when restoring from the cache
// as it can cause ugly jumps before we manage to restore the cached position.
page.mainFrame().selection().suppressScrolling();
bool hadProhibitsScrolling = false;
FrameView* frameView = page.mainFrame().view();
if (frameView) {
hadProhibitsScrolling = frameView->prohibitsScrolling();
frameView->setProhibitsScrolling(true);
}
#endif
element->updateFocusAppearance(SelectionRestorationMode::RestoreOrSelectAll);
#if PLATFORM(IOS_FAMILY)
if (frameView)
frameView->setProhibitsScrolling(hadProhibitsScrolling);
page.mainFrame().selection().restoreScrolling();
#endif
}
if (m_needsDeviceOrPageScaleChanged)
page.mainFrame().deviceOrPageScaleFactorChanged();
page.setNeedsRecalcStyleInAllFrames();
#if ENABLE(VIDEO)
if (m_needsCaptionPreferencesChanged)
page.captionPreferencesChanged();
#endif
if (m_needsUpdateContentsSize) {
if (FrameView* frameView = page.mainFrame().view())
frameView->updateContentsSize();
}
firePageShowAndPopStateEvents(page);
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
for (auto& domain : m_loadedSubresourceDomains)
page.mainFrame().loader().client().didLoadFromRegistrableDomain(WTFMove(domain));
#endif
clear();
}
void CachedPage::clear()
{
ASSERT(m_cachedMainFrame);
m_cachedMainFrame->clear();
m_cachedMainFrame = nullptr;
#if ENABLE(VIDEO)
m_needsCaptionPreferencesChanged = false;
#endif
m_needsDeviceOrPageScaleChanged = false;
m_needsUpdateContentsSize = false;
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
m_loadedSubresourceDomains.clear();
#endif
}
bool CachedPage::hasExpired() const
{
return MonotonicTime::now() > m_expirationTime;
}
} // namespace WebCore