910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_RULES_REGISTRY_H__
#define EXTENSIONS_BROWSER_API_DECLARATIVE_RULES_REGISTRY_H__

#include <stddef.h>

#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/one_shot_event.h"
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/common/api/events.h"
#include "extensions/common/extension_id.h"

namespace content {
class BrowserContext;
}

namespace base {
class Value;
}  // namespace base

namespace extensions {

class Extension;
class RulesCacheDelegate;

// A base class for RulesRegistries that takes care of storing the
// api::events::Rule objects. It contains all the methods that need to run
// on the registry thread; methods that need to run on the UI thread are
// separated in the RulesCacheDelegate object.
class RulesRegistry : public base::RefCountedThreadSafe<RulesRegistry> {
 public:
  enum Defaults { DEFAULT_PRIORITY = 100 };
  // After the RulesCacheDelegate object (the part of the registry which runs on
  // the UI thread) is created, a pointer to it is passed to |*ui_part|.
  // In tests, `browser_context` and `ui_part` can be NULL (at the same time).
  // In that case the storage functionality disabled (no RulesCacheDelegate
  // object created).
  RulesRegistry(content::BrowserContext* browser_context,
                const std::string& event_name,
                RulesCacheDelegate* cache_delegate,
                int id);

  RulesRegistry(const RulesRegistry&) = delete;
  RulesRegistry& operator=(const RulesRegistry&) = delete;

  const base::OneShotEvent& ready() const { return ready_; }

  // RulesRegistry implementation:

  // Registers `rules` in this RulesRegistry. If a concrete RuleRegistry does
  // not support some of the rules, it may ignore them.
  //
  // `rules` is a list of Rule instances following the definition of the
  // declarative extension APIs. It is guaranteed that each rule in `rules` has
  // a unique name within the scope of `extension_id` that has not been
  // registered before, unless it has been removed again.
  //
  // Returns an empty string if the function is successful or an error message
  // otherwise. If the function is successful, and if the `rules_out` parameter
  // is non-null, pointers to the added rules are returned.
  //
  // IMPORTANT: This function is atomic. Either all rules that are deemed
  // relevant are added or none.
  std::string AddRules(
      const ExtensionId& extension_id,
      std::vector<api::events::Rule> rules_in,
      std::vector<const api::events::Rule*>* rules_out = nullptr);

  // Unregisters all rules listed in `rule_identifiers` and owned by
  // `extension_id` from this RulesRegistry.
  // Some or all IDs in `rule_identifiers` may not be stored in this
  // RulesRegistry and are ignored.
  //
  // Returns an empty string if the function is successful or an error
  // message otherwise.
  //
  // IMPORTANT: This function is atomic. Either all rules that are deemed
  // relevant are removed or none.
  std::string RemoveRules(const ExtensionId& extension_id,
                          const std::vector<std::string>& rule_identifiers);

  // Same as RemoveAllRules but acts on all rules owned by `extension_id`.
  std::string RemoveAllRules(const ExtensionId& extension_id);

  // Returns all rules listed in `rule_identifiers` and owned by `extension_id`
  // registered in this RuleRegistry. Entries in `rule_identifiers` that
  // are unknown are ignored.
  //
  // The returned rules are stored in `out`.
  void GetRules(const ExtensionId& extension_id,
                const std::vector<std::string>& rule_identifiers,
                std::vector<const api::events::Rule*>* out);

  // Same as GetRules but returns all rules owned by `extension_id`.
  void GetAllRules(const ExtensionId& extension_id,
                   std::vector<const api::events::Rule*>* out);

  // Called to notify the RulesRegistry that the registry service is being
  // shut down.
  void OnShutdown();

  // Called to notify the RulesRegistry that the extension availability has
  // changed, so that the registry can update which rules are active.
  void OnExtensionUnloaded(const Extension* extension);
  void OnExtensionUninstalled(const Extension* extension);
  void OnExtensionLoaded(const Extension* extension);

  // Returns the number of entries in used_rule_identifiers_ for leak detection.
  // Every ExtensionId counts as one entry, even if it contains no rules.
  size_t GetNumberOfUsedRuleIdentifiersForTesting() const;

  // Returns the RulesCacheDelegate. This is used for testing.
  RulesCacheDelegate* rules_cache_delegate_for_testing() const {
    return cache_delegate_.get();
  }

  // Returns the context where the rules registry lives.
  content::BrowserContext* browser_context() const { return browser_context_; }

  // The name of the event with which rules are registered.
  const std::string& event_name() const { return event_name_; }

  // The unique identifier for this RulesRegistry object.
  int id() const { return id_; }

 protected:
  virtual ~RulesRegistry();

  // These functions need to apply the rules to the browser, while the base
  // class will handle defaulting empty fields before calling *Impl, and will
  // automatically cache the rules and re-call *Impl on browser startup.
  virtual std::string AddRulesImpl(
      const ExtensionId& extension_id,
      const std::vector<const api::events::Rule*>& rules) = 0;
  virtual std::string RemoveRulesImpl(
      const ExtensionId& extension_id,
      const std::vector<std::string>& rule_identifiers) = 0;
  virtual std::string RemoveAllRulesImpl(const ExtensionId& extension_id) = 0;

 private:
  friend class base::RefCountedThreadSafe<RulesRegistry>;
  friend class RulesCacheDelegate;

  using RuleId = std::string;
  using RulesDictionaryKey = std::pair<ExtensionId, RuleId>;

  // NOTE: The property of stability of iterators of a map during insertion is
  // relied upon here. If this type needs to change, beware that this will
  // severely complicate returning valid pointers to callers of member functions
  // of this class.
  using RulesDictionary = std::map<RulesDictionaryKey, api::events::Rule>;
  enum ProcessChangedRulesState {
    // ProcessChangedRules can never be called, `cache_delegate_` is NULL.
    NEVER_PROCESS,
    // A task to call ProcessChangedRules is scheduled for future execution.
    SCHEDULED_FOR_PROCESSING,
    // No task to call ProcessChangedRules is scheduled yet, but it is possible
    // to schedule one.
    NOT_SCHEDULED_FOR_PROCESSING
  };
  using ProcessStateMap = std::map<ExtensionId, ProcessChangedRulesState>;

  base::WeakPtr<RulesRegistry> GetWeakPtr() {
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    return weak_ptr_factory_.GetWeakPtr();
  }

  // Internal implementation of the AddRules interface which adds the rules to
  // the `destination` RulesDictionary. If the function is successful, and if
  // the `rules_out` parameter is non-null, pointers to the added rules are
  // returned.
  std::string AddRulesInternal(
      const ExtensionId& extension_id,
      std::vector<api::events::Rule> rules_in,
      RulesDictionary* destination,
      std::vector<const api::events::Rule*>* rules_out);

  // The precondition for calling this method is that all rules have unique IDs.
  // AddRules establishes this precondition and calls into this method.
  // Stored rules already meet this precondition and so they avoid calling
  // CheckAndFillInOptionalRules for improved performance.
  //
  // Returns an empty string if the function is successful or an error
  // message otherwise. If the function is successful, and if the
  // `rules_out` parameter is non-null, pointers to the added rules are
  // returned.
  std::string AddRulesNoFill(const ExtensionId& extension_id,
                             std::vector<api::events::Rule> rules_in,
                             RulesDictionary* destination,
                             std::vector<const api::events::Rule*>* rules_out);

  // Same as GetRules but returns all rules owned by `extension_id` for a given
  // `rules` dictionary.
  void GetRules(const ExtensionId& extension_id,
                RulesDictionary* rules,
                std::vector<const api::events::Rule*>* out);

  // Common processing after extension's rules have changed.
  void ProcessChangedRules(const ExtensionId& extension_id);

  // Calls ProcessChangedRules if
  // |process_changed_rules_requested_(extension_id)| ==
  // NOT_SCHEDULED_FOR_PROCESSING.
  void MaybeProcessChangedRules(const ExtensionId& extension_id);

  // This method implements the functionality of RemoveAllRules, except for not
  // calling MaybeProcessChangedRules. That way updating the rules store and
  // extension prefs is avoided. This method is called when an extension is
  // uninstalled, that way there is no clash with the preferences being wiped.
  // Set `remove_manifest_rules` to true if `manifest_rules_` should be cleared
  // along with `rules_`.
  std::string RemoveAllRulesNoStoreUpdate(const ExtensionId& extension_id,
                                          bool remove_manifest_rules);

  void MarkReady();

  // Deserialize the rules from the given Value object and add them to the
  // RulesRegistry.
  void DeserializeAndAddRules(const ExtensionId& extension_id,
                              std::optional<base::Value> rules);

  // Reports an internal error with the specified params to the extensions
  // client.
  void ReportInternalError(const ExtensionId& extension_id,
                           const std::string& error);

  // The context to which this rules registry belongs.
  raw_ptr<content::BrowserContext> browser_context_;

  // The name of the event with which rules are registered.
  const std::string event_name_;

  // The key that identifies the context in which these rules apply.
  int id_;

  RulesDictionary rules_;

  RulesDictionary manifest_rules_;

  // Signaled when we have finished reading from storage for all extensions that
  // are loaded on startup.
  base::OneShotEvent ready_;

  ProcessStateMap process_changed_rules_requested_;

  // Returns whether any existing rule is registered with identifier `rule_id`
  // for extension `extension_id`.
  bool IsUniqueId(const ExtensionId& extension_id,
                  const std::string& rule_id) const;

  // Creates an ID that is unique within the scope of`extension_id`.
  std::string GenerateUniqueId(const ExtensionId& extension_id);

  // Verifies that all `rules` have unique IDs or initializes them with
  // unique IDs if they don't have one. In case of duplicate IDs, this function
  // returns a non-empty error message.
  std::string CheckAndFillInOptionalRules(
      const ExtensionId& extension_id,
      std::vector<api::events::Rule>* rules);

  // Initializes the priority fields in case they have not been set.
  void FillInOptionalPriorities(std::vector<api::events::Rule>* rules);

  // Removes all `identifiers` of `extension_id` from `used_rule_identifiers_`.
  void RemoveUsedRuleIdentifiers(const ExtensionId& extension_id,
                                 const std::vector<std::string>& identifiers);

  // Same as RemoveUsedRuleIdentifiers but operates on all rules of
  // `extension_id`.
  void RemoveAllUsedRuleIdentifiers(const ExtensionId& extension_id);

  using RuleIdentifier = std::string;
  std::map<ExtensionId, std::set<RuleIdentifier>> used_rule_identifiers_;
  int last_generated_rule_identifier_id_;

  // `cache_delegate_` is owned by the registry service. If `cache_delegate_` is
  // NULL, then the storage functionality is disabled (this is used in tests).
  // This registry cannot own `cache_delegate_` because during the time after
  // rules registry service shuts down on UI thread, and the registry is
  // destroyed on its thread, the use of the `cache_delegate_` would not be
  // safe. The registry only ever associates with one RulesCacheDelegate
  // instance.
  base::WeakPtr<RulesCacheDelegate> cache_delegate_;

  base::WeakPtrFactory<RulesRegistry> weak_ptr_factory_{this};
};

}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_API_DECLARATIVE_RULES_REGISTRY_H__