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 "nsWrapperCacheInlines.h"
#include "nsDOMEventTargetHelper.h"
#include "nsIObserverService.h"
using namespace mozilla;
@ -92,7 +93,7 @@ using namespace mozilla;
#define TRUE_OR_FAIL_WEBSOCKET(x, ret) \
PR_BEGIN_MACRO \
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); \
return ret; \
} \
@ -227,10 +228,9 @@ nsWebSocket::FailConnection(PRUint16 aReasonCode,
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
ConsoleError();
nsresult rv = CloseConnection(aReasonCode, aReasonString);
NS_ENSURE_SUCCESS(rv, rv);
CloseConnection(aReasonCode, aReasonString);
rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
nsresult rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
if (NS_FAILED(rv))
NS_WARNING("Failed to dispatch the error event");
@ -250,6 +250,12 @@ nsWebSocket::Disconnect()
if (loadGroup)
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
// until the end of the method.
nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
@ -307,6 +313,13 @@ nsWebSocket::OnStart(nsISupports *aContext)
if (mDisconnected)
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()) {
mChannel->GetProtocol(mEstablishedProtocol);
}
@ -405,7 +418,7 @@ nsWebSocket::GetInterface(const nsIID &aIID, void **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(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIRequest)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebSocket)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
@ -1085,6 +1100,14 @@ nsWebSocket::DontKeepAliveAnyMore()
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
nsWebSocket::UpdateURI()
{
@ -1496,6 +1519,19 @@ nsWebSocket::Init(nsIPrincipal* aPrincipal,
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 =
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
JSContext* cx = nsnull;
@ -1571,6 +1607,35 @@ nsWebSocket::Init(nsIPrincipal* aPrincipal,
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
//-----------------------------------------------------------------------------

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

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

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

@ -2319,17 +2319,16 @@ WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
LOG(("WebSocketChannel::Close() %p\n", this));
NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
if (mRequestedClose) {
return NS_OK;
}
if (!mTransport) {
LOG(("WebSocketChannel::Close() without transport - aborting."));
AbortSession(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
if (reason.Length() > 123)
return NS_ERROR_ILLEGAL_VALUE;