(*aTextCallback)(this, Substring(aAttributeValue, mark, backup - mark), aClosure);
(*aTextCallback)(this, Substring(aAttributeValue, mark, backup), aClosure);
nsXULTemplateBuilder::ParseAttribute(const nsAReadableString& aAttributeValue,                                     void (*aVariableCallback)(nsXULTemplateBuilder*, const nsAReadableString&, void*),                                     void (*aTextCallback)(nsXULTemplateBuilder*, const nsAReadableString&, void*),                                     void* aClosure){    // XXX wish we could use iterators, but we can't right now.    PRInt32 len = aAttributeValue.Length();    PRUint32 mark = 0, backup = 0;    for (PRInt32 i = 0; i < len; backup = ++i) {        // A variable is either prefixed with '?' (in the extended        // syntax) or "rdf:" (in the simple syntax).        PRBool isvar;        if (aAttributeValue[i] == PRUnichar('?')) {            isvar = PR_TRUE;        }        else if ((aAttributeValue[i] == PRUnichar('r') && ++i < len) &&                 (aAttributeValue[i] == PRUnichar('d') && ++i < len) &&                 (aAttributeValue[i] == PRUnichar('f') && ++i < len) &&                 (aAttributeValue[i] == PRUnichar(':'))) {            isvar = PR_TRUE;        }        else {            isvar = PR_FALSE;        }        if (! isvar || ++i >= len) {            // It's not a variable, or we ran off the end of the            // string after the initial variable prefix. Since we may            // have slurped down some characters before realizing that            // fact, back up to the point where we started.            i = backup;            continue;        }        else if (backup != mark && aTextCallback) {            // Okay, we've found a variable, and there's some vanilla            // text that's been buffered up. Flush it.            (*aTextCallback)(this, Substring(aAttributeValue, mark, backup - mark), aClosure);        }        // Construct a substring that is the symbol we need to look up        // in the rule's symbol table. The symbol is terminated by a        // space character, a caret, or the end of the string,        // whichever comes first.        PRUint32 first = backup;        PRUnichar c = 0;        while (i < len) {            c = aAttributeValue[i];            if ((c == PRUnichar(' ')) || (c == PRUnichar('^')))                break;            ++i;        }        PRUint32 last = i;        // Back up so we don't consume the terminating character        // *unless* the terminating character was a caret: the caret        // means "concatenate with no space in between".        if (c != PRUnichar('^'))            --i;        (*aVariableCallback)(this, Substring(aAttributeValue, first, last - first), aClosure);        mark = i + 1;    }    if (backup != mark && aTextCallback) {        // If there's any text left over, then fire the text callback        (*aTextCallback)(this, Substring(aAttributeValue, mark, backup - mark), aClosure);    }}