Bug 696085: WebSocket connection opening across page loads r=smaug

This commit is contained in:
Jason Duell 2012-02-21 14:57:10 -08:00
Родитель cde59a53a2
Коммит 80872a0825
3 изменённых файлов: 80 добавлений и 10 удалений

Просмотреть файл

@ -84,6 +84,7 @@
#include "nsDOMFile.h" #include "nsDOMFile.h"
#include "nsWrapperCacheInlines.h" #include "nsWrapperCacheInlines.h"
#include "nsDOMEventTargetHelper.h" #include "nsDOMEventTargetHelper.h"
#include "nsIObserverService.h"
using namespace mozilla; using namespace mozilla;
@ -92,7 +93,7 @@ using namespace mozilla;
#define TRUE_OR_FAIL_WEBSOCKET(x, ret) \ #define TRUE_OR_FAIL_WEBSOCKET(x, ret) \
PR_BEGIN_MACRO \ PR_BEGIN_MACRO \
if (NS_UNLIKELY(!(x))) { \ if (NS_UNLIKELY(!(x))) { \
NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \ NS_WARNING("TRUE_OR_FAIL_WEBSOCKET(" #x ") failed"); \
FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \ FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \
return ret; \ return ret; \
} \ } \
@ -227,10 +228,9 @@ nsWebSocket::FailConnection(PRUint16 aReasonCode,
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
ConsoleError(); ConsoleError();
nsresult rv = CloseConnection(aReasonCode, aReasonString); CloseConnection(aReasonCode, aReasonString);
NS_ENSURE_SUCCESS(rv, rv);
rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error")); nsresult rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
if (NS_FAILED(rv)) if (NS_FAILED(rv))
NS_WARNING("Failed to dispatch the error event"); NS_WARNING("Failed to dispatch the error event");
@ -250,6 +250,12 @@ nsWebSocket::Disconnect()
if (loadGroup) if (loadGroup)
loadGroup->RemoveRequest(this, nsnull, NS_OK); loadGroup->RemoveRequest(this, nsnull, NS_OK);
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
}
// DontKeepAliveAnyMore() can release the object. So hold a reference to this // DontKeepAliveAnyMore() can release the object. So hold a reference to this
// until the end of the method. // until the end of the method.
nsRefPtr<nsWebSocket> kungfuDeathGrip = this; nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
@ -307,6 +313,13 @@ nsWebSocket::OnStart(nsISupports *aContext)
if (mDisconnected) if (mDisconnected)
return NS_OK; return NS_OK;
// Attempt to kill "ghost" websocket: but usually too early for check to fail
nsresult rv = CheckInnerWindowCorrectness();
if (NS_FAILED(rv)) {
FailConnectionQuietly();
return rv;
}
if (!mRequestedProtocolList.IsEmpty()) { if (!mRequestedProtocolList.IsEmpty()) {
mChannel->GetProtocol(mEstablishedProtocol); mChannel->GetProtocol(mEstablishedProtocol);
} }
@ -405,7 +418,7 @@ nsWebSocket::GetInterface(const nsIID &aIID, void **aResult)
return wwatch->GetPrompt(outerWindow, aIID, aResult); return wwatch->GetPrompt(outerWindow, aIID, aResult);
} }
return NS_ERROR_UNEXPECTED; return QueryInterface(aIID, aResult);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -499,6 +512,8 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsWebSocket)
NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer) NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener) NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIRequest) NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebSocket) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebSocket)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
@ -1085,6 +1100,14 @@ nsWebSocket::DontKeepAliveAnyMore()
mCheckMustKeepAlive = false; mCheckMustKeepAlive = false;
} }
void
nsWebSocket::FailConnectionQuietly()
{
// Fail without console error or JS onerror message: onmessage/onclose will
// also be blocked so long as CheckInnerWindowCorrectness is failing.
CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
}
nsresult nsresult
nsWebSocket::UpdateURI() nsWebSocket::UpdateURI()
{ {
@ -1496,6 +1519,19 @@ nsWebSocket::Init(nsIPrincipal* aPrincipal,
mOwner = nsnull; mOwner = nsnull;
} }
// Attempt to kill "ghost" websocket: but usually too early for check to fail
rv = CheckInnerWindowCorrectness();
NS_ENSURE_SUCCESS(rv, rv);
// Shut down websocket if window is frozen or destroyed (only needed for
// "ghost" websockets--see bug 696085)
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
NS_ENSURE_STATE(os);
rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
NS_ENSURE_SUCCESS(rv, rv);
rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIJSContextStack> stack = nsCOMPtr<nsIJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1"); do_GetService("@mozilla.org/js/xpc/ContextStack;1");
JSContext* cx = nsnull; JSContext* cx = nsnull;
@ -1571,6 +1607,35 @@ nsWebSocket::Init(nsIPrincipal* aPrincipal,
return NS_OK; return NS_OK;
} }
//-----------------------------------------------------------------------------
// nsWebSocket::nsIObserver
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsWebSocket::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
if ((mReadyState == nsIWebSocket::CLOSING) ||
(mReadyState == nsIWebSocket::CLOSED)) {
return NS_OK;
}
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
if (!mOwner || window != mOwner) {
return NS_OK;
}
if ((strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) ||
(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0))
{
FailConnectionQuietly();
}
return NS_OK;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// nsWebSocket::nsIRequest // nsWebSocket::nsIRequest
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

Просмотреть файл

@ -54,7 +54,9 @@
#include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestor.h"
#include "nsIWebSocketChannel.h" #include "nsIWebSocketChannel.h"
#include "nsIWebSocketListener.h" #include "nsIWebSocketListener.h"
#include "nsIObserver.h"
#include "nsIRequest.h" #include "nsIRequest.h"
#include "nsWeakReference.h"
#define DEFAULT_WS_SCHEME_PORT 80 #define DEFAULT_WS_SCHEME_PORT 80
#define DEFAULT_WSS_SCHEME_PORT 443 #define DEFAULT_WSS_SCHEME_PORT 443
@ -74,6 +76,8 @@ class nsWebSocket: public nsDOMEventTargetHelper,
public nsIJSNativeInitializer, public nsIJSNativeInitializer,
public nsIInterfaceRequestor, public nsIInterfaceRequestor,
public nsIWebSocketListener, public nsIWebSocketListener,
public nsIObserver,
public nsSupportsWeakReference,
public nsIRequest public nsIRequest
{ {
friend class nsWSCloseEvent; friend class nsWSCloseEvent;
@ -88,6 +92,7 @@ public:
NS_DECL_NSIWEBSOCKET NS_DECL_NSIWEBSOCKET
NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIWEBSOCKETLISTENER NS_DECL_NSIWEBSOCKETLISTENER
NS_DECL_NSIOBSERVER
NS_DECL_NSIREQUEST NS_DECL_NSIREQUEST
// nsIJSNativeInitializer // nsIJSNativeInitializer
@ -114,6 +119,7 @@ protected:
// These methods when called can release the WebSocket object // These methods when called can release the WebSocket object
nsresult FailConnection(PRUint16 reasonCode, nsresult FailConnection(PRUint16 reasonCode,
const nsACString& aReasonString = EmptyCString()); const nsACString& aReasonString = EmptyCString());
void FailConnectionQuietly();
nsresult CloseConnection(PRUint16 reasonCode, nsresult CloseConnection(PRUint16 reasonCode,
const nsACString& aReasonString = EmptyCString()); const nsACString& aReasonString = EmptyCString());
nsresult Disconnect(); nsresult Disconnect();

Просмотреть файл

@ -2319,17 +2319,16 @@ WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
LOG(("WebSocketChannel::Close() %p\n", this)); LOG(("WebSocketChannel::Close() %p\n", this));
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
if (mRequestedClose) {
return NS_OK;
}
if (!mTransport) { if (!mTransport) {
LOG(("WebSocketChannel::Close() without transport - aborting.")); LOG(("WebSocketChannel::Close() without transport - aborting."));
AbortSession(NS_ERROR_NOT_CONNECTED); AbortSession(NS_ERROR_NOT_CONNECTED);
return NS_ERROR_NOT_CONNECTED; return NS_ERROR_NOT_CONNECTED;
} }
if (mRequestedClose) {
LOG(("WebSocketChannel:: Double close error\n"));
return NS_ERROR_UNEXPECTED;
}
// The API requires the UTF-8 string to be 123 or less bytes // The API requires the UTF-8 string to be 123 or less bytes
if (reason.Length() > 123) if (reason.Length() > 123)
return NS_ERROR_ILLEGAL_VALUE; return NS_ERROR_ILLEGAL_VALUE;