Bug 475636: Disallow refresh to javascript uris. r/sr=bz
authorJonas Sicking <jonas@sicking.cc>
Mon, 09 Mar 2009 23:03:39 -0700
changeset 25969 6328cc687abe66553c26b33934b2ed1bbcb72d4c
parent 25968 9e485bb3604e5fef5ee813327f0f87b8997c72cf
child 25970 2e5e44527a3f6d4f5b868bf735ed7b9845f190e2
push idunknown
push userunknown
push dateunknown
bugs475636
milestone1.9.2a1pre
Bug 475636: Disallow refresh to javascript uris. r/sr=bz
docshell/base/nsDocShell.cpp
docshell/base/nsDocShellLoadInfo.cpp
docshell/base/nsDocShellLoadInfo.h
docshell/base/nsIDocShellLoadInfo.idl
docshell/test/Makefile.in
docshell/test/file_bug475636.sjs
docshell/test/test_bug475636.html
parser/htmlparser/src/nsExpatDriver.cpp
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -722,16 +722,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
       return NS_OK; // JS may not handle returning of an error code
     }
     nsresult rv;
     nsCOMPtr<nsIURI> referrer;
     nsCOMPtr<nsIInputStream> postStream;
     nsCOMPtr<nsIInputStream> headersStream;
     nsCOMPtr<nsISupports> owner;
     PRBool inheritOwner = PR_FALSE;
+    PRBool ownerIsExplicit = PR_FALSE;
     PRBool sendReferrer = PR_TRUE;
     nsCOMPtr<nsISHEntry> shEntry;
     nsXPIDLString target;
     PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);    
 
     NS_ENSURE_ARG(aURI);
 
     // Extract the info from the DocShellLoadInfo struct...
@@ -740,16 +741,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
 
         nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
         aLoadInfo->GetLoadType(&lt);
         // Get the appropriate loadType from nsIDocShellLoadInfo type
         loadType = ConvertDocShellLoadInfoToLoadType(lt);
 
         aLoadInfo->GetOwner(getter_AddRefs(owner));
         aLoadInfo->GetInheritOwner(&inheritOwner);
+        aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
         aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
         aLoadInfo->GetTarget(getter_Copies(target));
         aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
         aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
         aLoadInfo->GetSendReferrer(&sendReferrer);
     }
 
 #if defined(PR_LOGGING) && defined(DEBUG)
@@ -857,94 +859,109 @@ nsDocShell::LoadURI(nsIURI * aURI,
     } // !shEntry
 
     if (shEntry) {
 #ifdef DEBUG
         PR_LOG(gDocShellLog, PR_LOG_DEBUG,
               ("nsDocShell[%p]: loading from session history", this));
 #endif
 
-        rv = LoadHistoryEntry(shEntry, loadType);
-    }
+        return LoadHistoryEntry(shEntry, loadType);
+    }
+
     // Perform the load...
-    else {
-        // We need an owner (a referring principal). 4 possibilities:
-        // (1) If the system principal was passed in and we're a typeContent
-        //     docshell, inherit the principal from the current document
-        //     instead.
-        // (2) In all other cases when the principal passed in is not null,
-        //     use that principal.
-        // (3) If the caller has allowed inheriting from the current document,
-        //     or if we're being called from system code (eg chrome JS or pure
-        //     C++) then inheritOwner should be true and InternalLoad will get
-        //     an owner from the current document. If none of these things are
-        //     true, then
-        // (4) we pass a null owner into the channel, and an owner will be
-        //     created later from the channel's internal data.
-        //
-        // NOTE: This all only works because the only thing the owner is used  
-        //       for in InternalLoad is data:, javascript:, and about:blank
-        //       URIs.  For other URIs this would all be dead wrong!
-        nsCOMPtr<nsIScriptSecurityManager> secMan =
-            do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+
+    // We need an owner (a referring principal).
+    //
+    // If ownerIsExplicit is not set there are 4 possibilities:
+    // (1) If the system principal was passed in and we're a typeContent
+    //     docshell, inherit the principal from the current document
+    //     instead.
+    // (2) In all other cases when the principal passed in is not null,
+    //     use that principal.
+    // (3) If the caller has allowed inheriting from the current document,
+    //     or if we're being called from system code (eg chrome JS or pure
+    //     C++) then inheritOwner should be true and InternalLoad will get
+    //     an owner from the current document. If none of these things are
+    //     true, then
+    // (4) we pass a null owner into the channel, and an owner will be
+    //     created later from the channel's internal data.
+    //
+    // If ownerIsExplicit *is* set, there are 4 possibilities
+    // (1) If the system principal was passed in and we're a typeContent
+    //     docshell, return an error.
+    // (2) In all other cases when the principal passed in is not null,
+    //     use that principal.
+    // (3) If the caller has allowed inheriting from the current document,
+    //     then inheritOwner should be true and InternalLoad will get an owner
+    //     from the current document. If none of these things are true, then
+    // (4) we pass a null owner into the channel, and an owner will be
+    //     created later from the channel's internal data.
+    //
+    // NOTE: This all only works because the only thing the owner is used  
+    //       for in InternalLoad is data:, javascript:, and about:blank
+    //       URIs.  For other URIs this would all be dead wrong!
+
+    nsCOMPtr<nsIScriptSecurityManager> secMan =
+        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (owner && mItemType != typeChrome) {
+        nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
+        PRBool isSystem;
+        rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
         NS_ENSURE_SUCCESS(rv, rv);
 
-        if (owner && mItemType != typeChrome) {
-            nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(owner);
-            PRBool isSystem;
-            rv = secMan->IsSystemPrincipal(ownerPrincipal, &isSystem);
-            NS_ENSURE_SUCCESS(rv, rv);
-            
-            if (isSystem) {
-                owner = nsnull;
-                inheritOwner = PR_TRUE;
+        if (isSystem) {
+            if (ownerIsExplicit) {
+                return NS_ERROR_DOM_SECURITY_ERR;
             }
-        }
-        if (!owner && !inheritOwner) {
-            // See if there's system or chrome JS code running
-            rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
-            if (NS_FAILED(rv)) {
-                // Set it back to false
-                inheritOwner = PR_FALSE;
-            }
-        }
-
-        PRUint32 flags = 0;
-
-        if (inheritOwner)
-            flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
-
-        if (!sendReferrer)
-            flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
+            owner = nsnull;
+            inheritOwner = PR_TRUE;
+        }
+    }
+    if (!owner && !inheritOwner && !ownerIsExplicit) {
+        // See if there's system or chrome JS code running
+        rv = secMan->SubjectPrincipalIsSystem(&inheritOwner);
+        if (NS_FAILED(rv)) {
+            // Set it back to false
+            inheritOwner = PR_FALSE;
+        }
+    }
+
+    PRUint32 flags = 0;
+
+    if (inheritOwner)
+        flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
+
+    if (!sendReferrer)
+        flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
             
-        if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
-            flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
-
-        if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
-            flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
-
-        if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
-            flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
-
-        rv = InternalLoad(aURI,
-                          referrer,
-                          owner,
-                          flags,
-                          target.get(),
-                          nsnull,         // No type hint
-                          postStream,
-                          headersStream,
-                          loadType,
-                          nsnull,         // No SHEntry
-                          aFirstParty,
-                          nsnull,         // No nsIDocShell
-                          nsnull);        // No nsIRequest
-    }
-
-    return rv;
+    if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP)
+        flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+
+    if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD)
+        flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+
+    if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER)
+        flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
+
+    return InternalLoad(aURI,
+                        referrer,
+                        owner,
+                        flags,
+                        target.get(),
+                        nsnull,         // No type hint
+                        postStream,
+                        headersStream,
+                        loadType,
+                        nsnull,         // No SHEntry
+                        aFirstParty,
+                        nsnull,         // No nsIDocShell
+                        nsnull);        // No nsIRequest
 }
 
 NS_IMETHODIMP
 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
                        const nsACString &aContentType,
                        const nsACString &aContentCharset,
                        nsIDocShellLoadInfo * aLoadInfo)
 {
@@ -4571,51 +4588,52 @@ nsDocShell::ForceRefreshURI(nsIURI * aUR
      */
     loadInfo->SetSendReferrer(PR_FALSE);
 
     /* for most refreshes the current URI is an appropriate
      * internal referrer
      */
     loadInfo->SetReferrer(mCurrentURI);
 
+    /* Don't ever "guess" on which owner to use to avoid picking
+     * the current owner.
+     */
+    loadInfo->SetOwnerIsExplicit(PR_TRUE);
+
     /* Check if this META refresh causes a redirection
      * to another site. 
      */
     PRBool equalUri = PR_FALSE;
     nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
-    if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh) {
-
-        /* It is a META refresh based redirection. Now check if it happened
-           within the threshold time we have in mind(15000 ms as defined by
-           REFRESH_REDIRECT_TIMER). If so, pass a REPLACE flag to LoadURI().
+    if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
+        aDelay <= REFRESH_REDIRECT_TIMER) {
+
+        /* It is a META refresh based redirection within the threshold time
+         * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
+         * Pass a REPLACE flag to LoadURI().
          */
-        if (aDelay <= REFRESH_REDIRECT_TIMER) {
-            loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
+        loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
             
-            /* for redirects we mimic HTTP, which passes the
-             *  original referrer
-             */
-            nsCOMPtr<nsIURI> internalReferrer;
-            GetReferringURI(getter_AddRefs(internalReferrer));
-            if (internalReferrer) {
-                loadInfo->SetReferrer(internalReferrer);
-            }
-        }
-        else
-            loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
-        /*
-         * LoadURI(...) will cancel all refresh timers... This causes the
-         * Timer and its refreshData instance to be released...
+        /* for redirects we mimic HTTP, which passes the
+         *  original referrer
          */
-        LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
-        return NS_OK;
-    }
-    else
+        nsCOMPtr<nsIURI> internalReferrer;
+        GetReferringURI(getter_AddRefs(internalReferrer));
+        if (internalReferrer) {
+            loadInfo->SetReferrer(internalReferrer);
+        }
+    }
+    else {
         loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
-
+    }
+
+    /*
+     * LoadURI(...) will cancel all refresh timers... This causes the
+     * Timer and its refreshData instance to be released...
+     */
     LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
 
     return NS_OK;
 }
 
 nsresult
 nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI,
                                       const nsACString & aHeader)
@@ -4821,16 +4839,28 @@ nsDocShell::SetupRefreshURIFromHeader(ns
         nsCOMPtr<nsIScriptSecurityManager>
             securityManager(do_GetService
                             (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
         if (NS_SUCCEEDED(rv)) {
             rv = securityManager->
                 CheckLoadURI(aBaseURI, uri,
                              nsIScriptSecurityManager::
                              LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
+
+            if (NS_SUCCEEDED(rv)) {
+                PRBool isjs = PR_TRUE;
+                rv = NS_URIChainHasFlags(uri,
+                  nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                if (isjs) {
+                    return NS_ERROR_FAILURE;
+                }
+            }
+
             if (NS_SUCCEEDED(rv)) {
                 // Since we can't travel back in time yet, just pretend
                 // negative numbers do nothing at all.
                 if (seconds < 0)
                     return NS_ERROR_FAILURE;
 
                 rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE);
             }
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -44,16 +44,17 @@
 #include "nsReadableUtils.h"
 
 //*****************************************************************************
 //***    nsDocShellLoadInfo: Object Management
 //*****************************************************************************
 
 nsDocShellLoadInfo::nsDocShellLoadInfo()
   : mInheritOwner(PR_FALSE),
+    mOwnerIsExplicit(PR_FALSE),
     mSendReferrer(PR_TRUE),
     mLoadType(nsIDocShellLoadInfo::loadNormal)
 {
 }
 
 nsDocShellLoadInfo::~nsDocShellLoadInfo()
 {
 }
@@ -113,16 +114,28 @@ NS_IMETHODIMP nsDocShellLoadInfo::GetInh
 }
 
 NS_IMETHODIMP nsDocShellLoadInfo::SetInheritOwner(PRBool aInheritOwner)
 {
    mInheritOwner = aInheritOwner;
    return NS_OK;
 }
 
+NS_IMETHODIMP nsDocShellLoadInfo::GetOwnerIsExplicit(PRBool* aOwnerIsExplicit)
+{
+   *aOwnerIsExplicit = mOwnerIsExplicit;
+   return NS_OK;
+}
+
+NS_IMETHODIMP nsDocShellLoadInfo::SetOwnerIsExplicit(PRBool aOwnerIsExplicit)
+{
+   mOwnerIsExplicit = aOwnerIsExplicit;
+   return NS_OK;
+}
+
 NS_IMETHODIMP nsDocShellLoadInfo::GetLoadType(nsDocShellInfoLoadType * aLoadType)
 {
    NS_ENSURE_ARG_POINTER(aLoadType);
 
    *aLoadType = mLoadType;
    return NS_OK;
 }
 
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -61,16 +61,17 @@ public:
 
 protected:
   virtual ~nsDocShellLoadInfo();
 
 protected:
   nsCOMPtr<nsIURI>                 mReferrer;
   nsCOMPtr<nsISupports>            mOwner;
   PRPackedBool                     mInheritOwner;
+  PRPackedBool                     mOwnerIsExplicit;
   PRPackedBool                     mSendReferrer;
   nsDocShellInfoLoadType           mLoadType;
   nsCOMPtr<nsISHEntry>             mSHEntry;
   nsString                         mTarget;
   nsCOMPtr<nsIInputStream>         mPostDataStream;
   nsCOMPtr<nsIInputStream>         mHeadersStream;
 };
 
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -46,32 +46,40 @@
  */
  
 interface nsIURI;
 interface nsIInputStream;
 interface nsISHEntry;
 
 typedef long nsDocShellInfoLoadType;
 
-[scriptable, uuid(4f813a88-7aca-4607-9896-d97270cdf15e)]
+[scriptable, uuid(92a0a637-373e-4647-9476-ead11e005c75)]
 interface nsIDocShellLoadInfo : nsISupports
 {
     /** This is the referrer for the load. */
     attribute nsIURI referrer;
 
     /** The owner of the load, that is, the entity responsible for 
      *  causing the load to occur. This should be a nsIPrincipal typically.
      */
     attribute nsISupports owner;
 
     /** If this attribute is true and no owner is specified, copy
      *  the owner from the referring document.
      */
     attribute boolean inheritOwner;
 
+    /** If this attribute is true only ever use the owner specify by
+     *  the owner and inheritOwner attributes.
+     *  If there are security reasons for why this is unsafe, such
+     *  as trying to use a systemprincipal owner for a content docshell
+     *  the load fails.
+     */
+    attribute boolean ownerIsExplicit;
+
     /* these are load type enums... */
     const long loadNormal = 0;                     // Normal Load
     const long loadNormalReplace = 1;              // Normal Load but replaces current history slot
     const long loadHistory = 2;                    // Load from history
     const long loadReloadNormal = 3;               // Reload
     const long loadReloadBypassCache = 4;
     const long loadReloadBypassProxy = 5;
     const long loadReloadBypassProxyAndCache = 6;
--- a/docshell/test/Makefile.in
+++ b/docshell/test/Makefile.in
@@ -64,12 +64,14 @@ include $(topsrcdir)/config/rules.mk
 		test_bug384014.html \
 		test_bug387979.html \
 		test_bug404548.html \
 		bug404548-subframe.html \
 		test_bug413310.html \
 		bug413310-subframe.html \
 		bug413310-post.sjs \
 		test_bug402210.html \
+		test_bug475636.html \
+		file_bug475636.sjs \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug475636.sjs
@@ -0,0 +1,90 @@
+jsURL = "javascript:" + escape('window.parent.postMessage("JS uri ran", "*");\
+return \'\
+<script>\
+window.parent.postMessage("Able to access private: " +\
+  window.parent.private, "*");\
+</script>\'');
+dataURL = "data:text/html," + escape('<!DOCTYPE HTML>\
+<script>\
+try {\
+  window.parent.postMessage("Able to access private: " +\
+    window.parent.private, "*");\
+}\
+catch (e) {\
+  window.parent.postMessage("pass", "*");\
+}\
+</script>');
+
+tests = [
+// Plain document should work as normal
+'<!DOCTYPE HTML>\
+<script>\
+try {\
+  window.parent.private;\
+  window.parent.postMessage("pass", "*");\
+}\
+catch (e) {\
+  window.parent.postMessage("Unble to access private", "*");\
+}\
+</script>',
+
+// refresh to plain doc
+{ refresh: "file_bug475636.sjs?1",
+  doc: '<!DOCTYPE HTML>' },
+
+// meta-refresh to plain doc
+'<!DOCTYPE HTML>\
+<head>\
+  <meta http-equiv="refresh" content="0; url=file_bug475636.sjs?1">\
+</head>',
+
+// refresh to data url
+{ refresh: dataURL,
+  doc: '<!DOCTYPE HTML>' },
+
+// meta-refresh to data url
+'<!DOCTYPE HTML>\
+<head>\
+  <meta http-equiv="refresh" content="0; url=' + dataURL + '">\
+</head>',
+
+// refresh to js url should not be followed
+{ refresh: jsURL,
+  doc:
+'<!DOCTYPE HTML>\
+<script>\
+setTimeout(function() {\
+  window.parent.postMessage("pass", "*");\
+}, 2000);\
+</script>' },
+
+// meta refresh to js url should not be followed
+'<!DOCTYPE HTML>\
+<head>\
+  <meta http-equiv="refresh" content="0; url=' + jsURL + '">\
+</head>\
+<script>\
+setTimeout(function() {\
+  window.parent.postMessage("pass", "*");\
+}, 2000);\
+</script>'
+];
+
+
+function handleRequest(request, response)
+{
+  dump("@@@@@@@@@hi there: " + request.queryString + "\n");
+  test = tests[parseInt(request.queryString, 10) - 1];
+  response.setHeader("Content-Type", "text/html");
+
+  if (!test) {
+    response.write('<script>parent.postMessage("done", "*");</script>');
+  }
+  else if (typeof test == "string") {
+    response.write(test);
+  }
+  else if (test.refresh) {
+    response.setHeader("Refresh", "0; url=" + test.refresh);
+    response.write(test.doc);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bug475636.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=475636
+Test that refresh to data: URIs don't inherit the principal
+-->
+<head>
+  <title>Test for Bug 475636</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="gen.next()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=475636">Mozilla Bug 475636</a>
+
+<div id="content" style="display: none">
+  
+</div>
+<iframe id=loader></iframe>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.8">
+
+SimpleTest.waitForExplicitFinish();
+
+gen = runTests();
+
+window.private = 42;
+
+window.addEventListener("message", function(e) {
+  gen.send(e.data);
+}, false);
+
+var url = "file_bug475636.sjs?";
+
+function runTests() {
+  var loader = document.getElementById('loader');
+  for (var testNum = 1; ; ++testNum) {
+    loader.src = url + testNum;
+    let res = (yield);
+    if (res == "done") {
+      SimpleTest.finish();
+      yield;
+    }
+    is(res, "pass");
+  }
+}
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/parser/htmlparser/src/nsExpatDriver.cpp
+++ b/parser/htmlparser/src/nsExpatDriver.cpp
@@ -1204,16 +1204,20 @@ nsExpatDriver::ConsumeToken(nsScanner& a
     start.advance(length);
 
     // It's possible for start to have passed end if we received more data
     // (e.g. if we spun the event loop in an inline script). Reload end now
     // to compensate.
     aScanner.EndReading(end);
   }
 
+  if (start == end && !aScanner->IsIncremental()) {
+    mInternalState = kEOF;
+  }
+
   aScanner.SetPosition(currentExpatPosition, PR_TRUE);
   aScanner.Mark();
 
   PR_LOG(gExpatDriverLog, PR_LOG_DEBUG,
          ("Remaining in expat's buffer: %i, remaining in scanner: %i.",
           mExpatBuffered, Distance(currentExpatPosition, end)));
 
   return NS_SUCCEEDED(mInternalState) ? kEOF : NS_OK;