parser/html/nsHtml5TreeOperation.h
author Kris Maglione <maglione.k@gmail.com>
Sun, 16 Dec 2018 18:36:32 -0800
changeset 456007 4a36400ab63cbc7ff3758c1a6fc1d1a209d9d131
parent 453405 519e2122622480badbf2281d6dc401d9e2c1a09f
permissions -rw-r--r--
Bug 1478124: Part 8d - Update netwerk module to use a static component manifest. r=mayhemer Differential Revision: https://phabricator.services.mozilla.com/D15042

/* 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/. */

#ifndef nsHtml5TreeOperation_h
#define nsHtml5TreeOperation_h

#include "nsHtml5DocumentMode.h"
#include "nsHtml5HtmlAttributes.h"
#include "mozilla/dom/FromParser.h"
#include "mozilla/NotNull.h"

class nsIContent;
class nsHtml5TreeOpExecutor;
class nsHtml5DocumentBuilder;
namespace mozilla {
class Encoding;

namespace dom {
class Text;
}  // namespace dom
}  // namespace mozilla

enum eHtml5TreeOperation {
  eTreeOpUninitialized,
  // main HTML5 ops
  eTreeOpAppend,
  eTreeOpDetach,
  eTreeOpAppendChildrenToNewParent,
  eTreeOpFosterParent,
  eTreeOpAppendToDocument,
  eTreeOpAddAttributes,
  eTreeOpDocumentMode,
  eTreeOpCreateHTMLElementNetwork,
  eTreeOpCreateHTMLElementNotNetwork,
  eTreeOpCreateSVGElementNetwork,
  eTreeOpCreateSVGElementNotNetwork,
  eTreeOpCreateMathMLElement,
  eTreeOpSetFormElement,
  eTreeOpAppendText,
  eTreeOpFosterParentText,
  eTreeOpAppendComment,
  eTreeOpAppendCommentToDocument,
  eTreeOpAppendDoctypeToDocument,
  eTreeOpGetDocumentFragmentForTemplate,
  eTreeOpGetFosterParent,
  // Gecko-specific on-pop ops
  eTreeOpMarkAsBroken,
  eTreeOpRunScript,
  eTreeOpRunScriptAsyncDefer,
  eTreeOpPreventScriptExecution,
  eTreeOpDoneAddingChildren,
  eTreeOpDoneCreatingElement,
  eTreeOpSetDocumentCharset,
  eTreeOpNeedsCharsetSwitchTo,
  eTreeOpUpdateStyleSheet,
  eTreeOpProcessMeta,
  eTreeOpProcessOfflineManifest,
  eTreeOpMarkMalformedIfScript,
  eTreeOpStreamEnded,
  eTreeOpSetStyleLineNumber,
  eTreeOpSetScriptLineNumberAndFreeze,
  eTreeOpSvgLoad,
  eTreeOpMaybeComplainAboutCharset,
  eTreeOpMaybeComplainAboutDeepTree,
  eTreeOpAddClass,
  eTreeOpAddViewSourceHref,
  eTreeOpAddViewSourceBase,
  eTreeOpAddError,
  eTreeOpAddLineNumberId,
  eTreeOpStartLayout,
  eTreeOpEnableEncodingMenu
};

class nsHtml5TreeOperationStringPair {
 private:
  nsString mPublicId;
  nsString mSystemId;

 public:
  nsHtml5TreeOperationStringPair(const nsAString& aPublicId,
                                 const nsAString& aSystemId)
      : mPublicId(aPublicId), mSystemId(aSystemId) {
    MOZ_COUNT_CTOR(nsHtml5TreeOperationStringPair);
  }

  ~nsHtml5TreeOperationStringPair() {
    MOZ_COUNT_DTOR(nsHtml5TreeOperationStringPair);
  }

  inline void Get(nsAString& aPublicId, nsAString& aSystemId) {
    aPublicId.Assign(mPublicId);
    aSystemId.Assign(mSystemId);
  }
};

class nsHtml5TreeOperation final {
  template <typename T>
  using NotNull = mozilla::NotNull<T>;
  using Encoding = mozilla::Encoding;

 public:
  static nsresult AppendTextToTextNode(const char16_t* aBuffer,
                                       uint32_t aLength,
                                       mozilla::dom::Text* aTextNode,
                                       nsHtml5DocumentBuilder* aBuilder);

  static nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
                             nsIContent* aParent,
                             nsHtml5DocumentBuilder* aBuilder);

  static nsresult Append(nsIContent* aNode, nsIContent* aParent,
                         nsHtml5DocumentBuilder* aBuilder);

  static nsresult AppendToDocument(nsIContent* aNode,
                                   nsHtml5DocumentBuilder* aBuilder);

  static void Detach(nsIContent* aNode, nsHtml5DocumentBuilder* aBuilder);

  static nsresult AppendChildrenToNewParent(nsIContent* aNode,
                                            nsIContent* aParent,
                                            nsHtml5DocumentBuilder* aBuilder);

  static nsresult FosterParent(nsIContent* aNode, nsIContent* aParent,
                               nsIContent* aTable,
                               nsHtml5DocumentBuilder* aBuilder);

  static nsresult AddAttributes(nsIContent* aNode,
                                nsHtml5HtmlAttributes* aAttributes,
                                nsHtml5DocumentBuilder* aBuilder);

  static void SetHTMLElementAttributes(mozilla::dom::Element* aElement,
                                       nsAtom* aName,
                                       nsHtml5HtmlAttributes* aAttributes);

  static nsIContent* CreateHTMLElement(
      nsAtom* aName, nsHtml5HtmlAttributes* aAttributes,
      mozilla::dom::FromParser aFromParser, nsNodeInfoManager* aNodeInfoManager,
      nsHtml5DocumentBuilder* aBuilder,
      mozilla::dom::HTMLContentCreatorFunction aCreator);

  static nsIContent* CreateSVGElement(
      nsAtom* aName, nsHtml5HtmlAttributes* aAttributes,
      mozilla::dom::FromParser aFromParser, nsNodeInfoManager* aNodeInfoManager,
      nsHtml5DocumentBuilder* aBuilder,
      mozilla::dom::SVGContentCreatorFunction aCreator);

  static nsIContent* CreateMathMLElement(nsAtom* aName,
                                         nsHtml5HtmlAttributes* aAttributes,
                                         nsNodeInfoManager* aNodeInfoManager,
                                         nsHtml5DocumentBuilder* aBuilder);

  static void SetFormElement(nsIContent* aNode, nsIContent* aParent);

  static nsresult AppendIsindexPrompt(nsIContent* parent,
                                      nsHtml5DocumentBuilder* aBuilder);

  static nsresult FosterParentText(nsIContent* aStackParent, char16_t* aBuffer,
                                   uint32_t aLength, nsIContent* aTable,
                                   nsHtml5DocumentBuilder* aBuilder);

  static nsresult AppendComment(nsIContent* aParent, char16_t* aBuffer,
                                int32_t aLength,
                                nsHtml5DocumentBuilder* aBuilder);

  static nsresult AppendCommentToDocument(char16_t* aBuffer, int32_t aLength,
                                          nsHtml5DocumentBuilder* aBuilder);

  static nsresult AppendDoctypeToDocument(nsAtom* aName,
                                          const nsAString& aPublicId,
                                          const nsAString& aSystemId,
                                          nsHtml5DocumentBuilder* aBuilder);

  static nsIContent* GetDocumentFragmentForTemplate(nsIContent* aNode);

  static nsIContent* GetFosterParent(nsIContent* aTable,
                                     nsIContent* aStackParent);

  static void PreventScriptExecution(nsIContent* aNode);

  static void DoneAddingChildren(nsIContent* aNode);

  static void DoneCreatingElement(nsIContent* aNode);

  static void SvgLoad(nsIContent* aNode);

  static void MarkMalformedIfScript(nsIContent* aNode);

  nsHtml5TreeOperation();

  ~nsHtml5TreeOperation();

  inline void Init(eHtml5TreeOperation aOpCode) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    mOpCode = aOpCode;
  }

  inline void Init(eHtml5TreeOperation aOpCode, nsIContentHandle* aNode) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aNode, "Initialized tree op with null node.");
    mOpCode = aOpCode;
    mOne.node = static_cast<nsIContent**>(aNode);
  }

  inline void Init(eHtml5TreeOperation aOpCode, nsIContentHandle* aNode,
                   nsIContentHandle* aParent) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aNode, "Initialized tree op with null node.");
    MOZ_ASSERT(aParent, "Initialized tree op with null parent.");
    mOpCode = aOpCode;
    mOne.node = static_cast<nsIContent**>(aNode);
    mTwo.node = static_cast<nsIContent**>(aParent);
  }

  inline void Init(eHtml5TreeOperation aOpCode, const nsACString& aString,
                   int32_t aInt32) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");

    int32_t len = aString.Length();
    char* str = new char[len + 1];
    const char* start = aString.BeginReading();
    for (int32_t i = 0; i < len; ++i) {
      str[i] = start[i];
    }
    str[len] = '\0';

    mOpCode = aOpCode;
    mOne.charPtr = str;
    mFour.integer = aInt32;
  }

  inline void Init(eHtml5TreeOperation aOpCode, const nsACString& aString,
                   int32_t aInt32, int32_t aLineNumber) {
    Init(aOpCode, aString, aInt32);
    mTwo.integer = aLineNumber;
  }

  inline void Init(eHtml5TreeOperation aOpCode,
                   NotNull<const Encoding*> aEncoding, int32_t aInt32) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");

    mOpCode = aOpCode;
    mOne.encoding = aEncoding;
    mFour.integer = aInt32;
  }

  inline void Init(eHtml5TreeOperation aOpCode,
                   NotNull<const Encoding*> aEncoding, int32_t aInt32,
                   int32_t aLineNumber) {
    Init(aOpCode, aEncoding, aInt32);
    mTwo.integer = aLineNumber;
  }

  inline void Init(eHtml5TreeOperation aOpCode, nsIContentHandle* aNode,
                   nsIContentHandle* aParent, nsIContentHandle* aTable) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aNode, "Initialized tree op with null node.");
    MOZ_ASSERT(aParent, "Initialized tree op with null parent.");
    MOZ_ASSERT(aTable, "Initialized tree op with null table.");
    mOpCode = aOpCode;
    mOne.node = static_cast<nsIContent**>(aNode);
    mTwo.node = static_cast<nsIContent**>(aParent);
    mThree.node = static_cast<nsIContent**>(aTable);
  }

  inline void Init(nsHtml5DocumentMode aMode) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    mOpCode = eTreeOpDocumentMode;
    mOne.mode = aMode;
  }

  inline void InitScript(nsIContentHandle* aNode) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aNode, "Initialized tree op with null node.");
    mOpCode = eTreeOpRunScript;
    mOne.node = static_cast<nsIContent**>(aNode);
    mTwo.state = nullptr;
  }

  inline void Init(int32_t aNamespace, nsAtom* aName,
                   nsHtml5HtmlAttributes* aAttributes,
                   nsIContentHandle* aTarget, nsIContentHandle* aIntendedParent,
                   bool aFromNetwork, nsHtml5ContentCreatorFunction aCreator) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aName, "Initialized tree op with null name.");
    MOZ_ASSERT(aTarget, "Initialized tree op with null target node.");

    if (aNamespace == kNameSpaceID_XHTML) {
      mOpCode = aFromNetwork ? eTreeOpCreateHTMLElementNetwork
                             : eTreeOpCreateHTMLElementNotNetwork;
      mFour.htmlCreator = aCreator.html;
    } else if (aNamespace == kNameSpaceID_SVG) {
      mOpCode = aFromNetwork ? eTreeOpCreateSVGElementNetwork
                             : eTreeOpCreateSVGElementNotNetwork;
      mFour.svgCreator = aCreator.svg;
    } else {
      MOZ_ASSERT(aNamespace == kNameSpaceID_MathML);
      mOpCode = eTreeOpCreateMathMLElement;
    }
    mFive.node = static_cast<nsIContent**>(aIntendedParent);
    mOne.node = static_cast<nsIContent**>(aTarget);
    mTwo.atom = aName;
    aName->AddRef();
    if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) {
      mThree.attributes = nullptr;
    } else {
      mThree.attributes = aAttributes;
    }
  }

  inline void Init(eHtml5TreeOperation aOpCode, char16_t* aBuffer,
                   int32_t aLength, nsIContentHandle* aStackParent,
                   nsIContentHandle* aTable) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aBuffer, "Initialized tree op with null buffer.");
    mOpCode = aOpCode;
    mOne.node = static_cast<nsIContent**>(aStackParent);
    mTwo.unicharPtr = aBuffer;
    mThree.node = static_cast<nsIContent**>(aTable);
    mFour.integer = aLength;
  }

  inline void Init(eHtml5TreeOperation aOpCode, char16_t* aBuffer,
                   int32_t aLength, nsIContentHandle* aParent) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aBuffer, "Initialized tree op with null buffer.");
    mOpCode = aOpCode;
    mOne.node = static_cast<nsIContent**>(aParent);
    mTwo.unicharPtr = aBuffer;
    mFour.integer = aLength;
  }

  inline void Init(eHtml5TreeOperation aOpCode, char16_t* aBuffer,
                   int32_t aLength) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aBuffer, "Initialized tree op with null buffer.");
    mOpCode = aOpCode;
    mTwo.unicharPtr = aBuffer;
    mFour.integer = aLength;
  }

  inline void Init(nsIContentHandle* aElement,
                   nsHtml5HtmlAttributes* aAttributes) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aElement, "Initialized tree op with null element.");
    mOpCode = eTreeOpAddAttributes;
    mOne.node = static_cast<nsIContent**>(aElement);
    mTwo.attributes = aAttributes;
  }

  inline void Init(nsAtom* aName, const nsAString& aPublicId,
                   const nsAString& aSystemId) {
    MOZ_ASSERT(aName);
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    mOpCode = eTreeOpAppendDoctypeToDocument;
    mOne.atom = aName;
    aName->AddRef();
    mTwo.stringPair = new nsHtml5TreeOperationStringPair(aPublicId, aSystemId);
  }

  inline void Init(nsIContentHandle* aElement, const char* aMsgId,
                   nsAtom* aAtom, nsAtom* aOtherAtom) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    mOpCode = eTreeOpAddError;
    mOne.node = static_cast<nsIContent**>(aElement);
    mTwo.charPtr = (char*)aMsgId;
    mThree.atom = aAtom;
    mFour.atom = aOtherAtom;
    if (aAtom) {
      aAtom->AddRef();
    }
    if (aOtherAtom) {
      aOtherAtom->AddRef();
    }
  }

  inline void Init(nsIContentHandle* aElement, const char* aMsgId,
                   nsAtom* aAtom) {
    Init(aElement, aMsgId, aAtom, nullptr);
  }

  inline void Init(nsIContentHandle* aElement, const char* aMsgId) {
    Init(aElement, aMsgId, nullptr, nullptr);
  }

  inline void Init(const char* aMsgId, bool aError, int32_t aLineNumber) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    mOpCode = eTreeOpMaybeComplainAboutCharset;
    mOne.charPtr = const_cast<char*>(aMsgId);
    mTwo.integer = aError;
    mThree.integer = aLineNumber;
  }

  inline void InitDeepTree(int32_t aLineNumber) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    mOpCode = eTreeOpMaybeComplainAboutDeepTree;
    mOne.integer = aLineNumber;
  }

  inline void Init(eHtml5TreeOperation aOpCode, const nsAString& aString) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");

    char16_t* str = ToNewUnicode(aString);
    mOpCode = aOpCode;
    mOne.unicharPtr = str;
  }

  inline void Init(eHtml5TreeOperation aOpCode, nsIContentHandle* aNode,
                   int32_t aInt) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aNode, "Initialized tree op with null node.");
    mOpCode = aOpCode;
    mOne.node = static_cast<nsIContent**>(aNode);
    mFour.integer = aInt;
  }

  inline void Init(nsresult aRv) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(NS_FAILED(aRv), "Initialized tree op with non-failure.");
    mOpCode = eTreeOpMarkAsBroken;
    mOne.result = aRv;
  }

  inline void InitAddClass(nsIContentHandle* aNode, const char16_t* aClass) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aNode, "Initialized tree op with null node.");
    MOZ_ASSERT(aClass, "Initialized tree op with null string.");
    // aClass must be a literal string that does not need freeing
    mOpCode = eTreeOpAddClass;
    mOne.node = static_cast<nsIContent**>(aNode);
    mTwo.unicharPtr = (char16_t*)aClass;
  }

  inline void InitAddLineNumberId(nsIContentHandle* aNode,
                                  const int32_t aLineNumber) {
    MOZ_ASSERT(mOpCode == eTreeOpUninitialized,
               "Op code must be uninitialized when initializing.");
    MOZ_ASSERT(aNode, "Initialized tree op with null node.");
    MOZ_ASSERT(aLineNumber > 0, "Initialized tree op with line number.");
    // aClass must be a literal string that does not need freeing
    mOpCode = eTreeOpAddLineNumberId;
    mOne.node = static_cast<nsIContent**>(aNode);
    mFour.integer = aLineNumber;
  }

  inline bool IsRunScript() { return mOpCode == eTreeOpRunScript; }

  inline bool IsMarkAsBroken() { return mOpCode == eTreeOpMarkAsBroken; }

  inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine) {
    NS_ASSERTION(
        IsRunScript(),
        "Setting a snapshot for a tree operation other than eTreeOpRunScript!");
    MOZ_ASSERT(aSnapshot, "Initialized tree op with null snapshot.");
    mTwo.state = aSnapshot;
    mFour.integer = aLine;
  }

  nsresult Perform(nsHtml5TreeOpExecutor* aBuilder, nsIContent** aScriptElement,
                   bool* aInterrupted, bool* aStreamEnded);

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

  // possible optimization:
  // Make the queue take items the size of pointer and make the op code
  // decide how many operands it dequeues after it.
  eHtml5TreeOperation mOpCode;
  union {
    nsIContent** node;
    nsAtom* atom;
    nsHtml5HtmlAttributes* attributes;
    nsHtml5DocumentMode mode;
    char16_t* unicharPtr;
    char* charPtr;
    nsHtml5TreeOperationStringPair* stringPair;
    nsAHtml5TreeBuilderState* state;
    int32_t integer;
    nsresult result;
    const Encoding* encoding;
    mozilla::dom::HTMLContentCreatorFunction htmlCreator;
    mozilla::dom::SVGContentCreatorFunction svgCreator;
  } mOne, mTwo, mThree, mFour, mFive;
};

#endif  // nsHtml5TreeOperation_h