diff --git a/content/html/content/src/nsHTMLFormElement.cpp b/content/html/content/src/nsHTMLFormElement.cpp
index 726c490b3538..25b7af5ba171 100644
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -975,16 +975,11 @@ nsHTMLFormElement::SubmitSubmission(nsIFormSubmission* aFormSubmission)
return NS_OK;
}
- // javascript URIs are not really submissions; they just call a function.
- // Also, they may synchronously call submit(), and we want them to be able to
- // do so while still disallowing other double submissions. (Bug 139798)
- // Note that any other URI types that are of equivalent type should also be
- // added here.
- PRBool schemeIsJavaScript = PR_FALSE;
- if (NS_SUCCEEDED(actionURI->SchemeIs("javascript", &schemeIsJavaScript)) &&
- schemeIsJavaScript) {
- mIsSubmitting = PR_FALSE;
- }
+ // No need to do anything special for javascript: URIs here. If the action
+ // gets changed to something else we'll drop the current submission anyway
+ // (due to the patch for bug 145142), and if it doesn't we don't really want
+ // to start several identical javascript: URIs going. In any case, the point
+ // is that the original fix for bug 139798 is no longer needed.
nsAutoString target;
rv = GetTarget(target);
diff --git a/docshell/base/nsWebShell.cpp b/docshell/base/nsWebShell.cpp
index 56ef47098fee..a824e1705974 100644
--- a/docshell/base/nsWebShell.cpp
+++ b/docshell/base/nsWebShell.cpp
@@ -742,6 +742,14 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent,
nsIDocShell** aDocShell,
nsIRequest** aRequest)
{
+ // Initialize the DocShell / Request
+ if (aDocShell) {
+ *aDocShell = nsnull;
+ }
+ if (aRequest) {
+ *aRequest = nsnull;
+ }
+
{
// defer to an external protocol handler if necessary...
nsCOMPtr extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
@@ -813,14 +821,6 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent,
anchor->GetType(typeHint);
}
- // Initialize the DocShell / Request
- if (aDocShell) {
- *aDocShell = nsnull;
- }
- if (aRequest) {
- *aRequest = nsnull;
- }
-
switch(aVerb) {
case eLinkVerb_New:
NS_ASSERTION(target.IsEmpty(), "Losing window name information");
diff --git a/dom/src/jsurl/nsJSProtocolHandler.cpp b/dom/src/jsurl/nsJSProtocolHandler.cpp
index 7be9dcd9507d..280e5cb75f3e 100644
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -72,6 +72,7 @@
#include "nsIXPConnect.h"
#include "nsContentUtils.h"
#include "nsJSUtils.h"
+#include "nsThreadUtils.h"
class nsJSThunk : public nsIInputStream
@@ -304,8 +305,17 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel)
nsnull,
&result,
&isUndefined);
- }
+ // If there's an error on cx as a result of that call, report
+ // it now -- either we're just running under the event loop,
+ // so we shouldn't propagate JS exceptions out of here, or we
+ // can't be sure that our caller is JS (and if it's not we'll
+ // lose the error), or it might be JS that then proceeds to
+ // cause an error of its own (which will also make us lose
+ // this error).
+ ::JS_ReportPendingException((JSContext*)scriptContext->GetNativeContext());
+ }
+
if (NS_FAILED(rv)) {
rv = NS_ERROR_MALFORMED_URI;
}
@@ -387,29 +397,36 @@ public:
nsresult Init(nsIURI *aURI);
+ // Actually evaluate the script.
+ void EvaluateScript();
+
protected:
virtual ~nsJSChannel();
nsresult StopAll();
- nsresult InternalOpen(PRBool aIsAsync, nsIStreamListener *aListener,
- nsISupports *aContext, nsIInputStream **aResult);
-
+ void NotifyListener();
+
protected:
nsCOMPtr mStreamChannel;
nsCOMPtr mListener; // Our final listener
+ nsCOMPtr mContext; // The context passed to AsyncOpen
+ nsresult mStatus; // Our status
nsLoadFlags mLoadFlags;
+ nsLoadFlags mActualLoadFlags; // See AsyncOpen
nsRefPtr mIOThunk;
PRPackedBool mIsActive;
- PRPackedBool mWasCanceled;
+ PRPackedBool mOpenedStreamChannel;
};
nsJSChannel::nsJSChannel() :
+ mStatus(NS_OK),
mLoadFlags(LOAD_NORMAL),
+ mActualLoadFlags(LOAD_NORMAL),
mIsActive(PR_FALSE),
- mWasCanceled(PR_FALSE)
+ mOpenedStreamChannel(PR_FALSE)
{
}
@@ -494,22 +511,19 @@ nsJSChannel::IsPending(PRBool *aResult)
NS_IMETHODIMP
nsJSChannel::GetStatus(nsresult *aResult)
{
- // We're always ok. Our status is independent of our underlying
- // stream's status.
- *aResult = NS_OK;
+ *aResult = mStatus;
return NS_OK;
}
NS_IMETHODIMP
nsJSChannel::Cancel(nsresult aStatus)
{
- // If we're canceled just record the fact that we were canceled,
- // the underlying stream will be canceled later, if needed. And we
- // don't care about the reason for the canceling, i.e. ignore
- // aStatus.
-
- mWasCanceled = PR_TRUE;
+ mStatus = aStatus;
+ if (mOpenedStreamChannel) {
+ mStreamChannel->Cancel(aStatus);
+ }
+
return NS_OK;
}
@@ -550,115 +564,159 @@ nsJSChannel::GetURI(nsIURI * *aURI)
NS_IMETHODIMP
nsJSChannel::Open(nsIInputStream **aResult)
{
- return InternalOpen(PR_FALSE, nsnull, nsnull, aResult);
+ nsresult rv = mIOThunk->EvaluateScript(mStreamChannel);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mStreamChannel->Open(aResult);
}
NS_IMETHODIMP
nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
{
+ NS_ENSURE_ARG(aListener);
+
mListener = aListener;
- nsresult rv = InternalOpen(PR_TRUE, this, aContext, nsnull);
- if (NS_FAILED(rv)) {
- mListener = nsnull;
- }
- return rv;
-}
+ mContext = aContext;
-nsresult
-nsJSChannel::InternalOpen(PRBool aIsAsync, nsIStreamListener *aListener,
- nsISupports *aContext, nsIInputStream **aResult)
-{
- nsCOMPtr loadGroup;
+ mIsActive = PR_TRUE;
// Temporarily set the LOAD_BACKGROUND flag to suppress load group observer
// notifications (and hence nsIWebProgressListener notifications) from
// being dispatched. This is required since we suppress LOAD_DOCUMENT_URI,
// which means that the DocLoader would not generate document start and
// stop notifications (see bug 257875).
- PRUint32 oldLoadFlags = mLoadFlags;
+ mActualLoadFlags = mLoadFlags;
mLoadFlags |= LOAD_BACKGROUND;
// Add the javascript channel to its loadgroup so that we know if
// network loads were canceled or not...
+ nsCOMPtr loadGroup;
mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
if (loadGroup) {
- loadGroup->AddRequest(this, aContext);
+ loadGroup->AddRequest(this, nsnull);
}
+ // post an event to do the rest
+ nsCOMPtr ev =
+ new nsRunnableMethod(this, &nsJSChannel::EvaluateScript);
+ nsresult rv = NS_DispatchToCurrentThread(ev);
+
+ if (NS_FAILED(rv)) {
+ loadGroup->RemoveRequest(this, nsnull, rv);
+ mIsActive = PR_FALSE;
+ mListener = nsnull;
+ mContext = nsnull;
+ }
+
+ return rv;
+}
+
+void
+nsJSChannel::EvaluateScript()
+{
// Synchronously execute the script...
// mIsActive is used to indicate the the request is 'busy' during the
// the script evaluation phase. This means that IsPending() will
// indicate the the request is busy while the script is executing...
- mIsActive = PR_TRUE;
- nsresult rv = mIOThunk->EvaluateScript(mStreamChannel);
+ // Note that we want to be in the loadgroup and pending while we evaluate
+ // the script, so that we find out if the loadgroup gets canceled by the
+ // script execution (in which case we shouldn't pump out data even if the
+ // script returns it).
+
+ if (NS_SUCCEEDED(mStatus)) {
+ nsresult rv = mIOThunk->EvaluateScript(mStreamChannel);
+
+ // Note that evaluation may have canceled us, so recheck mStatus again
+ if (NS_FAILED(rv) && NS_SUCCEEDED(mStatus)) {
+ mStatus = rv;
+ }
+ }
+
// Remove the javascript channel from its loadgroup...
+ nsCOMPtr loadGroup;
+ mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
if (loadGroup) {
- loadGroup->RemoveRequest(this, aContext, rv);
+ loadGroup->RemoveRequest(this, nsnull, mStatus);
}
// Reset load flags to their original value...
- mLoadFlags = oldLoadFlags;
+ mLoadFlags = mActualLoadFlags;
// We're no longer active, it's now up to the stream channel to do
// the loading, if needed.
mIsActive = PR_FALSE;
- if (NS_SUCCEEDED(rv) && !mWasCanceled) {
- // EvaluateScript() succeeded, and we were not canceled, that
- // means there's data to parse as a result of evaluating the
- // script.
+ if (NS_FAILED(mStatus)) {
+ NotifyListener();
+ return;
+ }
+
+ // EvaluateScript() succeeded, and we were not canceled, that
+ // means there's data to parse as a result of evaluating the
+ // script.
- // Get the stream channels load flags (!= mLoadFlags).
- nsLoadFlags loadFlags;
- mStreamChannel->GetLoadFlags(&loadFlags);
+ // Get the stream channels load flags (!= mLoadFlags).
+ nsLoadFlags loadFlags;
+ mStreamChannel->GetLoadFlags(&loadFlags);
- if (loadFlags & LOAD_DOCUMENT_URI) {
- // We're loaded as the document channel. If we go on,
- // we'll blow away the current document. Make sure that's
- // ok. If so, stop all pending network loads.
+ if (loadFlags & LOAD_DOCUMENT_URI) {
+ // We're loaded as the document channel. If we go on,
+ // we'll blow away the current document. Make sure that's
+ // ok. If so, stop all pending network loads.
- nsCOMPtr docShell;
- NS_QueryNotificationCallbacks(mStreamChannel, docShell);
- if (docShell) {
- nsCOMPtr cv;
- docShell->GetContentViewer(getter_AddRefs(cv));
+ nsCOMPtr docShell;
+ NS_QueryNotificationCallbacks(mStreamChannel, docShell);
+ if (docShell) {
+ nsCOMPtr cv;
+ docShell->GetContentViewer(getter_AddRefs(cv));
- if (cv) {
- PRBool okToUnload;
+ if (cv) {
+ PRBool okToUnload;
- if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) &&
- !okToUnload) {
- // The user didn't want to unload the current
- // page, translate this into an undefined
- // return from the javascript: URL...
- rv = NS_ERROR_DOM_RETVAL_UNDEFINED;
- }
+ if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) &&
+ !okToUnload) {
+ // The user didn't want to unload the current
+ // page, translate this into an undefined
+ // return from the javascript: URL...
+ mStatus = NS_ERROR_DOM_RETVAL_UNDEFINED;
}
}
-
- if (NS_SUCCEEDED(rv)) {
- rv = StopAll();
- }
}
- if (NS_SUCCEEDED(rv)) {
- // This will add mStreamChannel to the load group.
-
- if (aIsAsync) {
- rv = mStreamChannel->AsyncOpen(aListener, aContext);
- } else {
- rv = mStreamChannel->Open(aResult);
- }
+ if (NS_SUCCEEDED(mStatus)) {
+ mStatus = StopAll();
}
}
- if (NS_FAILED(rv)) {
- // Propagate the failure down to the underlying channel...
- mStreamChannel->Cancel(rv);
+ if (NS_FAILED(mStatus)) {
+ NotifyListener();
+ return;
+ }
+
+ mStatus = mStreamChannel->AsyncOpen(this, mContext);
+ if (NS_FAILED(mStatus)) {
+ NotifyListener();
+ } else {
+ // mStreamChannel will call OnStartRequest and OnStopRequest on
+ // us, so we'll be sure to call them on our listener.
+ mOpenedStreamChannel = PR_TRUE;
}
- return rv;
+ return;
+}
+
+void
+nsJSChannel::NotifyListener()
+{
+ // Make sure to drop our ref to mListener
+ nsCOMPtr listener;
+ listener.swap(mListener);
+
+ listener->OnStartRequest(this, mContext);
+ listener->OnStopRequest(this, mContext, mStatus);
+
+ mContext = nsnull;
}
NS_IMETHODIMP
@@ -678,6 +736,9 @@ nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
// 'document channels' in the loadgroup if a javascript: URL is
// loaded while a document is being loaded in the same window.
+ // XXXbz this, and a whole lot of other hackery, could go away if we'd just
+ // cancel the current document load on javascript: load start like IE does.
+
mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI;
// ... but the underlying stream channel should get this bit, if
@@ -797,6 +858,8 @@ nsJSChannel::OnStopRequest(nsIRequest* aRequest,
// Make sure to drop our ref to mListener
nsCOMPtr listener;
listener.swap(mListener);
+
+ mContext = nsnull;
return listener->OnStopRequest(this, aContext, aStatus);
}
diff --git a/uriloader/base/nsURILoader.cpp b/uriloader/base/nsURILoader.cpp
index 7e809f969f60..8bb1ed3d3f47 100644
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -893,9 +893,8 @@ NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel *channel,
rv = channel->AsyncOpen(loader, nsnull);
// no content from this load - that's OK.
- if (rv == NS_ERROR_DOM_RETVAL_UNDEFINED ||
- rv == NS_ERROR_NO_CONTENT) {
- LOG((" rv is NS_ERROR_DOM_RETVAL_UNDEFINED or NS_ERROR_NO_CONTENT -- doing nothing"));
+ if (rv == NS_ERROR_NO_CONTENT) {
+ LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
rv = NS_OK;
}
} else if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {