// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_CHROME_CONTENT_RULES_REGISTRY_H_
#define CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_CHROME_CONTENT_RULES_REGISTRY_H_

#include <stddef.h>

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

#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/linked_ptr.h"
#include "chrome/browser/extensions/api/declarative_content/content_action.h"
#include "chrome/browser/extensions/api/declarative_content/content_condition.h"
#include "chrome/browser/extensions/api/declarative_content/content_predicate_evaluator.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "extensions/browser/api/declarative_content/content_rules_registry.h"
#include "extensions/common/extension_id.h"

namespace content {
class BrowserContext;
class WebContents;
}

namespace extensions {

class Extension;

// The ChromeContentRulesRegistry is responsible for managing
// the internal representation of rules for the Declarative Content API.
//
// Here is the high level overview of this functionality:
//
// api::events::Rule consists of conditions and actions, these are
// represented as a ContentRule with ContentConditions and ContentActions.
//
// A note on incognito support: separate instances of ChromeContentRulesRegistry
// are created for incognito and non-incognito contexts. The incognito instance,
// however, is only responsible for applying rules registered by the incognito
// side of split-mode extensions to incognito tabs. The non-incognito instance
// handles incognito tabs for spanning-mode extensions, plus all non-incognito
// tabs.
class ChromeContentRulesRegistry
    : public ContentRulesRegistry,
      public content::NotificationObserver,
      public ContentPredicateEvaluator::Delegate {
 public:
  using PredicateEvaluatorsFactory =
      base::Callback<std::vector<std::unique_ptr<ContentPredicateEvaluator>>(
          ContentPredicateEvaluator::Delegate*)>;

  // For testing, |cache_delegate| can be NULL. In that case it constructs the
  // registry with storage functionality suspended.
  ChromeContentRulesRegistry(
      content::BrowserContext* browser_context,
      RulesCacheDelegate* cache_delegate,
      const PredicateEvaluatorsFactory& evaluators_factory);

  // ContentRulesRegistry:
  void MonitorWebContentsForRuleEvaluation(
      content::WebContents* contents) override;
  void DidFinishNavigation(
      content::WebContents* tab,
      content::NavigationHandle* navigation_handle) override;

  // RulesRegistry:
  std::string AddRulesImpl(
      const std::string& extension_id,
      const std::vector<linked_ptr<api::events::Rule>>& rules) override;
  std::string RemoveRulesImpl(
      const std::string& extension_id,
      const std::vector<std::string>& rule_identifiers) override;
  std::string RemoveAllRulesImpl(const std::string& extension_id) override;

  // content::NotificationObserver:
  void Observe(int type,
               const content::NotificationSource& source,
               const content::NotificationDetails& details) override;

  // DeclarativeContentConditionTrackerDelegate:
  void RequestEvaluation(content::WebContents* contents) override;
  bool ShouldManageConditionsForBrowserContext(
      content::BrowserContext* context) override;

  // Returns the number of active rules.
  size_t GetActiveRulesCountForTesting();

 protected:
  ~ChromeContentRulesRegistry() override;

 private:
  // The internal declarative rule representation. Corresponds to a declarative
  // API rule: https://developer.chrome.com/extensions/events.html#declarative.
  struct ContentRule {
   public:
    ContentRule(const Extension* extension,
                std::vector<std::unique_ptr<const ContentCondition>> conditions,
                std::vector<std::unique_ptr<const ContentAction>> actions,
                int priority);
    ~ContentRule();

    const Extension* extension;
    std::vector<std::unique_ptr<const ContentCondition>> conditions;
    std::vector<std::unique_ptr<const ContentAction>> actions;
    int priority;

   private:
    DISALLOW_COPY_AND_ASSIGN(ContentRule);
  };

  // Specifies what to do with evaluation requests.
  // TODO(wittman): Try to eliminate the need for IGNORE after refactoring to
  // treat all condition evaluation consistently.
  enum EvaluationDisposition {
    EVALUATE_REQUESTS,  // Evaluate immediately.
    DEFER_REQUESTS,  // Defer for later evaluation.
    IGNORE_REQUESTS  // Ignore.
  };

  class EvaluationScope;

  // Creates a ContentRule for |extension| given a json definition.  The format
  // of each condition and action's json is up to the specific ContentCondition
  // and ContentAction.  |extension| may be NULL in tests.  If |error| is empty,
  // the translation was successful and the returned rule is internally
  // consistent.
  std::unique_ptr<const ContentRule> CreateRule(
      const Extension* extension,
      const std::map<std::string, ContentPredicateFactory*>&
          predicate_factories,
      const api::events::Rule& api_rule,
      std::string* error);

  // True if this object is managing the rules for |context|.
  bool ManagingRulesForBrowserContext(content::BrowserContext* context);

  // True if |condition| matches on |tab|.
  static bool EvaluateConditionForTab(const ContentCondition* condition,
                                      content::WebContents* tab);

  std::set<const ContentRule*> GetMatchingRules(
      content::WebContents* tab) const;

  // Evaluates the conditions for |tab| based on the tab state and matching CSS
  // selectors.
  void EvaluateConditionsForTab(content::WebContents* tab);

  // Returns true if a rule created by |extension| should be evaluated for an
  // incognito renderer.
  bool ShouldEvaluateExtensionRulesForIncognitoRenderer(
      const Extension* extension) const;

  using ExtensionIdRuleIdPair = std::pair<extensions::ExtensionId, std::string>;
  using RulesMap = std::map<ExtensionIdRuleIdPair,
                            linked_ptr<const ContentRule>>;

  RulesMap content_rules_;

  // Maps a WebContents to the set of rules that match on that WebContents.
  // This lets us call Revert as appropriate. Note that this is expected to have
  // a key-value pair for every WebContents the registry is tracking, even if
  // the value is the empty set.
  std::map<content::WebContents*, std::set<const ContentRule*>> active_rules_;

  // The evaluators responsible for creating predicates and tracking
  // predicate-related state.
  std::vector<std::unique_ptr<ContentPredicateEvaluator>> evaluators_;

  // Specifies what to do with evaluation requests.
  EvaluationDisposition evaluation_disposition_;

  // Contains WebContents which require rule evaluation. Only used while
  // |evaluation_disposition_| is DEFER.
  std::set<content::WebContents*> evaluation_pending_;

  // Manages our notification registrations.
  content::NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(ChromeContentRulesRegistry);
};

}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_CONTENT_CHROME_CONTENT_RULES_REGISTRY_H_
