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.