mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 959114 - Remove websockify dependency from LayerScope. r=vlad, r=cku
This commit is contained in:
parent
5b0fafef7e
commit
60b21194d8
@ -10,6 +10,7 @@
|
||||
#include "Effects.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "TexturePoolOGL.h"
|
||||
#include "mozilla/layers/TextureHostOGL.h"
|
||||
|
||||
@ -29,11 +30,16 @@
|
||||
#include <memory>
|
||||
#include "mozilla/Compression.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/SHA1.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIServerSocket.h"
|
||||
#include "nsReadLine.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
@ -52,13 +58,267 @@ using namespace mozilla::gl;
|
||||
using namespace mozilla;
|
||||
|
||||
class DebugDataSender;
|
||||
class DebugGLData;
|
||||
|
||||
static bool gDebugConnected = false;
|
||||
static nsCOMPtr<nsIServerSocket> gDebugServerSocket;
|
||||
static nsCOMPtr<nsIThread> gDebugSenderThread;
|
||||
static nsCOMPtr<nsISocketTransport> gDebugSenderTransport;
|
||||
static nsCOMPtr<nsIOutputStream> gDebugStream;
|
||||
static nsCOMPtr<DebugDataSender> gCurrentSender;
|
||||
/* This class handle websocket protocol which included
|
||||
* handshake and data frame's header
|
||||
*/
|
||||
class LayerScopeWebSocketHandler : public nsIInputStreamCallback {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
enum SocketStateType {
|
||||
NoHandshake,
|
||||
HandshakeSuccess,
|
||||
HandshakeFailed
|
||||
};
|
||||
|
||||
LayerScopeWebSocketHandler()
|
||||
: mState(NoHandshake)
|
||||
{ }
|
||||
|
||||
virtual ~LayerScopeWebSocketHandler()
|
||||
{
|
||||
if (mTransport) {
|
||||
mTransport->Close(NS_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenStream(nsISocketTransport* aTransport) {
|
||||
MOZ_ASSERT(aTransport);
|
||||
|
||||
mTransport = aTransport;
|
||||
mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING,
|
||||
0,
|
||||
0,
|
||||
getter_AddRefs(mOutputStream));
|
||||
|
||||
nsCOMPtr<nsIInputStream> debugInputStream;
|
||||
mTransport->OpenInputStream(0,
|
||||
0,
|
||||
0,
|
||||
getter_AddRefs(debugInputStream));
|
||||
mInputStream = do_QueryInterface(debugInputStream);
|
||||
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
|
||||
}
|
||||
|
||||
bool WriteToStream(void *ptr, uint32_t size) {
|
||||
if (mState == NoHandshake) {
|
||||
// Not yet handshake, just return true in case of
|
||||
// LayerScope remove this handle
|
||||
return true;
|
||||
} else if (mState == HandshakeFailed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate WebSocket header
|
||||
uint8_t wsHeader[10];
|
||||
int wsHeaderSize = 0;
|
||||
const uint8_t opcode = 0x2;
|
||||
wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode;
|
||||
if (size <= 125) {
|
||||
wsHeaderSize = 2;
|
||||
wsHeader[1] = size;
|
||||
} else if (size < 65536) {
|
||||
wsHeaderSize = 4;
|
||||
wsHeader[1] = 0x7E;
|
||||
NetworkEndian::writeUint16(wsHeader + 2, size);
|
||||
} else {
|
||||
wsHeaderSize = 10;
|
||||
wsHeader[1] = 0x7F;
|
||||
NetworkEndian::writeUint64(wsHeader + 2, size);
|
||||
}
|
||||
|
||||
// Send WebSocket header
|
||||
nsresult rv;
|
||||
uint32_t cnt;
|
||||
rv = mOutputStream->Write(reinterpret_cast<char*>(wsHeader),
|
||||
wsHeaderSize, &cnt);
|
||||
if (NS_FAILED(rv))
|
||||
return false;
|
||||
|
||||
uint32_t written = 0;
|
||||
while (written < size) {
|
||||
uint32_t cnt;
|
||||
rv = mOutputStream->Write(reinterpret_cast<char*>(ptr) + written,
|
||||
size - written, &cnt);
|
||||
if (NS_FAILED(rv))
|
||||
return false;
|
||||
|
||||
written += cnt;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsIInputStreamCallback
|
||||
NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *stream) MOZ_OVERRIDE
|
||||
{
|
||||
nsTArray<nsCString> protocolString;
|
||||
ReadInputStreamData(protocolString);
|
||||
|
||||
if (WebSocketHandshake(protocolString)) {
|
||||
mState = HandshakeSuccess;
|
||||
} else {
|
||||
mState = HandshakeFailed;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
void ReadInputStreamData(nsTArray<nsCString>& aProtocolString)
|
||||
{
|
||||
nsLineBuffer<char> lineBuffer;
|
||||
nsCString line;
|
||||
bool more = true;
|
||||
do {
|
||||
NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more);
|
||||
|
||||
if (line.Length() > 0) {
|
||||
aProtocolString.AppendElement(line);
|
||||
}
|
||||
} while (more && line.Length() > 0);
|
||||
}
|
||||
|
||||
bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString)
|
||||
{
|
||||
nsresult rv;
|
||||
bool isWebSocket = false;
|
||||
nsCString version;
|
||||
nsCString wsKey;
|
||||
nsCString protocol;
|
||||
|
||||
// Validate WebSocket client request.
|
||||
if (aProtocolString.Length() == 0)
|
||||
return false;
|
||||
|
||||
// Check that the HTTP method is GET
|
||||
const char* HTTP_METHOD = "GET ";
|
||||
if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < aProtocolString.Length(); ++i) {
|
||||
const char* line = aProtocolString[i].get();
|
||||
const char* prop_pos = strchr(line, ':');
|
||||
if (prop_pos != nullptr) {
|
||||
nsCString key(line, prop_pos - line);
|
||||
nsCString value(prop_pos + 2);
|
||||
if (key.EqualsIgnoreCase("upgrade") &&
|
||||
value.EqualsIgnoreCase("websocket")) {
|
||||
isWebSocket = true;
|
||||
} else if (key.EqualsIgnoreCase("sec-websocket-version")) {
|
||||
version = value;
|
||||
} else if (key.EqualsIgnoreCase("sec-websocket-key")) {
|
||||
wsKey = value;
|
||||
} else if (key.EqualsIgnoreCase("sec-websocket-protocol")) {
|
||||
protocol = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isWebSocket) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(version.Equals("7") || version.Equals("8") || version.Equals("13"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(protocol.EqualsIgnoreCase("binary"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Client request is valid. Start to generate and send server response.
|
||||
nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||
nsAutoCString res;
|
||||
SHA1Sum sha1;
|
||||
nsCString combined(wsKey + guid);
|
||||
sha1.update(combined.get(), combined.Length());
|
||||
uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long.
|
||||
sha1.finish(digest);
|
||||
nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::HashSize);
|
||||
Base64Encode(newString, res);
|
||||
|
||||
nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
|
||||
response.Append("Upgrade: websocket\r\n");
|
||||
response.Append("Connection: Upgrade\r\n");
|
||||
response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
|
||||
response.Append("Sec-WebSocket-Protocol: binary\r\n\r\n");
|
||||
uint32_t written = 0;
|
||||
uint32_t size = response.Length();
|
||||
while (written < size) {
|
||||
uint32_t cnt;
|
||||
rv = mOutputStream->Write(const_cast<char*>(response.get()) + written,
|
||||
size - written, &cnt);
|
||||
if (NS_FAILED(rv))
|
||||
return false;
|
||||
|
||||
written += cnt;
|
||||
}
|
||||
mOutputStream->Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> mOutputStream;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInputStream;
|
||||
nsCOMPtr<nsISocketTransport> mTransport;
|
||||
SocketStateType mState;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(LayerScopeWebSocketHandler, nsIInputStreamCallback);
|
||||
|
||||
class LayerScopeWebSocketManager {
|
||||
public:
|
||||
LayerScopeWebSocketManager();
|
||||
~LayerScopeWebSocketManager();
|
||||
|
||||
void AddConnection(nsISocketTransport *aTransport)
|
||||
{
|
||||
MOZ_ASSERT(aTransport);
|
||||
nsRefPtr<LayerScopeWebSocketHandler> temp = new LayerScopeWebSocketHandler();
|
||||
temp->OpenStream(aTransport);
|
||||
mHandlers.AppendElement(temp.get());
|
||||
}
|
||||
|
||||
void RemoveConnection(uint32_t aIndex)
|
||||
{
|
||||
MOZ_ASSERT(aIndex < mHandlers.Length());
|
||||
mHandlers.RemoveElementAt(aIndex);
|
||||
}
|
||||
|
||||
void RemoveAllConnections()
|
||||
{
|
||||
mHandlers.Clear();
|
||||
}
|
||||
|
||||
bool WriteAll(void *ptr, uint32_t size)
|
||||
{
|
||||
for (int32_t i = mHandlers.Length() - 1; i >= 0; --i) {
|
||||
if (!mHandlers[i]->WriteToStream(ptr, size)) {
|
||||
// Send failed, remove this handler
|
||||
RemoveConnection(i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsConnected()
|
||||
{
|
||||
return (mHandlers.Length() != 0) ? true : false;
|
||||
}
|
||||
|
||||
void AppendDebugData(DebugGLData *aDebugData);
|
||||
void DispatchDebugData();
|
||||
private:
|
||||
nsTArray<nsRefPtr<LayerScopeWebSocketHandler> > mHandlers;
|
||||
nsCOMPtr<nsIThread> mDebugSenderThread;
|
||||
nsCOMPtr<DebugDataSender> mCurrentSender;
|
||||
nsCOMPtr<nsIServerSocket> mServerSocket;
|
||||
};
|
||||
|
||||
static StaticAutoPtr<LayerScopeWebSocketManager> gLayerScopeWebSocketManager;
|
||||
|
||||
class DebugGLData : public LinkedListElement<DebugGLData> {
|
||||
public:
|
||||
@ -111,19 +371,9 @@ public:
|
||||
}
|
||||
|
||||
static bool WriteToStream(void *ptr, uint32_t size) {
|
||||
uint32_t written = 0;
|
||||
nsresult rv;
|
||||
while (written < size) {
|
||||
uint32_t cnt;
|
||||
rv = gDebugStream->Write(reinterpret_cast<char*>(ptr) + written,
|
||||
size - written, &cnt);
|
||||
if (NS_FAILED(rv))
|
||||
return false;
|
||||
|
||||
written += cnt;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (!gLayerScopeWebSocketManager)
|
||||
return true;
|
||||
return gLayerScopeWebSocketManager->WriteAll(ptr, size);
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -284,23 +534,15 @@ protected:
|
||||
static bool
|
||||
CheckSender()
|
||||
{
|
||||
if (!gDebugConnected)
|
||||
if (!gLayerScopeWebSocketManager)
|
||||
return false;
|
||||
|
||||
// At some point we may want to support sending
|
||||
// data in between frames.
|
||||
#if 1
|
||||
if (!gCurrentSender)
|
||||
if (!gLayerScopeWebSocketManager->IsConnected())
|
||||
return false;
|
||||
#else
|
||||
if (!gCurrentSender)
|
||||
gCurrentSender = new DebugDataSender();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class DebugListener : public nsIServerSocketListener
|
||||
{
|
||||
public:
|
||||
@ -315,10 +557,11 @@ public:
|
||||
NS_IMETHODIMP OnSocketAccepted(nsIServerSocket *aServ,
|
||||
nsISocketTransport *aTransport)
|
||||
{
|
||||
if (!gLayerScopeWebSocketManager)
|
||||
return NS_OK;
|
||||
|
||||
printf_stderr("*** LayerScope: Accepted connection\n");
|
||||
gDebugConnected = true;
|
||||
gDebugSenderTransport = aTransport;
|
||||
aTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(gDebugStream));
|
||||
gLayerScopeWebSocketManager->AddConnection(aTransport);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -368,13 +611,6 @@ public:
|
||||
DebugGLData *d;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// If we got closed while trying to write some stuff earlier, just
|
||||
// throw away everything.
|
||||
if (!gDebugStream) {
|
||||
Cleanup();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
while ((d = mList->popFirst()) != nullptr) {
|
||||
std::auto_ptr<DebugGLData> cleaner(d);
|
||||
if (!d->Write()) {
|
||||
@ -386,10 +622,7 @@ public:
|
||||
Cleanup();
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
gDebugSenderTransport->Close(rv);
|
||||
gDebugConnected = false;
|
||||
gDebugStream = nullptr;
|
||||
gDebugServerSocket = nullptr;
|
||||
LayerScope::DestroyServerSocket();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -408,30 +641,26 @@ LayerScope::CreateServerSocket()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gDebugSenderThread) {
|
||||
NS_NewThread(getter_AddRefs(gDebugSenderThread));
|
||||
}
|
||||
|
||||
if (!gDebugServerSocket) {
|
||||
gDebugServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
|
||||
int port = Preferences::GetInt("gfx.layerscope.port", 23456);
|
||||
gDebugServerSocket->Init(port, false, -1);
|
||||
gDebugServerSocket->AsyncListen(new DebugListener);
|
||||
if (!gLayerScopeWebSocketManager) {
|
||||
gLayerScopeWebSocketManager = new LayerScopeWebSocketManager();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayerScope::DestroyServerSocket()
|
||||
{
|
||||
gDebugConnected = false;
|
||||
gDebugStream = nullptr;
|
||||
gDebugServerSocket = nullptr;
|
||||
if (gLayerScopeWebSocketManager) {
|
||||
gLayerScopeWebSocketManager->RemoveAllConnections();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayerScope::BeginFrame(GLContext* aGLContext, int64_t aFrameStamp)
|
||||
{
|
||||
if (!gDebugConnected)
|
||||
if (!gLayerScopeWebSocketManager)
|
||||
return;
|
||||
|
||||
if (!gLayerScopeWebSocketManager->IsConnected())
|
||||
return;
|
||||
|
||||
#if 0
|
||||
@ -442,8 +671,7 @@ LayerScope::BeginFrame(GLContext* aGLContext, int64_t aFrameStamp)
|
||||
}
|
||||
#endif
|
||||
|
||||
gCurrentSender = new DebugDataSender();
|
||||
gCurrentSender->Append(new DebugGLData(DebugGLData::FrameStart, aGLContext, aFrameStamp));
|
||||
gLayerScopeWebSocketManager->AppendDebugData(new DebugGLData(DebugGLData::FrameStart, aGLContext, aFrameStamp));
|
||||
}
|
||||
|
||||
void
|
||||
@ -452,9 +680,8 @@ LayerScope::EndFrame(GLContext* aGLContext)
|
||||
if (!CheckSender())
|
||||
return;
|
||||
|
||||
gCurrentSender->Append(new DebugGLData(DebugGLData::FrameEnd, aGLContext));
|
||||
gDebugSenderThread->Dispatch(gCurrentSender, NS_DISPATCH_NORMAL);
|
||||
gCurrentSender = nullptr;
|
||||
gLayerScopeWebSocketManager->AppendDebugData(new DebugGLData(DebugGLData::FrameEnd, aGLContext));
|
||||
gLayerScopeWebSocketManager->DispatchDebugData();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -463,7 +690,7 @@ SendColor(void* aLayerRef, const gfxRGBA& aColor, int aWidth, int aHeight)
|
||||
if (!CheckSender())
|
||||
return;
|
||||
|
||||
gCurrentSender->Append(
|
||||
gLayerScopeWebSocketManager->AppendDebugData(
|
||||
new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight));
|
||||
}
|
||||
|
||||
@ -500,7 +727,7 @@ SendTextureSource(GLContext* aGLContext,
|
||||
gfxIntSize(size.width, size.height),
|
||||
shaderProgram, aFlipY);
|
||||
|
||||
gCurrentSender->Append(
|
||||
gLayerScopeWebSocketManager->AppendDebugData(
|
||||
new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
|
||||
textureId, img));
|
||||
}
|
||||
@ -585,5 +812,34 @@ LayerScope::SendEffectChain(GLContext* aGLContext,
|
||||
// TODO:
|
||||
}
|
||||
|
||||
LayerScopeWebSocketManager::LayerScopeWebSocketManager()
|
||||
{
|
||||
NS_NewThread(getter_AddRefs(mDebugSenderThread));
|
||||
|
||||
mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
|
||||
int port = Preferences::GetInt("gfx.layerscope.port", 23456);
|
||||
mServerSocket->Init(port, false, -1);
|
||||
mServerSocket->AsyncListen(new DebugListener);
|
||||
}
|
||||
|
||||
LayerScopeWebSocketManager::~LayerScopeWebSocketManager()
|
||||
{
|
||||
}
|
||||
|
||||
void LayerScopeWebSocketManager::AppendDebugData(DebugGLData *aDebugData)
|
||||
{
|
||||
if (!mCurrentSender) {
|
||||
mCurrentSender = new DebugDataSender();
|
||||
}
|
||||
|
||||
mCurrentSender->Append(aDebugData);
|
||||
}
|
||||
|
||||
void LayerScopeWebSocketManager::DispatchDebugData()
|
||||
{
|
||||
mDebugSenderThread->Dispatch(mCurrentSender, NS_DISPATCH_NORMAL);
|
||||
mCurrentSender = nullptr;
|
||||
}
|
||||
|
||||
} /* layers */
|
||||
} /* mozilla */
|
||||
|
Loading…
Reference in New Issue
Block a user