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) {