/* 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 mozilla_dom_DOMString_h
#define mozilla_dom_DOMString_h

#include "nsAtom.h"
#include "nsDOMString.h"
#include "nsString.h"

namespace mozilla::dom {

// DOMString is an AutoTString with a few extra dangerous methods needed for
// performance in some cases, where we want to propagate a "weak" reference to a
// string up. See bug 2023628 for some context.
//
// Some of these methods could actually live in nsTSubString, perhaps, but
// they're hard to get right, and make assumptions about what kind of strings
// they are called with in order to save cycles.
class DOMString final : public nsAutoString {
 public:
  enum NullHandling { eTreatNullAsNull, eTreatNullAsEmpty, eNullNotExpected };

  void SetKnownLiveAtom(nsAtom* aAtom, NullHandling aNullHandling) {
    AssertSetKnownLivePrecondition();
    MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
    if (aNullHandling == eNullNotExpected || aAtom) {
      // Static atoms are backed by literals, and dynamic ones by StringBuffer.
      DataFlags flags = DataFlags::TERMINATED;
      const char_type* data;
      if (aAtom->IsStatic()) {
        data = aAtom->AsStatic()->String();
        flags |= DataFlags::LITERAL;
      } else {
        MOZ_ASSERT(aAtom->AsDynamic()->StringBuffer());
        data = aAtom->AsDynamic()->String();
        flags |= DataFlags::STRINGBUFFER;
      }
      SetData(const_cast<char_type*>(data), aAtom->GetLength(), flags);
      AssertValid();
    } else if (aNullHandling == eTreatNullAsNull) {
      SetNull();
    }
  }

  void SetKnownLiveString(const nsAString& aString) {
    AssertSetKnownLivePrecondition();
    MOZ_ASSERT(aString.IsTerminated(),
               "If we are not terminated, then we need copying or so");
    // NOTE: This is a bit subtle, but the data flags we copy are basically "all
    // except OWNED | INLINE", which cause us to copy null-ness,
    // refcounted-ness, etc.
    const char_type* data = aString.Data();
    SetData(
        const_cast<char_type*>(data), aString.Length(),
        aString.GetDataFlags() & (DataFlags::TERMINATED | DataFlags::LITERAL |
                                  DataFlags::STRINGBUFFER | DataFlags::VOIDED));
    AssertValid();
  }

  void SetKnownLiveStringBuffer(StringBuffer* aBuffer, LengthStorage aLen) {
    AssertSetKnownLivePrecondition();
    MOZ_ASSERT(aBuffer);
    // NOTE: No DataFlags::OWNED.
    SetData(static_cast<char_type*>(aBuffer->Data()), aLen,
            DataFlags::STRINGBUFFER | DataFlags::TERMINATED);
    AssertValid();
  }

  void SetNull() { SetIsVoid(true); }
  bool IsNull() const { return IsVoid(); }

 private:
  void AssertSetKnownLivePrecondition() {
    MOZ_ASSERT(IsEmpty(), "We rely on this being called only on empty strings");
    MOZ_ASSERT(!(mDataFlags & DataFlags::OWNED), "Would leak");
  }
};

}  // namespace mozilla::dom

#endif  // mozilla_dom_DOMString_h
