| Index: chrome/renderer/render_thread.cc | 
| =================================================================== | 
| --- chrome/renderer/render_thread.cc	(revision 24223) | 
| +++ chrome/renderer/render_thread.cc	(working copy) | 
| @@ -9,6 +9,7 @@ | 
|  | 
| #include "base/command_line.h" | 
| #include "base/lazy_instance.h" | 
| +#include "base/logging.h" | 
| #include "base/shared_memory.h" | 
| #include "base/stats_table.h" | 
| #include "base/thread_local.h" | 
| @@ -58,6 +59,8 @@ | 
|  | 
| namespace { | 
| static const unsigned int kCacheStatsDelayMS = 2000 /* milliseconds */; | 
| +static const double kInitialIdleHandlerDelayS = 1.0 /* seconds */; | 
| + | 
| static base::LazyInstance<base::ThreadLocalPointer<RenderThread> > lazy_tls( | 
| base::LINKER_INITIALIZED); | 
|  | 
| @@ -105,8 +108,11 @@ | 
| #endif | 
|  | 
| plugin_refresh_allowed_ = true; | 
| -  cache_stats_factory_.reset( | 
| -      new ScopedRunnableMethodFactory<RenderThread>(this)); | 
| +  cache_stats_task_pending_ = false; | 
| +  widget_count_ = 0; | 
| +  hidden_widget_count_ = 0; | 
| +  idle_notification_delay_in_s_ = kInitialIdleHandlerDelayS; | 
| +  task_factory_.reset(new ScopedRunnableMethodFactory<RenderThread>(this)); | 
|  | 
| visited_link_slave_.reset(new VisitedLinkSlave()); | 
| user_script_slave_.reset(new UserScriptSlave()); | 
| @@ -156,6 +162,30 @@ | 
| channel()->RemoveFilter(filter); | 
| } | 
|  | 
| +void RenderThread::WidgetHidden() { | 
| +  DCHECK(hidden_widget_count_ <= widget_count_); | 
| +  hidden_widget_count_++ ; | 
| +  if (hidden_widget_count_ == widget_count_) { | 
| +    // Reset the delay. | 
| +    idle_notification_delay_in_s_ = kInitialIdleHandlerDelayS; | 
| + | 
| +    // Schedule the IdleHandler to wakeup in a bit. | 
| +    MessageLoop::current()->PostDelayedTask(FROM_HERE, | 
| +        task_factory_->NewRunnableMethod(&RenderThread::IdleHandler), | 
| +        static_cast<int64>(floor(idle_notification_delay_in_s_)) * 1000); | 
| +  } | 
| +} | 
| + | 
| +void RenderThread::WidgetRestored() { | 
| +  DCHECK(hidden_widget_count_ > 0); | 
| +  hidden_widget_count_--; | 
| + | 
| +  // Note: we may have a timer pending to call the IdleHandler (see the | 
| +  // WidgetHidden() code).  But we don't bother to cancel it as it is | 
| +  // benign and won't do anything if the tab is un-hidden when it is | 
| +  // called. | 
| +} | 
| + | 
| void RenderThread::Resolve(const char* name, size_t length) { | 
| return dns_master_->Resolve(name, length); | 
| } | 
| @@ -316,15 +346,17 @@ | 
| WebCache::UsageStats stats; | 
| WebCache::getUsageStats(&stats); | 
| Send(new ViewHostMsg_UpdatedCacheStats(stats)); | 
| +  cache_stats_task_pending_ = false; | 
| } | 
|  | 
| void RenderThread::InformHostOfCacheStatsLater() { | 
| // Rate limit informing the host of our cache stats. | 
| -  if (!cache_stats_factory_->empty()) | 
| +  if (cache_stats_task_pending_) | 
| return; | 
|  | 
| +  cache_stats_task_pending_ = true; | 
| MessageLoop::current()->PostDelayedTask(FROM_HERE, | 
| -      cache_stats_factory_->NewRunnableMethod( | 
| +      task_factory_->NewRunnableMethod( | 
| &RenderThread::InformHostOfCacheStats), | 
| kCacheStatsDelayMS); | 
| } | 
| @@ -417,6 +449,31 @@ | 
| WebKit::enableMediaPlayer(); | 
| } | 
|  | 
| +void RenderThread::IdleHandler() { | 
| +  // It is possible that the timer was set while the widgets were idle, | 
| +  // but that they are no longer idle.  If so, just return. | 
| +  if (hidden_widget_count_ < widget_count_) | 
| +    return; | 
| + | 
| +  LOG(INFO) << "RenderThread calling v8 IdleNotification for " << this; | 
| + | 
| +  // When V8::IdleNotification returns true, it means that it has cleaned up | 
| +  // as much as it can.  There is no point in continuing to call it. | 
| +  if (!v8::V8::IdleNotification(false)) { | 
| +    // Dampen the delay using the algorithm: | 
| +    //    delay = delay + 1 / (delay + 2) | 
| +    // Using floor(delay) has a dampening effect such as: | 
| +    //    1s, 1, 1, 2, 2, 2, 2, 3, 3, ... | 
| +    idle_notification_delay_in_s_ += | 
| +        1.0 / (idle_notification_delay_in_s_ + 2.0); | 
| + | 
| +    // Schedule the next timer. | 
| +    MessageLoop::current()->PostDelayedTask(FROM_HERE, | 
| +        task_factory_->NewRunnableMethod(&RenderThread::IdleHandler), | 
| +        static_cast<int64>(floor(idle_notification_delay_in_s_)) * 1000); | 
| +  } | 
| +} | 
| + | 
| void RenderThread::OnExtensionMessageInvoke(const std::string& function_name, | 
| const ListValue& args) { | 
| RendererExtensionBindings::Invoke(function_name, args, NULL); | 
|  |