summaryrefslogtreecommitdiffstats
path: root/chrome/browser/prerender
diff options
context:
space:
mode:
authortburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-24 20:19:26 +0000
committertburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-24 20:19:26 +0000
commitba9f8fbb8beef7b261b82a6466dea8176af7d684 (patch)
tree59dc22bfbebd8e94c303e2ba9d797fcddef71fc6 /chrome/browser/prerender
parenta55edc4733a6e13ff29c07a58eb61e3c5e177f10 (diff)
downloadchromium_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.cc6
-rw-r--r--chrome/browser/prerender/prerender_contents.cc47
-rw-r--r--chrome/browser/prerender/prerender_contents.h23
-rw-r--r--chrome/browser/prerender/prerender_final_status.h3
-rw-r--r--chrome/browser/prerender/prerender_manager.cc51
-rw-r--r--chrome/browser/prerender/prerender_manager.h19
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);
};