blob: 334157271fa9310d38ebaddb7bd65ab2bf83eea6 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_MANAGER_H_
#define CHROME_BROWSER_PRERENDER_PRERENDER_MANAGER_H_
#include <list>
#include <map>
#include <string>
#include <utility>
#include "base/gtest_prod_util.h"
#include "base/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "base/time.h"
#include "base/timer.h"
#include "chrome/browser/prerender/prerender_config.h"
#include "chrome/browser/prerender/prerender_contents.h"
#include "chrome/browser/prerender/prerender_final_status.h"
#include "chrome/browser/prerender/prerender_origin.h"
#include "chrome/browser/profiles/profile_keyed_service.h"
#include "googleurl/src/gurl.h"
class Profile;
namespace base {
class DictionaryValue;
}
namespace content {
class WebContents;
}
namespace gfx {
class Size;
}
#if defined(COMPILER_GCC)
namespace BASE_HASH_NAMESPACE {
template <>
struct hash<content::WebContents*> {
std::size_t operator()(content::WebContents* value) const {
return reinterpret_cast<std::size_t>(value);
}
};
}
#endif
namespace prerender {
class PrerenderCondition;
class PrerenderHandle;
class PrerenderHistograms;
class PrerenderHistory;
class PrerenderLocalPredictor;
class PrerenderTracker;
// PrerenderManager is responsible for initiating and keeping prerendered
// views of web pages. All methods must be called on the UI thread unless
// indicated otherwise.
class PrerenderManager : public base::SupportsWeakPtr<PrerenderManager>,
public base::NonThreadSafe,
public ProfileKeyedService {
public:
// PrerenderManagerMode is used in a UMA_HISTOGRAM, so please do not
// add in the middle.
enum PrerenderManagerMode {
PRERENDER_MODE_DISABLED,
PRERENDER_MODE_ENABLED,
PRERENDER_MODE_EXPERIMENT_CONTROL_GROUP,
PRERENDER_MODE_EXPERIMENT_PRERENDER_GROUP,
PRERENDER_MODE_EXPERIMENT_5MIN_TTL_GROUP,
PRERENDER_MODE_EXPERIMENT_NO_USE_GROUP,
PRERENDER_MODE_MAX
};
// One or more of these flags must be passed to ClearData() to specify just
// what data to clear. See function declaration for more information.
enum ClearFlags {
CLEAR_PRERENDER_CONTENTS = 0x1 << 0,
CLEAR_PRERENDER_HISTORY = 0x1 << 1,
CLEAR_MAX = 0x1 << 2
};
// ID indicating that no experiment is active.
static const uint8 kNoExperiment = 0;
// Owned by a Profile object for the lifetime of the profile.
PrerenderManager(Profile* profile, PrerenderTracker* prerender_tracker);
virtual ~PrerenderManager();
// ProfileKeyedService implementation.
virtual void Shutdown() OVERRIDE;
// Entry points for adding prerenders.
// Adds a prerender for |url| if valid. |process_id| and |route_id| identify
// the RenderView that the prerender request came from. If |size| is empty, a
// default from the PrerenderConfig is used. Returns a caller-owned
// PrerenderHandle* if the URL was added, NULL if it was not. If the launching
// RenderView is itself prerendering, the prerender is added as a pending
// prerender.
PrerenderHandle* AddPrerenderFromLinkRelPrerender(
int process_id,
int route_id,
const GURL& url,
const content::Referrer& referrer,
const gfx::Size& size);
// Adds a prerender for |url| if valid. As the prerender request is coming
// from a source without a RenderViewHost (i.e., the omnibox) we don't have a
// child or route id, or a referrer. This method uses sensible values for
// those. The |session_storage_namespace| matches the namespace of the active
// tab at the time the prerender is generated from the omnibox. Returns a
// caller-owned PrerenderHandle*, or NULL.
PrerenderHandle* AddPrerenderFromOmnibox(
const GURL& url,
content::SessionStorageNamespace* session_storage_namespace,
const gfx::Size& size);
// If |process_id| and |view_id| refer to a running prerender, destroy
// it with |final_status|.
virtual void DestroyPrerenderForRenderView(int process_id,
int view_id,
FinalStatus final_status);
// Cancels all active prerenders.
void CancelAllPrerenders();
// If |url| matches a valid prerendered page, try to swap it into
// |web_contents| and merge browsing histories. Returns |true| if a
// prerendered page is swapped in, |false| otherwise.
bool MaybeUsePrerenderedPage(content::WebContents* web_contents,
const GURL& url);
// Moves a PrerenderContents to the pending delete list from the list of
// active prerenders when prerendering should be cancelled.
void MoveEntryToPendingDelete(PrerenderContents* entry,
FinalStatus final_status);
// Records the perceived page load time for a page - effectively the time from
// when the user navigates to a page to when it finishes loading. The actual
// load may have started prior to navigation due to prerender hints.
// This must be called on the UI thread.
// |fraction_plt_elapsed_at_swap_in| must either be in [0.0, 1.0], or a value
// outside that range indicating that it doesn't apply.
static void RecordPerceivedPageLoadTime(
base::TimeDelta perceived_page_load_time,
double fraction_plt_elapsed_at_swap_in,
content::WebContents* web_contents,
const GURL& url);
// Set whether prerendering is currently enabled for this manager.
// Must be called on the UI thread.
// If |enabled| is false, existing prerendered pages will still persist until
// they time out, but new ones will not be generated.
void set_enabled(bool enabled);
// Controls if we launch or squash prefetch requests as they arrive from
// renderers.
static bool IsPrefetchEnabled();
static void SetIsPrefetchEnabled(bool enabled);
static PrerenderManagerMode GetMode();
static void SetMode(PrerenderManagerMode mode);
static const char* GetModeString();
static bool IsPrerenderingPossible();
static bool ActuallyPrerendering();
static bool IsControlGroup();
static bool IsNoUseGroup();
// Query the list of current prerender pages to see if the given web contents
// is prerendering a page.
bool IsWebContentsPrerendering(content::WebContents* web_contents) const;
// Maintaining and querying the set of WebContents belonging to this
// PrerenderManager that are currently showing prerendered pages.
void MarkWebContentsAsPrerendered(content::WebContents* web_contents);
void MarkWebContentsAsWouldBePrerendered(content::WebContents* web_contents);
void MarkWebContentsAsNotPrerendered(content::WebContents* web_contents);
bool IsWebContentsPrerendered(content::WebContents* web_contents) const;
bool WouldWebContentsBePrerendered(content::WebContents* web_contents) const;
// Checks whether |url| has been recently navigated to.
bool HasRecentlyBeenNavigatedTo(const GURL& url);
// Returns true if the method given is invalid for prerendering.
static bool IsValidHttpMethod(const std::string& method);
// Returns a Value object containing the active pages being prerendered, and
// a history of pages which were prerendered. The caller is responsible for
// deleting the return value.
base::DictionaryValue* GetAsValue() const;
// Clears the data indicated by which bits of clear_flags are set.
//
// If the CLEAR_PRERENDER_CONTENTS bit is set, all active prerenders are
// cancelled and then deleted, and any WebContents queued for destruction are
// destroyed as well.
//
// If the CLEAR_PRERENDER_HISTORY bit is set, the prerender history is
// cleared, including any entries newly created by destroying them in
// response to the CLEAR_PRERENDER_CONTENTS flag.
//
// Intended to be used when clearing the cache or history.
void ClearData(int clear_flags);
// Record a final status of a prerendered page in a histogram.
// This variation allows specifying whether prerendering had been started
// (necessary to flag MatchComplete dummies).
void RecordFinalStatusWithMatchCompleteStatus(
Origin origin,
uint8 experiment_id,
PrerenderContents::MatchCompleteStatus mc_status,
FinalStatus final_status) const;
const Config& config() const { return config_; }
Config& mutable_config() { return config_; }
PrerenderTracker* prerender_tracker() { return prerender_tracker_; }
// Adds a condition. This is owned by the PrerenderManager.
void AddCondition(const PrerenderCondition* condition);
// Records that some visible tab navigated (or was redirected) to the
// provided URL.
void RecordNavigation(const GURL& url);
Profile* profile() const { return profile_; }
PrerenderHistograms* histograms() const { return histograms_.get(); }
protected:
class PrerenderData : public base::SupportsWeakPtr<PrerenderData> {
public:
// Constructor for a pending prerender, which will get its contents later.
explicit PrerenderData(PrerenderManager* manager);
// Constructor for an active prerender.
PrerenderData(PrerenderManager* manager, PrerenderContents* contents);
~PrerenderData();
// A new PrerenderHandle has been created for this PrerenderData.
void OnNewHandle();
// The launcher associated with a handle is navigating away from the context
// that launched this prerender. If the prerender is active, it may stay
// alive briefly though, in case we we going through a redirect chain that
// will eventually land at it.
void OnNavigateAwayByHandle();
// The launcher associated with a handle has taken explicit action to cancel
// this prerender. We may well destroy the prerender in this case if no
// other handles continue to track it.
void OnCancelByHandle();
PrerenderContents* contents() { return contents_; }
private:
friend class PrerenderManager;
PrerenderManager* manager_;
PrerenderContents* contents_;
// The number of distinct PrerenderHandles created for |this|, including
// ones that have called PrerenderData::OnNavigateAwayByHandle(), but not
// counting the ones that have called PrerenderData::OnCancelByHandle(). For
// pending prerenders, this will always be 1, since the PrerenderManager
// only merges handles of running prerenders.
int handle_count_;
DISALLOW_COPY_AND_ASSIGN(PrerenderData);
};
void SetPrerenderContentsFactory(
PrerenderContents::Factory* prerender_contents_factory);
// Adds a prerender from a pending Prerender, called by
// PrerenderContents::StartPendingPrerenders.
void StartPendingPrerender(
PrerenderHandle* existing_prerender_handle,
Origin origin,
int process_id,
const GURL& url,
const content::Referrer& referrer,
const gfx::Size& size,
content::SessionStorageNamespace* session_storage_namespace);
void DestroyPendingPrerenderData(PrerenderData* pending_prerender_data);
// Utility method that is called from the virtual Shutdown method on this
// class but is called directly from the TestPrerenderManager in the unit
// tests.
void DoShutdown();
private:
friend class PrerenderBrowserTest;
friend class PrerenderContents;
friend class PrerenderHandle;
friend class UnitTestPrerenderManager;
class OnCloseTabContentsDeleter;
struct NavigationRecord;
typedef base::hash_map<content::WebContents*, bool> WouldBePrerenderedMap;
// Time window for which we record old navigations, in milliseconds.
static const int kNavigationRecordWindowMs = 5000;
void OnCancelPrerenderHandle(PrerenderData* prerender_data);
// Adds a prerender for |url| from |referrer| initiated from the process
// |child_id|. The |origin| specifies how the prerender was added. If |size|
// is empty, then PrerenderContents::StartPrerendering will instead use a
// default from PrerenderConfig. Returns a PrerenderHandle*, owned by the
// caller, or NULL.
PrerenderHandle* AddPrerender(
Origin origin,
int child_id,
const GURL& url,
const content::Referrer& referrer,
const gfx::Size& size,
content::SessionStorageNamespace* session_storage_namespace);
void StartSchedulingPeriodicCleanups();
void StopSchedulingPeriodicCleanups();
void EvictOldestPrerendersIfNecessary();
// Deletes stale and cancelled prerendered PrerenderContents, as well as
// WebContents that have been replaced by prerendered WebContents.
// Also identifies and kills PrerenderContents that use too much
// resources.
void PeriodicCleanup();
// Posts a task to call PeriodicCleanup. Results in quicker destruction of
// objects. If |this| is deleted before the task is run, the task will
// automatically be cancelled.
void PostCleanupTask();
base::TimeDelta GetMaxAge() const;
bool IsPrerenderFresh(base::TimeTicks start) const;
void DeleteOldEntries();
virtual base::Time GetCurrentTime() const;
virtual base::TimeTicks GetCurrentTimeTicks() const;
virtual PrerenderContents* CreatePrerenderContents(
const GURL& url,
const content::Referrer& referrer,
Origin origin,
uint8 experiment_id);
// Deletes any PrerenderContents that have been added to the pending delete
// list.
void DeletePendingDeleteEntries();
// Finds the active PrerenderData object for a running prerender matching
// |url| and |session_storage_namespace|.
PrerenderData* FindPrerenderData(
const GURL& url,
const content::SessionStorageNamespace* session_storage_namespace);
// If |child_id| and |route_id| correspond to a RenderView that is an active
// prerender, returns the PrerenderData object for that prerender. Otherwise,
// returns NULL.
PrerenderData* FindPrerenderDataForChildAndRoute(int child_id, int route_id);
// Given the |prerender_contents|, find the iterator in active_prerender_list_
// correponding to the given prerender.
std::list<linked_ptr<PrerenderData> >::iterator
FindIteratorForPrerenderContents(PrerenderContents* prerender_contents);
bool DoesRateLimitAllowPrerender() const;
// Deletes old WebContents that have been replaced by prerendered ones. This
// is needed because they're replaced in a callback from the old WebContents,
// so cannot immediately be deleted.
void DeleteOldTabContents();
// Cleans up old NavigationRecord's.
void CleanUpOldNavigations();
// Arrange for the given tab contents to be deleted asap. If deleter is not
// NULL, deletes that as well.
void ScheduleDeleteOldTabContents(TabContents* tab,
OnCloseTabContentsDeleter* deleter);
// Adds to the history list.
void AddToHistory(PrerenderContents* contents);
// Returns a new Value representing the pages currently being prerendered. The
// caller is responsible for delete'ing the return value.
base::Value* GetActivePrerendersAsValue() const;
// Destroys all pending prerenders using FinalStatus. Also deletes them as
// well as any swapped out WebContents queued for destruction.
// Used both on destruction, and when clearing the browsing history.
void DestroyAllContents(FinalStatus final_status);
// Helper function to destroy a PrerenderContents with the specified
// final_status, while at the same time recording that for the MatchComplete
// case, that this prerender would have been used.
void DestroyAndMarkMatchCompleteAsUsed(PrerenderContents* prerender_contents,
FinalStatus final_status);
// Record a final status of a prerendered page in a histogram.
// This is a helper function which will ultimately call
// RecordFinalStatusWthMatchCompleteStatus, using MATCH_COMPLETE_DEFAULT.
void RecordFinalStatus(Origin origin,
uint8 experiment_id,
FinalStatus final_status) const;
// Returns whether prerendering is currently enabled for this manager.
// Must be called on the UI thread.
bool IsEnabled() const;
// The configuration.
Config config_;
// Specifies whether prerendering is currently enabled for this
// manager. The value can change dynamically during the lifetime
// of the PrerenderManager.
bool enabled_;
static bool is_prefetch_enabled_;
// The profile that owns this PrerenderManager.
Profile* profile_;
PrerenderTracker* prerender_tracker_;
// List of all running prerenders. It is kept sorted, in increasing order by
// expiry time. This list owns the PrerenderData objects contained in it.
std::list<linked_ptr<PrerenderData> > active_prerender_list_;
// List of all pending prerenders.
std::list<linked_ptr<PrerenderData> > pending_prerender_list_;
// List of recent navigations in this profile, sorted by ascending
// navigate_time_.
std::list<NavigationRecord> navigations_;
// List of prerender elements to be deleted
std::list<PrerenderContents*> pending_delete_list_;
// Set of WebContents which are currently displaying a prerendered page.
base::hash_set<content::WebContents*> prerendered_tab_contents_set_;
// WebContents that would have been swapped out for a prerendered WebContents
// if the user was not part of the control group for measurement. When the
// WebContents gets a provisional load, the WebContents is removed from
// the map since the new navigation would not have swapped in a prerender.
// However, one complication exists because the first provisional load after
// the WebContents is marked as "Would Have Been Prerendered" is actually to
// the prerendered location. So, we need to keep a boolean around that does
// not clear the item from the map on the first provisional load, but does
// for subsequent loads.
WouldBePrerenderedMap would_be_prerendered_map_;
scoped_ptr<PrerenderContents::Factory> prerender_contents_factory_;
static PrerenderManagerMode mode_;
// A count of how many prerenders we do per session. Initialized to 0 then
// incremented and emitted to a histogram on each successful prerender.
static int prerenders_per_session_count_;
// RepeatingTimer to perform periodic cleanups of pending prerendered
// pages.
base::RepeatingTimer<PrerenderManager> repeating_timer_;
// Track time of last prerender to limit prerender spam.
base::TimeTicks last_prerender_start_time_;
std::list<TabContents*> old_tab_contents_list_;
// Cancels pending tasks on deletion.
base::WeakPtrFactory<PrerenderManager> weak_factory_;
ScopedVector<OnCloseTabContentsDeleter> on_close_tab_contents_deleters_;
scoped_ptr<PrerenderHistory> prerender_history_;
std::list<const PrerenderCondition*> prerender_conditions_;
scoped_ptr<PrerenderHistograms> histograms_;
scoped_ptr<PrerenderLocalPredictor> local_predictor_;
DISALLOW_COPY_AND_ASSIGN(PrerenderManager);
};
PrerenderManager* FindPrerenderManagerUsingRenderProcessId(
int render_process_id);
} // namespace prerender
#endif // CHROME_BROWSER_PRERENDER_PRERENDER_MANAGER_H_