diff options
Diffstat (limited to 'Source/WebCore/history')
-rw-r--r-- | Source/WebCore/history/BackForwardClient.h | 28 | ||||
-rw-r--r-- | Source/WebCore/history/BackForwardController.cpp | 19 | ||||
-rw-r--r-- | Source/WebCore/history/BackForwardController.h | 21 | ||||
-rw-r--r-- | Source/WebCore/history/BackForwardList.cpp | 300 | ||||
-rw-r--r-- | Source/WebCore/history/BackForwardList.h | 97 | ||||
-rw-r--r-- | Source/WebCore/history/CachedFrame.cpp | 144 | ||||
-rw-r--r-- | Source/WebCore/history/CachedFrame.h | 30 | ||||
-rw-r--r-- | Source/WebCore/history/CachedFramePlatformData.h | 8 | ||||
-rw-r--r-- | Source/WebCore/history/CachedPage.cpp | 103 | ||||
-rw-r--r-- | Source/WebCore/history/CachedPage.h | 36 | ||||
-rw-r--r-- | Source/WebCore/history/HistoryItem.cpp | 474 | ||||
-rw-r--r-- | Source/WebCore/history/HistoryItem.h | 220 | ||||
-rw-r--r-- | Source/WebCore/history/PageCache.cpp | 703 | ||||
-rw-r--r-- | Source/WebCore/history/PageCache.h | 110 |
14 files changed, 724 insertions, 1569 deletions
diff --git a/Source/WebCore/history/BackForwardClient.h b/Source/WebCore/history/BackForwardClient.h index 8ba55e578..f1bafdecc 100644 --- a/Source/WebCore/history/BackForwardClient.h +++ b/Source/WebCore/history/BackForwardClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2010, 2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2009 Google, Inc. All rights reserved. * @@ -12,10 +12,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -25,8 +25,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BackForwardClient_h -#define BackForwardClient_h +#pragma once #include <wtf/Forward.h> #include <wtf/RefCounted.h> @@ -41,7 +40,7 @@ public: { } - virtual void addItem(PassRefPtr<HistoryItem>) = 0; + virtual void addItem(Ref<HistoryItem>&&) = 0; virtual void goToItem(HistoryItem*) = 0; @@ -50,23 +49,6 @@ public: virtual int forwardListCount() = 0; virtual void close() = 0; - -#if PLATFORM(IOS) - // FIXME: These methods seem to violate the encapsulation of this class. - virtual unsigned current() = 0; - virtual void setCurrent(unsigned newCurrent) = 0; - - // FIXME: Consider renaming this method once we upstream the iOS changes to WebView.mm. - virtual bool clearAllPageCaches() = 0; -#endif - - // FIXME: Delete these once all callers are using BackForwardController - // instead of calling this directly. - HistoryItem* backItem() { return itemAtIndex(-1); } - HistoryItem* currentItem() { return itemAtIndex(0); } - HistoryItem* forwardItem() { return itemAtIndex(1); } }; } // namespace WebCore - -#endif // BackForwardClient_h diff --git a/Source/WebCore/history/BackForwardController.cpp b/Source/WebCore/history/BackForwardController.cpp index 2d1d02009..1bc8b5a5f 100644 --- a/Source/WebCore/history/BackForwardController.cpp +++ b/Source/WebCore/history/BackForwardController.cpp @@ -26,18 +26,15 @@ #include "config.h" #include "BackForwardController.h" -#include "BackForwardList.h" -#include "HistoryItem.h" +#include "BackForwardClient.h" #include "Page.h" namespace WebCore { -BackForwardController::BackForwardController(Page& page, PassRefPtr<BackForwardClient> client) +BackForwardController::BackForwardController(Page& page, Ref<BackForwardClient>&& client) : m_page(page) - , m_client(client) + , m_client(WTFMove(client)) { - if (!m_client) - m_client = BackForwardList::create(&page); } BackForwardController::~BackForwardController() @@ -74,7 +71,7 @@ void BackForwardController::goBackOrForward(int distance) if (!item) return; - m_page.goToItem(item, FrameLoadTypeIndexedBackForward); + m_page.goToItem(*item, FrameLoadType::IndexedBackForward); } bool BackForwardController::goBack() @@ -83,7 +80,7 @@ bool BackForwardController::goBack() if (!item) return false; - m_page.goToItem(item, FrameLoadTypeBack); + m_page.goToItem(*item, FrameLoadType::Back); return true; } @@ -93,13 +90,13 @@ bool BackForwardController::goForward() if (!item) return false; - m_page.goToItem(item, FrameLoadTypeForward); + m_page.goToItem(*item, FrameLoadType::Forward); return true; } -void BackForwardController::addItem(PassRefPtr<HistoryItem> item) +void BackForwardController::addItem(Ref<HistoryItem>&& item) { - m_client->addItem(item); + m_client->addItem(WTFMove(item)); } void BackForwardController::setCurrentItem(HistoryItem* item) diff --git a/Source/WebCore/history/BackForwardController.h b/Source/WebCore/history/BackForwardController.h index 638774643..60693d788 100644 --- a/Source/WebCore/history/BackForwardController.h +++ b/Source/WebCore/history/BackForwardController.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BackForwardController_h -#define BackForwardController_h +#pragma once #include <wtf/Noncopyable.h> #include <wtf/Forward.h> @@ -39,25 +38,25 @@ class Page; class BackForwardController { WTF_MAKE_NONCOPYABLE(BackForwardController); WTF_MAKE_FAST_ALLOCATED; public: - BackForwardController(Page&, PassRefPtr<BackForwardClient>); + BackForwardController(Page&, Ref<BackForwardClient>&&); ~BackForwardController(); BackForwardClient* client() const { return m_client.get(); } - bool canGoBackOrForward(int distance) const; + WEBCORE_EXPORT bool canGoBackOrForward(int distance) const; void goBackOrForward(int distance); - bool goBack(); - bool goForward(); + WEBCORE_EXPORT bool goBack(); + WEBCORE_EXPORT bool goForward(); - void addItem(PassRefPtr<HistoryItem>); + void addItem(Ref<HistoryItem>&&); void setCurrentItem(HistoryItem*); int count() const; - int backCount() const; - int forwardCount() const; + WEBCORE_EXPORT int backCount() const; + WEBCORE_EXPORT int forwardCount() const; - HistoryItem* itemAtIndex(int); + WEBCORE_EXPORT HistoryItem* itemAtIndex(int); void close(); @@ -71,5 +70,3 @@ private: }; } // namespace WebCore - -#endif // BackForwardController_h diff --git a/Source/WebCore/history/BackForwardList.cpp b/Source/WebCore/history/BackForwardList.cpp deleted file mode 100644 index b031ef16d..000000000 --- a/Source/WebCore/history/BackForwardList.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2008 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 COMPUTER, 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 "BackForwardList.h" - -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameLoaderClient.h" -#include "HistoryItem.h" -#include "Logging.h" -#include "Page.h" -#include "PageCache.h" -#include "SerializedScriptValue.h" - -namespace WebCore { - -static const unsigned DefaultCapacity = 100; -static const unsigned NoCurrentItemIndex = UINT_MAX; - -BackForwardList::BackForwardList(Page* page) - : m_page(page) - , m_current(NoCurrentItemIndex) - , m_capacity(DefaultCapacity) - , m_closed(true) - , m_enabled(true) -{ -} - -BackForwardList::~BackForwardList() -{ - ASSERT(m_closed); -} - -void BackForwardList::addItem(PassRefPtr<HistoryItem> prpItem) -{ - ASSERT(prpItem); - if (m_capacity == 0 || !m_enabled) - return; - - // Toss anything in the forward list - if (m_current != NoCurrentItemIndex) { - unsigned targetSize = m_current + 1; - while (m_entries.size() > targetSize) { - RefPtr<HistoryItem> item = m_entries.last(); - m_entries.removeLast(); - m_entryHash.remove(item); - pageCache()->remove(item.get()); - } - } - - // Toss the first item if the list is getting too big, as long as we're not using it - // (or even if we are, if we only want 1 entry). - if (m_entries.size() == m_capacity && (m_current != 0 || m_capacity == 1)) { - RefPtr<HistoryItem> item = m_entries[0]; - m_entries.remove(0); - m_entryHash.remove(item); - pageCache()->remove(item.get()); - m_current--; - } - - m_entryHash.add(prpItem.get()); - m_entries.insert(m_current + 1, prpItem); - m_current++; -} - -void BackForwardList::goBack() -{ - ASSERT(m_current > 0); - if (m_current > 0) { - m_current--; - } -} - -void BackForwardList::goForward() -{ - ASSERT(m_current < m_entries.size() - 1); - if (m_current < m_entries.size() - 1) { - m_current++; - } -} - -void BackForwardList::goToItem(HistoryItem* item) -{ - if (!m_entries.size() || !item) - return; - - unsigned int index = 0; - for (; index < m_entries.size(); ++index) - if (m_entries[index] == item) - break; - if (index < m_entries.size()) { - m_current = index; - } -} - -HistoryItem* BackForwardList::backItem() -{ - if (m_current && m_current != NoCurrentItemIndex) - return m_entries[m_current - 1].get(); - return 0; -} - -HistoryItem* BackForwardList::currentItem() -{ - if (m_current != NoCurrentItemIndex) - return m_entries[m_current].get(); - return 0; -} - -HistoryItem* BackForwardList::forwardItem() -{ - if (m_entries.size() && m_current < m_entries.size() - 1) - return m_entries[m_current + 1].get(); - return 0; -} - -void BackForwardList::backListWithLimit(int limit, HistoryItemVector& list) -{ - list.clear(); - if (m_current != NoCurrentItemIndex) { - unsigned first = std::max((int)m_current - limit, 0); - for (; first < m_current; ++first) - list.append(m_entries[first]); - } -} - -void BackForwardList::forwardListWithLimit(int limit, HistoryItemVector& list) -{ - ASSERT(limit > -1); - list.clear(); - if (!m_entries.size()) - return; - - unsigned lastEntry = m_entries.size() - 1; - if (m_current < lastEntry) { - int last = std::min(m_current + limit, lastEntry); - limit = m_current + 1; - for (; limit <= last; ++limit) - list.append(m_entries[limit]); - } -} - -int BackForwardList::capacity() -{ - return m_capacity; -} - -void BackForwardList::setCapacity(int size) -{ - while (size < (int)m_entries.size()) { - RefPtr<HistoryItem> item = m_entries.last(); - m_entries.removeLast(); - m_entryHash.remove(item); - pageCache()->remove(item.get()); - } - - if (!size) - m_current = NoCurrentItemIndex; - else if (m_current > m_entries.size() - 1) { - m_current = m_entries.size() - 1; - } - m_capacity = size; -} - -bool BackForwardList::enabled() -{ - return m_enabled; -} - -void BackForwardList::setEnabled(bool enabled) -{ - m_enabled = enabled; - if (!enabled) { - int capacity = m_capacity; - setCapacity(0); - setCapacity(capacity); - } -} - -int BackForwardList::backListCount() -{ - return m_current == NoCurrentItemIndex ? 0 : m_current; -} - -int BackForwardList::forwardListCount() -{ - return m_current == NoCurrentItemIndex ? 0 : (int)m_entries.size() - (m_current + 1); -} - -HistoryItem* BackForwardList::itemAtIndex(int index) -{ - // Do range checks without doing math on index to avoid overflow. - if (index < -(int)m_current) - return 0; - - if (index > forwardListCount()) - return 0; - - return m_entries[index + m_current].get(); -} - -HistoryItemVector& BackForwardList::entries() -{ - return m_entries; -} - -#if PLATFORM(IOS) -unsigned BackForwardList::current() -{ - return m_current; -} - -void BackForwardList::setCurrent(unsigned newCurrent) -{ - m_current = newCurrent; -} - -bool BackForwardList::clearAllPageCaches() -{ - bool didRemoveAtLeastOneItem = false; - unsigned length = m_entries.size(); - for (unsigned i = 0; i < length; ++i) { - HistoryItem* item = m_entries[i].get(); - if (item->isInPageCache()) { - didRemoveAtLeastOneItem = true; - pageCache()->remove(item); - } - } - return didRemoveAtLeastOneItem; -} -#endif - -void BackForwardList::close() -{ - int size = m_entries.size(); - for (int i = 0; i < size; ++i) - pageCache()->remove(m_entries[i].get()); - m_entries.clear(); - m_entryHash.clear(); - m_page = 0; - m_closed = true; -} - -bool BackForwardList::closed() -{ - return m_closed; -} - -void BackForwardList::removeItem(HistoryItem* item) -{ - if (!item) - return; - - for (unsigned i = 0; i < m_entries.size(); ++i) - if (m_entries[i] == item) { - m_entries.remove(i); - m_entryHash.remove(item); - if (m_current == NoCurrentItemIndex || m_current < i) - break; - if (m_current > i) - m_current--; - else { - size_t count = m_entries.size(); - if (m_current >= count) - m_current = count ? count - 1 : NoCurrentItemIndex; - } - break; - } -} - -bool BackForwardList::containsItem(HistoryItem* entry) -{ - return m_entryHash.contains(entry); -} - -}; // namespace WebCore diff --git a/Source/WebCore/history/BackForwardList.h b/Source/WebCore/history/BackForwardList.h deleted file mode 100644 index ae7adfa16..000000000 --- a/Source/WebCore/history/BackForwardList.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. - * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) - * Copyright (C) 2009 Google, 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 COMPUTER, 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 COMPUTER, 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. - */ - -#ifndef BackForwardList_h -#define BackForwardList_h - -#include "BackForwardClient.h" -#include <wtf/HashSet.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class Page; - -typedef Vector<RefPtr<HistoryItem>> HistoryItemVector; -typedef HashSet<RefPtr<HistoryItem>> HistoryItemHashSet; - -class BackForwardList : public BackForwardClient { -public: - static PassRefPtr<BackForwardList> create(Page* page) { return adoptRef(new BackForwardList(page)); } - virtual ~BackForwardList(); - - Page* page() { return m_page; } - - virtual void addItem(PassRefPtr<HistoryItem>) override; - void goBack(); - void goForward(); - virtual void goToItem(HistoryItem*) override; - - HistoryItem* backItem(); - HistoryItem* currentItem(); - HistoryItem* forwardItem(); - virtual HistoryItem* itemAtIndex(int) override; - - void backListWithLimit(int, HistoryItemVector&); - void forwardListWithLimit(int, HistoryItemVector&); - - int capacity(); - void setCapacity(int); - bool enabled(); - void setEnabled(bool); - virtual int backListCount() override; - virtual int forwardListCount() override; - bool containsItem(HistoryItem*); - - virtual void close() override; - bool closed(); - - void removeItem(HistoryItem*); - HistoryItemVector& entries(); - -#if PLATFORM(IOS) - virtual unsigned current() override; - virtual void setCurrent(unsigned newCurrent) override; - - virtual bool clearAllPageCaches() override; -#endif - -private: - explicit BackForwardList(Page*); - - Page* m_page; - HistoryItemVector m_entries; - HistoryItemHashSet m_entryHash; - unsigned m_current; - unsigned m_capacity; - bool m_closed; - bool m_enabled; -}; - -} // namespace WebCore - -#endif // BackForwardList_h diff --git a/Source/WebCore/history/CachedFrame.cpp b/Source/WebCore/history/CachedFrame.cpp index 1f61671d7..210285243 100644 --- a/Source/WebCore/history/CachedFrame.cpp +++ b/Source/WebCore/history/CachedFrame.cpp @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -24,44 +24,32 @@ */ #include "config.h" -#include "CachedPage.h" +#include "CachedFrame.h" -#include "AnimationController.h" +#include "CSSAnimationController.h" #include "CachedFramePlatformData.h" +#include "CachedPage.h" #include "DOMWindow.h" #include "Document.h" #include "DocumentLoader.h" -#include "EventHandler.h" -#include "EventNames.h" -#include "ExceptionCode.h" -#include "FocusController.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "FrameView.h" -#include "HistoryController.h" -#include "HistoryItem.h" #include "Logging.h" #include "MainFrame.h" #include "Page.h" -#include "PageTransitionEvent.h" +#include "PageCache.h" +#include "SVGDocumentExtensions.h" #include "ScriptController.h" #include "SerializedScriptValue.h" #include <wtf/RefCountedLeakCounter.h> #include <wtf/text/CString.h> -#if ENABLE(SVG) -#include "SVGDocumentExtensions.h" -#endif - -#if ENABLE(TOUCH_EVENTS) +#if PLATFORM(IOS) || ENABLE(TOUCH_EVENTS) #include "Chrome.h" #include "ChromeClient.h" #endif -#if USE(ACCELERATED_COMPOSITING) -#include "PageCache.h" -#endif - namespace WebCore { DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedFrameCounter, ("CachedFrame")); @@ -70,12 +58,8 @@ CachedFrameBase::CachedFrameBase(Frame& frame) : m_document(frame.document()) , m_documentLoader(frame.loader().documentLoader()) , m_view(frame.view()) - , m_mousePressNode(frame.eventHandler().mousePressNode()) , m_url(frame.document()->url()) , m_isMainFrame(!frame.tree().parent()) -#if USE(ACCELERATED_COMPOSITING) - , m_isComposited(frame.view()->hasCompositedContent()) -#endif { } @@ -88,6 +72,17 @@ CachedFrameBase::~CachedFrameBase() 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); @@ -98,31 +93,27 @@ void CachedFrameBase::restore() Frame& frame = m_view->frame(); m_cachedFrameScriptData->restore(frame); -#if ENABLE(SVG) if (m_document->svgExtensions()) - m_document->accessSVGExtensions()->unpauseAnimations(); -#endif + m_document->accessSVGExtensions().unpauseAnimations(); frame.animation().resumeAnimationsForDocument(m_document.get()); - frame.eventHandler().setMousePressNode(m_mousePressNode.get()); - m_document->resumeActiveDOMObjects(ActiveDOMObject::DocumentWillBecomeInactive); - m_document->resumeScriptedAnimationControllerCallbacks(); + + m_document->resume(ActiveDOMObject::PageCache); // It is necessary to update any platform script objects after restoring the // cached page. frame.script().updatePlatformScriptObjects(); -#if USE(ACCELERATED_COMPOSITING) - if (m_isComposited) - frame.view()->restoreBackingStores(); -#endif - frame.loader().client().didRestoreFromPageCache(); + pruneDetachedChildFrames(); + // Reconstruct the FrameTree. And open the child CachedFrames in their respective FrameLoaders. - for (unsigned i = 0; i < m_childFrames.size(); ++i) { - frame.tree().appendChild(&m_childFrames[i]->view()->frame()); - m_childFrames[i]->open(); + 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) @@ -131,25 +122,14 @@ void CachedFrameBase::restore() if (DOMWindow* domWindow = m_document->domWindow()) { // FIXME: Add SCROLL_LISTENER to the list of event types on Document, and use m_document->hasListenerType(). See <rdar://problem/9615482>. + // FIXME: Can use Document::hasListenerType() now. if (domWindow->scrollEventListenerCount() && frame.page()) - frame.page()->chrome().client().setNeedsScrollNotifications(&frame, true); + frame.page()->chrome().client().setNeedsScrollNotifications(frame, true); } } #endif - // FIXME: update Page Visibility state here. - // https://bugs.webkit.org/show_bug.cgi?id=116770 - m_document->enqueuePageshowEvent(PageshowEventPersisted); - - HistoryItem* historyItem = frame.loader().history().currentItem(); - m_document->enqueuePopstateEvent(historyItem && historyItem->stateObject() ? historyItem->stateObject() : SerializedScriptValue::nullValue()); - -#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS) - if (m_document->hasTouchEventHandlers()) - m_document->page()->chrome().client().needTouchEvents(true); -#endif - - m_document->documentDidResumeFromPageCache(); + frame.view()->didRestoreFromPageCache(); } CachedFrame::CachedFrame(Frame& frame) @@ -161,38 +141,21 @@ CachedFrame::CachedFrame(Frame& frame) ASSERT(m_document); ASSERT(m_documentLoader); ASSERT(m_view); - - if (frame.page()->focusController().focusedFrame() == &frame) - frame.page()->focusController().setFocusedFrame(&frame.mainFrame()); - - // Custom scrollbar renderers will get reattached when the document comes out of the page cache - m_view->detachCustomScrollbars(); - - m_document->setInPageCache(true); - frame.loader().stopLoading(UnloadEventPolicyUnloadAndPageHide); + ASSERT(m_document->pageCacheState() == Document::InPageCache); // Create the CachedFrames for all Frames in the FrameTree. for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) m_childFrames.append(std::make_unique<CachedFrame>(*child)); - // Active DOM objects must be suspended before we cache the frame script data, - // but after we've fired the pagehide event, in case that creates more objects. - // Suspending must also happen after we've recursed over child frames, in case - // those create more objects. - m_document->documentWillSuspendForPageCache(); - m_document->suspendScriptedAnimationControllerCallbacks(); - m_document->suspendActiveDOMObjects(ActiveDOMObject::DocumentWillBecomeInactive); + // Active DOM objects must be suspended before we cache the frame script data. + m_document->suspend(ActiveDOMObject::PageCache); + m_cachedFrameScriptData = std::make_unique<ScriptCachedFrameData>(frame); - m_document->domWindow()->suspendForPageCache(); + m_document->domWindow()->suspendForDocumentSuspension(); frame.loader().client().savePlatformDataToCachedFrame(this); -#if USE(ACCELERATED_COMPOSITING) - if (m_isComposited && pageCache()->shouldClearBackingStores()) - frame.view()->clearBackingStores(); -#endif - // documentWillSuspendForPageCache() can set up a layout timer on the FrameView, so clear timers after that. frame.clearTimers(); @@ -201,7 +164,7 @@ CachedFrame::CachedFrame(Frame& frame) // 1 - We reuse the main frame, so when it navigates to a new page load it needs to start with a blank FrameTree. // 2 - It's much easier to destroy a CachedFrame while it resides in the PageCache if it is disconnected from its parent. for (unsigned i = 0; i < m_childFrames.size(); ++i) - frame.tree().removeChild(&m_childFrames[i]->view()->frame()); + frame.tree().removeChild(m_childFrames[i]->view()->frame()); if (!m_isMainFrame) frame.page()->decrementSubframeCount(); @@ -219,18 +182,25 @@ CachedFrame::CachedFrame(Frame& frame) if (m_isMainFrame) { if (DOMWindow* domWindow = m_document->domWindow()) { if (domWindow->scrollEventListenerCount() && frame.page()) - frame.page()->chrome().client().setNeedsScrollNotifications(&frame, false); + 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); } @@ -243,7 +213,7 @@ void CachedFrame::clear() // This means the CachedFrame has been: // 1 - Successfully restore()'d by going back/forward. // 2 - destroy()'ed because the PageCache is pruning or the WebView was closed. - ASSERT(!m_document->inPageCache()); + ASSERT(m_document->pageCacheState() == Document::NotInPageCache); ASSERT(m_view); ASSERT(!m_document->frame() || m_document->frame() == &m_view->frame()); @@ -252,7 +222,6 @@ void CachedFrame::clear() m_document = nullptr; m_view = nullptr; - m_mousePressNode = nullptr; m_url = URL(); m_cachedFramePlatformData = nullptr; @@ -265,15 +234,15 @@ void CachedFrame::destroy() return; // Only CachedFrames that are still in the PageCache should be destroyed in this manner - ASSERT(m_document->inPageCache()); + ASSERT(m_document->pageCacheState() == Document::InPageCache); ASSERT(m_view); - ASSERT(m_document->frame() == &m_view->frame()); + ASSERT(!m_document->frame()); m_document->domWindow()->willDestroyCachedFrame(); - if (!m_isMainFrame) { - m_view->frame().detachFromPage(); + 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) @@ -284,11 +253,13 @@ void CachedFrame::destroy() Frame::clearTimers(m_view.get(), m_document.get()); + m_view->frame().animation().detachFromDocument(m_document.get()); + // FIXME: Why do we need to call removeAllEventListeners here? When the document is in page cache, this method won't work // fully anyway, because the document won't be able to access its DOMWindow object (due to being frameless). m_document->removeAllEventListeners(); - m_document->setInPageCache(false); + m_document->setPageCacheState(Document::NotInPageCache); m_document->prepareForDestruction(); clear(); @@ -296,7 +267,7 @@ void CachedFrame::destroy() void CachedFrame::setCachedFramePlatformData(std::unique_ptr<CachedFramePlatformData> data) { - m_cachedFramePlatformData = std::move(data); + m_cachedFramePlatformData = WTFMove(data); } CachedFramePlatformData* CachedFrame::cachedFramePlatformData() @@ -304,6 +275,11 @@ CachedFramePlatformData* CachedFrame::cachedFramePlatformData() return m_cachedFramePlatformData.get(); } +void CachedFrame::setHasInsecureContent(HasInsecureContent hasInsecureContent) +{ + m_hasInsecureContent = hasInsecureContent; +} + int CachedFrame::descendantFrameCount() const { int count = m_childFrames.size(); diff --git a/Source/WebCore/history/CachedFrame.h b/Source/WebCore/history/CachedFrame.h index e9f367bbd..e0be571a2 100644 --- a/Source/WebCore/history/CachedFrame.h +++ b/Source/WebCore/history/CachedFrame.h @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedFrame_h -#define CachedFrame_h +#pragma once #include "DOMWindow.h" #include "URL.h" @@ -39,6 +38,7 @@ class Document; class DocumentLoader; class FrameView; class Node; +enum class HasInsecureContent; class CachedFrameBase { public: @@ -52,23 +52,23 @@ public: protected: CachedFrameBase(Frame&); ~CachedFrameBase(); - + + void pruneDetachedChildFrames(); + RefPtr<Document> m_document; RefPtr<DocumentLoader> m_documentLoader; RefPtr<FrameView> m_view; - RefPtr<Node> m_mousePressNode; URL m_url; std::unique_ptr<ScriptCachedFrameData> m_cachedFrameScriptData; std::unique_ptr<CachedFramePlatformData> m_cachedFramePlatformData; bool m_isMainFrame; -#if USE(ACCELERATED_COMPOSITING) - bool m_isComposited; -#endif - + std::optional<HasInsecureContent> m_hasInsecureContent; + Vector<std::unique_ptr<CachedFrame>> m_childFrames; }; class CachedFrame : private CachedFrameBase { + WTF_MAKE_FAST_ALLOCATED; public: explicit CachedFrame(Frame&); @@ -76,18 +76,18 @@ public: void clear(); void destroy(); - void setCachedFramePlatformData(std::unique_ptr<CachedFramePlatformData>); - CachedFramePlatformData* cachedFramePlatformData(); + WEBCORE_EXPORT void setCachedFramePlatformData(std::unique_ptr<CachedFramePlatformData>); + WEBCORE_EXPORT CachedFramePlatformData* cachedFramePlatformData(); + + WEBCORE_EXPORT void setHasInsecureContent(HasInsecureContent); + std::optional<HasInsecureContent> hasInsecureContent() const { return m_hasInsecureContent; } using CachedFrameBase::document; using CachedFrameBase::view; using CachedFrameBase::url; DocumentLoader* documentLoader() const { return m_documentLoader.get(); } - Node* mousePressNode() const { return m_mousePressNode.get(); } int descendantFrameCount() const; }; } // namespace WebCore - -#endif // CachedFrame_h diff --git a/Source/WebCore/history/CachedFramePlatformData.h b/Source/WebCore/history/CachedFramePlatformData.h index 01da8e5a6..56f4431b9 100644 --- a/Source/WebCore/history/CachedFramePlatformData.h +++ b/Source/WebCore/history/CachedFramePlatformData.h @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple 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. * @@ -25,8 +25,8 @@ * (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 CachedFramePlatformData_h -#define CachedFramePlatformData_h + +#pragma once namespace WebCore { @@ -41,5 +41,3 @@ public: }; } // namespace WebCore - -#endif // CachedFramePlatformData_h diff --git a/Source/WebCore/history/CachedPage.cpp b/Source/WebCore/history/CachedPage.cpp index cad18497e..3ddd9c18d 100644 --- a/Source/WebCore/history/CachedPage.cpp +++ b/Source/WebCore/history/CachedPage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * 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 @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -30,9 +30,13 @@ #include "Element.h" #include "FocusController.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> @@ -50,13 +54,9 @@ namespace WebCore { DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedPageCounter, ("CachedPage")); CachedPage::CachedPage(Page& page) - : m_timeStamp(monotonicallyIncreasingTime()) - , m_expirationTime(m_timeStamp + page.settings().backForwardCacheExpirationInterval()) + : m_page(page) + , m_expirationTime(monotonicallyIncreasingTime() + page.settings().backForwardCacheExpirationInterval()) , m_cachedMainFrame(std::make_unique<CachedFrame>(page.mainFrame())) - , m_needStyleRecalcForVisitedLinks(false) - , m_needsFullStyleRecalc(false) - , m_needsCaptionPreferencesChanged(false) - , m_needsDeviceScaleChanged(false) { #ifndef NDEBUG cachedPageCounter.increment(); @@ -69,8 +69,33 @@ CachedPage::~CachedPage() cachedPageCounter.decrement(); #endif - destroy(); - ASSERT(!m_cachedMainFrame); + 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().traverseNextInPostOrderWithWrap(true); child; child = child->tree().traverseNextInPostOrderWithWrap(false)) + childFrames.append(*child); + + for (auto& child : childFrames) { + if (!child->tree().isDescendantOf(&mainFrame)) + continue; + auto* document = child->document(); + if (!document) + continue; + + // FIXME: Update Page Visibility state here. + // https://bugs.webkit.org/show_bug.cgi?id=116770 + document->dispatchPageshowEvent(PageshowEventPersisted); + + auto* historyItem = child->loader().history().currentItem(); + if (historyItem && historyItem->stateObject()) + document->dispatchPopstateEvent(historyItem->stateObject()); + } } void CachedPage::restore(Page& page) @@ -79,7 +104,13 @@ void CachedPage::restore(Page& page) ASSERT(m_cachedMainFrame->view()->frame().isMainFrame()); ASSERT(!page.subframeCount()); - m_cachedMainFrame->open(); + { + // Do not dispatch DOM events as their JavaScript listeners could cause the page to be put + // into the page cache before we have finished restoring it from the page cache. + NoEventDispatchAssertion noEventDispatchAssertion; + + 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. @@ -89,32 +120,39 @@ void CachedPage::restore(Page& page) // 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(true); + element->updateFocusAppearance(SelectionRestorationMode::Restore); #if PLATFORM(IOS) + if (frameView) + frameView->setProhibitsScrolling(hadProhibitsScrolling); page.mainFrame().selection().restoreScrolling(); #endif } - if (m_needStyleRecalcForVisitedLinks) { - for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) - frame->document()->visitedLinkState().invalidateStyleForAllLinks(); - } - -#if USE(ACCELERATED_COMPOSITING) - if (m_needsDeviceScaleChanged) { + if (m_needsDeviceOrPageScaleChanged) page.mainFrame().deviceOrPageScaleFactorChanged(); - } -#endif - if (m_needsFullStyleRecalc) - page.setNeedsRecalcStyleInAllFrames(); + 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(); } @@ -122,17 +160,12 @@ void CachedPage::clear() { ASSERT(m_cachedMainFrame); m_cachedMainFrame->clear(); - m_cachedMainFrame = 0; - m_needStyleRecalcForVisitedLinks = false; - m_needsFullStyleRecalc = false; -} - -void CachedPage::destroy() -{ - if (m_cachedMainFrame) - m_cachedMainFrame->destroy(); - - m_cachedMainFrame = 0; + m_cachedMainFrame = nullptr; +#if ENABLE(VIDEO_TRACK) + m_needsCaptionPreferencesChanged = false; +#endif + m_needsDeviceOrPageScaleChanged = false; + m_needsUpdateContentsSize = false; } bool CachedPage::hasExpired() const diff --git a/Source/WebCore/history/CachedPage.h b/Source/WebCore/history/CachedPage.h index bb83e0fff..73bdd5a9c 100644 --- a/Source/WebCore/history/CachedPage.h +++ b/Source/WebCore/history/CachedPage.h @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -23,18 +23,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CachedPage_h -#define CachedPage_h +#pragma once #include "CachedFrame.h" namespace WebCore { - + class Document; class DocumentLoader; class Page; class CachedPage { + WTF_MAKE_FAST_ALLOCATED; public: explicit CachedPage(Page&); ~CachedPage(); @@ -42,37 +42,31 @@ public: void restore(Page&); void clear(); + Page& page() const { return m_page; } Document* document() const { return m_cachedMainFrame->document(); } DocumentLoader* documentLoader() const { return m_cachedMainFrame->documentLoader(); } - double timeStamp() const { return m_timeStamp; } bool hasExpired() const; CachedFrame* cachedMainFrame() { return m_cachedMainFrame.get(); } - void markForVistedLinkStyleRecalc() { m_needStyleRecalcForVisitedLinks = true; } - void markForFullStyleRecalc() { m_needsFullStyleRecalc = true; } #if ENABLE(VIDEO_TRACK) void markForCaptionPreferencesChanged() { m_needsCaptionPreferencesChanged = true; } #endif -#if USE(ACCELERATED_COMPOSITING) - void markForDeviceScaleChanged() { m_needsDeviceScaleChanged = true; } -#endif + void markForDeviceOrPageScaleChanged() { m_needsDeviceOrPageScaleChanged = true; } -private: - void destroy(); + void markForContentsSizeChanged() { m_needsUpdateContentsSize = true; } - double m_timeStamp; +private: + Page& m_page; double m_expirationTime; std::unique_ptr<CachedFrame> m_cachedMainFrame; - bool m_needStyleRecalcForVisitedLinks; - bool m_needsFullStyleRecalc; - bool m_needsCaptionPreferencesChanged; - bool m_needsDeviceScaleChanged; +#if ENABLE(VIDEO_TRACK) + bool m_needsCaptionPreferencesChanged { false }; +#endif + bool m_needsDeviceOrPageScaleChanged { false }; + bool m_needsUpdateContentsSize { false }; }; } // namespace WebCore - -#endif // CachedPage_h - diff --git a/Source/WebCore/history/HistoryItem.cpp b/Source/WebCore/history/HistoryItem.cpp index 8ce0841b9..7a5fbbb45 100644 --- a/Source/WebCore/history/HistoryItem.cpp +++ b/Source/WebCore/history/HistoryItem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2008, 2011, 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 @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -37,14 +37,10 @@ #include <stdio.h> #include <wtf/CurrentTime.h> #include <wtf/DateMath.h> -#include <wtf/Decoder.h> -#include <wtf/Encoder.h> #include <wtf/text/CString.h> namespace WebCore { -const uint32_t backForwardTreeEncodingVersion = 2; - static long long generateSequenceNumber() { // Initialize to the current time to reduce the likelihood of generating @@ -57,7 +53,7 @@ static void defaultNotifyHistoryItemChanged(HistoryItem*) { } -void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged; +WEBCORE_EXPORT void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged; HistoryItem::HistoryItem() : m_pageScaleFactor(0) @@ -65,13 +61,7 @@ HistoryItem::HistoryItem() , m_isTargetItem(false) , m_itemSequenceNumber(generateSequenceNumber()) , m_documentSequenceNumber(generateSequenceNumber()) - , m_next(0) - , m_prev(0) -#if PLATFORM(IOS) - , m_scale(0) - , m_scaleIsInitial(false) - , m_bookmarkID(0) -#endif + , m_pruningReason(PruningReason::None) { } @@ -84,14 +74,8 @@ HistoryItem::HistoryItem(const String& urlString, const String& title) , m_isTargetItem(false) , m_itemSequenceNumber(generateSequenceNumber()) , m_documentSequenceNumber(generateSequenceNumber()) - , m_next(0) - , m_prev(0) -#if PLATFORM(IOS) - , m_scale(0) - , m_scaleIsInitial(false) - , m_bookmarkID(0) -#endif -{ + , m_pruningReason(PruningReason::None) +{ iconDatabase().retainIconForPageURL(m_urlString); } @@ -105,39 +89,11 @@ HistoryItem::HistoryItem(const String& urlString, const String& title, const Str , m_isTargetItem(false) , m_itemSequenceNumber(generateSequenceNumber()) , m_documentSequenceNumber(generateSequenceNumber()) - , m_next(0) - , m_prev(0) -#if PLATFORM(IOS) - , m_scale(0) - , m_scaleIsInitial(false) - , m_bookmarkID(0) -#endif + , m_pruningReason(PruningReason::None) { iconDatabase().retainIconForPageURL(m_urlString); } -HistoryItem::HistoryItem(const URL& url, const String& target, const String& parent, const String& title) - : m_urlString(url.string()) - , m_originalURLString(url.string()) - , m_target(target) - , m_parent(parent) - , m_title(title) - , m_pageScaleFactor(0) - , m_lastVisitWasFailure(false) - , m_isTargetItem(false) - , m_itemSequenceNumber(generateSequenceNumber()) - , m_documentSequenceNumber(generateSequenceNumber()) - , m_next(0) - , m_prev(0) -#if PLATFORM(IOS) - , m_scale(0) - , m_scaleIsInitial(false) - , m_bookmarkID(0) -#endif -{ - iconDatabase().retainIconForPageURL(m_urlString); -} - HistoryItem::~HistoryItem() { ASSERT(!m_cachedPage); @@ -150,21 +106,20 @@ inline HistoryItem::HistoryItem(const HistoryItem& item) , m_originalURLString(item.m_originalURLString) , m_referrer(item.m_referrer) , m_target(item.m_target) - , m_parent(item.m_parent) , m_title(item.m_title) , m_displayTitle(item.m_displayTitle) - , m_scrollPoint(item.m_scrollPoint) + , m_scrollPosition(item.m_scrollPosition) , m_pageScaleFactor(item.m_pageScaleFactor) , m_lastVisitWasFailure(item.m_lastVisitWasFailure) , m_isTargetItem(item.m_isTargetItem) , m_itemSequenceNumber(item.m_itemSequenceNumber) , m_documentSequenceNumber(item.m_documentSequenceNumber) , m_formContentType(item.m_formContentType) + , m_pruningReason(PruningReason::None) #if PLATFORM(IOS) + , m_obscuredInset(item.m_obscuredInset) , m_scale(item.m_scale) , m_scaleIsInitial(item.m_scaleIsInitial) - , m_bookmarkID(item.m_bookmarkID) - , m_sharedLinkUniqueIdentifier(item.m_sharedLinkUniqueIdentifier) #endif { if (item.m_formData) @@ -174,14 +129,11 @@ inline HistoryItem::HistoryItem(const HistoryItem& item) m_children.reserveInitialCapacity(size); for (unsigned i = 0; i < size; ++i) m_children.uncheckedAppend(item.m_children[i]->copy()); - - if (item.m_redirectURLs) - m_redirectURLs = std::make_unique<Vector<String>>(*item.m_redirectURLs); } -PassRefPtr<HistoryItem> HistoryItem::copy() const +Ref<HistoryItem> HistoryItem::copy() const { - return adoptRef(new HistoryItem(*this)); + return adoptRef(*new HistoryItem(*this)); } void HistoryItem::reset() @@ -192,21 +144,18 @@ void HistoryItem::reset() m_originalURLString = String(); m_referrer = String(); m_target = String(); - m_parent = String(); m_title = String(); m_displayTitle = String(); m_lastVisitWasFailure = false; m_isTargetItem = false; - m_redirectURLs = nullptr; - m_itemSequenceNumber = generateSequenceNumber(); - m_stateObject = 0; + m_stateObject = nullptr; m_documentSequenceNumber = generateSequenceNumber(); - m_formData = 0; + m_formData = nullptr; m_formContentType = String(); clearChildren(); @@ -259,11 +208,6 @@ const String& HistoryItem::target() const return m_target; } -const String& HistoryItem::parent() const -{ - return m_parent; -} - void HistoryItem::setAlternateTitle(const String& alternateTitle) { m_displayTitle = alternateTitle; @@ -283,7 +227,7 @@ void HistoryItem::setURLString(const String& urlString) void HistoryItem::setURL(const URL& url) { - pageCache()->remove(this); + PageCache::singleton().remove(*this); setURLString(url.string()); clearDocumentState(); } @@ -312,25 +256,19 @@ void HistoryItem::setTarget(const String& target) notifyHistoryItemChanged(this); } -void HistoryItem::setParent(const String& parent) -{ - m_parent = parent; -} - -const IntPoint& HistoryItem::scrollPoint() const +const IntPoint& HistoryItem::scrollPosition() const { - return m_scrollPoint; + return m_scrollPosition; } -void HistoryItem::setScrollPoint(const IntPoint& point) +void HistoryItem::setScrollPosition(const IntPoint& position) { - m_scrollPoint = point; + m_scrollPosition = position; } -void HistoryItem::clearScrollPoint() +void HistoryItem::clearScrollPosition() { - m_scrollPoint.setX(0); - m_scrollPoint.setY(0); + m_scrollPosition = IntPoint(); } float HistoryItem::pageScaleFactor() const @@ -358,6 +296,16 @@ void HistoryItem::clearDocumentState() m_documentState.clear(); } +void HistoryItem::setShouldOpenExternalURLsPolicy(ShouldOpenExternalURLsPolicy policy) +{ + m_shouldOpenExternalURLsPolicy = policy; +} + +ShouldOpenExternalURLsPolicy HistoryItem::shouldOpenExternalURLsPolicy() const +{ + return m_shouldOpenExternalURLsPolicy; +} + bool HistoryItem::isTargetItem() const { return m_isTargetItem; @@ -368,71 +316,52 @@ void HistoryItem::setIsTargetItem(bool flag) m_isTargetItem = flag; } -void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object) +void HistoryItem::setStateObject(RefPtr<SerializedScriptValue>&& object) { - m_stateObject = object; + m_stateObject = WTFMove(object); } -void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child) +void HistoryItem::addChildItem(Ref<HistoryItem>&& child) { ASSERT(!childItemWithTarget(child->target())); - m_children.append(child); + m_children.append(WTFMove(child)); } -void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child) +void HistoryItem::setChildItem(Ref<HistoryItem>&& child) { ASSERT(!child->isTargetItem()); unsigned size = m_children.size(); for (unsigned i = 0; i < size; ++i) { if (m_children[i]->target() == child->target()) { child->setIsTargetItem(m_children[i]->isTargetItem()); - m_children[i] = child; + m_children[i] = WTFMove(child); return; } } - m_children.append(child); + m_children.append(WTFMove(child)); } -HistoryItem* HistoryItem::childItemWithTarget(const String& target) const +HistoryItem* HistoryItem::childItemWithTarget(const String& target) { unsigned size = m_children.size(); for (unsigned i = 0; i < size; ++i) { if (m_children[i]->target() == target) - return m_children[i].get(); + return m_children[i].ptr(); } - return 0; + return nullptr; } -HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) const +HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) { unsigned size = m_children.size(); for (unsigned i = 0; i < size; ++i) { if (m_children[i]->documentSequenceNumber() == number) - return m_children[i].get(); + return m_children[i].ptr(); } - return 0; -} - -// <rdar://problem/4895849> HistoryItem::findTargetItem() should be replaced with a non-recursive method. -HistoryItem* HistoryItem::findTargetItem() -{ - if (m_isTargetItem) - return this; - unsigned size = m_children.size(); - for (unsigned i = 0; i < size; ++i) { - if (HistoryItem* match = m_children[i]->targetItem()) - return match; - } - return 0; -} - -HistoryItem* HistoryItem::targetItem() -{ - HistoryItem* foundItem = findTargetItem(); - return foundItem ? foundItem : this; + return nullptr; } -const HistoryItemVector& HistoryItem::children() const +const Vector<Ref<HistoryItem>>& HistoryItem::children() const { return m_children; } @@ -447,49 +376,37 @@ void HistoryItem::clearChildren() m_children.clear(); } -bool HistoryItem::isAncestorOf(const HistoryItem* item) const -{ - for (size_t i = 0; i < m_children.size(); ++i) { - HistoryItem* child = m_children[i].get(); - if (child == item) - return true; - if (child->isAncestorOf(item)) - return true; - } - return false; -} - // We do same-document navigation if going to a different item and if either of the following is true: // - The other item corresponds to the same document (for history entries created via pushState or fragment changes). // - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation) -bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const +bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem& otherItem) const { - if (this == otherItem) + if (this == &otherItem) return false; - if (stateObject() || otherItem->stateObject()) - return documentSequenceNumber() == otherItem->documentSequenceNumber(); + if (stateObject() || otherItem.stateObject()) + return documentSequenceNumber() == otherItem.documentSequenceNumber(); - if ((url().hasFragmentIdentifier() || otherItem->url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem->url())) - return documentSequenceNumber() == otherItem->documentSequenceNumber(); + if ((url().hasFragmentIdentifier() || otherItem.url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem.url())) + return documentSequenceNumber() == otherItem.documentSequenceNumber(); return hasSameDocumentTree(otherItem); } // Does a recursive check that this item and its descendants have the same // document sequence numbers as the other item. -bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const +bool HistoryItem::hasSameDocumentTree(HistoryItem& otherItem) const { - if (documentSequenceNumber() != otherItem->documentSequenceNumber()) + if (documentSequenceNumber() != otherItem.documentSequenceNumber()) return false; - if (children().size() != otherItem->children().size()) + if (children().size() != otherItem.children().size()) return false; for (size_t i = 0; i < children().size(); i++) { - HistoryItem* child = children()[i].get(); - HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber()); - if (!otherChild || !child->hasSameDocumentTree(otherChild)) + auto& child = children()[i].get(); + auto* otherChild = otherItem.childItemWithDocumentSequenceNumber(child.documentSequenceNumber()); + if (!otherChild || !child.hasSameDocumentTree(*otherChild)) return false; } @@ -498,16 +415,16 @@ bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const // Does a non-recursive check that this item and its immediate children have the // same frames as the other item. -bool HistoryItem::hasSameFrames(HistoryItem* otherItem) const +bool HistoryItem::hasSameFrames(HistoryItem& otherItem) const { - if (target() != otherItem->target()) + if (target() != otherItem.target()) return false; - if (children().size() != otherItem->children().size()) + if (children().size() != otherItem.children().size()) return false; for (size_t i = 0; i < children().size(); i++) { - if (!otherItem->childItemWithTarget(children()[i]->target())) + if (!otherItem.childItemWithTarget(children()[i]->target())) return false; } @@ -523,20 +440,20 @@ void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request) { m_referrer = request.httpReferrer(); - if (equalIgnoringCase(request.httpMethod(), "POST")) { + if (equalLettersIgnoringASCIICase(request.httpMethod(), "post")) { // FIXME: Eventually we have to make this smart enough to handle the case where // we have a stream for the body to handle the "data interspersed with files" feature. m_formData = request.httpBody(); m_formContentType = request.httpContentType(); } else { - m_formData = 0; + m_formData = nullptr; m_formContentType = String(); } } -void HistoryItem::setFormData(PassRefPtr<FormData> formData) +void HistoryItem::setFormData(RefPtr<FormData>&& formData) { - m_formData = formData; + m_formData = WTFMove(formData); } void HistoryItem::setFormContentType(const String& formContentType) @@ -549,266 +466,15 @@ FormData* HistoryItem::formData() return m_formData.get(); } -bool HistoryItem::isCurrentDocument(Document* doc) const +bool HistoryItem::isCurrentDocument(Document& document) const { // FIXME: We should find a better way to check if this is the current document. - return equalIgnoringFragmentIdentifier(url(), doc->url()); -} - -void HistoryItem::addRedirectURL(const String& url) -{ - if (!m_redirectURLs) - m_redirectURLs = std::make_unique<Vector<String>>(); - - // Our API allows us to store all the URLs in the redirect chain, but for - // now we only have a use for the final URL. - (*m_redirectURLs).resize(1); - (*m_redirectURLs)[0] = url; -} - -Vector<String>* HistoryItem::redirectURLs() const -{ - return m_redirectURLs.get(); -} - -void HistoryItem::setRedirectURLs(std::unique_ptr<Vector<String>> redirectURLs) -{ - m_redirectURLs = std::move(redirectURLs); -} - -void HistoryItem::encodeBackForwardTree(Encoder& encoder) const -{ - encoder.encodeUInt32(backForwardTreeEncodingVersion); - - encodeBackForwardTreeNode(encoder); -} - -void HistoryItem::encodeBackForwardTree(KeyedEncoder& encoder) const -{ - encoder.encodeUInt32("version", backForwardTreeEncodingVersion); - - encoder.encodeObject("root", *this, [](KeyedEncoder& encoder, const HistoryItem& item) { - item.encodeBackForwardTreeNode(encoder); - }); + return equalIgnoringFragmentIdentifier(url(), document.url()); } -void HistoryItem::encodeBackForwardTreeNode(Encoder& encoder) const +void HistoryItem::notifyChanged() { - size_t size = m_children.size(); - encoder.encodeUInt64(size); - for (size_t i = 0; i < size; ++i) { - const HistoryItem& child = *m_children[i]; - - encoder.encodeString(child.m_originalURLString); - - encoder.encodeString(child.m_urlString); - - child.encodeBackForwardTreeNode(encoder); - } - - encoder.encodeInt64(m_documentSequenceNumber); - - size = m_documentState.size(); - encoder.encodeUInt64(size); - for (size_t i = 0; i < size; ++i) - encoder.encodeString(m_documentState[i]); - - encoder.encodeString(m_formContentType); - - encoder.encodeBool(m_formData); - if (m_formData) - m_formData->encode(encoder); - - encoder.encodeInt64(m_itemSequenceNumber); - - encoder.encodeString(m_referrer); - - encoder.encodeInt32(m_scrollPoint.x()); - encoder.encodeInt32(m_scrollPoint.y()); - - encoder.encodeFloat(m_pageScaleFactor); - - encoder.encodeBool(m_stateObject); - if (m_stateObject) - encoder.encodeBytes(m_stateObject->data().data(), m_stateObject->data().size()); - - encoder.encodeString(m_target); -} - -void HistoryItem::encodeBackForwardTreeNode(KeyedEncoder& encoder) const -{ - encoder.encodeObjects("children", m_children.begin(), m_children.end(), [](KeyedEncoder& encoder, const RefPtr<HistoryItem>& child) { - encoder.encodeString("originalURLString", child->m_originalURLString); - encoder.encodeString("urlString", child->m_urlString); - - child->encodeBackForwardTreeNode(encoder); - }); - - encoder.encodeInt64("documentSequenceNumber", m_documentSequenceNumber); - - encoder.encodeObjects("documentState", m_documentState.begin(), m_documentState.end(), [](KeyedEncoder& encoder, const String& string) { - encoder.encodeString("string", string); - }); - - encoder.encodeString("formContentType", m_formContentType); - - encoder.encodeConditionalObject("formData", m_formData.get(), [](KeyedEncoder&, const FormData&) { - // FIXME: Implement. - }); - - encoder.encodeInt64("itemSequenceNumber", m_itemSequenceNumber); - - encoder.encodeString("referrer", m_referrer); - - encoder.encodeObject("scrollPoint", m_scrollPoint, [](KeyedEncoder& encoder, const IntPoint& scrollPoint) { - encoder.encodeInt32("x", scrollPoint.x()); - encoder.encodeInt32("y", scrollPoint.y()); - }); - - encoder.encodeFloat("pageScaleFactor", m_pageScaleFactor); - - encoder.encodeConditionalObject("stateObject", m_stateObject.get(), [](KeyedEncoder& encoder, const SerializedScriptValue& stateObject) { - encoder.encodeBytes("data", stateObject.data().data(), stateObject.data().size()); - }); - - encoder.encodeString("target", m_target); -} - -struct DecodeRecursionStackElement { - RefPtr<HistoryItem> node; - size_t i; - uint64_t size; - - DecodeRecursionStackElement(PassRefPtr<HistoryItem> node, size_t i, uint64_t size) - : node(node) - , i(i) - , size(size) - { - } -}; - -PassRefPtr<HistoryItem> HistoryItem::decodeBackForwardTree(const String& topURLString, const String& topTitle, const String& topOriginalURLString, Decoder& decoder) -{ - // Since the data stream is not trusted, the decode has to be non-recursive. - // We don't want bad data to cause a stack overflow. - - uint32_t version; - if (!decoder.decodeUInt32(version)) - return 0; - if (version != backForwardTreeEncodingVersion) - return 0; - - String urlString = topURLString; - String title = topTitle; - String originalURLString = topOriginalURLString; - - Vector<DecodeRecursionStackElement, 16> recursionStack; - -recurse: - RefPtr<HistoryItem> node = create(urlString, title); - - node->setOriginalURLString(originalURLString); - - title = String(); - - uint64_t size; - if (!decoder.decodeUInt64(size)) - return 0; - size_t i; - RefPtr<HistoryItem> child; - for (i = 0; i < size; ++i) { - if (!decoder.decodeString(originalURLString)) - return 0; - - if (!decoder.decodeString(urlString)) - return 0; - - recursionStack.append(DecodeRecursionStackElement(node.release(), i, size)); - goto recurse; - -resume: - node->m_children.append(child.release()); - } - - if (!decoder.decodeInt64(node->m_documentSequenceNumber)) - return 0; - - if (!decoder.decodeUInt64(size)) - return 0; - for (i = 0; i < size; ++i) { - String state; - if (!decoder.decodeString(state)) - return 0; - node->m_documentState.append(state); - } - - if (!decoder.decodeString(node->m_formContentType)) - return 0; - - bool hasFormData; - if (!decoder.decodeBool(hasFormData)) - return 0; - if (hasFormData) { - node->m_formData = FormData::decode(decoder); - if (!node->m_formData) - return 0; - } - - if (!decoder.decodeInt64(node->m_itemSequenceNumber)) - return 0; - - if (!decoder.decodeString(node->m_referrer)) - return 0; - - int32_t x; - if (!decoder.decodeInt32(x)) - return 0; - int32_t y; - if (!decoder.decodeInt32(y)) - return 0; - node->m_scrollPoint = IntPoint(x, y); - - if (!decoder.decodeFloat(node->m_pageScaleFactor)) - return 0; - - bool hasStateObject; - if (!decoder.decodeBool(hasStateObject)) - return 0; - if (hasStateObject) { - Vector<uint8_t> bytes; - if (!decoder.decodeBytes(bytes)) - return 0; - node->m_stateObject = SerializedScriptValue::adopt(bytes); - } - - if (!decoder.decodeString(node->m_target)) - return 0; - - // Simulate recursion with our own stack. - if (!recursionStack.isEmpty()) { - DecodeRecursionStackElement& element = recursionStack.last(); - child = node.release(); - node = element.node.release(); - i = element.i; - size = element.size; - recursionStack.removeLast(); - goto resume; - } - - return node.release(); -} - -PassRefPtr<HistoryItem> HistoryItem::decodeBackForwardTree(const String&, const String&, const String&, KeyedDecoder& decoder) -{ - uint32_t version; - if (!decoder.decodeUInt32("version", version)) - return nullptr; - - if (version != backForwardTreeEncodingVersion) - return nullptr; - - // FIXME: Implement. - return nullptr; + notifyHistoryItemChanged(this); } #ifndef NDEBUG diff --git a/Source/WebCore/history/HistoryItem.h b/Source/WebCore/history/HistoryItem.h index 9ae72afa0..60b0b8c5a 100644 --- a/Source/WebCore/history/HistoryItem.h +++ b/Source/WebCore/history/HistoryItem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2008, 2011, 2014 Apple Inc. All rights reserved. * Copyright (C) 2012 Research In Motion Limited. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -24,10 +24,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef HistoryItem_h -#define HistoryItem_h +#pragma once +#include "FloatRect.h" +#include "FrameLoaderTypes.h" #include "IntPoint.h" +#include "IntRect.h" #include "SerializedScriptValue.h" #include <memory> #include <wtf/RefCounted.h> @@ -37,7 +39,7 @@ #include "ViewportArguments.h" #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) #import <wtf/RetainPtr.h> typedef struct objc_object* id; #endif @@ -49,89 +51,78 @@ class Document; class FormData; class HistoryItem; class Image; -class KeyedDecoder; -class KeyedEncoder; class ResourceRequest; class URL; +enum class PruningReason; -typedef Vector<RefPtr<HistoryItem>> HistoryItemVector; - -extern void (*notifyHistoryItemChanged)(HistoryItem*); +WEBCORE_EXPORT extern void (*notifyHistoryItemChanged)(HistoryItem*); class HistoryItem : public RefCounted<HistoryItem> { friend class PageCache; public: - static PassRefPtr<HistoryItem> create() { return adoptRef(new HistoryItem); } - static PassRefPtr<HistoryItem> create(const String& urlString, const String& title) - { - return adoptRef(new HistoryItem(urlString, title)); - } - static PassRefPtr<HistoryItem> create(const String& urlString, const String& title, const String& alternateTitle) + static Ref<HistoryItem> create() { return adoptRef(*new HistoryItem); } + static Ref<HistoryItem> create(const String& urlString, const String& title) { - return adoptRef(new HistoryItem(urlString, title, alternateTitle)); + return adoptRef(*new HistoryItem(urlString, title)); } - static PassRefPtr<HistoryItem> create(const URL& url, const String& target, const String& parent, const String& title) + static Ref<HistoryItem> create(const String& urlString, const String& title, const String& alternateTitle) { - return adoptRef(new HistoryItem(url, target, parent, title)); + return adoptRef(*new HistoryItem(urlString, title, alternateTitle)); } - ~HistoryItem(); + WEBCORE_EXPORT ~HistoryItem(); - PassRefPtr<HistoryItem> copy() const; + WEBCORE_EXPORT Ref<HistoryItem> copy() const; // Resets the HistoryItem to its initial state, as returned by create(). void reset(); - void encodeBackForwardTree(Encoder&) const; - void encodeBackForwardTree(KeyedEncoder&) const; - static PassRefPtr<HistoryItem> decodeBackForwardTree(const String& urlString, const String& title, const String& originalURLString, Decoder&); - static PassRefPtr<HistoryItem> decodeBackForwardTree(const String& urlString, const String& title, const String& originalURLString, KeyedDecoder&); - - const String& originalURLString() const; - const String& urlString() const; - const String& title() const; + WEBCORE_EXPORT const String& originalURLString() const; + WEBCORE_EXPORT const String& urlString() const; + WEBCORE_EXPORT const String& title() const; bool isInPageCache() const { return m_cachedPage.get(); } - bool hasCachedPageExpired() const; + WEBCORE_EXPORT bool hasCachedPageExpired() const; - void setAlternateTitle(const String& alternateTitle); - const String& alternateTitle() const; + WEBCORE_EXPORT void setAlternateTitle(const String&); + WEBCORE_EXPORT const String& alternateTitle() const; - const String& parent() const; - URL url() const; - URL originalURL() const; - const String& referrer() const; - const String& target() const; - bool isTargetItem() const; + WEBCORE_EXPORT URL url() const; + WEBCORE_EXPORT URL originalURL() const; + WEBCORE_EXPORT const String& referrer() const; + WEBCORE_EXPORT const String& target() const; + WEBCORE_EXPORT bool isTargetItem() const; - FormData* formData(); - String formContentType() const; + WEBCORE_EXPORT FormData* formData(); + WEBCORE_EXPORT String formContentType() const; bool lastVisitWasFailure() const { return m_lastVisitWasFailure; } - const IntPoint& scrollPoint() const; - void setScrollPoint(const IntPoint&); - void clearScrollPoint(); + WEBCORE_EXPORT const IntPoint& scrollPosition() const; + WEBCORE_EXPORT void setScrollPosition(const IntPoint&); + void clearScrollPosition(); - float pageScaleFactor() const; - void setPageScaleFactor(float); + WEBCORE_EXPORT float pageScaleFactor() const; + WEBCORE_EXPORT void setPageScaleFactor(float); - const Vector<String>& documentState() const; - void setDocumentState(const Vector<String>&); + WEBCORE_EXPORT const Vector<String>& documentState() const; + WEBCORE_EXPORT void setDocumentState(const Vector<String>&); void clearDocumentState(); + WEBCORE_EXPORT void setShouldOpenExternalURLsPolicy(ShouldOpenExternalURLsPolicy); + WEBCORE_EXPORT ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy() const; + void setURL(const URL&); - void setURLString(const String&); - void setOriginalURLString(const String&); - void setReferrer(const String&); - void setTarget(const String&); - void setParent(const String&); - void setTitle(const String&); - void setIsTargetItem(bool); + WEBCORE_EXPORT void setURLString(const String&); + WEBCORE_EXPORT void setOriginalURLString(const String&); + WEBCORE_EXPORT void setReferrer(const String&); + WEBCORE_EXPORT void setTarget(const String&); + WEBCORE_EXPORT void setTitle(const String&); + WEBCORE_EXPORT void setIsTargetItem(bool); - void setStateObject(PassRefPtr<SerializedScriptValue> object); - PassRefPtr<SerializedScriptValue> stateObject() const { return m_stateObject; } + WEBCORE_EXPORT void setStateObject(RefPtr<SerializedScriptValue>&&); + SerializedScriptValue* stateObject() const { return m_stateObject.get(); } void setItemSequenceNumber(long long number) { m_itemSequenceNumber = number; } long long itemSequenceNumber() const { return m_itemSequenceNumber; } @@ -140,38 +131,32 @@ public: long long documentSequenceNumber() const { return m_documentSequenceNumber; } void setFormInfoFromRequest(const ResourceRequest&); - void setFormData(PassRefPtr<FormData>); - void setFormContentType(const String&); + WEBCORE_EXPORT void setFormData(RefPtr<FormData>&&); + WEBCORE_EXPORT void setFormContentType(const String&); void setLastVisitWasFailure(bool wasFailure) { m_lastVisitWasFailure = wasFailure; } - void addChildItem(PassRefPtr<HistoryItem>); - void setChildItem(PassRefPtr<HistoryItem>); - HistoryItem* childItemWithTarget(const String&) const; - HistoryItem* childItemWithDocumentSequenceNumber(long long number) const; - HistoryItem* targetItem(); - const HistoryItemVector& children() const; - bool hasChildren() const; + WEBCORE_EXPORT void addChildItem(Ref<HistoryItem>&&); + void setChildItem(Ref<HistoryItem>&&); + WEBCORE_EXPORT HistoryItem* childItemWithTarget(const String&); + HistoryItem* childItemWithDocumentSequenceNumber(long long number); + WEBCORE_EXPORT const Vector<Ref<HistoryItem>>& children() const; + WEBCORE_EXPORT bool hasChildren() const; void clearChildren(); - bool isAncestorOf(const HistoryItem*) const; - bool shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const; - bool hasSameFrames(HistoryItem* otherItem) const; - - void addRedirectURL(const String&); - Vector<String>* redirectURLs() const; - void setRedirectURLs(std::unique_ptr<Vector<String>>); + bool shouldDoSameDocumentNavigationTo(HistoryItem& otherItem) const; + bool hasSameFrames(HistoryItem& otherItem) const; - bool isCurrentDocument(Document*) const; + bool isCurrentDocument(Document&) const; -#if PLATFORM(MAC) - id viewState() const; - void setViewState(id); +#if PLATFORM(COCOA) + WEBCORE_EXPORT id viewState() const; + WEBCORE_EXPORT void setViewState(id); // Transient properties may be of any ObjC type. They are intended to be used to store state per back/forward list entry. // The properties will not be persisted; when the history item is removed, the properties will be lost. - id getTransientProperty(const String&) const; - void setTransientProperty(const String&, id); + WEBCORE_EXPORT id getTransientProperty(const String&) const; + WEBCORE_EXPORT void setTransientProperty(const String&, id); #endif #ifndef NDEBUG @@ -180,8 +165,24 @@ public: #endif #if PLATFORM(IOS) + FloatRect exposedContentRect() const { return m_exposedContentRect; } + void setExposedContentRect(FloatRect exposedContentRect) { m_exposedContentRect = exposedContentRect; } + + IntRect unobscuredContentRect() const { return m_unobscuredContentRect; } + void setUnobscuredContentRect(IntRect unobscuredContentRect) { m_unobscuredContentRect = unobscuredContentRect; } + + FloatSize obscuredInset() const { return m_obscuredInset; } + void setObscuredInset(const FloatSize& inset) { m_obscuredInset = inset; } + + FloatSize minimumLayoutSizeInScrollViewCoordinates() const { return m_minimumLayoutSizeInScrollViewCoordinates; } + void setMinimumLayoutSizeInScrollViewCoordinates(FloatSize minimumLayoutSizeInScrollViewCoordinates) { m_minimumLayoutSizeInScrollViewCoordinates = minimumLayoutSizeInScrollViewCoordinates; } + + IntSize contentSize() const { return m_contentSize; } + void setContentSize(IntSize contentSize) { m_contentSize = contentSize; } + float scale() const { return m_scale; } bool scaleIsInitial() const { return m_scaleIsInitial; } + void setScaleIsInitial(bool scaleIsInitial) { m_scaleIsInitial = scaleIsInitial; } void setScale(float newScale, bool isInitial) { m_scale = newScale; @@ -190,46 +191,40 @@ public: const ViewportArguments& viewportArguments() const { return m_viewportArguments; } void setViewportArguments(const ViewportArguments& viewportArguments) { m_viewportArguments = viewportArguments; } - - uint32_t bookmarkID() const { return m_bookmarkID; } - void setBookmarkID(uint32_t bookmarkID) { m_bookmarkID = bookmarkID; } - String sharedLinkUniqueIdentifier() const { return m_sharedLinkUniqueIdentifier; } - void setSharedLinkUniqueIdentifier(const String& sharedLinkUniqueidentifier) { m_sharedLinkUniqueIdentifier = sharedLinkUniqueidentifier; } #endif -private: - HistoryItem(); - HistoryItem(const String& urlString, const String& title); - HistoryItem(const String& urlString, const String& title, const String& alternateTitle); - HistoryItem(const URL& url, const String& frameName, const String& parent, const String& title); + void notifyChanged(); - explicit HistoryItem(const HistoryItem&); + void setWasRestoredFromSession(bool wasRestoredFromSession) { m_wasRestoredFromSession = wasRestoredFromSession; } + bool wasRestoredFromSession() const { return m_wasRestoredFromSession; } - bool hasSameDocumentTree(HistoryItem* otherItem) const; +private: + WEBCORE_EXPORT HistoryItem(); + WEBCORE_EXPORT HistoryItem(const String& urlString, const String& title); + WEBCORE_EXPORT HistoryItem(const String& urlString, const String& title, const String& alternateTitle); - HistoryItem* findTargetItem(); + HistoryItem(const HistoryItem&); - void encodeBackForwardTreeNode(Encoder&) const; - void encodeBackForwardTreeNode(KeyedEncoder&) const; + bool hasSameDocumentTree(HistoryItem& otherItem) const; String m_urlString; String m_originalURLString; String m_referrer; String m_target; - String m_parent; String m_title; String m_displayTitle; - IntPoint m_scrollPoint; + IntPoint m_scrollPosition; float m_pageScaleFactor; Vector<String> m_documentState; + + ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow }; - HistoryItemVector m_children; + Vector<Ref<HistoryItem>> m_children; bool m_lastVisitWasFailure; bool m_isTargetItem; - - std::unique_ptr<Vector<String>> m_redirectURLs; + bool m_wasRestoredFromSession { false }; // If two HistoryItems have the same item sequence number, then they are // clones of one another. Traversing history from one such HistoryItem to @@ -250,30 +245,29 @@ private: String m_formContentType; // PageCache controls these fields. - HistoryItem* m_next; - HistoryItem* m_prev; std::unique_ptr<CachedPage> m_cachedPage; + PruningReason m_pruningReason; #if PLATFORM(IOS) - float m_scale; - bool m_scaleIsInitial; + FloatRect m_exposedContentRect; + IntRect m_unobscuredContentRect; + FloatSize m_minimumLayoutSizeInScrollViewCoordinates; + IntSize m_contentSize; + FloatSize m_obscuredInset; + float m_scale { 0 }; // Note that UIWebView looks for a non-zero value, so this has to start as 0. + bool m_scaleIsInitial { false }; ViewportArguments m_viewportArguments; - - uint32_t m_bookmarkID; - String m_sharedLinkUniqueIdentifier; #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) RetainPtr<id> m_viewState; std::unique_ptr<HashMap<String, RetainPtr<id>>> m_transientProperties; #endif -}; //class HistoryItem +}; -} //namespace WebCore +} // namespace WebCore -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. +#if ENABLE(TREE_DEBUGGING) +// Outside the WebCore namespace for ease of invocation from the debugger. extern "C" int showTree(const WebCore::HistoryItem*); #endif - -#endif // HISTORYITEM_H diff --git a/Source/WebCore/history/PageCache.cpp b/Source/WebCore/history/PageCache.cpp index 57ec24efb..02f8ff418 100644 --- a/Source/WebCore/history/PageCache.cpp +++ b/Source/WebCore/history/PageCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2014, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -28,28 +28,29 @@ #include "ApplicationCacheHost.h" #include "BackForwardController.h" -#include "MemoryCache.h" #include "CachedPage.h" #include "DOMWindow.h" -#include "DatabaseManager.h" #include "DeviceMotionController.h" #include "DeviceOrientationController.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" #include "Document.h" #include "DocumentLoader.h" +#include "FocusController.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" -#include "FrameLoaderStateMachine.h" #include "FrameView.h" -#include "HistogramSupport.h" #include "HistoryController.h" -#include "HistoryItem.h" +#include "IgnoreOpensDuringUnloadCountIncrementer.h" #include "Logging.h" #include "MainFrame.h" +#include "MemoryPressureHandler.h" +#include "NoEventDispatchAssertion.h" #include "Page.h" #include "Settings.h" -#include "SharedWorkerRepository.h" #include "SubframeLoader.h" -#include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/SetForScope.h> #include <wtf/text/CString.h> #include <wtf/text/StringConcatenate.h> @@ -57,514 +58,448 @@ #include "DeviceProximityController.h" #endif -#if PLATFORM(IOS) -#include "MemoryPressureHandler.h" -#endif - namespace WebCore { -#if !defined(NDEBUG) - #define PCLOG(...) LOG(PageCache, "%*s%s", indentLevel*4, "", makeString(__VA_ARGS__).utf8().data()) - -// Used in histograms, please only add at the end, and do not remove elements (renaming e.g. to "FooEnumUnused1" is fine). -// This is because statistics may be gathered from histograms between versions over time, and re-using values causes collisions. -enum ReasonFrameCannotBeInPageCache { - NoDocumentLoader = 0, - MainDocumentError, - IsErrorPage, - HasPlugins, - IsHttpsAndCacheControlled, - HasUnloadListener, - HasDatabaseHandles, - HasSharedWorkers, - NoHistoryItem, - QuickRedirectComing, - IsLoadingInAPISense, - IsStopping, - CannotSuspendActiveDOMObjects, - DocumentLoaderUsesApplicationCache, - ClientDeniesCaching, - NumberOfReasonsFramesCannotBeInPageCache, -}; -COMPILE_ASSERT(NumberOfReasonsFramesCannotBeInPageCache <= sizeof(unsigned)*8, ReasonFrameCannotBeInPageCacheDoesNotFitInBitmap); - -static unsigned logCanCacheFrameDecision(Frame* frame, int indentLevel) + +static inline void logPageCacheFailureDiagnosticMessage(DiagnosticLoggingClient& client, const String& reason) +{ + client.logDiagnosticMessage(DiagnosticLoggingKeys::pageCacheFailureKey(), reason, ShouldSample::Yes); +} + +static inline void logPageCacheFailureDiagnosticMessage(Page* page, const String& reason) +{ + if (!page) + return; + + logPageCacheFailureDiagnosticMessage(page->diagnosticLoggingClient(), reason); +} + +static bool canCacheFrame(Frame& frame, DiagnosticLoggingClient& diagnosticLoggingClient, unsigned indentLevel) { PCLOG("+---"); - if (!frame->loader().documentLoader()) { + FrameLoader& frameLoader = frame.loader(); + + // Prevent page caching if a subframe is still in provisional load stage. + // We only do this check for subframes because the main frame is reused when navigating to a new page. + if (!frame.isMainFrame() && frameLoader.state() == FrameStateProvisional) { + PCLOG(" -Frame is in provisional load stage"); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::provisionalLoadKey()); + return false; + } + + DocumentLoader* documentLoader = frameLoader.documentLoader(); + if (!documentLoader) { PCLOG(" -There is no DocumentLoader object"); - return 1 << NoDocumentLoader; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noDocumentLoaderKey()); + return false; } - URL currentURL = frame->loader().documentLoader()->url(); - URL newURL = frame->loader().provisionalDocumentLoader() ? frame->loader().provisionalDocumentLoader()->url() : URL(); + URL currentURL = documentLoader->url(); + URL newURL = frameLoader.provisionalDocumentLoader() ? frameLoader.provisionalDocumentLoader()->url() : URL(); if (!newURL.isEmpty()) PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):"); else PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:"); - unsigned rejectReasons = 0; - if (!frame->loader().documentLoader()->mainDocumentError().isNull()) { + bool isCacheable = true; + if (!documentLoader->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); -#if !PLATFORM(IOS) - rejectReasons |= 1 << MainDocumentError; -#else - if (frame->loader().documentLoader()->mainDocumentError().isCancellation() && frame->loader().documentLoader()->subresourceLoadersArePageCacheAcceptable()) - PCLOG(" -But, it was a cancellation and all loaders during the cancel were loading images."); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::mainDocumentErrorKey()); + + if (documentLoader->mainDocumentError().isCancellation() && documentLoader->subresourceLoadersArePageCacheAcceptable()) + PCLOG(" -But, it was a cancellation and all loaders during the cancelation were loading images or XHR."); else - rejectReasons |= 1 << MainDocumentError; -#endif + isCacheable = false; } - if (frame->loader().documentLoader()->substituteData().isValid() && frame->loader().documentLoader()->substituteData().failingURL().isEmpty()) { + // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs). + if (documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty()) { PCLOG(" -Frame is an error page"); - rejectReasons |= 1 << IsErrorPage; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isErrorPageKey()); + isCacheable = false; } - if (frame->loader().subframeLoader().containsPlugins() && !frame->page()->settings().pageCacheSupportsPlugins()) { + if (frameLoader.subframeLoader().containsPlugins() && !frame.page()->settings().pageCacheSupportsPlugins()) { PCLOG(" -Frame contains plugins"); - rejectReasons |= 1 << HasPlugins; - } - if (frame->document()->url().protocolIs("https") - && (frame->loader().documentLoader()->response().cacheControlContainsNoCache() - || frame->loader().documentLoader()->response().cacheControlContainsNoStore())) { - PCLOG(" -Frame is HTTPS, and cache control prohibits caching or storing"); - rejectReasons |= 1 << IsHttpsAndCacheControlled; - } - if (frame->document()->domWindow() && frame->document()->domWindow()->hasEventListeners(eventNames().unloadEvent)) { - PCLOG(" -Frame has an unload event listener"); -#if !PLATFORM(IOS) - rejectReasons |= 1 << HasUnloadListener; -#else - // iOS allows pages with unload event listeners to enter the page cache. - PCLOG(" -BUT iOS allows these pages to be cached."); -#endif - } -#if ENABLE(SQL_DATABASE) - if (DatabaseManager::manager().hasOpenDatabases(frame->document())) { - PCLOG(" -Frame has open database handles"); - rejectReasons |= 1 << HasDatabaseHandles; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::hasPluginsKey()); + isCacheable = false; } -#endif -#if ENABLE(SHARED_WORKERS) - if (SharedWorkerRepository::hasSharedWorkers(frame->document())) { - PCLOG(" -Frame has associated SharedWorkers"); - rejectReasons |= 1 << HasSharedWorkers; + if (frame.isMainFrame() && frame.document() && frame.document()->url().protocolIs("https") && documentLoader->response().cacheControlContainsNoStore()) { + PCLOG(" -Frame is HTTPS, and cache control prohibits storing"); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::httpsNoStoreKey()); + isCacheable = false; } -#endif - if (!frame->loader().history().currentItem()) { - PCLOG(" -No current history item"); - rejectReasons |= 1 << NoHistoryItem; + if (frame.isMainFrame() && !frameLoader.history().currentItem()) { + PCLOG(" -Main frame has no current history item"); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noCurrentHistoryItemKey()); + isCacheable = false; } - if (frame->loader().quickRedirectComing()) { + if (frameLoader.quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); - rejectReasons |= 1 << QuickRedirectComing; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::quirkRedirectComingKey()); + isCacheable = false; } - if (frame->loader().documentLoader()->isLoadingInAPISense()) { - PCLOG(" -DocumentLoader is still loading in API sense"); - rejectReasons |= 1 << IsLoadingInAPISense; + if (documentLoader->isLoading()) { + PCLOG(" -DocumentLoader is still loading"); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isLoadingKey()); + isCacheable = false; } - if (frame->loader().documentLoader()->isStopping()) { + if (documentLoader->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); - rejectReasons |= 1 << IsStopping; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::documentLoaderStoppingKey()); + isCacheable = false; } - if (!frame->document()->canSuspendActiveDOMObjects()) { - PCLOG(" -The document cannot suspect its active DOM Objects"); - rejectReasons |= 1 << CannotSuspendActiveDOMObjects; + + Vector<ActiveDOMObject*> unsuspendableObjects; + if (frame.document() && !frame.document()->canSuspendActiveDOMObjectsForDocumentSuspension(&unsuspendableObjects)) { + PCLOG(" -The document cannot suspend its active DOM Objects"); + for (auto* activeDOMObject : unsuspendableObjects) { + PCLOG(" - Unsuspendable: ", activeDOMObject->activeDOMObjectName()); + diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::unsuspendableDOMObjectKey(), activeDOMObject->activeDOMObjectName(), ShouldSample::Yes); + UNUSED_PARAM(activeDOMObject); + } + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::cannotSuspendActiveDOMObjectsKey()); + isCacheable = false; } - if (!frame->loader().documentLoader()->applicationCacheHost()->canCacheInPageCache()) { + // FIXME: We should investigating caching frames that have an associated + // application cache. <rdar://problem/5917899> tracks that work. + if (!documentLoader->applicationCacheHost().canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); - rejectReasons |= 1 << DocumentLoaderUsesApplicationCache; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::applicationCacheKey()); + isCacheable = false; } - if (!frame->loader().client().canCachePage()) { + if (!frameLoader.client().canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); - rejectReasons |= 1 << ClientDeniesCaching; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deniedByClientKey()); + isCacheable = false; } - HistogramSupport::histogramEnumeration("PageCache.FrameCacheable", !rejectReasons, 2); - int reasonCount = 0; - for (int i = 0; i < NumberOfReasonsFramesCannotBeInPageCache; ++i) { - if (rejectReasons & (1 << i)) { - ++reasonCount; - HistogramSupport::histogramEnumeration("PageCache.FrameRejectReason", i, NumberOfReasonsFramesCannotBeInPageCache); - } + for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) { + if (!canCacheFrame(*child, diagnosticLoggingClient, indentLevel + 1)) + isCacheable = false; } - HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonCount", reasonCount, 1 + NumberOfReasonsFramesCannotBeInPageCache); - - for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) - rejectReasons |= logCanCacheFrameDecision(child, indentLevel + 1); - PCLOG(rejectReasons ? " Frame CANNOT be cached" : " Frame CAN be cached"); + PCLOG(isCacheable ? " Frame CAN be cached" : " Frame CANNOT be cached"); PCLOG("+---"); - return rejectReasons; + return isCacheable; } -// Used in histograms, please only add at the end, and do not remove elements (renaming e.g. to "FooEnumUnused1" is fine). -// This is because statistics may be gathered from histograms between versions over time, and re-using values causes collisions. -enum ReasonPageCannotBeInPageCache { - FrameCannotBeInPageCache = 0, - DisabledBackForwardList, - DisabledPageCache, - UsesDeviceMotion, - UsesDeviceOrientation, - IsReload, - IsReloadFromOrigin, - IsSameLoad, - NumberOfReasonsPagesCannotBeInPageCache, -}; -COMPILE_ASSERT(NumberOfReasonsPagesCannotBeInPageCache <= sizeof(unsigned)*8, ReasonPageCannotBeInPageCacheDoesNotFitInBitmap); - -static void logCanCachePageDecision(Page* page) +static bool canCachePage(Page& page) { - // Only bother logging for main frames that have actually loaded and have content. - if (page->mainFrame().loader().stateMachine()->creatingInitialEmptyDocument()) - return; - URL currentURL = page->mainFrame().loader().documentLoader() ? page->mainFrame().loader().documentLoader()->url() : URL(); - if (currentURL.isEmpty()) - return; - - int indentLevel = 0; + unsigned indentLevel = 0; PCLOG("--------\n Determining if page can be cached:"); + + DiagnosticLoggingClient& diagnosticLoggingClient = page.diagnosticLoggingClient(); + bool isCacheable = canCacheFrame(page.mainFrame(), diagnosticLoggingClient, indentLevel + 1); - unsigned rejectReasons = 0; - unsigned frameRejectReasons = logCanCacheFrameDecision(&page->mainFrame(), indentLevel+1); - if (frameRejectReasons) - rejectReasons |= 1 << FrameCannotBeInPageCache; - - if (!page->settings().usesPageCache()) { + if (!page.settings().usesPageCache() || page.isResourceCachingDisabled()) { PCLOG(" -Page settings says b/f cache disabled"); - rejectReasons |= 1 << DisabledPageCache; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isDisabledKey()); + isCacheable = false; } #if ENABLE(DEVICE_ORIENTATION) && !PLATFORM(IOS) - if (DeviceMotionController::isActiveAt(page)) { + if (DeviceMotionController::isActiveAt(&page)) { PCLOG(" -Page is using DeviceMotion"); - rejectReasons |= 1 << UsesDeviceMotion; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deviceMotionKey()); + isCacheable = false; } - if (DeviceOrientationController::isActiveAt(page)) { + if (DeviceOrientationController::isActiveAt(&page)) { PCLOG(" -Page is using DeviceOrientation"); - rejectReasons |= 1 << UsesDeviceOrientation; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deviceOrientationKey()); + isCacheable = false; } #endif #if ENABLE(PROXIMITY_EVENTS) if (DeviceProximityController::isActiveAt(page)) { PCLOG(" -Page is using DeviceProximity"); - rejectReasons |= 1 << UsesDeviceMotion; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, deviceProximityKey); + isCacheable = false; } #endif - FrameLoadType loadType = page->mainFrame().loader().loadType(); - if (loadType == FrameLoadTypeReload) { + FrameLoadType loadType = page.mainFrame().loader().loadType(); + switch (loadType) { + case FrameLoadType::Reload: + // No point writing to the cache on a reload, since we will just write over it again when we leave that page. PCLOG(" -Load type is: Reload"); - rejectReasons |= 1 << IsReload; - } - if (loadType == FrameLoadTypeReloadFromOrigin) { - PCLOG(" -Load type is: Reload from origin"); - rejectReasons |= 1 << IsReloadFromOrigin; - } - if (loadType == FrameLoadTypeSame) { + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::reloadKey()); + isCacheable = false; + break; + case FrameLoadType::Same: // user loads same URL again (but not reload button) + // No point writing to the cache on a same load, since we will just write over it again when we leave that page. PCLOG(" -Load type is: Same"); - rejectReasons |= 1 << IsSameLoad; + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::sameLoadKey()); + isCacheable = false; + break; + case FrameLoadType::RedirectWithLockedBackForwardList: + // Don't write to the cache if in the middle of a redirect, since we will want to store the final page we end up on. + PCLOG(" -Load type is: RedirectWithLockedBackForwardList"); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::redirectKey()); + isCacheable = false; + break; + case FrameLoadType::Replace: + // No point writing to the cache on a replace, since we will just write over it again when we leave that page. + PCLOG(" -Load type is: Replace"); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::replaceKey()); + isCacheable = false; + break; + case FrameLoadType::ReloadFromOrigin: { + // No point writing to the cache on a reload, since we will just write over it again when we leave that page. + PCLOG(" -Load type is: ReloadFromOrigin"); + logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::reloadFromOriginKey()); + isCacheable = false; + break; + } + case FrameLoadType::Standard: + case FrameLoadType::Back: + case FrameLoadType::Forward: + case FrameLoadType::IndexedBackForward: // a multi-item hop in the backforward list + // Cacheable. + break; } - PCLOG(rejectReasons ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------"); - - HistogramSupport::histogramEnumeration("PageCache.PageCacheable", !rejectReasons, 2); - int reasonCount = 0; - for (int i = 0; i < NumberOfReasonsPagesCannotBeInPageCache; ++i) { - if (rejectReasons & (1 << i)) { - ++reasonCount; - HistogramSupport::histogramEnumeration("PageCache.PageRejectReason", i, NumberOfReasonsPagesCannotBeInPageCache); - } - } - HistogramSupport::histogramEnumeration("PageCache.PageRejectReasonCount", reasonCount, 1 + NumberOfReasonsPagesCannotBeInPageCache); - const bool settingsDisabledPageCache = rejectReasons & (1 << DisabledPageCache); - HistogramSupport::histogramEnumeration("PageCache.PageRejectReasonCountExcludingSettings", reasonCount - settingsDisabledPageCache, NumberOfReasonsPagesCannotBeInPageCache); - - // Report also on the frame reasons by page; this is distinct from the per frame statistics since it coalesces the - // causes from all subframes together. - HistogramSupport::histogramEnumeration("PageCache.FrameCacheableByPage", !frameRejectReasons, 2); - int frameReasonCount = 0; - for (int i = 0; i <= NumberOfReasonsFramesCannotBeInPageCache; ++i) { - if (frameRejectReasons & (1 << i)) { - ++frameReasonCount; - HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonByPage", i, NumberOfReasonsFramesCannotBeInPageCache); - } - } - - HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonCountByPage", frameReasonCount, 1 + NumberOfReasonsFramesCannotBeInPageCache); -} - -#endif // !defined(NDEBUG) + if (isCacheable) + PCLOG(" Page CAN be cached\n--------"); + else + PCLOG(" Page CANNOT be cached\n--------"); -PageCache* pageCache() -{ - static PageCache* staticPageCache = new PageCache; - return staticPageCache; + diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::canCacheKey(), isCacheable ? DiagnosticLoggingResultPass : DiagnosticLoggingResultFail, ShouldSample::Yes); + return isCacheable; } -PageCache::PageCache() - : m_capacity(0) - , m_size(0) - , m_head(0) - , m_tail(0) -#if USE(ACCELERATED_COMPOSITING) - , m_shouldClearBackingStores(false) -#endif +PageCache& PageCache::singleton() { + static NeverDestroyed<PageCache> globalPageCache; + return globalPageCache; } -bool PageCache::canCachePageContainingThisFrame(Frame* frame) +bool PageCache::canCache(Page& page) const { - for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) { - if (!canCachePageContainingThisFrame(child)) - return false; - } - - FrameLoader& frameLoader = frame->loader(); - DocumentLoader* documentLoader = frameLoader.documentLoader(); - Document* document = frame->document(); - - return documentLoader -#if !PLATFORM(IOS) - && documentLoader->mainDocumentError().isNull() -#else - && (documentLoader->mainDocumentError().isNull() || (documentLoader->mainDocumentError().isCancellation() && documentLoader->subresourceLoadersArePageCacheAcceptable())) -#endif - // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs). - && !(documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty()) - && (!frameLoader.subframeLoader().containsPlugins() || frame->page()->settings().pageCacheSupportsPlugins()) - && (!document->url().protocolIs("https") || (!documentLoader->response().cacheControlContainsNoCache() && !documentLoader->response().cacheControlContainsNoStore())) -#if !PLATFORM(IOS) - && (!document->domWindow() || !document->domWindow()->hasEventListeners(eventNames().unloadEvent)) -#endif -#if ENABLE(SQL_DATABASE) - && !DatabaseManager::manager().hasOpenDatabases(document) -#endif -#if ENABLE(SHARED_WORKERS) - && !SharedWorkerRepository::hasSharedWorkers(document) -#endif - && frameLoader.history().currentItem() - && !frameLoader.quickRedirectComing() - && !documentLoader->isLoadingInAPISense() - && !documentLoader->isStopping() - && document->canSuspendActiveDOMObjects() - // FIXME: We should investigating caching frames that have an associated - // application cache. <rdar://problem/5917899> tracks that work. - && documentLoader->applicationCacheHost()->canCacheInPageCache() - && frameLoader.client().canCachePage(); -} - -bool PageCache::canCache(Page* page) const -{ - if (!page) + if (!m_maxSize) { + logPageCacheFailureDiagnosticMessage(&page, DiagnosticLoggingKeys::isDisabledKey()); return false; - -#if !defined(NDEBUG) - logCanCachePageDecision(page); -#endif + } -#if PLATFORM(IOS) - if (memoryPressureHandler().hasReceivedMemoryPressure()) + if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) { + logPageCacheFailureDiagnosticMessage(&page, DiagnosticLoggingKeys::underMemoryPressureKey()); return false; -#endif - - // Cache the page, if possible. - // Don't write to the cache if in the middle of a redirect, since we will want to - // store the final page we end up on. - // No point writing to the cache on a reload or loadSame, since we will just write - // over it again when we leave that page. - FrameLoadType loadType = page->mainFrame().loader().loadType(); + } - return m_capacity > 0 - && canCachePageContainingThisFrame(&page->mainFrame()) - && page->settings().usesPageCache() -#if ENABLE(DEVICE_ORIENTATION) && !PLATFORM(IOS) - && !DeviceMotionController::isActiveAt(page) - && !DeviceOrientationController::isActiveAt(page) -#endif -#if ENABLE(PROXIMITY_EVENTS) - && !DeviceProximityController::isActiveAt(page) -#endif - && (loadType == FrameLoadTypeStandard - || loadType == FrameLoadTypeBack - || loadType == FrameLoadTypeForward - || loadType == FrameLoadTypeIndexedBackForward); + return canCachePage(page); } -void PageCache::pruneToCapacityNow(int capacity) +void PageCache::pruneToSizeNow(unsigned size, PruningReason pruningReason) { - int savedCapacity = m_capacity; - setCapacity(capacity); - setCapacity(savedCapacity); + SetForScope<unsigned> change(m_maxSize, size); + prune(pruningReason); } -void PageCache::setCapacity(int capacity) +void PageCache::setMaxSize(unsigned maxSize) { - ASSERT(capacity >= 0); - m_capacity = std::max(capacity, 0); - - prune(); + m_maxSize = maxSize; + prune(PruningReason::None); } -int PageCache::frameCount() const +unsigned PageCache::frameCount() const { - int frameCount = 0; - for (HistoryItem* current = m_head; current; current = current->m_next) { - ++frameCount; - ASSERT(current->m_cachedPage); - frameCount += current->m_cachedPage ? current->m_cachedPage->cachedMainFrame()->descendantFrameCount() : 0; + unsigned frameCount = m_items.size(); + for (auto& item : m_items) { + ASSERT(item->m_cachedPage); + frameCount += item->m_cachedPage->cachedMainFrame()->descendantFrameCount(); } return frameCount; } -void PageCache::markPagesForVistedLinkStyleRecalc() +void PageCache::markPagesForDeviceOrPageScaleChanged(Page& page) { - for (HistoryItem* current = m_head; current; current = current->m_next) { - if (current->m_cachedPage) - current->m_cachedPage->markForVistedLinkStyleRecalc(); + for (auto& item : m_items) { + CachedPage& cachedPage = *item->m_cachedPage; + if (&page.mainFrame() == &cachedPage.cachedMainFrame()->view()->frame()) + cachedPage.markForDeviceOrPageScaleChanged(); } } -void PageCache::markPagesForFullStyleRecalc(Page* page) +void PageCache::markPagesForContentsSizeChanged(Page& page) { - for (HistoryItem* current = m_head; current; current = current->m_next) { - CachedPage* cachedPage = current->m_cachedPage.get(); - if (cachedPage && &page->mainFrame() == &cachedPage->cachedMainFrame()->view()->frame()) - cachedPage->markForFullStyleRecalc(); + for (auto& item : m_items) { + CachedPage& cachedPage = *item->m_cachedPage; + if (&page.mainFrame() == &cachedPage.cachedMainFrame()->view()->frame()) + cachedPage.markForContentsSizeChanged(); } } - -#if USE(ACCELERATED_COMPOSITING) -void PageCache::markPagesForDeviceScaleChanged(Page* page) +#if ENABLE(VIDEO_TRACK) +void PageCache::markPagesForCaptionPreferencesChanged() { - for (HistoryItem* current = m_head; current; current = current->m_next) { - CachedPage* cachedPage = current->m_cachedPage.get(); - if (cachedPage && &page->mainFrame() == &cachedPage->cachedMainFrame()->view()->frame()) - cachedPage->markForDeviceScaleChanged(); + for (auto& item : m_items) { + ASSERT(item->m_cachedPage); + item->m_cachedPage->markForCaptionPreferencesChanged(); } } #endif -#if ENABLE(VIDEO_TRACK) -void PageCache::markPagesForCaptionPreferencesChanged() +static String pruningReasonToDiagnosticLoggingKey(PruningReason pruningReason) +{ + switch (pruningReason) { + case PruningReason::MemoryPressure: + return DiagnosticLoggingKeys::prunedDueToMemoryPressureKey(); + case PruningReason::ProcessSuspended: + return DiagnosticLoggingKeys::prunedDueToProcessSuspended(); + case PruningReason::ReachedMaxSize: + return DiagnosticLoggingKeys::prunedDueToMaxSizeReached(); + case PruningReason::None: + break; + } + ASSERT_NOT_REACHED(); + return emptyString(); +} + +static void setPageCacheState(Page& page, Document::PageCacheState pageCacheState) { - for (HistoryItem* current = m_head; current; current = current->m_next) { - if (current->m_cachedPage) - current->m_cachedPage->markForCaptionPreferencesChanged(); + for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (auto* document = frame->document()) + document->setPageCacheState(pageCacheState); } } -#endif -void PageCache::add(PassRefPtr<HistoryItem> prpItem, Page& page) +// When entering page cache, tear down the render tree before setting the in-cache flag. +// This maintains the invariant that render trees are never present in the page cache. +// Note that destruction happens bottom-up so that the main frame's tree dies last. +static void destroyRenderTree(MainFrame& mainFrame) { - ASSERT(prpItem); - ASSERT(canCache(&page)); - - HistoryItem* item = prpItem.leakRef(); // Balanced in remove(). + for (Frame* frame = mainFrame.tree().traversePreviousWithWrap(true); frame; frame = frame->tree().traversePreviousWithWrap(false)) { + if (!frame->document()) + continue; + auto& document = *frame->document(); + if (document.hasLivingRenderTree()) + document.destroyRenderTree(); + } +} - // Remove stale cache entry if necessary. - if (item->m_cachedPage) - remove(item); +static void firePageHideEventRecursively(Frame& frame) +{ + auto* document = frame.document(); + if (!document) + return; - item->m_cachedPage = std::make_unique<CachedPage>(page); - addToLRUList(item); - ++m_size; - - prune(); + // stopLoading() will fire the pagehide event in each subframe and the HTML specification states + // that the parent document's ignore-opens-during-unload counter should be incremented while the + // pagehide event is being fired in its subframes: + // https://html.spec.whatwg.org/multipage/browsers.html#unload-a-document + IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(document); + + frame.loader().stopLoading(UnloadEventPolicyUnloadAndPageHide); + + for (RefPtr<Frame> child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) + firePageHideEventRecursively(*child); } -std::unique_ptr<CachedPage> PageCache::take(HistoryItem* item) +void PageCache::addIfCacheable(HistoryItem& item, Page* page) { - if (!item) - return nullptr; + if (item.isInPageCache()) + return; - std::unique_ptr<CachedPage> cachedPage = std::move(item->m_cachedPage); + if (!page || !canCache(*page)) + return; - removeFromLRUList(item); - --m_size; + setPageCacheState(*page, Document::AboutToEnterPageCache); - item->deref(); // Balanced in add(). + // Focus the main frame, defocusing a focused subframe (if we have one). We do this here, + // before the page enters the page cache, while we still can dispatch DOM blur/focus events. + if (page->focusController().focusedFrame()) + page->focusController().setFocusedFrame(&page->mainFrame()); - if (!cachedPage) - return nullptr; + // Fire the pagehide event in all frames. + firePageHideEventRecursively(page->mainFrame()); - if (cachedPage->hasExpired()) { - LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item->url().string().ascii().data()); - return nullptr; + // Check that the page is still page-cacheable after firing the pagehide event. The JS event handlers + // could have altered the page in a way that could prevent caching. + if (!canCache(*page)) { + setPageCacheState(*page, Document::NotInPageCache); + return; } - return cachedPage; -} + destroyRenderTree(page->mainFrame()); -CachedPage* PageCache::get(HistoryItem* item) -{ - if (!item) - return 0; + setPageCacheState(*page, Document::InPageCache); - if (CachedPage* cachedPage = item->m_cachedPage.get()) { - if (!cachedPage->hasExpired()) - return cachedPage; - - LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item->url().string().ascii().data()); - pageCache()->remove(item); - } - return 0; + // Make sure we no longer fire any JS events past this point. + NoEventDispatchAssertion assertNoEventDispatch; + + item.m_cachedPage = std::make_unique<CachedPage>(*page); + item.m_pruningReason = PruningReason::None; + m_items.add(&item); + + prune(PruningReason::ReachedMaxSize); } -void PageCache::remove(HistoryItem* item) +std::unique_ptr<CachedPage> PageCache::take(HistoryItem& item, Page* page) { - // Safely ignore attempts to remove items not in the cache. - if (!item || !item->m_cachedPage) - return; + if (!item.m_cachedPage) { + if (item.m_pruningReason != PruningReason::None) + logPageCacheFailureDiagnosticMessage(page, pruningReasonToDiagnosticLoggingKey(item.m_pruningReason)); + return nullptr; + } - item->m_cachedPage = nullptr; - removeFromLRUList(item); - --m_size; + m_items.remove(&item); + std::unique_ptr<CachedPage> cachedPage = WTFMove(item.m_cachedPage); - item->deref(); // Balanced in add(). + if (cachedPage->hasExpired() || (page && page->isResourceCachingDisabled())) { + LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item.url().string().ascii().data()); + logPageCacheFailureDiagnosticMessage(page, DiagnosticLoggingKeys::expiredKey()); + return nullptr; + } + + return cachedPage; } -void PageCache::prune() +void PageCache::removeAllItemsForPage(Page& page) { - while (m_size > m_capacity) { - ASSERT(m_tail && m_tail->m_cachedPage); - remove(m_tail); + for (auto it = m_items.begin(); it != m_items.end();) { + // Increment iterator first so it stays valid after the removal. + auto current = it; + ++it; + if (&(*current)->m_cachedPage->page() == &page) { + (*current)->m_cachedPage = nullptr; + m_items.remove(current); + } } } -void PageCache::addToLRUList(HistoryItem* item) +CachedPage* PageCache::get(HistoryItem& item, Page* page) { - item->m_next = m_head; - item->m_prev = 0; + CachedPage* cachedPage = item.m_cachedPage.get(); + if (!cachedPage) { + if (item.m_pruningReason != PruningReason::None) + logPageCacheFailureDiagnosticMessage(page, pruningReasonToDiagnosticLoggingKey(item.m_pruningReason)); + return nullptr; + } - if (m_head) { - ASSERT(m_tail); - m_head->m_prev = item; - } else { - ASSERT(!m_tail); - m_tail = item; + if (cachedPage->hasExpired() || (page && page->isResourceCachingDisabled())) { + LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item.url().string().ascii().data()); + logPageCacheFailureDiagnosticMessage(page, DiagnosticLoggingKeys::expiredKey()); + remove(item); + return nullptr; } + return cachedPage; +} + +void PageCache::remove(HistoryItem& item) +{ + // Safely ignore attempts to remove items not in the cache. + if (!item.m_cachedPage) + return; - m_head = item; + m_items.remove(&item); + item.m_cachedPage = nullptr; } -void PageCache::removeFromLRUList(HistoryItem* item) +void PageCache::prune(PruningReason pruningReason) { - if (!item->m_next) { - ASSERT(item == m_tail); - m_tail = item->m_prev; - } else { - ASSERT(item != m_tail); - item->m_next->m_prev = item->m_prev; - } - - if (!item->m_prev) { - ASSERT(item == m_head); - m_head = item->m_next; - } else { - ASSERT(item != m_head); - item->m_prev->m_next = item->m_next; + while (pageCount() > maxSize()) { + auto oldestItem = m_items.takeFirst(); + oldestItem->m_cachedPage = nullptr; + oldestItem->m_pruningReason = pruningReason; } } diff --git a/Source/WebCore/history/PageCache.h b/Source/WebCore/history/PageCache.h index ebab10c1e..edda9e755 100644 --- a/Source/WebCore/history/PageCache.h +++ b/Source/WebCore/history/PageCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 COMPUTER, INC. ``AS IS'' AND ANY + * 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 COMPUTER, INC. OR + * 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 @@ -23,83 +23,63 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageCache_h -#define PageCache_h +#pragma once +#include "HistoryItem.h" #include "Timer.h" #include <wtf/Forward.h> -#include <wtf/HashSet.h> +#include <wtf/ListHashSet.h> #include <wtf/Noncopyable.h> namespace WebCore { - class CachedPage; - class Frame; - class HistoryItem; - class Page; - - class PageCache { - WTF_MAKE_NONCOPYABLE(PageCache); WTF_MAKE_FAST_ALLOCATED; - public: - friend PageCache* pageCache(); - - bool canCache(Page*) const; - - void setCapacity(int); // number of pages to cache - int capacity() { return m_capacity; } - - void add(PassRefPtr<HistoryItem>, Page&); // Prunes if capacity() is exceeded. - void remove(HistoryItem*); - CachedPage* get(HistoryItem* item); - std::unique_ptr<CachedPage> take(HistoryItem*); - - int pageCount() const { return m_size; } - int frameCount() const; - - void markPagesForVistedLinkStyleRecalc(); - - // Will mark all cached pages associated with the given page as needing style recalc. - void markPagesForFullStyleRecalc(Page*); - - // Used when memory is low to prune some cached pages. - void pruneToCapacityNow(int capacity); +class CachedPage; +class Frame; +class Page; -#if ENABLE(VIDEO_TRACK) - void markPagesForCaptionPreferencesChanged(); -#endif +enum class PruningReason { None, ProcessSuspended, MemoryPressure, ReachedMaxSize }; -#if USE(ACCELERATED_COMPOSITING) - bool shouldClearBackingStores() const { return m_shouldClearBackingStores; } - void setShouldClearBackingStores(bool flag) { m_shouldClearBackingStores = flag; } - void markPagesForDeviceScaleChanged(Page*); -#endif +class PageCache { + WTF_MAKE_NONCOPYABLE(PageCache); WTF_MAKE_FAST_ALLOCATED; +public: + // Function to obtain the global page cache. + WEBCORE_EXPORT static PageCache& singleton(); + + bool canCache(Page&) const; - private: - PageCache(); // Use pageCache() instead. - ~PageCache(); // Not implemented to make sure nobody accidentally calls delete -- WebCore does not delete singletons. - - static bool canCachePageContainingThisFrame(Frame*); + // Used when memory is low to prune some cached pages. + WEBCORE_EXPORT void pruneToSizeNow(unsigned maxSize, PruningReason); + WEBCORE_EXPORT void setMaxSize(unsigned); // number of pages to cache. + unsigned maxSize() const { return m_maxSize; } - void addToLRUList(HistoryItem*); // Adds to the head of the list. - void removeFromLRUList(HistoryItem*); + void addIfCacheable(HistoryItem&, Page*); // Prunes if maxSize() is exceeded. + WEBCORE_EXPORT void remove(HistoryItem&); + CachedPage* get(HistoryItem&, Page*); + std::unique_ptr<CachedPage> take(HistoryItem&, Page*); - void prune(); + void removeAllItemsForPage(Page&); - int m_capacity; - int m_size; + unsigned pageCount() const { return m_items.size(); } + WEBCORE_EXPORT unsigned frameCount() const; - // LRU List - HistoryItem* m_head; - HistoryItem* m_tail; - -#if USE(ACCELERATED_COMPOSITING) - bool m_shouldClearBackingStores; + void markPagesForDeviceOrPageScaleChanged(Page&); + void markPagesForContentsSizeChanged(Page&); +#if ENABLE(VIDEO_TRACK) + void markPagesForCaptionPreferencesChanged(); #endif - }; - // Function to obtain the global page cache. - PageCache* pageCache(); +private: + PageCache() = default; // Use singleton() instead. + ~PageCache() = delete; // Make sure nobody accidentally calls delete -- WebCore does not delete singletons. -} // namespace WebCore + static bool canCachePageContainingThisFrame(Frame&); + + void prune(PruningReason); -#endif // PageCache_h + ListHashSet<RefPtr<HistoryItem>> m_items; + unsigned m_maxSize {0}; + + friend class WTF::NeverDestroyed<PageCache>; +}; + +} // namespace WebCore |