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