예제 #1
0
PRBool
nsCounterManager::AddResetOrIncrement(nsIFrame *aFrame, PRInt32 aIndex,
                                      const nsStyleCounterData *aCounterData,
                                      nsCounterNode::Type aType)
{
    nsCounterChangeNode *node =
        new nsCounterChangeNode(aFrame, aType, aCounterData->mValue, aIndex);
    if (!node)
        return PR_FALSE;

    nsCounterList *counterList = CounterListFor(aCounterData->mCounter);
    if (!counterList) {
        NS_NOTREACHED("CounterListFor failed (should only happen on OOM)");
        return PR_FALSE;
    }

    counterList->Insert(node);
    if (!counterList->IsLast(node)) {
        // Tell the caller it's responsible for recalculating the entire
        // list.
        counterList->SetDirty();
        return PR_TRUE;
    }

    // Don't call Calc() if the list is already dirty -- it'll be recalculated
    // anyway, and trying to calculate with a dirty list doesn't work.
    if (NS_LIKELY(!counterList->IsDirty())) {
        node->Calc(counterList);
    }
    return PR_FALSE;
}
 nsHtml5OtherDocUpdate(nsIDocument* aCurrentDoc, nsIDocument* aExecutorDoc)
 {
   NS_PRECONDITION(aCurrentDoc, "Node has no doc?");
   NS_PRECONDITION(aExecutorDoc, "Executor has no doc?");
   if (NS_LIKELY(aCurrentDoc == aExecutorDoc)) {
     mDocument = nsnull;
   } else {
     mDocument = aCurrentDoc;
     aCurrentDoc->BeginUpdate(UPDATE_CONTENT_MODEL);        
   }
 }
예제 #3
0
void
nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
{
  PRUint32 fromCount = aFromLine->GetChildCount();
  PRUint32 toCount = GetChildCount();
  MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
  PRUint32 fromNewCount = fromCount - toCount;
  if (NS_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
    aFromLine->mChildCount = fromNewCount;
    MOZ_ASSERT(toCount < kMinChildCountForHashtable);
  } else if (fromNewCount < kMinChildCountForHashtable) {
    // aFromLine has a hash table but will not have it after moving the frames
    // so this line can steal the hash table if it needs it.
    if (toCount >= kMinChildCountForHashtable) {
      StealHashTableFrom(aFromLine, fromNewCount);
    } else {
      delete aFromLine->mFrames;
      aFromLine->mFlags.mHasHashedFrames = 0;
      aFromLine->mChildCount = fromNewCount;
    }
  } else {
    // aFromLine still needs a hash table.
    if (toCount < kMinChildCountForHashtable) {
      // remove the moved frames from it
      nsIFrame* f = mFirstChild;
      for (PRUint32 i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
        aFromLine->mFrames->RemoveEntry(f);
      }
    } else if (toCount <= fromNewCount) {
      // This line needs a hash table, allocate a hash table for it since that
      // means fewer hash ops.
      nsIFrame* f = mFirstChild;
      for (PRUint32 i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
        aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
      }
      SwitchToHashtable(); // toCount PutEntry
    } else {
      // This line needs a hash table, but it's fewer hash ops to steal
      // aFromLine's hash table and allocate a new hash table for that line.
      StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
      aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
    }
  }
}
예제 #4
0
nscoord
nsComboboxControlFrame::GetIntrinsicWidth(nsIRenderingContext* aRenderingContext,
                                          nsLayoutUtils::IntrinsicWidthType aType)
{
  // get the scrollbar width, we'll use this later
  nscoord scrollbarWidth = 0;
  nsPresContext* presContext = PresContext();
  if (mListControlFrame) {
    nsIScrollableFrame* scrollable = do_QueryFrame(mListControlFrame);
    NS_ASSERTION(scrollable, "List must be a scrollable frame");
    scrollbarWidth =
      scrollable->GetDesiredScrollbarSizes(presContext, aRenderingContext).LeftRight();
  }

  nscoord displayWidth = 0;
  if (NS_LIKELY(mDisplayFrame)) {
    displayWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                                                        mDisplayFrame,
                                                        aType);
  }

  if (mDropdownFrame) {
    nscoord dropdownContentWidth;
    if (aType == nsLayoutUtils::MIN_WIDTH) {
      dropdownContentWidth = mDropdownFrame->GetMinWidth(aRenderingContext);
    } else {
      NS_ASSERTION(aType == nsLayoutUtils::PREF_WIDTH, "Unexpected type");
      dropdownContentWidth = mDropdownFrame->GetPrefWidth(aRenderingContext);
    }
    dropdownContentWidth = NSCoordSaturatingSubtract(dropdownContentWidth, 
                                                     scrollbarWidth,
                                                     nscoord_MAX);
  
    displayWidth = PR_MAX(dropdownContentWidth, displayWidth);
  }

  // add room for the dropmarker button if there is one
  if (!IsThemed() || presContext->GetTheme()->ThemeNeedsComboboxDropmarker())
    displayWidth += scrollbarWidth;

  return displayWidth;

}
예제 #5
0
// static
nsStringBuffer*
nsCSSValue::BufferFromString(const nsString& aValue)
{
  nsStringBuffer* buffer = nsStringBuffer::FromString(aValue);
  if (buffer) {
    buffer->AddRef();
    return buffer;
  }
  
  PRUnichar length = aValue.Length();
  buffer = nsStringBuffer::Alloc((length + 1) * sizeof(PRUnichar));
  if (NS_LIKELY(buffer != 0)) {
    PRUnichar* data = static_cast<PRUnichar*>(buffer->Data());
    nsCharTraits<PRUnichar>::copy(data, aValue.get(), length);
    // Null-terminate.
    data[length] = 0;
  }

  return buffer;
}
예제 #6
0
// static
already_AddRefed<nsStringBuffer>
nsCSSValue::BufferFromString(const nsString& aValue)
{
  nsStringBuffer* buffer = nsStringBuffer::FromString(aValue);
  if (buffer) {
    buffer->AddRef();
    return buffer;
  }
  
  PRUnichar length = aValue.Length();

  // NOTE: Alloc prouduces a new, already-addref'd (refcnt = 1) buffer.
  buffer = nsStringBuffer::Alloc((length + 1) * sizeof(PRUnichar));
  if (NS_LIKELY(buffer != 0)) {
    PRUnichar* data = static_cast<PRUnichar*>(buffer->Data());
    nsCharTraits<PRUnichar>::copy(data, aValue.get(), length);
    // Null-terminate.
    data[length] = 0;
  }

  return buffer;
}
예제 #7
0
/**
 * This method consumes a start tag and all of its attributes.
 *
 * @param aChar The last character read from the scanner.
 * @param aToken The OUT parameter that holds our resulting token. (allocated
 *               by the function using mTokenAllocator
 * @param aScanner Our source of data
 * @param aFlushTokens is an OUT parameter use to tell consumers to flush
 *                     the current tokens after processing the current one.
 * @return Error result.
 */
nsresult
nsHTMLTokenizer::ConsumeStartTag(PRUnichar aChar,
                                 CToken*& aToken,
                                 nsScanner& aScanner,
                                 bool& aFlushTokens)
{
  // Remember this for later in case you have to unwind...
  PRInt32 theDequeSize = mTokenDeque.GetSize();
  nsresult result = NS_OK;

  nsTokenAllocator* theAllocator = this->GetTokenAllocator();
  aToken = theAllocator->CreateTokenOfType(eToken_start, eHTMLTag_unknown);
  NS_ENSURE_TRUE(aToken, NS_ERROR_OUT_OF_MEMORY);

  // Tell the new token to finish consuming text...
  result = aToken->Consume(aChar, aScanner, mFlags);

  if (NS_SUCCEEDED(result)) {
    AddToken(aToken, result, &mTokenDeque, theAllocator);

    eHTMLTags theTag = (eHTMLTags)aToken->GetTypeID();

    // Good. Now, let's see if the next char is ">".
    // If so, we have a complete tag, otherwise, we have attributes.
    result = aScanner.Peek(aChar);
    if (NS_FAILED(result)) {
      aToken->SetInError(true);

      // Don't return early here so we can create a text and end token for
      // the special <iframe>, <script> and similar tags down below.
      result = NS_OK;
    } else {
      if (kGreaterThan != aChar) { // Look for a '>'
        result = ConsumeAttributes(aChar, aToken, aScanner);
      } else {
        aScanner.GetChar(aChar);
      }
    }

    /*  Now that that's over with, we have one more problem to solve.
        In the case that we just read a <SCRIPT> or <STYLE> tags, we should go and
        consume all the content itself.
        But XML doesn't treat these tags differently, so we shouldn't if the
        document is XML.
     */
    if (NS_SUCCEEDED(result) && !(mFlags & NS_IPARSER_FLAG_XML)) {
      bool isCDATA = gHTMLElements[theTag].CanContainType(kCDATA);
      bool isPCDATA = eHTMLTag_textarea == theTag ||
                        eHTMLTag_title    == theTag;

      // XXX This is an evil hack, we should be able to handle these properly
      // in the DTD.
      if ((eHTMLTag_iframe == theTag &&
            (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED)) ||
          (eHTMLTag_noframes == theTag &&
            (mFlags & NS_IPARSER_FLAG_FRAMES_ENABLED)) ||
          (eHTMLTag_noscript == theTag &&
            (mFlags & NS_IPARSER_FLAG_SCRIPT_ENABLED)) ||
          (eHTMLTag_noembed == theTag)) {
        isCDATA = true;
      }

      // Plaintext contains CDATA, but it's special, so we handle it
      // differently than the other CDATA elements
      if (eHTMLTag_plaintext == theTag) {
        isCDATA = false;

        // Note: We check in ConsumeToken() for this flag, and if we see it
        // we only construct text tokens (which is what we want).
        mFlags |= NS_IPARSER_FLAG_PLAIN_TEXT;
      }


      if (isCDATA || isPCDATA) {
        bool done = false;
        nsDependentString endTagName(nsHTMLTags::GetStringValue(theTag)); 

        CToken* text =
            theAllocator->CreateTokenOfType(eToken_text, eHTMLTag_text);
        NS_ENSURE_TRUE(text, NS_ERROR_OUT_OF_MEMORY);

        CTextToken* textToken = static_cast<CTextToken*>(text);

        if (isCDATA) {
          result = textToken->ConsumeCharacterData(theTag != eHTMLTag_script,
                                                   aScanner,
                                                   endTagName,
                                                   mFlags,
                                                   done);

          // Only flush tokens for <script>, to give ourselves more of a
          // chance of allowing inlines to contain blocks.
          aFlushTokens = done && theTag == eHTMLTag_script;
        } else if (isPCDATA) {
          // Title is consumed conservatively in order to not regress
          // bug 42945
          result = textToken->ConsumeParsedCharacterData(
                                                  theTag == eHTMLTag_textarea,
                                                  theTag == eHTMLTag_title,
                                                  aScanner,
                                                  endTagName,
                                                  mFlags,
                                                  done);

          // Note: we *don't* set aFlushTokens here.
        }

        // We want to do this unless result is kEOF, in which case we will
        // simply unwind our stack and wait for more data anyway.
        if (kEOF != result) {
          AddToken(text, NS_OK, &mTokenDeque, theAllocator);
          CToken* endToken = nullptr;

          if (NS_SUCCEEDED(result) && done) {
            PRUnichar theChar;
            // Get the <
            result = aScanner.GetChar(theChar);
            NS_ASSERTION(NS_SUCCEEDED(result) && theChar == kLessThan,
                         "CTextToken::Consume*Data is broken!");
#ifdef DEBUG
            // Ensure we have a /
            PRUnichar tempChar;  // Don't change non-debug vars in debug-only code
            result = aScanner.Peek(tempChar);
            NS_ASSERTION(NS_SUCCEEDED(result) && tempChar == kForwardSlash,
                         "CTextToken::Consume*Data is broken!");
#endif
            result = ConsumeEndTag(PRUnichar('/'), endToken, aScanner);
            if (!(mFlags & NS_IPARSER_FLAG_VIEW_SOURCE) &&
                NS_SUCCEEDED(result)) {
              // If ConsumeCharacterData returned a success result (and
              // we're not in view source), then we want to make sure that
              // we're going to execute this script (since the result means
              // that we've found an end tag that satisfies all of the right
              // conditions).
              endToken->SetInError(false);
            }
          } else if (result == kFakeEndTag &&
                    !(mFlags & NS_IPARSER_FLAG_VIEW_SOURCE)) {
            result = NS_OK;
            endToken = theAllocator->CreateTokenOfType(eToken_end, theTag,
                                                       endTagName);
            AddToken(endToken, result, &mTokenDeque, theAllocator);
            if (NS_LIKELY(endToken != nullptr)) {
              endToken->SetInError(true);
            }
            else {
              result = NS_ERROR_OUT_OF_MEMORY;
            }
          } else if (result == kFakeEndTag) {
            // If we are here, we are both faking having seen the end tag
            // and are in view-source.
            result = NS_OK;
          }
        } else {
          IF_FREE(text, mTokenAllocator);
        }
      }
    }

    // This code is confusing, so pay attention.
    // If you're here, it's because we were in the midst of consuming a start
    // tag but ran out of data (not in the stream, but in this *part* of the
    // stream. For simplicity, we have to unwind our input. Therefore, we pop
    // and discard any new tokens we've queued this round. Later we can get
    // smarter about this.
    if (NS_FAILED(result)) {
      while (mTokenDeque.GetSize()>theDequeSize) {
        CToken* theToken = (CToken*)mTokenDeque.Pop();
        IF_FREE(theToken, mTokenAllocator);
      }
    }
  } else {
    IF_FREE(aToken, mTokenAllocator);
  }

  return result;
}
예제 #8
0
/**
 * This method is called just after we've consumed a start or end
 * tag, and we now have to consume its attributes.
 * 
 * @param   aChar is the last char read
 * @param   aToken is the start or end tag that "owns" these attributes.
 * @param   aScanner represents our input source
 * @return  Error result.
 */
nsresult
nsHTMLTokenizer::ConsumeAttributes(PRUnichar aChar,
                                   CToken* aToken,
                                   nsScanner& aScanner)
{
  bool done = false;
  nsresult result = NS_OK;
  PRInt16 theAttrCount = 0;

  nsTokenAllocator* theAllocator = this->GetTokenAllocator();

  while (!done && result == NS_OK) {
    CAttributeToken* theToken =
      static_cast<CAttributeToken*>
                 (theAllocator->CreateTokenOfType(eToken_attribute,
                                                     eHTMLTag_unknown));
    if (NS_LIKELY(theToken != nullptr)) {
      // Tell the new token to finish consuming text...
      result = theToken->Consume(aChar, aScanner, mFlags);

      if (NS_SUCCEEDED(result)) {
        ++theAttrCount;
        AddToken((CToken*&)theToken, result, &mTokenDeque, theAllocator);
      } else {
        IF_FREE(theToken, mTokenAllocator);
        // Bad attribute returns shouldn't propagate out.
        if (NS_ERROR_HTMLPARSER_BADATTRIBUTE == result) {
          result = NS_OK;
        }
      }
    }
    else {
      result = NS_ERROR_OUT_OF_MEMORY;
    }

#ifdef DEBUG
    if (NS_SUCCEEDED(result)) {
      PRInt32 newline = 0;
      aScanner.SkipWhitespace(newline);
      NS_ASSERTION(newline == 0,
          "CAttribute::Consume() failed to collect all the newlines!");
    }
#endif
    if (NS_SUCCEEDED(result)) {
      result = aScanner.Peek(aChar);
      if (NS_SUCCEEDED(result)) {
        if (aChar == kGreaterThan) { // You just ate the '>'
          aScanner.GetChar(aChar); // Skip the '>'
          done = true;
        } else if (aChar == kLessThan) {
          aToken->SetInError(true);
          done = true;
        }
      }
    }
  }

  if (NS_FAILED(result)) {
    aToken->SetInError(true);

    if (!aScanner.IsIncremental()) {
      result = NS_OK;
    }
  }

  aToken->SetAttributeCount(theAttrCount);
  return result;
}
예제 #9
0
PRBool
nsCSSScanner::NextURL(nsCSSToken& aToken)
{
  EatWhiteSpace();

  PRInt32 ch = Read();
  if (ch < 0) {
    return PR_FALSE;
  }

  // STRING
  if ((ch == '"') || (ch == '\'')) {
#ifdef DEBUG
    PRBool ok =
#endif
      ParseString(ch, aToken);
    NS_ABORT_IF_FALSE(ok, "ParseString should never fail, "
                          "since there's always something read");

    NS_ABORT_IF_FALSE(aToken.mType == eCSSToken_String ||
                      aToken.mType == eCSSToken_Bad_String,
                      "unexpected token type");
    if (NS_LIKELY(aToken.mType == eCSSToken_String)) {
      EatWhiteSpace();
      if (LookAheadOrEOF(')')) {
        aToken.mType = eCSSToken_URL;
      } else {
        aToken.mType = eCSSToken_Bad_URL;
      }
    } else {
      aToken.mType = eCSSToken_Bad_URL;
    }
    return PR_TRUE;
  }

  // Process a url lexical token. A CSS1 url token can contain
  // characters beyond identifier characters (e.g. '/', ':', etc.)
  // Because of this the normal rules for tokenizing the input don't
  // apply very well. To simplify the parser and relax some of the
  // requirements on the scanner we parse url's here. If we find a
  // malformed URL then we emit a token of type "Bad_URL" so that
  // the CSS1 parser can ignore the invalid input.  The parser must
  // treat a Bad_URL token like a Function token, and process
  // tokens until a matching parenthesis.

  aToken.mType = eCSSToken_Bad_URL;
  aToken.mSymbol = PRUnichar(0);
  nsString& ident = aToken.mIdent;
  ident.SetLength(0);

  Pushback(ch);

  // start of a non-quoted url (which may be empty)
  PRBool ok = PR_TRUE;
  for (;;) {
    ch = Read();
    if (ch < 0) break;
    if (ch == CSS_ESCAPE) {
      ParseAndAppendEscape(ident);
    } else if (IsWhitespace(ch)) {
      // Whitespace is allowed at the end of the URL
      EatWhiteSpace();
      // Consume the close paren if we have it; if not we're an invalid URL.
      ok = LookAheadOrEOF(')');
      break;
    } else if (ch == '"' || ch == '\'' || ch == '(' || ch < PRUnichar(' ')) {
      // This is an invalid URL spec
      ok = PR_FALSE;
      Pushback(ch); // push it back so the parser can match tokens and
                    // then closing parenthesis
      break;
    } else if (ch == ')') {
      // All done
      break;
    } else {
      // A regular url character.
      ident.Append(PRUnichar(ch));
    }
  }

  // If the result of the above scanning is ok then change the token
  // type to a useful one.
  if (ok) {
    aToken.mType = eCSSToken_URL;
  }
  return PR_TRUE;
}
예제 #10
0
JSBool
WrapObject(JSContext *cx, JSObject *parent, jsval *vp, XPCWrappedNative* wn)
{
  NS_ASSERTION(XPCPerThreadData::IsMainThread(cx),
               "Can't do this off the main thread!");

  // Our argument should be a wrapped native object, but the caller may have
  // passed it in as an optimization.
  JSObject *wrappedObj;
  if (JSVAL_IS_PRIMITIVE(*vp) ||
      !(wrappedObj = JSVAL_TO_OBJECT(*vp)) ||
      wrappedObj->getClass() == &XOWClass) {
    return JS_TRUE;
  }

  if (!wn &&
      !(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wrappedObj))) {
    return JS_TRUE;
  }

  CheckWindow(wn);

  // The parent must be the inner global object for its scope.
  parent = JS_GetGlobalForObject(cx, parent);
  OBJ_TO_INNER_OBJECT(cx, parent);
  if (!parent) {
    return JS_FALSE;
  }

  XPCWrappedNativeWithXOW *wnxow = nsnull;
  if (wn->NeedsXOW()) {
    JSObject *innerWrappedObj = wrappedObj;
    OBJ_TO_INNER_OBJECT(cx, innerWrappedObj);
    if (!innerWrappedObj) {
      return JS_FALSE;
    }

    if (innerWrappedObj == parent) {
      wnxow = static_cast<XPCWrappedNativeWithXOW *>(wn);
      JSObject *xow = wnxow->GetXOW();
      if (xow) {
        *vp = OBJECT_TO_JSVAL(xow);
        return JS_TRUE;
      }
    }
  }

  XPCWrappedNative *parentwn =
    XPCWrappedNative::GetWrappedNativeOfJSObject(cx, parent);
  XPCWrappedNativeScope *parentScope;
  if (NS_LIKELY(parentwn)) {
    parentScope = parentwn->GetScope();
  } else {
    parentScope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent);
  }

  JSObject *outerObj = nsnull;
  WrappedNative2WrapperMap *map = parentScope->GetWrapperMap();

  outerObj = map->Find(wrappedObj);
  if (outerObj) {
    NS_ASSERTION(outerObj->getClass() == &XOWClass,
                 "What crazy object are we getting here?");
    *vp = OBJECT_TO_JSVAL(outerObj);

    if (wnxow) {
      // NB: wnxow->GetXOW() must have returned false.
      SetFlags(cx, outerObj, AddFlags(GetFlags(cx, outerObj), FLAG_IS_CACHED));
      wnxow->SetXOW(outerObj);
    }

    return JS_TRUE;
  }

  outerObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&XOWClass), nsnull,
                                        parent);
  if (!outerObj) {
    return JS_FALSE;
  }

  jsval flags = INT_TO_JSVAL(wnxow ? FLAG_IS_CACHED : 0);
  if (!JS_SetReservedSlot(cx, outerObj, sWrappedObjSlot, *vp) ||
      !JS_SetReservedSlot(cx, outerObj, sFlagsSlot, flags) ||
      !JS_SetReservedSlot(cx, outerObj, XPC_XOW_ScopeSlot,
                          PRIVATE_TO_JSVAL(parentScope))) {
    return JS_FALSE;
  }

  *vp = OBJECT_TO_JSVAL(outerObj);

  map->Add(wn->GetScope()->GetWrapperMap(), wrappedObj, outerObj);
  if(wnxow) {
    wnxow->SetXOW(outerObj);
  }

  return JS_TRUE;
}
예제 #11
0
// Calculates the codepoint of the UTF8 sequence starting at aStr.  Sets aNext
// to the byte following the end of the sequence.
//
// If the sequence is invalid, or if computing the codepoint would take us off
// the end of the string (as marked by aEnd), returns -1 and does not set
// aNext.  Note that this function doesn't check that aStr < aEnd -- it assumes
// you've done that already.
static NS_ALWAYS_INLINE PRUint32
GetLowerUTF8Codepoint(const char* aStr, const char* aEnd, const char **aNext)
{
  // Convert to unsigned char so that stuffing chars into PRUint32s doesn't
  // sign extend.
  const unsigned char *str = (unsigned char*)aStr;

  if (UTF8traits::isASCII(str[0])) {
    // It's ASCII; just convert to lower-case and return it.
    *aNext = aStr + 1;
    return gASCIIToLower[*str];
  }
  if (UTF8traits::is2byte(str[0]) && NS_LIKELY(aStr + 1 < aEnd)) {
    // It's a two-byte sequence, so it looks like
    //  110XXXXX 10XXXXXX.
    // This is definitely in the BMP, so we can store straightaway into a
    // PRUint16.

    PRUint16 c;
    c  = (str[0] & 0x1F) << 6;
    c += (str[1] & 0x3F);

    // we don't go through ToLowerCase here, because we know this isn't
    // an ASCII character so the ASCII fast-path there is useless
    c = mozilla::unicode::GetLowercase(c);

    *aNext = aStr + 2;
    return c;
  }
  if (UTF8traits::is3byte(str[0]) && NS_LIKELY(aStr + 2 < aEnd)) {
    // It's a three-byte sequence, so it looks like
    //  1110XXXX 10XXXXXX 10XXXXXX.
    // This will just barely fit into 16-bits, so store into a PRUint16.

    PRUint16 c;
    c  = (str[0] & 0x0F) << 12;
    c += (str[1] & 0x3F) << 6;
    c += (str[2] & 0x3F);

    c = mozilla::unicode::GetLowercase(c);

    *aNext = aStr + 3;
    return c;
  }
  if (UTF8traits::is4byte(str[0]) && NS_LIKELY(aStr + 3 < aEnd)) {
    // It's a four-byte sequence, so it looks like
    //   11110XXX 10XXXXXX 10XXXXXX 10XXXXXX.

    PRUint32 c;
    c  = (str[0] & 0x07) << 18;
    c += (str[1] & 0x3F) << 12;
    c += (str[2] & 0x3F) << 6;
    c += (str[3] & 0x3F);

    c = mozilla::unicode::GetLowercase(c);

    *aNext = aStr + 4;
    return c;
  }

  // Hm, we don't understand this sequence.
  return -1;
}