Changeset 195605 in webkit


Ignore:
Timestamp:
Jan 26, 2016 11:57:49 AM (8 years ago)
Author:
Chris Dumez
Message:

Make sure a page is still PageCache-able after firing the 'pagehide' events
https://bugs.webkit.org/show_bug.cgi?id=153449

Reviewed by Andreas Kling.

Make sure a page is still PageCache-able after firing the 'pagehide'
events and abort if it isn't. This should improve robustness and it is
easy for pagehide event handlers to do things that would make a Page no
longer PageCache-able and this leads to bugs that are difficult to
investigate.

To achieve this, the 'pagehide' event firing logic was moved out of the
CachedFrame constructor. It now happens earlier in
PageCache::addIfCacheable() after checking if the page is cacheable and
before constructing the CachedPage / CachedFrames. After firing the
'pagehide' event in PageCache::addIfCacheable(), we check again that
the page is still cacheable and we abort early if it is not.

  • history/CachedFrame.cpp:

(WebCore::CachedFrame::CachedFrame):

  • history/PageCache.cpp:

(WebCore::setInPageCache):
(WebCore::firePageHideEventRecursively):
(WebCore::PageCache::addIfCacheable):

  • history/PageCache.h:
  • loader/FrameLoader.cpp:

(WebCore::FrameLoader::commitProvisionalLoad):

Location:
trunk/Source/WebCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r195603 r195605  
     12016-01-26  Chris Dumez  <cdumez@apple.com>
     2
     3        Make sure a page is still PageCache-able after firing the 'pagehide' events
     4        https://bugs.webkit.org/show_bug.cgi?id=153449
     5
     6        Reviewed by Andreas Kling.
     7
     8        Make sure a page is still PageCache-able after firing the 'pagehide'
     9        events and abort if it isn't. This should improve robustness and it is
     10        easy for pagehide event handlers to do things that would make a Page no
     11        longer PageCache-able and this leads to bugs that are difficult to
     12        investigate.
     13
     14        To achieve this, the 'pagehide' event firing logic was moved out of the
     15        CachedFrame constructor. It now happens earlier in
     16        PageCache::addIfCacheable() after checking if the page is cacheable and
     17        before constructing the CachedPage / CachedFrames. After firing the
     18        'pagehide' event in PageCache::addIfCacheable(), we check again that
     19        the page is still cacheable and we abort early if it is not.
     20
     21        * history/CachedFrame.cpp:
     22        (WebCore::CachedFrame::CachedFrame):
     23        * history/PageCache.cpp:
     24        (WebCore::setInPageCache):
     25        (WebCore::firePageHideEventRecursively):
     26        (WebCore::PageCache::addIfCacheable):
     27        * history/PageCache.h:
     28        * loader/FrameLoader.cpp:
     29        (WebCore::FrameLoader::commitProvisionalLoad):
     30
    1312016-01-26  Beth Dakin  <bdakin@apple.com>
    232
  • trunk/Source/WebCore/history/CachedFrame.cpp

    r195496 r195605  
    4040#include "HistoryController.h"
    4141#include "HistoryItem.h"
    42 #include "IgnoreOpensDuringUnloadCountIncrementer.h"
    4342#include "Logging.h"
    4443#include "MainFrame.h"
     
    156155    m_view->detachCustomScrollbars();
    157156
    158     m_document->setInPageCache(true);
    159     frame.loader().stopLoading(UnloadEventPolicyUnloadAndPageHide);
    160 
    161     {
    162         // The following will fire the pagehide event in each subframe and the HTML specification states
    163         // that the parent document's ignore-opens-during-unload counter should be incremented while the
    164         // pagehide event is being fired in its subframes:
    165         // https://html.spec.whatwg.org/multipage/browsers.html#unload-a-document
    166         IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_document.get());
    167 
    168         // Create the CachedFrames for all Frames in the FrameTree.
    169         for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling())
    170             m_childFrames.append(std::make_unique<CachedFrame>(*child));
    171     }
    172 
    173     // Active DOM objects must be suspended before we cache the frame script data,
    174     // but after we've fired the pagehide event, in case that creates more objects.
    175     // Suspending must also happen after we've recursed over child frames, in case
    176     // those create more objects.
    177 
     157    ASSERT(m_document->inPageCache());
     158
     159    // Create the CachedFrames for all Frames in the FrameTree.
     160    for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling())
     161        m_childFrames.append(std::make_unique<CachedFrame>(*child));
     162
     163    // Active DOM objects must be suspended before we cache the frame script data.
    178164    m_document->suspend();
    179165
  • trunk/Source/WebCore/history/PageCache.cpp

    r195601 r195605  
    4242#include "FrameView.h"
    4343#include "HistoryController.h"
     44#include "IgnoreOpensDuringUnloadCountIncrementer.h"
    4445#include "Logging.h"
    4546#include "MainFrame.h"
     
    273274}
    274275   
    275 bool PageCache::canCache(Page* page) const
    276 {
    277     if (!page)
     276bool PageCache::canCache(Page& page) const
     277{
     278    if (!m_maxSize) {
     279        logPageCacheFailureDiagnosticMessage(&page, DiagnosticLoggingKeys::isDisabledKey());
    278280        return false;
    279 
    280     if (!m_maxSize) {
    281         logPageCacheFailureDiagnosticMessage(page, DiagnosticLoggingKeys::isDisabledKey());
     281    }
     282
     283    if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
     284        logPageCacheFailureDiagnosticMessage(&page, DiagnosticLoggingKeys::underMemoryPressureKey());
    282285        return false;
    283286    }
    284 
    285     if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
    286         logPageCacheFailureDiagnosticMessage(page, DiagnosticLoggingKeys::underMemoryPressureKey());
    287         return false;
    288     }
    289    
    290     return canCachePage(*page);
     287   
     288    return canCachePage(page);
    291289}
    292290
     
    375373}
    376374
    377 void PageCache::add(HistoryItem& item, Page& page)
    378 {
    379     ASSERT(canCache(&page));
    380 
    381     // Remove stale cache entry if necessary.
    382     remove(item);
    383 
    384     item.m_cachedPage = std::make_unique<CachedPage>(page);
     375static void setInPageCache(Page& page, bool isInPageCache)
     376{
     377    for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
     378        if (auto* document = frame->document())
     379            document->setInPageCache(isInPageCache);
     380    }
     381}
     382
     383static void firePageHideEventRecursively(Frame& frame)
     384{
     385    auto* document = frame.document();
     386    if (!document)
     387        return;
     388
     389    // stopLoading() will fire the pagehide event in each subframe and the HTML specification states
     390    // that the parent document's ignore-opens-during-unload counter should be incremented while the
     391    // pagehide event is being fired in its subframes:
     392    // https://html.spec.whatwg.org/multipage/browsers.html#unload-a-document
     393    IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(document);
     394
     395    frame.loader().stopLoading(UnloadEventPolicyUnloadAndPageHide);
     396
     397    for (RefPtr<Frame> child = frame.tree().firstChild(); child; child = child->tree().nextSibling())
     398        firePageHideEventRecursively(*child);
     399}
     400
     401void PageCache::addIfCacheable(HistoryItem& item, Page* page)
     402{
     403    if (item.isInPageCache())
     404        return;
     405
     406    if (!page || !canCache(*page))
     407        return;
     408
     409    // Make sure all the documents know they are being added to the PageCache.
     410    setInPageCache(*page, true);
     411
     412    // Fire the pagehide event in all frames.
     413    firePageHideEventRecursively(page->mainFrame());
     414
     415    // Check that the page is still page-cacheable after firing the pagehide event. The JS event handlers
     416    // could have altered the page in a way that could prevent caching.
     417    if (!canCache(*page)) {
     418        setInPageCache(*page, false);
     419        return;
     420    }
     421
     422    // Make sure we no longer fire any JS events past this point.
     423    NoEventDispatchAssertion assertNoEventDispatch;
     424
     425    item.m_cachedPage = std::make_unique<CachedPage>(*page);
    385426    item.m_pruningReason = PruningReason::None;
    386427    m_items.add(&item);
  • trunk/Source/WebCore/history/PageCache.h

    r184304 r195605  
    4747    WEBCORE_EXPORT static PageCache& singleton();
    4848
    49     bool canCache(Page*) const;
     49    bool canCache(Page&) const;
    5050
    5151    // Used when memory is low to prune some cached pages.
     
    5454    unsigned maxSize() const { return m_maxSize; }
    5555
    56     void add(HistoryItem&, Page&); // Prunes if maxSize() is exceeded.
     56    void addIfCacheable(HistoryItem&, Page*); // Prunes if maxSize() is exceeded.
    5757    WEBCORE_EXPORT void remove(HistoryItem&);
    5858    CachedPage* get(HistoryItem&, Page*);
  • trunk/Source/WebCore/loader/FrameLoader.cpp

    r195496 r195605  
    17731773    // Check to see if we need to cache the page we are navigating away from into the back/forward cache.
    17741774    // We are doing this here because we know for sure that a new page is about to be loaded.
    1775     HistoryItem& item = *history().currentItem();
    1776     if (!m_frame.tree().parent() && PageCache::singleton().canCache(m_frame.page()) && !item.isInPageCache())
    1777         PageCache::singleton().add(item, *m_frame.page());
     1775    if (!m_frame.tree().parent() && history().currentItem())
     1776        PageCache::singleton().addIfCacheable(*history().currentItem(), m_frame.page());
    17781777
    17791778    if (m_loadType != FrameLoadType::Replace)
  • trunk/Source/WebCore/page/Page.cpp

    r195449 r195605  
    18641864bool Page::canTabSuspend()
    18651865{
    1866     return s_tabSuspensionIsEnabled && !m_isPrerender && (m_pageThrottler.activityState() == PageActivityState::NoFlags) && PageCache::singleton().canCache(this);
     1866    return s_tabSuspensionIsEnabled && !m_isPrerender && (m_pageThrottler.activityState() == PageActivityState::NoFlags) && PageCache::singleton().canCache(*this);
    18671867}
    18681868
Note: See TracChangeset for help on using the changeset viewer.