Bug 404386 - nsScanner construction is 5% of setting innerHTML. r=mrbkap,smontagu. sr=jst. a=blocking1.9.
authorbent.mozilla@gmail.com
Tue, 29 Jan 2008 14:12:22 -0800
changeset 10953 ea69b58ff5ce7273140590c0711efd7cdf440aab
parent 10952 6f12c9ffdc9aa28aa68a271560fb9fecd2e925b5
child 10954 b20cdb83dacc5cfeb849ac9c9243ffedc31cafc7
push idunknown
push userunknown
push dateunknown
reviewersmrbkap, smontagu, jst, blocking1
bugs404386
milestone1.9b3pre
Bug 404386 - nsScanner construction is 5% of setting innerHTML. r=mrbkap,smontagu. sr=jst. a=blocking1.9.
intl/uconv/src/nsCharsetAliasImp.cpp
intl/uconv/src/nsCharsetConverterManager.cpp
intl/uconv/src/nsCharsetConverterManager.h
intl/uconv/src/nsUConvModule.cpp
parser/htmlparser/src/nsParser.cpp
parser/htmlparser/src/nsParser.h
parser/htmlparser/src/nsScanner.cpp
--- a/intl/uconv/src/nsCharsetAliasImp.cpp
+++ b/intl/uconv/src/nsCharsetAliasImp.cpp
@@ -58,66 +58,70 @@ nsCharsetAlias2::nsCharsetAlias2()
   mDelegate = nsnull; // delay the load of mDelegate untill we need it.
 }
 //--------------------------------------------------------------
 nsCharsetAlias2::~nsCharsetAlias2()
 {
   if(mDelegate)
      delete mDelegate;
 }
+
+// 
+static const char* kAliases[][3] = {
+  // Triple with { lower-case test string, out string, length of out string }
+  { "iso-8859-1", "ISO-8859-1", (const char*)10 },
+  { "utf-8",      "UTF-8",      (const char*)5  },
+  { "x-sjis",     "Shift_JIS",  (const char*)9  },
+  { "shift_jis",  "Shift_JIS",  (const char*)9  }
+};
+
 //--------------------------------------------------------------
 NS_IMETHODIMP nsCharsetAlias2::GetPreferred(const nsACString& aAlias,
                                             nsACString& oResult)
 {
    if (aAlias.IsEmpty()) return NS_ERROR_NULL_POINTER;
    NS_TIMELINE_START_TIMER("nsCharsetAlias2:GetPreferred");
 
-   nsCAutoString aKey(aAlias);
-   ToLowerCase(aKey);
-   oResult.Truncate();
 
    // Delay loading charsetalias.properties by hardcoding the most
    // frequent aliases.  Note that it's possible to recur in to this
    // function *while loading* charsetalias.properties (see bug 190951),
    // so we might have an |mDelegate| already that isn't valid yet, but
    // the load is guaranteed to be "UTF-8" so things will be OK.
-   if(aKey.EqualsLiteral("utf-8")) {
-     oResult.AssignLiteral("UTF-8");
-     NS_TIMELINE_STOP_TIMER("nsCharsetAlias2:GetPreferred");
-     return NS_OK;
-   } 
-   if(aKey.EqualsLiteral("iso-8859-1")) {
-     oResult.AssignLiteral("ISO-8859-1");
-     NS_TIMELINE_STOP_TIMER("nsCharsetAlias2:GetPreferred");
-     return NS_OK;
-   } 
-   if(aKey.EqualsLiteral("x-sjis") ||
-      aKey.EqualsLiteral("shift_jis")) {
-     oResult.AssignLiteral("Shift_JIS");
-     NS_TIMELINE_STOP_TIMER("nsCharsetAlias2:GetPreferred");
-     return NS_OK;
-   } 
+   for (PRUint32 index = 0; index < NS_ARRAY_LENGTH(kAliases); index++) {
+     if (aAlias.LowerCaseEqualsASCII(kAliases[index][0])) {
+       oResult.Assign(nsDependentCString(kAliases[index][1],
+                                         (PRUint32)kAliases[index][2]));
+       NS_TIMELINE_STOP_TIMER("nsCharsetAlias2:GetPreferred");
+       return NS_OK;
+     }
+   }
+
+   oResult.Truncate();
 
    if(!mDelegate) {
      //load charsetalias.properties string bundle with all remaining aliases
-     // we may need to protect the following section with a lock so we won't call the 
+     // we may need to protect the following section with a lock so we won't call the
      // 'new nsGREResProperties' from two different threads
      mDelegate = new nsGREResProperties( NS_LITERAL_CSTRING("charsetalias.properties") );
      NS_ASSERTION(mDelegate, "cannot create nsGREResProperties");
      if(nsnull == mDelegate)
        return NS_ERROR_OUT_OF_MEMORY;
    }
 
    NS_TIMELINE_STOP_TIMER("nsCharsetAlias2:GetPreferred");
    NS_TIMELINE_MARK_TIMER("nsCharsetAlias2:GetPreferred");
 
+   nsCAutoString key(aAlias);
+   ToLowerCase(key);
+
    // hack for now, have to fix nsGREResProperties, but we can't until
    // string bundles use UTF8 keys
    nsAutoString result;
-   nsresult rv = mDelegate->Get(NS_ConvertASCIItoUTF16(aKey), result);
+   nsresult rv = mDelegate->Get(NS_ConvertASCIItoUTF16(key), result);
    LossyAppendUTF16toASCII(result, oResult);
    return rv;
 }
 
 //--------------------------------------------------------------
 NS_IMETHODIMP
 nsCharsetAlias2::Equals(const nsACString& aCharset1,
                         const nsACString& aCharset2, PRBool* oResult)
--- a/intl/uconv/src/nsCharsetConverterManager.cpp
+++ b/intl/uconv/src/nsCharsetConverterManager.cpp
@@ -81,16 +81,22 @@ nsCharsetConverterManager::nsCharsetConv
 }
 
 nsCharsetConverterManager::~nsCharsetConverterManager() 
 {
   NS_IF_RELEASE(mDataBundle);
   NS_IF_RELEASE(mTitleBundle);
 }
 
+nsresult nsCharsetConverterManager::Init()
+{
+  if (!mDecoderHash.Init())
+    return NS_ERROR_OUT_OF_MEMORY;
+  return NS_OK;
+}
 
 nsresult nsCharsetConverterManager::RegisterConverterManagerData()
 {
   nsresult rv;
   nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
   if (NS_FAILED(rv))
     return rv;
 
@@ -249,40 +255,38 @@ nsCharsetConverterManager::GetUnicodeDec
     if (decoder) {
       NS_ADDREF(*aResult = decoder);
       return NS_OK;
     }
   }
 #endif
   nsresult rv = NS_OK;
 
-  NS_NAMED_LITERAL_CSTRING(kUnicodeDecoderContractIDBase,
-                           NS_UNICODEDECODER_CONTRACTID_BASE);
-
-  nsCAutoString contractid(kUnicodeDecoderContractIDBase +
-                           nsDependentCString(aSrc));
-
-  if (!strncmp(aSrc,
-               NS_1BYTE_CODER_PATTERN,
-               NS_1BYTE_CODER_PATTERN_LEN))
+  NS_NAMED_LITERAL_CSTRING(contractbase, NS_UNICODEDECODER_CONTRACTID_BASE);
+  nsDependentCString src(aSrc);
+  
+  if (!strncmp(aSrc, NS_1BYTE_CODER_PATTERN, NS_1BYTE_CODER_PATTERN_LEN))
   {
-    // Single byte decoders dont hold state. Optimize by using a service.
-    decoder = do_GetService(contractid.get(), &rv);
+    // Single byte decoders don't hold state. Optimize by using a service, and
+    // cache it in our hash to avoid repeated trips through the service manager.
+    if (!mDecoderHash.Get(aSrc, getter_AddRefs(decoder))) {
+      decoder = do_GetService(PromiseFlatCString(contractbase + src).get(),
+                              &rv);
+      if (NS_SUCCEEDED(rv))
+        mDecoderHash.Put(aSrc, decoder);
+    }
   }
   else
   {
-    decoder = do_CreateInstance(contractid.get(), &rv);
+    decoder = do_CreateInstance(PromiseFlatCString(contractbase + src).get(),
+                                &rv);
   }
-  if(NS_FAILED(rv))
-    rv = NS_ERROR_UCONV_NOCONV;
-  else
-  {
-    *aResult = decoder.get();
-    NS_ADDREF(*aResult);
-  }
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_UCONV_NOCONV);
+
+  decoder.forget(aResult);
   return rv;
 }
 
 nsresult 
 nsCharsetConverterManager::GetList(const nsACString& aCategory,
                                    const nsACString& aPrefix,
                                    nsIUTF8StringEnumerator** aResult)
 {
--- a/intl/uconv/src/nsCharsetConverterManager.h
+++ b/intl/uconv/src/nsCharsetConverterManager.h
@@ -35,40 +35,45 @@
  *
  * ***** END LICENSE BLOCK ***** */
 #ifndef nsCharsetConverterManager_h__
 #define nsCharsetConverterManager_h__
 
 #include "nsISupports.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIStringBundle.h"
+#include "nsInterfaceHashtable.h"
 
 #ifdef MOZ_USE_NATIVE_UCONV
 #include "nsINativeUConvService.h"
 #endif
 
 class nsCharsetConverterManager : public nsICharsetConverterManager
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSICHARSETCONVERTERMANAGER
 
 public:
 
   nsCharsetConverterManager();
   virtual ~nsCharsetConverterManager();
 
+  nsresult Init();
+
 private:
 
   nsIStringBundle * mDataBundle;
   nsIStringBundle * mTitleBundle;
 
 #ifdef MOZ_USE_NATIVE_UCONV
   nsCOMPtr<nsINativeUConvService> mNativeUC;
 #endif
 
+  nsInterfaceHashtable<nsCharPtrHashKey, nsIUnicodeDecoder> mDecoderHash;
+
   nsresult LoadExtensibleBundle(const char * aRegistryKey, 
       nsIStringBundle ** aResult);
 
   static nsresult RegisterConverterCategory(nsICategoryManager*,
                                             const char* aCategory,
                                             const char* aURL);
 
   nsresult GetBundleValue(nsIStringBundle * aBundle,
--- a/intl/uconv/src/nsUConvModule.cpp
+++ b/intl/uconv/src/nsUConvModule.cpp
@@ -605,17 +605,17 @@ nsConverterManagerDataRegister(nsICompon
                                 nsIFile* aPath,
                                 const char *aLocation,
                                 const char *aType,
                                 const nsModuleComponentInfo* aInfo)
 {
   return nsCharsetConverterManager::RegisterConverterManagerData();
 }
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsCharsetConverterManager)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCharsetConverterManager, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextToSubURI)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF8ConverterService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCharsetAlias2)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsConverterInputStream)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsConverterOutputStream)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPlatformCharset, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableUnicodeConverter)
 
--- a/parser/htmlparser/src/nsParser.cpp
+++ b/parser/htmlparser/src/nsParser.cpp
@@ -43,16 +43,17 @@
 #include "nsCRT.h"
 #include "nsScanner.h"
 #include "plstr.h"
 #include "nsIStringStream.h"
 #include "nsIChannel.h"
 #include "nsICachingChannel.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsICharsetAlias.h"
+#include "nsICharsetConverterManager.h"
 #include "nsIInputStream.h"
 #include "CNavDTD.h"
 #include "prenv.h"
 #include "nsParserCIID.h"
 #include "nsReadableUtils.h"
 #include "nsCOMPtr.h"
 #include "nsExpatDriver.h"
 #include "nsIServiceManager.h"
@@ -149,16 +150,19 @@ public:
   {
     mParser->HandleParserContinueEvent(this);
     return NS_OK;
   }
 };
 
 //-------------- End ParseContinue Event Definition ------------------------
 
+nsICharsetAlias* nsParser::sCharsetAliasService = nsnull;
+nsICharsetConverterManager* nsParser::sCharsetConverterManager = nsnull;
+
 /**
  *  This gets called when the htmlparser module is initialized.
  */
 // static
 nsresult
 nsParser::Init()
 {
   nsresult rv;
@@ -199,31 +203,44 @@ nsParser::Init()
         if (!sParserDataListeners)
           return NS_ERROR_OUT_OF_MEMORY;
       }
 
       sParserDataListeners->AppendObject(listener);
     }
   }
 
+  nsCOMPtr<nsICharsetAlias> charsetAlias =
+    do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  nsCOMPtr<nsICharsetConverterManager> charsetConverter =
+    do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  charsetAlias.swap(sCharsetAliasService);
+  charsetConverter.swap(sCharsetConverterManager);
+
   return NS_OK;
 }
 
 
 /**
  *  This gets called when the htmlparser module is shutdown.
  */
 // static
 void nsParser::Shutdown()
 {
   delete sParserDataListeners;
   sParserDataListeners = nsnull;
+  
+  NS_IF_RELEASE(sCharsetAliasService);
+  NS_IF_RELEASE(sCharsetConverterManager);
 }
 
-
 #ifdef DEBUG
 static PRBool gDumpContent=PR_FALSE;
 #endif
 
 /**
  *  default constructor
  */
 nsParser::nsParser()
--- a/parser/htmlparser/src/nsParser.h
+++ b/parser/htmlparser/src/nsParser.h
@@ -85,16 +85,18 @@
 #include "nsTimer.h"
 #include "nsThreadUtils.h"
 #include "nsIContentSink.h"
 #include "nsIParserFilter.h"
 #include "nsCOMArray.h"
 #include "nsIUnicharStreamListener.h"
 #include "nsCycleCollectionParticipant.h"
 
+class nsICharsetConverterManager;
+class nsICharsetAlias;
 class nsIDTD;
 class nsScanner;
 class nsIProgressEventSink;
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4275 )
 #endif
 
@@ -119,17 +121,16 @@ class nsParser : public nsIParser,
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsParser, nsIParser)
 
     /**
      * default constructor
      * @update	gess5/11/98
      */
     nsParser();
 
-
     /**
      * Destructor
      * @update	gess5/11/98
      */
     virtual ~nsParser();
 
     /**
      * Select given content sink into parser for parser output
@@ -367,16 +368,24 @@ class nsParser : public nsIParser,
     /**
      * Called by top-level scanners when data from necko is added to
      * the scanner.
      */
     nsresult DataAdded(const nsSubstring& aData, nsIRequest *aRequest);
 
     static nsCOMArray<nsIUnicharStreamListener> *sParserDataListeners;
 
+    static nsICharsetAlias* GetCharsetAliasService() {
+      return sCharsetAliasService;
+    }
+
+    static nsICharsetConverterManager* GetCharsetConverterManager() {
+      return sCharsetConverterManager;
+    }
+
 protected:
 
     /**
      * 
      * @update	gess5/18/98
      * @param 
      * @return
      */
@@ -450,17 +459,18 @@ protected:
     PRInt32             mCharsetSource;
     
     PRUint16            mFlags;
 
     nsString            mUnusedInput;
     nsCString           mCharset;
     nsCString           mCommandStr;
 
-    
+    static nsICharsetAlias*            sCharsetAliasService;
+    static nsICharsetConverterManager* sCharsetConverterManager;
    
 public:  
    
     MOZ_TIMER_DECLARE(mParseTime)
     MOZ_TIMER_DECLARE(mDTDTime)
     MOZ_TIMER_DECLARE(mTokenizeTime)
 };
 
--- a/parser/htmlparser/src/nsScanner.cpp
+++ b/parser/htmlparser/src/nsScanner.cpp
@@ -98,17 +98,16 @@ nsScanner::nsScanner(const nsAString& an
   mCountRemaining = 0;
   mFirstNonWhitespacePosition = -1;
   AppendToBuffer(anHTMLString);
   mSlidingBuffer->BeginReading(mCurrentPosition);
   mMarkPosition = mCurrentPosition;
   mIncremental = PR_FALSE;
   mUnicodeDecoder = 0;
   mCharsetSource = kCharsetUninitialized;
-  SetDocumentCharset(aCharset, aSource);
 }
 
 /**
  *  Use this constructor if you want i/o to be based on strings 
  *  the scanner receives. If you pass a null filename, you
  *  can still provide data to the scanner via append.
  *
  *  @update  gess 5/12/98
@@ -144,52 +143,58 @@ nsScanner::nsScanner(nsString& aFilename
 
 nsresult nsScanner::SetDocumentCharset(const nsACString& aCharset , PRInt32 aSource) {
 
   nsresult res = NS_OK;
 
   if( aSource < mCharsetSource) // priority is lower the the current one , just
     return res;
 
-  nsCOMPtr<nsICharsetAlias> calias(do_GetService(NS_CHARSETALIAS_CONTRACTID, &res));
-  NS_ASSERTION( nsnull != calias, "cannot find charset alias");
-  if( NS_SUCCEEDED(res) && (nsnull != calias))
+  nsICharsetAlias* calias = nsParser::GetCharsetAliasService();
+  NS_ASSERTION(calias, "Must have the charset alias service!");
+
+  if (!mCharset.IsEmpty())
   {
-    PRBool same = PR_FALSE;
+    PRBool same;
     res = calias->Equals(aCharset, mCharset, &same);
     if(NS_SUCCEEDED(res) && same)
     {
       return NS_OK; // no difference, don't change it
     }
-    // different, need to change it
-    nsCAutoString charsetName;
-    res = calias->GetPreferred(aCharset, charsetName);
+  }
+
+  // different, need to change it
+  nsCString charsetName;
+  res = calias->GetPreferred(aCharset, charsetName);
 
-    if(NS_FAILED(res) && (kCharsetUninitialized == mCharsetSource) )
-    {
-       // failed - unknown alias , fallback to ISO-8859-1
-      charsetName.AssignLiteral("ISO-8859-1");
-    }
-    mCharset = charsetName;
-    mCharsetSource = aSource;
+  if(NS_FAILED(res) && (mCharsetSource == kCharsetUninitialized))
+  {
+     // failed - unknown alias , fallback to ISO-8859-1
+    mCharset.AssignLiteral("ISO-8859-1");
+  }
+  else
+  {
+    mCharset.Assign(charsetName);
+  }
 
-    nsCOMPtr<nsICharsetConverterManager> ccm = 
-             do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
-    if(NS_SUCCEEDED(res) && (nsnull != ccm))
-    {
-      nsIUnicodeDecoder * decoder = nsnull;
-      res = ccm->GetUnicodeDecoderRaw(mCharset.get(), &decoder);
-      if(NS_SUCCEEDED(res) && (nsnull != decoder))
-      {
-         NS_IF_RELEASE(mUnicodeDecoder);
+  mCharsetSource = aSource;
+
+  NS_ASSERTION(nsParser::GetCharsetConverterManager(),
+               "Must have the charset converter manager!");
 
-         mUnicodeDecoder = decoder;
-      }    
-    }
+  nsIUnicodeDecoder * decoder = nsnull;
+  res = nsParser::GetCharsetConverterManager()->
+    GetUnicodeDecoderRaw(mCharset.get(), &decoder);
+  if(NS_SUCCEEDED(res) && (nsnull != decoder))
+  {
+     NS_IF_RELEASE(mUnicodeDecoder);
+
+     mUnicodeDecoder = decoder;
   }
+
   return res;
 }
 
 
 /**
  *  default destructor
  *  
  *  @update  gess 3/25/98