diff options
author | tburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 20:19:26 +0000 |
---|---|---|
committer | tburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 20:19:26 +0000 |
commit | ba9f8fbb8beef7b261b82a6466dea8176af7d684 (patch) | |
tree | 59dc22bfbebd8e94c303e2ba9d797fcddef71fc6 /chrome/browser/prerender | |
parent | a55edc4733a6e13ff29c07a58eb61e3c5e177f10 (diff) | |
download | chromium_src-ba9f8fbb8beef7b261b82a6466dea8176af7d684.zip chromium_src-ba9f8fbb8beef7b261b82a6466dea8176af7d684.tar.gz chromium_src-ba9f8fbb8beef7b261b82a6466dea8176af7d684.tar.bz2 |
Ensure that Prerendering uses not more than 100MB per prerendered page.
TEST=PrerenderBrowserTest.PrerenderExcessiveMemory
BUG=73855
Review URL: http://codereview.chromium.org/6576009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75936 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/prerender')
-rw-r--r-- | chrome/browser/prerender/prerender_browsertest.cc | 6 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.cc | 47 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_contents.h | 23 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_final_status.h | 3 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.cc | 51 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.h | 19 |
6 files changed, 148 insertions, 1 deletions
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc index 9803097..1643fd1 100644 --- a/chrome/browser/prerender/prerender_browsertest.cc +++ b/chrome/browser/prerender/prerender_browsertest.cc @@ -317,4 +317,10 @@ IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, FLAKY_PrerenderRedirectToHttps) { 2); } +// Checks that renderers using excessive memory will be terminated. +IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, PrerenderExcessiveMemory) { + PrerenderTestURL("prerender_excessive_memory.html", + FINAL_STATUS_MEMORY_LIMIT_EXCEEDED, 1); +} + } // namespace prerender diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc index 4b1162b..5d5c033 100644 --- a/chrome/browser/prerender/prerender_contents.cc +++ b/chrome/browser/prerender/prerender_contents.cc @@ -4,6 +4,7 @@ #include "chrome/browser/prerender/prerender_contents.h" +#include "base/process_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/background_contents_service.h" #include "chrome/browser/prerender/prerender_final_status.h" @@ -22,6 +23,10 @@ #include "content/browser/browsing_instance.h" #include "ui/gfx/rect.h" +#if defined(OS_MACOSX) +#include "chrome/browser/mach_broker_mac.h" +#endif + namespace prerender { class PrerenderContentsFactoryImpl : public PrerenderContents::Factory { @@ -68,6 +73,9 @@ void PrerenderContents::StartPrerendering() { SiteInstance* site_instance = SiteInstance::CreateSiteInstance(profile_); render_view_host_ = new RenderViewHost(site_instance, this, MSG_ROUTING_NONE, NULL); + // Hide the RVH, so that we will run at a lower CPU priority. + // Once the RVH is being swapped into a tab, we will Restore it again. + render_view_host_->WasHidden(); render_view_host_->AllowScriptToClose(true); // Close ourselves when the application is shutting down. @@ -387,4 +395,43 @@ void PrerenderContents::Destroy(FinalStatus final_status) { delete this; } +void PrerenderContents::OnJSOutOfMemory() { + Destroy(FINAL_STATUS_JS_OUT_OF_MEMORY); +} + +void PrerenderContents::RendererUnresponsive(RenderViewHost* render_view_host, + bool is_during_unload) { + Destroy(FINAL_STATUS_RENDERER_UNRESPONSIVE); +} + + +base::ProcessMetrics* PrerenderContents::MaybeGetProcessMetrics() { + if (process_metrics_.get() == NULL) { + base::ProcessHandle handle = render_view_host_->process()->GetHandle(); + if (handle == base::kNullProcessHandle) + return NULL; +#if !defined(OS_MACOSX) + process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(handle)); +#else + process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics( + handle, + MachBroker::GetInstance())); +#endif + } + + return process_metrics_.get(); +} + +void PrerenderContents::DestroyWhenUsingTooManyResources() { + base::ProcessMetrics* metrics = MaybeGetProcessMetrics(); + if (metrics == NULL) + return; + + size_t private_bytes, shared_bytes; + if (metrics->GetMemoryBytes(&private_bytes, &shared_bytes)) { + if (private_bytes > kMaxPrerenderPrivateMB * 1024 * 1024) + Destroy(FINAL_STATUS_MEMORY_LIMIT_EXCEEDED); + } +} + } // namespace prerender diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h index 7b631f4..d10f2ab 100644 --- a/chrome/browser/prerender/prerender_contents.h +++ b/chrome/browser/prerender/prerender_contents.h @@ -23,6 +23,10 @@ class TabContents; struct WebPreferences; struct ViewHostMsg_FrameNavigate_Params; +namespace base { +class ProcessMetrics; +} + namespace gfx { class Rect; } @@ -62,6 +66,10 @@ class PrerenderContents : public RenderViewHostDelegate, virtual void StartPrerendering(); + // Verifies that the prerendering is not using too many resources, and kills + // it if not. + void DestroyWhenUsingTooManyResources(); + RenderViewHost* render_view_host() { return render_view_host_; } // Allows replacing of the RenderViewHost owned by this class, including // replacing with a NULL value. When a caller uses this, the caller will @@ -167,6 +175,10 @@ class PrerenderContents : public RenderViewHostDelegate, const std::string& value); virtual void ClearInspectorSettings(); + virtual void OnJSOutOfMemory(); + virtual void RendererUnresponsive(RenderViewHost* render_view_host, + bool is_during_unload); + protected: PrerenderContents(PrerenderManager* prerender_manager, Profile* profile, const GURL& url, const std::vector<GURL>& alias_urls, @@ -197,6 +209,9 @@ class PrerenderContents : public RenderViewHostDelegate, // delete |this|. void Destroy(FinalStatus reason); + // Returns the ProcessMetrics for the render process, if it exists. + base::ProcessMetrics* MaybeGetProcessMetrics(); + // The prerender manager owning this object. PrerenderManager* prerender_manager_; @@ -243,6 +258,14 @@ class PrerenderContents : public RenderViewHostDelegate, // (potentially only partially) prerendered page is shown to the user. base::TimeTicks load_start_time_; + // Process Metrics of the render process associated with the + // RenderViewHost for this object. + scoped_ptr<base::ProcessMetrics> process_metrics_; + + // Maximum amount of private memory that may be used per PrerenderContents, + // in MB. + static const int kMaxPrerenderPrivateMB = 100; + DISALLOW_COPY_AND_ASSIGN(PrerenderContents); }; diff --git a/chrome/browser/prerender/prerender_final_status.h b/chrome/browser/prerender/prerender_final_status.h index fba235c..ca9798a 100644 --- a/chrome/browser/prerender/prerender_final_status.h +++ b/chrome/browser/prerender/prerender_final_status.h @@ -22,6 +22,9 @@ enum FinalStatus { FINAL_STATUS_AUTH_NEEDED, FINAL_STATUS_HTTPS, FINAL_STATUS_DOWNLOAD, + FINAL_STATUS_MEMORY_LIMIT_EXCEEDED, + FINAL_STATUS_JS_OUT_OF_MEMORY, + FINAL_STATUS_RENDERER_UNRESPONSIVE, FINAL_STATUS_MAX, }; diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index 5cf0bca..1e64672 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc @@ -57,7 +57,8 @@ PrerenderManager::PrerenderManager(Profile* profile) max_prerender_age_(base::TimeDelta::FromSeconds( kDefaultMaxPrerenderAgeSeconds)), max_elements_(kDefaultMaxPrerenderElements), - prerender_contents_factory_(PrerenderContents::CreateFactory()) { + prerender_contents_factory_(PrerenderContents::CreateFactory()), + periodic_cleanups_active_(false) { } PrerenderManager::~PrerenderManager() { @@ -92,6 +93,7 @@ bool PrerenderManager::AddPreload(const GURL& url, data.contents_->set_final_status(FINAL_STATUS_EVICTED); delete data.contents_; } + StartSchedulingPeriodicCleanups(); return true; } @@ -104,6 +106,8 @@ void PrerenderManager::DeleteOldEntries() { data.contents_->set_final_status(FINAL_STATUS_TIMED_OUT); delete data.contents_; } + if (prerender_list_.empty()) + StopSchedulingPeriodicCleanups(); } PrerenderContents* PrerenderManager::GetEntry(const GURL& url) { @@ -133,6 +137,9 @@ bool PrerenderManager::MaybeUsePreloadedPage(TabContents* tc, const GURL& url) { pc->set_final_status(FINAL_STATUS_USED); RenderViewHost* rvh = pc->render_view_host(); + // RenderViewHosts in PrerenderContents start out hidden. + // Since we are actually using it now, restore it. + rvh->WasRestored(); pc->set_render_view_host(NULL); rvh->Send(new ViewMsg_DisplayPrerenderedPage(rvh->routing_id())); tc->SwapInRenderViewHost(rvh); @@ -259,4 +266,46 @@ bool PrerenderManager::ShouldRecordWindowedPPLT() const { return elapsed_time <= base::TimeDelta::FromSeconds(kWindowedPPLTSeconds); } +void PrerenderManager::StartSchedulingPeriodicCleanups() { + if (periodic_cleanups_active_) + return; + periodic_cleanups_active_ = true; + SchedulePeriodicCleanup(); +} + +void PrerenderManager::StopSchedulingPeriodicCleanups() { + if (!periodic_cleanups_active_) + return; + periodic_cleanups_active_ = false; +} + +void PrerenderManager::SchedulePeriodicCleanup() { + if (!periodic_cleanups_active_) + return; + BrowserThread::PostDelayedTask( + BrowserThread::UI, + FROM_HERE, + NewRunnableMethod(this, &PrerenderManager::PeriodicCleanup), + kPeriodicCleanupIntervalMs); +} + +void PrerenderManager::PeriodicCleanup() { + DeleteOldEntries(); + // Grab a copy of the current PrerenderContents pointers, so that we + // will not interfere with potential deletions of the list. + std::vector<PrerenderContents*> prerender_contents; + for (std::list<PrerenderContentsData>::iterator it = prerender_list_.begin(); + it != prerender_list_.end(); + ++it) { + prerender_contents.push_back(it->contents_); + } + for (std::vector<PrerenderContents*>::iterator it = + prerender_contents.begin(); + it != prerender_contents.end(); + ++it) { + (*it)->DestroyWhenUsingTooManyResources(); + } + SchedulePeriodicCleanup(); +} + } // namespace prerender diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h index 962ef03..8aeaacc 100644 --- a/chrome/browser/prerender/prerender_manager.h +++ b/chrome/browser/prerender/prerender_manager.h @@ -89,6 +89,18 @@ class PrerenderManager : public base::RefCounted<PrerenderManager> { friend class base::RefCounted<PrerenderManager>; struct PrerenderContentsData; + // Starts and stops scheduling periodic cleanups, respectively. + void StartSchedulingPeriodicCleanups(); + void StopSchedulingPeriodicCleanups(); + + // Schedules a periodic cleanup. + void SchedulePeriodicCleanup(); + + // Deletes stale prerendered PrerenderContents. + // Also identifies and kills PrerenderContents that use too much + // resources. + void PeriodicCleanup(); + bool IsPrerenderElementFresh(const base::Time start) const; void DeleteOldEntries(); virtual base::Time GetCurrentTime() const; @@ -124,6 +136,9 @@ class PrerenderManager : public base::RefCounted<PrerenderManager> { // observed link rel=prefetch tag. static const int kWindowedPPLTSeconds = 30; + // Time interval at which periodic cleanups are performed. + static const int kPeriodicCleanupIntervalMs = 1000; + scoped_ptr<PrerenderContents::Factory> prerender_contents_factory_; static PrerenderManagerMode mode_; @@ -134,6 +149,10 @@ class PrerenderManager : public base::RefCounted<PrerenderManager> { // This static variable should only be modified on the UI thread. static base::TimeTicks last_prefetch_seen_time_; + // Indicates whether we are currently performing periodic cleanups + // of pending prerendered pages. + bool periodic_cleanups_active_; + DISALLOW_COPY_AND_ASSIGN(PrerenderManager); }; |