Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / extensions / browser / quota_service.h
blobff985f44d28a53750e5631edb4082b92902d3384
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // The QuotaService uses heuristics to limit abusive requests
6 // made by extensions. In this model 'items' (e.g individual bookmarks) are
7 // represented by a 'Bucket' that holds state for that item for one single
8 // interval of time. The interval of time is defined as 'how long we need to
9 // watch an item (for a particular heuristic) before making a decision about
10 // quota violations'. A heuristic is two functions: one mapping input
11 // arguments to a unique Bucket (the BucketMapper), and another to determine
12 // if a new request involving such an item at a given time is a violation.
14 #ifndef EXTENSIONS_BROWSER_QUOTA_SERVICE_H_
15 #define EXTENSIONS_BROWSER_QUOTA_SERVICE_H_
17 #include <list>
18 #include <map>
19 #include <string>
21 #include "base/compiler_specific.h"
22 #include "base/containers/hash_tables.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/threading/non_thread_safe.h"
25 #include "base/time/time.h"
26 #include "base/timer/timer.h"
27 #include "base/values.h"
29 class ExtensionFunction;
31 namespace extensions {
32 class QuotaLimitHeuristic;
34 typedef std::list<QuotaLimitHeuristic*> QuotaLimitHeuristics;
36 // The QuotaService takes care that calls to certain extension
37 // functions do not exceed predefined quotas.
39 // The QuotaService needs to live entirely on one thread, i.e. be created,
40 // called and destroyed on the same thread, due to its use of a RepeatingTimer.
41 // It is not a KeyedService because instances exist on both the UI
42 // and IO threads.
43 class QuotaService : public base::NonThreadSafe {
44 public:
45 // Some concrete heuristics (declared below) that ExtensionFunctions can
46 // use to help the service make decisions about quota violations.
47 class TimedLimit;
49 QuotaService();
50 virtual ~QuotaService();
52 // Decide whether the invocation of |function| with argument |args| by the
53 // extension specified by |extension_id| results in a quota limit violation.
54 // Returns an error message representing the failure if quota was exceeded,
55 // or empty-string if the request is fine and can proceed.
56 std::string Assess(const std::string& extension_id,
57 ExtensionFunction* function,
58 const base::ListValue* args,
59 const base::TimeTicks& event_time);
61 private:
62 typedef std::string ExtensionId;
63 typedef std::string FunctionName;
64 // All QuotaLimitHeuristic instances in this map are owned by us.
65 typedef std::map<FunctionName, QuotaLimitHeuristics> FunctionHeuristicsMap;
67 // Purge resets all accumulated data as if the service was just created.
68 // Called periodically so we don't consume an unbounded amount of memory
69 // while tracking quota.
70 void Purge();
71 void PurgeFunctionHeuristicsMap(FunctionHeuristicsMap* map);
72 base::RepeatingTimer<QuotaService> purge_timer_;
74 // Our quota tracking state for extensions that have invoked quota limited
75 // functions. Each extension is treated separately, so extension ids are the
76 // key for the mapping. As an extension invokes functions, the map keeps
77 // track of which functions it has invoked and the heuristics for each one.
78 // Each heuristic will be evaluated and ANDed together to get a final answer.
79 std::map<ExtensionId, FunctionHeuristicsMap> function_heuristics_;
81 DISALLOW_COPY_AND_ASSIGN(QuotaService);
84 // A QuotaLimitHeuristic is two things: 1, A heuristic to map extension
85 // function arguments to corresponding Buckets for each input arg, and 2) a
86 // heuristic for determining if a new event involving a particular item
87 // (represented by its Bucket) constitutes a quota violation.
88 class QuotaLimitHeuristic {
89 public:
90 // Parameters to configure the amount of tokens allotted to individual
91 // Bucket objects (see Below) and how often they are replenished.
92 struct Config {
93 // The maximum number of tokens a bucket can contain, and is refilled to
94 // every epoch.
95 int64 refill_token_count;
97 // Specifies how frequently the bucket is logically refilled with tokens.
98 base::TimeDelta refill_interval;
101 // A Bucket is how the heuristic portrays an individual item (since quota
102 // limits are per item) and all associated state for an item that needs to
103 // carry through multiple calls to Apply. It "holds" tokens, which are
104 // debited and credited in response to new events involving the item being
105 // being represented. For convenience, instead of actually periodically
106 // refilling buckets they are just 'Reset' on-demand (e.g. when new events
107 // come in). So, a bucket has an expiration to denote it has becomes stale.
108 class Bucket {
109 public:
110 Bucket() : num_tokens_(0) {}
111 // Removes a token from this bucket, and returns true if the bucket had
112 // any tokens in the first place.
113 bool DeductToken() { return num_tokens_-- > 0; }
115 // Returns true if this bucket has tokens to deduct.
116 bool has_tokens() const { return num_tokens_ > 0; }
118 // Reset this bucket to specification (from internal configuration), to be
119 // valid from |start| until the first refill interval elapses and it needs
120 // to be reset again.
121 void Reset(const Config& config, const base::TimeTicks& start);
123 // The time at which the token count and next expiration should be reset,
124 // via a call to Reset.
125 const base::TimeTicks& expiration() { return expiration_; }
127 private:
128 base::TimeTicks expiration_;
129 int64 num_tokens_;
130 DISALLOW_COPY_AND_ASSIGN(Bucket);
132 typedef std::list<Bucket*> BucketList;
134 // A helper interface to retrieve the bucket corresponding to |args| from
135 // the set of buckets (which is typically stored in the BucketMapper itself)
136 // for this QuotaLimitHeuristic.
137 class BucketMapper {
138 public:
139 virtual ~BucketMapper() {}
140 // In most cases, this should simply extract item IDs from the arguments
141 // (e.g for bookmark operations involving an existing item). If a problem
142 // occurs while parsing |args|, the function aborts - buckets may be non-
143 // empty). The expectation is that invalid args and associated errors are
144 // handled by the ExtensionFunction itself so we don't concern ourselves.
145 virtual void GetBucketsForArgs(const base::ListValue* args,
146 BucketList* buckets) = 0;
149 // Maps all calls to the same bucket, regardless of |args|, for this
150 // QuotaLimitHeuristic.
151 class SingletonBucketMapper : public BucketMapper {
152 public:
153 SingletonBucketMapper() {}
154 ~SingletonBucketMapper() override {}
155 void GetBucketsForArgs(const base::ListValue* args,
156 BucketList* buckets) override;
158 private:
159 Bucket bucket_;
160 DISALLOW_COPY_AND_ASSIGN(SingletonBucketMapper);
163 // Ownership of |map| is given to the new QuotaLimitHeuristic.
164 QuotaLimitHeuristic(const Config& config,
165 BucketMapper* map,
166 const std::string& name);
167 virtual ~QuotaLimitHeuristic();
169 // Determines if sufficient quota exists (according to the Apply
170 // implementation of a derived class) to perform an operation with |args|,
171 // based on the history of similar operations with similar arguments (which
172 // is retrieved using the BucketMapper).
173 bool ApplyToArgs(const base::ListValue* args,
174 const base::TimeTicks& event_time);
176 // Returns an error formatted according to this heuristic.
177 std::string GetError() const;
179 protected:
180 const Config& config() { return config_; }
182 // Determine if the new event occurring at |event_time| involving |bucket|
183 // constitutes a quota violation according to this heuristic.
184 virtual bool Apply(Bucket* bucket, const base::TimeTicks& event_time) = 0;
186 private:
187 friend class QuotaLimitHeuristicTest;
189 const Config config_;
191 // The mapper used in Map. Cannot be NULL.
192 scoped_ptr<BucketMapper> bucket_mapper_;
194 // The name of the heuristic for formatting error messages.
195 std::string name_;
197 DISALLOW_COPY_AND_ASSIGN(QuotaLimitHeuristic);
200 // A simple per-item heuristic to limit the number of events that can occur in
201 // a given period of time; e.g "no more than 100 events in an hour".
202 class QuotaService::TimedLimit : public QuotaLimitHeuristic {
203 public:
204 TimedLimit(const Config& config, BucketMapper* map, const std::string& name)
205 : QuotaLimitHeuristic(config, map, name) {}
206 bool Apply(Bucket* bucket, const base::TimeTicks& event_time) override;
209 } // namespace extensions
211 #endif // EXTENSIONS_BROWSER_QUOTA_SERVICE_H_