/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* parsing of CSS stylesheets, based on a token stream from the CSS scanner */

#ifndef nsCSSParser_h___
#define nsCSSParser_h___

#include "mozilla/Attributes.h"
#include "mozilla/css/Loader.h"

#include "nsCSSPropertyID.h"
#include "nsCSSScanner.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsStringFwd.h"
#include "nsTArrayForwardDeclare.h"

class nsIPrincipal;
class nsIURI;
struct nsCSSSelectorList;
class nsMediaList;
class nsMediaQuery;
class nsCSSKeyframeRule;
class nsCSSValue;
struct nsRuleData;

namespace mozilla {
class CSSStyleSheet;
class CSSVariableValues;
namespace css {
class Rule;
class Declaration;
class StyleRule;
} // namespace css
} // namespace mozilla

// Interface to the css parser.

class MOZ_STACK_CLASS nsCSSParser {
public:
  explicit nsCSSParser(mozilla::css::Loader* aLoader = nullptr,
                       mozilla::CSSStyleSheet* aSheet = nullptr);
  ~nsCSSParser();

  static void Startup();
  static void Shutdown();

private:
  nsCSSParser(nsCSSParser const&) = delete;
  nsCSSParser& operator=(nsCSSParser const&) = delete;

public:
  /**
   * Parse aInput into the stylesheet that was previously passed to the
   * constructor.  Calling this method on an nsCSSParser that had nullptr
   * passed in as the style sheet is an error.
   *
   * @param aInput the data to parse
   * @param aSheetURL the URI to use as the sheet URI (for error reporting).
   *                  This must match the URI of the sheet passed to
   *                  the constructor.
   * @param aBaseURI the URI to use for relative URI resolution
   * @param aSheetPrincipal the principal of the stylesheet.  This must match
   *                        the principal of the sheet passed to the
   *                        constructor.
   * @param aLineNumber the line number of the first line of the sheet.
   * @param aReusableSheets style sheets that can be reused by an @import.
   *                        This can be nullptr.
   */
  nsresult ParseSheet(const nsAString& aInput,
                      nsIURI*          aSheetURL,
                      nsIURI*          aBaseURI,
                      nsIPrincipal*    aSheetPrincipal,
                      uint32_t         aLineNumber,
                      mozilla::css::LoaderReusableStyleSheets* aReusableSheets =
                        nullptr);

  // Parse HTML style attribute or its equivalent in other markup
  // languages.  aBaseURL is the base url to use for relative links in
  // the declaration.
  already_AddRefed<mozilla::css::Declaration>
           ParseStyleAttribute(const nsAString&  aAttributeValue,
                               nsIURI*           aDocURL,
                               nsIURI*           aBaseURL,
                               nsIPrincipal*     aNodePrincipal);

  // Parse the body of a declaration block.  Very similar to
  // ParseStyleAttribute, but used under different circumstances.
  // The contents of aDeclaration will be erased and replaced with the
  // results of parsing; aChanged will be set true if the aDeclaration
  // argument was modified.
  nsresult ParseDeclarations(const nsAString&  aBuffer,
                             nsIURI*           aSheetURL,
                             nsIURI*           aBaseURL,
                             nsIPrincipal*     aSheetPrincipal,
                             mozilla::css::Declaration* aDeclaration,
                             bool*           aChanged);

  nsresult ParseRule(const nsAString&        aRule,
                     nsIURI*                 aSheetURL,
                     nsIURI*                 aBaseURL,
                     nsIPrincipal*           aSheetPrincipal,
                     mozilla::css::Rule**    aResult);

  // Parse the value of a single CSS property, and add or replace that
  // property in aDeclaration.
  //
  // SVG "mapped attributes" (which correspond directly to CSS
  // properties) are parsed slightly differently from regular CSS; in
  // particular, units may be omitted from <length>.  The 'aIsSVGMode'
  // argument controls this quirk.  Note that this *only* applies to
  // mapped attributes, not inline styles or full style sheets in SVG.
  void ParseProperty(const nsCSSPropertyID aPropID,
                     const nsAString&    aPropValue,
                     nsIURI*             aSheetURL,
                     nsIURI*             aBaseURL,
                     nsIPrincipal*       aSheetPrincipal,
                     mozilla::css::Declaration* aDeclaration,
                     bool*               aChanged,
                     bool                aIsImportant,
                     bool                aIsSVGMode = false);

  // Same as ParseProperty but returns an nsCSSValue in aResult
  // rather than storing the property in a Declaration.  aPropID
  // must be a longhand property.
  void ParseLonghandProperty(const nsCSSPropertyID aPropID,
                             const nsAString&    aPropValue,
                             nsIURI*             aSheetURL,
                             nsIURI*             aBaseURL,
                             nsIPrincipal*       aSheetPrincipal,
                             nsCSSValue&         aResult);

  // Parse the value of a CSS transform property. Returns
  // whether the value was successfully parsed. If
  // aDisallowRelativeValues is true then this method will
  // only successfully parse if all values are numbers or
  // have non-relative dimensions.
  bool ParseTransformProperty(const nsAString& aPropValue,
                              bool             aDisallowRelativeValues,
                              nsCSSValue&      aResult);

  // The same as ParseProperty but for a variable.
  void ParseVariable(const nsAString&    aVariableName,
                     const nsAString&    aPropValue,
                     nsIURI*             aSheetURL,
                     nsIURI*             aBaseURL,
                     nsIPrincipal*       aSheetPrincipal,
                     mozilla::css::Declaration* aDeclaration,
                     bool*               aChanged,
                     bool                aIsImportant);
  /**
   * Parse aBuffer into a media list |aMediaList|, which must be
   * non-null, replacing its current contents.  If aHTMLMode is true,
   * parse according to HTML rules, with commas as the most important
   * delimiter.  Otherwise, parse according to CSS rules, with
   * parentheses and strings more important than commas.  |aURL| and
   * |aLineNumber| are used for error reporting.
   */
  void ParseMediaList(const nsSubstring& aBuffer,
                      nsIURI*            aURL,
                      uint32_t           aLineNumber,
                      nsMediaList*       aMediaList,
                      bool               aHTMLMode);

  /*
   * Parse aBuffer into a list of media queries and their associated values,
   * according to grammar:
   *    <source-size-list> = <source-size>#?
   *    <source-size> = <media-condition>? <length>
   *
   * Note that this grammar is top-level: The function expects to consume the
   * entire input buffer.
   *
   * Output arrays overwritten (not appended) and are cleared in case of parse
   * failure.
   */
  bool ParseSourceSizeList(const nsAString& aBuffer,
                           nsIURI* aURI, // for error reporting
                           uint32_t aLineNumber, // for error reporting
                           InfallibleTArray< nsAutoPtr<nsMediaQuery> >& aQueries,
                           InfallibleTArray<nsCSSValue>& aValues,
                           bool aHTMLMode);

  /**
   * Parse aBuffer into a nsCSSValue |aValue|. Will return false
   * if aBuffer is not a valid font family list.
   */
  bool ParseFontFamilyListString(const nsSubstring& aBuffer,
                                 nsIURI*            aURL,
                                 uint32_t           aLineNumber,
                                 nsCSSValue&        aValue);

  /**
   * Parse aBuffer into a nsCSSValue |aValue|. Will return false
   * if aBuffer is not a valid CSS color specification.
   * One can use nsRuleNode::ComputeColor to compute an nscolor from
   * the returned nsCSSValue.
   */
  bool ParseColorString(const nsSubstring& aBuffer,
                        nsIURI*            aURL,
                        uint32_t           aLineNumber,
                        nsCSSValue&        aValue,
                        bool               aSuppressErrors = false);

  /**
   * Parse aBuffer into a selector list.  On success, caller must
   * delete *aSelectorList when done with it.
   */
  nsresult ParseSelectorString(const nsSubstring&  aSelectorString,
                               nsIURI*             aURL,
                               uint32_t            aLineNumber,
                               nsCSSSelectorList** aSelectorList);

  /*
   * Parse a keyframe rule (which goes inside an @keyframes rule).
   * Return it if the parse was successful.
   */
  already_AddRefed<nsCSSKeyframeRule>
  ParseKeyframeRule(const nsSubstring& aBuffer,
                    nsIURI*            aURL,
                    uint32_t           aLineNumber);

  /*
   * Parse a selector list for a keyframe rule.  Return whether
   * the parse succeeded.
   */
  bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString,
                                   nsIURI*            aURL,
                                   uint32_t           aLineNumber,
                                   InfallibleTArray<float>& aSelectorList);

  /**
   * Parse a property and value and return whether the property/value pair
   * is supported.
   */
  bool EvaluateSupportsDeclaration(const nsAString& aProperty,
                                   const nsAString& aValue,
                                   nsIURI* aDocURL,
                                   nsIURI* aBaseURL,
                                   nsIPrincipal* aDocPrincipal);

  /**
   * Parse an @supports condition and returns the result of evaluating the
   * condition.
   */
  bool EvaluateSupportsCondition(const nsAString& aCondition,
                                 nsIURI* aDocURL,
                                 nsIURI* aBaseURL,
                                 nsIPrincipal* aDocPrincipal);

  typedef void (*VariableEnumFunc)(const nsAString&, void*);

  /**
   * Parses aPropertyValue as a property value and calls aFunc for each
   * variable reference that is found.  Returns false if there was
   * a syntax error in the use of variable references.
   */
  bool EnumerateVariableReferences(const nsAString& aPropertyValue,
                                   VariableEnumFunc aFunc,
                                   void* aData);

  /**
   * Parses aPropertyValue as a property value and resolves variable references
   * using the values in aVariables.
   */
  bool ResolveVariableValue(const nsAString& aPropertyValue,
                            const mozilla::CSSVariableValues* aVariables,
                            nsString& aResult,
                            nsCSSTokenSerializationType& aFirstToken,
                            nsCSSTokenSerializationType& aLastToken);

  /**
   * Parses a string as a CSS token stream value for particular property,
   * resolving any variable references.  The parsed property value is stored
   * in the specified nsRuleData object.  If aShorthandPropertyID has a value
   * other than eCSSProperty_UNKNOWN, this is the property that will be parsed;
   * otherwise, aPropertyID will be parsed.  Either way, only aPropertyID,
   * a longhand property, will be copied over to the rule data.
   *
   * If the property cannot be parsed, it will be treated as if 'initial' or
   * 'inherit' were specified, for non-inherited and inherited properties
   * respectively.
   */
  void ParsePropertyWithVariableReferences(
                                   nsCSSPropertyID aPropertyID,
                                   nsCSSPropertyID aShorthandPropertyID,
                                   const nsAString& aValue,
                                   const mozilla::CSSVariableValues* aVariables,
                                   nsRuleData* aRuleData,
                                   nsIURI* aDocURL,
                                   nsIURI* aBaseURL,
                                   nsIPrincipal* aDocPrincipal,
                                   mozilla::CSSStyleSheet* aSheet,
                                   uint32_t aLineNumber,
                                   uint32_t aLineOffset);

  bool ParseCounterStyleName(const nsAString& aBuffer,
                             nsIURI* aURL,
                             nsAString& aName);

  bool ParseCounterDescriptor(nsCSSCounterDesc aDescID,
                              const nsAString& aBuffer,
                              nsIURI* aSheetURL,
                              nsIURI* aBaseURL,
                              nsIPrincipal* aSheetPrincipal,
                              nsCSSValue& aValue);

  bool ParseFontFaceDescriptor(nsCSSFontDesc aDescID,
                               const nsAString& aBuffer,
                               nsIURI* aSheetURL,
                               nsIURI* aBaseURL,
                               nsIPrincipal* aSheetPrincipal,
                               nsCSSValue& aValue);

  // Check whether a given value can be applied to a property.
  bool IsValueValidForProperty(const nsCSSPropertyID aPropID,
                               const nsAString&    aPropValue);

  // Return the default value to be used for -moz-control-character-visibility,
  // from preferences (cached by our Startup(), so that both nsStyleText and
  // nsRuleNode can have fast access to it).
  static uint8_t ControlCharVisibilityDefault();

protected:
  // This is a CSSParserImpl*, but if we expose that type name in this
  // header, we can't put the type definition (in nsCSSParser.cpp) in
  // the anonymous namespace.
  void* mImpl;
};

#endif /* nsCSSParser_h___ */
