mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
8a0a002cf2
- Bug 1148708: Add missing 'override' annotations in DocAccessibleChild.h. rs=ehsan (d606358545)
- Bug 1210408 - make nsMaiInterfaceAction work with proxies, r=tbsaunde (f7c819c6ae)
- Bug 1210407 - teach nsMaiInterfaceTable to use proxies, r=tbsaunde (4ca4f10b5f)
- bug 1185157 make sure we don't send an event to a destroyed ipc document r=billm (23acf53f75)
- bug 1214864 - make SetCarretOffset() async r=davidb (e3079e9b2d)
- missing of Bug 1139972 - IPC Proxy for charAt, r=tbsaunde (e9593ed752)
- bug 1191598 - Pass MOZ_CURRENT_PROJECT in environment when running post-build automation steps for universal mac builds. r=gps (fc342c6ced)
- Bug 1164596 - Add mach android-emulator command; r=ahal (afeb9b27d1)
- Bug 1223149 - Add basic usage documentation for mach build; r=glandium (bfb802d175)
- Bug 1182301 - Improve 'mach build' notifications. r=gps (2c65a122d1)
- Bug 1184696 - Add clobber targets to |mach clobber|; Ability to clobber compiled python files, r=gps (35d8be292e)
- Bug 1117958 - Allow any debugging options to the run or gtest mach subcommands to automatically enable debugging. r=gps (32f986af4b)
- Bug 1180081 - Properly rebuild gtest/libxul before running gtests. r=gps (80db9a3d49)
- Bug 1171647, part 1 - Define a new function to convert the mode to a string. r=njn (61ad16f5ba)
- Bug 1171647, part 2 - Remove redundant assertion for dark matter mode. r=njn (b5ac9519f3)
- Bug 1058178, part 1 - Implement DMD heap scanning mode. r=njn (60e1079536)
- Bug 1058178, part 2 - Implement address clamping analysis for DMD scan logs. r=njn (45c0326b93)
- Bug 1102388 - Fix DMD static constructor ordering dependency. r=mccr8 (59b87897a1)
- Bug 1128705 - Don't redefine PAGE_SIZE in DMD if it's already defined. r=erahm (49216348ee)
- Bug 1179042 - Add a script for analyzing memory blocks using a heap scan DMD log. r=njn DONTBUILD (1c08d2d66e)
- Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat (1c999d139b)
- Bug 1158772 - fix non-idiomatic memset call in nsDeque.cpp; r=erahm (de6b555245)
- Bug 1199400 - Part 1: Use CheckedInt when growing nsDeque capacity. r=froydnj (dfdf6814a3)
- Bug 1199400 - Part 0: Remove unused nsDequeIterator. r=froydnj (38d69d7f47)
- Bug 1199400 - Part 2: Add tests for possible nsDeque corner cases. r=froydnj (931baff195)
- Bug 1201997 - Part 1 - Converted compiled test to gtest for nsDeque class. Added tests to test untested methods. r=froydn (e893916651)
- Bug 1201997 - Part 2 - Removing unused methods from the nsDeque class. r=froydn (41595a90ac)
- Bug 1201997 - Part 3 - Make internally used methods private. r=froydn (8cd3afd96f)
- Bug 1201997 - Part 4 - Change size and offset variables to size_t.r=froydn (73eabc8d60)
- Bug 1215140 P1 Add an nsIConsoleReportCollector interface to support navigation channel logging. r=bz (8a41535e2b)
- Bug 1215140 P2 Make HttpBaseChannel implement nsIConsoleReportCollector. r=bz (75fca301f2)
- Bug 1197679 - If nsUnknownDecoder is involved in e10s DivertToParent can break. r=jduell (5d94a12504)
- Bug 1178991 - smartptr for http converter r=hurley (8e7fbc8443)
- bug 366559 - patch 2, fix nsHTTPCompressConv indentation r=bagder (ba762da587)
- bug 366559 - patch 3, fix nsHTTPCompressConv bracing style r=bagder (54195ab451)
- bits of bug 366559 - patch 7, content-encoding brotli for http (f0b4051022)
- Bug 1205112 - Make PushEvent.data nullable. r=mt,smaug (775db32856)
- Bug 1193414 - SharedWorkers thread should be kept alive also when the SharedWorker object is CCed, r=khuey (b77ea8125c)
- Bug 1206520: Add about:config prefs to enable throwing on asm.js validation failures; r=bz (c42126665d)
- Bug 1193414 - Telemetry for SharedWorker spawning. r=bkelly (77984b7bcc)
- Bug 1205676 - Enable WPT service-worker/unregister-then-register-new-script.https.html in e10s, r=nsm (ec24939cf6)
- Bug 1193133 - Throw when calling postMessage from a Service Worker dom object with no global. r=bkelly (526dcacfab)
- Bug 1181871 P1 Only enforce Cache Context shared data destruction on target thread after init. r=ehsan (cdbf3ed3a8)
- Bug 1181871 P2 Fix ServiceWorkerManager usage of stack-based ErrorResult. r=ehsan (c449195d90)
- minor cleanup and missing bit of 1198230 (02f459db05)
- Bug 1143717 - Implement the ServiceWorkerMessageEvent interface. r=baku (027b3465f2)
- fix misspatch (708eee4e84)
- Bug 1188545 - Disentangle service workers from shared workers and refactor event dispatching code into a separate class. r=nsm,mrbkap (fb5b5341c9)
- Bug 1205228 - Change PackagedAppVerifier to notify the verification result asynchronously. r=valentin. (9edda0fa00)
- Bug 1178518 - Packaged App Utils. r=valentin (f60f3b7a93)
- Bug 1213150 - Part 1: Add a nsContentUtils::IsNonSubresourceRequest helper; r=jdm (b509cc3cc9)
- Bug 1213150 - Part 2: Rework ShouldPrepareForIntercept() in terms of subresource requests; r=jdm (2e92fe8780)
- Bug 1213150 - Part 3: Remove nsIInterceptedChannel.isNavigation; r=jdm (becf1cc12f)
- Bug 1213150 follow-up: fix build bustage (8d73d6ca73)
- Bug 1198394 - Part 1: Allow interception of HSTS upgraded connections in non-e10s mode; r=mcmanus (f504c5be08)
- Bug 1198394 - Part 2: Add a test for interception of HSTS upgraded connections; r=jdm (054e984eef)
- Bug 1187011 - Don't allow response body with null body status. r=bkelly (b1860741d1)
- missing bit of 1140788 (29d319712e)
- Bug 1213436 - Reject core dumps with node IDs that don't fit in an IEEE 754 double; r=sfink (3c1f6fdda0)
- Bug 1211006 - Add Debugger.Source.prototype.canonicalId; r=ejpbruel (eef7b79fce)
- Bug 1199218 - Implement JS::ubi::Node::size for js::LazyScript referents; r=sfink (098a48d240)
- Bug 1220031 - Add JS::ubi::Node::scriptFilename; r=sfink (6b824ae680)
- Bug 1143575. Remove unused MediaQueue::Empty. r=cpearce (de737f3433)
- Bug 1209933 - Make sure all parent runtime pointers are the topmost parent, r=billm. (fe824d967d)
- Bug 1197012 - Fix ThrowTypeError in Notification. r=mccr8 (0b1a097526)
- Bug 1197893 - Check the number of arguments for ThrowTypeError() and ThrowRangeError() at compile time. r=peterv (d98c7d78a0)
- Bug 1142083 - Add test for IDN Unicode domain redirect. r=mcmanus (0c8961fe17)
- Bug 1187159 - Add mochitest for loading packaged apps (iframe+fetch+mozapp) r=jduell (ce90ea561b)
- Bug 1186290 - Notify TabParent to switch process when loading a signed package. r=honzab, r=kanru. (c58a14554a)
- fix (15e2df75eb)
- Bug 1206124 P1 Fix "same-origin" CORS credentials in FetchDriver. r=ehsan (fae1bb6ab3)
- Bug 1206124 P2 Test fetch() with credentials and redirects. r=ehsan (ffc6254112)
- Bug 1211751: Remove nsIChannelEventSink-forwarding from EventSource and FetchDriver. It's never needed. r=smaug (adafe5737a)
- Bug 1212433 Fail fetch() calls that require preflight and also redirect. r=sicking a=abillings (c0d6742b9e)
- Bug 1193128 - Fix base64 decoding when fetching data URIs. r=baku (80bafa291a)
- Bug 1195167 part 1: Let necko handle all protocols. r=bkelly (bb932b0ada)
- Bug 1195167 part 2: Remove redundant aCORSFlag argument and instead use mCORSFlagEverSet. r=bkelly (beadafcad0)
- Bug 1195167 part 3: Remove more scheme-specific handling from FetchDriver. r=bkelly (d00b38db9e)
- Bug 1195167 part 4: Remove FetchDriver::BasicFetch since it is empty. r=bkelly (c5ed097267)
- Bug 1210413 P2 Test CORS credentials on cross-origin redirects. r=sicking a=dveditz (b4eeb8aac0)
- Bug 1210413 P1 Propagate new channel load flags from child to parent on redirect. r=jduell a=dveditz (8b329af4fa)
- Bug 1195167 part 5: Make FetchDriver use AsyncOpen2. r=bkelly (cc217c4cc1)
- Bug 1195167 part 6: Some code simplification since necko handles fetch recursion. r=bkelly (f3b6da2262)
- Bug 1195167: Followup to fix test which I forgot to change (81e7439a2e)
- Bug 1215746: Remove RequestMode::Cors_with_forced_preflight. r=bkelly (0336e812b6)
- Bug 1211000: Move CORS preflight logic from nsCORSListenerProxy to nsCORSPreflightListener. r=ehsan (bf2f71cf22)
- missing bit of Bug 1211443 - Drop scheduled update if decoder initialization isn't done yet. r=jya (f6bc074e33)
- Bug 1182571: Fix nsILoadInfo->GetContentPolicyType API to be less ambigious. Audit and fix all users of it. r=ckerschb (5af6fa7442)
- fix (e40c8e7625)
- Bug 1173811 - Part 1: Propagate the response URL to intercepted channels when necessary (non-e10s). r=mayhemer,bkelly (26f4f13c28)
- Bug 1173811 - Part 2: Propagate the response URL to intercepted channels when necessary (e10s). r=mayhemer,bkelly (a603fe1df2)
- Bug 1154309 - Add New Resource Timing Fields r=bz,hurley (1d14eb6bef)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (a5d18bb637)
- Bug 1175685 - add OriginAttribute to LoadInfo. r=jonas, r=ckerschb, r=michal (fb07d2c8aa)
- Bug 1212904 P1 Add a LoadTainting enumeration. r=jduell (a1db8a3e99)
- Bug 1212904 P2 Add LoadTainting information to nsILoadInfo. r=jduell (2482e5e334)
- Bug 1221151 - use [infallible] in nsILoadInfo.idl instead of manual %{C++ blocks; r=jduell (aae73129b6)
- Bug 1045891 - CSP 2 child-src implementation r=ckerschb (792920aeb9)
- Bug 1219931 - CSP: Don't allow removing a policy (r=sicking) (9daaab4186)
- Bug 1208661 - Dump client-side layer textures. r=BenWa (1f2d17d515)
1895 lines
53 KiB
C++
1895 lines
53 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* vim:set ts=4 sw=4 sts=4 et: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
|
|
#include "LayerScope.h"
|
|
|
|
#include "nsAppRunner.h"
|
|
#include "Composer2D.h"
|
|
#include "Effects.h"
|
|
#include "mozilla/Endian.h"
|
|
#include "mozilla/MathAlgorithms.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
|
|
#include "TexturePoolOGL.h"
|
|
#include "mozilla/layers/CompositorOGL.h"
|
|
#include "mozilla/layers/CompositorParent.h"
|
|
#include "mozilla/layers/LayerManagerComposite.h"
|
|
#include "mozilla/layers/TextureHostOGL.h"
|
|
|
|
#include "gfxContext.h"
|
|
#include "gfxUtils.h"
|
|
#include "gfxPrefs.h"
|
|
#include "nsIWidget.h"
|
|
|
|
#include "GLContext.h"
|
|
#include "GLContextProvider.h"
|
|
#include "GLReadTexImageHelper.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIConsoleService.h"
|
|
|
|
#include <memory>
|
|
#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"
|
|
#include <list>
|
|
|
|
// Undo the damage done by mozzconf.h
|
|
#undef compress
|
|
#include "mozilla/Compression.h"
|
|
|
|
// Protocol buffer (generated automatically)
|
|
#include "protobuf/LayerScopePacket.pb.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using namespace mozilla::Compression;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::gl;
|
|
using namespace mozilla;
|
|
using namespace layerscope;
|
|
|
|
class DebugDataSender;
|
|
class DebugGLData;
|
|
|
|
/*
|
|
* Manage Websocket connections
|
|
*/
|
|
class LayerScopeWebSocketManager {
|
|
public:
|
|
LayerScopeWebSocketManager();
|
|
~LayerScopeWebSocketManager();
|
|
|
|
void RemoveAllConnections()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MutexAutoLock lock(mHandlerMutex);
|
|
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()
|
|
{
|
|
// This funtion can be called in both main thread and compositor thread.
|
|
MutexAutoLock lock(mHandlerMutex);
|
|
return (mHandlers.Length() != 0) ? true : false;
|
|
}
|
|
|
|
void AppendDebugData(DebugGLData *aDebugData);
|
|
void CleanDebugData();
|
|
void DispatchDebugData();
|
|
|
|
private:
|
|
void AddConnection(nsISocketTransport *aTransport)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aTransport);
|
|
|
|
MutexAutoLock lock(mHandlerMutex);
|
|
|
|
RefPtr<SocketHandler> temp = new SocketHandler();
|
|
temp->OpenStream(aTransport);
|
|
mHandlers.AppendElement(temp.get());
|
|
}
|
|
|
|
void RemoveConnection(uint32_t aIndex)
|
|
{
|
|
// TBD: RemoveConnection is executed on the compositor thread and
|
|
// AddConntection is executed on the main thread, which might be
|
|
// a problem if a user disconnect and connect readlly quickly at
|
|
// viewer side.
|
|
|
|
// We should dispatch RemoveConnection onto main thead.
|
|
MOZ_ASSERT(aIndex < mHandlers.Length());
|
|
|
|
MutexAutoLock lock(mHandlerMutex);
|
|
mHandlers.RemoveElementAt(aIndex);
|
|
}
|
|
|
|
friend class SocketListener;
|
|
class SocketListener : public nsIServerSocketListener
|
|
{
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
SocketListener() { }
|
|
|
|
/* nsIServerSocketListener */
|
|
NS_IMETHODIMP OnSocketAccepted(nsIServerSocket *aServ,
|
|
nsISocketTransport *aTransport) override;
|
|
NS_IMETHODIMP OnStopListening(nsIServerSocket *aServ,
|
|
nsresult aStatus) override
|
|
{
|
|
return NS_OK;
|
|
}
|
|
private:
|
|
virtual ~SocketListener() { }
|
|
};
|
|
|
|
/*
|
|
* This class handle websocket protocol which included
|
|
* handshake and data frame's header
|
|
*/
|
|
class SocketHandler : public nsIInputStreamCallback {
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
SocketHandler()
|
|
: mState(NoHandshake)
|
|
, mConnected(false)
|
|
{ }
|
|
|
|
void OpenStream(nsISocketTransport* aTransport);
|
|
bool WriteToStream(void *aPtr, uint32_t aSize);
|
|
|
|
// nsIInputStreamCallback
|
|
NS_IMETHODIMP OnInputStreamReady(nsIAsyncInputStream *aStream) override;
|
|
|
|
private:
|
|
virtual ~SocketHandler() { CloseConnection(); }
|
|
|
|
void ReadInputStreamData(nsTArray<nsCString>& aProtocolString);
|
|
bool WebSocketHandshake(nsTArray<nsCString>& aProtocolString);
|
|
void ApplyMask(uint32_t aMask, uint8_t *aData, uint64_t aLen);
|
|
bool HandleDataFrame(uint8_t *aData, uint32_t aSize);
|
|
void CloseConnection();
|
|
|
|
nsresult HandleSocketMessage(nsIAsyncInputStream *aStream);
|
|
nsresult ProcessInput(uint8_t *aBuffer, uint32_t aCount);
|
|
|
|
private:
|
|
enum SocketStateType {
|
|
NoHandshake,
|
|
HandshakeSuccess,
|
|
HandshakeFailed
|
|
};
|
|
SocketStateType mState;
|
|
|
|
nsCOMPtr<nsIOutputStream> mOutputStream;
|
|
nsCOMPtr<nsIAsyncInputStream> mInputStream;
|
|
nsCOMPtr<nsISocketTransport> mTransport;
|
|
bool mConnected;
|
|
};
|
|
|
|
nsTArray<RefPtr<SocketHandler> > mHandlers;
|
|
nsCOMPtr<nsIThread> mDebugSenderThread;
|
|
RefPtr<DebugDataSender> mCurrentSender;
|
|
nsCOMPtr<nsIServerSocket> mServerSocket;
|
|
|
|
// Keep mHandlers accessing thread safe.
|
|
Mutex mHandlerMutex;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketListener,
|
|
nsIServerSocketListener);
|
|
NS_IMPL_ISUPPORTS(LayerScopeWebSocketManager::SocketHandler,
|
|
nsIInputStreamCallback);
|
|
|
|
class DrawSession {
|
|
public:
|
|
DrawSession()
|
|
: mOffsetX(0.0)
|
|
, mOffsetY(0.0)
|
|
, mRects(0)
|
|
{ }
|
|
|
|
float mOffsetX;
|
|
float mOffsetY;
|
|
gfx::Matrix4x4 mMVMatrix;
|
|
size_t mRects;
|
|
gfx::Rect mLayerRects[4];
|
|
gfx::Rect mTextureRects[4];
|
|
std::list<GLuint> mTexIDs;
|
|
};
|
|
|
|
class ContentMonitor {
|
|
public:
|
|
using THArray = nsTArray<const TextureHost *>;
|
|
|
|
// Notify the content of a TextureHost was changed.
|
|
void SetChangedHost(const TextureHost* host) {
|
|
if (THArray::NoIndex == mChangedHosts.IndexOf(host)) {
|
|
mChangedHosts.AppendElement(host);
|
|
}
|
|
}
|
|
|
|
// Clear changed flag of a host.
|
|
void ClearChangedHost(const TextureHost* host) {
|
|
if (THArray::NoIndex != mChangedHosts.IndexOf(host)) {
|
|
mChangedHosts.RemoveElement(host);
|
|
}
|
|
}
|
|
|
|
// Return true iff host is a new one or the content of it had been changed.
|
|
bool IsChangedOrNew(const TextureHost* host) {
|
|
if (THArray::NoIndex == mSeenHosts.IndexOf(host)) {
|
|
mSeenHosts.AppendElement(host);
|
|
return true;
|
|
}
|
|
|
|
if (decltype(mChangedHosts)::NoIndex != mChangedHosts.IndexOf(host)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Empty() {
|
|
mSeenHosts.SetLength(0);
|
|
mChangedHosts.SetLength(0);
|
|
}
|
|
private:
|
|
THArray mSeenHosts;
|
|
THArray mChangedHosts;
|
|
};
|
|
|
|
/*
|
|
* Hold all singleton objects used by LayerScope.
|
|
*/
|
|
class LayerScopeManager
|
|
{
|
|
public:
|
|
void CreateServerSocket()
|
|
{
|
|
// WebSocketManager must be created on the main thread.
|
|
if (NS_IsMainThread()) {
|
|
mWebSocketManager = mozilla::MakeUnique<LayerScopeWebSocketManager>();
|
|
} else {
|
|
// Dispatch creation to main thread, and make sure we
|
|
// dispatch this only once after booting
|
|
static bool dispatched = false;
|
|
if (dispatched) {
|
|
return;
|
|
}
|
|
|
|
DebugOnly<nsresult> rv =
|
|
NS_DispatchToMainThread(new CreateServerSocketRunnable(this));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv),
|
|
"Failed to dispatch WebSocket Creation to main thread");
|
|
dispatched = true;
|
|
}
|
|
}
|
|
|
|
void DestroyServerSocket()
|
|
{
|
|
// Destroy Web Server Socket
|
|
if (mWebSocketManager) {
|
|
mWebSocketManager->RemoveAllConnections();
|
|
}
|
|
}
|
|
|
|
LayerScopeWebSocketManager* GetSocketManager()
|
|
{
|
|
return mWebSocketManager.get();
|
|
}
|
|
|
|
ContentMonitor* GetContentMonitor()
|
|
{
|
|
if (!mContentMonitor.get()) {
|
|
mContentMonitor = mozilla::MakeUnique<ContentMonitor>();
|
|
}
|
|
|
|
return mContentMonitor.get();
|
|
}
|
|
|
|
void NewDrawSession() {
|
|
mSession = mozilla::MakeUnique<DrawSession>();
|
|
}
|
|
|
|
DrawSession& CurrentSession() {
|
|
return *mSession;
|
|
}
|
|
|
|
void SetPixelScale(double scale) {
|
|
mScale = scale;
|
|
}
|
|
|
|
double GetPixelScale() const {
|
|
return mScale;
|
|
}
|
|
|
|
LayerScopeManager()
|
|
: mScale(1.0)
|
|
{
|
|
}
|
|
private:
|
|
friend class CreateServerSocketRunnable;
|
|
class CreateServerSocketRunnable : public nsRunnable
|
|
{
|
|
public:
|
|
CreateServerSocketRunnable(LayerScopeManager *aLayerScopeManager)
|
|
: mLayerScopeManager(aLayerScopeManager)
|
|
{
|
|
}
|
|
NS_IMETHOD Run() {
|
|
mLayerScopeManager->mWebSocketManager =
|
|
mozilla::MakeUnique<LayerScopeWebSocketManager>();
|
|
return NS_OK;
|
|
}
|
|
private:
|
|
LayerScopeManager* mLayerScopeManager;
|
|
};
|
|
|
|
mozilla::UniquePtr<LayerScopeWebSocketManager> mWebSocketManager;
|
|
mozilla::UniquePtr<DrawSession> mSession;
|
|
mozilla::UniquePtr<ContentMonitor> mContentMonitor;
|
|
double mScale;
|
|
};
|
|
|
|
LayerScopeManager gLayerScopeManager;
|
|
|
|
/*
|
|
* DebugGLData is the base class of
|
|
* 1. DebugGLFrameStatusData (Frame start/end packet)
|
|
* 2. DebugGLColorData (Color data packet)
|
|
* 3. DebugGLTextureData (Texture data packet)
|
|
* 4. DebugGLLayersData (Layers Tree data packet)
|
|
* 5. DebugGLMetaData (Meta data packet)
|
|
*/
|
|
class DebugGLData: public LinkedListElement<DebugGLData> {
|
|
public:
|
|
explicit DebugGLData(Packet::DataType aDataType)
|
|
: mDataType(aDataType)
|
|
{ }
|
|
|
|
virtual ~DebugGLData() { }
|
|
|
|
virtual bool Write() = 0;
|
|
|
|
protected:
|
|
static bool WriteToStream(Packet& aPacket) {
|
|
if (!gLayerScopeManager.GetSocketManager())
|
|
return true;
|
|
|
|
uint32_t size = aPacket.ByteSize();
|
|
auto data = MakeUnique<uint8_t[]>(size);
|
|
aPacket.SerializeToArray(data.get(), size);
|
|
return gLayerScopeManager.GetSocketManager()->WriteAll(data.get(), size);
|
|
}
|
|
|
|
Packet::DataType mDataType;
|
|
};
|
|
|
|
class DebugGLFrameStatusData final: public DebugGLData
|
|
{
|
|
public:
|
|
DebugGLFrameStatusData(Packet::DataType aDataType,
|
|
int64_t aValue)
|
|
: DebugGLData(aDataType),
|
|
mFrameStamp(aValue)
|
|
{ }
|
|
|
|
explicit DebugGLFrameStatusData(Packet::DataType aDataType)
|
|
: DebugGLData(aDataType),
|
|
mFrameStamp(0)
|
|
{ }
|
|
|
|
virtual bool Write() override {
|
|
Packet packet;
|
|
packet.set_type(mDataType);
|
|
|
|
FramePacket* fp = packet.mutable_frame();
|
|
fp->set_value(static_cast<uint64_t>(mFrameStamp));
|
|
|
|
fp->set_scale(gLayerScopeManager.GetPixelScale());
|
|
|
|
return WriteToStream(packet);
|
|
}
|
|
|
|
protected:
|
|
int64_t mFrameStamp;
|
|
};
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
// B2G optimization.
|
|
class DebugGLGraphicBuffer final: public DebugGLData {
|
|
public:
|
|
DebugGLGraphicBuffer(void *layerRef,
|
|
GLenum target,
|
|
GLuint name,
|
|
const LayerRenderState &aState)
|
|
: DebugGLData(Packet::TEXTURE),
|
|
mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
|
|
mTarget(target),
|
|
mName(name),
|
|
mState(aState)
|
|
{
|
|
}
|
|
|
|
virtual bool Write() override {
|
|
return WriteToStream(mPacket);
|
|
}
|
|
|
|
bool TryPack(bool packData) {
|
|
android::sp<android::GraphicBuffer> buffer = mState.mSurface;
|
|
MOZ_ASSERT(buffer.get());
|
|
|
|
mPacket.set_type(mDataType);
|
|
TexturePacket* tp = mPacket.mutable_texture();
|
|
tp->set_layerref(mLayerRef);
|
|
tp->set_name(mName);
|
|
tp->set_target(mTarget);
|
|
|
|
int pFormat = buffer->getPixelFormat();
|
|
if (HAL_PIXEL_FORMAT_RGBA_8888 != pFormat &&
|
|
HAL_PIXEL_FORMAT_RGBX_8888 != pFormat) {
|
|
return false;
|
|
}
|
|
|
|
int32_t stride = buffer->getStride() * 4;
|
|
int32_t height = buffer->getHeight();
|
|
int32_t width = buffer->getWidth();
|
|
int32_t sourceSize = stride * height;
|
|
if (sourceSize <= 0) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t dFormat = mState.FormatRBSwapped() ?
|
|
LOCAL_GL_BGRA : LOCAL_GL_RGBA;
|
|
tp->set_dataformat(dFormat);
|
|
tp->set_dataformat((1 << 16 | tp->dataformat()));
|
|
tp->set_width(width);
|
|
tp->set_height(height);
|
|
tp->set_stride(stride);
|
|
|
|
if (packData) {
|
|
uint8_t* grallocData = nullptr;
|
|
if (BAD_VALUE == buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN |
|
|
GRALLOC_USAGE_SW_WRITE_NEVER,
|
|
reinterpret_cast<void**>(&grallocData)))
|
|
{
|
|
return false;
|
|
}
|
|
// Do not return before buffer->unlock();
|
|
auto compressedData =
|
|
MakeUnique<char[]>(LZ4::maxCompressedSize(sourceSize));
|
|
int compressedSize = LZ4::compress((char*)grallocData,
|
|
sourceSize,
|
|
compressedData.get());
|
|
|
|
if (compressedSize > 0) {
|
|
tp->set_data(compressedData.get(), compressedSize);
|
|
} else {
|
|
buffer->unlock();
|
|
return false;
|
|
}
|
|
|
|
buffer->unlock();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
uint64_t mLayerRef;
|
|
GLenum mTarget;
|
|
GLuint mName;
|
|
const LayerRenderState &mState;
|
|
Packet mPacket;
|
|
};
|
|
#endif
|
|
|
|
class DebugGLTextureData final: public DebugGLData {
|
|
public:
|
|
DebugGLTextureData(GLContext* cx,
|
|
void* layerRef,
|
|
GLenum target,
|
|
GLuint name,
|
|
DataSourceSurface* img)
|
|
: DebugGLData(Packet::TEXTURE),
|
|
mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
|
|
mTarget(target),
|
|
mName(name),
|
|
mContextAddress(reinterpret_cast<intptr_t>(cx)),
|
|
mDatasize(0)
|
|
{
|
|
// pre-packing
|
|
// DataSourceSurface may have locked buffer,
|
|
// so we should compress now, and then it could
|
|
// be unlocked outside.
|
|
pack(img);
|
|
}
|
|
|
|
virtual bool Write() override {
|
|
return WriteToStream(mPacket);
|
|
}
|
|
|
|
private:
|
|
void pack(DataSourceSurface* aImage) {
|
|
mPacket.set_type(mDataType);
|
|
|
|
TexturePacket* tp = mPacket.mutable_texture();
|
|
tp->set_layerref(mLayerRef);
|
|
tp->set_name(mName);
|
|
tp->set_target(mTarget);
|
|
tp->set_dataformat(LOCAL_GL_RGBA);
|
|
tp->set_glcontext(static_cast<uint64_t>(mContextAddress));
|
|
|
|
if (aImage) {
|
|
tp->set_width(aImage->GetSize().width);
|
|
tp->set_height(aImage->GetSize().height);
|
|
tp->set_stride(aImage->Stride());
|
|
|
|
mDatasize = aImage->GetSize().height * aImage->Stride();
|
|
|
|
auto compresseddata = MakeUnique<char[]>(LZ4::maxCompressedSize(mDatasize));
|
|
if (compresseddata) {
|
|
int ndatasize = LZ4::compress((char*)aImage->GetData(),
|
|
mDatasize,
|
|
compresseddata.get());
|
|
if (ndatasize > 0) {
|
|
mDatasize = ndatasize;
|
|
tp->set_dataformat((1 << 16 | tp->dataformat()));
|
|
tp->set_data(compresseddata.get(), mDatasize);
|
|
} else {
|
|
NS_WARNING("Compress data failed");
|
|
tp->set_data(aImage->GetData(), mDatasize);
|
|
}
|
|
} else {
|
|
NS_WARNING("Couldn't new compressed data.");
|
|
tp->set_data(aImage->GetData(), mDatasize);
|
|
}
|
|
} else {
|
|
tp->set_width(0);
|
|
tp->set_height(0);
|
|
tp->set_stride(0);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
uint64_t mLayerRef;
|
|
GLenum mTarget;
|
|
GLuint mName;
|
|
intptr_t mContextAddress;
|
|
uint32_t mDatasize;
|
|
|
|
// Packet data
|
|
Packet mPacket;
|
|
};
|
|
|
|
class DebugGLColorData final: public DebugGLData {
|
|
public:
|
|
DebugGLColorData(void* layerRef,
|
|
const Color& color,
|
|
int width,
|
|
int height)
|
|
: DebugGLData(Packet::COLOR),
|
|
mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
|
|
mColor(color.ToABGR()),
|
|
mSize(width, height)
|
|
{ }
|
|
|
|
virtual bool Write() override {
|
|
Packet packet;
|
|
packet.set_type(mDataType);
|
|
|
|
ColorPacket* cp = packet.mutable_color();
|
|
cp->set_layerref(mLayerRef);
|
|
cp->set_color(mColor);
|
|
cp->set_width(mSize.width);
|
|
cp->set_height(mSize.height);
|
|
|
|
return WriteToStream(packet);
|
|
}
|
|
|
|
protected:
|
|
uint64_t mLayerRef;
|
|
uint32_t mColor;
|
|
IntSize mSize;
|
|
};
|
|
|
|
class DebugGLLayersData final: public DebugGLData {
|
|
public:
|
|
explicit DebugGLLayersData(UniquePtr<Packet> aPacket)
|
|
: DebugGLData(Packet::LAYERS),
|
|
mPacket(Move(aPacket))
|
|
{ }
|
|
|
|
virtual bool Write() override {
|
|
mPacket->set_type(mDataType);
|
|
return WriteToStream(*mPacket);
|
|
}
|
|
|
|
protected:
|
|
UniquePtr<Packet> mPacket;
|
|
};
|
|
|
|
class DebugGLMetaData final: public DebugGLData
|
|
{
|
|
public:
|
|
DebugGLMetaData(Packet::DataType aDataType,
|
|
bool aValue)
|
|
: DebugGLData(aDataType),
|
|
mComposedByHwc(aValue)
|
|
{ }
|
|
|
|
explicit DebugGLMetaData(Packet::DataType aDataType)
|
|
: DebugGLData(aDataType),
|
|
mComposedByHwc(false)
|
|
{ }
|
|
|
|
virtual bool Write() override {
|
|
Packet packet;
|
|
packet.set_type(mDataType);
|
|
|
|
MetaPacket* mp = packet.mutable_meta();
|
|
mp->set_composedbyhwc(mComposedByHwc);
|
|
|
|
return WriteToStream(packet);
|
|
}
|
|
|
|
protected:
|
|
bool mComposedByHwc;
|
|
};
|
|
|
|
class DebugGLDrawData final: public DebugGLData {
|
|
public:
|
|
DebugGLDrawData(float aOffsetX,
|
|
float aOffsetY,
|
|
const gfx::Matrix4x4& aMVMatrix,
|
|
size_t aRects,
|
|
const gfx::Rect* aLayerRects,
|
|
const gfx::Rect* aTextureRects,
|
|
const std::list<GLuint> aTexIDs,
|
|
void* aLayerRef)
|
|
: DebugGLData(Packet::DRAW),
|
|
mOffsetX(aOffsetX),
|
|
mOffsetY(aOffsetY),
|
|
mMVMatrix(aMVMatrix),
|
|
mRects(aRects),
|
|
mTexIDs(aTexIDs),
|
|
mLayerRef(reinterpret_cast<uint64_t>(aLayerRef))
|
|
{
|
|
for (size_t i = 0; i < mRects; i++){
|
|
mLayerRects[i] = aLayerRects[i];
|
|
mTextureRects[i] = aTextureRects[i];
|
|
}
|
|
}
|
|
|
|
virtual bool Write() override {
|
|
Packet packet;
|
|
packet.set_type(mDataType);
|
|
|
|
DrawPacket* dp = packet.mutable_draw();
|
|
dp->set_layerref(mLayerRef);
|
|
|
|
dp->set_offsetx(mOffsetX);
|
|
dp->set_offsety(mOffsetY);
|
|
|
|
auto element = reinterpret_cast<Float *>(&mMVMatrix);
|
|
for (int i = 0; i < 16; i++) {
|
|
dp->add_mvmatrix(*element++);
|
|
}
|
|
dp->set_totalrects(mRects);
|
|
|
|
MOZ_ASSERT(mRects > 0 && mRects < 4);
|
|
for (size_t i = 0; i < mRects; i++) {
|
|
// Vertex
|
|
layerscope::DrawPacket::Rect* pRect = dp->add_layerrect();
|
|
pRect->set_x(mLayerRects[i].x);
|
|
pRect->set_y(mLayerRects[i].y);
|
|
pRect->set_w(mLayerRects[i].width);
|
|
pRect->set_h(mLayerRects[i].height);
|
|
|
|
// UV
|
|
pRect = dp->add_texturerect();
|
|
pRect->set_x(mTextureRects[i].x);
|
|
pRect->set_y(mTextureRects[i].y);
|
|
pRect->set_w(mTextureRects[i].width);
|
|
pRect->set_h(mTextureRects[i].height);
|
|
}
|
|
|
|
for (GLuint texId: mTexIDs) {
|
|
dp->add_texids(texId);
|
|
}
|
|
|
|
return WriteToStream(packet);
|
|
}
|
|
|
|
protected:
|
|
float mOffsetX;
|
|
float mOffsetY;
|
|
gfx::Matrix4x4 mMVMatrix;
|
|
size_t mRects;
|
|
gfx::Rect mLayerRects[4];
|
|
gfx::Rect mTextureRects[4];
|
|
std::list<GLuint> mTexIDs;
|
|
uint64_t mLayerRef;
|
|
};
|
|
|
|
class DebugDataSender
|
|
{
|
|
public:
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DebugDataSender)
|
|
|
|
// Append a DebugData into mList on mThread
|
|
class AppendTask: public nsIRunnable
|
|
{
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
AppendTask(DebugDataSender *host, DebugGLData *d)
|
|
: mData(d),
|
|
mHost(host)
|
|
{ }
|
|
|
|
NS_IMETHODIMP Run() override {
|
|
mHost->mList.insertBack(mData);
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
virtual ~AppendTask() { }
|
|
|
|
DebugGLData *mData;
|
|
// Keep a strong reference to DebugDataSender to prevent this object
|
|
// accessing mHost on mThread, when it's been destroyed on the main
|
|
// thread.
|
|
RefPtr<DebugDataSender> mHost;
|
|
};
|
|
|
|
// Clear all DebugData in mList on mThead.
|
|
class ClearTask: public nsIRunnable
|
|
{
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
explicit ClearTask(DebugDataSender *host)
|
|
: mHost(host)
|
|
{ }
|
|
|
|
NS_IMETHODIMP Run() override {
|
|
mHost->RemoveData();
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
virtual ~ClearTask() { }
|
|
|
|
RefPtr<DebugDataSender> mHost;
|
|
};
|
|
|
|
// Send all DebugData in mList via websocket, and then, clean up
|
|
// mList on mThread.
|
|
class SendTask: public nsIRunnable
|
|
{
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
explicit SendTask(DebugDataSender *host)
|
|
: mHost(host)
|
|
{ }
|
|
|
|
NS_IMETHODIMP Run() override {
|
|
// Sendout all appended debug data.
|
|
DebugGLData *d = nullptr;
|
|
while ((d = mHost->mList.popFirst()) != nullptr) {
|
|
UniquePtr<DebugGLData> cleaner(d);
|
|
if (!d->Write()) {
|
|
gLayerScopeManager.DestroyServerSocket();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Cleanup.
|
|
mHost->RemoveData();
|
|
return NS_OK;
|
|
}
|
|
private:
|
|
virtual ~SendTask() { }
|
|
|
|
RefPtr<DebugDataSender> mHost;
|
|
};
|
|
|
|
explicit DebugDataSender(nsIThread *thread)
|
|
: mThread(thread)
|
|
{ }
|
|
|
|
void Append(DebugGLData *d) {
|
|
mThread->Dispatch(new AppendTask(this, d), NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
void Cleanup() {
|
|
mThread->Dispatch(new ClearTask(this), NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
void Send() {
|
|
mThread->Dispatch(new SendTask(this), NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
protected:
|
|
virtual ~DebugDataSender() {}
|
|
void RemoveData() {
|
|
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
|
|
if (mList.isEmpty())
|
|
return;
|
|
|
|
DebugGLData *d;
|
|
while ((d = mList.popFirst()) != nullptr)
|
|
delete d;
|
|
}
|
|
|
|
// We can only modify or aceess mList on mThread.
|
|
LinkedList<DebugGLData> mList;
|
|
nsCOMPtr<nsIThread> mThread;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(DebugDataSender::AppendTask, nsIRunnable);
|
|
NS_IMPL_ISUPPORTS(DebugDataSender::ClearTask, nsIRunnable);
|
|
NS_IMPL_ISUPPORTS(DebugDataSender::SendTask, nsIRunnable);
|
|
|
|
|
|
/*
|
|
* LayerScope SendXXX Structure
|
|
* 1. SendLayer
|
|
* 2. SendEffectChain
|
|
* 1. SendTexturedEffect
|
|
* -> SendTextureSource
|
|
* 2. SendYCbCrEffect
|
|
* -> SendTextureSource
|
|
* 3. SendColor
|
|
*/
|
|
class SenderHelper
|
|
{
|
|
// Sender public APIs
|
|
public:
|
|
static void SendLayer(LayerComposite* aLayer,
|
|
int aWidth,
|
|
int aHeight);
|
|
|
|
static void SendEffectChain(gl::GLContext* aGLContext,
|
|
const EffectChain& aEffectChain,
|
|
int aWidth = 0,
|
|
int aHeight = 0);
|
|
|
|
static void SetLayersTreeSendable(bool aSet) {sLayersTreeSendable = aSet;}
|
|
|
|
static void SetLayersBufferSendable(bool aSet) {sLayersBufferSendable = aSet;}
|
|
|
|
static bool GetLayersTreeSendable() {return sLayersTreeSendable;}
|
|
|
|
static void ClearTextureIdList();
|
|
|
|
|
|
// Sender private functions
|
|
private:
|
|
static void SendColor(void* aLayerRef,
|
|
const Color& aColor,
|
|
int aWidth,
|
|
int aHeight);
|
|
static void SendTextureSource(GLContext* aGLContext,
|
|
void* aLayerRef,
|
|
TextureSourceOGL* aSource,
|
|
GLuint aTexID,
|
|
bool aFlipY);
|
|
#ifdef MOZ_WIDGET_GONK
|
|
static bool SendGraphicBuffer(void* aLayerRef,
|
|
TextureSourceOGL* aSource,
|
|
GLuint aTexID,
|
|
const TexturedEffect* aEffect);
|
|
#endif
|
|
static void SendTexturedEffect(GLContext* aGLContext,
|
|
void* aLayerRef,
|
|
const TexturedEffect* aEffect);
|
|
static void SendYCbCrEffect(GLContext* aGLContext,
|
|
void* aLayerRef,
|
|
const EffectYCbCr* aEffect);
|
|
static GLuint GetTextureID(GLContext* aGLContext,
|
|
TextureSourceOGL* aSource);
|
|
static bool IsTextureIdContainsInList(GLuint aTextureId);
|
|
// Data fields
|
|
private:
|
|
static bool sLayersTreeSendable;
|
|
static bool sLayersBufferSendable;
|
|
static std::list<GLuint> sTextureIdList;
|
|
};
|
|
|
|
bool SenderHelper::sLayersTreeSendable = true;
|
|
bool SenderHelper::sLayersBufferSendable = true;
|
|
std::list<GLuint> SenderHelper::sTextureIdList;
|
|
|
|
|
|
// ----------------------------------------------
|
|
// SenderHelper implementation
|
|
// ----------------------------------------------
|
|
void
|
|
SenderHelper::ClearTextureIdList()
|
|
{
|
|
std::list<GLuint>::iterator it;
|
|
while (!sTextureIdList.empty()) {
|
|
it = sTextureIdList.begin();
|
|
sTextureIdList.erase(it);
|
|
}
|
|
}
|
|
|
|
bool
|
|
SenderHelper::IsTextureIdContainsInList(GLuint aTextureId)
|
|
{
|
|
for (std::list<GLuint>::iterator it = sTextureIdList.begin();
|
|
it != sTextureIdList.end(); ++it) {
|
|
if (*it == aTextureId) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
SenderHelper::SendLayer(LayerComposite* aLayer,
|
|
int aWidth,
|
|
int aHeight)
|
|
{
|
|
MOZ_ASSERT(aLayer && aLayer->GetLayer());
|
|
if (!aLayer || !aLayer->GetLayer()) {
|
|
return;
|
|
}
|
|
|
|
switch (aLayer->GetLayer()->GetType()) {
|
|
case Layer::TYPE_COLOR: {
|
|
EffectChain effect;
|
|
aLayer->GenEffectChain(effect);
|
|
SenderHelper::SendEffectChain(nullptr, effect, aWidth, aHeight);
|
|
break;
|
|
}
|
|
case Layer::TYPE_IMAGE:
|
|
case Layer::TYPE_CANVAS:
|
|
case Layer::TYPE_PAINTED: {
|
|
// Get CompositableHost and Compositor
|
|
CompositableHost* compHost = aLayer->GetCompositableHost();
|
|
Compositor* comp = compHost->GetCompositor();
|
|
// Send EffectChain only for CompositorOGL
|
|
if (LayersBackend::LAYERS_OPENGL == comp->GetBackendType()) {
|
|
CompositorOGL* compOGL = static_cast<CompositorOGL*>(comp);
|
|
EffectChain effect;
|
|
// Generate primary effect (lock and gen)
|
|
AutoLockCompositableHost lock(compHost);
|
|
aLayer->GenEffectChain(effect);
|
|
SenderHelper::SendEffectChain(compOGL->gl(), effect);
|
|
}
|
|
break;
|
|
}
|
|
case Layer::TYPE_CONTAINER:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
SenderHelper::SendColor(void* aLayerRef,
|
|
const Color& aColor,
|
|
int aWidth,
|
|
int aHeight)
|
|
{
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(
|
|
new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight));
|
|
}
|
|
|
|
GLuint
|
|
SenderHelper::GetTextureID(GLContext* aGLContext,
|
|
TextureSourceOGL* aSource) {
|
|
GLenum textureTarget = aSource->GetTextureTarget();
|
|
aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::Filter::LINEAR);
|
|
|
|
GLuint texID = 0;
|
|
// This is horrid hack. It assumes that aGLContext matches the context
|
|
// aSource has bound to.
|
|
if (textureTarget == LOCAL_GL_TEXTURE_2D) {
|
|
aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &texID);
|
|
} else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
|
|
aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &texID);
|
|
} else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
|
|
aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &texID);
|
|
}
|
|
|
|
return texID;
|
|
}
|
|
|
|
void
|
|
SenderHelper::SendTextureSource(GLContext* aGLContext,
|
|
void* aLayerRef,
|
|
TextureSourceOGL* aSource,
|
|
GLuint aTexID,
|
|
bool aFlipY)
|
|
{
|
|
MOZ_ASSERT(aGLContext);
|
|
if (!aGLContext) {
|
|
return;
|
|
}
|
|
|
|
GLenum textureTarget = aSource->GetTextureTarget();
|
|
ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(textureTarget,
|
|
aSource->GetFormat());
|
|
int shaderConfig = config.mFeatures;
|
|
|
|
gfx::IntSize size = aSource->GetSize();
|
|
|
|
// By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding
|
|
// texture correctly. texID is used for tracking in DebugGLTextureData.
|
|
RefPtr<DataSourceSurface> img =
|
|
aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
|
|
size,
|
|
shaderConfig, aFlipY);
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(
|
|
new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
|
|
aTexID, img));
|
|
|
|
sTextureIdList.push_back(aTexID);
|
|
gLayerScopeManager.CurrentSession().mTexIDs.push_back(aTexID);
|
|
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
bool
|
|
SenderHelper::SendGraphicBuffer(void* aLayerRef,
|
|
TextureSourceOGL* aSource,
|
|
GLuint aTexID,
|
|
const TexturedEffect* aEffect) {
|
|
if (!aEffect->mState.mSurface.get()) {
|
|
return false;
|
|
}
|
|
|
|
GLenum target = aSource->GetTextureTarget();
|
|
mozilla::UniquePtr<DebugGLGraphicBuffer> package =
|
|
MakeUnique<DebugGLGraphicBuffer>(aLayerRef, target, aTexID, aEffect->mState);
|
|
|
|
// The texure content in this TexureHost is not altered,
|
|
// we don't need to send it again.
|
|
bool changed = gLayerScopeManager.GetContentMonitor()->IsChangedOrNew(
|
|
aEffect->mState.mTexture);
|
|
if (!package->TryPack(changed)) {
|
|
return false;
|
|
}
|
|
|
|
// Transfer ownership to SocketManager.
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(package.release());
|
|
sTextureIdList.push_back(aTexID);
|
|
|
|
gLayerScopeManager.CurrentSession().mTexIDs.push_back(aTexID);
|
|
|
|
gLayerScopeManager.GetContentMonitor()->ClearChangedHost(aEffect->mState.mTexture);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
SenderHelper::SendTexturedEffect(GLContext* aGLContext,
|
|
void* aLayerRef,
|
|
const TexturedEffect* aEffect)
|
|
{
|
|
TextureSourceOGL* source = aEffect->mTexture->AsSourceOGL();
|
|
if (!source) {
|
|
return;
|
|
}
|
|
|
|
GLuint texID = GetTextureID(aGLContext, source);
|
|
if (IsTextureIdContainsInList(texID)) {
|
|
return;
|
|
}
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
if (SendGraphicBuffer(aLayerRef, source, texID, aEffect)) {
|
|
return;
|
|
}
|
|
#endif
|
|
// Fallback texture sending path.
|
|
// Render to texture and read pixels back.
|
|
SendTextureSource(aGLContext, aLayerRef, source, texID, false);
|
|
}
|
|
|
|
void
|
|
SenderHelper::SendYCbCrEffect(GLContext* aGLContext,
|
|
void* aLayerRef,
|
|
const EffectYCbCr* aEffect)
|
|
{
|
|
TextureSource* sourceYCbCr = aEffect->mTexture;
|
|
if (!sourceYCbCr)
|
|
return;
|
|
|
|
const int Y = 0, Cb = 1, Cr = 2;
|
|
TextureSourceOGL* sourceY = sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
|
|
TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL();
|
|
TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL();
|
|
|
|
GLuint texID = GetTextureID(aGLContext, sourceY);
|
|
if (!IsTextureIdContainsInList(texID)) {
|
|
SendTextureSource(aGLContext, aLayerRef, sourceY, texID, false);
|
|
}
|
|
|
|
texID = GetTextureID(aGLContext, sourceCb);
|
|
if (!IsTextureIdContainsInList(texID)) {
|
|
SendTextureSource(aGLContext, aLayerRef, sourceCb, texID, false);
|
|
}
|
|
|
|
texID = GetTextureID(aGLContext, sourceCr);
|
|
if (!IsTextureIdContainsInList(texID)) {
|
|
SendTextureSource(aGLContext, aLayerRef, sourceCr, texID, false);
|
|
}
|
|
}
|
|
|
|
void
|
|
SenderHelper::SendEffectChain(GLContext* aGLContext,
|
|
const EffectChain& aEffectChain,
|
|
int aWidth,
|
|
int aHeight)
|
|
{
|
|
if (!sLayersBufferSendable) return;
|
|
|
|
const Effect* primaryEffect = aEffectChain.mPrimaryEffect;
|
|
switch (primaryEffect->mType) {
|
|
case EffectTypes::RGB: {
|
|
const TexturedEffect* texturedEffect =
|
|
static_cast<const TexturedEffect*>(primaryEffect);
|
|
SendTexturedEffect(aGLContext, aEffectChain.mLayerRef, texturedEffect);
|
|
break;
|
|
}
|
|
case EffectTypes::YCBCR: {
|
|
const EffectYCbCr* yCbCrEffect =
|
|
static_cast<const EffectYCbCr*>(primaryEffect);
|
|
SendYCbCrEffect(aGLContext, aEffectChain.mLayerRef, yCbCrEffect);
|
|
break;
|
|
}
|
|
case EffectTypes::SOLID_COLOR: {
|
|
const EffectSolidColor* solidColorEffect =
|
|
static_cast<const EffectSolidColor*>(primaryEffect);
|
|
SendColor(aEffectChain.mLayerRef, solidColorEffect->mColor,
|
|
aWidth, aHeight);
|
|
break;
|
|
}
|
|
case EffectTypes::COMPONENT_ALPHA:
|
|
case EffectTypes::RENDER_TARGET:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//const Effect* secondaryEffect = aEffectChain.mSecondaryEffects[EffectTypes::MASK];
|
|
// TODO:
|
|
}
|
|
|
|
void
|
|
LayerScope::ContentChanged(TextureHost *host)
|
|
{
|
|
if (!CheckSendable()) {
|
|
return;
|
|
}
|
|
|
|
gLayerScopeManager.GetContentMonitor()->SetChangedHost(host);
|
|
}
|
|
|
|
// ----------------------------------------------
|
|
// SocketHandler implementation
|
|
// ----------------------------------------------
|
|
void
|
|
LayerScopeWebSocketManager::SocketHandler::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
|
|
LayerScopeWebSocketManager::SocketHandler::WriteToStream(void *aPtr,
|
|
uint32_t aSize)
|
|
{
|
|
if (mState == NoHandshake) {
|
|
// Not yet handshake, just return true in case of
|
|
// LayerScope remove this handle
|
|
return true;
|
|
} else if (mState == HandshakeFailed) {
|
|
return false;
|
|
}
|
|
|
|
if (!mOutputStream) {
|
|
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 (aSize <= 125) {
|
|
wsHeaderSize = 2;
|
|
wsHeader[1] = aSize;
|
|
} else if (aSize < 65536) {
|
|
wsHeaderSize = 4;
|
|
wsHeader[1] = 0x7E;
|
|
NetworkEndian::writeUint16(wsHeader + 2, aSize);
|
|
} else {
|
|
wsHeaderSize = 10;
|
|
wsHeader[1] = 0x7F;
|
|
NetworkEndian::writeUint64(wsHeader + 2, aSize);
|
|
}
|
|
|
|
// 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 < aSize) {
|
|
uint32_t cnt;
|
|
rv = mOutputStream->Write(reinterpret_cast<char*>(aPtr) + written,
|
|
aSize - written, &cnt);
|
|
if (NS_FAILED(rv))
|
|
return false;
|
|
|
|
written += cnt;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LayerScopeWebSocketManager::SocketHandler::OnInputStreamReady(nsIAsyncInputStream *aStream)
|
|
{
|
|
MOZ_ASSERT(mInputStream);
|
|
|
|
if (!mInputStream) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!mConnected) {
|
|
nsTArray<nsCString> protocolString;
|
|
ReadInputStreamData(protocolString);
|
|
|
|
if (WebSocketHandshake(protocolString)) {
|
|
mState = HandshakeSuccess;
|
|
mConnected = true;
|
|
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
|
|
} else {
|
|
mState = HandshakeFailed;
|
|
}
|
|
return NS_OK;
|
|
} else {
|
|
return HandleSocketMessage(aStream);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerScopeWebSocketManager::SocketHandler::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
|
|
LayerScopeWebSocketManager::SocketHandler::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.EqualsLiteral("7") ||
|
|
version.EqualsLiteral("8") ||
|
|
version.EqualsLiteral("13"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!(protocol.EqualsIgnoreCase("binary"))) {
|
|
return false;
|
|
}
|
|
|
|
if (!mOutputStream) {
|
|
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::kHashSize]; // SHA1 digests are 20 bytes long.
|
|
sha1.finish(digest);
|
|
nsCString newString(reinterpret_cast<char*>(digest), SHA1Sum::kHashSize);
|
|
Base64Encode(newString, res);
|
|
|
|
nsCString response("HTTP/1.1 101 Switching Protocols\r\n");
|
|
response.AppendLiteral("Upgrade: websocket\r\n");
|
|
response.AppendLiteral("Connection: Upgrade\r\n");
|
|
response.Append(nsCString("Sec-WebSocket-Accept: ") + res + nsCString("\r\n"));
|
|
response.AppendLiteral("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;
|
|
}
|
|
|
|
nsresult
|
|
LayerScopeWebSocketManager::SocketHandler::HandleSocketMessage(nsIAsyncInputStream *aStream)
|
|
{
|
|
// The reading and parsing of this input stream is customized for layer viewer.
|
|
const uint32_t cPacketSize = 1024;
|
|
char buffer[cPacketSize];
|
|
uint32_t count = 0;
|
|
nsresult rv = NS_OK;
|
|
|
|
do {
|
|
rv = mInputStream->Read((char *)buffer, cPacketSize, &count);
|
|
|
|
// TODO: combine packets if we have to read more than once
|
|
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
|
|
mInputStream->AsyncWait(this, 0, 0, NS_GetCurrentThread());
|
|
return NS_OK;
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
break;
|
|
}
|
|
|
|
if (count == 0) {
|
|
// NS_BASE_STREAM_CLOSED
|
|
CloseConnection();
|
|
break;
|
|
}
|
|
|
|
rv = ProcessInput(reinterpret_cast<uint8_t *>(buffer), count);
|
|
} while (NS_SUCCEEDED(rv) && mInputStream);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
LayerScopeWebSocketManager::SocketHandler::ProcessInput(uint8_t *aBuffer,
|
|
uint32_t aCount)
|
|
{
|
|
uint32_t avail = aCount;
|
|
|
|
// Decode Websocket data frame
|
|
if (avail <= 2) {
|
|
NS_WARNING("Packet size is less than 2 bytes");
|
|
return NS_OK;
|
|
}
|
|
|
|
// First byte, data type, only care the opcode
|
|
// rsvBits: aBuffer[0] & 0x70 (0111 0000)
|
|
uint8_t finBit = aBuffer[0] & 0x80; // 1000 0000
|
|
uint8_t opcode = aBuffer[0] & 0x0F; // 0000 1111
|
|
|
|
if (!finBit) {
|
|
NS_WARNING("We cannot handle multi-fragments messages in Layerscope websocket parser.");
|
|
return NS_OK;
|
|
}
|
|
|
|
// Second byte, data length
|
|
uint8_t maskBit = aBuffer[1] & 0x80; // 1000 0000
|
|
int64_t payloadLength64 = aBuffer[1] & 0x7F; // 0111 1111
|
|
|
|
if (!maskBit) {
|
|
NS_WARNING("Client to Server should set the mask bit");
|
|
return NS_OK;
|
|
}
|
|
|
|
uint32_t framingLength = 2 + 4; // 4 for masks
|
|
|
|
if (payloadLength64 < 126) {
|
|
if (avail < framingLength)
|
|
return NS_OK;
|
|
} else if (payloadLength64 == 126) {
|
|
// 16 bit length field
|
|
framingLength += 2;
|
|
if (avail < framingLength) {
|
|
return NS_OK;
|
|
}
|
|
|
|
payloadLength64 = aBuffer[2] << 8 | aBuffer[3];
|
|
} else {
|
|
// 64 bit length
|
|
framingLength += 8;
|
|
if (avail < framingLength) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aBuffer[2] & 0x80) {
|
|
// Section 4.2 says that the most significant bit MUST be
|
|
// 0. (i.e. this is really a 63 bit value)
|
|
NS_WARNING("High bit of 64 bit length set");
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
|
|
// copy this in case it is unaligned
|
|
payloadLength64 = NetworkEndian::readInt64(aBuffer + 2);
|
|
}
|
|
|
|
uint8_t *payload = aBuffer + framingLength;
|
|
avail -= framingLength;
|
|
|
|
uint32_t payloadLength = static_cast<uint32_t>(payloadLength64);
|
|
if (avail < payloadLength) {
|
|
NS_WARNING("Packet size mismatch the payload length");
|
|
return NS_OK;
|
|
}
|
|
|
|
// Apply mask
|
|
uint32_t mask = NetworkEndian::readUint32(payload - 4);
|
|
ApplyMask(mask, payload, payloadLength);
|
|
|
|
if (opcode == 0x8) {
|
|
// opcode == 0x8 means connection close
|
|
CloseConnection();
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
if (!HandleDataFrame(payload, payloadLength)) {
|
|
NS_WARNING("Cannot decode payload data by the protocol buffer");
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
LayerScopeWebSocketManager::SocketHandler::ApplyMask(uint32_t aMask,
|
|
uint8_t *aData,
|
|
uint64_t aLen)
|
|
{
|
|
if (!aData || aLen == 0) {
|
|
return;
|
|
}
|
|
|
|
// Optimally we want to apply the mask 32 bits at a time,
|
|
// but the buffer might not be alligned. So we first deal with
|
|
// 0 to 3 bytes of preamble individually
|
|
while (aLen && (reinterpret_cast<uintptr_t>(aData) & 3)) {
|
|
*aData ^= aMask >> 24;
|
|
aMask = RotateLeft(aMask, 8);
|
|
aData++;
|
|
aLen--;
|
|
}
|
|
|
|
// perform mask on full words of data
|
|
uint32_t *iData = reinterpret_cast<uint32_t *>(aData);
|
|
uint32_t *end = iData + (aLen >> 2);
|
|
NetworkEndian::writeUint32(&aMask, aMask);
|
|
for (; iData < end; iData++) {
|
|
*iData ^= aMask;
|
|
}
|
|
aMask = NetworkEndian::readUint32(&aMask);
|
|
aData = (uint8_t *)iData;
|
|
aLen = aLen % 4;
|
|
|
|
// There maybe up to 3 trailing bytes that need to be dealt with
|
|
// individually
|
|
while (aLen) {
|
|
*aData ^= aMask >> 24;
|
|
aMask = RotateLeft(aMask, 8);
|
|
aData++;
|
|
aLen--;
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayerScopeWebSocketManager::SocketHandler::HandleDataFrame(uint8_t *aData,
|
|
uint32_t aSize)
|
|
{
|
|
// Handle payload data by protocol buffer
|
|
auto p = MakeUnique<CommandPacket>();
|
|
p->ParseFromArray(static_cast<void*>(aData), aSize);
|
|
|
|
if (!p->has_type()) {
|
|
MOZ_ASSERT(false, "Protocol buffer decoding failed or cannot recongize it");
|
|
return false;
|
|
}
|
|
|
|
switch (p->type()) {
|
|
case CommandPacket::LAYERS_TREE:
|
|
if (p->has_value()) {
|
|
SenderHelper::SetLayersTreeSendable(p->value());
|
|
}
|
|
break;
|
|
|
|
case CommandPacket::LAYERS_BUFFER:
|
|
if (p->has_value()) {
|
|
SenderHelper::SetLayersBufferSendable(p->value());
|
|
}
|
|
break;
|
|
|
|
case CommandPacket::NO_OP:
|
|
default:
|
|
NS_WARNING("Invalid message type");
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
LayerScopeWebSocketManager::SocketHandler::CloseConnection()
|
|
{
|
|
gLayerScopeManager.GetSocketManager()->CleanDebugData();
|
|
if (mInputStream) {
|
|
mInputStream->AsyncWait(nullptr, 0, 0, nullptr);
|
|
mInputStream = nullptr;
|
|
}
|
|
if (mOutputStream) {
|
|
mOutputStream = nullptr;
|
|
}
|
|
if (mTransport) {
|
|
mTransport->Close(NS_BASE_STREAM_CLOSED);
|
|
mTransport = nullptr;
|
|
}
|
|
mConnected = false;
|
|
}
|
|
|
|
// ----------------------------------------------
|
|
// LayerScopeWebSocketManager implementation
|
|
// ----------------------------------------------
|
|
LayerScopeWebSocketManager::LayerScopeWebSocketManager()
|
|
: mHandlerMutex("LayerScopeWebSocketManager::mHandlerMutex")
|
|
{
|
|
NS_NewThread(getter_AddRefs(mDebugSenderThread));
|
|
|
|
mServerSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID);
|
|
int port = gfxPrefs::LayerScopePort();
|
|
mServerSocket->Init(port, false, -1);
|
|
mServerSocket->AsyncListen(new SocketListener);
|
|
}
|
|
|
|
LayerScopeWebSocketManager::~LayerScopeWebSocketManager()
|
|
{
|
|
mServerSocket->Close();
|
|
}
|
|
|
|
void
|
|
LayerScopeWebSocketManager::AppendDebugData(DebugGLData *aDebugData)
|
|
{
|
|
if (!mCurrentSender) {
|
|
mCurrentSender = new DebugDataSender(mDebugSenderThread);
|
|
}
|
|
|
|
mCurrentSender->Append(aDebugData);
|
|
}
|
|
|
|
void
|
|
LayerScopeWebSocketManager::CleanDebugData()
|
|
{
|
|
if (mCurrentSender) {
|
|
mCurrentSender->Cleanup();
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerScopeWebSocketManager::DispatchDebugData()
|
|
{
|
|
MOZ_ASSERT(mCurrentSender.get() != nullptr);
|
|
|
|
mCurrentSender->Send();
|
|
mCurrentSender = nullptr;
|
|
}
|
|
|
|
NS_IMETHODIMP LayerScopeWebSocketManager::SocketListener::OnSocketAccepted(
|
|
nsIServerSocket *aServ,
|
|
nsISocketTransport *aTransport)
|
|
{
|
|
if (!gLayerScopeManager.GetSocketManager())
|
|
return NS_OK;
|
|
|
|
printf_stderr("*** LayerScope: Accepted connection\n");
|
|
gLayerScopeManager.GetSocketManager()->AddConnection(aTransport);
|
|
gLayerScopeManager.GetContentMonitor()->Empty();
|
|
return NS_OK;
|
|
}
|
|
|
|
// ----------------------------------------------
|
|
// LayerScope implementation
|
|
// ----------------------------------------------
|
|
/*static*/
|
|
void
|
|
LayerScope::Init()
|
|
{
|
|
if (!gfxPrefs::LayerScopeEnabled()) {
|
|
return;
|
|
}
|
|
|
|
gLayerScopeManager.CreateServerSocket();
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::DrawBegin()
|
|
{
|
|
if (!CheckSendable()) {
|
|
return;
|
|
}
|
|
|
|
gLayerScopeManager.NewDrawSession();
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::SetRenderOffset(float aX, float aY)
|
|
{
|
|
if (!CheckSendable()) {
|
|
return;
|
|
}
|
|
|
|
gLayerScopeManager.CurrentSession().mOffsetX = aX;
|
|
gLayerScopeManager.CurrentSession().mOffsetY = aY;
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::SetLayerTransform(const gfx::Matrix4x4& aMatrix)
|
|
{
|
|
if (!CheckSendable()) {
|
|
return;
|
|
}
|
|
|
|
gLayerScopeManager.CurrentSession().mMVMatrix = aMatrix;
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::SetDrawRects(size_t aRects,
|
|
const gfx::Rect* aLayerRects,
|
|
const gfx::Rect* aTextureRects)
|
|
{
|
|
if (!CheckSendable()) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(aRects > 0 && aRects <= 4);
|
|
MOZ_ASSERT(aLayerRects);
|
|
|
|
gLayerScopeManager.CurrentSession().mRects = aRects;
|
|
|
|
for (size_t i = 0; i < aRects; i++){
|
|
gLayerScopeManager.CurrentSession().mLayerRects[i] = aLayerRects[i];
|
|
gLayerScopeManager.CurrentSession().mTextureRects[i] = aTextureRects[i];
|
|
}
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::DrawEnd(gl::GLContext* aGLContext,
|
|
const EffectChain& aEffectChain,
|
|
int aWidth,
|
|
int aHeight)
|
|
{
|
|
// Protect this public function
|
|
if (!CheckSendable()) {
|
|
return;
|
|
}
|
|
|
|
// 1. Send textures.
|
|
SenderHelper::SendEffectChain(aGLContext, aEffectChain, aWidth, aHeight);
|
|
|
|
// 2. Send parameters of draw call, such as uniforms and attributes of
|
|
// vertex adnd fragment shader.
|
|
DrawSession& draws = gLayerScopeManager.CurrentSession();
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(
|
|
new DebugGLDrawData(draws.mOffsetX, draws.mOffsetY,
|
|
draws.mMVMatrix, draws.mRects,
|
|
draws.mLayerRects,
|
|
draws.mTextureRects,
|
|
draws.mTexIDs,
|
|
aEffectChain.mLayerRef));
|
|
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::SendLayer(LayerComposite* aLayer,
|
|
int aWidth,
|
|
int aHeight)
|
|
{
|
|
// Protect this public function
|
|
if (!CheckSendable()) {
|
|
return;
|
|
}
|
|
SenderHelper::SendLayer(aLayer, aWidth, aHeight);
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
|
|
{
|
|
// Protect this public function
|
|
if (!CheckSendable() || !SenderHelper::GetLayersTreeSendable()) {
|
|
return;
|
|
}
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(
|
|
new DebugGLLayersData(Move(aPacket)));
|
|
}
|
|
|
|
/*static*/
|
|
bool
|
|
LayerScope::CheckSendable()
|
|
{
|
|
// Only compositor threads check LayerScope status
|
|
MOZ_ASSERT(CompositorParent::IsInCompositorThread() || gIsGtest);
|
|
|
|
if (!gfxPrefs::LayerScopeEnabled()) {
|
|
return false;
|
|
}
|
|
if (!gLayerScopeManager.GetSocketManager()) {
|
|
Init();
|
|
return false;
|
|
}
|
|
if (!gLayerScopeManager.GetSocketManager()->IsConnected()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::CleanLayer()
|
|
{
|
|
if (CheckSendable()) {
|
|
gLayerScopeManager.GetSocketManager()->CleanDebugData();
|
|
}
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::SetHWComposed()
|
|
{
|
|
if (CheckSendable()) {
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(
|
|
new DebugGLMetaData(Packet::META, true));
|
|
}
|
|
}
|
|
|
|
/*static*/
|
|
void
|
|
LayerScope::SetPixelScale(double devPixelsPerCSSPixel)
|
|
{
|
|
gLayerScopeManager.SetPixelScale(devPixelsPerCSSPixel);
|
|
}
|
|
|
|
// ----------------------------------------------
|
|
// LayerScopeAutoFrame implementation
|
|
// ----------------------------------------------
|
|
LayerScopeAutoFrame::LayerScopeAutoFrame(int64_t aFrameStamp)
|
|
{
|
|
// Do Begin Frame
|
|
BeginFrame(aFrameStamp);
|
|
}
|
|
|
|
LayerScopeAutoFrame::~LayerScopeAutoFrame()
|
|
{
|
|
// Do End Frame
|
|
EndFrame();
|
|
}
|
|
|
|
void
|
|
LayerScopeAutoFrame::BeginFrame(int64_t aFrameStamp)
|
|
{
|
|
if (!LayerScope::CheckSendable()) {
|
|
return;
|
|
}
|
|
SenderHelper::ClearTextureIdList();
|
|
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(
|
|
new DebugGLFrameStatusData(Packet::FRAMESTART, aFrameStamp));
|
|
}
|
|
|
|
void
|
|
LayerScopeAutoFrame::EndFrame()
|
|
{
|
|
if (!LayerScope::CheckSendable()) {
|
|
return;
|
|
}
|
|
|
|
gLayerScopeManager.GetSocketManager()->AppendDebugData(
|
|
new DebugGLFrameStatusData(Packet::FRAMEEND));
|
|
gLayerScopeManager.GetSocketManager()->DispatchDebugData();
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|