Home | History | Annotate | Download | only in declarative
      1 // Copyright (c) 2012 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.
      4 
      5 #include "chrome/browser/extensions/api/declarative/rules_registry.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/stl_util.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "base/time/time.h"
     16 #include "base/values.h"
     17 #include "chrome/browser/chrome_notification_types.h"
     18 #include "chrome/browser/extensions/api/declarative/rules_cache_delegate.h"
     19 #include "chrome/browser/extensions/extension_service.h"
     20 #include "chrome/browser/extensions/extension_util.h"
     21 #include "chrome/browser/extensions/state_store.h"
     22 #include "chrome/browser/profiles/profile.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "content/public/browser/notification_details.h"
     25 #include "content/public/browser/notification_source.h"
     26 #include "extensions/browser/extension_prefs.h"
     27 #include "extensions/browser/extension_system.h"
     28 #include "extensions/common/extension.h"
     29 
     30 namespace {
     31 
     32 const char kSuccess[] = "";
     33 const char kDuplicateRuleId[] = "Duplicate rule ID: %s";
     34 
     35 scoped_ptr<base::Value> RulesToValue(
     36     const std::vector<linked_ptr<extensions::RulesRegistry::Rule> >& rules) {
     37   scoped_ptr<base::ListValue> list(new base::ListValue());
     38   for (size_t i = 0; i < rules.size(); ++i)
     39     list->Append(rules[i]->ToValue().release());
     40   return list.PassAs<base::Value>();
     41 }
     42 
     43 std::vector<linked_ptr<extensions::RulesRegistry::Rule> > RulesFromValue(
     44     const base::Value* value) {
     45   std::vector<linked_ptr<extensions::RulesRegistry::Rule> > rules;
     46 
     47   const base::ListValue* list = NULL;
     48   if (!value || !value->GetAsList(&list))
     49     return rules;
     50 
     51   rules.reserve(list->GetSize());
     52   for (size_t i = 0; i < list->GetSize(); ++i) {
     53     const base::DictionaryValue* dict = NULL;
     54     if (!list->GetDictionary(i, &dict))
     55       continue;
     56     linked_ptr<extensions::RulesRegistry::Rule> rule(
     57         new extensions::RulesRegistry::Rule());
     58     if (extensions::RulesRegistry::Rule::Populate(*dict, rule.get()))
     59       rules.push_back(rule);
     60   }
     61 
     62   return rules;
     63 }
     64 
     65 std::string ToId(int identifier) {
     66   return base::StringPrintf("_%d_", identifier);
     67 }
     68 
     69 }  // namespace
     70 
     71 
     72 namespace extensions {
     73 
     74 // RulesRegistry
     75 
     76 RulesRegistry::RulesRegistry(Profile* profile,
     77                              const std::string& event_name,
     78                              content::BrowserThread::ID owner_thread,
     79                              RulesCacheDelegate* cache_delegate,
     80                              const WebViewKey& webview_key)
     81     : profile_(profile),
     82       owner_thread_(owner_thread),
     83       event_name_(event_name),
     84       webview_key_(webview_key),
     85       ready_(/*signaled=*/!cache_delegate),  // Immediately ready if no cache
     86                                              // delegate to wait for.
     87       weak_ptr_factory_(profile ? this : NULL),
     88       last_generated_rule_identifier_id_(0) {
     89   if (cache_delegate) {
     90     cache_delegate_ = cache_delegate->GetWeakPtr();
     91     cache_delegate->Init(this);
     92   }
     93 }
     94 
     95 std::string RulesRegistry::AddRulesNoFill(
     96     const std::string& extension_id,
     97     const std::vector<linked_ptr<Rule> >& rules) {
     98   DCHECK_CURRENTLY_ON(owner_thread());
     99 
    100   // Verify that all rule IDs are new.
    101   for (std::vector<linked_ptr<Rule> >::const_iterator i =
    102       rules.begin(); i != rules.end(); ++i) {
    103     const RuleId& rule_id = *((*i)->id);
    104     // Every rule should have a priority assigned.
    105     DCHECK((*i)->priority);
    106     RulesDictionaryKey key(extension_id, rule_id);
    107     if (rules_.find(key) != rules_.end())
    108       return base::StringPrintf(kDuplicateRuleId, rule_id.c_str());
    109   }
    110 
    111   std::string error = AddRulesImpl(extension_id, rules);
    112 
    113   if (!error.empty())
    114     return error;
    115 
    116   // Commit all rules into |rules_| on success.
    117   for (std::vector<linked_ptr<Rule> >::const_iterator i =
    118       rules.begin(); i != rules.end(); ++i) {
    119     const RuleId& rule_id = *((*i)->id);
    120     RulesDictionaryKey key(extension_id, rule_id);
    121     rules_[key] = *i;
    122   }
    123 
    124   MaybeProcessChangedRules(extension_id);
    125   return kSuccess;
    126 }
    127 
    128 std::string RulesRegistry::AddRules(
    129     const std::string& extension_id,
    130     const std::vector<linked_ptr<Rule> >& rules) {
    131   DCHECK_CURRENTLY_ON(owner_thread());
    132 
    133   std::string error = CheckAndFillInOptionalRules(extension_id, rules);
    134   if (!error.empty())
    135     return error;
    136   FillInOptionalPriorities(rules);
    137 
    138   return AddRulesNoFill(extension_id, rules);
    139 }
    140 
    141 std::string RulesRegistry::RemoveRules(
    142     const std::string& extension_id,
    143     const std::vector<std::string>& rule_identifiers) {
    144   DCHECK_CURRENTLY_ON(owner_thread());
    145 
    146   std::string error = RemoveRulesImpl(extension_id, rule_identifiers);
    147 
    148   if (!error.empty())
    149     return error;
    150 
    151   for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
    152        i != rule_identifiers.end();
    153        ++i) {
    154     RulesDictionaryKey lookup_key(extension_id, *i);
    155     rules_.erase(lookup_key);
    156   }
    157 
    158   MaybeProcessChangedRules(extension_id);
    159   RemoveUsedRuleIdentifiers(extension_id, rule_identifiers);
    160   return kSuccess;
    161 }
    162 
    163 std::string RulesRegistry::RemoveAllRules(const std::string& extension_id) {
    164   std::string result = RulesRegistry::RemoveAllRulesNoStoreUpdate(extension_id);
    165   MaybeProcessChangedRules(extension_id);  // Now update the prefs and store.
    166   return result;
    167 }
    168 
    169 std::string RulesRegistry::RemoveAllRulesNoStoreUpdate(
    170     const std::string& extension_id) {
    171   DCHECK_CURRENTLY_ON(owner_thread());
    172 
    173   std::string error = RemoveAllRulesImpl(extension_id);
    174 
    175   if (!error.empty())
    176     return error;
    177 
    178   for (RulesDictionary::const_iterator i = rules_.begin();
    179       i != rules_.end();) {
    180     const RulesDictionaryKey& key = i->first;
    181     ++i;
    182     if (key.first == extension_id)
    183       rules_.erase(key);
    184   }
    185 
    186   RemoveAllUsedRuleIdentifiers(extension_id);
    187   return kSuccess;
    188 }
    189 
    190 void RulesRegistry::GetRules(const std::string& extension_id,
    191                              const std::vector<std::string>& rule_identifiers,
    192                              std::vector<linked_ptr<Rule> >* out) {
    193   DCHECK_CURRENTLY_ON(owner_thread());
    194 
    195   for (std::vector<std::string>::const_iterator i = rule_identifiers.begin();
    196       i != rule_identifiers.end(); ++i) {
    197     RulesDictionaryKey lookup_key(extension_id, *i);
    198     RulesDictionary::iterator entry = rules_.find(lookup_key);
    199     if (entry != rules_.end())
    200       out->push_back(entry->second);
    201   }
    202 }
    203 
    204 void RulesRegistry::GetAllRules(const std::string& extension_id,
    205                                 std::vector<linked_ptr<Rule> >* out) {
    206   DCHECK_CURRENTLY_ON(owner_thread());
    207 
    208   for (RulesDictionary::const_iterator i = rules_.begin();
    209       i != rules_.end(); ++i) {
    210     const RulesDictionaryKey& key = i->first;
    211     if (key.first == extension_id)
    212       out->push_back(i->second);
    213   }
    214 }
    215 
    216 void RulesRegistry::OnExtensionUnloaded(const std::string& extension_id) {
    217   DCHECK_CURRENTLY_ON(owner_thread());
    218   std::string error = RemoveAllRulesImpl(extension_id);
    219   if (!error.empty())
    220     LOG(ERROR) << error;
    221 }
    222 
    223 void RulesRegistry::OnExtensionUninstalled(const std::string& extension_id) {
    224   DCHECK_CURRENTLY_ON(owner_thread());
    225   std::string error = RemoveAllRulesNoStoreUpdate(extension_id);
    226   if (!error.empty())
    227     LOG(ERROR) << error;
    228 }
    229 
    230 void RulesRegistry::OnExtensionLoaded(const std::string& extension_id) {
    231   DCHECK_CURRENTLY_ON(owner_thread());
    232   std::vector<linked_ptr<Rule> > rules;
    233   GetAllRules(extension_id, &rules);
    234   std::string error = AddRulesImpl(extension_id, rules);
    235   if (!error.empty())
    236     LOG(ERROR) << error;
    237 }
    238 
    239 size_t RulesRegistry::GetNumberOfUsedRuleIdentifiersForTesting() const {
    240   size_t entry_count = 0u;
    241   for (RuleIdentifiersMap::const_iterator extension =
    242            used_rule_identifiers_.begin();
    243        extension != used_rule_identifiers_.end();
    244        ++extension) {
    245     // Each extension is counted as 1 just for being there. Otherwise we miss
    246     // keys with empty values.
    247     entry_count += 1u + extension->second.size();
    248   }
    249   return entry_count;
    250 }
    251 
    252 void RulesRegistry::DeserializeAndAddRules(
    253     const std::string& extension_id,
    254     scoped_ptr<base::Value> rules) {
    255   DCHECK_CURRENTLY_ON(owner_thread());
    256 
    257   AddRulesNoFill(extension_id, RulesFromValue(rules.get()));
    258 }
    259 
    260 RulesRegistry::~RulesRegistry() {
    261 }
    262 
    263 void RulesRegistry::MarkReady(base::Time storage_init_time) {
    264   DCHECK_CURRENTLY_ON(owner_thread());
    265 
    266   if (!storage_init_time.is_null()) {
    267     UMA_HISTOGRAM_TIMES("Extensions.DeclarativeRulesStorageInitialization",
    268                         base::Time::Now() - storage_init_time);
    269   }
    270 
    271   ready_.Signal();
    272 }
    273 
    274 void RulesRegistry::ProcessChangedRules(const std::string& extension_id) {
    275   DCHECK_CURRENTLY_ON(owner_thread());
    276 
    277   DCHECK(ContainsKey(process_changed_rules_requested_, extension_id));
    278   process_changed_rules_requested_[extension_id] = NOT_SCHEDULED_FOR_PROCESSING;
    279 
    280   std::vector<linked_ptr<Rule> > new_rules;
    281   GetAllRules(extension_id, &new_rules);
    282   content::BrowserThread::PostTask(
    283       content::BrowserThread::UI,
    284       FROM_HERE,
    285       base::Bind(&RulesCacheDelegate::WriteToStorage,
    286                  cache_delegate_,
    287                  extension_id,
    288                  base::Passed(RulesToValue(new_rules))));
    289 }
    290 
    291 void RulesRegistry::MaybeProcessChangedRules(const std::string& extension_id) {
    292   // Read and initialize |process_changed_rules_requested_[extension_id]| if
    293   // necessary. (Note that the insertion below will not overwrite
    294   // |process_changed_rules_requested_[extension_id]| if that already exists.
    295   std::pair<ProcessStateMap::iterator, bool> insertion =
    296       process_changed_rules_requested_.insert(std::make_pair(
    297           extension_id,
    298           profile_ ? NOT_SCHEDULED_FOR_PROCESSING : NEVER_PROCESS));
    299   if (insertion.first->second != NOT_SCHEDULED_FOR_PROCESSING)
    300     return;
    301 
    302   process_changed_rules_requested_[extension_id] = SCHEDULED_FOR_PROCESSING;
    303   ready_.Post(FROM_HERE,
    304               base::Bind(&RulesRegistry::ProcessChangedRules,
    305                          weak_ptr_factory_.GetWeakPtr(),
    306                          extension_id));
    307 }
    308 
    309 bool RulesRegistry::IsUniqueId(const std::string& extension_id,
    310                                const std::string& rule_id) const {
    311   RuleIdentifiersMap::const_iterator identifiers =
    312       used_rule_identifiers_.find(extension_id);
    313   if (identifiers == used_rule_identifiers_.end())
    314     return true;
    315   return identifiers->second.find(rule_id) == identifiers->second.end();
    316 }
    317 
    318 std::string RulesRegistry::GenerateUniqueId(const std::string& extension_id) {
    319   while (!IsUniqueId(extension_id, ToId(last_generated_rule_identifier_id_)))
    320     ++last_generated_rule_identifier_id_;
    321   return ToId(last_generated_rule_identifier_id_);
    322 }
    323 
    324 std::string RulesRegistry::CheckAndFillInOptionalRules(
    325     const std::string& extension_id,
    326     const std::vector<linked_ptr<Rule> >& rules) {
    327   // IDs we have inserted, in case we need to rollback this operation.
    328   std::vector<std::string> rollback_log;
    329 
    330   // First we insert all rules with existing identifier, so that generated
    331   // identifiers cannot collide with identifiers passed by the caller.
    332   for (std::vector<linked_ptr<Rule> >::const_iterator i = rules.begin();
    333        i != rules.end();
    334        ++i) {
    335     Rule* rule = i->get();
    336     if (rule->id.get()) {
    337       std::string id = *(rule->id);
    338       if (!IsUniqueId(extension_id, id)) {
    339         RemoveUsedRuleIdentifiers(extension_id, rollback_log);
    340         return "Id " + id + " was used multiple times.";
    341       }
    342       used_rule_identifiers_[extension_id].insert(id);
    343     }
    344   }
    345   // Now we generate IDs in case they were not specified in the rules. This
    346   // cannot fail so we do not need to keep track of a rollback log.
    347   for (std::vector<linked_ptr<Rule> >::const_iterator i = rules.begin();
    348        i != rules.end();
    349        ++i) {
    350     Rule* rule = i->get();
    351     if (!rule->id.get()) {
    352       rule->id.reset(new std::string(GenerateUniqueId(extension_id)));
    353       used_rule_identifiers_[extension_id].insert(*(rule->id));
    354     }
    355   }
    356   return std::string();
    357 }
    358 
    359 void RulesRegistry::FillInOptionalPriorities(
    360     const std::vector<linked_ptr<Rule> >& rules) {
    361   std::vector<linked_ptr<Rule> >::const_iterator i;
    362   for (i = rules.begin(); i != rules.end(); ++i) {
    363     if (!(*i)->priority.get())
    364       (*i)->priority.reset(new int(DEFAULT_PRIORITY));
    365   }
    366 }
    367 
    368 void RulesRegistry::RemoveUsedRuleIdentifiers(
    369     const std::string& extension_id,
    370     const std::vector<std::string>& identifiers) {
    371   std::vector<std::string>::const_iterator i;
    372   for (i = identifiers.begin(); i != identifiers.end(); ++i)
    373     used_rule_identifiers_[extension_id].erase(*i);
    374 }
    375 
    376 void RulesRegistry::RemoveAllUsedRuleIdentifiers(
    377     const std::string& extension_id) {
    378   used_rule_identifiers_.erase(extension_id);
    379 }
    380 
    381 }  // namespace extensions
    382