diff --git a/content/html/document/src/nsHTMLContentSink.cpp b/content/html/document/src/nsHTMLContentSink.cpp index 5aca0e878d26..5d56fbc83111 100644 --- a/content/html/document/src/nsHTMLContentSink.cpp +++ b/content/html/document/src/nsHTMLContentSink.cpp @@ -4656,7 +4656,7 @@ HTMLContentSink::ProcessHeaderData(nsIAtom* aHeader,const nsAReadableString& aVa nsCOMPtr reefer = do_QueryInterface(mWebShell); if (reefer) { - rv = reefer->RefreshURIFromHeader(baseURI, aValue); + rv = reefer->SetupRefreshURIFromHeader(baseURI, aValue); if (NS_FAILED(rv)) return rv; } } // END refresh diff --git a/content/xml/document/src/nsXMLContentSink.cpp b/content/xml/document/src/nsXMLContentSink.cpp index 2439e4903327..6d20b0016837 100644 --- a/content/xml/document/src/nsXMLContentSink.cpp +++ b/content/xml/document/src/nsXMLContentSink.cpp @@ -783,7 +783,7 @@ nsXMLContentSink::ProcessHeaderData(nsIAtom* aHeader,const nsAReadableString& aV nsCOMPtr reefer = do_QueryInterface(mWebShell); if (reefer) { - rv = reefer->RefreshURIFromHeader(baseURI, aValue); + rv = reefer->SetupRefreshURIFromHeader(baseURI, aValue); if (NS_FAILED(rv)) return rv; } } // END refresh diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 043acea5b3ac..acc7da078ea0 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -3236,13 +3236,14 @@ nsDocShell::ReportScriptError(nsIScriptError * errorObject) //***************************************************************************** NS_IMETHODIMP -nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat, - PRBool aMetaRefresh) +nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat, PRBool aMetaRefresh) { NS_ENSURE_ARG(aURI); nsRefreshTimer *refreshTimer = new nsRefreshTimer(); NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY); + PRUint32 busyFlags = 0; + GetBusyFlags(&busyFlags); nsCOMPtr dataRef = refreshTimer; // Get the ref count to 1 @@ -3257,39 +3258,274 @@ nsDocShell::RefreshURI(nsIURI * aURI, PRInt32 aDelay, PRBool aRepeat, NS_ERROR_FAILURE); } - nsCOMPtr timer = do_CreateInstance("@mozilla.org/timer;1"); - NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE); - - mRefreshURIList->AppendElement(timer); // owning timer ref - timer->Init(refreshTimer, aDelay); + if (busyFlags & BUSY_FLAGS_BUSY) { + // We are busy loading another page. Don't create the + // timer right now. Instead queue up the request and trigger the + // timer in EndPageLoad(). + mRefreshURIList->AppendElement(refreshTimer); + } + else { + // There is no page loading going on right now. Create the + // timer and fire it right away. + nsCOMPtr timer = do_CreateInstance("@mozilla.org/timer;1"); + NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE); + mRefreshURIList->AppendElement(timer); // owning timer ref + timer->Init(refreshTimer, aDelay); + } return NS_OK; } + +nsresult +nsDocShell::SetupRefreshURIFromHeader(nsIURI * aBaseURI, + const nsAReadableString & aHeader) +{ + // Refresh headers are parsed with the following format in mind + // + // By the time we are here, the following is true: + // header = "REFRESH" + // content = "5; URL=http://uri" // note the URL attribute is + // optional, if it is absent, the currently loaded url is used. + // Also note that the seconds and URL separator can be either + // a ';' or a ','. The ',' separator should be illegal but CNN + // is using it. + // + // We need to handle the following strings, where + // - X is a set of digits + // - URI is either a relative or absolute URI + // + // Note that URI should start with "url=" but we allow omission + // + // "" || ";" || "," + // empty string. use the currently loaded URI + // and refresh immediately. + // "X" || "X;" || "X," + // Refresh the currently loaded URI in X seconds. + // "X; URI" || "X, URI" + // Refresh using URI as the destination in X seconds. + // "URI" || "; URI" || ", URI" + // Refresh immediately using URI as the destination. + // + // Currently, anything immediately following the URI, if + // seperated by any char in the set "'\"\t\r\n " will be + // ignored. So "10; url=go.html ; foo=bar" will work, + // and so will "10; url='go.html'; foo=bar". However, + // "10; url=go.html; foo=bar" will result in the uri + // "go.html;" since ';' and ',' are valid uri characters. + // + // Note that we need to remove any tokens wrapping the URI. + // These tokens currently include spaces, double and single + // quotes. + + // when done, seconds is 0 or the given number of seconds + // uriAttrib is empty or the URI specified + nsAutoString uriAttrib; + PRInt32 seconds = 0; + + nsReadingIterator < PRUnichar > iter, tokenStart, doneIterating; + + aHeader.BeginReading(iter); + aHeader.EndReading(doneIterating); + + // skip leading whitespace + while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) + ++iter; + + tokenStart = iter; + + // skip leading + and - + if (iter != doneIterating && (*iter == '-' || *iter == '+')) + ++iter; + + // parse number + while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) { + seconds = seconds * 10 + (*iter - '0'); + ++iter; + } + + if (iter != doneIterating) { + // if we started with a '-', number is negative + if (*tokenStart == '-') + seconds = -seconds; + + // skip to next ';' or ',' + while (iter != doneIterating && !(*iter == ';' || *iter == ',')) + ++iter; + + // skip ';' or ',' + if (iter != doneIterating && (*iter == ';' || *iter == ',')) { + ++iter; + + // skip whitespace + while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) + ++iter; + } + } + + // possible start of URI + tokenStart = iter; + + // skip "url = " to real start of URI + if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) { + ++iter; + if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) { + ++iter; + if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) { + ++iter; + + // skip whitespace + while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) + ++iter; + + if (iter != doneIterating && *iter == '=') { + ++iter; + + // skip whitespace + while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // found real start of URI + tokenStart = iter; + } + } + } + } + + // skip a leading '"' or '\'' + if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\'')) + ++tokenStart; + + // set iter to start of URI + iter = tokenStart; + + // tokenStart here points to the beginning of URI + + // skip anything which isn't whitespace + while (iter != doneIterating && !nsCRT::IsAsciiSpace(*iter)) + ++iter; + + // move iter one back if the last character is a '"' or '\'' + if (iter != tokenStart) { + --iter; + if (!(*iter == '"' || *iter == '\'')) + ++iter; + } + + // URI is whatever's contained from tokenStart to iter. + // note: if tokenStart == doneIterating, so is iter. + + nsresult rv = NS_OK; + + nsCOMPtr uri; + if (tokenStart == iter) { + uri = aBaseURI; + } + else { + uriAttrib = Substring(tokenStart, iter); + rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, aBaseURI); + } + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr + securityManager(do_GetService + (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = securityManager->CheckLoadURI(aBaseURI, uri, + nsIScriptSecurityManager:: + DISALLOW_FROM_MAIL); + if (NS_SUCCEEDED(rv)) { + // since we can't travel back in time yet, just pretend it was meant figuratively + if (seconds < 0) + seconds = 0; + + rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE); + } + } + } + return rv; +} + +NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel) +{ + nsresult + rv; + nsCOMPtr httpChannel(do_QueryInterface(aChannel, &rv)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr referrer; + rv = httpChannel->GetReferrer(getter_AddRefs(referrer)); + + if (NS_SUCCEEDED(rv)) { + SetReferrerURI(referrer); + + nsXPIDLCString refreshHeader; + rv = httpChannel->GetResponseHeader("refresh", + getter_Copies(refreshHeader)); + + if (refreshHeader) + rv = SetupRefreshURIFromHeader(mCurrentURI, + NS_ConvertUTF8toUCS2(refreshHeader)); + } + } + return rv; +} + NS_IMETHODIMP nsDocShell::CancelRefreshURITimers() { if (!mRefreshURIList) return NS_OK; - PRUint32 n; + PRUint32 n=0; mRefreshURIList->Count(&n); while (n) { nsCOMPtr element; - mRefreshURIList->GetElementAt(0, getter_AddRefs(element)); + mRefreshURIList->GetElementAt(--n, getter_AddRefs(element)); nsCOMPtr timer(do_QueryInterface(element)); - mRefreshURIList->RemoveElementAt(0); // bye bye owning timer ref + mRefreshURIList->RemoveElementAt(n); // bye bye owning timer ref if (timer) - timer->Cancel(); - n--; + timer->Cancel(); } return NS_OK; } +NS_IMETHODIMP +nsDocShell::RefreshURIFromQueue() +{ + if (!mRefreshURIList) + return NS_OK; + PRUint32 n = 0; + mRefreshURIList->Count(&n); + + while (n) { + nsCOMPtr element; + mRefreshURIList->GetElementAt(--n, getter_AddRefs(element)); + nsCOMPtr refreshInfo(do_QueryInterface(element)); + + if (refreshInfo) { + // This is the nsRefreshTimer object, waiting to be + // setup in a timer object and fired. + // Create the timer and trigger it. + PRUint32 delay = refreshInfo->GetDelay(); + nsCOMPtr timer = do_CreateInstance("@mozilla.org/timer;1"); + if (timer) { + // Replace the nsRefreshTimer element in the queue with + // its corresponding timer object, so that in case another + // load comes through before the timer can go off, the timer will + // get cancelled in CancelRefreshURITimer() + mRefreshURIList->ReplaceElementAt(timer, n); + timer->Init(refreshInfo, delay); + } + } + } // while + + return NS_OK; +} + //***************************************************************************** // nsDocShell::nsIContentViewerContainer //***************************************************************************** @@ -3508,6 +3744,10 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress, mEODForCurrentDocument = PR_TRUE; } + // if there's a refresh header in the channel, this method + // will set it up for us. + RefreshURIFromQueue(); + return NS_OK; } @@ -4925,203 +5165,10 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, // if there's a refresh header in the channel, this method // will set it up for us. SetupRefreshURI(aChannel); - + return NS_OK; } -nsresult -nsDocShell::RefreshURIFromHeader(nsIURI * aBaseURI, - const nsAReadableString & aHeader) -{ - // Refresh headers are parsed with the following format in mind - // - // By the time we are here, the following is true: - // header = "REFRESH" - // content = "5; URL=http://uri" // note the URL attribute is - // optional, if it is absent, the currently loaded url is used. - // Also note that the seconds and URL separator can be either - // a ';' or a ','. The ',' separator should be illegal but CNN - // is using it. - // - // We need to handle the following strings, where - // - X is a set of digits - // - URI is either a relative or absolute URI - // - // Note that URI should start with "url=" but we allow omission - // - // "" || ";" || "," - // empty string. use the currently loaded URI - // and refresh immediately. - // "X" || "X;" || "X," - // Refresh the currently loaded URI in X seconds. - // "X; URI" || "X, URI" - // Refresh using URI as the destination in X seconds. - // "URI" || "; URI" || ", URI" - // Refresh immediately using URI as the destination. - // - // Currently, anything immediately following the URI, if - // seperated by any char in the set "'\"\t\r\n " will be - // ignored. So "10; url=go.html ; foo=bar" will work, - // and so will "10; url='go.html'; foo=bar". However, - // "10; url=go.html; foo=bar" will result in the uri - // "go.html;" since ';' and ',' are valid uri characters. - // - // Note that we need to remove any tokens wrapping the URI. - // These tokens currently include spaces, double and single - // quotes. - - // when done, seconds is 0 or the given number of seconds - // uriAttrib is empty or the URI specified - nsAutoString uriAttrib; - PRInt32 seconds = 0; - - nsReadingIterator < PRUnichar > iter, tokenStart, doneIterating; - - aHeader.BeginReading(iter); - aHeader.EndReading(doneIterating); - - // skip leading whitespace - while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) - ++iter; - - tokenStart = iter; - - // skip leading + and - - if (iter != doneIterating && (*iter == '-' || *iter == '+')) - ++iter; - - // parse number - while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) { - seconds = seconds * 10 + (*iter - '0'); - ++iter; - } - - if (iter != doneIterating) { - // if we started with a '-', number is negative - if (*tokenStart == '-') - seconds = -seconds; - - // skip to next ';' or ',' - while (iter != doneIterating && !(*iter == ';' || *iter == ',')) - ++iter; - - // skip ';' or ',' - if (iter != doneIterating && (*iter == ';' || *iter == ',')) { - ++iter; - - // skip whitespace - while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) - ++iter; - } - } - - // possible start of URI - tokenStart = iter; - - // skip "url = " to real start of URI - if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) { - ++iter; - if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) { - ++iter; - if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) { - ++iter; - - // skip whitespace - while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) - ++iter; - - if (iter != doneIterating && *iter == '=') { - ++iter; - - // skip whitespace - while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) - ++iter; - - // found real start of URI - tokenStart = iter; - } - } - } - } - - // skip a leading '"' or '\'' - if (tokenStart != doneIterating && (*tokenStart == '"' || *tokenStart == '\'')) - ++tokenStart; - - // set iter to start of URI - iter = tokenStart; - - // tokenStart here points to the beginning of URI - - // skip anything which isn't whitespace - while (iter != doneIterating && !nsCRT::IsAsciiSpace(*iter)) - ++iter; - - // move iter one back if the last character is a '"' or '\'' - if (iter != tokenStart) { - --iter; - if (!(*iter == '"' || *iter == '\'')) - ++iter; - } - - // URI is whatever's contained from tokenStart to iter. - // note: if tokenStart == doneIterating, so is iter. - - nsresult rv = NS_OK; - - nsCOMPtr uri; - if (tokenStart == iter) { - uri = aBaseURI; - } - else { - uriAttrib = Substring(tokenStart, iter); - rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, aBaseURI); - } - - if (NS_SUCCEEDED(rv)) { - nsCOMPtr - securityManager(do_GetService - (NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); - if (NS_SUCCEEDED(rv)) { - rv = securityManager->CheckLoadURI(aBaseURI, uri, - nsIScriptSecurityManager:: - DISALLOW_FROM_MAIL); - if (NS_SUCCEEDED(rv)) { - // since we can't travel back in time yet, just pretend it was meant figuratively - if (seconds < 0) - seconds = 0; - - rv = RefreshURI(uri, seconds * 1000, PR_FALSE, PR_TRUE); - } - } - } - return rv; -} - -NS_IMETHODIMP nsDocShell::SetupRefreshURI(nsIChannel * aChannel) -{ - nsresult - rv; - nsCOMPtr httpChannel(do_QueryInterface(aChannel, &rv)); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr referrer; - rv = httpChannel->GetReferrer(getter_AddRefs(referrer)); - - if (NS_SUCCEEDED(rv)) { - SetReferrerURI(referrer); - - nsXPIDLCString refreshHeader; - rv = httpChannel->GetResponseHeader("refresh", - getter_Copies(refreshHeader)); - - if (refreshHeader) - rv = RefreshURIFromHeader(mCurrentURI, - NS_ConvertUTF8toUCS2(refreshHeader)); - } - } - return rv; -} - NS_IMETHODIMP nsDocShell::OnLoadingSite(nsIChannel * aChannel) { diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 573daff0be92..d94a93e4fd05 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -122,6 +122,7 @@ public: // nsITimerCallback interface NS_IMETHOD_(void) Notify(nsITimer * timer); + PRInt32 GetDelay() { return mDelay ;} nsCOMPtr mDocShell; nsCOMPtr mURI; @@ -129,6 +130,7 @@ public: PRInt32 mDelay; PRBool mMetaRefresh; + protected: virtual ~nsRefreshTimer(); }; @@ -245,6 +247,7 @@ protected: NS_IMETHOD EnsureScriptEnvironment(); NS_IMETHOD EnsureEditorData(); NS_IMETHOD EnsureFind(); + NS_IMETHOD RefreshURIFromQueue(); static inline PRUint32 PRTimeToSeconds(PRTime t_usec) diff --git a/webshell/public/nsIRefreshURI.idl b/webshell/public/nsIRefreshURI.idl index c4e9cc4dda8b..42a85f2cfae0 100644 --- a/webshell/public/nsIRefreshURI.idl +++ b/webshell/public/nsIRefreshURI.idl @@ -26,29 +26,39 @@ interface nsIChannel; [scriptable, uuid(69EFC430-2EFE-11d2-9E5D-006008BF092E)] interface nsIRefreshURI : nsISupports { /** - * Reloads a uri after waiting millis milliseconds. + * Load a uri after waiting for aMillis milliseconds. If the docshell + * is busy loading a page currently, the refresh request will be + * queued and executed when the current load finishes. * - * @param uri The uri to refresh. - * @param millis The number of milliseconds to wait. - * @param repeat Do you want the uri to be repeatedly refreshed every millis milliseconds. - * @param flag to check if this is for a META refresh + * @param aUri The uri to refresh. + * @param aMillis The number of milliseconds to wait. + * @param aRepeat Flag to indicate if the uri is to be + * repeatedly refreshed every aMillis milliseconds. + * @parem aMetaRefresh Flag to indicate if this is a Meta refresh. */ void refreshURI(in nsIURI aURI, in long aMillis, in boolean aRepeat, in boolean aMetaRefresh); /** - * Checks the passed in channel to see if there is a refresh header, if there is, - * will setup a refreahURI by calling refreshURI + * Checks the passed in channel to see if there is a refresh header, + * if there is, will setup a timer to refresh the uri found + * in the header. If docshell is busy loading a page currently, the + * request will be queued and executed when the current page + * finishes loading. + * + * @param aChannel The channel to be parsed. */ void setupRefreshURI(in nsIChannel aChannel); /** - * Parses the passed in header string and will call refreshURI with - * the parsed values + * Parses the passed in header string and sets up a refreshURI if + * a "refresh" header is found. If docshell is busy loading a page + * currently, the request will be queued and executed when + * the current page finishes loading. * - * @param aBaseURI base URI to resolve refresh uri against - * @param aHeader The meta refresh header string + * @param aBaseURI base URI to resolve refresh uri with. + * @param aHeader The meta refresh header string. */ - void refreshURIFromHeader(in nsIURI aBaseURI, in AString aHeader); + void setupRefreshURIFromHeader(in nsIURI aBaseURI, in AString aHeader); /** * Cancels all timer loads.