Bug 620106 - Allow chardet (if enabled) reload the page during load. r=sicking, a=blocking2.0-final.
authorHenri Sivonen <hsivonen@iki.fi>
Fri, 11 Feb 2011 12:33:32 +0200
changeset 62346 584a654b4be6329583ec32856c7a38a8f5c8b546
parent 62345 8013e7a30984388b47a3f980088417c3e76ecf75
child 62347 5485a9335191b43060eaaf149728da28e75af1d9
push idunknown
push userunknown
push dateunknown
reviewerssicking, blocking2.0-final
bugs620106
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 620106 - Allow chardet (if enabled) reload the page during load. r=sicking, a=blocking2.0-final.
extensions/universalchardet/tests/Makefile.in
extensions/universalchardet/tests/bug620106_text.html
extensions/universalchardet/tests/test_bug620106.html
parser/html/nsHtml5StreamParser.cpp
parser/html/nsHtml5StreamParser.h
parser/html/nsHtml5TreeBuilderCppSupplement.h
parser/html/nsHtml5TreeBuilderHSupplement.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
parser/html/nsHtml5TreeOperation.cpp
parser/html/nsHtml5TreeOperation.h
--- a/extensions/universalchardet/tests/Makefile.in
+++ b/extensions/universalchardet/tests/Makefile.in
@@ -63,12 +63,14 @@ relativesrcdir = extensions/universalcha
 		test_bug426271-utf-8.html \
 		bug431054_text.html \
 		test_bug431054.html \
 		test_bug431054-japanese.html \
 		bug488426_text.html \
 		test_bug488426.html \
 		bug547487_text.html \
 		test_bug547487.html \
+		bug620106_text.html \
+		test_bug620106.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/extensions/universalchardet/tests/bug620106_text.html
@@ -0,0 +1,1045 @@
+<!DOCTYPE html>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+テストのエンコーディングを検出。もう一度テスト。まだエンコードの検出をテストします。これが正しく検出されていますか?
+
new file mode 100644
--- /dev/null
+++ b/extensions/universalchardet/tests/test_bug620106.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=620106
+-->
+<head>
+  <title>Test for Bug 620106</title>
+  <script type="text/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="text/javascript" 
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+          </script>
+  <script type="text/javascript" src="CharsetDetectionTests.js"></script>
+  <link rel="stylesheet" type="text/css" 
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=620106">Mozilla Bug 620106</a>
+<p id="display"></p>
+<div id="content" style="display: none">  
+</div>
+<iframe id="testframe"></iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 620106 **/
+CharsetDetectionTests("bug620106_text.html",
+		      "EUC-JP",
+		      new Array("universal_charset_detector"));
+</script>
+</pre>
+</body>
+</html>
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -201,18 +201,19 @@ nsHtml5StreamParser::nsHtml5StreamParser
   // to make the chardet refcount its observer (nsHtml5StreamParser)
   // on the main thread.
   const nsAdoptingString& detectorName = 
     nsContentUtils::GetLocalizedStringPref("intl.charset.detector");
   if (!detectorName.IsEmpty()) {
     nsCAutoString detectorContractID;
     detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
     AppendUTF16toUTF8(detectorName, detectorContractID);
-    if (mChardet = do_CreateInstance(detectorContractID.get())) {
+    if ((mChardet = do_CreateInstance(detectorContractID.get()))) {
       (void) mChardet->Init(this);
+      mFeedChardet = PR_TRUE;
     }
   }
 
   // There's a zeroing operator new for everything else
 }
 
 nsHtml5StreamParser::~nsHtml5StreamParser()
 {
@@ -241,19 +242,38 @@ nsHtml5StreamParser::GetChannel(nsIChann
                     NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
 nsHtml5StreamParser::Notify(const char* aCharset, nsDetectionConfident aConf)
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   if (aConf == eBestAnswer || aConf == eSureAnswer) {
-    mCharset.Assign(aCharset);
-    mCharsetSource = kCharsetFromAutoDetection;
-    mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
+    mFeedChardet = PR_FALSE; // just in case
+    if (HasDecoder()) {
+      if (mCharset.Equals(aCharset)) {
+        NS_ASSERTION(mCharsetSource < kCharsetFromAutoDetection,
+            "Why are we running chardet at all?");
+        mCharsetSource = kCharsetFromAutoDetection;
+        mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
+      } else {
+        // We've already committed to a decoder. Request a reload from the
+        // docshell.
+        nsCAutoString charset(aCharset);
+        mTreeBuilder->NeedsCharsetSwitchTo(charset, kCharsetFromAutoDetection);
+        FlushTreeOpsAndDisarmTimer();
+        Interrupt();
+      }
+    } else {
+      // Got a confident answer from the sniffing buffer. That code will
+      // take care of setting up the decoder.
+      mCharset.Assign(aCharset);
+      mCharsetSource = kCharsetFromAutoDetection;
+      mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
+    }
   }
   return NS_OK;
 }
 
 nsresult
 nsHtml5StreamParser::SetupDecodingAndWriteSniffingBufferAndCurrentSegment(const PRUint8* aFromSegment, // can be null
                                                                           PRUint32 aCount,
                                                                           PRUint32* aWriteCount)
@@ -301,48 +321,66 @@ nsHtml5StreamParser::SetupDecodingFromBo
   nsresult rv = NS_OK;
   nsCOMPtr<nsICharsetConverterManager> convManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = convManager->GetUnicodeDecoderRaw(aDecoderCharsetName, getter_AddRefs(mUnicodeDecoder));
   NS_ENSURE_SUCCESS(rv, rv);
   mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
   mCharset.Assign(aCharsetName);
   mCharsetSource = kCharsetFromByteOrderMark;
+  mFeedChardet = PR_FALSE;
   mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
   mSniffingBuffer = nsnull;
   mMetaScanner = nsnull;
   mBomState = BOM_SNIFFING_OVER;
   return rv;
 }
 
 nsresult
 nsHtml5StreamParser::FinalizeSniffing(const PRUint8* aFromSegment, // can be null
                                       PRUint32 aCount,
                                       PRUint32* aWriteCount,
                                       PRUint32 aCountToSniffingLimit)
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   // meta scan failed.
   if (mCharsetSource >= kCharsetFromHintPrevDoc) {
+    mFeedChardet = PR_FALSE;
     return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
   }
   // maybe try chardet now; 
-  if (mChardet) {
+  if (mFeedChardet) {
+    PRBool dontFeed;
     nsresult rv;
-    PRBool dontFeed = PR_FALSE;
     if (mSniffingBuffer) {
       rv = mChardet->DoIt((const char*)mSniffingBuffer.get(), mSniffingLength, &dontFeed);
+      mFeedChardet = !dontFeed;
       NS_ENSURE_SUCCESS(rv, rv);
     }
-    if (!dontFeed && aFromSegment) {
-      rv = mChardet->DoIt((const char*)aFromSegment, aCountToSniffingLimit, &dontFeed);
+    if (mFeedChardet && aFromSegment) {
+      rv = mChardet->DoIt((const char*)aFromSegment,
+                          // Avoid buffer boundary-dependent behavior when
+                          // reparsing is forbidden. If reparse is forbidden,
+                          // act as if we only saw the first 1024 bytes.
+                          // When reparsing isn't forbidden, buffer boundaries
+                          // can have an effect on whether the page is loaded
+                          // once or twice. :-(
+                          mReparseForbidden ? aCountToSniffingLimit : aCount,
+                          &dontFeed);
+      mFeedChardet = !dontFeed;
       NS_ENSURE_SUCCESS(rv, rv);
     }
-    rv = mChardet->Done();
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (mFeedChardet && (!aFromSegment || mReparseForbidden)) {
+      // mReparseForbidden is checked so that we get to use the sniffing
+      // buffer with the best guess so far if we aren't allowed to guess
+      // better later.
+      mFeedChardet = PR_FALSE;
+      rv = mChardet->Done();
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
     // fall thru; callback may have changed charset  
   }
   if (mCharsetSource == kCharsetUninitialized) {
     // Hopefully this case is never needed, but dealing with it anyway
     mCharset.AssignLiteral("windows-1252");
     mCharsetSource = kCharsetFromWeakDocTypeDefault;
     mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
   }
@@ -434,30 +472,32 @@ nsHtml5StreamParser::SniffStreamBytes(co
     // this is the last buffer
     PRUint32 countToSniffingLimit = NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE - mSniffingLength;
     nsHtml5ByteReadable readable(aFromSegment, aFromSegment + countToSniffingLimit);
     mMetaScanner->sniff(&readable, getter_AddRefs(mUnicodeDecoder), mCharset);
     if (mUnicodeDecoder) {
       mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
       // meta scan successful
       mCharsetSource = kCharsetFromMetaPrescan;
+      mFeedChardet = PR_FALSE;
       mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
       mMetaScanner = nsnull;
       return WriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
     }
     return FinalizeSniffing(aFromSegment, aCount, aWriteCount, countToSniffingLimit);
   }
 
   // not the last buffer
   nsHtml5ByteReadable readable(aFromSegment, aFromSegment + aCount);
   mMetaScanner->sniff(&readable, getter_AddRefs(mUnicodeDecoder), mCharset);
   if (mUnicodeDecoder) {
     // meta scan successful
     mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
     mCharsetSource = kCharsetFromMetaPrescan;
+    mFeedChardet = PR_FALSE;
     mTreeBuilder->SetDocumentCharset(mCharset, mCharsetSource);
     mMetaScanner = nsnull;
     return WriteSniffingBufferAndCurrentSegment(aFromSegment, aCount, aWriteCount);
   }
   if (!mSniffingBuffer) {
     mSniffingBuffer = new PRUint8[NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE];
   }
   memcpy(mSniffingBuffer + mSniffingLength, aFromSegment, aCount);
@@ -583,16 +623,20 @@ nsHtml5StreamParser::OnStartRequest(nsIR
     // XXX does Necko have a way to renavigate POST, etc. without hitting
     // the network?
     if (!method.EqualsLiteral("GET")) {
       // This is the old Gecko behavior but the HTML5 spec disagrees.
       // Don't reparse on POST.
       mReparseForbidden = PR_TRUE;
     }
   }
+
+  if (mCharsetSource >= kCharsetFromAutoDetection) {
+    mFeedChardet = PR_FALSE;
+  }
   
   if (mCharsetSource <= kCharsetFromMetaPrescan) {
     // we aren't ready to commit to an encoding yet
     // leave converter uninstantiated for now
     return NS_OK;
   }
   
   nsCOMPtr<nsICharsetConverterManager> convManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
@@ -619,16 +663,18 @@ nsHtml5StreamParser::DoStopRequest()
   if (IsTerminated()) {
     return;
   }
 
   if (!mUnicodeDecoder) {
     PRUint32 writeCount;
     FinalizeSniffing(nsnull, 0, &writeCount, 0);
     // dropped nsresult here
+  } else if (mFeedChardet) {
+    mChardet->Done();
   }
 
   mStreamState = STREAM_ENDED;
 
   if (IsTerminatedOrInterrupted()) {
     return;
   }
 
@@ -676,18 +722,26 @@ nsHtml5StreamParser::DoDataAvailable(PRU
                   "DoDataAvailable called when stream not open.");
   mTokenizerMutex.AssertCurrentThreadOwns();
 
   if (IsTerminated()) {
     return;
   }
 
   PRUint32 writeCount;
-  HasDecoder() ? WriteStreamBytes(aBuffer, aLength, &writeCount) :
-                 SniffStreamBytes(aBuffer, aLength, &writeCount);
+  if (HasDecoder()) {
+    if (mFeedChardet) {
+      PRBool dontFeed;
+      mChardet->DoIt((const char*)aBuffer, aLength, &dontFeed);
+      mFeedChardet = !dontFeed;
+    }
+    WriteStreamBytes(aBuffer, aLength, &writeCount);
+  } else {
+    SniffStreamBytes(aBuffer, aLength, &writeCount);
+  }
   // dropping nsresult here
   NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
 
   if (IsTerminatedOrInterrupted()) {
     return;
   }
 
   ParseAvailableData();
@@ -812,17 +866,17 @@ nsHtml5StreamParser::internalEncodingDec
       preferred.LowerCaseEqualsLiteral("jis_x0212-1990") ||
       preferred.LowerCaseEqualsLiteral("x-jis0208") ||
       preferred.LowerCaseEqualsLiteral("x-imap4-modified-utf7") ||
       preferred.LowerCaseEqualsLiteral("x-user-defined")) {
     // Not a rough ASCII superset
     return PR_FALSE;
   }
 
-  mTreeBuilder->NeedsCharsetSwitchTo(preferred);
+  mTreeBuilder->NeedsCharsetSwitchTo(preferred, kCharsetFromMetaTag);
   FlushTreeOpsAndDisarmTimer();
   Interrupt();
   // the tree op executor will cause the stream parser to terminate
   // if the charset switch request is accepted or it'll uninterrupt 
   // if the request failed.
   return PR_TRUE;
 }
 
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -462,16 +462,21 @@ class nsHtml5StreamParser : public nsISt
     nsCOMPtr<nsIRunnable>         mLoadFlusher;
 
     /**
      * The chardet instance if chardet is enabled.
      */
     nsCOMPtr<nsICharsetDetector>  mChardet;
 
     /**
+     * If false, don't push data to chardet.
+     */
+    PRBool                        mFeedChardet;
+
+    /**
      * Timer for flushing tree ops once in a while when not speculating.
      */
     nsCOMPtr<nsITimer>            mFlushTimer;
 
     /**
      * Keeps track whether mFlushTimer has been armed. Unfortunately,
      * nsITimer doesn't enable querying this from the timer itself.
      */
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -642,21 +642,22 @@ nsHtml5TreeBuilder::StreamEnded()
   if (!fragment) {
     nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
     NS_ASSERTION(treeOp, "Tree op allocation failed.");
     treeOp->Init(eTreeOpStreamEnded);
   }
 }
 
 void
-nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset)
+nsHtml5TreeBuilder::NeedsCharsetSwitchTo(const nsACString& aCharset,
+                                         PRInt32 aCharsetSource)
 {
   nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
   NS_ASSERTION(treeOp, "Tree op allocation failed.");
-  treeOp->Init(eTreeOpNeedsCharsetSwitchTo, aCharset);  
+  treeOp->Init(eTreeOpNeedsCharsetSwitchTo, aCharset, aCharsetSource);
 }
 
 void
 nsHtml5TreeBuilder::AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine)
 {
   NS_PRECONDITION(HasScript(), "No script to add a snapshot to!");
   NS_PRECONDITION(aSnapshot, "Got null snapshot.");
   mOpQueue.ElementAt(mOpQueue.Length() - 1).SetSnapshot(aSnapshot, aLine);
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -87,13 +87,13 @@
     PRBool Flush();
     
     void FlushLoads();
 
     void SetDocumentCharset(nsACString& aCharset, PRInt32 aCharsetSource);
 
     void StreamEnded();
 
-    void NeedsCharsetSwitchTo(const nsACString& aEncoding);
+    void NeedsCharsetSwitchTo(const nsACString& aEncoding, PRInt32 aSource);
 
     void AddSnapshotToScript(nsAHtml5TreeBuilderState* aSnapshot, PRInt32 aLine);
 
     void DropHandles();
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -759,33 +759,34 @@ nsHtml5TreeOpExecutor::Init(nsIDocument*
 void
 nsHtml5TreeOpExecutor::Start()
 {
   NS_PRECONDITION(!mStarted, "Tried to start when already started.");
   mStarted = PR_TRUE;
 }
 
 void
-nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(const char* aEncoding)
+nsHtml5TreeOpExecutor::NeedsCharsetSwitchTo(const char* aEncoding,
+                                            PRInt32 aSource)
 {
   EndDocUpdate();
 
   if(NS_UNLIKELY(!mParser)) {
     // got terminate
     return;
   }
   
   nsCOMPtr<nsIWebShellServices> wss = do_QueryInterface(mDocShell);
   if (!wss) {
     return;
   }
 
   // ask the webshellservice to load the URL
   if (NS_SUCCEEDED(wss->StopDocumentLoad())) {
-    wss->ReloadDocument(aEncoding, kCharsetFromMetaTag);
+    wss->ReloadDocument(aEncoding, aSource);
   }
   // if the charset switch was accepted, wss has called Terminate() on the
   // parser by now
 
   if (!mParser) {
     // success
     return;
   }
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -340,17 +340,17 @@ class nsHtml5TreeOpExecutor : public nsC
     void RunFlushLoop();
 
     void FlushDocumentWrite();
 
     void MaybeSuspend();
 
     void Start();
 
-    void NeedsCharsetSwitchTo(const char* aEncoding);
+    void NeedsCharsetSwitchTo(const char* aEncoding, PRInt32 aSource);
     
     PRBool IsComplete() {
       return !mParser;
     }
     
     PRBool HasStarted() {
       return mStarted;
     }
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -665,17 +665,18 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
       char* str = mOne.charPtr;
       PRInt32 charsetSource = mInt;
       nsDependentCString dependentString(str);
       aBuilder->SetDocumentCharsetAndSource(dependentString, charsetSource);
       return rv;
     }
     case eTreeOpNeedsCharsetSwitchTo: {
       char* str = mOne.charPtr;
-      aBuilder->NeedsCharsetSwitchTo(str);
+      PRInt32 charsetSource = mInt;
+      aBuilder->NeedsCharsetSwitchTo(str, charsetSource);
       return rv;    
     }
     case eTreeOpUpdateStyleSheet: {
       nsIContent* node = *(mOne.node);
       aBuilder->FlushPendingAppendNotifications();
       aBuilder->UpdateStyleSheet(node);
       return rv;
     }
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -141,17 +141,29 @@ class nsHtml5TreeOperation {
       mOpCode = aOpCode;
       mOne.node = aNode;
       mTwo.node = aParent;
     }
     
     inline void Init(eHtml5TreeOperation aOpCode, 
                      const nsACString& aString,
                      PRInt32 aInt32) {
-      Init(aOpCode, aString);
+      NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+        "Op code must be uninitialized when initializing.");
+
+      PRInt32 len = aString.Length();
+      char* str = new char[len + 1];
+      const char* start = aString.BeginReading();
+      for (PRInt32 i = 0; i < len; ++i) {
+        str[i] = start[i];
+      }
+      str[len] = '\0';
+
+      mOpCode = aOpCode;
+      mOne.charPtr = str;
       mInt = aInt32;
     }
 
     inline void Init(eHtml5TreeOperation aOpCode,
                      nsIContent** aNode,
                      nsIContent** aParent, 
                      nsIContent** aTable) {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
@@ -257,32 +269,16 @@ class nsHtml5TreeOperation {
                      const nsAString& aSystemId) {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
         "Op code must be uninitialized when initializing.");
       mOpCode = eTreeOpAppendDoctypeToDocument;
       mOne.atom = aName;
       mTwo.stringPair = new nsHtml5TreeOperationStringPair(aPublicId, aSystemId);
     }
     
-    inline void Init(eHtml5TreeOperation aOpCode, const nsACString& aString) {
-      NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
-        "Op code must be uninitialized when initializing.");
-
-      PRInt32 len = aString.Length();
-      char* str = new char[len + 1];
-      const char* start = aString.BeginReading();
-      for (PRInt32 i = 0; i < len; ++i) {
-        str[i] = start[i];
-      }
-      str[len] = '\0';
-
-      mOpCode = aOpCode;
-      mOne.charPtr = str;
-    }
-
     inline void Init(eHtml5TreeOperation aOpCode, const nsAString& aString) {
       NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
         "Op code must be uninitialized when initializing.");
 
       PRUnichar* str = NS_StringCloneData(aString);
       mOpCode = aOpCode;
       mOne.unicharPtr = str;
     }