michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */ michael@0: #include "LayerScope.h" michael@0: michael@0: #include "Composer2D.h" michael@0: #include "Effects.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Endian.h" michael@0: #include "TexturePoolOGL.h" michael@0: #include "mozilla/layers/TextureHostOGL.h" michael@0: michael@0: #include "gfxColor.h" michael@0: #include "gfxContext.h" michael@0: #include "gfxUtils.h" michael@0: #include "gfxPrefs.h" michael@0: #include "nsIWidget.h" michael@0: michael@0: #include "GLContext.h" michael@0: #include "GLContextProvider.h" michael@0: #include "GLReadTexImageHelper.h" michael@0: michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIConsoleService.h" michael@0: michael@0: #include michael@0: #include "mozilla/LinkedList.h" michael@0: #include "mozilla/Base64.h" michael@0: #include "mozilla/SHA1.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsISocketTransport.h" michael@0: #include "nsIServerSocket.h" michael@0: #include "nsReadLine.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsIOutputStream.h" michael@0: #include "nsIAsyncInputStream.h" michael@0: #include "nsIEventTarget.h" michael@0: #include "nsProxyRelease.h" michael@0: michael@0: // Undo the damage done by mozzconf.h michael@0: #undef compress michael@0: #include "mozilla/Compression.h" michael@0: michael@0: #ifdef __GNUC__ michael@0: #define PACKED_STRUCT __attribute__((packed)) michael@0: #else michael@0: #define PACKED_STRUCT michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: using namespace mozilla::Compression; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::gl; michael@0: using namespace mozilla; michael@0: michael@0: class DebugDataSender; michael@0: class DebugGLData; michael@0: michael@0: /* This class handle websocket protocol which included michael@0: * handshake and data frame's header michael@0: */ michael@0: class LayerScopeWebSocketHandler : public nsIInputStreamCallback { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: enum SocketStateType { michael@0: NoHandshake, michael@0: HandshakeSuccess, michael@0: HandshakeFailed michael@0: }; michael@0: michael@0: LayerScopeWebSocketHandler() michael@0: : mState(NoHandshake) michael@0: { } michael@0: michael@0: virtual ~LayerScopeWebSocketHandler() michael@0: { michael@0: if (mTransport) { michael@0: mTransport->Close(NS_OK); michael@0: } michael@0: } michael@0: michael@0: void OpenStream(nsISocketTransport* aTransport) { michael@0: MOZ_ASSERT(aTransport); michael@0: michael@0: mTransport = aTransport; michael@0: mTransport->OpenOutputStream(nsITransport::OPEN_BLOCKING, michael@0: 0, michael@0: 0, michael@0: getter_AddRefs(mOutputStream)); michael@0: michael@0: nsCOMPtr debugInputStream; michael@0: mTransport->OpenInputStream(0, michael@0: 0, michael@0: 0, michael@0: getter_AddRefs(debugInputStream)); michael@0: mInputStream = do_QueryInterface(debugInputStream); michael@0: mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread()); michael@0: } michael@0: michael@0: bool WriteToStream(void *ptr, uint32_t size) { michael@0: if (mState == NoHandshake) { michael@0: // Not yet handshake, just return true in case of michael@0: // LayerScope remove this handle michael@0: return true; michael@0: } else if (mState == HandshakeFailed) { michael@0: return false; michael@0: } michael@0: michael@0: // Generate WebSocket header michael@0: uint8_t wsHeader[10]; michael@0: int wsHeaderSize = 0; michael@0: const uint8_t opcode = 0x2; michael@0: wsHeader[0] = 0x80 | (opcode & 0x0f); // FIN + opcode; michael@0: if (size <= 125) { michael@0: wsHeaderSize = 2; michael@0: wsHeader[1] = size; michael@0: } else if (size < 65536) { michael@0: wsHeaderSize = 4; michael@0: wsHeader[1] = 0x7E; michael@0: NetworkEndian::writeUint16(wsHeader + 2, size); michael@0: } else { michael@0: wsHeaderSize = 10; michael@0: wsHeader[1] = 0x7F; michael@0: NetworkEndian::writeUint64(wsHeader + 2, size); michael@0: } michael@0: michael@0: // Send WebSocket header michael@0: nsresult rv; michael@0: uint32_t cnt; michael@0: rv = mOutputStream->Write(reinterpret_cast(wsHeader), michael@0: wsHeaderSize, &cnt); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: uint32_t written = 0; michael@0: while (written < size) { michael@0: uint32_t cnt; michael@0: rv = mOutputStream->Write(reinterpret_cast(ptr) + written, michael@0: size - written, &cnt); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: written += cnt; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // nsIInputStreamCallback michael@0: NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *stream) MOZ_OVERRIDE michael@0: { michael@0: nsTArray protocolString; michael@0: ReadInputStreamData(protocolString); michael@0: michael@0: if (WebSocketHandshake(protocolString)) { michael@0: mState = HandshakeSuccess; michael@0: } else { michael@0: mState = HandshakeFailed; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: void ReadInputStreamData(nsTArray& aProtocolString) michael@0: { michael@0: nsLineBuffer lineBuffer; michael@0: nsCString line; michael@0: bool more = true; michael@0: do { michael@0: NS_ReadLine(mInputStream.get(), &lineBuffer, line, &more); michael@0: michael@0: if (line.Length() > 0) { michael@0: aProtocolString.AppendElement(line); michael@0: } michael@0: } while (more && line.Length() > 0); michael@0: } michael@0: michael@0: bool WebSocketHandshake(nsTArray& aProtocolString) michael@0: { michael@0: nsresult rv; michael@0: bool isWebSocket = false; michael@0: nsCString version; michael@0: nsCString wsKey; michael@0: nsCString protocol; michael@0: michael@0: // Validate WebSocket client request. michael@0: if (aProtocolString.Length() == 0) michael@0: return false; michael@0: michael@0: // Check that the HTTP method is GET michael@0: const char* HTTP_METHOD = "GET "; michael@0: if (strncmp(aProtocolString[0].get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) { michael@0: return false; michael@0: } michael@0: michael@0: for (uint32_t i = 1; i < aProtocolString.Length(); ++i) { michael@0: const char* line = aProtocolString[i].get(); michael@0: const char* prop_pos = strchr(line, ':'); michael@0: if (prop_pos != nullptr) { michael@0: nsCString key(line, prop_pos - line); michael@0: nsCString value(prop_pos + 2); michael@0: if (key.EqualsIgnoreCase("upgrade") && michael@0: value.EqualsIgnoreCase("websocket")) { michael@0: isWebSocket = true; michael@0: } else if (key.EqualsIgnoreCase("sec-websocket-version")) { michael@0: version = value; michael@0: } else if (key.EqualsIgnoreCase("sec-websocket-key")) { michael@0: wsKey = value; michael@0: } else if (key.EqualsIgnoreCase("sec-websocket-protocol")) { michael@0: protocol = value; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!isWebSocket) { michael@0: return false; michael@0: } michael@0: michael@0: if (!(version.Equals("7") || version.Equals("8") || version.Equals("13"))) { michael@0: return false; michael@0: } michael@0: michael@0: if (!(protocol.EqualsIgnoreCase("binary"))) { michael@0: return false; michael@0: } michael@0: michael@0: // Client request is valid. Start to generate and send server response. michael@0: nsAutoCString guid("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); michael@0: nsAutoCString res; michael@0: SHA1Sum sha1; michael@0: nsCString combined(wsKey + guid); michael@0: sha1.update(combined.get(), combined.Length()); michael@0: uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long. michael@0: sha1.finish(digest); michael@0: nsCString newString(reinterpret_cast(digest), SHA1Sum::HashSize); michael@0: Base64Encode(newString, res); michael@0: michael@0: nsCString response("HTTP/1.1 101 Switching Protocols\r\n"); michael@0: response.Append("Upgrade: websocket\r\n"); michael@0: response.Append("Connection: Upgrade\r\n"); michael@0: response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n")); michael@0: response.Append("Sec-WebSocket-Protocol: binary\r\n\r\n"); michael@0: uint32_t written = 0; michael@0: uint32_t size = response.Length(); michael@0: while (written < size) { michael@0: uint32_t cnt; michael@0: rv = mOutputStream->Write(const_cast(response.get()) + written, michael@0: size - written, &cnt); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: written += cnt; michael@0: } michael@0: mOutputStream->Flush(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr mOutputStream; michael@0: nsCOMPtr mInputStream; michael@0: nsCOMPtr mTransport; michael@0: SocketStateType mState; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(LayerScopeWebSocketHandler, nsIInputStreamCallback); michael@0: michael@0: class LayerScopeWebSocketManager { michael@0: public: michael@0: LayerScopeWebSocketManager(); michael@0: ~LayerScopeWebSocketManager(); michael@0: michael@0: void AddConnection(nsISocketTransport *aTransport) michael@0: { michael@0: MOZ_ASSERT(aTransport); michael@0: nsRefPtr temp = new LayerScopeWebSocketHandler(); michael@0: temp->OpenStream(aTransport); michael@0: mHandlers.AppendElement(temp.get()); michael@0: } michael@0: michael@0: void RemoveConnection(uint32_t aIndex) michael@0: { michael@0: MOZ_ASSERT(aIndex < mHandlers.Length()); michael@0: mHandlers.RemoveElementAt(aIndex); michael@0: } michael@0: michael@0: void RemoveAllConnections() michael@0: { michael@0: mHandlers.Clear(); michael@0: } michael@0: michael@0: bool WriteAll(void *ptr, uint32_t size) michael@0: { michael@0: for (int32_t i = mHandlers.Length() - 1; i >= 0; --i) { michael@0: if (!mHandlers[i]->WriteToStream(ptr, size)) { michael@0: // Send failed, remove this handler michael@0: RemoveConnection(i); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool IsConnected() michael@0: { michael@0: return (mHandlers.Length() != 0) ? true : false; michael@0: } michael@0: michael@0: void AppendDebugData(DebugGLData *aDebugData); michael@0: void DispatchDebugData(); michael@0: private: michael@0: nsTArray > mHandlers; michael@0: nsCOMPtr mDebugSenderThread; michael@0: nsRefPtr mCurrentSender; michael@0: nsCOMPtr mServerSocket; michael@0: }; michael@0: michael@0: static StaticAutoPtr gLayerScopeWebSocketManager; michael@0: michael@0: class DebugGLData : public LinkedListElement { michael@0: public: michael@0: typedef enum { michael@0: FrameStart, michael@0: FrameEnd, michael@0: TextureData, michael@0: ColorData michael@0: } DataType; michael@0: michael@0: virtual ~DebugGLData() { } michael@0: michael@0: DataType GetDataType() const { return mDataType; } michael@0: intptr_t GetContextAddress() const { return mContextAddress; } michael@0: int64_t GetValue() const { return mValue; } michael@0: michael@0: DebugGLData(DataType dataType) michael@0: : mDataType(dataType), michael@0: mContextAddress(0), michael@0: mValue(0) michael@0: { } michael@0: michael@0: DebugGLData(DataType dataType, GLContext* cx) michael@0: : mDataType(dataType), michael@0: mContextAddress(reinterpret_cast(cx)), michael@0: mValue(0) michael@0: { } michael@0: michael@0: DebugGLData(DataType dataType, GLContext* cx, int64_t value) michael@0: : mDataType(dataType), michael@0: mContextAddress(reinterpret_cast(cx)), michael@0: mValue(value) michael@0: { } michael@0: michael@0: virtual bool Write() { michael@0: if (mDataType != FrameStart && michael@0: mDataType != FrameEnd) michael@0: { michael@0: NS_WARNING("Unimplemented data type!"); michael@0: return false; michael@0: } michael@0: michael@0: DebugGLData::BasicPacket packet; michael@0: michael@0: packet.type = mDataType; michael@0: packet.ptr = static_cast(mContextAddress); michael@0: packet.value = mValue; michael@0: michael@0: return WriteToStream(&packet, sizeof(packet)); michael@0: } michael@0: michael@0: static bool WriteToStream(void *ptr, uint32_t size) { michael@0: if (!gLayerScopeWebSocketManager) michael@0: return true; michael@0: return gLayerScopeWebSocketManager->WriteAll(ptr, size); michael@0: } michael@0: michael@0: protected: michael@0: DataType mDataType; michael@0: intptr_t mContextAddress; michael@0: int64_t mValue; michael@0: michael@0: public: michael@0: // the data packet formats; all packed michael@0: #ifdef _MSC_VER michael@0: #pragma pack(push, 1) michael@0: #endif michael@0: typedef struct { michael@0: uint32_t type; michael@0: uint64_t ptr; michael@0: uint64_t value; michael@0: } PACKED_STRUCT BasicPacket; michael@0: michael@0: typedef struct { michael@0: uint32_t type; michael@0: uint64_t ptr; michael@0: uint64_t layerref; michael@0: uint32_t color; michael@0: uint32_t width; michael@0: uint32_t height; michael@0: } PACKED_STRUCT ColorPacket; michael@0: michael@0: typedef struct { michael@0: uint32_t type; michael@0: uint64_t ptr; michael@0: uint64_t layerref; michael@0: uint32_t name; michael@0: uint32_t width; michael@0: uint32_t height; michael@0: uint32_t stride; michael@0: uint32_t format; michael@0: uint32_t target; michael@0: uint32_t dataFormat; michael@0: uint32_t dataSize; michael@0: } PACKED_STRUCT TexturePacket; michael@0: #ifdef _MSC_VER michael@0: #pragma pack(pop) michael@0: #endif michael@0: }; michael@0: michael@0: class DebugGLTextureData : public DebugGLData { michael@0: public: michael@0: DebugGLTextureData(GLContext* cx, void* layerRef, GLuint target, GLenum name, DataSourceSurface* img) michael@0: : DebugGLData(DebugGLData::TextureData, cx), michael@0: mLayerRef(layerRef), michael@0: mTarget(target), michael@0: mName(name), michael@0: mImage(img) michael@0: { } michael@0: michael@0: void *GetLayerRef() const { return mLayerRef; } michael@0: GLuint GetName() const { return mName; } michael@0: DataSourceSurface* GetImage() const { return mImage; } michael@0: GLenum GetTextureTarget() const { return mTarget; } michael@0: michael@0: virtual bool Write() { michael@0: DebugGLData::TexturePacket packet; michael@0: char* dataptr = nullptr; michael@0: uint32_t datasize = 0; michael@0: std::auto_ptr compresseddata; michael@0: michael@0: packet.type = mDataType; michael@0: packet.ptr = static_cast(mContextAddress); michael@0: packet.layerref = reinterpret_cast(mLayerRef); michael@0: packet.name = mName; michael@0: packet.format = 0; michael@0: packet.target = mTarget; michael@0: packet.dataFormat = LOCAL_GL_RGBA; michael@0: michael@0: if (mImage) { michael@0: packet.width = mImage->GetSize().width; michael@0: packet.height = mImage->GetSize().height; michael@0: packet.stride = mImage->Stride(); michael@0: packet.dataSize = mImage->GetSize().height * mImage->Stride(); michael@0: michael@0: dataptr = (char*) mImage->GetData(); michael@0: datasize = packet.dataSize; michael@0: michael@0: compresseddata = std::auto_ptr((char*) moz_malloc(LZ4::maxCompressedSize(datasize))); michael@0: if (compresseddata.get()) { michael@0: int ndatasize = LZ4::compress(dataptr, datasize, compresseddata.get()); michael@0: if (ndatasize > 0) { michael@0: datasize = ndatasize; michael@0: dataptr = compresseddata.get(); michael@0: michael@0: packet.dataFormat = (1 << 16) | packet.dataFormat; michael@0: packet.dataSize = datasize; michael@0: } michael@0: } michael@0: } else { michael@0: packet.width = 0; michael@0: packet.height = 0; michael@0: packet.stride = 0; michael@0: packet.dataSize = 0; michael@0: } michael@0: michael@0: // write the packet header data michael@0: if (!WriteToStream(&packet, sizeof(packet))) michael@0: return false; michael@0: michael@0: // then the image data michael@0: if (!WriteToStream(dataptr, datasize)) michael@0: return false; michael@0: michael@0: // then pad out to 4 bytes michael@0: if (datasize % 4 != 0) { michael@0: static char buf[] = { 0, 0, 0, 0 }; michael@0: if (!WriteToStream(buf, 4 - (datasize % 4))) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: protected: michael@0: void* mLayerRef; michael@0: GLenum mTarget; michael@0: GLuint mName; michael@0: RefPtr mImage; michael@0: }; michael@0: michael@0: class DebugGLColorData : public DebugGLData { michael@0: public: michael@0: DebugGLColorData(void* layerRef, const gfxRGBA& color, int width, int height) michael@0: : DebugGLData(DebugGLData::ColorData), michael@0: mColor(color.Packed()), michael@0: mSize(width, height) michael@0: { } michael@0: michael@0: void *GetLayerRef() const { return mLayerRef; } michael@0: uint32_t GetColor() const { return mColor; } michael@0: const nsIntSize& GetSize() const { return mSize; } michael@0: michael@0: virtual bool Write() { michael@0: DebugGLData::ColorPacket packet; michael@0: michael@0: packet.type = mDataType; michael@0: packet.ptr = static_cast(mContextAddress); michael@0: packet.layerref = reinterpret_cast(mLayerRef); michael@0: packet.color = mColor; michael@0: packet.width = mSize.width; michael@0: packet.height = mSize.height; michael@0: michael@0: return WriteToStream(&packet, sizeof(packet)); michael@0: } michael@0: michael@0: protected: michael@0: void *mLayerRef; michael@0: uint32_t mColor; michael@0: nsIntSize mSize; michael@0: }; michael@0: michael@0: static bool michael@0: CheckSender() michael@0: { michael@0: if (!gLayerScopeWebSocketManager) michael@0: return false; michael@0: michael@0: if (!gLayerScopeWebSocketManager->IsConnected()) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: class DebugListener : public nsIServerSocketListener michael@0: { michael@0: public: michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: DebugListener() { } michael@0: virtual ~DebugListener() { } michael@0: michael@0: /* nsIServerSocketListener */ michael@0: michael@0: NS_IMETHODIMP OnSocketAccepted(nsIServerSocket *aServ, michael@0: nsISocketTransport *aTransport) michael@0: { michael@0: if (!gLayerScopeWebSocketManager) michael@0: return NS_OK; michael@0: michael@0: printf_stderr("*** LayerScope: Accepted connection\n"); michael@0: gLayerScopeWebSocketManager->AddConnection(aTransport); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP OnStopListening(nsIServerSocket *aServ, michael@0: nsresult aStatus) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(DebugListener, nsIServerSocketListener); michael@0: michael@0: michael@0: class DebugDataSender : public nsIRunnable michael@0: { michael@0: public: michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: DebugDataSender() { michael@0: mList = new LinkedList(); michael@0: } michael@0: michael@0: virtual ~DebugDataSender() { michael@0: Cleanup(); michael@0: } michael@0: michael@0: void Append(DebugGLData *d) { michael@0: mList->insertBack(d); michael@0: } michael@0: michael@0: void Cleanup() { michael@0: if (!mList) michael@0: return; michael@0: michael@0: DebugGLData *d; michael@0: while ((d = mList->popFirst()) != nullptr) michael@0: delete d; michael@0: delete mList; michael@0: michael@0: mList = nullptr; michael@0: } michael@0: michael@0: /* nsIRunnable impl; send the data */ michael@0: michael@0: NS_IMETHODIMP Run() { michael@0: DebugGLData *d; michael@0: nsresult rv = NS_OK; michael@0: michael@0: while ((d = mList->popFirst()) != nullptr) { michael@0: std::auto_ptr cleaner(d); michael@0: if (!d->Write()) { michael@0: rv = NS_ERROR_FAILURE; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: Cleanup(); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: LayerScope::DestroyServerSocket(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: LinkedList *mList; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(DebugDataSender, nsIRunnable); michael@0: michael@0: void michael@0: LayerScope::CreateServerSocket() michael@0: { michael@0: if (!gfxPrefs::LayerScopeEnabled()) { michael@0: return; michael@0: } michael@0: michael@0: if (!gLayerScopeWebSocketManager) { michael@0: gLayerScopeWebSocketManager = new LayerScopeWebSocketManager(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerScope::DestroyServerSocket() michael@0: { michael@0: if (gLayerScopeWebSocketManager) { michael@0: gLayerScopeWebSocketManager->RemoveAllConnections(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: LayerScope::BeginFrame(GLContext* aGLContext, int64_t aFrameStamp) michael@0: { michael@0: if (!gLayerScopeWebSocketManager) michael@0: return; michael@0: michael@0: if (!gLayerScopeWebSocketManager->IsConnected()) michael@0: return; michael@0: michael@0: #if 0 michael@0: // if we're sending data in between frames, flush the list down the socket, michael@0: // and start a new one michael@0: if (gCurrentSender) { michael@0: gDebugSenderThread->Dispatch(gCurrentSender, NS_DISPATCH_NORMAL); michael@0: } michael@0: #endif michael@0: michael@0: gLayerScopeWebSocketManager->AppendDebugData(new DebugGLData(DebugGLData::FrameStart, aGLContext, aFrameStamp)); michael@0: } michael@0: michael@0: void michael@0: LayerScope::EndFrame(GLContext* aGLContext) michael@0: { michael@0: if (!CheckSender()) michael@0: return; michael@0: michael@0: gLayerScopeWebSocketManager->AppendDebugData(new DebugGLData(DebugGLData::FrameEnd, aGLContext)); michael@0: gLayerScopeWebSocketManager->DispatchDebugData(); michael@0: } michael@0: michael@0: static void michael@0: SendColor(void* aLayerRef, const gfxRGBA& aColor, int aWidth, int aHeight) michael@0: { michael@0: if (!CheckSender()) michael@0: return; michael@0: michael@0: gLayerScopeWebSocketManager->AppendDebugData( michael@0: new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight)); michael@0: } michael@0: michael@0: static void michael@0: SendTextureSource(GLContext* aGLContext, michael@0: void* aLayerRef, michael@0: TextureSourceOGL* aSource, michael@0: bool aFlipY) michael@0: { michael@0: GLenum textureTarget = aSource->GetTextureTarget(); michael@0: ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(textureTarget, michael@0: aSource->GetFormat()); michael@0: int shaderConfig = config.mFeatures; michael@0: michael@0: aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::Filter::LINEAR); michael@0: michael@0: GLuint textureId = 0; michael@0: // This is horrid hack. It assumes that aGLContext matches the context michael@0: // aSource has bound to. michael@0: if (textureTarget == LOCAL_GL_TEXTURE_2D) { michael@0: aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &textureId); michael@0: } else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { michael@0: aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &textureId); michael@0: } else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) { michael@0: aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &textureId); michael@0: } michael@0: michael@0: gfx::IntSize size = aSource->GetSize(); michael@0: michael@0: // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding michael@0: // texture correctly. textureId is used for tracking in DebugGLTextureData. michael@0: RefPtr img = michael@0: aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget, michael@0: size, michael@0: shaderConfig, aFlipY); michael@0: michael@0: gLayerScopeWebSocketManager->AppendDebugData( michael@0: new DebugGLTextureData(aGLContext, aLayerRef, textureTarget, michael@0: textureId, img)); michael@0: } michael@0: michael@0: static void michael@0: SendTexturedEffect(GLContext* aGLContext, michael@0: void* aLayerRef, michael@0: const TexturedEffect* aEffect) michael@0: { michael@0: TextureSourceOGL* source = aEffect->mTexture->AsSourceOGL(); michael@0: if (!source) michael@0: return; michael@0: michael@0: bool flipY = false; michael@0: SendTextureSource(aGLContext, aLayerRef, source, flipY); michael@0: } michael@0: michael@0: static void michael@0: SendYCbCrEffect(GLContext* aGLContext, michael@0: void* aLayerRef, michael@0: const EffectYCbCr* aEffect) michael@0: { michael@0: TextureSource* sourceYCbCr = aEffect->mTexture; michael@0: if (!sourceYCbCr) michael@0: return; michael@0: michael@0: const int Y = 0, Cb = 1, Cr = 2; michael@0: TextureSourceOGL* sourceY = sourceYCbCr->GetSubSource(Y)->AsSourceOGL(); michael@0: TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL(); michael@0: TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL(); michael@0: michael@0: bool flipY = false; michael@0: SendTextureSource(aGLContext, aLayerRef, sourceY, flipY); michael@0: SendTextureSource(aGLContext, aLayerRef, sourceCb, flipY); michael@0: SendTextureSource(aGLContext, aLayerRef, sourceCr, flipY); michael@0: } michael@0: michael@0: void michael@0: LayerScope::SendEffectChain(GLContext* aGLContext, michael@0: const EffectChain& aEffectChain, michael@0: int aWidth, int aHeight) michael@0: { michael@0: if (!CheckSender()) michael@0: return; michael@0: michael@0: const Effect* primaryEffect = aEffectChain.mPrimaryEffect; michael@0: switch (primaryEffect->mType) { michael@0: case EFFECT_RGB: michael@0: { michael@0: const TexturedEffect* texturedEffect = michael@0: static_cast(primaryEffect); michael@0: SendTexturedEffect(aGLContext, aEffectChain.mLayerRef, texturedEffect); michael@0: } michael@0: break; michael@0: case EFFECT_YCBCR: michael@0: { michael@0: const EffectYCbCr* yCbCrEffect = michael@0: static_cast(primaryEffect); michael@0: SendYCbCrEffect(aGLContext, aEffectChain.mLayerRef, yCbCrEffect); michael@0: } michael@0: case EFFECT_SOLID_COLOR: michael@0: { michael@0: const EffectSolidColor* solidColorEffect = michael@0: static_cast(primaryEffect); michael@0: gfxRGBA color(solidColorEffect->mColor.r, michael@0: solidColorEffect->mColor.g, michael@0: solidColorEffect->mColor.b, michael@0: solidColorEffect->mColor.a); michael@0: SendColor(aEffectChain.mLayerRef, color, aWidth, aHeight); michael@0: } michael@0: break; michael@0: case EFFECT_COMPONENT_ALPHA: michael@0: case EFFECT_RENDER_TARGET: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: //const Effect* secondaryEffect = aEffectChain.mSecondaryEffects[EFFECT_MASK]; michael@0: // TODO: michael@0: } michael@0: michael@0: LayerScopeWebSocketManager::LayerScopeWebSocketManager() michael@0: { michael@0: NS_NewThread(getter_AddRefs(mDebugSenderThread)); michael@0: michael@0: mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID); michael@0: int port = gfxPrefs::LayerScopePort(); michael@0: mServerSocket->Init(port, false, -1); michael@0: mServerSocket->AsyncListen(new DebugListener); michael@0: } michael@0: michael@0: LayerScopeWebSocketManager::~LayerScopeWebSocketManager() michael@0: { michael@0: } michael@0: michael@0: void LayerScopeWebSocketManager::AppendDebugData(DebugGLData *aDebugData) michael@0: { michael@0: if (!mCurrentSender) { michael@0: mCurrentSender = new DebugDataSender(); michael@0: } michael@0: michael@0: mCurrentSender->Append(aDebugData); michael@0: } michael@0: michael@0: void LayerScopeWebSocketManager::DispatchDebugData() michael@0: { michael@0: mDebugSenderThread->Dispatch(mCurrentSender, NS_DISPATCH_NORMAL); michael@0: mCurrentSender = nullptr; michael@0: } michael@0: michael@0: } /* layers */ michael@0: } /* mozilla */