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