Esempio n. 1
0
already_AddRefed<nsAtom>
CharacterData::GetCurrentValueAtom()
{
  nsAutoString val;
  GetData(val);
  return NS_Atomize(val);
}
Esempio n. 2
0
nsresult XULPersist::ApplyPersistentAttributesToElements(
    const nsAString& aID, nsCOMArray<Element>& aElements) {
  nsAutoCString utf8uri;
  nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  NS_ConvertUTF8toUTF16 uri(utf8uri);

  // Get a list of attributes for which persisted values are available
  nsCOMPtr<nsIStringEnumerator> attrs;
  rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  while (1) {
    bool hasmore = PR_FALSE;
    attrs->HasMore(&hasmore);
    if (!hasmore) {
      break;
    }

    nsAutoString attrstr;
    attrs->GetNext(attrstr);

    nsAutoString value;
    rv = mLocalStore->GetValue(uri, aID, attrstr, value);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    RefPtr<nsAtom> attr = NS_Atomize(attrstr);
    if (NS_WARN_IF(!attr)) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

    uint32_t cnt = aElements.Length();
    for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
      Element* element = aElements.SafeElementAt(i);
      if (!element) {
        continue;
      }

      // Applying persistent attributes to top level windows is handled
      // by nsXULWindow.
      if (element->IsXULElement(nsGkAtoms::window)) {
        if (nsCOMPtr<nsIXULWindow> win =
                mDocument->GetXULWindowIfToplevelChrome()) {
          continue;
        }
      }

      Unused << element->SetAttr(kNameSpaceID_None, attr, value, true);
    }
  }

  return NS_OK;
}
Esempio n. 3
0
/*
 * txIdPattern
 *
 * txIdPattern matches if the given node has a ID attribute with one
 * of the space delimited values.
 * This looks like the id() function, but may only have LITERALs as
 * argument.
 */
txIdPattern::txIdPattern(const nsAString& aString) {
  nsWhitespaceTokenizer tokenizer(aString);
  while (tokenizer.hasMoreTokens()) {
    // this can fail, XXX move to a Init(aString) method
    RefPtr<nsAtom> atom = NS_Atomize(tokenizer.nextToken());
    mIds.AppendElement(atom);
  }
}
Esempio n. 4
0
int32_t
txNamespaceMap::lookupNamespaceWithDefault(const nsAString& aPrefix)
{
    RefPtr<nsAtom> prefix = NS_Atomize(aPrefix);
    if (prefix != nsGkAtoms::_poundDefault) {
        return lookupNamespace(prefix);
    }

    return lookupNamespace(nullptr);
}
Esempio n. 5
0
void
EventTarget::SetEventHandler(const nsAString& aType,
                             EventHandlerNonNull* aHandler,
                             ErrorResult& aRv)
{
  if (!StringBeginsWith(aType, NS_LITERAL_STRING("on"))) {
    aRv.Throw(NS_ERROR_INVALID_ARG);
    return;
  }
  RefPtr<nsAtom> type = NS_Atomize(aType);
  SetEventHandler(type, aHandler);
}
Esempio n. 6
0
Element* AnonymousContent::GetElementById(const nsAString& aElementId) {
  // This can be made faster in the future if needed.
  RefPtr<nsAtom> elementId = NS_Atomize(aElementId);
  for (nsIContent* node = mContentNode; node;
       node = node->GetNextNode(mContentNode)) {
    if (!node->IsElement()) {
      continue;
    }
    nsAtom* id = node->AsElement()->GetID();
    if (id && id == elementId) {
      return node->AsElement();
    }
  }
  return nullptr;
}
Esempio n. 7
0
Element*
AnonymousContent::GetElementById(const nsAString& aElementId)
{
    // This can be made faster in the future if needed.
    nsCOMPtr<nsIAtom> elementId = NS_Atomize(aElementId);
    for (nsIContent* kid = mContentNode->GetFirstChild(); kid;
            kid = kid->GetNextNode(mContentNode)) {
        if (!kid->IsElement()) {
            continue;
        }
        nsIAtom* id = kid->AsElement()->GetID();
        if (id && id == elementId) {
            return kid->AsElement();
        }
    }
    return nullptr;
}
 explicit PropertyAtomHolder(const nsAString& aProperty)
 {
   nsCSSPropertyID propID =
     nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
   if (propID == eCSSPropertyExtra_variable) {
     mIsCustomProperty = true;
     mAtom = NS_Atomize(
       Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH)).take();
   } else {
     mIsCustomProperty = false;
     if (propID != eCSSProperty_UNKNOWN) {
       mAtom = nsCSSProps::AtomForProperty(propID);
     } else {
       mAtom = nullptr;
     }
   }
 }
NS_IMETHODIMP
nsThebesFontEnumerator::EnumerateFonts(const char *aLangGroup,
                                       const char *aGeneric,
                                       uint32_t *aCount,
                                       char16_t ***aResult)
{
    NS_ENSURE_ARG_POINTER(aCount);
    NS_ENSURE_ARG_POINTER(aResult);

    nsTArray<nsString> fontList;

    nsAutoCString generic;
    if (aGeneric)
        generic.Assign(aGeneric);
    else
        generic.SetIsVoid(true);

    nsCOMPtr<nsIAtom> langGroupAtom;
    if (aLangGroup) {
        nsAutoCString lowered;
        lowered.Assign(aLangGroup);
        ToLowerCase(lowered);
        langGroupAtom = NS_Atomize(lowered);
    }

    nsresult rv = gfxPlatform::GetPlatform()->GetFontList(langGroupAtom, generic, fontList);

    if (NS_FAILED(rv)) {
        *aCount = 0;
        *aResult = nullptr;
        /* XXX in this case, do we want to return the CSS generics? */
        return NS_OK;
    }

    char16_t **fs = static_cast<char16_t **>
                                (moz_xmalloc(fontList.Length() * sizeof(char16_t*)));
    for (uint32_t i = 0; i < fontList.Length(); i++) {
        fs[i] = ToNewUnicode(fontList[i]);
    }

    *aResult = fs;
    *aCount = fontList.Length();

    return NS_OK;
}
Esempio n. 10
0
void
EventTarget::SetEventHandler(const nsAString& aType,
                             EventHandlerNonNull* aHandler,
                             ErrorResult& aRv)
{
  if (!StringBeginsWith(aType, NS_LITERAL_STRING("on"))) {
    aRv.Throw(NS_ERROR_INVALID_ARG);
    return;
  }
  if (NS_IsMainThread()) {
    nsCOMPtr<nsIAtom> type = NS_Atomize(aType);
    SetEventHandler(type, EmptyString(), aHandler);
    return;
  }
  SetEventHandler(nullptr,
                  Substring(aType, 2), // Remove "on"
                  aHandler);
}
Esempio n. 11
0
already_AddRefed<DataTransfer>
DataTransfer::MozCloneForEvent(const nsAString& aEvent, ErrorResult& aRv)
{
  RefPtr<nsAtom> atomEvt = NS_Atomize(aEvent);
  if (!atomEvt) {
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    return nullptr;
  }
  EventMessage eventMessage = nsContentUtils::GetEventMessage(atomEvt);

  RefPtr<DataTransfer> dt;
  nsresult rv = Clone(mParent, eventMessage, false, false, getter_AddRefs(dt));
  if (NS_FAILED(rv)) {
    aRv.Throw(rv);
    return nullptr;
  }
  return dt.forget();
}
Esempio n. 12
0
void
nsReferencedElement::ResetWithID(nsIContent* aFromContent, const nsString& aID,
                                 bool aWatch)
{
  nsIDocument *doc = aFromContent->OwnerDoc();
  if (!doc)
    return;

  // XXX Need to take care of XBL/XBL2

  if (aWatch) {
    nsCOMPtr<nsIAtom> atom = NS_Atomize(aID);
    if (!atom)
      return;
    atom.swap(mWatchID);
  }

  mReferencingImage = false;

  HaveNewDocument(doc, aWatch, aID);
}
// XXX currently you can not add the same adjacency (i.e. you can't have multiple
// XXX stream converters registering to handle the same from-to combination. It's
// XXX not programatically prohibited, it's just that results are un-predictable
// XXX right now.
nsresult
nsStreamConverterService::AddAdjacency(const char *aContractID) {
    nsresult rv;
    // first parse out the FROM and TO MIME-types.

    nsAutoCString fromStr, toStr;
    rv = ParseFromTo(aContractID, fromStr, toStr);
    if (NS_FAILED(rv)) return rv;

    // Each MIME-type is a vertex in the graph, so first lets make sure
    // each MIME-type is represented as a key in our hashtable.

    nsCOMArray<nsIAtom> *fromEdges = mAdjacencyList.Get(fromStr);
    if (!fromEdges) {
        // There is no fromStr vertex, create one.
        fromEdges = new nsCOMArray<nsIAtom>();
        mAdjacencyList.Put(fromStr, fromEdges);
    }

    if (!mAdjacencyList.Get(toStr)) {
        // There is no toStr vertex, create one.
        mAdjacencyList.Put(toStr, new nsCOMArray<nsIAtom>());
    }

    // Now we know the FROM and TO types are represented as keys in the hashtable.
    // Let's "connect" the verticies, making an edge.

    nsCOMPtr<nsIAtom> vertex = NS_Atomize(toStr);
    if (!vertex) return NS_ERROR_OUT_OF_MEMORY;

    NS_ASSERTION(fromEdges, "something wrong in adjacency list construction");
    if (!fromEdges)
        return NS_ERROR_FAILURE;

    return fromEdges->AppendObject(vertex) ? NS_OK : NS_ERROR_FAILURE;
}
Esempio n. 14
0
int main(int argc, char** argv)
{
  ScopedXPCOM xpcom("TestStaticAtoms");
  if (xpcom.failed()) {
    return 1;
  }

  TestingAtoms::AddRefAtoms();

  NS_SealStaticAtomTable();

  nsCOMPtr<nsIAtom> atom = NS_Atomize("foo");
  if (!atom) {
    fail("Didn't get an atom for foo.");
    return 1;
  }
  if (atom->IsStaticAtom()) {
    passed("foo is a static atom");
  } else {
    fail("foo is not a static atom.");
    return 1;
  }
  if (atom == TestingAtoms::foo) {
    passed("foo is the right pointer");
  } else {
    fail("foo was not the right pointer");
    return 1;
  }
  nsIAtom* staticAtom = NS_GetStaticAtom(NS_LITERAL_STRING("foo"));
  if (!staticAtom) {
    fail("Did not get a static atom for foo");
    return 1;
  }

  if (atom == staticAtom) {
    passed("NS_Atomize and NS_GetStaticAtom returned the same atom.");
  } else {
    fail("NS_Atomize and NS_GetStaticAtom returned different atoms.");
    return 1;
  }

  MoreTestingAtoms::AddRefAtoms();
  
  atom = NS_Atomize("qux");
  if (!atom) {
    fail("Didn't get an atom for qux.");
    return 1;
  }
  if (atom->IsStaticAtom()) {
    passed("qux is a static atom");
  } else {
    fail("qux is not a static atom.");
    return 1;
  }
  if (atom == MoreTestingAtoms::qux) {
    passed("qux is the right pointer");
  } else {
    fail("qux was not the right pointer");
    return 1;
  }
  staticAtom = NS_GetStaticAtom(NS_LITERAL_STRING("qux"));
  if (staticAtom) {
    fail("Got an atom for qux. The static atom table was not sealed properly.");
    return 1;
  }
  return 0;
}
Esempio n. 15
0
void
nsReferencedElement::Reset(nsIContent* aFromContent, nsIURI* aURI,
                           bool aWatch, bool aReferenceImage)
{
  MOZ_ASSERT(aFromContent, "Reset() expects non-null content pointer");

  Unlink();

  if (!aURI)
    return;

  nsAutoCString refPart;
  aURI->GetRef(refPart);
  // Unescape %-escapes in the reference. The result will be in the
  // origin charset of the URL, hopefully...
  NS_UnescapeURL(refPart);

  nsAutoCString charset;
  aURI->GetOriginCharset(charset);
  nsAutoString ref;
  nsresult rv = nsContentUtils::ConvertStringFromEncoding(charset,
                                                          refPart,
                                                          ref);
  if (NS_FAILED(rv)) {
    // XXX Eww. If fallible malloc failed, using a conversion method that
    // assumes UTF-8 and doesn't handle UTF-8 errors.
    // https://bugzilla.mozilla.org/show_bug.cgi?id=951082
    CopyUTF8toUTF16(refPart, ref);
  }
  if (ref.IsEmpty())
    return;

  // Get the current document
  nsIDocument *doc = aFromContent->OwnerDoc();
  if (!doc)
    return;

  nsIContent* bindingParent = aFromContent->GetBindingParent();
  if (bindingParent) {
    nsXBLBinding* binding = bindingParent->GetXBLBinding();
    if (binding) {
      bool isEqualExceptRef;
      rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(),
                                 &isEqualExceptRef);
      if (NS_SUCCEEDED(rv) && isEqualExceptRef) {
        // XXX sXBL/XBL2 issue
        // Our content is an anonymous XBL element from a binding inside the
        // same document that the referenced URI points to. In order to avoid
        // the risk of ID collisions we restrict ourselves to anonymous
        // elements from this binding; specifically, URIs that are relative to
        // the binding document should resolve to the copy of the target
        // element that has been inserted into the bound document.
        // If the URI points to a different document we don't need this
        // restriction.
        nsINodeList* anonymousChildren =
          doc->BindingManager()->GetAnonymousNodesFor(bindingParent);

        if (anonymousChildren) {
          uint32_t length;
          anonymousChildren->GetLength(&length);
          for (uint32_t i = 0; i < length && !mElement; ++i) {
            mElement =
              nsContentUtils::MatchElementId(anonymousChildren->Item(i), ref);
          }
        }

        // We don't have watching working yet for XBL, so bail out here.
        return;
      }
    }
  }

  bool isEqualExceptRef;
  rv = aURI->EqualsExceptRef(doc->GetDocumentURI(), &isEqualExceptRef);
  if (NS_FAILED(rv) || !isEqualExceptRef) {
    RefPtr<nsIDocument::ExternalResourceLoad> load;
    doc = doc->RequestExternalResource(aURI, aFromContent,
                                       getter_AddRefs(load));
    if (!doc) {
      if (!load || !aWatch) {
        // Nothing will ever happen here
        return;
      }

      DocumentLoadNotification* observer =
        new DocumentLoadNotification(this, ref);
      mPendingNotification = observer;
      if (observer) {
        load->AddObserver(observer);
      }
      // Keep going so we set up our watching stuff a bit
    }
  }

  if (aWatch) {
    nsCOMPtr<nsIAtom> atom = NS_Atomize(ref);
    if (!atom)
      return;
    atom.swap(mWatchID);
  }

  mReferencingImage = aReferenceImage;

  HaveNewDocument(doc, aWatch, ref);
}
Esempio n. 16
0
nsresult txPatternParser::createLocPathPattern(txExprLexer& aLexer,
                                               txIParseContext* aContext,
                                               txPattern*& aPattern)
{
    nsresult rv = NS_OK;

    bool isChild = true;
    bool isAbsolute = false;
    txPattern* stepPattern = 0;
    txLocPathPattern* pathPattern = 0;

    Token::Type type = aLexer.peek()->mType;
    switch (type) {
        case Token::ANCESTOR_OP:
            isChild = false;
            isAbsolute = true;
            aLexer.nextToken();
            break;
        case Token::PARENT_OP:
            aLexer.nextToken();
            isAbsolute = true;
            if (aLexer.peek()->mType == Token::END ||
                aLexer.peek()->mType == Token::UNION_OP) {
                aPattern = new txRootPattern();
                return NS_OK;
            }
            break;
        case Token::FUNCTION_NAME_AND_PAREN:
            // id(Literal) or key(Literal, Literal)
            {
                RefPtr<nsAtom> nameAtom =
                    NS_Atomize(aLexer.nextToken()->Value());
                if (nameAtom == nsGkAtoms::id) {
                    rv = createIdPattern(aLexer, stepPattern);
                }
                else if (nameAtom == nsGkAtoms::key) {
                    rv = createKeyPattern(aLexer, aContext, stepPattern);
                }
                if (NS_FAILED(rv))
                    return rv;
            }
            break;
        default:
            break;
    }
    if (!stepPattern) {
        rv = createStepPattern(aLexer, aContext, stepPattern);
        if (NS_FAILED(rv))
            return rv;
    }

    type = aLexer.peek()->mType;
    if (!isAbsolute && type != Token::PARENT_OP
        && type != Token::ANCESTOR_OP) {
        aPattern = stepPattern;
        return NS_OK;
    }

    pathPattern = new txLocPathPattern();
    if (isAbsolute) {
        txRootPattern* root = new txRootPattern();
#ifdef TX_TO_STRING
        root->setSerialize(false);
#endif

        rv = pathPattern->addStep(root, isChild);
        if (NS_FAILED(rv)) {
            delete stepPattern;
            delete pathPattern;
            delete root;
            return NS_ERROR_OUT_OF_MEMORY;
        }
    }

    rv = pathPattern->addStep(stepPattern, isChild);
    if (NS_FAILED(rv)) {
        delete stepPattern;
        delete pathPattern;
        return NS_ERROR_OUT_OF_MEMORY;
    }
    stepPattern = 0; // stepPattern is part of pathPattern now

    while (type == Token::PARENT_OP || type == Token::ANCESTOR_OP) {
        isChild = type == Token::PARENT_OP;
        aLexer.nextToken();
        rv = createStepPattern(aLexer, aContext, stepPattern);
        if (NS_FAILED(rv)) {
            delete pathPattern;
            return rv;
        }
        rv = pathPattern->addStep(stepPattern, isChild);
        if (NS_FAILED(rv)) {
            delete stepPattern;
            delete pathPattern;
            return NS_ERROR_OUT_OF_MEMORY;
        }
        stepPattern = 0; // stepPattern is part of pathPattern now
        type = aLexer.peek()->mType;
    }
    aPattern = pathPattern;
    return rv;
}
Esempio n. 17
0
nsresult CharacterData::SetTextInternal(
    uint32_t aOffset, uint32_t aCount, const char16_t* aBuffer,
    uint32_t aLength, bool aNotify,
    CharacterDataChangeInfo::Details* aDetails) {
  MOZ_ASSERT(aBuffer || !aLength, "Null buffer passed to SetTextInternal!");

  // sanitize arguments
  uint32_t textLength = mText.GetLength();
  if (aOffset > textLength) {
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
  }

  if (aCount > textLength - aOffset) {
    aCount = textLength - aOffset;
  }

  uint32_t endOffset = aOffset + aCount;

  // Make sure the text fragment can hold the new data.
  if (aLength > aCount && !mText.CanGrowBy(aLength - aCount)) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  Document* document = GetComposedDoc();
  mozAutoDocUpdate updateBatch(document, aNotify);

  bool haveMutationListeners =
      aNotify && nsContentUtils::HasMutationListeners(
                     this, NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED, this);

  RefPtr<nsAtom> oldValue;
  if (haveMutationListeners) {
    oldValue = GetCurrentValueAtom();
  }

  if (aNotify) {
    CharacterDataChangeInfo info = {aOffset == textLength, aOffset, endOffset,
                                    aLength, aDetails};
    nsNodeUtils::CharacterDataWillChange(this, info);
  }

  Directionality oldDir = eDir_NotSet;
  bool dirAffectsAncestor =
      (NodeType() == TEXT_NODE &&
       TextNodeWillChangeDirection(static_cast<nsTextNode*>(this), &oldDir,
                                   aOffset));

  if (aOffset == 0 && endOffset == textLength) {
    // Replacing whole text or old text was empty.  Don't bother to check for
    // bidi in this string if the document already has bidi enabled.
    // If this is marked as "maybe modified frequently", the text should be
    // stored as char16_t since converting char* to char16_t* is expensive.
    bool ok =
        mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled(),
                    HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY));
    NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
  } else if (aOffset == textLength) {
    // Appending to existing
    bool ok =
        mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled(),
                     HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY));
    NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
  } else {
    // Merging old and new

    bool bidi = mText.IsBidi();

    // Allocate new buffer
    int32_t newLength = textLength - aCount + aLength;
    // Use nsString and not nsAutoString so that we get a nsStringBuffer which
    // can be just AddRefed in nsTextFragment.
    nsString to;
    to.SetCapacity(newLength);

    // Copy over appropriate data
    if (aOffset) {
      mText.AppendTo(to, 0, aOffset);
    }
    if (aLength) {
      to.Append(aBuffer, aLength);
      if (!bidi && (!document || !document->GetBidiEnabled())) {
        bidi = HasRTLChars(MakeSpan(aBuffer, aLength));
      }
    }
    if (endOffset != textLength) {
      mText.AppendTo(to, endOffset, textLength - endOffset);
    }

    // If this is marked as "maybe modified frequently", the text should be
    // stored as char16_t since converting char* to char16_t* is expensive.
    // Use char16_t also when we have bidi characters.
    bool use2b = HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY) || bidi;
    bool ok = mText.SetTo(to, false, use2b);
    mText.SetBidi(bidi);

    NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
  }

  UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE);

  if (document && mText.IsBidi()) {
    // If we found bidi characters in mText.SetTo() above, indicate that the
    // document contains bidi characters.
    document->SetBidiEnabled();
  }

  if (dirAffectsAncestor) {
    // dirAffectsAncestor being true implies that we have a text node, see
    // above.
    MOZ_ASSERT(NodeType() == TEXT_NODE);
    TextNodeChangedDirection(static_cast<nsTextNode*>(this), oldDir, aNotify);
  }

  // Notify observers
  if (aNotify) {
    CharacterDataChangeInfo info = {aOffset == textLength, aOffset, endOffset,
                                    aLength, aDetails};
    nsNodeUtils::CharacterDataChanged(this, info);

    if (haveMutationListeners) {
      InternalMutationEvent mutation(true, eLegacyCharacterDataModified);

      mutation.mPrevAttrValue = oldValue;
      if (aLength > 0) {
        nsAutoString val;
        mText.AppendTo(val);
        mutation.mNewAttrValue = NS_Atomize(val);
      }

      mozAutoSubtreeModified subtree(OwnerDoc(), this);
      (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
    }
  }

  return NS_OK;
}
Esempio n. 18
0
nsIAtom*
Gecko_Atomize(const char* aString, uint32_t aLength)
{
  return NS_Atomize(nsDependentCSubstring(aString, aLength)).take();
}
Esempio n. 19
0
nsresult
XULSortServiceImpl::InitializeSortState(nsIContent* aRootElement,
                                        nsIContent* aContainer,
                                        const nsAString& aSortKey,
                                        const nsAString& aSortHints,
                                        nsSortState* aSortState)
{
  // used as an optimization for the content builder
  if (aContainer != aSortState->lastContainer.get()) {
    aSortState->lastContainer = aContainer;
    aSortState->lastWasFirst = false;
    aSortState->lastWasLast = false;
  }

  // The attributes allowed are either:
  //    sort="key1 key2 ..."
  // or sortResource="key1" sortResource2="key2"
  // The latter is for backwards compatibility, and is equivalent to concatenating
  // both values in the sort attribute
  nsAutoString sort(aSortKey);
  aSortState->sortKeys.Clear();
  if (sort.IsEmpty()) {
    nsAutoString sortResource, sortResource2;
    aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource, sortResource);
    if (!sortResource.IsEmpty()) {
      nsCOMPtr<nsIAtom> sortkeyatom = NS_Atomize(sortResource);
      aSortState->sortKeys.AppendObject(sortkeyatom);
      sort.Append(sortResource);

      aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource2, sortResource2);
      if (!sortResource2.IsEmpty()) {
        nsCOMPtr<nsIAtom> sortkeyatom2 = NS_Atomize(sortResource2);
        aSortState->sortKeys.AppendObject(sortkeyatom2);
        sort.Append(' ');
        sort.Append(sortResource2);
      }
    }
  }
  else {
    nsWhitespaceTokenizer tokenizer(sort);
    while (tokenizer.hasMoreTokens()) {
      nsCOMPtr<nsIAtom> keyatom = NS_Atomize(tokenizer.nextToken());
      NS_ENSURE_TRUE(keyatom, NS_ERROR_OUT_OF_MEMORY);
      aSortState->sortKeys.AppendObject(keyatom);
    }
  }

  aSortState->sort.Assign(sort);
  aSortState->direction = nsSortState_natural;

  bool noNaturalState = false;
  nsWhitespaceTokenizer tokenizer(aSortHints);
  while (tokenizer.hasMoreTokens()) {
    const nsDependentSubstring& token(tokenizer.nextToken());
    if (token.EqualsLiteral("comparecase"))
      aSortState->sortHints |= nsIXULSortService::SORT_COMPARECASE;
    else if (token.EqualsLiteral("integer"))
      aSortState->sortHints |= nsIXULSortService::SORT_INTEGER;
    else if (token.EqualsLiteral("descending"))
      aSortState->direction = nsSortState_descending;
    else if (token.EqualsLiteral("ascending"))
      aSortState->direction = nsSortState_ascending;
    else if (token.EqualsLiteral("twostate"))
      noNaturalState = true;
  }

  // if the twostate flag was set, the natural order is skipped and only
  // ascending and descending are allowed
  if (aSortState->direction == nsSortState_natural && noNaturalState) {
    aSortState->direction = nsSortState_ascending;
  }

  // set up sort order info
  aSortState->invertSort = false;

  nsAutoString existingsort;
  aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, existingsort);
  nsAutoString existingsortDirection;
  aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, existingsortDirection);

  // if just switching direction, set the invertSort flag
  if (sort.Equals(existingsort)) {
    if (aSortState->direction == nsSortState_descending) {
      if (existingsortDirection.EqualsLiteral("ascending"))
        aSortState->invertSort = true;
    }
    else if (aSortState->direction == nsSortState_ascending &&
             existingsortDirection.EqualsLiteral("descending")) {
      aSortState->invertSort = true;
    }
  }

  // sort items between separators independently
  aSortState->inbetweenSeparatorSort =
    aRootElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortSeparators,
                              nsGkAtoms::_true, eCaseMatters);

  // sort static content (non template generated nodes) after generated content
  aSortState->sortStaticsLast = aRootElement->AttrValueIs(kNameSpaceID_None,
                                  nsGkAtoms::sortStaticsLast,
                                  nsGkAtoms::_true, eCaseMatters);

  aSortState->initialized = true;

  return NS_OK;
}