diff options
Diffstat (limited to 'chrome/browser/extensions/extension_webrequest_api.cc')
-rw-r--r-- | chrome/browser/extensions/extension_webrequest_api.cc | 135 |
1 files changed, 134 insertions, 1 deletions
diff --git a/chrome/browser/extensions/extension_webrequest_api.cc b/chrome/browser/extensions/extension_webrequest_api.cc index bc6b493..f1cf685 100644 --- a/chrome/browser/extensions/extension_webrequest_api.cc +++ b/chrome/browser/extensions/extension_webrequest_api.cc @@ -10,6 +10,7 @@ #include "base/json/json_writer.h" #include "base/metrics/histogram.h" #include "base/string_number_conversions.h" +#include "base/time.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" @@ -309,6 +310,10 @@ void NotifyWebRequestAPIUsed(void* profile_id, const Extension* extension) { } } +void ClearCacheOnNavigationOnUI() { + WebCacheManager::GetInstance()->ClearCacheOnNavigation(); +} + } // namespace // Represents a single unique listener to an event, along with whatever filter @@ -525,6 +530,9 @@ int ExtensionWebRequestEventRouter::OnBeforeRequest( if (!profile) return net::OK; + if (IsPageLoad(request)) + NotifyPageLoad(); + if (!HasWebRequestScheme(request->url())) return net::OK; @@ -1066,6 +1074,34 @@ void ExtensionWebRequestEventRouter::OnOTRProfileDestroyed( cross_profile_map_.erase(original_profile); } +void ExtensionWebRequestEventRouter::AddCallbackForPageLoad( + const base::Closure& callback) { + callbacks_for_page_load_.push_back(callback); +} + +bool ExtensionWebRequestEventRouter::IsPageLoad( + net::URLRequest* request) const { + bool is_main_frame = false; + int64 frame_id = -1; + int tab_id = -1; + int window_id = -1; + ResourceType::Type resource_type = ResourceType::LAST_TYPE; + + ExtractRequestInfoDetails(request, &is_main_frame, &frame_id, &tab_id, + &window_id, &resource_type); + + return resource_type == ResourceType::MAIN_FRAME; +} + +void ExtensionWebRequestEventRouter::NotifyPageLoad() { + for (CallbacksForPageLoad::const_iterator i = + callbacks_for_page_load_.begin(); + i != callbacks_for_page_load_.end(); ++i) { + i->Run(); + } + callbacks_for_page_load_.clear(); +} + void ExtensionWebRequestEventRouter::GetMatchingListenersImpl( void* profile, ExtensionInfoMap* extension_info_map, @@ -1556,6 +1592,73 @@ void ExtensionWebRequestEventRouter::ClearSignaled(uint64 request_id, iter->second &= ~event_type; } +// Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChanged. +// +// Each call of webRequest.handlerBehaviorChanged() clears the in-memory cache +// of WebKit at the time of the next page load (top level navigation event). +// This quota heuristic is intended to limit the number of times the cache is +// cleared by an extension. +// +// As we want to account for the number of times the cache is really cleared +// (opposed to the number of times webRequest.handlerBehaviorChanged() is +// called), we cannot decide whether a call of +// webRequest.handlerBehaviorChanged() should trigger a quota violation at the +// time it is called. Instead we only decrement the bucket counter at the time +// when the cache is cleared (when page loads happen). +class ClearCacheQuotaHeuristic : public QuotaLimitHeuristic { + public: + ClearCacheQuotaHeuristic(const Config& config, BucketMapper* map) + : QuotaLimitHeuristic(config, map), + callback_registered_(false), + weak_ptr_factory_(this) {} + virtual ~ClearCacheQuotaHeuristic() {} + virtual bool Apply(Bucket* bucket, + const base::TimeTicks& event_time) OVERRIDE; + + private: + // Callback that is triggered by the ExtensionWebRequestEventRouter on a page + // load. + // + // We don't need to take care of the life time of |bucket|: It is owned by the + // BucketMapper of our base class in |QuotaLimitHeuristic::bucket_mapper_|. As + // long as |this| exists, the respective BucketMapper and its bucket will + // exist as well. + void OnPageLoad(Bucket* bucket); + + // Flag to prevent that we register more than one call back in-between + // clearing the cache. + bool callback_registered_; + + base::WeakPtrFactory<ClearCacheQuotaHeuristic> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ClearCacheQuotaHeuristic); +}; + +bool ClearCacheQuotaHeuristic::Apply(Bucket* bucket, + const base::TimeTicks& event_time) { + if (event_time > bucket->expiration()) + bucket->Reset(config(), event_time); + + // Call bucket->DeductToken() on a new page load, this is when + // webRequest.handlerBehaviorChanged() clears the cache. + if (!callback_registered_) { + ExtensionWebRequestEventRouter::GetInstance()->AddCallbackForPageLoad( + base::Bind(&ClearCacheQuotaHeuristic::OnPageLoad, + weak_ptr_factory_.GetWeakPtr(), + bucket)); + callback_registered_ = true; + } + + // We only check whether tokens are left here. Deducting a token happens in + // OnPageLoad(). + return bucket->has_tokens(); +} + +void ClearCacheQuotaHeuristic::OnPageLoad(Bucket* bucket) { + callback_registered_ = false; + bucket->DeductToken(); +} + bool WebRequestAddEventListener::RunImpl() { // Argument 0 is the callback, which we don't use here. @@ -1717,10 +1820,40 @@ bool WebRequestEventHandled::RunImpl() { } bool WebRequestHandlerBehaviorChanged::RunImpl() { - WebCacheManager::GetInstance()->ClearCacheOnNavigation(); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&ClearCacheOnNavigationOnUI)); return true; } +void WebRequestHandlerBehaviorChanged::GetQuotaLimitHeuristics( + std::list<QuotaLimitHeuristic*>* heuristics) const { + QuotaLimitHeuristic::Config config = { + 20, // Refill 20 tokens per interval. + base::TimeDelta::FromMinutes(10) // 10 minutes refill interval. + }; + QuotaLimitHeuristic::BucketMapper* bucket_mapper = + new QuotaLimitHeuristic::SingletonBucketMapper(); + ClearCacheQuotaHeuristic* heuristic = + new ClearCacheQuotaHeuristic(config, bucket_mapper); + heuristics->push_back(heuristic); +} + +void WebRequestHandlerBehaviorChanged::OnQuotaExceeded() { + // Post warning message. + std::set<std::string> extension_ids; + extension_ids.insert(extension_id()); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&ExtensionWarningSet::NotifyWarningsOnUI, + profile_id(), + extension_ids, + ExtensionWarningSet::kRepeatedCacheFlushes)); + + // Continue gracefully. + Run(); +} + void SendExtensionWebRequestStatusToHost(RenderProcessHost* host) { Profile* profile = Profile::FromBrowserContext(host->browser_context()); if (!profile || !profile->GetExtensionService()) |