From 59becbde7577d035f2e9a2a54b94ff2d554e7e73 Mon Sep 17 00:00:00 2001
From: ZiRee <tinacloud@gmx.de>
Date: Thu, 28 Jul 2022 16:12:17 +0000
Subject: GCC11.1 warning: moving a local object in a return statement prevents
 copy elision [-Werror=pessimizing-move]

---
 indra/llcommon/threadsafeschedule.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/llcommon/threadsafeschedule.h b/indra/llcommon/threadsafeschedule.h
index 3e0da94c02..0c3a541196 100644
--- a/indra/llcommon/threadsafeschedule.h
+++ b/indra/llcommon/threadsafeschedule.h
@@ -248,7 +248,7 @@ namespace LL
                 TimePoint until = TimePoint::clock::now() + std::chrono::hours(24);
                 pop_result popped = tryPopUntil_(lock, until, tt);
                 if (popped == POPPED)
-                    return std::move(tt);
+                    return tt;
 
                 // DONE: throw, just as super::pop() does
                 if (popped == DONE)
-- 
cgit v1.2.3


From 97b0b87f7a908750763a4a357be15dc42f8f8cd3 Mon Sep 17 00:00:00 2001
From: ZiRee <tinacloud@gmx.de>
Date: Thu, 28 Jul 2022 16:15:35 +0000
Subject: Creating an LLVector4 from LLColor3 causes an array out of bounds
 read on reading .mV[3]. Doing a detour via LLVector3 fixes this but maybe
 there is a less roundabout way.

---
 indra/newview/llsettingsvo.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp
index 7c762170a7..707b602fc6 100644
--- a/indra/newview/llsettingsvo.cpp
+++ b/indra/newview/llsettingsvo.cpp
@@ -694,8 +694,8 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force)
 
         LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky();
 
-        LLVector4 sunDiffuse = LLVector4(psky->getSunlightColor().mV);
-        LLVector4 moonDiffuse = LLVector4(psky->getMoonlightColor().mV);
+        LLVector4 sunDiffuse = LLVector4(LLVector3(psky->getSunlightColor().mV));
+        LLVector4 moonDiffuse = LLVector4(LLVector3(psky->getMoonlightColor().mV));
 
         shader->uniform4fv(LLShaderMgr::SUNLIGHT_COLOR, sunDiffuse);
         shader->uniform4fv(LLShaderMgr::MOONLIGHT_COLOR, moonDiffuse);
-- 
cgit v1.2.3


From 94cb1ba16c5b301779e437f73af9e06558f15760 Mon Sep 17 00:00:00 2001
From: ZiRee <tinacloud@gmx.de>
Date: Thu, 28 Jul 2022 16:18:30 +0000
Subject: GCC11.1 does not like these being floats, so cast them to U32 before
 using them as array size: error: expression in new-declarator must have
 integral or enumeration type

---
 indra/newview/llvosky.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
index 1aa00bc894..909588367b 100644
--- a/indra/newview/llvosky.cpp
+++ b/indra/newview/llvosky.cpp
@@ -100,8 +100,8 @@ LLSkyTex::LLSkyTex() :
 void LLSkyTex::init(bool isShiny)
 {
     mIsShiny = isShiny;
-	mSkyData = new LLColor4[SKYTEX_RESOLUTION * SKYTEX_RESOLUTION];
-	mSkyDirs = new LLVector3[SKYTEX_RESOLUTION * SKYTEX_RESOLUTION];
+	mSkyData = new LLColor4[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)];
+	mSkyDirs = new LLVector3[(U32)(SKYTEX_RESOLUTION * SKYTEX_RESOLUTION)];
 
 	for (S32 i = 0; i < 2; ++i)
 	{
-- 
cgit v1.2.3


From e980999fb6d76089e224f32668cc4bde8f88dbaa Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Mon, 19 Sep 2022 17:48:55 +0300
Subject: SL-18171 Remove link that opens old help browser

---
 .../skins/default/xui/en/floater_object_weights.xml | 21 +--------------------
 1 file changed, 1 insertion(+), 20 deletions(-)

diff --git a/indra/newview/skins/default/xui/en/floater_object_weights.xml b/indra/newview/skins/default/xui/en/floater_object_weights.xml
index eb283a1043..889efa061c 100644
--- a/indra/newview/skins/default/xui/en/floater_object_weights.xml
+++ b/indra/newview/skins/default/xui/en/floater_object_weights.xml
@@ -2,7 +2,7 @@
 <floater
  can_close="true"
  can_tear_off="false"
- height="315"
+ height="289"
  help_topic="object_weights"
  layout="topleft"
  name="object_weights"
@@ -320,23 +320,4 @@
      top_delta="0"
      value="Total capacity"
      width="130" />
-    <view_border
-     bevel_style="none"
-     follows="top|left"
-     height="0"
-     layout="topleft"
-     left="10"
-     name="land_impacts_text_border"
-     top_pad="5"
-     width="180"/>
-
-    <text
-     follows="left|top"
-     height="16"
-     layout="topleft"
-     left="10"
-     name="help_SLURL"
-     top_pad="10"
-     value="[secondlife:///app/help/object_weights What is all this?...]"
-     width="180" />
 </floater>
-- 
cgit v1.2.3


From 1b31ab5c5279829ace4144c7495ad4f961c2f202 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 14:42:53 -0400
Subject: Introduce a U8* based interface to unzip_llsd and unpackVolumeFaces

---
 indra/llcommon/llsdserialize.cpp | 121 +++++++++++++++++++++------------------
 indra/llcommon/llsdserialize.h   |   6 ++
 indra/llmath/llvolume.cpp        |  20 ++++++-
 indra/llmath/llvolume.h          |   6 +-
 4 files changed, 96 insertions(+), 57 deletions(-)

diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index 8b4a0ee6d8..a510b73096 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -34,6 +34,9 @@
 #include <iostream>
 #include "apr_base64.h"
 
+#include <boost/iostreams/device/array.hpp>
+#include <boost/iostreams/stream.hpp>
+
 #ifdef LL_USESYSTEMLIBS
 # include <zlib.h>
 #else
@@ -2128,7 +2131,9 @@ std::string zip_llsd(LLSD& data)
 		{ //copy result into output
 			if (strm.avail_out >= CHUNK)
 			{
-				free(output);
+				deflateEnd(&strm);
+				if(output)
+					free(output);
 				LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
 				return std::string();
 			}
@@ -2151,7 +2156,9 @@ std::string zip_llsd(LLSD& data)
 		}
 		else 
 		{
-			free(output);
+			deflateEnd(&strm);
+			if(output)
+				free(output);
 			LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
 			return std::string();
 		}
@@ -2162,7 +2169,8 @@ std::string zip_llsd(LLSD& data)
 
 	std::string result((char*) output, size);
 	deflateEnd(&strm);
-	free(output);
+	if(output)
+		free(output);
 
 	return result;
 }
@@ -2171,54 +2179,67 @@ std::string zip_llsd(LLSD& data)
 // not very efficient -- creats a copy of decompressed LLSD block in memory
 // and deserializes from that copy using LLSDSerialize
 LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, S32 size)
+{
+	std::unique_ptr<U8[]> in = std::unique_ptr<U8[]>(new(std::nothrow) U8[size]);
+	if (!in)
+	{
+		return ZR_MEM_ERROR;
+	}
+	is.read((char*) in.get(), size); 
+
+	return unzip_llsd(data, in.get(), size);
+}
+
+LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size)
 {
 	U8* result = NULL;
 	U32 cur_size = 0;
 	z_stream strm;
 		
-	const U32 CHUNK = 65536;
+	constexpr U32 CHUNK = 1024 * 512;
 
-	U8 *in = new(std::nothrow) U8[size];
-	if (!in)
+	static thread_local std::unique_ptr<U8[]> out;
+	if (!out)
 	{
-		return ZR_MEM_ERROR;
+		out = std::unique_ptr<U8[]>(new(std::nothrow) U8[CHUNK]);
 	}
-	is.read((char*) in, size); 
-
-	U8 out[CHUNK];
 		
 	strm.zalloc = Z_NULL;
 	strm.zfree = Z_NULL;
 	strm.opaque = Z_NULL;
 	strm.avail_in = size;
-	strm.next_in = in;
+	strm.next_in = const_cast<U8*>(in);
 
 	S32 ret = inflateInit(&strm);
 	
 	do
 	{
 		strm.avail_out = CHUNK;
-		strm.next_out = out;
+		strm.next_out = out.get();
 		ret = inflate(&strm, Z_NO_FLUSH);
-		if (ret == Z_STREAM_ERROR)
+		switch (ret)
+		{
+		case Z_NEED_DICT:
+		case Z_DATA_ERROR:
 		{
 			inflateEnd(&strm);
 			free(result);
-			delete [] in;
 			return ZR_DATA_ERROR;
 		}
-		
-		switch (ret)
+		case Z_STREAM_ERROR:
+		case Z_BUF_ERROR:
 		{
-		case Z_NEED_DICT:
-			ret = Z_DATA_ERROR;
-		case Z_DATA_ERROR:
+			inflateEnd(&strm);
+			free(result);
+			return ZR_BUFFER_ERROR;
+		}
+
 		case Z_MEM_ERROR:
+		{
 			inflateEnd(&strm);
 			free(result);
-			delete [] in;
 			return ZR_MEM_ERROR;
-			break;
+		}
 		}
 
 		U32 have = CHUNK-strm.avail_out;
@@ -2231,17 +2252,15 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is,
 			{
 				free(result);
 			}
-			delete[] in;
 			return ZR_MEM_ERROR;
 		}
 		result = new_result;
-		memcpy(result+cur_size, out, have);
+		memcpy(result+cur_size, out.get(), have);
 		cur_size += have;
 
-	} while (ret == Z_OK);
+	} while (ret == Z_OK && ret != Z_STREAM_END);
 
 	inflateEnd(&strm);
-	delete [] in;
 
 	if (ret != Z_STREAM_END)
 	{
@@ -2251,37 +2270,11 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is,
 
 	//result now points to the decompressed LLSD block
 	{
-		std::istringstream istr;
-		// Since we are using this for meshes, data we are dealing with tend to be large.
-		// So string can potentially fail to allocate, make sure this won't cause problems
-		try
-		{
-			std::string res_str((char*)result, cur_size);
-
-			std::string deprecated_header("<? LLSD/Binary ?>");
-
-			if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
-			{
-				res_str = res_str.substr(deprecated_header.size() + 1, cur_size);
-			}
-			cur_size = res_str.size();
-
-			istr.str(res_str);
-		}
-#ifdef LL_WINDOWS
-		catch (std::length_error)
-		{
-			free(result);
-			return ZR_SIZE_ERROR;
-		}
-#endif
-		catch (std::bad_alloc&)
-		{
-			free(result);
-			return ZR_MEM_ERROR;
-		}
+		char* result_ptr = strip_deprecated_header((char*)result, cur_size);
 
-		if (!LLSDSerialize::fromBinary(data, istr, cur_size, UNZIP_LLSD_MAX_DEPTH))
+		boost::iostreams::stream<boost::iostreams::array_source> istrm(result_ptr, cur_size);
+		
+		if (!LLSDSerialize::fromBinary(data, istrm, cur_size, UNZIP_LLSD_MAX_DEPTH))
 		{
 			free(result);
 			return ZR_PARSE_ERROR;
@@ -2395,4 +2388,22 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32
 	return result;
 }
 
+char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size)
+{
+	const char* deprecated_header = "<? LLSD/Binary ?>";
+	constexpr size_t deprecated_header_size = 17;
+
+	if (cur_size > deprecated_header_size
+		&& memcmp(in, deprecated_header, deprecated_header_size) == 0)
+	{
+		in = in + deprecated_header_size;
+		cur_size = cur_size - deprecated_header_size;
+		if (header_size)
+		{
+			*header_size = deprecated_header_size + 1;
+		}
+	}
+
+	return in;
+}
 
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
index d6079fd9fa..d33d2b6f34 100644
--- a/indra/llcommon/llsdserialize.h
+++ b/indra/llcommon/llsdserialize.h
@@ -858,9 +858,12 @@ public:
         ZR_SIZE_ERROR,
         ZR_DATA_ERROR,
         ZR_PARSE_ERROR,
+		ZR_BUFFER_ERROR,
+		ZR_VERSION_ERROR
     } EZipRresult;
     // return OK or reason for failure
     static EZipRresult unzip_llsd(LLSD& data, std::istream& is, S32 size);
+	static EZipRresult unzip_llsd(LLSD& data, const U8* in, S32 size);
 };
 
 //dirty little zip functions -- yell at davep
@@ -868,4 +871,7 @@ LL_COMMON_API std::string zip_llsd(LLSD& data);
 
 
 LL_COMMON_API U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize,std::istream& is, S32 size);
+
+// returns a pointer to the array or past the array if the deprecated header exists
+LL_COMMON_API char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size = nullptr);
 #endif // LL_LLSDSERIALIZE_H
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 93f1d508f3..91457fbebe 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2391,7 +2391,25 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 		LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL;
 		return false;
 	}
-	
+	return unpackVolumeFacesInternal(mdl);
+}
+
+bool LLVolume::unpackVolumeFaces(U8* in_data, S32 size)
+{
+	//input data is now pointing at a zlib compressed block of LLSD
+	//decompress block
+	LLSD mdl;
+	U32 uzip_result = LLUZipHelper::unzip_llsd(mdl, in_data, size);
+	if (uzip_result != LLUZipHelper::ZR_OK)
+	{
+		LL_DEBUGS("MeshStreaming") << "Failed to unzip LLSD blob for LoD with code " << uzip_result << " , will probably fetch from sim again." << LL_ENDL;
+		return false;
+	}
+	return unpackVolumeFacesInternal(mdl);
+}
+
+bool LLVolume::unpackVolumeFacesInternal(const LLSD& mdl)
+{
 	{
 		U32 face_count = mdl.size();
 
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 9697952f5b..b53b0fd1cc 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -1096,8 +1096,12 @@ protected:
 	BOOL generate();
 	void createVolumeFaces();
 public:
-	virtual bool unpackVolumeFaces(std::istream& is, S32 size);
+	bool unpackVolumeFaces(std::istream& is, S32 size);
+	bool unpackVolumeFaces(U8* in_data, S32 size);
+private:
+	bool unpackVolumeFacesInternal(const LLSD& mdl);
 
+public:
 	virtual void setMeshAssetLoaded(BOOL loaded);
 	virtual BOOL isMeshAssetLoaded();
 
-- 
cgit v1.2.3


From 4be11d87b5941b0a2159f62ab40500945cc873d5 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 14:44:19 -0400
Subject: Utilize pointer based unzip_llsd and unpackVolumeFaces in meshrepo
 and materialmgr

---
 indra/newview/llmaterialmgr.cpp    | 18 +++++--------
 indra/newview/llmeshrepository.cpp | 52 ++++----------------------------------
 2 files changed, 11 insertions(+), 59 deletions(-)

diff --git a/indra/newview/llmaterialmgr.cpp b/indra/newview/llmaterialmgr.cpp
index 11aa607393..6e1e6506d9 100644
--- a/indra/newview/llmaterialmgr.cpp
+++ b/indra/newview/llmaterialmgr.cpp
@@ -429,12 +429,10 @@ void LLMaterialMgr::onGetResponse(bool success, const LLSD& content, const LLUUI
 	llassert(content.has(MATERIALS_CAP_ZIP_FIELD));
 	llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary());
 
-	LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
-	std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size());
-	std::istringstream content_stream(content_string);
+	const LLSD::Binary& content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
 
 	LLSD response_data;
-	U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_stream, content_binary.size());
+	U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_binary.data(), content_binary.size());
 	if (uzip_result != LLUZipHelper::ZR_OK)
 	{
 		LL_WARNS("Materials") << "Cannot unzip LLSD binary content: " << uzip_result << LL_ENDL;
@@ -472,12 +470,10 @@ void LLMaterialMgr::onGetAllResponse(bool success, const LLSD& content, const LL
 	llassert(content.has(MATERIALS_CAP_ZIP_FIELD));
 	llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary());
 
-	LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
-	std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size());
-	std::istringstream content_stream(content_string);
+	const LLSD::Binary& content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
 
 	LLSD response_data;
-	U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_stream, content_binary.size());
+	U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_binary.data(), content_binary.size());
 	if (uzip_result != LLUZipHelper::ZR_OK)
 	{
 		LL_WARNS("Materials") << "Cannot unzip LLSD binary content: " << uzip_result << LL_ENDL;
@@ -541,12 +537,10 @@ void LLMaterialMgr::onPutResponse(bool success, const LLSD& content)
 	llassert(content.has(MATERIALS_CAP_ZIP_FIELD));
 	llassert(content[MATERIALS_CAP_ZIP_FIELD].isBinary());
 
-	LLSD::Binary content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
-	std::string content_string(reinterpret_cast<const char*>(content_binary.data()), content_binary.size());
-	std::istringstream content_stream(content_string);
+	const LLSD::Binary& content_binary = content[MATERIALS_CAP_ZIP_FIELD].asBinary();
 
 	LLSD response_data;
-	U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_stream, content_binary.size());
+	U32 uzip_result = LLUZipHelper::unzip_llsd(response_data, content_binary.data(), content_binary.size());
 	if (uzip_result != LLUZipHelper::ZR_OK)
 	{
 		LL_WARNS("Materials") << "Cannot unzip LLSD binary content: " << uzip_result << LL_ENDL;
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 4dd0543693..e1ff233354 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1911,19 +1911,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
 	}
 
 	LLPointer<LLVolume> volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod));
-	std::istringstream stream;
-	try
-	{
-		std::string mesh_string((char*)data, data_size);
-		stream.str(mesh_string);
-	}
-	catch (std::bad_alloc&)
-	{
-		// out of memory, we won't be able to process this mesh
-		return MESH_OUT_OF_MEMORY;
-	}
-
-	if (volume->unpackVolumeFaces(stream, data_size))
+	if (volume->unpackVolumeFaces(data, data_size))
 	{
 		if (volume->getNumFaces() > 0)
 		{
@@ -1953,10 +1941,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 	{
         try
         {
-            std::string res_str((char*)data, data_size);
-            std::istringstream stream(res_str);
-
-            U32 uzip_result = LLUZipHelper::unzip_llsd(skin, stream, data_size);
+            U32 uzip_result = LLUZipHelper::unzip_llsd(skin, data, data_size);
             if (uzip_result != LLUZipHelper::ZR_OK)
             {
                 LL_WARNS(LOG_MESH) << "Mesh skin info parse error.  Not a valid mesh asset!  ID:  " << mesh_id
@@ -1994,10 +1979,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
     {
         try
         {
-            std::string res_str((char*)data, data_size);
-            std::istringstream stream(res_str);
-
-            U32 uzip_result = LLUZipHelper::unzip_llsd(decomp, stream, data_size);
+            U32 uzip_result = LLUZipHelper::unzip_llsd(decomp, data, data_size);
             if (uzip_result != LLUZipHelper::ZR_OK)
             {
                 LL_WARNS(LOG_MESH) << "Mesh decomposition parse error.  Not a valid mesh asset!  ID:  " << mesh_id
@@ -2006,7 +1988,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3
                 return false;
             }
         }
-        catch (std::bad_alloc&)
+        catch (const std::bad_alloc&)
         {
             LL_WARNS(LOG_MESH) << "Out of memory for mesh ID " << mesh_id << " of size: " << data_size << LL_ENDL;
             return false;
@@ -2043,32 +2025,8 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_
 		volume_params.setSculptID(mesh_id, LL_SCULPT_TYPE_MESH);
 		LLPointer<LLVolume> volume = new LLVolume(volume_params,0);
 
-        std::istringstream stream;
-        try
-        {
-            std::string mesh_string((char*)data, data_size);
-            stream.str(mesh_string);
-        }
-        catch (std::bad_alloc&)
-        {
-            // out of memory, we won't be able to process this mesh
-            delete d;
-            return MESH_OUT_OF_MEMORY;
-        }
-
-		if (volume->unpackVolumeFaces(stream, data_size))
+		if (volume->unpackVolumeFaces(data, data_size))
 		{
-			//load volume faces into decomposition buffer
-			S32 vertex_count = 0;
-			S32 index_count = 0;
-
-			for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
-			{
-				const LLVolumeFace& face = volume->getVolumeFace(i);
-				vertex_count += face.mNumVertices;
-				index_count += face.mNumIndices;
-			}
-
 			d->mPhysicsShapeMesh.clear();
 
 			std::vector<LLVector3>& pos = d->mPhysicsShapeMesh.mPositions;
-- 
cgit v1.2.3


From e097794919bff8456da498382b250891ab5235a0 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 14:48:19 -0400
Subject: Optimize mesh header size and data access throughout meshrepo

---
 indra/newview/llmeshrepository.cpp | 99 +++++++++++++++++++++-----------------
 indra/newview/llmeshrepository.h   |  4 +-
 2 files changed, 55 insertions(+), 48 deletions(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index e1ff233354..968d2ec8be 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -138,7 +138,7 @@
 //                               data copied
 //                               headerReceived() invoked
 //                                 LLSD parsed
-//                                 mMeshHeader, mMeshHeaderSize updated
+//                                 mMeshHeader updated
 //                                 scan mPendingLOD for LOD request
 //                                 push LODRequest to mLODReqQ
 //                             ...
@@ -246,7 +246,6 @@
 //     sActiveLODRequests       mMutex        rw.any.mMutex, ro.repo.none [1]
 //     sMaxConcurrentRequests   mMutex        wo.main.none, ro.repo.none, ro.main.mMutex
 //     mMeshHeader              mHeaderMutex  rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0]
-//     mMeshHeaderSize          mHeaderMutex  rw.repo.mHeaderMutex
 //     mSkinRequests            mMutex        rw.repo.mMutex, ro.repo.none [5]
 //     mSkinInfoQ               mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0])
 //     mDecompositionRequests   mMutex        rw.repo.mMutex, ro.repo.none [5]
@@ -1178,10 +1177,13 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32
 
 void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 { //could be called from any thread
+	const LLUUID& mesh_id = mesh_params.getSculptID();
 	LLMutexLock lock(mMutex);
-	mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID());
+	LLMutexLock header_lock(mHeaderMutex);
+	mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
 	if (iter != mMeshHeader.end())
 	{ //if we have the header, request LOD byte range
+
 		LODRequest req(mesh_params, lod);
 		{
 			mLODReqQ.push(req);
@@ -1317,7 +1319,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 
 	mHeaderMutex->lock();
 
-	if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+	auto header_it = mMeshHeader.find(mesh_id);
+	if (header_it == mMeshHeader.end())
 	{ //we have no header info for this mesh, do nothing
 		mHeaderMutex->unlock();
 		return false;
@@ -1325,13 +1328,14 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 
 	++LLMeshRepository::sMeshRequestCount;
 	bool ret = true;
-	U32 header_size = mMeshHeaderSize[mesh_id];
+	U32 header_size = header_it->second.first;
 	
 	if (header_size > 0)
 	{
-		S32 version = mMeshHeader[mesh_id]["version"].asInteger();
-		S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger();
-		S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger();
+		const LLSD& header = header_it->second.second;
+		S32 version = header["version"].asInteger();
+		S32 offset = header_size + header["skin"]["offset"].asInteger();
+		S32 size = header["skin"]["size"].asInteger();
 
 		mHeaderMutex->unlock();
 
@@ -1413,21 +1417,23 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)
 
 	mHeaderMutex->lock();
 
-	if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+	auto header_it = mMeshHeader.find(mesh_id);
+	if (header_it == mMeshHeader.end())
 	{ //we have no header info for this mesh, do nothing
 		mHeaderMutex->unlock();
 		return false;
 	}
 
 	++LLMeshRepository::sMeshRequestCount;
-	U32 header_size = mMeshHeaderSize[mesh_id];
+	U32 header_size = header_it->second.first;
 	bool ret = true;
 	
 	if (header_size > 0)
 	{
-		S32 version = mMeshHeader[mesh_id]["version"].asInteger();
-		S32 offset = header_size + mMeshHeader[mesh_id]["physics_convex"]["offset"].asInteger();
-		S32 size = mMeshHeader[mesh_id]["physics_convex"]["size"].asInteger();
+		const auto& header = header_it->second.second;
+		S32 version = header["version"].asInteger();
+		S32 offset = header_size + header["physics_convex"]["offset"].asInteger();
+		S32 size = header["physics_convex"]["size"].asInteger();
 
 		mHeaderMutex->unlock();
 
@@ -1510,21 +1516,23 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)
 
 	mHeaderMutex->lock();
 
-	if (mMeshHeader.find(mesh_id) == mMeshHeader.end())
+	auto header_it = mMeshHeader.find(mesh_id);
+	if (header_it == mMeshHeader.end())
 	{ //we have no header info for this mesh, do nothing
 		mHeaderMutex->unlock();
 		return false;
 	}
 
 	++LLMeshRepository::sMeshRequestCount;
-	U32 header_size = mMeshHeaderSize[mesh_id];
+	U32 header_size = header_it->second.first;
 	bool ret = true;
 
 	if (header_size > 0)
 	{
-		S32 version = mMeshHeader[mesh_id]["version"].asInteger();
-		S32 offset = header_size + mMeshHeader[mesh_id]["physics_mesh"]["offset"].asInteger();
-		S32 size = mMeshHeader[mesh_id]["physics_mesh"]["size"].asInteger();
+		const auto& header = header_it->second.second;
+		S32 version = header["version"].asInteger();
+		S32 offset = header_size + header["physics_mesh"]["offset"].asInteger();
+		S32 size = header["physics_mesh"]["size"].asInteger();
 
 		mHeaderMutex->unlock();
 
@@ -1704,20 +1712,25 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 		return false;
 	}
 
-	mHeaderMutex->lock();
+	const LLUUID& mesh_id = mesh_params.getSculptID();
 
+	mHeaderMutex->lock();
+	auto header_it = mMeshHeader.find(mesh_id);
+	if (header_it == mMeshHeader.end())
+	{ //we have no header info for this mesh, do nothing
+		mHeaderMutex->unlock();
+		return false;
+	}
 	++LLMeshRepository::sMeshRequestCount;
 	bool retval = true;
-
-	LLUUID mesh_id = mesh_params.getSculptID();
 	
-	U32 header_size = mMeshHeaderSize[mesh_id];
-
+	U32 header_size = header_it->second.first;
 	if (header_size > 0)
 	{
-		S32 version = mMeshHeader[mesh_id]["version"].asInteger();
-		S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger();
-		S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger();
+		const auto& header = header_it->second.second;
+		S32 version = header["version"].asInteger();
+		S32 offset = header_size + header[header_lod[lod]]["offset"].asInteger();
+		S32 size = header[header_lod[lod]]["size"].asInteger();
 		mHeaderMutex->unlock();
 				
 		if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0)
@@ -1878,8 +1891,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
 		
 		{
 			LLMutexLock lock(mHeaderMutex);
-			mMeshHeaderSize[mesh_id] = header_size;
-			mMeshHeader[mesh_id] = header;
+			mMeshHeader[mesh_id] = { header_size, header };
             LLMeshRepository::sCacheBytesHeaders += header_size;
 		}
 
@@ -2928,7 +2940,7 @@ S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo
 
 	if (iter != mMeshHeader.end())
 	{
-		LLSD& header = iter->second;
+		LLSD& header = iter->second.second;
 
 		return LLMeshRepository::getActualMeshLOD(header, lod);
 	}
@@ -3173,8 +3185,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
 		LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id);
 		if (iter != gMeshRepo.mThread->mMeshHeader.end())
 		{
-			header_bytes = (S32)gMeshRepo.mThread->mMeshHeaderSize[mesh_id];
-			header = iter->second;
+			header_bytes = (S32)iter->second.first;
+			header = iter->second.second;
 		}
 
 		if (header_bytes > 0
@@ -4142,16 +4154,13 @@ bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)
 bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id)
 {
     LLMutexLock lock(mHeaderMutex);
-    if (mMeshHeaderSize[mesh_id] > 0)
+    mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
+    if (iter != mMeshHeader.end() && iter->second.first > 0)
     {
-        mesh_header_map::iterator iter = mMeshHeader.find(mesh_id);
-        if (iter != mMeshHeader.end())
+        LLSD &mesh = iter->second.second;
+        if (mesh.has("physics_mesh") && mesh["physics_mesh"].has("size") && (mesh["physics_mesh"]["size"].asInteger() > 0))
         {
-            LLSD &mesh = iter->second;
-            if (mesh.has("physics_mesh") && mesh["physics_mesh"].has("size") && (mesh["physics_mesh"]["size"].asInteger() > 0))
-            {
-                return true;
-            }
+            return true;
         }
     }
 
@@ -4176,9 +4185,9 @@ S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
 	{
 		LLMutexLock lock(mThread->mHeaderMutex);
 		LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
-		if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)
+		if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
 		{
-			LLSD& header = iter->second;
+			const LLSD& header = iter->second.second;
 
 			if (header.has("404"))
 			{
@@ -4282,9 +4291,9 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by
     {
         LLMutexLock lock(mThread->mHeaderMutex);
         LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
-        if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)
+        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
         {
-            result  = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);
+            result  = getStreamingCostLegacy(iter->second.second, radius, bytes, bytes_visible, lod, unscaled_value);
         }
     }
     if (result > 0.f)
@@ -4597,9 +4606,9 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data)
     {
         LLMutexLock lock(mThread->mHeaderMutex);
         LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
-        if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)
+        if (iter != mThread->mMeshHeader.end() && iter->second.first > 0)
         {
-            LLSD& header = iter->second;
+            LLSD& header = iter->second.second;
 
             bool header_invalid = (header.has("404")
                                    || !header.has("lowest_lod")
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index f61da3e571..6f43b0aa83 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -210,10 +210,8 @@ public:
 	LLCondition* mSignal;
 
 	//map of known mesh headers
-	typedef std::map<LLUUID, LLSD> mesh_header_map;
+	typedef boost::unordered_map<LLUUID, std::pair<U32, LLSD>> mesh_header_map; // pair is header_size and data
 	mesh_header_map mMeshHeader;
-	
-	std::map<LLUUID, U32> mMeshHeaderSize;
 
 	class HeaderRequest : public RequestStats
 	{ 
-- 
cgit v1.2.3


From 7dcdc4c37823c2db86454d3ba142f09b0475b4d8 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 14:54:42 -0400
Subject: Replaced usage of LLVolumeParams as map key in meshrepo with the mesh
 uuid

---
 indra/newview/llmeshrepository.cpp | 47 ++++++++++++++++++++++----------------
 indra/newview/llmeshrepository.h   |  4 ++--
 2 files changed, 29 insertions(+), 22 deletions(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 968d2ec8be..d5b14dc6aa 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1193,8 +1193,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 	else
 	{ 
 		HeaderRequest req(mesh_params);
-		
-		pending_lod_map::iterator pending = mPendingLOD.find(mesh_params);
+		pending_lod_map::iterator pending = mPendingLOD.find(mesh_id);
 
 		if (pending != mPendingLOD.end())
 		{ //append this lod request to existing header request
@@ -1204,7 +1203,7 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)
 		else
 		{ //if no header request is pending, fetch header
 			mHeaderReqQ.push(req);
-			mPendingLOD[mesh_params].push_back(lod);
+			mPendingLOD[mesh_id].push_back(lod);
 		}
 	}
 }
@@ -1805,16 +1804,19 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 				}
 				else
 				{
+					LLMutexLock lock(mMutex);
 					mUnavailableQ.push(LODRequest(mesh_params, lod));
 				}
 			}
 			else
 			{
+				LLMutexLock lock(mMutex);
 				mUnavailableQ.push(LODRequest(mesh_params, lod));
 			}
 		}
 		else
 		{
+			LLMutexLock lock(mMutex);
 			mUnavailableQ.push(LODRequest(mesh_params, lod));
 		}
 	}
@@ -1899,7 +1901,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
 		LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time.
 
 		//check for pending requests
-		pending_lod_map::iterator iter = mPendingLOD.find(mesh_params);
+		pending_lod_map::iterator iter = mPendingLOD.find(mesh_id);
 		if (iter != mPendingLOD.end())
 		{
 			for (U32 i = 0; i < iter->second.size(); ++i)
@@ -3605,15 +3607,19 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 	{
 		LLMutexLock lock(mMeshMutex);
 		//add volume to list of loading meshes
-		mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params);
+		const auto& mesh_id = mesh_params.getSculptID();
+		mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id);
 		if (iter != mLoadingMeshes[detail].end())
 		{ //request pending for this mesh, append volume id to list
-			iter->second.insert(vobj->getID());
+			auto it = std::find(iter->second.begin(), iter->second.end(), vobj->getID());
+			if (it == iter->second.end()) {
+				iter->second.push_back(vobj->getID());
+			}
 		}
 		else
 		{
 			//first request for this mesh
-			mLoadingMeshes[detail][mesh_params].insert(vobj->getID());
+			mLoadingMeshes[detail][mesh_id].push_back(vobj->getID());
 			mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
 			LLMeshRepository::sLODPending++;
 		}
@@ -3840,7 +3846,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 					for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)
 					{
 						F32 max_score = 0.f;
-						for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+						for (std::vector<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
 						{
 							LLViewerObject* object = gObjectList.findObject(*obj_iter);
 							
@@ -3855,7 +3861,7 @@ void LLMeshRepository::notifyLoadedMeshes()
 							}
 						}
 				
-						score_map[iter->first.getSculptID()] = max_score;
+						score_map[iter->first] = max_score;
 					}
 				}
 
@@ -3953,14 +3959,15 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 	S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());
 
 	//get list of objects waiting to be notified this mesh is loaded
-	mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params);
+	const auto& mesh_id = mesh_params.getSculptID();
+	mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_id);
 
 	if (volume && obj_iter != mLoadingMeshes[detail].end())
 	{
 		//make sure target volume is still valid
 		if (volume->getNumVolumeFaces() <= 0)
 		{
-			LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume.  ID:  " << mesh_params.getSculptID()
+			LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume.  ID:  " << mesh_id
 							   << LL_ENDL;
 		}
 		
@@ -3974,13 +3981,13 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 			}
 			else
 			{
-				LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_params.getSculptID()
+				LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_id
 								   << LL_ENDL;
 			}
 		}
 
 		//notify waiting LLVOVolume instances that their requested mesh is available
-		for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		for (std::vector<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
 		{
 			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
 			if (vobj)
@@ -3989,20 +3996,20 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 			}
 		}
 		
-		mLoadingMeshes[detail].erase(mesh_params);
+		mLoadingMeshes[detail].erase(obj_iter);
 	}
 }
 
 void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)
 { //called from main thread
 	//get list of objects waiting to be notified this mesh is loaded
-	mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params);
-
-	F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
-
+	const auto& mesh_id = mesh_params.getSculptID();
+	mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id);
 	if (obj_iter != mLoadingMeshes[lod].end())
 	{
-		for (std::set<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
+
+		for (std::vector<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
 		{
 			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
 			if (vobj)
@@ -4018,7 +4025,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
 			}
 		}
 		
-		mLoadingMeshes[lod].erase(mesh_params);
+		mLoadingMeshes[lod].erase(obj_iter);
 	}
 }
 
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 6f43b0aa83..2cade8f01e 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -308,7 +308,7 @@ public:
 	std::queue<LoadedMesh> mLoadedQ;
 
 	//map of pending header requests and currently desired LODs
-	typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map;
+	typedef boost::unordered_map<LLUUID, std::vector<S32> > pending_lod_map;
 	pending_lod_map mPendingLOD;
 
 	// llcorehttp library interface objects.
@@ -611,7 +611,7 @@ public:
 	static void metricsProgress(unsigned int count);
 	static void metricsUpdate();
 	
-	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
+	typedef boost::unordered_map<LLUUID, std::vector<LLUUID> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
 	
 	typedef std::unordered_map<LLUUID, LLMeshSkinInfo> skin_map;
-- 
cgit v1.2.3


From e83146ce0c4310c7a0c03a10b562e7317df034f6 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 16:26:18 -0400
Subject: Optimize away constant map finds in getSkinInfo by caching mesh skin
 into in vovolume

---
 indra/llprimitive/llmodel.cpp      | 10 ++++++
 indra/llprimitive/llmodel.h        |  3 +-
 indra/newview/llmeshrepository.cpp | 71 +++++++++++++++++++++++++++++++-------
 indra/newview/llmeshrepository.h   |  9 +++--
 indra/newview/llvovolume.cpp       | 36 ++++++++++++++++++-
 indra/newview/llvovolume.h         |  4 +++
 6 files changed, 116 insertions(+), 17 deletions(-)

diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp
index 555164f3b0..444d9a5366 100644
--- a/indra/llprimitive/llmodel.cpp
+++ b/indra/llprimitive/llmodel.cpp
@@ -1389,6 +1389,16 @@ LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin):
 	fromLLSD(skin);
 }
 
+LLMeshSkinInfo::LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& skin) :
+	mMeshID(mesh_id),
+	mPelvisOffset(0.0),
+	mLockScaleIfJointPosition(false),
+	mInvalidJointsScrubbed(false),
+	mJointNumsInitialized(false)
+{
+	fromLLSD(skin);
+}
+
 void LLMeshSkinInfo::fromLLSD(LLSD& skin)
 {
 	if (skin.has("joint_names"))
diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h
index a6ab96ab18..9c99bb75a0 100644
--- a/indra/llprimitive/llmodel.h
+++ b/indra/llprimitive/llmodel.h
@@ -41,12 +41,13 @@ class domMesh;
 #define MAX_MODEL_FACES 8
 
 LL_ALIGN_PREFIX(16)
-class LLMeshSkinInfo 
+class LLMeshSkinInfo : public LLRefCount
 {
     LL_ALIGN_NEW
 public:
 	LLMeshSkinInfo();
 	LLMeshSkinInfo(LLSD& data);
+	LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& data);
 	void fromLLSD(LLSD& data);
 	LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const;
     void updateHash();
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index d5b14dc6aa..ced64b655e 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -1972,8 +1972,16 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat
 	}
 	
 	{
-		LLMeshSkinInfo info(skin);
-		info.mMeshID = mesh_id;
+		LLMeshSkinInfo* info = nullptr;
+		try
+		{
+			info = new LLMeshSkinInfo(mesh_id, skin);
+		}
+		catch (const std::bad_alloc& ex)
+		{
+			LL_WARNS() << "Failed to allocate skin info with exception: " << ex.what()  << LL_ENDL;
+			return false;
+		}
 
         // LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL;
 		{
@@ -2898,7 +2906,7 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 	{
 		if (mMutex->trylock())
 		{
-			std::list<LLMeshSkinInfo> skin_info_q;
+			std::list<LLMeshSkinInfo*> skin_info_q;
 			std::list<LLModel::Decomposition*> decomp_q;
 
 			if (! mSkinInfoQ.empty())
@@ -3491,7 +3499,7 @@ LLMeshRepository::LLMeshRepository()
   mMeshThreadCount(0),
   mThread(NULL)
 {
-
+	mSkinInfoCullTimer.resetWithExpiry(10.f);
 }
 
 void LLMeshRepository::init()
@@ -3776,6 +3784,28 @@ void LLMeshRepository::notifyLoadedMeshes()
 	//call completed callbacks on finished decompositions
 	mDecompThread->notifyCompleted();
 
+	if (mSkinInfoCullTimer.checkExpirationAndReset(10.f)) 
+	{
+		//// Clean up dead skin info
+		//U64Bytes skinbytes(0);
+		for (auto iter = mSkinMap.begin(), ender = mSkinMap.end(); iter != ender;)
+		{
+			auto copy_iter = iter++;
+
+			//skinbytes += U64Bytes(sizeof(LLMeshSkinInfo));
+			//skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(std::string));
+			//skinbytes += U64Bytes(copy_iter->second->mJointNums.size() * sizeof(S32));
+			//skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4a));
+			//skinbytes += U64Bytes(copy_iter->second->mJointNames.size() * sizeof(LLMatrix4));
+
+			if (copy_iter->second->getNumRefs() == 1)
+			{
+				mSkinMap.erase(copy_iter);
+			}
+		}
+		//LL_INFOS() << "Skin info cache elements:" << mSkinMap.size() << " Memory: " << U64Kilobytes(skinbytes) << LL_ENDL;
+	}
+
 	// For major operations, attempt to get the required locks
 	// without blocking and punt if they're not available.  The
 	// longest run of holdoffs is kept in sMaxLockHoldoffs just
@@ -3913,13 +3943,13 @@ void LLMeshRepository::notifyLoadedMeshes()
 	mThread->mSignal->signal();
 }
 
-void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
+void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info)
 {
-	mSkinMap[info.mMeshID] = info;
+	mSkinMap[info->mMeshID] = info; // Cache into LLPointer
     // Alternative: We can get skin size from header
-    sCacheBytesSkins += info.sizeBytes();
+    sCacheBytesSkins += info->sizeBytes();
 
-	skin_load_map::iterator iter = mLoadingSkins.find(info.mMeshID);
+	skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID);
 	if (iter != mLoadingSkins.end())
 	{
 		for (std::set<LLUUID>::iterator obj_id = iter->second.begin(); obj_id != iter->second.end(); ++obj_id)
@@ -3927,10 +3957,27 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)
 			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*obj_id);
 			if (vobj)
 			{
-				vobj->notifyMeshLoaded();
+				vobj->notifySkinInfoLoaded(info);
+			}
+		}
+		mLoadingSkins.erase(iter);
+	}
+}
+
+void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id)
+{
+	skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
+	if (iter != mLoadingSkins.end())
+	{
+		for (std::set<LLUUID>::iterator obj_id = iter->second.begin(); obj_id != iter->second.end(); ++obj_id)
+		{
+			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*obj_id);
+			if (vobj)
+			{
+				vobj->notifySkinInfoUnavailable();
 			}
 		}
-		mLoadingSkins.erase(info.mMeshID);
+		mLoadingSkins.erase(iter);
 	}
 }
 
@@ -4042,7 +4089,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const
         skin_map::iterator iter = mSkinMap.find(mesh_id);
         if (iter != mSkinMap.end())
         {
-            return &(iter->second);
+            return iter->second;
         }
 
         //no skin info known about given mesh, try to fetch it
@@ -4058,7 +4105,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const
             mLoadingSkins[mesh_id].insert(requesting_obj->getID());
         }
     }
-	return NULL;
+	return nullptr;
 }
 
 void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 2cade8f01e..a5b985ee76 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -284,7 +284,7 @@ public:
 	std::set<UUIDBasedRequest> mSkinRequests;
 	
 	// list of completed skin info requests
-	std::list<LLMeshSkinInfo> mSkinInfoQ;
+	std::list<LLMeshSkinInfo*> mSkinInfoQ;
 
 	//set of requested decompositions
 	std::set<UUIDBasedRequest> mDecompositionRequests;
@@ -581,7 +581,8 @@ public:
 	void notifyLoadedMeshes();
 	void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume);
 	void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod);
-	void notifySkinInfoReceived(LLMeshSkinInfo& info);
+	void notifySkinInfoReceived(LLMeshSkinInfo* info);
+	void notifySkinInfoUnavailable(const LLUUID& info);
 	void notifyDecompositionReceived(LLModel::Decomposition* info);
 
 	S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
@@ -614,7 +615,7 @@ public:
 	typedef boost::unordered_map<LLUUID, std::vector<LLUUID> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
 	
-	typedef std::unordered_map<LLUUID, LLMeshSkinInfo> skin_map;
+	typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map;
 	skin_map mSkinMap;
 
 	typedef std::map<LLUUID, LLModel::Decomposition*> decomposition_map;
@@ -650,6 +651,8 @@ public:
 	std::vector<LLMeshUploadThread*> mUploadWaitList;
 
 	LLPhysicsDecomp* mDecompThread;
+
+	LLFrameTimer     mSkinInfoCullTimer;
 	
 	class inventory_data
 	{
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index f4a938e57d..d5583f05f3 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -228,6 +228,9 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
     mColorChanged = FALSE;
 	mSpotLightPriority = 0.f;
 
+	mSkinInfoFailed = false;
+	mSkinInfo = NULL;
+
 	mMediaImplList.resize(getNumTEs());
 	mLastFetchedMediaVersion = -1;
     mServerDrawableUpdateCount = 0;
@@ -1095,6 +1098,12 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
 			// if it's a mesh
 			if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
 			{
+				if (mSkinInfo && mSkinInfo->mMeshID != volume_params.getSculptID())
+				{
+					mSkinInfo = NULL;
+					mSkinInfoFailed = false;
+				}
+
 				if (!getVolume()->isMeshAssetLoaded())
 				{ 
 					//load request not yet issued, request pipeline load this mesh
@@ -1106,6 +1115,14 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
 					}
 				}
 				
+				if (!mSkinInfo && !mSkinInfoFailed)
+				{
+					const LLMeshSkinInfo* skin_info = gMeshRepo.getSkinInfo(volume_params.getSculptID(), this);
+					if (skin_info)
+					{
+						notifySkinInfoLoaded(skin_info);
+					}
+				}
 			}
 			else // otherwise is sculptie
 			{
@@ -1158,6 +1175,9 @@ void LLVOVolume::updateSculptTexture()
 		{
 			mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
 		}
+
+		mSkinInfoFailed = false;
+		mSkinInfo = NULL;
 	}
 	else
 	{
@@ -1212,6 +1232,20 @@ void LLVOVolume::notifyMeshLoaded()
     updateVisualComplexity();
 }
 
+void LLVOVolume::notifySkinInfoLoaded(const LLMeshSkinInfo* skin)
+{
+	mSkinInfoFailed = false;
+	mSkinInfo = skin;
+
+	notifyMeshLoaded();
+}
+
+void LLVOVolume::notifySkinInfoUnavailable()
+{
+	mSkinInfoFailed = true;
+	mSkinInfo = nullptr;
+}
+
 // sculpt replaces generate() for sculpted surfaces
 void LLVOVolume::sculpt()
 {	
@@ -3645,7 +3679,7 @@ const LLMeshSkinInfo* LLVOVolume::getSkinInfo() const
 {
     if (getVolume())
     {
-        return gMeshRepo.getSkinInfo(getMeshID(), this);
+         return mSkinInfo;
     }
     else
     {
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 4136c13315..12681e2bd9 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -350,6 +350,8 @@ public:
     void updateVisualComplexity();
     
 	void notifyMeshLoaded();
+	void notifySkinInfoLoaded(const LLMeshSkinInfo* skin);
+	void notifySkinInfoUnavailable();
 	
 	// Returns 'true' iff the media data for this object is in flight
 	bool isMediaDataBeingFetched() const;
@@ -433,6 +435,8 @@ private:
 
 	LLPointer<LLRiggedVolume> mRiggedVolume;
 
+	bool mSkinInfoFailed;
+	LLConstPointer<LLMeshSkinInfo> mSkinInfo;
 	// statics
 public:
 	static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop
-- 
cgit v1.2.3


From bd20b61b8261623d75bbb22e7d368743d7bd971a Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 17:14:19 -0400
Subject: Optimize away gObjectList finds during mesh load

---
 indra/newview/llmeshrepository.cpp | 55 +++++++++++++++++++++++++-------------
 indra/newview/llmeshrepository.h   |  7 ++---
 indra/newview/llvovolume.cpp       |  2 ++
 3 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index ced64b655e..ed942eca31 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -3600,6 +3600,22 @@ S32 LLMeshRepository::update()
 	return size ;
 }
 
+void LLMeshRepository::unregisterMesh(LLVOVolume* vobj)
+{
+	for (auto& lod : mLoadingMeshes)
+	{
+		for (auto& param : lod)
+		{
+			vector_replace_with_last(param.second, vobj);
+		}
+	}
+
+	for (auto& skin_pair : mLoadingSkins)
+	{
+		vector_replace_with_last(skin_pair.second, vobj);
+	}
+}
+
 S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH);
@@ -3619,15 +3635,15 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
 		mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id);
 		if (iter != mLoadingMeshes[detail].end())
 		{ //request pending for this mesh, append volume id to list
-			auto it = std::find(iter->second.begin(), iter->second.end(), vobj->getID());
+			auto it = std::find(iter->second.begin(), iter->second.end(), vobj);
 			if (it == iter->second.end()) {
-				iter->second.push_back(vobj->getID());
+				iter->second.push_back(vobj);
 			}
 		}
 		else
 		{
 			//first request for this mesh
-			mLoadingMeshes[detail][mesh_id].push_back(vobj->getID());
+			mLoadingMeshes[detail][mesh_id].push_back(vobj);
 			mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail));
 			LLMeshRepository::sLODPending++;
 		}
@@ -3876,10 +3892,9 @@ void LLMeshRepository::notifyLoadedMeshes()
 					for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)
 					{
 						F32 max_score = 0.f;
-						for (std::vector<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
+						for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
 						{
-							LLViewerObject* object = gObjectList.findObject(*obj_iter);
-							
+							LLVOVolume* object = *obj_iter;						
 							if (object)
 							{
 								LLDrawable* drawable = object->mDrawable;
@@ -3952,9 +3967,8 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info)
 	skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID);
 	if (iter != mLoadingSkins.end())
 	{
-		for (std::set<LLUUID>::iterator obj_id = iter->second.begin(); obj_id != iter->second.end(); ++obj_id)
+		for (LLVOVolume* vobj : iter->second)
 		{
-			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*obj_id);
 			if (vobj)
 			{
 				vobj->notifySkinInfoLoaded(info);
@@ -3969,9 +3983,8 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id)
 	skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
 	if (iter != mLoadingSkins.end())
 	{
-		for (std::set<LLUUID>::iterator obj_id = iter->second.begin(); obj_id != iter->second.end(); ++obj_id)
+		for (LLVOVolume* vobj : iter->second)
 		{
-			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*obj_id);
 			if (vobj)
 			{
 				vobj->notifySkinInfoUnavailable();
@@ -4034,9 +4047,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
 		}
 
 		//notify waiting LLVOVolume instances that their requested mesh is available
-		for (std::vector<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		for (LLVOVolume* vobj : obj_iter->second)
 		{
-			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
 			if (vobj)
 			{
 				vobj->notifyMeshLoaded();
@@ -4056,9 +4068,8 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
 	{
 		F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod);
 
-		for (std::vector<LLUUID>::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter)
+		for (LLVOVolume* vobj : obj_iter->second)
 		{
-			LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter);
 			if (vobj)
 			{
 				LLVolume* obj_volume = vobj->getVolume();
@@ -4081,7 +4092,7 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo
 	return mThread->getActualMeshLOD(mesh_params, lod);
 }
 
-const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj)
+const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
     if (mesh_id.notNull())
@@ -4098,11 +4109,19 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const
             LLMutexLock lock(mMeshMutex);
             //add volume to list of loading meshes
             skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
-            if (iter == mLoadingSkins.end())
-            { //no request pending for this skin info
+			if (iter != mLoadingSkins.end())
+			{ //request pending for this mesh, append volume id to list
+				auto it = std::find(iter->second.begin(), iter->second.end(), requesting_obj);
+				if (it == iter->second.end()) {
+					iter->second.push_back(requesting_obj);
+				}
+			}
+			else
+			{
+				//first request for this mesh
+				mLoadingSkins[mesh_id].push_back(requesting_obj);
                 mPendingSkinRequests.push(mesh_id);
             }
-            mLoadingSkins[mesh_id].insert(requesting_obj->getID());
         }
     }
 	return nullptr;
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index a5b985ee76..01a8427757 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -575,6 +575,7 @@ public:
 	void shutdown();
 	S32 update();
 
+	void unregisterMesh(LLVOVolume* volume);
 	//mesh management functions
 	S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1);
 	
@@ -587,7 +588,7 @@ public:
 
 	S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod);
 	static S32 getActualMeshLOD(LLSD& header, S32 lod);
-	const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj = nullptr);
+	const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id, LLVOVolume* requesting_obj = nullptr);
 	LLModel::Decomposition* getDecomposition(const LLUUID& mesh_id);
 	void fetchPhysicsShape(const LLUUID& mesh_id);
 	bool hasPhysicsShape(const LLUUID& mesh_id);
@@ -612,7 +613,7 @@ public:
 	static void metricsProgress(unsigned int count);
 	static void metricsUpdate();
 	
-	typedef boost::unordered_map<LLUUID, std::vector<LLUUID> > mesh_load_map;
+	typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
 	
 	typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map;
@@ -626,7 +627,7 @@ public:
 	std::vector<LLMeshRepoThread::LODRequest> mPendingRequests;
 	
 	//list of mesh ids awaiting skin info
-	typedef std::map<LLUUID, std::set<LLUUID> > skin_load_map;
+	typedef boost::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map;
 	skin_load_map mLoadingSkins;
 
 	//list of mesh ids that need to send skin info fetch requests
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index d5583f05f3..8cfa83ba46 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -247,6 +247,8 @@ LLVOVolume::~LLVOVolume()
 	delete mVolumeImpl;
 	mVolumeImpl = NULL;
 
+	gMeshRepo.unregisterMesh(this);
+
 	if(!mMediaImplList.empty())
 	{
 		for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
-- 
cgit v1.2.3


From 9ec86c84e3d75feb5505a98e96db542b054e162e Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 17:35:23 -0400
Subject: Add proper retry support to skin info fetch

---
 indra/newview/llmeshrepository.cpp | 115 +++++++++++++++++++++++++------------
 indra/newview/llmeshrepository.h   |   9 ++-
 2 files changed, 83 insertions(+), 41 deletions(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index ed942eca31..53ce952739 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -857,6 +857,12 @@ LLMeshRepoThread::~LLMeshRepoThread()
 	mHttpRequestSet.clear();
     mHttpHeaders.reset();
 
+	while (!mSkinInfoQ.empty())
+    {
+        delete mSkinInfoQ.front();
+        mSkinInfoQ.pop_front();
+    }
+
     while (!mDecompositionQ.empty())
     {
         delete mDecompositionQ.front();
@@ -946,6 +952,7 @@ void LLMeshRepoThread::run()
                     else
                     {
                         // too many fails
+						LLMutexLock lock(mMutex);
                         mUnavailableQ.push(req);
                         LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL;
                     }
@@ -1022,37 +1029,42 @@ void LLMeshRepoThread::run()
 
             if (!mSkinRequests.empty())
             {
-                std::set<UUIDBasedRequest> incomplete;
-                while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
-                {
-                    mMutex->lock();
-                    std::set<UUIDBasedRequest>::iterator iter = mSkinRequests.begin();
-                    UUIDBasedRequest req = *iter;
-                    mSkinRequests.erase(iter);
-                    mMutex->unlock();
-                    if (req.isDelayed())
-                    {
-                        incomplete.insert(req);
-                    }
-                    else if (!fetchMeshSkinInfo(req.mId))
-                    {
-                        if (req.canRetry())
-                        {
-                            req.updateTime();
-                            incomplete.insert(req);
-                        }
-                        else
-                        {
-                            LL_DEBUGS() << "mSkinRequests failed: " << req.mId << LL_ENDL;
-                        }
-                    }
-                }
+				std::list<UUIDBasedRequest> incomplete;
+				while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)
+				{
 
-                if (!incomplete.empty())
-                {
-                    LLMutexLock locker(mMutex);
-                    mSkinRequests.insert(incomplete.begin(), incomplete.end());
-                }
+					mMutex->lock();
+					auto req = mSkinRequests.front();
+					mSkinRequests.pop_front();
+					mMutex->unlock();
+					if (req.isDelayed())
+					{
+						incomplete.emplace_back(req);
+					}
+					else if (!fetchMeshSkinInfo(req.mId, req.canRetry()))
+					{
+						if (req.canRetry())
+						{
+							req.updateTime();
+							incomplete.emplace_back(req);
+						}
+						else
+						{
+							LLMutexLock locker(mMutex);
+							mSkinUnavailableQ.push_back(req);
+							LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL;
+						}
+					}
+				}
+
+				if (!incomplete.empty())
+				{
+					LLMutexLock locker(mMutex);
+					for (const auto& req : incomplete)
+					{
+						mSkinRequests.push_back(req);
+					}
+				}
             }
 
             // holding lock, try next list
@@ -1151,7 +1163,7 @@ void LLMeshRepoThread::run()
 // Mutex:  LLMeshRepoThread::mMutex must be held on entry
 void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id)
 {
-	mSkinRequests.insert(UUIDBasedRequest(mesh_id));
+	mSkinRequests.push_back(UUIDBasedRequest(mesh_id));
 }
 
 // Mutex:  LLMeshRepoThread::mMutex must be held on entry
@@ -1308,7 +1320,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url,
 }
 
 
-bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
+bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry)
 {
 	
 	if (!mHeaderMutex)
@@ -1390,13 +1402,28 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)
 									   << LL_ENDL;
 					ret = false;
 				}
-				else
+				else if(can_retry)
 				{
 					handler->mHttpHandle = handle;
 					mHttpRequestSet.insert(handler);
 				}
+				else
+				{
+					LLMutexLock locker(mMutex);
+					mSkinUnavailableQ.emplace_back(mesh_id);
+				}
+			}
+			else
+			{
+				LLMutexLock locker(mMutex);
+				mSkinUnavailableQ.emplace_back(mesh_id);
 			}
 		}
+		else
+		{
+			LLMutexLock locker(mMutex);
+			mSkinUnavailableQ.emplace_back(mesh_id);
+		}
 	}
 	else
 	{	
@@ -2906,13 +2933,20 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 	{
 		if (mMutex->trylock())
 		{
-			std::list<LLMeshSkinInfo*> skin_info_q;
+			std::deque<LLMeshSkinInfo*> skin_info_q;
+			std::deque<UUIDBasedRequest> skin_info_unavail_q;
 			std::list<LLModel::Decomposition*> decomp_q;
 
 			if (! mSkinInfoQ.empty())
 			{
 				skin_info_q.swap(mSkinInfoQ);
 			}
+
+			if (! mSkinUnavailableQ.empty())
+			{
+				skin_info_unavail_q.swap(mSkinUnavailableQ);
+			}
+
 			if (! mDecompositionQ.empty())
 			{
 				decomp_q.swap(mDecompositionQ);
@@ -2926,6 +2960,11 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 				gMeshRepo.notifySkinInfoReceived(skin_info_q.front());
 				skin_info_q.pop_front();
 			}
+			while (! skin_info_unavail_q.empty())
+			{
+				gMeshRepo.notifySkinInfoUnavailable(skin_info_unavail_q.front().mId);
+				skin_info_unavail_q.pop_front();
+			}
 
 			while (! decomp_q.empty())
 			{
@@ -3357,9 +3396,8 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status)
 					   << ", Reason:  " << status.toString()
 					   << " (" << status.toTerseString() << ").  Not retrying."
 					   << LL_ENDL;
-
-	// *TODO:  Mark mesh unavailable on error.  For now, simply leave
-	// request unfulfilled rather than retry forever.
+		LLMutexLock lock(gMeshRepo.mThread->mMutex);
+		gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
 }
 
 void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
@@ -3390,7 +3428,8 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /*
 		LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID
 						   << ", Unknown reason.  Not retrying."
 						   << LL_ENDL;
-		// *TODO:  Mark mesh unavailable on error
+		LLMutexLock lock(gMeshRepo.mThread->mMutex);
+		gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID);
 	}
 }
 
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index 01a8427757..f62216d8ba 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -281,10 +281,13 @@ public:
 	};
 
 	//set of requested skin info
-	std::set<UUIDBasedRequest> mSkinRequests;
+	std::deque<UUIDBasedRequest> mSkinRequests;
 	
 	// list of completed skin info requests
-	std::list<LLMeshSkinInfo*> mSkinInfoQ;
+	std::deque<LLMeshSkinInfo*> mSkinInfoQ;
+
+	// list of skin info requests that have failed or are unavailaibe
+	std::deque<UUIDBasedRequest> mSkinUnavailableQ;
 
 	//set of requested decompositions
 	std::set<UUIDBasedRequest> mDecompositionRequests;
@@ -352,7 +355,7 @@ public:
 
 	//send request for skin info, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-	bool fetchMeshSkinInfo(const LLUUID& mesh_id);
+	bool fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry = true);
 
 	//send request for decomposition, returns true if header info exists 
 	//  (should hold onto mesh_id and try again later if header info does not exist)
-- 
cgit v1.2.3


From 07449892df0cfcfa22583d7d7c7b29098e3af499 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 17:54:30 -0400
Subject: Optimize mesh queue processing on main thread to reduce mutex
 contention

---
 indra/newview/llmeshrepository.cpp | 83 +++++++++++++++++++++-----------------
 indra/newview/llmeshrepository.h   |  4 +-
 2 files changed, 47 insertions(+), 40 deletions(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 53ce952739..8c85b30e04 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -953,7 +953,7 @@ void LLMeshRepoThread::run()
                     {
                         // too many fails
 						LLMutexLock lock(mMutex);
-                        mUnavailableQ.push(req);
+                        mUnavailableQ.push_back(req);
                         LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL;
                     }
                 }
@@ -1832,19 +1832,19 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,
 				else
 				{
 					LLMutexLock lock(mMutex);
-					mUnavailableQ.push(LODRequest(mesh_params, lod));
+					mUnavailableQ.push_back(LODRequest(mesh_params, lod));
 				}
 			}
 			else
 			{
 				LLMutexLock lock(mMutex);
-				mUnavailableQ.push(LODRequest(mesh_params, lod));
+				mUnavailableQ.push_back(LODRequest(mesh_params, lod));
 			}
 		}
 		else
 		{
 			LLMutexLock lock(mMutex);
-			mUnavailableQ.push(LODRequest(mesh_params, lod));
+			mUnavailableQ.push_back(LODRequest(mesh_params, lod));
 		}
 	}
 	else
@@ -1959,7 +1959,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
 			LoadedMesh mesh(volume, mesh_params, lod);
 			{
 				LLMutexLock lock(mMutex);
-				mLoadedQ.push(mesh);
+				mLoadedQ.push_back(mesh);
 				// LLPointer is not thread safe, since we added this pointer into
 				// threaded list, make sure counter gets decreased inside mutex lock
 				// and won't affect mLoadedQ processing
@@ -2888,45 +2888,52 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		return;
 	}
 
-	while (!mLoadedQ.empty())
+	if (!mLoadedQ.empty())
 	{
+		std::deque<LoadedMesh> loaded_queue;
+
 		mMutex->lock();
-		if (mLoadedQ.empty())
+		if (!mLoadedQ.empty())
 		{
+			loaded_queue.swap(mLoadedQ);
 			mMutex->unlock();
-			break;
-		}
-		LoadedMesh mesh = mLoadedQ.front(); // make sure nothing else owns volume pointer by this point
-		mLoadedQ.pop();
-		mMutex->unlock();
-		
-		update_metrics = true;
-		if (mesh.mVolume->getNumVolumeFaces() > 0)
-		{
-			gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
-		}
-		else
-		{
-			gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, 
-				LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
+
+			update_metrics = true;
+
+			// Process the elements free of the lock
+			for (const auto& mesh : loaded_queue)
+			{
+				if (mesh.mVolume->getNumVolumeFaces() > 0)
+				{
+					gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume);
+				}
+				else
+				{
+					gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams,
+						LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail()));
+				}
+			}
 		}
 	}
 
-	while (!mUnavailableQ.empty())
+	if (!mUnavailableQ.empty())
 	{
+		std::deque<LODRequest> unavil_queue;
+
 		mMutex->lock();
-		if (mUnavailableQ.empty())
+		if (!mUnavailableQ.empty())
 		{
+			unavil_queue.swap(mUnavailableQ);
 			mMutex->unlock();
-			break;
-		}
-		
-		LODRequest req = mUnavailableQ.front();
-		mUnavailableQ.pop();
-		mMutex->unlock();
 
-		update_metrics = true;
-		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
+			update_metrics = true;
+
+			// Process the elements free of the lock
+			for (const auto& req : unavil_queue)
+			{
+				gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);
+			}
+		}
 	}
 
 	if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty())
@@ -3192,7 +3199,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)
 	LLMutexLock lock(gMeshRepo.mThread->mMutex);
 	for (int i(0); i < 4; ++i)
 	{
-		gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));
+		gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
 	}
 }
 
@@ -3221,7 +3228,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
 		LLMutexLock lock(gMeshRepo.mThread->mMutex);
 		for (int i(0); i < 4; ++i)
 		{
-			gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));
+			gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
 		}
 	}
 	else if (data && data_size > 0)
@@ -3303,7 +3310,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b
 			LLMutexLock lock(gMeshRepo.mThread->mMutex);
 			for (int i(0); i < 4; ++i)
 			{
-				gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));
+				gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i));
 			}
 		}
 	}
@@ -3330,7 +3337,7 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status)
 					   << LL_ENDL;
 
 	LLMutexLock lock(gMeshRepo.mThread->mMutex);
-	gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+	gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
 }
 
 void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */,
@@ -3367,7 +3374,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
 							   << " Not retrying."
 							   << LL_ENDL;
 			LLMutexLock lock(gMeshRepo.mThread->mMutex);
-			gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+			gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
 		}
 	}
 	else
@@ -3378,7 +3385,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body
 						   << " Data size: " << data_size
 						   << LL_ENDL;
 		LLMutexLock lock(gMeshRepo.mThread->mMutex);
-		gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
+		gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));
 	}
 }
 
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index f62216d8ba..e3688ff243 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -305,10 +305,10 @@ public:
 	std::queue<LODRequest> mLODReqQ;
 
 	//queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD)
-	std::queue<LODRequest> mUnavailableQ;
+	std::deque<LODRequest> mUnavailableQ;
 
 	//queue of successfully loaded meshes
-	std::queue<LoadedMesh> mLoadedQ;
+	std::deque<LoadedMesh> mLoadedQ;
 
 	//map of pending header requests and currently desired LODs
 	typedef boost::unordered_map<LLUUID, std::vector<S32> > pending_lod_map;
-- 
cgit v1.2.3


From 9f633e087fa0855ee8358e3e84924872ec5d965f Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 27 Sep 2022 17:58:01 -0400
Subject: Optimize away many string copies in mesh header processing with boost
 iostream array adapters

---
 indra/newview/llmeshrepository.cpp | 25 ++++++-------------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 8c85b30e04..3ed7e9289d 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -77,6 +77,8 @@
 #include "lluploaddialog.h"
 #include "llfloaterreg.h"
 
+#include "boost/iostreams/device/array.hpp"
+#include "boost/iostreams/stream.hpp"
 #include "boost/lexical_cast.hpp"
 
 #ifndef LL_WINDOWS
@@ -1863,27 +1865,12 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
 	U32 header_size = 0;
 	if (data_size > 0)
 	{
-        std::istringstream stream;
-        try
-        {
-            std::string res_str((char*)data, data_size);
-
-            std::string deprecated_header("<? LLSD/Binary ?>");
+		U32 dsize = data_size;
+		char* result_ptr = strip_deprecated_header((char*)data, dsize, &header_size);
 
-            if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
-            {
-                res_str = res_str.substr(deprecated_header.size() + 1, data_size);
-                header_size = deprecated_header.size() + 1;
-            }
-            data_size = res_str.size();
+		data_size = dsize;
 
-            stream.str(res_str);
-        }
-        catch (std::bad_alloc&)
-        {
-            // out of memory, we won't be able to process this mesh
-            return MESH_OUT_OF_MEMORY;
-        }
+		boost::iostreams::stream<boost::iostreams::array_source> stream(result_ptr, data_size);
 
 		if (!LLSDSerialize::fromBinary(header, stream, data_size))
 		{
-- 
cgit v1.2.3


From d1701fb5b1a1cef281b04a8004f133ff7efa7db3 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 28 Sep 2022 09:55:57 -0400
Subject: Fix small bug in unavail skin info processing

---
 indra/newview/llmeshrepository.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 3ed7e9289d..f937754368 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2923,7 +2923,7 @@ void LLMeshRepoThread::notifyLoadedMeshes()
 		}
 	}
 
-	if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty())
+	if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty())
 	{
 		if (mMutex->trylock())
 		{
-- 
cgit v1.2.3


From 6614a6ee9987ac2b199aaabe7d9cd520c613c13f Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 28 Sep 2022 18:42:10 +0300
Subject: SL-18249 Optimizations to mesh and material loading

---
 doc/contributions.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/doc/contributions.txt b/doc/contributions.txt
index 2c1e5487ce..d74ec13637 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -1384,6 +1384,7 @@ Sovereign Engineer
 	SL-14732
 	SL-15096
 	SL-16127
+	SL-18249
 SpacedOut Frye
 	VWR-34
 	VWR-45
-- 
cgit v1.2.3


From 9104b899961a9ceb61de73528e85f1a240f30cb5 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Fri, 30 Sep 2022 00:26:44 +0300
Subject: SL-18235 Remove outfit buttons from unpacking dialog

---
 indra/newview/llfloateropenobject.cpp              | 14 ------
 indra/newview/llfloateropenobject.h                |  2 -
 indra/newview/llpanelobjectinventory.cpp           | 21 ++++++---
 indra/newview/llpanelobjectinventory.h             | 11 ++++-
 .../skins/default/xui/en/floater_openobject.xml    | 52 ++--------------------
 5 files changed, 28 insertions(+), 72 deletions(-)

diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp
index 2a1749bd42..a682064dad 100644
--- a/indra/newview/llfloateropenobject.cpp
+++ b/indra/newview/llfloateropenobject.cpp
@@ -56,8 +56,6 @@ LLFloaterOpenObject::LLFloaterOpenObject(const LLSD& key)
 	mDirty(TRUE)
 {
 	mCommitCallbackRegistrar.add("OpenObject.MoveToInventory",	boost::bind(&LLFloaterOpenObject::onClickMoveToInventory, this));
-	mCommitCallbackRegistrar.add("OpenObject.MoveAndWear",		boost::bind(&LLFloaterOpenObject::onClickMoveAndWear, this));
-	mCommitCallbackRegistrar.add("OpenObject.ReplaceOutfit",	boost::bind(&LLFloaterOpenObject::onClickReplace, this));
 	mCommitCallbackRegistrar.add("OpenObject.Cancel",			boost::bind(&LLFloaterOpenObject::onClickCancel, this));
 }
 
@@ -243,18 +241,6 @@ void LLFloaterOpenObject::onClickMoveToInventory()
 	closeFloater();
 }
 
-void LLFloaterOpenObject::onClickMoveAndWear()
-{
-	moveToInventory(true, false);
-	closeFloater();
-}
-
-void LLFloaterOpenObject::onClickReplace()
-{
-	moveToInventory(true, true);
-	closeFloater();
-}
-
 void LLFloaterOpenObject::onClickCancel()
 {
 	closeFloater();
diff --git a/indra/newview/llfloateropenobject.h b/indra/newview/llfloateropenobject.h
index 2e761f99bf..745753316b 100644
--- a/indra/newview/llfloateropenobject.h
+++ b/indra/newview/llfloateropenobject.h
@@ -63,8 +63,6 @@ protected:
 	void moveToInventory(bool wear, bool replace = false);
 
 	void onClickMoveToInventory();
-	void onClickMoveAndWear();
-	void onClickReplace();
 	void onClickCancel();
 	static void callbackCreateInventoryCategory(const LLUUID& category_id, LLUUID object_id, bool wear, bool replace = false);
 	static void callbackMoveInventory(S32 result, void* data);
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index cfaa9456be..5ac5f0d429 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -1275,7 +1275,8 @@ LLPanelObjectInventory::LLPanelObjectInventory(const LLPanelObjectInventory::Par
 	mHaveInventory(FALSE),
 	mIsInventoryEmpty(TRUE),
 	mInventoryNeedsUpdate(FALSE),
-	mInventoryViewModel(p.name)
+	mInventoryViewModel(p.name),
+    mShowRootFolder(p.show_root_folder)
 {
 	// Setup context menu callbacks
 	mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelObjectInventory::doToSelected, this, _2));
@@ -1526,15 +1527,23 @@ void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root
 		p.font_highlight_color = item_color;
 
 		LLFolderViewFolder* new_folder = LLUICtrlFactory::create<LLFolderViewFolder>(p);
-		new_folder->addToFolder(mFolders);
-		new_folder->toggleOpen();
+
+        if (mShowRootFolder)
+        {
+            new_folder->addToFolder(mFolders);
+            new_folder->toggleOpen();
+        }
 
 		if (!contents.empty())
 		{
-			createViewsForCategory(&contents, inventory_root, new_folder);
+			createViewsForCategory(&contents, inventory_root, mShowRootFolder ? new_folder : mFolders);
 		}
-        // Refresh for label to add item count
-        new_folder->refresh();
+
+        if (mShowRootFolder)
+        {
+            // Refresh for label to add item count
+            new_folder->refresh();
+        }
 	}
 }
 
diff --git a/indra/newview/llpanelobjectinventory.h b/indra/newview/llpanelobjectinventory.h
index 7b9ecfb8f3..0e450d8ce9 100644
--- a/indra/newview/llpanelobjectinventory.h
+++ b/indra/newview/llpanelobjectinventory.h
@@ -48,8 +48,14 @@ class LLViewerObject;
 class LLPanelObjectInventory : public LLPanel, public LLVOInventoryListener
 {
 public:
-	// dummy param block for template registration purposes
-	struct Params : public LLPanel::Params {};
+    struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+    {
+        Optional<bool> show_root_folder;
+
+        Params()
+            : show_root_folder("show_root_folder", true)
+        {}
+    };
 
 	LLPanelObjectInventory(const Params&);
 	virtual ~LLPanelObjectInventory();
@@ -110,6 +116,7 @@ private:
 	BOOL mIsInventoryEmpty; // 'Empty' label
 	BOOL mInventoryNeedsUpdate; // for idle, set on changed callback
 	LLFolderViewModelInventory	mInventoryViewModel;	
+    bool mShowRootFolder;
 };
 
 #endif // LL_LLPANELOBJECTINVENTORY_H
diff --git a/indra/newview/skins/default/xui/en/floater_openobject.xml b/indra/newview/skins/default/xui/en/floater_openobject.xml
index 912db80bcc..ec03d7d32c 100644
--- a/indra/newview/skins/default/xui/en/floater_openobject.xml
+++ b/indra/newview/skins/default/xui/en/floater_openobject.xml
@@ -3,7 +3,7 @@
  legacy_header_height="18"
  can_resize="true"
  default_tab_group="1"
- height="370"
+ height="350"
  layout="topleft"
  min_height="190"
  min_width="285"
@@ -31,62 +31,18 @@
      background_visible="false"
      draw_border="false"
      follows="all"
-     height="240"
+     height="265"
      layout="topleft"
+     show_root_folder="false"
      left="10"
      name="object_contents"
      top_pad="0"
      width="284" />
-  	<view_border
-     bevel_style="none"
-     follows="bottom|left"
-     height="50"
-     highlight_light_color="0.6 0.6 0.6"
-     layout="topleft"
-     left="10"
-     name="border"
-     top_pad="5"
-     width="270"/> 
-  	<text
-  	 follows="bottom|left"
-  	 height="15"
-  	 layout="topleft"
-  	 left="15"
-  	 name="border_note"
-  	 text_color="White"
-  	 top_delta="5">
-  	 	Copy to inventory and wear
-    </text>  
- 	<button
-     follows="bottom|left"
-     height="23"
-     label="Add to outfit"
-     label_selected="Add to outfit"
-     layout="topleft"
- 	 left="15"    
-     name="copy_and_wear_button" 	
- 	 top_pad="3"	
-     width="135">
-        <button.commit_callback
-         function="OpenObject.MoveAndWear" />
-    </button>
-	<button
-     follows="bottom|left"
-     height="23"
-     label="Replace outfit"
-     label_selected="Replace outfit"
-     layout="topleft"
-     left_pad="5"
-     name="copy_and_replace_button"
-     width="120">
-        <button.commit_callback
-         function="OpenObject.ReplaceOutfit" />
-    </button>  
     <button
      follows="bottom|left"
      height="23"
      label="Only copy to inventory"
-     label_selected="Only copy to inventory"
+     label_selected="Copy to inventory"
      layout="topleft"
      left="15"
      name="copy_to_inventory_button"
-- 
cgit v1.2.3


From 994d432e6655d2ed8c45c0d0e804629cb0f650c5 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 13 Jan 2021 18:26:13 -0500
Subject: Port required changes to render rainbows and sun dogs to class1
 deferred sky shader and remove redundant and slow class2 version.

# Conflicts:
#	indra/newview/app_settings/shaders/class2/deferred/skyF.glsl
---
 .../app_settings/shaders/class1/deferred/skyF.glsl |  43 ++++-
 .../app_settings/shaders/class1/deferred/skyV.glsl |  35 ++--
 .../app_settings/shaders/class2/deferred/skyF.glsl | 199 ---------------------
 .../app_settings/shaders/class2/deferred/skyV.glsl |  42 -----
 4 files changed, 56 insertions(+), 263 deletions(-)
 delete mode 100644 indra/newview/app_settings/shaders/class2/deferred/skyF.glsl
 delete mode 100644 indra/newview/app_settings/shaders/class2/deferred/skyV.glsl

diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
index 331249dc33..9772063f03 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
@@ -25,6 +25,17 @@
  
 /*[EXTRA_CODE_HERE]*/
 
+// Inputs
+VARYING vec4 vary_HazeColor;
+VARYING float vary_LightNormPosDot;
+
+uniform sampler2D rainbow_map;
+uniform sampler2D halo_map;
+
+uniform float moisture_level;
+uniform float droplet_radius;
+uniform float ice_level;
+
 #ifdef DEFINE_GL_FRAGCOLOR
 out vec4 frag_data[3];
 #else
@@ -35,11 +46,27 @@ out vec4 frag_data[3];
 // The fragment shader for the sky
 /////////////////////////////////////////////////////////////////////////
 
-VARYING vec4 vary_HazeColor;
+vec3 rainbow(float d)
+{
+    // 'Interesting' values of d are -0.75 .. -0.825, i.e. when view vec nearly opposite of sun vec
+    // Rainbox tex is mapped with REPEAT, so -.75 as tex coord is same as 0.25.  -0.825 -> 0.175. etc.
+    // SL-13629
+    // Unfortunately the texture is inverted, so we need to invert the y coord, but keep the 'interesting'
+    // part within the same 0.175..0.250 range, i.e. d = (1 - d) - 1.575
+    d         = clamp(-0.575 - d, 0.0, 1.0);
+    float rad = (droplet_radius - 5.0f) / 1024.0f;
+    return pow(texture2D(rainbow_map, vec2(rad+0.5, d)).rgb, vec3(1.8)) * moisture_level;
+}
+
+vec3 halo22(float d)
+{
+    d       = clamp(d, 0.1, 1.0);
+    float v = sqrt(clamp(1 - (d * d), 0, 1));
+    return texture2D(halo_map, vec2(0, v)).rgb * ice_level;
+}
 
 /// Soft clips the light with a gamma correction
 vec3 scaleSoftClip(vec3 light);
-vec3 srgb_to_linear(vec3 c);
 
 void main()
 {
@@ -48,14 +75,18 @@ void main()
     // the fragment) if the sky wouldn't show up because the clouds 
     // are fully opaque.
 
-    vec4 color;
-    color = vary_HazeColor;
+    vec4 color = vary_HazeColor;
 
+    float  rel_pos_lightnorm = vary_LightNormPosDot;
+    float optic_d = rel_pos_lightnorm;
+    vec3  halo_22 = halo22(optic_d);
+    color.rgb += rainbow(optic_d);
+    color.rgb += halo_22;
     color.rgb *= 2.;
     color.rgb = scaleSoftClip(color.rgb);
 
-    /// Gamma correct for WL (soft clip effect).
-    frag_data[0] = vec4(color.rgb, 0.0);
+    // Gamma correct for WL (soft clip effect).
+    frag_data[0] = vec4(color.rgb, 1.0);
     frag_data[1] = vec4(0.0,0.0,0.0,0.0);
     frag_data[2] = vec4(0.0,0.0,0.0,1.0); //1.0 in norm.w masks off fog
 
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
index 28a1faf24f..6db4690bff 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyV.glsl
@@ -33,6 +33,7 @@ ATTRIBUTE vec3 position;
 
 // Output parameters
 VARYING vec4 vary_HazeColor;
+VARYING float vary_LightNormPosDot;
 
 // Inputs
 uniform vec3 camPosLocal;
@@ -72,27 +73,29 @@ void main()
     vec3 rel_pos = position.xyz - camPosLocal.xyz + vec3(0, 50, 0);
 
     // Adj position vector to clamp altitude
-    if (rel_pos.y > 0)
+    if (rel_pos.y > 0.)
     {
         rel_pos *= (max_y / rel_pos.y);
     }
-    if (rel_pos.y < 0)
+    if (rel_pos.y < 0.)
     {
         rel_pos *= (-32000. / rel_pos.y);
     }
 
-    // Can normalize then
-    vec3 rel_pos_norm = normalize(rel_pos);
+    // Normalized
+    vec3  rel_pos_norm = normalize(rel_pos);
+    float rel_pos_len  = length(rel_pos);
 
-    float rel_pos_len = length(rel_pos);
+    // Grab this value and pass to frag shader for rainbows
+    float rel_pos_lightnorm_dot = dot(rel_pos_norm, lightnorm.xyz);
+    vary_LightNormPosDot = rel_pos_lightnorm_dot;
 
     // Initialize temp variables
     vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color;
-    vec4 light_atten;
 
     // Sunlight attenuation effect (hue and brightness) due to atmosphere
     // this is used later for sunlight modulation at various altitudes
-    light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y);
+    vec4 light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y);
 
     // Calculate relative weights
     vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density));
@@ -112,7 +115,7 @@ void main()
     combined_haze = exp(-combined_haze * density_dist);
 
     // Compute haze glow
-    float haze_glow = 1.0 - dot(rel_pos_norm, lightnorm.xyz);
+    float haze_glow = 1.0 - rel_pos_lightnorm_dot;
     // haze_glow is 0 at the sun and increases away from sun
     haze_glow = max(haze_glow, .001);
     // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
@@ -123,30 +126,30 @@ void main()
 
     // Add "minimum anti-solar illumination"
     // For sun, add to glow.  For moon, remove glow entirely. SL-13768
-    haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (haze_glow + 0.25);
+    haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (sun_moon_glow_factor * (haze_glow + 0.25));
 
-    vec4 color =
-        (blue_horizon * blue_weight * (sunlight + ambient_color) + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient_color));
+    // Haze color above cloud
+    vec4 color = (blue_horizon * blue_weight * (sunlight + ambient_color)
+               + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient_color));
 
     // Final atmosphere additive
     color *= (1. - combined_haze);
 
     // Increase ambient when there are more clouds
-    vec4 tmpAmbient = ambient_color;
-    tmpAmbient += max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5;
+    vec4 ambient = ambient_color + max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5;
 
     // Dim sunlight by cloud shadow percentage
     sunlight *= max(0.0, (1. - cloud_shadow));
 
     // Haze color below cloud
-    vec4 additiveColorBelowCloud =
-        (blue_horizon * blue_weight * (sunlight + tmpAmbient) + (haze_horizon * haze_weight) * (sunlight * haze_glow + tmpAmbient));
+    vec4 add_below_cloud = (blue_horizon * blue_weight * (sunlight + ambient) 
+                         + (haze_horizon * haze_weight) * (sunlight * haze_glow + ambient));
 
     // Attenuate cloud color by atmosphere
     combined_haze = sqrt(combined_haze);  // less atmos opacity (more transparency) below clouds
 
     // At horizon, blend high altitude sky color towards the darker color below the clouds
-    color += (additiveColorBelowCloud - color) * (1. - sqrt(combined_haze));
+    color += (add_below_cloud - color) * (1. - sqrt(combined_haze));
 
     // Haze color above cloud
     vary_HazeColor = color;
diff --git a/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl
deleted file mode 100644
index 6841a8194f..0000000000
--- a/indra/newview/app_settings/shaders/class2/deferred/skyF.glsl
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * @file class2/deferred/skyF.glsl
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2005, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-uniform mat4 modelview_projection_matrix;
-
-// SKY ////////////////////////////////////////////////////////////////////////
-// The vertex shader for creating the atmospheric sky
-///////////////////////////////////////////////////////////////////////////////
-
-// Inputs
-uniform vec3 camPosLocal;
-
-uniform vec4  lightnorm;
-uniform vec4  sunlight_color;
-uniform vec4  moonlight_color;
-uniform int   sun_up_factor;
-uniform vec4  ambient_color;
-uniform vec4  blue_horizon;
-uniform vec4  blue_density;
-uniform float haze_horizon;
-uniform float haze_density;
-
-uniform float cloud_shadow;
-uniform float density_multiplier;
-uniform float distance_multiplier;
-uniform float max_y;
-
-uniform vec4  glow;
-uniform float sun_moon_glow_factor;
-
-uniform vec4 cloud_color;
-
-#ifdef DEFINE_GL_FRAGCOLOR
-out vec4 frag_data[3];
-#else
-#define frag_data gl_FragData
-#endif
-
-VARYING vec3 pos;
-
-/////////////////////////////////////////////////////////////////////////
-// The fragment shader for the sky
-/////////////////////////////////////////////////////////////////////////
-
-uniform sampler2D rainbow_map;
-uniform sampler2D halo_map;
-
-uniform float moisture_level;
-uniform float droplet_radius;
-uniform float ice_level;
-
-vec3 rainbow(float d)
-{
-    // d is the dot product of view and sun directions, so ranging -1.0..1.0
-    // 'interesting' values of d are the range -0.75..-0.825, when view is nearly opposite of sun vec
-    // Rainbox texture mode is GL_REPEAT, so tc of -.75 is equiv to 0.25, -0.825 equiv to 0.175.
-
-    // SL-13629 Rainbow texture has colors within the correct .175...250 range, but order is inverted.
-    // Rather than replace the texture, we mirror and translate the y tc to keep the colors within the
-    // interesting range, but in reversed order:  i.e. d = (1 - d) - 1.575
-    d = clamp(-0.575 - d, 0.0, 1.0);
-
-    // With the colors in the lower 1/4 of the texture, inverting the coords leaves most of it inaccessible.
-    // So, we can stretch the texcoord above the colors (ie > 0.25) to fill the entire remaining coordinate
-    // space. This improves gradation, reduces banding within the rainbow interior. (1-0.25) / (0.425/0.25) = 4.2857
-    float interior_coord = max(0.0, d - 0.25) * 4.2857;
-    d = clamp(d, 0.0, 0.25) + interior_coord;
-
-    float rad = (droplet_radius - 5.0f) / 1024.0f;
-    return pow(texture2D(rainbow_map, vec2(rad, d)).rgb, vec3(1.8)) * moisture_level;
-}
-
-vec3 halo22(float d)
-{
-    d       = clamp(d, 0.1, 1.0);
-    float v = sqrt(clamp(1 - (d * d), 0, 1));
-    return texture2D(halo_map, vec2(0, v)).rgb * ice_level;
-}
-
-/// Soft clips the light with a gamma correction
-vec3 scaleSoftClip(vec3 light);
-
-void main()
-{
-    // World / view / projection
-    // Get relative position (offset why?)
-    vec3 rel_pos = pos.xyz - camPosLocal.xyz + vec3(0, 50, 0);
-
-    // Adj position vector to clamp altitude
-    if (rel_pos.y > 0.)
-    {
-        rel_pos *= (max_y / rel_pos.y);
-    }
-    if (rel_pos.y < 0.)
-    {
-        rel_pos *= (-32000. / rel_pos.y);
-    }
-
-    // Normalized
-    vec3  rel_pos_norm = normalize(rel_pos);
-    float rel_pos_len  = length(rel_pos);
-
-    // Initialize temp variables
-    vec4 sunlight = (sun_up_factor == 1) ? sunlight_color : moonlight_color;
-
-    // Sunlight attenuation effect (hue and brightness) due to atmosphere
-    // this is used later for sunlight modulation at various altitudes
-    vec4 light_atten = (blue_density + vec4(haze_density * 0.25)) * (density_multiplier * max_y);
-
-    // Calculate relative weights
-    vec4 combined_haze = abs(blue_density) + vec4(abs(haze_density));
-    vec4 blue_weight   = blue_density / combined_haze;
-    vec4 haze_weight   = haze_density / combined_haze;
-
-    // Compute sunlight from rel_pos & lightnorm (for long rays like sky)
-    float off_axis = 1.0 / max(1e-6, max(0, rel_pos_norm.y) + lightnorm.y);
-    sunlight *= exp(-light_atten * off_axis);
-
-    // Distance
-    float density_dist = rel_pos_len * density_multiplier;
-
-    // Transparency (-> combined_haze)
-    // ATI Bugfix -- can't store combined_haze*density_dist in a variable because the ati
-    // compiler gets confused.
-    combined_haze = exp(-combined_haze * density_dist);
-
-    // Compute haze glow
-    float haze_glow = dot(rel_pos_norm, lightnorm.xyz);
-    haze_glow       = 1. - haze_glow;
-    // haze_glow is 0 at the sun and increases away from sun
-    haze_glow = max(haze_glow, .001);
-    // Set a minimum "angle" (smaller glow.y allows tighter, brighter hotspot)
-    haze_glow *= glow.x;
-    // Higher glow.x gives dimmer glow (because next step is 1 / "angle")
-    haze_glow = pow(haze_glow, glow.z);
-    // glow.z should be negative, so we're doing a sort of (1 / "angle") function
-
-    // Add "minimum anti-solar illumination"
-    // For sun, add to glow.  For moon, remove glow entirely. SL-13768
-    haze_glow = (sun_moon_glow_factor < 1.0) ? 0.0 : (sun_moon_glow_factor * (haze_glow + 0.25));
-
-    // Haze color above cloud
-    vec4 color = blue_horizon * blue_weight * (sunlight + ambient_color) 
-               + haze_horizon * haze_weight * (sunlight * haze_glow + ambient_color);
-
-    // Final atmosphere additive
-    color *= (1. - combined_haze);
-
-    // Increase ambient when there are more clouds
-    // TODO 9/20: DJH what does this do?  max(0,(1-ambient)) will change the color
-    vec4 ambient = ambient_color + max(vec4(0), (1. - ambient_color)) * cloud_shadow * 0.5;
-
-    // Dim sunlight by cloud shadow percentage
-    sunlight *= max(0.0, (1. - cloud_shadow));
-
-    // Haze color below cloud
-    vec4 add_below_cloud = blue_horizon * blue_weight * (sunlight + ambient) 
-                         + haze_horizon * haze_weight * (sunlight * haze_glow + ambient);
-
-    // Attenuate cloud color by atmosphere
-    combined_haze = sqrt(combined_haze);  // less atmos opacity (more transparency) below clouds
-
-    // At horizon, blend high altitude sky color towards the darker color below the clouds
-    color += (add_below_cloud - color) * (1. - sqrt(combined_haze));
-
-    float optic_d = dot(rel_pos_norm, lightnorm.xyz);
-    vec3  halo_22 = halo22(optic_d);
-    color.rgb += rainbow(optic_d);
-    color.rgb += halo_22;
-    color.rgb *= 2.;
-    color.rgb = scaleSoftClip(color.rgb);
-
-    // Gamma correct for WL (soft clip effect).
-    frag_data[0] = vec4(color.rgb, 1.0);
-    frag_data[1] = vec4(0.0, 0.0, 0.0, 0.0);
-    frag_data[2] = vec4(0.0, 0.0, 0.0, 1.0);  // 1.0 in norm.w masks off fog
-}
diff --git a/indra/newview/app_settings/shaders/class2/deferred/skyV.glsl b/indra/newview/app_settings/shaders/class2/deferred/skyV.glsl
deleted file mode 100644
index bcf775577a..0000000000
--- a/indra/newview/app_settings/shaders/class2/deferred/skyV.glsl
+++ /dev/null
@@ -1,42 +0,0 @@
-/** 
- * @file WLSkyV.glsl
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2005, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-uniform mat4 modelview_projection_matrix;
-
-ATTRIBUTE vec3 position;
-
-// SKY ////////////////////////////////////////////////////////////////////////
-// The vertex shader for creating the atmospheric sky
-///////////////////////////////////////////////////////////////////////////////
-
-VARYING vec3 pos;
-
-void main()
-{
-
-	// World / view / projection
-    pos = position.xyz;
-	gl_Position = modelview_projection_matrix * vec4(position.xyz, 1.0);
-}
-- 
cgit v1.2.3


From 338aeef7e9e8cfd167489e0707f3e4a884d2c00f Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 30 Sep 2022 21:54:10 +0300
Subject: SL-14696 SL-13629 Re-add gradation fix after merging a contribution

---
 doc/contributions.txt                                        | 1 +
 indra/newview/app_settings/shaders/class1/deferred/skyF.glsl | 7 +++++++
 2 files changed, 8 insertions(+)

diff --git a/doc/contributions.txt b/doc/contributions.txt
index d74ec13637..1408bab8ca 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -1377,6 +1377,7 @@ Sovereign Engineer
     OPEN-343
 	SL-11625
     BUG-229030
+	SL-14696
 	SL-14705
 	SL-14706
 	SL-14707
diff --git a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
index 9772063f03..de22312d3c 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/skyF.glsl
@@ -54,6 +54,13 @@ vec3 rainbow(float d)
     // Unfortunately the texture is inverted, so we need to invert the y coord, but keep the 'interesting'
     // part within the same 0.175..0.250 range, i.e. d = (1 - d) - 1.575
     d         = clamp(-0.575 - d, 0.0, 1.0);
+
+    // With the colors in the lower 1/4 of the texture, inverting the coords leaves most of it inaccessible.
+    // So, we can stretch the texcoord above the colors (ie > 0.25) to fill the entire remaining coordinate
+    // space. This improves gradation, reduces banding within the rainbow interior. (1-0.25) / (0.425/0.25) = 4.2857
+    float interior_coord = max(0.0, d - 0.25) * 4.2857;
+    d = clamp(d, 0.0, 0.25) + interior_coord;
+
     float rad = (droplet_radius - 5.0f) / 1024.0f;
     return pow(texture2D(rainbow_map, vec2(rad+0.5, d)).rgb, vec3(1.8)) * moisture_level;
 }
-- 
cgit v1.2.3


From c844bf0ab8acbbb2d4ef0814d8ed7ac9291b7f44 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 17 Dec 2021 01:39:40 +0200
Subject: SL-16542 Pull in chunks of graham's openjpeg code, update openjpeg
 and enable partial bitstreams

Openjpeg was modified: "p_max_len -= l_nb_bytes_read;" was causing an overflow.
I'm not sure if I did something incorectly in opj_skip/opj_seek viewer side, but seems like openjpeg should have been checking remaining space in p_max_len either way.
P.S. Many thanks to Chafey and Neopallium for implementing openjpeg's partial bitstream support
---
 autobuild.xml                                      |   14 +-
 indra/cmake/Copy3rdPartyLibs.cmake                 |    4 +-
 indra/cmake/FindOpenJPEG.cmake                     |    5 +-
 indra/cmake/OpenJPEG.cmake                         |   14 +-
 .../integration_tests/llui_libtest/CMakeLists.txt  |    4 +-
 indra/llimagej2coj/llimagej2coj.cpp                | 1101 +++++++++++++-------
 indra/newview/CMakeLists.txt                       |    6 +-
 indra/newview/viewer_manifest.py                   |    4 +-
 8 files changed, 744 insertions(+), 408 deletions(-)

diff --git a/autobuild.xml b/autobuild.xml
index 5a5ffa2898..801da4e773 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2195,9 +2195,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>5abf2d9c0b250821c59cc60cd94fd8af</string>
+              <string>89e6436b256c98818fa736a460a04016</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54840/510064/openjpeg-1.5.1.538970-darwin64-538970.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105184/920580/openjpeg-2.5.0.575325-darwin64-575325.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -2219,9 +2219,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>222a406ecb4071a9cc9635353afa337e</string>
+              <string>97b15b312c2dec38981ec63bd77d0491</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54977/511775/openjpeg-1.5.1.538970-windows-538970.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105185/920592/openjpeg-2.5.0.575325-windows-575325.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -2231,16 +2231,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>5b5c80807fa8161f3480be3d89fe9516</string>
+              <string>cdb55c8b074ce4f166ebc597f13f62a1</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/54974/511767/openjpeg-1.5.1.538970-windows64-538970.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105186/920591/openjpeg-2.5.0.575325-windows64-575325.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
         <key>version</key>
-        <string>1.5.1.538970</string>
+        <string>2.5.0.575325</string>
       </map>
       <key>openssl</key>
       <map>
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index ff705101de..04b4202ca3 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -52,7 +52,7 @@ if(WINDOWS)
 
     set(release_src_dir "${ARCH_PREBUILT_DIRS_RELEASE}")
     set(release_files
-        openjpeg.dll
+        openjp2.dll
         libapr-1.dll
         libaprutil-1.dll
         libapriconv-1.dll
@@ -220,7 +220,7 @@ elseif(LINUX)
         libgobject-2.0.so
         libhunspell-1.3.so.0.0.0
         libopenal.so
-        libopenjpeg.so
+        libopenjp2.so
         libuuid.so.16
         libuuid.so.16.0.22
         libfontconfig.so.1.8.0
diff --git a/indra/cmake/FindOpenJPEG.cmake b/indra/cmake/FindOpenJPEG.cmake
index 949384eec4..afe319fb06 100644
--- a/indra/cmake/FindOpenJPEG.cmake
+++ b/indra/cmake/FindOpenJPEG.cmake
@@ -9,14 +9,15 @@
 # also defined, but not for general use are
 #  OPENJPEG_LIBRARY, where to find the OpenJPEG library.
 
-FIND_PATH(OPENJPEG_INCLUDE_DIR openjpeg.h
+FIND_PATH(OPENJPEG_INCLUDE_DIR openjp2.h
 /usr/local/include/openjpeg
 /usr/local/include
 /usr/include/openjpeg
 /usr/include
+include/openjpeg
 )
 
-SET(OPENJPEG_NAMES ${OPENJPEG_NAMES} openjpeg)
+SET(OPENJPEG_NAMES ${OPENJPEG_NAMES} openjp2)
 FIND_LIBRARY(OPENJPEG_LIBRARY
   NAMES ${OPENJPEG_NAMES}
   PATHS /usr/lib /usr/local/lib
diff --git a/indra/cmake/OpenJPEG.cmake b/indra/cmake/OpenJPEG.cmake
index bf0bde2ba7..a078c97cb8 100644
--- a/indra/cmake/OpenJPEG.cmake
+++ b/indra/cmake/OpenJPEG.cmake
@@ -8,15 +8,7 @@ if (USESYSTEMLIBS)
   include(FindOpenJPEG)
 else (USESYSTEMLIBS)
   use_prebuilt_binary(openjpeg)
-  
-  if(WINDOWS)
-    # Windows has differently named release and debug openjpeg(d) libs.
-    set(OPENJPEG_LIBRARIES 
-        debug openjpegd
-        optimized openjpeg)
-  else(WINDOWS)
-    set(OPENJPEG_LIBRARIES openjpeg)
-  endif(WINDOWS)
-  
-    set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg)
+
+  set(OPENJPEG_LIBRARIES openjp2)
+  set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg)
 endif (USESYSTEMLIBS)
diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt
index d7706e73b2..3957ede77f 100644
--- a/indra/integration_tests/llui_libtest/CMakeLists.txt
+++ b/indra/integration_tests/llui_libtest/CMakeLists.txt
@@ -99,14 +99,14 @@ if (WINDOWS)
     # Copy over OpenJPEG.dll
     # *NOTE: On Windows with VS2005, only the first comment prints
     set(OPENJPEG_RELEASE
-        "${ARCH_PREBUILT_DIRS_RELEASE}/openjpeg.dll")
+        "${ARCH_PREBUILT_DIRS_RELEASE}/openjp2.dll")
     add_custom_command( TARGET llui_libtest POST_BUILD
         COMMAND ${CMAKE_COMMAND} -E copy_if_different 
             ${OPENJPEG_RELEASE} ${CMAKE_CURRENT_BINARY_DIR}
         COMMENT "Copying OpenJPEG DLLs to binary directory"
         )
     set(OPENJPEG_DEBUG
-        "${ARCH_PREBUILT_DIRS_DEBUG}/openjpegd.dll")
+        "${ARCH_PREBUILT_DIRS_DEBUG}/openjp2.dll")
     add_custom_command( TARGET llui_libtest POST_BUILD
         COMMAND ${CMAKE_COMMAND} -E copy_if_different 
             ${OPENJPEG_DEBUG} ${CMAKE_CURRENT_BINARY_DIR}
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 925da5674b..944629cf4c 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -29,40 +29,41 @@
 
 // this is defined so that we get static linking.
 #include "openjpeg.h"
+#include "event.h"
+#include "cio.h"
 
-#include "lltimer.h"
-//#include "llmemory.h"
+#define MAX_ENCODED_DISCARD_LEVELS 5
 
 // Factory function: see declaration in llimagej2c.cpp
 LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
 {
-	return new LLImageJ2COJ();
+    return new LLImageJ2COJ();
 }
 
 std::string LLImageJ2COJ::getEngineInfo() const
 {
 #ifdef OPENJPEG_VERSION
-	return std::string("OpenJPEG: " OPENJPEG_VERSION ", Runtime: ")
-		+ opj_version();
+    return std::string("OpenJPEG: " OPENJPEG_VERSION ", Runtime: ")
+        + opj_version();
 #else
-	return std::string("OpenJPEG runtime: ") + opj_version();
+    return std::string("OpenJPEG runtime: ") + opj_version();
 #endif
 }
 
 // Return string from message, eliminating final \n if present
 static std::string chomp(const char* msg)
 {
-	// stomp trailing \n
-	std::string message = msg;
-	if (!message.empty())
-	{
-		size_t last = message.size() - 1;
-		if (message[last] == '\n')
-		{
-			message.resize( last );
-		}
-	}
-	return message;
+    // stomp trailing \n
+    std::string message = msg;
+    if (!message.empty())
+    {
+        size_t last = message.size() - 1;
+        if (message[last] == '\n')
+        {
+            message.resize(last);
+        }
+}
+    return message;
 }
 
 /**
@@ -70,419 +71,761 @@ sample error callback expecting a LLFILE* client object
 */
 void error_callback(const char* msg, void*)
 {
-	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
+    LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
 }
 /**
 sample warning callback expecting a LLFILE* client object
 */
 void warning_callback(const char* msg, void*)
 {
-	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
+    LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
 }
 /**
 sample debug callback expecting no client object
 */
 void info_callback(const char* msg, void*)
 {
-	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
+    LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
 }
 
 // Divide a by 2 to the power of b and round upwards
 int ceildivpow2(int a, int b)
 {
-	return (a + (1 << b) - 1) >> b;
+    return (a + (1 << b) - 1) >> b;
 }
 
-
-LLImageJ2COJ::LLImageJ2COJ()
-	: LLImageJ2CImpl()
+class JPEG2KBase
 {
-}
+public:
+    JPEG2KBase() {}
 
+    U8*        buffer = nullptr;
+    OPJ_SIZE_T size = 0;
+    OPJ_OFF_T  offset = 0;
+};
 
-LLImageJ2COJ::~LLImageJ2COJ()
+#define WANT_VERBOSE_OPJ_SPAM LL_DEBUG
+
+static void opj_info(const char* msg, void* user_data)
 {
+    llassert(user_data);
+#if WANT_VERBOSE_OPJ_SPAM
+    LL_INFOS("OpenJPEG") << msg << LL_ENDL;
+#endif
 }
 
-bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region)
+static void opj_warn(const char* msg, void* user_data)
 {
-	// No specific implementation for this method in the OpenJpeg case
-	return false;
+    llassert(user_data);
+#if WANT_VERBOSE_OPJ_SPAM
+    LL_WARNS("OpenJPEG") << msg << LL_ENDL;
+#endif
 }
 
-bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels)
+static void opj_error(const char* msg, void* user_data)
 {
-	// No specific implementation for this method in the OpenJpeg case
-	return false;
+    llassert(user_data);
+#if WANT_VERBOSE_OPJ_SPAM
+    LL_WARNS("OpenJPEG") << msg << LL_ENDL;
+#endif
 }
 
-bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
+static OPJ_SIZE_T opj_read(void * buffer, OPJ_SIZE_T bytes, void* user_data)
 {
-	//
-	// FIXME: Get the comment field out of the texture
-	//
-
-	LLTimer decode_timer;
-
-	opj_dparameters_t parameters;	/* decompression parameters */
-	opj_event_mgr_t event_mgr;		/* event manager */
-	opj_image_t *image = NULL;
-
-	opj_dinfo_t* dinfo = NULL;	/* handle to a decompressor */
-	opj_cio_t *cio = NULL;
-
-
-	/* configure the event callbacks (not required) */
-	memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
-	event_mgr.error_handler = error_callback;
-	event_mgr.warning_handler = warning_callback;
-	event_mgr.info_handler = info_callback;
-
-	/* set decoding parameters to default values */
-	opj_set_default_decoder_parameters(&parameters);
-
-	parameters.cp_reduce = base.getRawDiscardLevel();
-
-	/* decode the code-stream */
-	/* ---------------------- */
-
-	/* JPEG-2000 codestream */
-
-	/* get a decoder handle */
-	dinfo = opj_create_decompress(CODEC_J2K);
-
-	/* catch events using our callbacks and give a local context */
-	opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);			
-
-	/* setup the decoder decoding parameters using user parameters */
-	opj_setup_decoder(dinfo, &parameters);
-
-	/* open a byte stream */
-	cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize());
-
-	/* decode the stream and fill the image structure */
-	image = opj_decode(dinfo, cio);
-
-	/* close the byte stream */
-	opj_cio_close(cio);
-
-	/* free remaining structures */
-	if(dinfo)
-	{
-		opj_destroy_decompress(dinfo);
-	}
-
-	// The image decode failed if the return was NULL or the component
-	// count was zero.  The latter is just a sanity check before we
-	// dereference the array.
-	if(!image || !image->numcomps)
-	{
-		LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL;
-		if (image)
-		{
-			opj_image_destroy(image);
-		}
-
-		return true; // done
-	}
-
-	// sometimes we get bad data out of the cache - check to see if the decode succeeded
-	for (S32 i = 0; i < image->numcomps; i++)
-	{
-		if (image->comps[i].factor != base.getRawDiscardLevel())
-		{
-			// if we didn't get the discard level we're expecting, fail
-			opj_image_destroy(image);
-			base.mDecoding = false;
-			return true;
-		}
-	}
-	
-	if(image->numcomps <= first_channel)
-	{
-		LL_WARNS() << "trying to decode more channels than are present in image: numcomps: " << image->numcomps << " first_channel: " << first_channel << LL_ENDL;
-		if (image)
-		{
-			opj_image_destroy(image);
-		}
-			
-		return true;
-	}
-
-	// Copy image data into our raw image format (instead of the separate channel format
-
-	S32 img_components = image->numcomps;
-	S32 channels = img_components - first_channel;
-	if( channels > max_channel_count )
-		channels = max_channel_count;
-
-	// Component buffers are allocated in an image width by height buffer.
-	// The image placed in that buffer is ceil(width/2^factor) by
-	// ceil(height/2^factor) and if the factor isn't zero it will be at the
-	// top left of the buffer with black filled in the rest of the pixels.
-	// It is integer math so the formula is written in ceildivpo2.
-	// (Assuming all the components have the same width, height and
-	// factor.)
-	S32 comp_width = image->comps[0].w;
-	S32 f=image->comps[0].factor;
-	S32 width = ceildivpow2(image->x1 - image->x0, f);
-	S32 height = ceildivpow2(image->y1 - image->y0, f);
-	raw_image.resize(width, height, channels);
-	U8 *rawp = raw_image.getData();
-
-	// first_channel is what channel to start copying from
-	// dest is what channel to copy to.  first_channel comes from the
-	// argument, dest always starts writing at channel zero.
-	for (S32 comp = first_channel, dest=0; comp < first_channel + channels;
-		comp++, dest++)
-	{
-		if (image->comps[comp].data)
-		{
-			S32 offset = dest;
-			for (S32 y = (height - 1); y >= 0; y--)
-			{
-				for (S32 x = 0; x < width; x++)
-				{
-					rawp[offset] = image->comps[comp].data[y*comp_width + x];
-					offset += channels;
-				}
-			}
-		}
-		else // Some rare OpenJPEG versions have this bug.
-		{
-			LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image! (NULL comp data - OpenJPEG bug)" << LL_ENDL;
-			opj_image_destroy(image);
-
-			return true; // done
-		}
-	}
-
-	/* free image data structure */
-	opj_image_destroy(image);
-
-	return true; // done
+    llassert(user_data);
+    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
+    OPJ_SIZE_T remainder = (jpeg_codec->size - jpeg_codec->offset);
+    if (remainder <= 0)
+    {
+        jpeg_codec->offset = jpeg_codec->size;
+        // Indicate end of stream (hacky?)
+        return (OPJ_OFF_T)-1;
+    }
+    OPJ_SIZE_T to_read = llclamp(U32(bytes), U32(0), U32(remainder));
+    memcpy(buffer, jpeg_codec->buffer + jpeg_codec->offset, to_read);
+    jpeg_codec->offset += to_read;
+    return to_read;
 }
 
-
-bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible)
+static OPJ_SIZE_T opj_write(void * buffer, OPJ_SIZE_T bytes, void* user_data)
 {
-	const S32 MAX_COMPS = 5;
-	opj_cparameters_t parameters;	/* compression parameters */
-	opj_event_mgr_t event_mgr;		/* event manager */
-
-
-	/* 
-	configure the event callbacks (not required)
-	setting of each callback is optional 
-	*/
-	memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
-	event_mgr.error_handler = error_callback;
-	event_mgr.warning_handler = warning_callback;
-	event_mgr.info_handler = info_callback;
-
-	/* set encoding parameters to default values */
-	opj_set_default_encoder_parameters(&parameters);
-	parameters.cod_format = 0;
-	parameters.cp_disto_alloc = 1;
-
-	if (reversible)
-	{
-		parameters.tcp_numlayers = 1;
-		parameters.tcp_rates[0] = 0.0f;
-	}
-	else
-	{
-		parameters.tcp_numlayers = 5;
-                parameters.tcp_rates[0] = 1920.0f;
-                parameters.tcp_rates[1] = 480.0f;
-                parameters.tcp_rates[2] = 120.0f;
-                parameters.tcp_rates[3] = 30.0f;
-		parameters.tcp_rates[4] = 10.0f;
-		parameters.irreversible = 1;
-		if (raw_image.getComponents() >= 3)
-		{
-			parameters.tcp_mct = 1;
-		}
-	}
-
-	if (!comment_text)
-	{
-		parameters.cp_comment = (char *) "";
-	}
-	else
-	{
-		// Awful hacky cast, too lazy to copy right now.
-		parameters.cp_comment = (char *) comment_text;
-	}
-
-	//
-	// Fill in the source image from our raw image
-	//
-	OPJ_COLOR_SPACE color_space = CLRSPC_SRGB;
-	opj_image_cmptparm_t cmptparm[MAX_COMPS];
-	opj_image_t * image = NULL;
-	S32 numcomps = raw_image.getComponents();
-	S32 width = raw_image.getWidth();
-	S32 height = raw_image.getHeight();
-
-	memset(&cmptparm[0], 0, MAX_COMPS * sizeof(opj_image_cmptparm_t));
-	for(S32 c = 0; c < numcomps; c++) {
-		cmptparm[c].prec = 8;
-		cmptparm[c].bpp = 8;
-		cmptparm[c].sgnd = 0;
-		cmptparm[c].dx = parameters.subsampling_dx;
-		cmptparm[c].dy = parameters.subsampling_dy;
-		cmptparm[c].w = width;
-		cmptparm[c].h = height;
-	}
-
-	/* create the image */
-	image = opj_image_create(numcomps, &cmptparm[0], color_space);
-
-	image->x1 = width;
-	image->y1 = height;
-
-	S32 i = 0;
-	const U8 *src_datap = raw_image.getData();
-	for (S32 y = height - 1; y >= 0; y--)
-	{
-		for (S32 x = 0; x < width; x++)
-		{
-			const U8 *pixel = src_datap + (y*width + x) * numcomps;
-			for (S32 c = 0; c < numcomps; c++)
-			{
-				image->comps[c].data[i] = *pixel;
-				pixel++;
-			}
-			i++;
-		}
-	}
-
-
-
-	/* encode the destination image */
-	/* ---------------------------- */
-
-	int codestream_length;
-	opj_cio_t *cio = NULL;
-
-	/* get a J2K compressor handle */
-	opj_cinfo_t* cinfo = opj_create_compress(CODEC_J2K);
-
-	/* catch events using our callbacks and give a local context */
-	opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, stderr);			
-
-	/* setup the encoder parameters using the current image and using user parameters */
-	opj_setup_encoder(cinfo, &parameters, image);
-
-	/* open a byte stream for writing */
-	/* allocate memory for all tiles */
-	cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0);
-
-	/* encode the image */
-	bool bSuccess = opj_encode(cinfo, cio, image, NULL);
-	if (!bSuccess)
-	{
-		opj_cio_close(cio);
-		LL_DEBUGS("Texture") << "Failed to encode image." << LL_ENDL;
-		return false;
-	}
-	codestream_length = cio_tell(cio);
-
-	base.copyData(cio->buffer, codestream_length);
-	base.updateData(); // set width, height
-
-	/* close and free the byte stream */
-	opj_cio_close(cio);
-
-	/* free remaining compression structures */
-	opj_destroy_compress(cinfo);
-
-
-	/* free user parameters structure */
-	if(parameters.cp_matrice) free(parameters.cp_matrice);
-
-	/* free image data */
-	opj_image_destroy(image);
-	return true;
+    llassert(user_data);
+    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
+    OPJ_SIZE_T remainder = jpeg_codec->size - jpeg_codec->offset;
+    if (remainder < bytes)
+    {
+        OPJ_SIZE_T new_size = jpeg_codec->size + (bytes - remainder);
+        U8* new_buffer = (U8*)ll_aligned_malloc_16(new_size);
+        memcpy(new_buffer, jpeg_codec->buffer, jpeg_codec->offset);
+        U8* old_buffer = jpeg_codec->buffer;
+        jpeg_codec->buffer = new_buffer;
+        ll_aligned_free_16(old_buffer);
+        jpeg_codec->size = new_size;
+    }
+    memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, bytes);
+    jpeg_codec->offset += bytes;
+    return bytes;
 }
 
-bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
+static OPJ_OFF_T opj_skip(OPJ_OFF_T bytes, void* user_data)
 {
-	//
-	// FIXME: We get metadata by decoding the ENTIRE image.
-	//
-
-	// Update the raw discard level
-	base.updateRawDiscardLevel();
-
-	opj_dparameters_t parameters;	/* decompression parameters */
-	opj_event_mgr_t event_mgr;		/* event manager */
-	opj_image_t *image = NULL;
-
-	opj_dinfo_t* dinfo = NULL;	/* handle to a decompressor */
-	opj_cio_t *cio = NULL;
-
-
-	/* configure the event callbacks (not required) */
-	memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
-	event_mgr.error_handler = error_callback;
-	event_mgr.warning_handler = warning_callback;
-	event_mgr.info_handler = info_callback;
-
-	/* set decoding parameters to default values */
-	opj_set_default_decoder_parameters(&parameters);
+    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
+    jpeg_codec->offset += bytes;
+    if (jpeg_codec->offset > jpeg_codec->size)
+    {
+        jpeg_codec->offset = jpeg_codec->size;
+        // Indicate end of stream
+        return (OPJ_OFF_T)-1;
+    }
+    return bytes;
+}
 
-	// Only decode what's required to get the size data.
-	parameters.cp_limit_decoding=LIMIT_TO_MAIN_HEADER;
+static OPJ_BOOL opj_seek(OPJ_OFF_T bytes, void * user_data)
+{
+    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
+    jpeg_codec->offset += bytes;
+    jpeg_codec->offset = llclamp(U32(jpeg_codec->offset), U32(0), U32(jpeg_codec->size));
+    return OPJ_TRUE;
+}
 
-	//parameters.cp_reduce = mRawDiscardLevel;
+static void opj_free_user_data(void * user_data)
+{
+    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
+    // Don't free, data is managed externally
+    jpeg_codec->buffer = nullptr;
+    jpeg_codec->size = 0;
+    jpeg_codec->offset = 0;
+}
 
-	/* decode the code-stream */
-	/* ---------------------- */
+static void opj_free_user_data_write(void * user_data)
+{
+    JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
+    // Free, data was allocated here
+    ll_aligned_free_16(jpeg_codec->buffer);
+    jpeg_codec->buffer = nullptr;
+    jpeg_codec->size = 0;
+    jpeg_codec->offset = 0;
+}
 
-	/* JPEG-2000 codestream */
+class JPEG2KDecode : public JPEG2KBase
+{
+public:
+
+    JPEG2KDecode(S8 discardLevel)
+    {
+        memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+        memset(&parameters, 0, sizeof(opj_dparameters_t));
+        event_mgr.error_handler = error_callback;
+        event_mgr.warning_handler = warning_callback;
+        event_mgr.info_handler = info_callback;
+        opj_set_default_decoder_parameters(&parameters);
+        parameters.cp_reduce = discardLevel;
+    }
+
+    ~JPEG2KDecode()
+    {
+        if (decoder)
+        {
+            opj_destroy_codec(decoder);
+        }
+        decoder = nullptr;
+
+        if (image)
+        {
+            opj_image_destroy(image);
+        }
+        image = nullptr;
+
+        if (stream)
+        {
+            opj_stream_destroy(stream);
+        }
+        stream = nullptr;
+
+        if (codestream_info)
+        {
+            opj_destroy_cstr_info(&codestream_info);
+        }
+        codestream_info = nullptr;
+    }
+
+    bool readHeader(
+        U8* data,
+        U32 dataSize,
+        S32& widthOut,
+        S32& heightOut,
+        S32& components,
+        S32& discard_level)
+    {
+        parameters.flags |= OPJ_DPARAMETERS_DUMP_FLAG;
+
+        decoder = opj_create_decompress(OPJ_CODEC_J2K);
+
+        if (!opj_setup_decoder(decoder, &parameters))
+        {
+            return false;
+        }
+
+        if (stream)
+        {
+            opj_stream_destroy(stream);
+        }
+
+        stream = opj_stream_create(dataSize, true);
+        if (!stream)
+        {
+            return false;
+        }
+
+        opj_stream_set_user_data(stream, this, opj_free_user_data);
+        opj_stream_set_user_data_length(stream, dataSize);
+        opj_stream_set_read_function(stream, opj_read);
+        opj_stream_set_write_function(stream, opj_write);
+        opj_stream_set_skip_function(stream, opj_skip);
+        opj_stream_set_seek_function(stream, opj_seek);
+
+        buffer = data;
+        size = dataSize;
+        offset = 0;
+
+        // enable decoding partially loaded images
+        opj_decoder_set_strict_mode(decoder, OPJ_FALSE);
+
+        /* Read the main header of the codestream and if necessary the JP2 boxes*/
+        if (!opj_read_header((opj_stream_t*)stream, decoder, &image))
+        {
+            return false;
+        }
+
+        codestream_info = opj_get_cstr_info(decoder);
+
+        if (!codestream_info)
+        {
+            return false;
+        }
+
+        U32 tileDimX = codestream_info->tdx;
+        U32 tileDimY = codestream_info->tdy;
+        U32 tilesW = codestream_info->tw;
+        U32 tilesH = codestream_info->th;
+
+        widthOut = S32(tilesW * tileDimX);
+        heightOut = S32(tilesH * tileDimY);
+        components = codestream_info->nbcomps;
+
+        discard_level = 0;
+        while (tilesW > 1 && tilesH > 1 && discard_level < MAX_DISCARD_LEVEL)
+        {
+            discard_level++;
+            tilesW >>= 1;
+            tilesH >>= 1;
+        }
+
+        return true;
+    }
+
+    bool decode(U8* data, U32 dataSize, U32* channels, U8 discard_level)
+    {
+        parameters.flags &= ~OPJ_DPARAMETERS_DUMP_FLAG;
+
+        decoder = opj_create_decompress(OPJ_CODEC_J2K);
+        opj_setup_decoder(decoder, &parameters);
+
+        opj_set_info_handler(decoder, opj_info, this);
+        opj_set_warning_handler(decoder, opj_warn, this);
+        opj_set_error_handler(decoder, opj_error, this);
+
+        if (stream)
+        {
+            opj_stream_destroy(stream);
+        }
+
+        stream = opj_stream_create(dataSize, true);
+        if (!stream)
+        {
+            return false;
+        }
+
+        opj_stream_set_user_data(stream, this, opj_free_user_data);
+        opj_stream_set_user_data_length(stream, dataSize);
+        opj_stream_set_read_function(stream, opj_read);
+        opj_stream_set_write_function(stream, opj_write);
+        opj_stream_set_skip_function(stream, opj_skip);
+        opj_stream_set_seek_function(stream, opj_seek);
+
+        buffer = data;
+        size = dataSize;
+        offset = 0;
+
+        if (image)
+        {
+            opj_image_destroy(image);
+            image = nullptr;
+        }
+
+        // needs to happen before opj_read_header and opj_decode...
+        opj_set_decoded_resolution_factor(decoder, discard_level);
+
+        // enable decoding partially loaded images
+        opj_decoder_set_strict_mode(decoder, OPJ_FALSE);
+
+        if (!opj_read_header(stream, decoder, &image))
+        {
+            return false;
+        }
+
+        // needs to happen before decode which may fail
+        if (channels)
+        {
+            *channels = image->numcomps;
+        }
+
+        OPJ_BOOL decoded = opj_decode(decoder, stream, image);
+
+        // count was zero.  The latter is just a sanity check before we
+        // dereference the array.
+        if (!decoded || !image || !image->numcomps)
+        {
+            opj_end_decompress(decoder, stream);
+            return false;
+        }
+
+        opj_end_decompress(decoder, stream);
+
+        return true;
+    }
+
+    opj_image_t* getImage() { return image; }
+
+private:
+    opj_dparameters_t         parameters;
+    opj_event_mgr_t           event_mgr;
+    opj_image_t*              image = nullptr;
+    opj_codec_t*              decoder = nullptr;
+    opj_stream_t*             stream = nullptr;
+    opj_codestream_info_v2_t* codestream_info = nullptr;
+};
+
+class JPEG2KEncode : public JPEG2KBase
+{
+public:
+    const OPJ_UINT32 TILE_SIZE = 64 * 64 * 3;
+
+    JPEG2KEncode(const char* comment_text_in, bool reversible)
+    {
+        memset(&parameters, 0, sizeof(opj_cparameters_t));
+        memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+        event_mgr.error_handler = error_callback;
+        event_mgr.warning_handler = warning_callback;
+        event_mgr.info_handler = info_callback;
+
+        opj_set_default_encoder_parameters(&parameters);
+        parameters.cod_format = 0;
+        parameters.cp_disto_alloc = 1;
+        parameters.max_cs_size = (1 << 15);
+
+        if (reversible)
+        {
+            parameters.tcp_numlayers = 1;
+            parameters.tcp_rates[0] = 1.0f;
+        }
+        else
+        {
+            parameters.tcp_numlayers = 5;
+            parameters.tcp_rates[0] = 1920.0f;
+            parameters.tcp_rates[1] = 960.0f;
+            parameters.tcp_rates[2] = 480.0f;
+            parameters.tcp_rates[3] = 120.0f;
+            parameters.tcp_rates[4] = 30.0f;
+            parameters.irreversible = 1;
+            parameters.tcp_mct = 1;
+        }
+
+        if (comment_text)
+        {
+            free(comment_text);
+        }
+        comment_text = comment_text_in ? strdup(comment_text_in) : nullptr;
+
+        parameters.cp_comment = comment_text ? comment_text : (char*)"no comment";
+        llassert(parameters.cp_comment);
+    }
+
+    ~JPEG2KEncode()
+    {
+        if (encoder)
+        {
+            opj_destroy_codec(encoder);
+        }
+        encoder = nullptr;
+
+        if (image)
+        {
+            opj_image_destroy(image);
+        }
+        image = nullptr;
+
+        if (stream)
+        {
+            opj_stream_destroy(stream);
+        }
+        stream = nullptr;
+
+        if (comment_text)
+        {
+            free(comment_text);
+        }
+        comment_text = nullptr;
+    }
+
+    bool encode(const LLImageRaw& rawImageIn, LLImageJ2C &compressedImageOut)
+    {
+        setImage(rawImageIn);
+
+        encoder = opj_create_compress(OPJ_CODEC_J2K);
+
+        parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0;
+        parameters.cod_format = OPJ_CODEC_J2K;
+        parameters.numresolution = MAX_ENCODED_DISCARD_LEVELS;
+        parameters.prog_order = OPJ_RLCP;
+        parameters.cp_disto_alloc = 1;
+
+        opj_setup_encoder(encoder, &parameters, image);
+
+        opj_set_info_handler(encoder, opj_info, this);
+        opj_set_warning_handler(encoder, opj_warn, this);
+        opj_set_error_handler(encoder, opj_error, this);
+
+        U32 tile_count = (rawImageIn.getWidth() >> 6) * (rawImageIn.getHeight() >> 6);
+        U32 data_size_guess = tile_count * TILE_SIZE;
+
+        // will be freed in opj_free_user_data_write
+        buffer = (U8*)ll_aligned_malloc_16(data_size_guess);
+        size = data_size_guess;
+        offset = 0;
+
+#if LL_DEBUG
+        memset(buffer, 0, data_size_guess);
+#endif
 
-	/* get a decoder handle */
-	dinfo = opj_create_decompress(CODEC_J2K);
+        if (stream)
+        {
+            opj_stream_destroy(stream);
+        }
+
+        stream = opj_stream_create(data_size_guess, false);
+        if (!stream)
+        {
+            return false;
+        }
+
+        opj_stream_set_user_data(stream, this, opj_free_user_data_write);
+        opj_stream_set_user_data_length(stream, data_size_guess);
+        opj_stream_set_read_function(stream, opj_read);
+        opj_stream_set_write_function(stream, opj_write);
+        opj_stream_set_skip_function(stream, opj_skip);
+        opj_stream_set_seek_function(stream, opj_seek);
+
+        OPJ_BOOL started = opj_start_compress(encoder, image, stream);
+
+        if (!started)
+        {
+            return false;
+        }
+
+        if (!opj_encode(encoder, stream))
+        {
+            return false;
+        }
+
+        OPJ_BOOL encoded = opj_end_compress(encoder, stream);
+
+        // if we successfully encoded, then stream out the compressed data...
+        if (encoded)
+        {
+            // "append" (set) the data we "streamed" (memcopied) for writing to the formatted image
+            // with side-effect of setting the actually encoded size  to same
+            compressedImageOut.allocateData(offset);
+            memcpy(compressedImageOut.getData(), buffer, offset);
+            compressedImageOut.updateData(); // update width, height etc from header
+        }
+        return encoded;
+    }
+
+    void setImage(const LLImageRaw& raw)
+    {
+        opj_image_cmptparm_t cmptparm[MAX_ENCODED_DISCARD_LEVELS];
+        memset(&cmptparm[0], 0, MAX_ENCODED_DISCARD_LEVELS * sizeof(opj_image_cmptparm_t));
+
+        S32 numcomps = raw.getComponents();
+        S32 width = raw.getWidth();
+        S32 height = raw.getHeight();
+
+        for (S32 c = 0; c < numcomps; c++)
+        {
+            cmptparm[c].prec = 8;
+            cmptparm[c].bpp = 8;
+            cmptparm[c].sgnd = 0;
+            cmptparm[c].dx = parameters.subsampling_dx;
+            cmptparm[c].dy = parameters.subsampling_dy;
+            cmptparm[c].w = width;
+            cmptparm[c].h = height;
+        }
+
+        image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB);
+
+        image->x1 = width;
+        image->y1 = height;
+
+        const U8 *src_datap = raw.getData();
+
+        S32 i = 0;
+        for (S32 y = height - 1; y >= 0; y--)
+        {
+            for (S32 x = 0; x < width; x++)
+            {
+                const U8 *pixel = src_datap + (y*width + x) * numcomps;
+                for (S32 c = 0; c < numcomps; c++)
+                {
+                    image->comps[c].data[i] = *pixel;
+                    pixel++;
+                }
+                i++;
+            }
+        }
+
+        // This likely works, but there seems to be an issue openjpeg side
+        // check over after gixing that.
+
+        // De-interleave to component plane data
+        /*
+        switch (numcomps)
+        {
+        case 0:
+        default:
+            break;
+
+        case 1:
+        {
+            U32 rBitDepth = image->comps[0].bpp;
+            U32 bytesPerPixel = rBitDepth >> 3;
+            memcpy(image->comps[0].data, src, width * height * bytesPerPixel);
+        }
+        break;
+
+        case 2:
+        {
+            U32 rBitDepth = image->comps[0].bpp;
+            U32 gBitDepth = image->comps[1].bpp;
+            U32 totalBitDepth = rBitDepth + gBitDepth;
+            U32 bytesPerPixel = totalBitDepth >> 3;
+            U32 stride = width * bytesPerPixel;
+            U32 offset = 0;
+            for (S32 y = height - 1; y >= 0; y--)
+            {
+                const U8* component = src + (y * stride);
+                for (S32 x = 0; x < width; x++)
+                {
+                    image->comps[0].data[offset] = *component++;
+                    image->comps[1].data[offset] = *component++;
+                    offset++;
+                }
+            }
+        }
+        break;
+
+        case 3:
+        {
+            U32 rBitDepth = image->comps[0].bpp;
+            U32 gBitDepth = image->comps[1].bpp;
+            U32 bBitDepth = image->comps[2].bpp;
+            U32 totalBitDepth = rBitDepth + gBitDepth + bBitDepth;
+            U32 bytesPerPixel = totalBitDepth >> 3;
+            U32 stride = width * bytesPerPixel;
+            U32 offset = 0;
+            for (S32 y = height - 1; y >= 0; y--)
+            {
+                const U8* component = src + (y * stride);
+                for (S32 x = 0; x < width; x++)
+                {
+                    image->comps[0].data[offset] = *component++;
+                    image->comps[1].data[offset] = *component++;
+                    image->comps[2].data[offset] = *component++;
+                    offset++;
+                }
+            }
+        }
+        break;
+
+
+        case 4:
+        {
+            U32 rBitDepth = image->comps[0].bpp;
+            U32 gBitDepth = image->comps[1].bpp;
+            U32 bBitDepth = image->comps[2].bpp;
+            U32 aBitDepth = image->comps[3].bpp;
+
+            U32 totalBitDepth = rBitDepth + gBitDepth + bBitDepth + aBitDepth;
+            U32 bytesPerPixel = totalBitDepth >> 3;
+
+            U32 stride = width * bytesPerPixel;
+            U32 offset = 0;
+            for (S32 y = height - 1; y >= 0; y--)
+            {
+                const U8* component = src + (y * stride);
+                for (S32 x = 0; x < width; x++)
+                {
+                    image->comps[0].data[offset] = *component++;
+                    image->comps[1].data[offset] = *component++;
+                    image->comps[2].data[offset] = *component++;
+                    image->comps[3].data[offset] = *component++;
+                    offset++;
+                }
+            }
+        }
+        break;
+        }*/
+    }
+
+    opj_image_t* getImage() { return image; }
+
+private:
+    opj_cparameters_t   parameters;
+    opj_event_mgr_t     event_mgr;
+    opj_image_t*        image = nullptr;
+    opj_codec_t*        encoder = nullptr;
+    opj_stream_t*       stream = nullptr;
+    char*               comment_text = nullptr;
+};
 
-	/* catch events using our callbacks and give a local context */
-	opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);			
 
-	/* setup the decoder decoding parameters using user parameters */
-	opj_setup_decoder(dinfo, &parameters);
+LLImageJ2COJ::LLImageJ2COJ()
+	: LLImageJ2CImpl()
+{
+}
 
-	/* open a byte stream */
-	cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize());
 
-	/* decode the stream and fill the image structure */
-	image = opj_decode(dinfo, cio);
+LLImageJ2COJ::~LLImageJ2COJ()
+{
+}
 
-	/* close the byte stream */
-	opj_cio_close(cio);
+bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region)
+{
+    base.mDiscardLevel = discard_level;
+	return false;
+}
 
-	/* free remaining structures */
-	if(dinfo)
-	{
-		opj_destroy_decompress(dinfo);
-	}
+bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size, int precincts_size, int levels)
+{
+	// No specific implementation for this method in the OpenJpeg case
+	return false;
+}
 
-	if(!image)
-	{
-		LL_WARNS() << "ERROR -> getMetadata: failed to decode image!" << LL_ENDL;
-		return false;
-	}
+bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
+{
+    JPEG2KDecode decoder(0);
+
+    U32 image_channels = 0;
+    S32 data_size = base.getDataSize();
+    S32 max_bytes = (base.getMaxBytes() ? base.getMaxBytes() : data_size);
+    bool decoded = decoder.decode(base.getData(), max_bytes, &image_channels, base.mDiscardLevel);
+
+    // set correct channel count early so failed decodes don't miss it...
+    S32 channels = (S32)image_channels - first_channel;
+    channels = llmin(channels, max_channel_count);
+
+    if (!decoded)
+    {
+        // reset the channel count if necessary
+        if (raw_image.getComponents() != channels)
+        {
+            raw_image.resize(raw_image.getWidth(), raw_image.getHeight(), S8(channels));
+        }
+
+        LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL;
+        return true; // done
+    }
+
+    opj_image_t *image = decoder.getImage();
+
+    // Component buffers are allocated in an image width by height buffer.
+    // The image placed in that buffer is ceil(width/2^factor) by
+    // ceil(height/2^factor) and if the factor isn't zero it will be at the
+    // top left of the buffer with black filled in the rest of the pixels.
+    // It is integer math so the formula is written in ceildivpo2.
+    // (Assuming all the components have the same width, height and
+    // factor.)
+    U32 comp_width = image->comps[0].w; // leave this unshifted by 'f' discard factor, the strides are always for the full buffer width
+    U32 f = image->comps[0].factor;
+
+    // do size the texture to the mem we'll acrually use...
+    U32 width = image->comps[0].w;
+    U32 height = image->comps[0].h;
+
+    raw_image.resize(U16(width), U16(height), S8(channels));
+
+    U8 *rawp = raw_image.getData();
+
+    // first_channel is what channel to start copying from
+    // dest is what channel to copy to.  first_channel comes from the
+    // argument, dest always starts writing at channel zero.
+    for (S32 comp = first_channel, dest = 0; comp < first_channel + channels; comp++, dest++)
+    {
+        llassert(image->comps[comp].data);
+        if (image->comps[comp].data)
+        {
+            S32 offset = dest;
+            for (S32 y = (height - 1); y >= 0; y--)
+            {
+                for (S32 x = 0; x < width; x++)
+                {
+                    rawp[offset] = image->comps[comp].data[y*comp_width + x];
+                    offset += channels;
+                }
+            }
+        }
+        else // Some rare OpenJPEG versions have this bug.
+        {
+            LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed! (OpenJPEG bug)" << LL_ENDL;
+        }
+    }
+
+    base.setDiscardLevel(f);
+
+    return true; // done
+}
 
-	// Copy image data into our raw image format (instead of the separate channel format
-	S32 width = 0;
-	S32 height = 0;
 
-	S32 img_components = image->numcomps;
-	width = image->x1 - image->x0;
-	height = image->y1 - image->y0;
-	base.setSize(width, height, img_components);
+bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible)
+{
+    JPEG2KEncode encode(comment_text, reversible);
+    bool encoded = encode.encode(raw_image, base);
+    if (encoded)
+    {
+        LL_WARNS() << "Openjpeg encoding implementation isn't complete, returning false" << LL_ENDL;
+    }
+    //return encoded;
+    return false;
+}
 
-	/* free image data structure */
-	opj_image_destroy(image);
-	return true;
+bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
+{
+    JPEG2KDecode decode(0);
+
+    S32 width = 0;
+    S32 height = 0;
+    S32 components = 0;
+    S32 discard_level = 0;
+
+    U32 dataSize = base.getDataSize();
+    U8* data = base.getData();
+    bool header_read = decode.readHeader(data, dataSize, width, height, components, discard_level);
+    if (!header_read)
+    {
+        return false;
+    }
+
+    base.mDiscardLevel = discard_level;
+    base.setSize(width, height, components);
+    return true;
 }
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 7d78ec9e3c..4fe83f0c45 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -1825,9 +1825,9 @@ if (WINDOWS)
       ${SHARED_LIB_STAGING_DIR}/Release/libcollada14dom22.dll
       ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libcollada14dom22.dll
       ${SHARED_LIB_STAGING_DIR}/Debug/libcollada14dom22-d.dll
-      ${SHARED_LIB_STAGING_DIR}/Release/openjpeg.dll
-      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjpeg.dll
-      ${SHARED_LIB_STAGING_DIR}/Debug/openjpegd.dll
+      ${SHARED_LIB_STAGING_DIR}/Release/openjp2.dll
+      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjp2.dll
+      ${SHARED_LIB_STAGING_DIR}/Debug/openjp2.dll
       ${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll
       ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll
       ${SHARED_LIB_STAGING_DIR}/Debug/libhunspell.dll
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index de5ac5ed3d..a1d2d12779 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -518,7 +518,7 @@ class WindowsManifest(ViewerManifest):
                 self.path("alut.dll")
 
             # For textures
-            self.path("openjpeg.dll")
+            self.path("openjp2.dll")
 
             # Uriparser
             self.path("uriparser.dll")
@@ -1498,7 +1498,7 @@ class Linux_i686_Manifest(LinuxManifest):
             self.path("libdirectfb-1.*.so.*")
             self.path("libfusion-1.*.so.*")
             self.path("libdirect-1.*.so.*")
-            self.path("libopenjpeg.so*")
+            self.path("libopenjp2.so*")
             self.path("libdirectfb-1.4.so.5")
             self.path("libfusion-1.4.so.5")
             self.path("libdirect-1.4.so.5*")
-- 
cgit v1.2.3


From 5404708de0cce19cb4b35fae10d78c447932e4ba Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 3 Oct 2022 21:09:47 +0300
Subject: SL-16542 Fix mac build not having some of openjpeg headers

---
 autobuild.xml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/autobuild.xml b/autobuild.xml
index 801da4e773..bdc5e085ef 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2195,9 +2195,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>89e6436b256c98818fa736a460a04016</string>
+              <string>6bb03fe0b9dd44d3850ae9cae2161d76</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105184/920580/openjpeg-2.5.0.575325-darwin64-575325.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105416/922669/openjpeg-2.5.0.575468-darwin64-575468.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -2219,9 +2219,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>97b15b312c2dec38981ec63bd77d0491</string>
+              <string>a01d66d72d7c865364cd5761e56e8735</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105185/920592/openjpeg-2.5.0.575325-windows-575325.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105418/922681/openjpeg-2.5.0.575468-windows-575468.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -2231,16 +2231,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>cdb55c8b074ce4f166ebc597f13f62a1</string>
+              <string>8bd727bc7d6e0e6223e9ea49c14dd511</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105186/920591/openjpeg-2.5.0.575325-windows64-575325.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105419/922682/openjpeg-2.5.0.575468-windows64-575468.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
         <key>version</key>
-        <string>2.5.0.575325</string>
+        <string>2.5.0.575468</string>
       </map>
       <key>openssl</key>
       <map>
-- 
cgit v1.2.3


From f7cc8e4c8c70bd6a48b9c3a8e99bef13ebca7d89 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 3 Oct 2022 23:47:19 +0300
Subject: SL-18047 Birthdate one day early

---
 indra/newview/llpanelprofile.cpp                                | 2 ++
 indra/newview/skins/default/xui/en/panel_profile_secondlife.xml | 6 +++++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index f4eaa78f11..d3898afcbd 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -1280,6 +1280,8 @@ void LLPanelProfileSecondLife::fillRightsData()
 
 void LLPanelProfileSecondLife::fillAgeData(const LLDate &born_on)
 {
+    // Date from server comes already converted to stl timezone,
+    // so display it as an UTC + 0
     std::string name_and_date = getString("date_format");
     LLSD args_name;
     args_name["datetime"] = (S32)born_on.secondsSinceEpoch();
diff --git a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
index 551b477876..777b37d666 100644
--- a/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile_secondlife.xml
@@ -9,9 +9,13 @@
  follows="all"
  layout="topleft"
 >
+  <!--
+  Date from server comes already converted to stl timezone,
+  so display it as an UTC+0
+  -->
    <string 
     name="date_format"
-    value="SL birthdate: [mth,datetime,slt] [day,datetime,slt], [year,datetime,slt]" />
+    value="SL birthdate: [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]" />
    <string
     name="age_format"
     value="[AGE]" />
-- 
cgit v1.2.3


From d6e8a9109e3adfcd28eee4d2509c0e2818c3caff Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 4 Oct 2022 13:04:52 +0300
Subject: SL-16542 Fix openjpeg seeking and encoding

---
 indra/llimagej2coj/llimagej2coj.cpp | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 944629cf4c..12985c3c7f 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -171,19 +171,28 @@ static OPJ_OFF_T opj_skip(OPJ_OFF_T bytes, void* user_data)
 {
     JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
     jpeg_codec->offset += bytes;
+
     if (jpeg_codec->offset > jpeg_codec->size)
     {
         jpeg_codec->offset = jpeg_codec->size;
         // Indicate end of stream
         return (OPJ_OFF_T)-1;
     }
+
+    if (jpeg_codec->offset < 0)
+    {
+        // Shouldn't be possible?
+        jpeg_codec->offset = 0;
+        return (OPJ_OFF_T)-1;
+    }
+
     return bytes;
 }
 
 static OPJ_BOOL opj_seek(OPJ_OFF_T bytes, void * user_data)
 {
     JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
-    jpeg_codec->offset += bytes;
+    jpeg_codec->offset = bytes;
     jpeg_codec->offset = llclamp(U32(jpeg_codec->offset), U32(0), U32(jpeg_codec->size));
     return OPJ_TRUE;
 }
@@ -420,7 +429,7 @@ public:
         event_mgr.info_handler = info_callback;
 
         opj_set_default_encoder_parameters(&parameters);
-        parameters.cod_format = 0;
+        parameters.cod_format = OPJ_CODEC_J2K;
         parameters.cp_disto_alloc = 1;
         parameters.max_cs_size = (1 << 15);
 
@@ -486,11 +495,13 @@ public:
 
         parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0;
         parameters.cod_format = OPJ_CODEC_J2K;
-        parameters.numresolution = MAX_ENCODED_DISCARD_LEVELS;
         parameters.prog_order = OPJ_RLCP;
         parameters.cp_disto_alloc = 1;
 
-        opj_setup_encoder(encoder, &parameters, image);
+        if (!opj_setup_encoder(encoder, &parameters, image))
+        {
+            return false;
+        }
 
         opj_set_info_handler(encoder, opj_info, this);
         opj_set_warning_handler(encoder, opj_warn, this);
@@ -504,9 +515,7 @@ public:
         size = data_size_guess;
         offset = 0;
 
-#if LL_DEBUG
         memset(buffer, 0, data_size_guess);
-#endif
 
         if (stream)
         {
@@ -804,8 +813,8 @@ bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
     {
         LL_WARNS() << "Openjpeg encoding implementation isn't complete, returning false" << LL_ENDL;
     }
-    //return encoded;
-    return false;
+    return encoded;
+    //return false;
 }
 
 bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
-- 
cgit v1.2.3


From d4506967ed0ab7e450313f3635b07a6e2bfa6ae0 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 4 Oct 2022 17:53:21 +0300
Subject: SL-16542 Openjpeg's header should have remained unchanged.

---
 indra/cmake/FindOpenJPEG.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/cmake/FindOpenJPEG.cmake b/indra/cmake/FindOpenJPEG.cmake
index afe319fb06..2d4353b54f 100644
--- a/indra/cmake/FindOpenJPEG.cmake
+++ b/indra/cmake/FindOpenJPEG.cmake
@@ -9,7 +9,7 @@
 # also defined, but not for general use are
 #  OPENJPEG_LIBRARY, where to find the OpenJPEG library.
 
-FIND_PATH(OPENJPEG_INCLUDE_DIR openjp2.h
+FIND_PATH(OPENJPEG_INCLUDE_DIR openjpeg.h
 /usr/local/include/openjpeg
 /usr/local/include
 /usr/include/openjpeg
-- 
cgit v1.2.3


From ad48f88d1f259248cf645e50437dd64c2cf58fb3 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 4 Oct 2022 19:51:16 +0300
Subject: SL-16542 Fixed openjpeg library's name

---
 autobuild.xml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/autobuild.xml b/autobuild.xml
index bdc5e085ef..1a353fe2e9 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2195,9 +2195,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>6bb03fe0b9dd44d3850ae9cae2161d76</string>
+              <string>8114c6a7e499ea20d325db0de08ce30a</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105416/922669/openjpeg-2.5.0.575468-darwin64-575468.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105469/923024/openjpeg-2.5.0.575496-darwin64-575496.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -2219,9 +2219,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>a01d66d72d7c865364cd5761e56e8735</string>
+              <string>edc9388870d951632a6d595792293e05</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105418/922681/openjpeg-2.5.0.575468-windows-575468.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105472/923036/openjpeg-2.5.0.575496-windows-575496.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -2231,16 +2231,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>8bd727bc7d6e0e6223e9ea49c14dd511</string>
+              <string>b95f0732f2388ebb0ddf33d4a30e0ff1</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105419/922682/openjpeg-2.5.0.575468-windows64-575468.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/105471/923037/openjpeg-2.5.0.575496-windows64-575496.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
         <key>version</key>
-        <string>2.5.0.575468</string>
+        <string>2.5.0.575496</string>
       </map>
       <key>openssl</key>
       <map>
-- 
cgit v1.2.3


From 9b2435cbb19d44f72c821cec8f9b17c0b4984905 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Wed, 5 Oct 2022 22:40:34 +0300
Subject: SL-18295 Use yellow color on all floaters' search fields when
 non-empty

---
 indra/newview/llfloaterpathfindinglinksets.cpp     | 19 ++++-----
 indra/newview/llfloaterpathfindinglinksets.h       |  5 ++-
 .../xui/en/floater_pathfinding_linksets.xml        | 49 ++++++++++++----------
 .../skins/default/xui/en/widgets/filter_editor.xml |  2 +-
 .../skins/default/xui/en/widgets/search_editor.xml |  2 +-
 5 files changed, 40 insertions(+), 37 deletions(-)

diff --git a/indra/newview/llfloaterpathfindinglinksets.cpp b/indra/newview/llfloaterpathfindinglinksets.cpp
index 1e46d7a402..03aede94c6 100644
--- a/indra/newview/llfloaterpathfindinglinksets.cpp
+++ b/indra/newview/llfloaterpathfindinglinksets.cpp
@@ -44,6 +44,7 @@
 #include "llpathfindinglinkset.h"
 #include "llpathfindinglinksetlist.h"
 #include "llpathfindingmanager.h"
+#include "llsearcheditor.h"
 #include "llscrolllistitem.h"
 #include "llsd.h"
 #include "lltextbase.h"
@@ -114,17 +115,13 @@ BOOL LLFloaterPathfindingLinksets::postBuild()
 {
 	mBeaconColor = LLUIColorTable::getInstance()->getColor("PathfindingLinksetBeaconColor");
 
-	mFilterByName = findChild<LLLineEditor>("filter_by_name");
-	llassert(mFilterByName != NULL);
-	mFilterByName->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this));
-	mFilterByName->setSelectAllonFocusReceived(true);
-	mFilterByName->setCommitOnFocusLost(true);
-
-	mFilterByDescription = findChild<LLLineEditor>("filter_by_description");
-	llassert(mFilterByDescription != NULL);
-	mFilterByDescription->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this));
-	mFilterByDescription->setSelectAllonFocusReceived(true);
-	mFilterByDescription->setCommitOnFocusLost(true);
+    mFilterByName = getChild<LLSearchEditor>("filter_by_name");
+    mFilterByName->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this));
+    mFilterByName->setCommitOnFocusLost(true);
+
+    mFilterByDescription = getChild<LLSearchEditor>("filter_by_description");
+    mFilterByDescription->setCommitCallback(boost::bind(&LLFloaterPathfindingLinksets::onApplyAllFilters, this));
+    mFilterByDescription->setCommitOnFocusLost(true);
 
 	mFilterByLinksetUse = findChild<LLComboBox>("filter_by_linkset_use");
 	llassert(mFilterByLinksetUse != NULL);
diff --git a/indra/newview/llfloaterpathfindinglinksets.h b/indra/newview/llfloaterpathfindinglinksets.h
index 7149da9215..a954d8a8ec 100644
--- a/indra/newview/llfloaterpathfindinglinksets.h
+++ b/indra/newview/llfloaterpathfindinglinksets.h
@@ -42,6 +42,7 @@ class LLSD;
 class LLTextBase;
 class LLUICtrl;
 class LLVector3;
+class LLSearchEditor;
 
 class LLFloaterPathfindingLinksets : public LLFloaterPathfindingObjects
 {
@@ -105,8 +106,8 @@ private:
 	LLPathfindingLinkset::ELinksetUse convertToLinksetUse(LLSD pXuiValue) const;
 	LLSD                              convertToXuiValue(LLPathfindingLinkset::ELinksetUse pLinksetUse) const;
 
-	LLLineEditor     *mFilterByName;
-	LLLineEditor     *mFilterByDescription;
+    LLSearchEditor   *mFilterByName;
+    LLSearchEditor   *mFilterByDescription;
 	LLComboBox       *mFilterByLinksetUse;
 	LLComboBox       *mEditLinksetUse;
 	LLScrollListItem *mEditLinksetUseUnset;
diff --git a/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
index 41384a77b8..59117c0178 100644
--- a/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
+++ b/indra/newview/skins/default/xui/en/floater_pathfinding_linksets.xml
@@ -82,17 +82,20 @@
         width="62">
       Name
     </text>
-    <line_editor
-        border_style="line"
-        border_thickness="1"
-        follows="left|top"
-        height="20"
-        layout="topleft"
-        left_pad="0"
-        top_pad="-18"
-        max_length_chars="255"
-        name="filter_by_name"
-        width="161" />
+
+    <search_editor
+       follows="left|top"
+       search_button_visible="false"
+       height="20"
+       text_readonly_color="DkGray"
+       label="Objects by Name"
+       layout="topleft"
+       left_pad="0"
+       top_pad="-18"
+       name="filter_by_name"
+       select_on_focus="true"
+       width="161">
+    </search_editor>
     <text
         name="linksets_desc_label"
         height="13"
@@ -108,17 +111,19 @@
         width="88">
       Description
     </text>
-    <line_editor
-        border_style="line"
-        border_thickness="1"
-        follows="left|top"
-        height="20"
-        layout="topleft"
-        left_pad="0"
-        top_pad="-17"
-        max_length_chars="255"
-        name="filter_by_description"
-        width="162" />
+    <search_editor
+       follows="left|top"
+       search_button_visible="false"
+       height="20"
+       text_readonly_color="DkGray"
+       label="Objects by Description"
+       layout="topleft"
+       left_pad="0"
+       top_pad="-17"
+       name="filter_by_description"
+       select_on_focus="true"
+       width="162">
+    </search_editor>
     <combo_box
         height="20"
         layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/widgets/filter_editor.xml b/indra/newview/skins/default/xui/en/widgets/filter_editor.xml
index 1c4822b8d5..9c80deeafc 100644
--- a/indra/newview/skins/default/xui/en/widgets/filter_editor.xml
+++ b/indra/newview/skins/default/xui/en/widgets/filter_editor.xml
@@ -6,7 +6,7 @@
   text_pad_left="7"
   select_on_focus="true"
   text_tentative_color="TextFgTentativeColor"
-  highlight_text_field="false"
+  highlight_text_field="true"
   background_image="TextField_Search_Off"
   background_image_disabled="TextField_Search_Disabled"
   background_image_focused="TextField_Search_Active"
diff --git a/indra/newview/skins/default/xui/en/widgets/search_editor.xml b/indra/newview/skins/default/xui/en/widgets/search_editor.xml
index dc5a07bf4f..18d99f1ed1 100644
--- a/indra/newview/skins/default/xui/en/widgets/search_editor.xml
+++ b/indra/newview/skins/default/xui/en/widgets/search_editor.xml
@@ -7,7 +7,7 @@
   text_pad_right="6" 
   select_on_focus="true"
   text_tentative_color="TextFgTentativeColor"
-  highlight_text_field="false"
+  highlight_text_field="true"
   background_image="TextField_Search_Off"
   background_image_disabled="TextField_Search_Disabled"
   background_image_focused="TextField_Search_Active"
-- 
cgit v1.2.3


From 072844f74449903296077d1eaaa62498169028e0 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 7 Oct 2022 00:28:10 +0300
Subject: SL-18311 Change "Upgrade to Premium" label in menu

---
 indra/newview/skins/default/xui/en/strings.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 6f95e282ca..b88a01f035 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -3983,7 +3983,7 @@ Please check http://status.secondlifegrid.net to see if there is a known problem
   <string name="Premium_PlusMembership">Premium Plus</string>
   <string name="InternalMembership">Internal</string> <!-- No need to translate -->
 
-  <string name="MembershipUpgradeText">Upgrade to Premium</string>
+  <string name="MembershipUpgradeText">Change membership plan...</string>
   <string name="MembershipPremiumText">My Premium membership</string>
 
   <!-- Question strings for delete items notifications -->
-- 
cgit v1.2.3


From 6f946b6b16bacfcd263f3c029d22d840b044cae8 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 11 Oct 2022 15:21:23 +0300
Subject: SL-18165 Tighten up some command permissions

---
 indra/newview/llbuycurrencyhtml.cpp         |  2 +-
 indra/newview/llcommandhandler.cpp          | 82 ++++++++++++++++--------
 indra/newview/llcommandhandler.h            | 10 +++
 indra/newview/llfloatersearch.cpp           |  2 +-
 indra/newview/llfloaterworldmap.cpp         |  4 +-
 indra/newview/llgroupactions.cpp            | 27 +++++++-
 indra/newview/llpaneleditwearable.cpp       |  2 +-
 indra/newview/llpanelprofile.cpp            | 24 +++++++
 indra/newview/llpanelprofileclassifieds.cpp | 24 +++++++
 indra/newview/llpanelprofilepicks.cpp       | 24 +++++++
 indra/newview/llviewerfloaterreg.cpp        | 98 ++++++++++++++++++++++++++++-
 indra/newview/llviewerinventory.cpp         |  2 +-
 12 files changed, 266 insertions(+), 35 deletions(-)

diff --git a/indra/newview/llbuycurrencyhtml.cpp b/indra/newview/llbuycurrencyhtml.cpp
index 1c69dadb12..7ad06f8eaa 100644
--- a/indra/newview/llbuycurrencyhtml.cpp
+++ b/indra/newview/llbuycurrencyhtml.cpp
@@ -41,7 +41,7 @@ class LLBuyCurrencyHTMLHandler :
 {
 public:
 	// requests will be throttled from a non-trusted browser
-	LLBuyCurrencyHTMLHandler() : LLCommandHandler( "buycurrencyhtml", UNTRUSTED_ALLOW ) {}
+	LLBuyCurrencyHTMLHandler() : LLCommandHandler( "buycurrencyhtml", UNTRUSTED_THROTTLE) {}
 
 	bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web)
 	{
diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp
index 23e2271eae..74f37961c7 100644
--- a/indra/newview/llcommandhandler.cpp
+++ b/indra/newview/llcommandhandler.cpp
@@ -39,6 +39,7 @@
 #define THROTTLE_PERIOD    5    // required seconds between throttled commands
 
 static LLCommandDispatcherListener sCommandDispatcherListener;
+const std::string LLCommandHandler::NAV_TYPE_CLICKED = "clicked";
 
 //---------------------------------------------------------------------------
 // Underlying registry for command handlers, not directly accessible.
@@ -64,6 +65,9 @@ public:
 				  bool trusted_browser);
 
 private:
+    void notifySlurlBlocked();
+    void notifySlurlThrottled();
+
 	friend LLSD LLCommandDispatcher::enumerate();
 	std::map<std::string, LLCommandHandlerInfo> mMap;
 };
@@ -96,8 +100,6 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
 										const std::string& nav_type,
 										bool trusted_browser)
 {
-	static bool slurl_blocked = false;
-	static bool slurl_throttled = false;
 	static F64 last_throttle_time = 0.0;
 	F64 cur_time = 0.0;
 	std::map<std::string, LLCommandHandlerInfo>::iterator it = mMap.find(cmd);
@@ -115,44 +117,45 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
 			// block request from external browser, but report as
 			// "handled" because it was well formatted.
 			LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL;
-			if (! slurl_blocked)
-			{
-				if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT)
-				{
-					// Note: commands can arrive before we initialize everything we need for Notification.
-					LLNotificationsUtil::add("BlockedSLURL");
-				}
-				slurl_blocked = true;
-			}
+            notifySlurlBlocked();
 			return true;
 
+        case LLCommandHandler::UNTRUSTED_CLICK_ONLY:
+            if (nav_type == LLCommandHandler::NAV_TYPE_CLICKED
+                && info.mHandler->canHandleUntrusted(params, query_map, web, nav_type))
+            {
+                break;
+            }
+            LL_WARNS_ONCE("SLURL") << "Blocked SLURL click-only command " << cmd << " from untrusted browser" << LL_ENDL;
+            notifySlurlBlocked();
+            return true;
+
 		case LLCommandHandler::UNTRUSTED_THROTTLE:
+			//skip initial request from external browser before STATE_BROWSER_INIT
+			if (LLStartUp::getStartupState() == STATE_FIRST)
+			{
+				return true;
+			}
+            if (!info.mHandler->canHandleUntrusted(params, query_map, web, nav_type))
+            {
+                LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL;
+                notifySlurlBlocked();
+                return true;
+            }
 			// if users actually click on a link, we don't need to throttle it
 			// (throttling mechanism is used to prevent an avalanche of clicks via
 			// javascript
-			if ( nav_type == "clicked" )
+			if (nav_type == LLCommandHandler::NAV_TYPE_CLICKED)
 			{
 				break;
 			}
-			//skip initial request from external browser before STATE_BROWSER_INIT
-			if (LLStartUp::getStartupState() == STATE_FIRST)
-			{
-				return true;
-			}
 			cur_time = LLTimer::getElapsedSeconds();
 			if (cur_time < last_throttle_time + THROTTLE_PERIOD)
 			{
 				// block request from external browser if it happened
 				// within THROTTLE_PERIOD seconds of the last command
 				LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL;
-				if (! slurl_throttled)
-				{
-					if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT)
-					{
-						LLNotificationsUtil::add("ThrottledSLURL");
-					}
-					slurl_throttled = true;
-				}
+                notifySlurlThrottled();
 				return true;
 			}
 			last_throttle_time = cur_time;
@@ -163,6 +166,34 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
 	return info.mHandler->handle(params, query_map, web);
 }
 
+void LLCommandHandlerRegistry::notifySlurlBlocked()
+{
+    static bool slurl_blocked = false;
+    if (!slurl_blocked)
+    {
+        if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT)
+        {
+            // Note: commands can arrive before we initialize everything we need for Notification.
+            LLNotificationsUtil::add("BlockedSLURL");
+        }
+        slurl_blocked = true;
+    }
+}
+
+void LLCommandHandlerRegistry::notifySlurlThrottled()
+{
+    static bool slurl_throttled = false;
+    if (!slurl_throttled)
+    {
+        if (LLStartUp::getStartupState() >= STATE_BROWSER_INIT)
+        {
+            // Note: commands can arrive before we initialize everything we need for Notification.
+            LLNotificationsUtil::add("ThrottledSLURL");
+        }
+        slurl_throttled = true;
+    }
+}
+
 //---------------------------------------------------------------------------
 // Automatic registration of commands, runs before main()
 //---------------------------------------------------------------------------
@@ -230,6 +261,7 @@ symbol_info symbols[] =
 {
 	ent(LLCommandHandler::UNTRUSTED_ALLOW),		  // allow commands from untrusted browsers
 	ent(LLCommandHandler::UNTRUSTED_BLOCK),		  // ignore commands from untrusted browsers
+    ent(LLCommandHandler::UNTRUSTED_CLICK_ONLY),  // allow untrusted, but only if clicked
 	ent(LLCommandHandler::UNTRUSTED_THROTTLE)	  // allow untrusted, but only a few per min.
 };
 
diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h
index 1e0895565a..763e3ee51f 100644
--- a/indra/newview/llcommandhandler.h
+++ b/indra/newview/llcommandhandler.h
@@ -65,9 +65,12 @@ public:
 	{
 		UNTRUSTED_ALLOW,       // allow commands from untrusted browsers
 		UNTRUSTED_BLOCK,       // ignore commands from untrusted browsers
+        UNTRUSTED_CLICK_ONLY,  // allow untrusted, but only if clicked
 		UNTRUSTED_THROTTLE     // allow untrusted, but only a few per min.
 	};
 
+    static const std::string NAV_TYPE_CLICKED;
+
 	LLCommandHandler(const char* command, EUntrustedAccess untrusted_access);
 		// Automatically registers object to get called when 
 		// command is executed.  All commands can be processed
@@ -76,6 +79,13 @@ public:
 		
 	virtual ~LLCommandHandler();
 
+    virtual bool canHandleUntrusted(
+        const LLSD& params,
+        const LLSD& query_map,
+        LLMediaCtrl* web,
+        const std::string& nav_type)
+    { return true; }
+
 	virtual bool handle(const LLSD& params,
 						const LLSD& query_map,
 						LLMediaCtrl* web) = 0;
diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp
index bb3ed77772..7e6af45515 100644
--- a/indra/newview/llfloatersearch.cpp
+++ b/indra/newview/llfloatersearch.cpp
@@ -45,7 +45,7 @@ class LLSearchHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_THROTTLE) { }
+	LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_CLICK_ONLY) { }
 	bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
 	{
 		if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableSearch"))
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
index 977023cfe4..4f772aa141 100644
--- a/indra/newview/llfloaterworldmap.cpp
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -123,7 +123,7 @@ class LLWorldMapHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_THROTTLE ) { }
+	LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_CLICK_ONLY ) { }
 	
 	bool handle(const LLSD& params, const LLSD& query_map,
 				LLMediaCtrl* web)
@@ -160,7 +160,7 @@ class LLMapTrackAvatarHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_THROTTLE) 
+	LLMapTrackAvatarHandler() : LLCommandHandler("maptrackavatar", UNTRUSTED_CLICK_ONLY) 
 	{ 
 	}
 	
diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp
index 84a1278767..815840f990 100644
--- a/indra/newview/llgroupactions.cpp
+++ b/indra/newview/llgroupactions.cpp
@@ -52,7 +52,32 @@ class LLGroupHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { }
+	LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_CLICK_ONLY) { }
+
+    virtual bool canHandleUntrusted(
+        const LLSD& params,
+        const LLSD& query_map,
+        LLMediaCtrl* web,
+        const std::string& nav_type)
+    {
+        if (params.size() < 1)
+        {
+            return true; // don't block, will fail later
+        }
+
+        if (nav_type == NAV_TYPE_CLICKED)
+        {
+            return true;
+        }
+
+        const std::string verb = params[0].asString();
+        if (verb == "create")
+        {
+            return false;
+        }
+        return true;
+    }
+
 	bool handle(const LLSD& tokens, const LLSD& query_map,
 				LLMediaCtrl* web)
 	{
diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp
index ea10aa75ae..0103bf628a 100644
--- a/indra/newview/llpaneleditwearable.cpp
+++ b/indra/newview/llpaneleditwearable.cpp
@@ -1663,7 +1663,7 @@ void LLPanelEditWearable::initPreviousAlphaTextureEntry(LLAvatarAppearanceDefine
 class LLMetricSystemHandler : public LLCommandHandler
 {
 public:
-        LLMetricSystemHandler() : LLCommandHandler("metricsystem", UNTRUSTED_THROTTLE) { }
+        LLMetricSystemHandler() : LLCommandHandler("metricsystem", UNTRUSTED_CLICK_ONLY) { }
 
         bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web)
         {
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index d3898afcbd..deebf0cd1b 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -484,6 +484,30 @@ public:
 	// requires trusted browser to trigger
 	LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { }
 
+    virtual bool canHandleUntrusted(
+        const LLSD& params,
+        const LLSD& query_map,
+        LLMediaCtrl* web,
+        const std::string& nav_type)
+    {
+        if (params.size() < 2)
+        {
+            return true; // don't block, will fail later
+        }
+
+        if (nav_type == NAV_TYPE_CLICKED)
+        {
+            return true;
+        }
+
+        const std::string verb = params[1].asString();
+        if (verb == "about" || verb == "inspect" || verb == "reportAbuse")
+        {
+            return true;
+        }
+        return false;
+    }
+
 	bool handle(const LLSD& params, const LLSD& query_map,
 		LLMediaCtrl* web)
 	{
diff --git a/indra/newview/llpanelprofileclassifieds.cpp b/indra/newview/llpanelprofileclassifieds.cpp
index a3913ddc49..1ff12b4f37 100644
--- a/indra/newview/llpanelprofileclassifieds.cpp
+++ b/indra/newview/llpanelprofileclassifieds.cpp
@@ -81,6 +81,30 @@ public:
 	
 	std::set<LLUUID> mClassifiedIds;
 	std::string mRequestVerb;
+
+    virtual bool canHandleUntrusted(
+        const LLSD& params,
+        const LLSD& query_map,
+        LLMediaCtrl* web,
+        const std::string& nav_type)
+    {
+        if (params.size() < 1)
+        {
+            return true; // don't block, will fail later
+        }
+
+        if (nav_type == NAV_TYPE_CLICKED)
+        {
+            return true;
+        }
+
+        const std::string verb = params[0].asString();
+        if (verb == "create")
+        {
+            return false;
+        }
+        return true;
+    }
     
 	bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web)
     {
diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp
index 774119f169..45d0252e4f 100644
--- a/indra/newview/llpanelprofilepicks.cpp
+++ b/indra/newview/llpanelprofilepicks.cpp
@@ -63,6 +63,30 @@ public:
     // requires trusted browser to trigger
     LLPickHandler() : LLCommandHandler("pick", UNTRUSTED_THROTTLE) { }
 
+    virtual bool canHandleUntrusted(
+        const LLSD& params,
+        const LLSD& query_map,
+        LLMediaCtrl* web,
+        const std::string& nav_type)
+    {
+        if (params.size() < 1)
+        {
+            return true; // don't block, will fail later
+        }
+
+        if (nav_type == NAV_TYPE_CLICKED)
+        {
+            return true;
+        }
+
+        const std::string verb = params[0].asString();
+        if (verb == "create")
+        {
+            return false;
+        }
+        return true;
+    }
+
     bool handle(const LLSD& params, const LLSD& query_map,
         LLMediaCtrl* web)
     {
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 06a6c5e373..5ac58d97be 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -173,11 +173,103 @@
 class LLFloaterOpenHandler : public LLCommandHandler
 {
 public:
-	// requires trusted browser to trigger
+	// requires trusted browser to trigger or an explicit click
 	LLFloaterOpenHandler() : LLCommandHandler("openfloater", UNTRUSTED_THROTTLE) { }
 
-	bool handle(const LLSD& params, const LLSD& query_map,
-				LLMediaCtrl* web)
+    virtual bool canHandleUntrusted(
+        const LLSD& params,
+        const LLSD& query_map,
+        LLMediaCtrl* web,
+        const std::string& nav_type)
+    {
+        if (params.size() != 1)
+        {
+            return true; // will fail silently
+        }
+        
+        std::string fl_name = params[0].asString();
+
+        if (nav_type == NAV_TYPE_CLICKED)
+        {
+            const std::list<std::string> blacklist_clicked = {
+                "camera_presets",
+                "delete_pref_preset",
+                "forget_username",
+                "god_tools",
+                "group_picker",
+                "hud",
+                "incoming_call",
+                "linkreplace",
+                "message_critical", // Modal!!! Login specific.
+                "message_tos", // Modal!!! Login specific.
+                "save_pref_preset",
+                "save_camera_preset",
+                "region_restarting",
+                "outfit_snapshot",
+                "upload_anim_bvh",
+                "upload_anim_anim",
+                "upload_image",
+                "upload_model",
+                "upload_script",
+                "upload_sound"
+            };
+            return std::find(blacklist_clicked.begin(), blacklist_clicked.end(), fl_name) == blacklist_clicked.end();
+        }
+        else
+        {
+            const std::list<std::string> blacklist_untrusted = {
+                "360capture",
+                "block_timers",
+                "add_payment_method",
+                "appearance",
+                "associate_listing",
+                "avatar_picker",
+                "camera",
+                "camera_presets",
+                "classified",
+                "add_landmark",
+                "delete_pref_preset",
+                "env_fixed_environmentent_water",
+                "env_fixed_environmentent_sky",
+                "env_edit_extdaycycle",
+                "font_test",
+                "forget_username",
+                "god_tools",
+                "group_picker",
+                "hud",
+                "incoming_call",
+                "linkreplace",
+                "mem_leaking",
+                "marketplace_validation",
+                "message_critical", // Modal!!! Login specific. If this is in use elsewhere, better to create a non modal variant
+                "message_tos", // Modal!!! Login specific.
+                "mute_object_by_name",
+                "publish_classified",
+                "save_pref_preset",
+                "save_camera_preset",
+                "region_restarting",
+                "script_debug",
+                "script_debug_output",
+                "sell_land",
+                "outfit_snapshot",
+                "upload_anim_bvh",
+                "upload_anim_anim",
+                "upload_image",
+                "upload_model",
+                "upload_script",
+                "upload_sound"
+            };
+            return std::find(blacklist_untrusted.begin(), blacklist_untrusted.end(), fl_name) == blacklist_untrusted.end();
+        }
+
+
+        return true;
+    }
+
+	bool handle(
+        const LLSD& params,
+        const LLSD& query_map,
+        LLMediaCtrl* web) override
 	{
 		if (params.size() != 1)
 		{
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 55ac817479..50252556de 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -232,7 +232,7 @@ class LLInventoryHandler : public LLCommandHandler
 {
 public:
 	// requires trusted browser to trigger
-	LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_THROTTLE) { }
+	LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_CLICK_ONLY) { }
 	
 	bool handle(const LLSD& params, const LLSD& query_map,
 				LLMediaCtrl* web)
-- 
cgit v1.2.3


From e8af383b2db1a094a6a9addbe4b318acb2d7978d Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 11 Oct 2022 22:18:24 +0300
Subject: SL-18351 Better meshoptimizer fallback handling

---
 indra/newview/llmodelpreview.cpp | 22 +++-------------------
 1 file changed, 3 insertions(+), 19 deletions(-)

diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp
index 3401587450..45b42a32ea 100644
--- a/indra/newview/llmodelpreview.cpp
+++ b/indra/newview/llmodelpreview.cpp
@@ -1905,32 +1905,16 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
                     if (sloppy_ratio < 0)
                     {
                         // Sloppy method didn't work, try with smaller decimation values
-                        S32 size_vertices = 0;
-
-                        for (U32 face_idx = 0; face_idx < base->getNumVolumeFaces(); ++face_idx)
-                        {
-                            const LLVolumeFace &face = base->getVolumeFace(face_idx);
-                            size_vertices += face.mNumVertices;
-                        }
-
-                        // Complex models aren't supposed to get here, they are supposed
-                        // to work on a first try of sloppy due to having more viggle room.
-                        // If they didn't, something is likely wrong, no point locking the
-                        // thread in a long calculation that will fail.
-                        const U32 too_many_vertices = 27000;
-                        if (size_vertices > too_many_vertices)
-                        {
-                            LL_WARNS() << "Sloppy optimization method failed for a complex model " << target_model->getName() << LL_ENDL;
-                        }
-                        else
                         {
                             // Find a decimator that does work
                             F32 sloppy_decimation_step = sqrt((F32)decimation); // example: 27->15->9->5->3
                             F32 sloppy_decimator = indices_decimator / sloppy_decimation_step;
+                            U64Microseconds end_time = LLTimer::getTotalTime() + U64Seconds(5);
 
                             while (sloppy_ratio < 0
                                 && sloppy_decimator > precise_ratio
-                                && sloppy_decimator > 1)// precise_ratio isn't supposed to be below 1, but check just in case
+                                && sloppy_decimator > 1 // precise_ratio isn't supposed to be below 1, but check just in case
+                                && end_time > LLTimer::getTotalTime())
                             {
                                 sloppy_ratio = genMeshOptimizerPerModel(base, target_model, sloppy_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY);
                                 sloppy_decimator = sloppy_decimator / sloppy_decimation_step;
-- 
cgit v1.2.3


From ece2e7b39a840a5126a9b34acf15450a969a6daa Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 11 Oct 2022 23:02:17 +0300
Subject: SL-18165 Mac build fix

---
 indra/newview/llviewerfloaterreg.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 5ac58d97be..b4382a4646 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -176,11 +176,11 @@ public:
 	// requires trusted browser to trigger or an explicit click
 	LLFloaterOpenHandler() : LLCommandHandler("openfloater", UNTRUSTED_THROTTLE) { }
 
-    virtual bool canHandleUntrusted(
+    bool canHandleUntrusted(
         const LLSD& params,
         const LLSD& query_map,
         LLMediaCtrl* web,
-        const std::string& nav_type)
+        const std::string& nav_type) override
     {
         if (params.size() != 1)
         {
-- 
cgit v1.2.3


From 2e52d921bf1d1f506db50371e1f4bc246aff45ef Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 13 Oct 2022 01:10:49 +0300
Subject: SL-16156 LLTrace::StatType crash

Don't record stats or run timer when making 360 snapshots.
---
 indra/newview/llfloater360capture.cpp | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp
index c075f7e8bd..8ad0bb3995 100644
--- a/indra/newview/llfloater360capture.cpp
+++ b/indra/newview/llfloater360capture.cpp
@@ -84,7 +84,7 @@ LLFloater360Capture::~LLFloater360Capture()
     // Tell the Simulator not to send us everything anymore
     // and revert to the regular "keyhole" frustum of interest
     // list updates.
-    if (gSavedSettings.getBOOL("360CaptureUseInterestListCap"))
+    if (gSavedSettings.getBOOL("360CaptureUseInterestListCap") && !LLApp::isExiting())
     {
         const bool send_everything = false;
         changeInterestListMode(send_everything);
@@ -537,7 +537,8 @@ void LLFloater360Capture::capture360Images()
     // We need to convert from the angle getYaw() gives us into something
     // the XMP data field wants (N=0, E=90, S=180, W= 270 etc.)
     mInitialHeadingDeg  = (360 + 90 - (int)(camera->getYaw() * RAD_TO_DEG)) % 360;
-    LL_INFOS("360Capture") << "Recording a heading of " << (int)(mInitialHeadingDeg) << LL_ENDL;
+    LL_INFOS("360Capture") << "Recording a heading of " << (int)(mInitialHeadingDeg)
+        << " Image size: " << (S32)mSourceImageSize << LL_ENDL;
 
     // camera constants for the square, cube map capture image
     camera->setAspect(1.0); // must set aspect ratio first to avoid undesirable clamping of vertical FoV
@@ -587,6 +588,9 @@ void LLFloater360Capture::capture360Images()
     // for each of the 6 directions we shoot...
     for (int i = 0; i < 6; i++)
     {
+        LLAppViewer::instance()->pauseMainloopTimeout();
+        LLViewerStats::instance().getRecording().stop();
+
         // these buffers are where the raw, captured pixels are stored and
         // the first time we use them, we have to make a new one
         if (mRawImages[i] == nullptr)
@@ -624,8 +628,10 @@ void LLFloater360Capture::capture360Images()
         auto duration = std::chrono::duration_cast<std::chrono::duration<double>>(t_end - t_start);
         encode_time_total += duration.count();
 
-        // ping the main loop in case the snapshot process takes a really long
-        // time and we get disconnected
+        LLViewerStats::instance().getRecording().resume();
+        LLAppViewer::instance()->resumeMainloopTimeout();
+        
+        // update main loop timeout state
         LLAppViewer::instance()->pingMainloopTimeout("LLFloater360Capture::capture360Images");
     }
 
-- 
cgit v1.2.3


From 01601142567e951a8a6841bd220d2d0e9bd7e341 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 13 Oct 2022 01:11:34 +0300
Subject: Revert "Restored SL-14961"

This partially reverts commit 935c1362a222f192bf913270d01f6c31c16e175b.
Reporting seems to have stoped working, trying the same way mac works.
---
 indra/llcommon/llcoros.cpp | 74 ++++++++++++++++------------------------------
 indra/llcommon/llcoros.h   |  6 +---
 2 files changed, 27 insertions(+), 53 deletions(-)

diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 14bfb98629..70d8dfc8b9 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -288,25 +288,15 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
     return name;
 }
 
+namespace
+{
+
 #if LL_WINDOWS
 
 static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
 
-U32 cpp_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop, const std::string& name)
+U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
 {
-    // C++ exceptions were logged in toplevelTryWrapper, but not SEH
-    // log SEH exceptions here, to make sure it gets into bugsplat's 
-    // report and because __try won't allow std::string operations
-    if (code != STATUS_MSC_EXCEPTION)
-    {
-        LL_WARNS() << "SEH crash in " << name << ", code: " << code << LL_ENDL;
-    }
-    // Handle bugsplat here, since GetExceptionInformation() can only be
-    // called from within filter for __except(filter), not from __except's {}
-    // Bugsplat should get all exceptions, C++ and SEH
-    LLApp::instance()->reportCrashToBugsplat(exception_infop);
-
-    // Only convert non C++ exceptions.
     if (code == STATUS_MSC_EXCEPTION)
     {
         // C++ exception, go on
@@ -319,28 +309,38 @@ U32 cpp_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop,
     }
 }
 
-void LLCoros::sehHandle(const std::string& name, const LLCoros::callable_t& callable)
+void sehandle(const LLCoros::callable_t& callable)
 {
     __try
     {
-        LLCoros::toplevelTryWrapper(name, callable);
+        callable();
     }
-    __except (cpp_exception_filter(GetExceptionCode(), GetExceptionInformation(), name))
+    __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
     {
-        // convert to C++ styled exception for handlers other than bugsplat
+        // convert to C++ styled exception
         // Note: it might be better to use _se_set_translator
         // if you want exception to inherit full callstack
-        //
-        // in case of bugsplat this will get to exceptionTerminateHandler and
-        // looks like fiber will terminate application after that
         char integer_string[512];
-        sprintf(integer_string, "SEH crash in %s, code: %lu\n", name.c_str(), GetExceptionCode());
+        sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
         throw std::exception(integer_string);
     }
 }
-#endif
 
-void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& callable)
+#else  // ! LL_WINDOWS
+
+inline void sehandle(const LLCoros::callable_t& callable)
+{
+    callable();
+}
+
+#endif // ! LL_WINDOWS
+
+} // anonymous namespace
+
+// Top-level wrapper around caller's coroutine callable.
+// Normally we like to pass strings and such by const reference -- but in this
+// case, we WANT to copy both the name and the callable to our local stack!
+void LLCoros::toplevel(std::string name, callable_t callable)
 {
     // keep the CoroData on this top-level function's stack frame
     CoroData corodata(name);
@@ -350,12 +350,12 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call
     // run the code the caller actually wants in the coroutine
     try
     {
-        callable();
+        sehandle(callable);
     }
     catch (const Stop& exc)
     {
         LL_INFOS("LLCoros") << "coroutine " << name << " terminating because "
-            << exc.what() << LL_ENDL;
+                            << exc.what() << LL_ENDL;
     }
     catch (const LLContinueError&)
     {
@@ -366,36 +366,14 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call
     }
     catch (...)
     {
-#if LL_WINDOWS
-        // Any OTHER kind of uncaught exception will cause the viewer to
-        // crash, SEH handling should catch it and report to bugsplat.
-        LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name));
-        // to not modify callstack
-        throw;
-#else
         // Stash any OTHER kind of uncaught exception in the rethrow() queue
         // to be rethrown by the main fiber.
         LL_WARNS("LLCoros") << "Capturing uncaught exception in coroutine "
                             << name << LL_ENDL;
         LLCoros::instance().saveException(name, std::current_exception());
-#endif
     }
 }
 
-// Top-level wrapper around caller's coroutine callable.
-// Normally we like to pass strings and such by const reference -- but in this
-// case, we WANT to copy both the name and the callable to our local stack!
-void LLCoros::toplevel(std::string name, callable_t callable)
-{
-#if LL_WINDOWS
-    // Because SEH can's have unwinding, need to call a wrapper
-    // 'try' is inside SEH handling to not catch LLContinue
-    sehHandle(name, callable);
-#else
-    toplevelTryWrapper(name, callable);
-#endif
-}
-
 //static
 void LLCoros::checkStop()
 {
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index dbff921f16..966ce03296 100644
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -307,11 +307,7 @@ public:
 
 private:
     std::string generateDistinctName(const std::string& prefix) const;
-    void toplevelTryWrapper(const std::string& name, const callable_t& callable);
-#if LL_WINDOWS
-    void sehHandle(const std::string& name, const callable_t& callable); // calls toplevelTryWrapper
-#endif
-    void toplevel(std::string name, callable_t callable); // calls sehHandle or toplevelTryWrapper
+    void toplevel(std::string name, callable_t callable);
     struct CoroData;
     static CoroData& get_CoroData(const std::string& caller);
     void saveException(const std::string& name, std::exception_ptr exc);
-- 
cgit v1.2.3


From dbbc71073979bccad6b2b05ebf914c5f9968b4e8 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Thu, 13 Oct 2022 14:39:29 +0300
Subject: SL-18348 FIXED Include next/previous face doesn't work

---
 doc/contributions.txt          | 1 +
 indra/newview/llviewermenu.cpp | 1 +
 2 files changed, 2 insertions(+)

diff --git a/doc/contributions.txt b/doc/contributions.txt
index 1408bab8ca..e7ac8795f9 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -1648,6 +1648,7 @@ Zi Ree
 	VWR-25588
 	STORM-1790
 	STORM-1842
+	SL-18348
 Zipherius Turas
 	VWR-76
 	VWR-77
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index f76031953d..6c06360636 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -5613,6 +5613,7 @@ class LLToolsSelectNextPartFace : public view_listener_t
                     }
                 }
                 LLSelectMgr::getInstance()->selectObjectOnly(to_select, new_te);
+                LLSelectMgr::getInstance()->addAsIndividual(to_select, new_te, false);
             }
             else
             {
-- 
cgit v1.2.3


From 68973b856ee4f096f7e1c0c4c72815f4ca676266 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 18 Oct 2022 09:11:54 -0400
Subject: Fix for selection beam particles not firing its update callback

---
 indra/newview/llviewerpartsource.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp
index f042040e98..1751ee1ebb 100644
--- a/indra/newview/llviewerpartsource.cpp
+++ b/indra/newview/llviewerpartsource.cpp
@@ -793,7 +793,7 @@ void LLViewerPartSourceBeam::update(const F32 dt)
 		}
 
 		LLViewerPart* part = new LLViewerPart();
-		part->init(this, mImagep, NULL);
+		part->init(this, mImagep, updatePart);
 
 		part->mFlags = LLPartData::LL_PART_INTERP_COLOR_MASK |
 						LLPartData::LL_PART_INTERP_SCALE_MASK |
-- 
cgit v1.2.3


From 46427eefd4d27704397f4fe3cf3bc9d29946c1b2 Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Tue, 18 Oct 2022 19:51:12 +0300
Subject: SL-17916 FIXED Unnecessary double click actions

---
 indra/newview/llviewerinput.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp
index 43b9cd90bd..6bab2c2100 100644
--- a/indra/newview/llviewerinput.cpp
+++ b/indra/newview/llviewerinput.cpp
@@ -1614,12 +1614,22 @@ BOOL LLViewerInput::handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask,
             clicktype = CLICK_DOUBLELEFT;
         }
 
+        // If the first LMB click is handled by the menu, skip the following double click
+        static bool skip_double_click = false;
+        if (clicktype == CLICK_LEFT && down )
+        {
+            skip_double_click = handled;
+        }
 
         if (double_click_sp && down)
         {
             // Consume click.
             // Due to handling, double click that is not handled will be immediately followed by LMB click
         }
+        else if (clicktype == CLICK_DOUBLELEFT && skip_double_click)
+        {
+            handled = true;
+        }
         // If UI handled 'down', it should handle 'up' as well
         // If we handle 'down' not by UI, then we should handle 'up'/'level' regardless of UI
         else if (handled)
-- 
cgit v1.2.3


From 1fb1a9df7a6664946f1f758330a0e6cfaef0dd8f Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 16:17:26 -0400
Subject: Fix leak of context menu branches Introduce menu bloat logging code

---
 indra/llui/llmenugl.cpp          | 58 +++++++++++++++++++++++++++-------------
 indra/llui/llmenugl.h            |  3 +--
 indra/newview/llviewerwindow.cpp | 53 ++++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 20 deletions(-)

diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 4264028338..5cb840fd61 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -4087,25 +4087,39 @@ void LLTearOffMenu::closeTearOff()
 }
 
 LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p) 
-:	LLMenuItemGL(p),
-	mBranch( p.branch()->getHandle() )
+:	LLMenuItemGL(p)
 {
-	mBranch.get()->hide();
-	mBranch.get()->setParentMenuItem(this);
+	LLContextMenu* branch = static_cast<LLContextMenu*>(p.branch);
+	if (branch)
+	{
+		mBranch = branch->getHandle();
+		branch->hide();
+		branch->setParentMenuItem(this);
+	}
+}
+
+LLContextMenuBranch::~LLContextMenuBranch()
+{
+	if (mBranch.get())
+	{
+		mBranch.get()->die();
+	}
 }
 
 // called to rebuild the draw label
 void LLContextMenuBranch::buildDrawLabel( void )
 {
+	auto menu = getBranch();
+	if (menu)
 	{
 		// default enablement is this -- if any of the subitems are
 		// enabled, this item is enabled. JC
-		U32 sub_count = mBranch.get()->getItemCount();
+		U32 sub_count = menu->getItemCount();
 		U32 i;
 		BOOL any_enabled = FALSE;
 		for (i = 0; i < sub_count; i++)
 		{
-			LLMenuItemGL* item = mBranch.get()->getItem(i);
+			LLMenuItemGL* item = menu->getItem(i);
 			item->buildDrawLabel();
 			if (item->getEnabled() && !item->getDrawTextDisabled() )
 			{
@@ -4127,13 +4141,17 @@ void LLContextMenuBranch::buildDrawLabel( void )
 
 void	LLContextMenuBranch::showSubMenu()
 {
-	LLMenuItemGL* menu_item = mBranch.get()->getParentMenuItem();
-	if (menu_item != NULL && menu_item->getVisible())
+	auto menu = getBranch();
+	if(menu)
 	{
-		S32 center_x;
-		S32 center_y;
-		localPointToScreen(getRect().getWidth(), getRect().getHeight() , &center_x, &center_y);
-		mBranch.get()->show(center_x, center_y);
+		LLMenuItemGL* menu_item = menu->getParentMenuItem();
+		if (menu_item != NULL && menu_item->getVisible())
+		{
+			S32 center_x;
+			S32 center_y;
+			localPointToScreen(getRect().getWidth(), getRect().getHeight(), &center_x, &center_y);
+			menu->show(center_x, center_y);
+		}
 	}
 }
 
@@ -4147,13 +4165,17 @@ void LLContextMenuBranch::setHighlight( BOOL highlight )
 {
 	if (highlight == getHighlight()) return;
 	LLMenuItemGL::setHighlight(highlight);
-	if( highlight )
-	{
-		showSubMenu();
-	}
-	else
+	auto menu = getBranch();
+	if (menu)
 	{
-		mBranch.get()->hide();
+		if (highlight)
+		{
+			showSubMenu();
+		}
+		else
+		{
+			menu->hide();
+		}
 	}
 }
 
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index abbfd9a24a..f84c4d41eb 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -745,8 +745,7 @@ public:
 
 	LLContextMenuBranch(const Params&);
 
-	virtual ~LLContextMenuBranch()
-	{}
+	virtual ~LLContextMenuBranch();
 
 	// called to rebuild the draw label
 	virtual void	buildDrawLabel( void );
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 5ce46b143a..49f5756976 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -3420,6 +3420,59 @@ void LLViewerWindow::updateUI()
 		root_view = mRootView;
 	}
 
+	static LLCachedControl<bool> dump_menu_holder(gSavedSettings, "DumpMenuHolderSize", false);
+	if (dump_menu_holder)
+	{
+		static bool init = false;
+		static LLFrameTimer child_count_timer;
+		static std::vector <std::string> child_vec;
+		if (!init)
+		{
+			child_count_timer.resetWithExpiry(5.f);
+			init = true;
+		}
+		if (child_count_timer.hasExpired())
+		{
+			LL_INFOS() << "gMenuHolder child count: " << gMenuHolder->getChildCount() << LL_ENDL;
+			std::vector<std::string> local_child_vec;
+			LLView::child_list_t child_list = *gMenuHolder->getChildList();
+			for (auto child : child_list)
+			{
+				local_child_vec.emplace_back(child->getName());
+			}
+			if (!local_child_vec.empty() && local_child_vec != child_vec)
+			{
+				std::vector<std::string> out_vec;
+				std::sort(local_child_vec.begin(), local_child_vec.end());
+				std::sort(child_vec.begin(), child_vec.end());
+				std::set_difference(child_vec.begin(), child_vec.end(), local_child_vec.begin(), local_child_vec.end(), std::inserter(out_vec, out_vec.begin()));
+				if (!out_vec.empty())
+				{
+					LL_INFOS() << "gMenuHolder removal diff size: '"<<out_vec.size() <<"' begin_child_diff";
+					for (auto str : out_vec)
+					{
+						LL_CONT << " : " << str;
+					}
+					LL_CONT << " : end_child_diff" << LL_ENDL;
+				}
+
+				out_vec.clear();
+				std::set_difference(local_child_vec.begin(), local_child_vec.end(), child_vec.begin(), child_vec.end(), std::inserter(out_vec, out_vec.begin()));
+				if (!out_vec.empty())
+				{
+					LL_INFOS() << "gMenuHolder addition diff size: '" << out_vec.size() << "' begin_child_diff";
+					for (auto str : out_vec)
+					{
+						LL_CONT << " : " << str;
+					}
+					LL_CONT << " : end_child_diff" << LL_ENDL;
+				}
+				child_vec.swap(local_child_vec);
+			}
+			child_count_timer.resetWithExpiry(5.f);
+		}
+	}
+
 	// only update mouse hover set when UI is visible (since we shouldn't send hover events to invisible UI
 	if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
 	{
-- 
cgit v1.2.3


From b9552e596fdeaf577d1de1e6d4d2ba5d46bceeb9 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 16:19:33 -0400
Subject: Reduce folderview menu bloat via lazy creation on right click

---
 indra/llui/llfolderview.cpp              | 58 +++++++++++++++++++++++---------
 indra/llui/llfolderview.h                |  3 ++
 indra/newview/llfloaterimcontainer.cpp   |  1 +
 indra/newview/llfloaterimsessiontab.cpp  |  1 +
 indra/newview/llinventorypanel.cpp       |  7 +++-
 indra/newview/llpanelmaininventory.cpp   | 12 +++++--
 indra/newview/llpanelobjectinventory.cpp |  1 +
 7 files changed, 63 insertions(+), 20 deletions(-)

diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp
index ea2ca68e47..e1869e8125 100644
--- a/indra/llui/llfolderview.cpp
+++ b/indra/llui/llfolderview.cpp
@@ -163,6 +163,7 @@ LLFolderView::LLFolderView(const Params& p)
 :	LLFolderViewFolder(p),
 	mScrollContainer( NULL ),
 	mPopupMenuHandle(),
+	mMenuFileName(p.options_menu),
 	mAllowMultiSelect(p.allow_multiselect),
 	mAllowDrag(p.allow_drag),
 	mShowEmptyMessage(p.show_empty_message),
@@ -182,6 +183,7 @@ LLFolderView::LLFolderView(const Params& p)
 	mMinWidth(0),
 	mDragAndDropThisFrame(FALSE),
 	mCallbackRegistrar(NULL),
+	mEnableRegistrar(NULL),
 	mUseEllipses(p.use_ellipses),
 	mDraggingOverItem(NULL),
 	mStatusTextBox(NULL),
@@ -244,17 +246,6 @@ LLFolderView::LLFolderView(const Params& p)
 	mStatusTextBox->setFollowsTop();
 	addChild(mStatusTextBox);
 
-
-	// make the popup menu available
-	llassert(LLMenuGL::sMenuContainer != NULL);
-	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(p.options_menu, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
-	if (!menu)
-	{
-		menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
-	}
-	menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
-	mPopupMenuHandle = menu->getHandle();
-
 	mViewModelItem->openItem();
 
 	mAreChildrenInited = true; // root folder is a special case due to not being loaded normally, assume that it's inited.
@@ -276,6 +267,7 @@ LLFolderView::~LLFolderView( void )
 	mStatusTextBox = NULL;
 
 	if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
+	mPopupMenuHandle.markDead();
 
 	mAutoOpenItems.removeAllNodes();
 	clearSelection();
@@ -1438,22 +1430,56 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
 	BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
 	S32 count = mSelectedItems.size();
 
-	LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get();
+	LLMenuGL* menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+	if (!menu)
+	{
+		if (mCallbackRegistrar)
+		{
+			mCallbackRegistrar->pushScope();
+		}
+		if (mEnableRegistrar)
+		{
+			mEnableRegistrar->pushScope();
+		}
+		llassert(LLMenuGL::sMenuContainer != NULL);
+		menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(mMenuFileName, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+		if (!menu)
+		{
+			menu = LLUICtrlFactory::getDefaultWidget<LLMenuGL>("inventory_menu");
+		}
+		menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
+		mPopupMenuHandle = menu->getHandle();
+		if (mEnableRegistrar)
+		{
+			mEnableRegistrar->popScope();
+		}
+		if (mCallbackRegistrar)
+		{
+			mCallbackRegistrar->popScope();
+		}
+	}
 	bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected();
-	if ((handled
-		&& ( count > 0 && (hasVisibleChildren()) ) // show menu only if selected items are visible
-		&& menu ) &&
+	if (menu && (handled
+		&& ( count > 0 && (hasVisibleChildren()) )) && // show menu only if selected items are visible
 		!hide_folder_menu)
 	{
 		if (mCallbackRegistrar)
         {
 			mCallbackRegistrar->pushScope();
         }
+		if (mEnableRegistrar)
+		{
+			mEnableRegistrar->pushScope();
+		}
 
 		updateMenuOptions(menu);
 	   
 		menu->updateParent(LLMenuGL::sMenuContainer);
 		LLMenuGL::showPopup(this, menu, x, y);
+		if (mEnableRegistrar)
+		{
+			mEnableRegistrar->popScope();
+		}
 		if (mCallbackRegistrar)
         {
 			mCallbackRegistrar->popScope();
@@ -1531,7 +1557,7 @@ void LLFolderView::deleteAllChildren()
 {
 	closeRenamer();
 	if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die();
-	mPopupMenuHandle = LLHandle<LLView>();
+	mPopupMenuHandle.markDead();
 	mScrollContainer = NULL;
 	mRenameItem = NULL;
 	mRenamer = NULL;
diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h
index 6bb5e6c02e..7dfa04828a 100644
--- a/indra/llui/llfolderview.h
+++ b/indra/llui/llfolderview.h
@@ -235,6 +235,7 @@ public:
 	bool showItemLinkOverlays() { return mShowItemLinkOverlays; }
 
 	void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; }
+	void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; }
 
 	LLPanel* getParentPanel() { return mParentPanel.get(); }
 	// DEBUG only
@@ -272,6 +273,7 @@ protected:
 
 protected:
 	LLHandle<LLView>					mPopupMenuHandle;
+	std::string						mMenuFileName;
 	
 	selected_items_t				mSelectedItems;
 	bool							mKeyboardSelection,
@@ -327,6 +329,7 @@ protected:
 	LLFolderViewItem*				mDraggingOverItem; // See EXT-719
 
 	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar;
+	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar;
 	
 public:
 	static F32 sAutoOpenTime;
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
index 703b5d0011..2720b7fcf7 100644
--- a/indra/newview/llfloaterimcontainer.cpp
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -211,6 +211,7 @@ BOOL LLFloaterIMContainer::postBuild()
     p.options_menu = "menu_conversation.xml";
 	mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
     mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
+	mConversationsRoot->setEnableRegistrar(&mEnableCallbackRegistrar);
 
 	// Add listener to conversation model events
 	mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLFloaterIMContainer::onConversationModelEvent, this, _1));
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index 93a0b39e02..f973ae43cc 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -317,6 +317,7 @@ BOOL LLFloaterIMSessionTab::postBuild()
     p.name = "root";
 	mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
     mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
+	mConversationsRoot->setEnableRegistrar(&mEnableCallbackRegistrar);
 	// Attach that root to the scroller
 	mScroller->addChild(mConversationsRoot);
 	mConversationsRoot->setScrollContainer(mScroller);
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 6b102c7500..c065c76dca 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -211,7 +211,11 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id )
     p.allow_drop = mParams.allow_drop_on_root;
     p.options_menu = "menu_inventory.xml";
 
-    return LLUICtrlFactory::create<LLFolderView>(p);
+	LLFolderView* fv = LLUICtrlFactory::create<LLFolderView>(p);
+	fv->setCallbackRegistrar(&mCommitCallbackRegistrar);
+	fv->setEnableRegistrar(&mEnableCallbackRegistrar);
+
+	return fv;
 }
 
 void LLInventoryPanel::clearFolderRoot()
@@ -264,6 +268,7 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
 	}
 	mCommitCallbackRegistrar.popScope();
 	mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar);
+	mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar);
 	
 	// Scroller
 		LLRect scroller_view_rect = getRect();
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 89256b40c4..81acb1c8b4 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -307,6 +307,13 @@ LLPanelMainInventory::~LLPanelMainInventory( void )
     
 	gInventory.removeObserver(this);
 	delete mSavedFolderState;
+
+	auto menu = mMenuAddHandle.get();
+	if(menu)
+	{
+		menu->die();
+		mMenuAddHandle.markDead();
+	}
 }
 
 LLInventoryPanel* LLPanelMainInventory::getAllItemsPanel()
@@ -1177,13 +1184,12 @@ void LLPanelMainInventory::initListCommandsHandlers()
 	mEnableCallbackRegistrar.add("Inventory.GearDefault.Check", boost::bind(&LLPanelMainInventory::isActionChecked, this, _2));
 	mEnableCallbackRegistrar.add("Inventory.GearDefault.Enable", boost::bind(&LLPanelMainInventory::isActionEnabled, this, _2));
 	mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-	mGearMenuButton->setMenu(mMenuGearDefault);
+	mGearMenuButton->setMenu(mMenuGearDefault, LLMenuButton::MP_TOP_LEFT, true);
 	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_inventory_add.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 	mMenuAddHandle = menu->getHandle();
 
 	mMenuVisibility = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_inventory_search_visibility.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-	mVisibilityMenuButton->setMenu(mMenuVisibility);
-	mVisibilityMenuButton->setMenuPosition(LLMenuButton::MP_BOTTOM_LEFT);
+	mVisibilityMenuButton->setMenu(mMenuVisibility, LLMenuButton::MP_BOTTOM_LEFT, true);
 
 	// Update the trash button when selected item(s) get worn or taken off.
 	LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLPanelMainInventory::updateListCommands, this));
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index cfaa9456be..17b4528d97 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -1360,6 +1360,7 @@ void LLPanelObjectInventory::reset()
 	mFolders = LLUICtrlFactory::create<LLFolderView>(p);
 
 	mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar);
+	mFolders->setEnableRegistrar(&mEnableCallbackRegistrar);
 
 	if (hasFocus())
 	{
-- 
cgit v1.2.3


From a48374e8cfbdf261b4902024fb66e60432602b12 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 16:22:24 -0400
Subject: Defer chat history menu creation to right click time to reduce heavy
 menu bloat and fix chat history menu hoarding

---
 indra/newview/llchathistory.cpp | 84 ++++++++++++++++++++++++++---------------
 1 file changed, 53 insertions(+), 31 deletions(-)

diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 7ff24f64ac..f4e1943ea2 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -141,6 +141,18 @@ public:
 		{
 			mAvatarNameCacheConnection.disconnect();
 		}
+		auto menu = mPopupMenuHandleAvatar.get();
+		if (menu)
+		{
+			menu->die();
+			mPopupMenuHandleAvatar.markDead();
+		}
+		menu = mPopupMenuHandleObject.get();
+		if (menu)
+		{
+			menu->die();
+			mPopupMenuHandleObject.markDead();
+		}
 	}
 
 	BOOL handleMouseUp(S32 x, S32 y, MASK mask)
@@ -567,36 +579,6 @@ public:
 
 	BOOL postBuild()
 	{
-		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
-		LLUICtrl::EnableCallbackRegistry::ScopedRegistrar registrar_enable;
-
-		registrar.add("AvatarIcon.Action", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemClicked, this, _2));
-		registrar_enable.add("AvatarIcon.Check", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemChecked, this, _2));
-		registrar_enable.add("AvatarIcon.Enable", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemEnabled, this, _2));
-		registrar_enable.add("AvatarIcon.Visible", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemVisible, this, _2));
-		registrar.add("ObjectIcon.Action", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemClicked, this, _2));
-		registrar_enable.add("ObjectIcon.Visible", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemVisible, this, _2));
-
-		LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-		if (menu)
-		{
-			mPopupMenuHandleAvatar = menu->getHandle();
-		}
-		else
-		{
-			LL_WARNS() << " Failed to create menu_avatar_icon.xml" << LL_ENDL;
-		}
-
-		menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_object_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-		if (menu)
-		{
-			mPopupMenuHandleObject = menu->getHandle();
-		}
-		else
-		{
-			LL_WARNS() << " Failed to create menu_object_icon.xml" << LL_ENDL;
-		}
-
 		setDoubleClickCallback(boost::bind(&LLChatHistoryHeader::showInspector, this));
 
 		setMouseEnterCallback(boost::bind(&LLChatHistoryHeader::showInfoCtrl, this));
@@ -883,13 +865,53 @@ protected:
 	void showObjectContextMenu(S32 x,S32 y)
 	{
 		LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleObject.get();
-		if(menu)
+		if (!menu)
+		{
+			LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+			LLUICtrl::EnableCallbackRegistry::ScopedRegistrar registrar_enable;
+			registrar.add("ObjectIcon.Action", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemClicked, this, _2));
+			registrar_enable.add("ObjectIcon.Visible", boost::bind(&LLChatHistoryHeader::onObjectIconContextMenuItemVisible, this, _2));
+
+			menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_object_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+			if (menu)
+			{
+				mPopupMenuHandleObject = menu->getHandle();
+				menu->updateParent(LLMenuGL::sMenuContainer);
+				LLMenuGL::showPopup(this, menu, x, y);
+			}
+			else
+			{
+				LL_WARNS() << " Failed to create menu_object_icon.xml" << LL_ENDL;
+			}
+		}
+		else
+		{
 			LLMenuGL::showPopup(this, menu, x, y);
+		}
 	}
 	
 	void showAvatarContextMenu(S32 x,S32 y)
 	{
 		LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandleAvatar.get();
+		if (!menu)
+		{
+			LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+			LLUICtrl::EnableCallbackRegistry::ScopedRegistrar registrar_enable;
+			registrar.add("AvatarIcon.Action", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemClicked, this, _2));
+			registrar_enable.add("AvatarIcon.Check", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemChecked, this, _2));
+			registrar_enable.add("AvatarIcon.Enable", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemEnabled, this, _2));
+			registrar_enable.add("AvatarIcon.Visible", boost::bind(&LLChatHistoryHeader::onAvatarIconContextMenuItemVisible, this, _2));
+
+			menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+			if (menu)
+			{
+				mPopupMenuHandleAvatar = menu->getHandle();
+			}
+			else
+			{
+				LL_WARNS() << " Failed to create menu_avatar_icon.xml" << LL_ENDL;
+			}
+		}
 
 		if(menu)
 		{
-- 
cgit v1.2.3


From bbd8df15de6b8f9b321f251c3e764ea654d5ecc7 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 16:23:25 -0400
Subject: Fix menu leaks and lazy creation in text editing and scroll lists

---
 indra/llui/lllineeditor.cpp         | 16 +++++++++-------
 indra/llui/llscrolllistctrl.cpp     | 28 ++++++++++++++++++++--------
 indra/llui/llscrolllistctrl.h       |  2 +-
 indra/llui/lltextbase.cpp           |  6 ++++++
 indra/llui/lltexteditor.cpp         | 31 +++++++++++++++++++++----------
 indra/llui/lltexteditor.h           |  2 +-
 indra/llui/lltoolbar.cpp            |  7 ++++++-
 indra/newview/lllistcontextmenu.cpp | 10 +++-------
 8 files changed, 67 insertions(+), 35 deletions(-)

diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 33037b5001..f16f8c3e8d 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -209,13 +209,6 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 
 	setPrevalidateInput(p.prevalidate_input_callback());
 	setPrevalidate(p.prevalidate_callback());
-
-	llassert(LLMenuGL::sMenuContainer != NULL);
-	LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>
-		("menu_text_editor.xml",
-		 LLMenuGL::sMenuContainer,
-		 LLMenuHolderGL::child_registry_t::instance());
-	setContextMenu(menu);
 }
  
 LLLineEditor::~LLLineEditor()
@@ -2637,6 +2630,15 @@ LLWString LLLineEditor::getConvertedText() const
 void LLLineEditor::showContextMenu(S32 x, S32 y)
 {
 	LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+	if (!menu)
+	{
+		llassert(LLMenuGL::sMenuContainer != NULL);
+		menu = LLUICtrlFactory::createFromFile<LLContextMenu>
+			("menu_text_editor.xml",
+				LLMenuGL::sMenuContainer,
+				LLMenuHolderGL::child_registry_t::instance());
+		setContextMenu(menu);
+	}
 
 	if (menu)
 	{
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 65c7b420ce..167593bd52 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -196,7 +196,6 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
 	mHighlightedItem(-1),
 	mBorder(NULL),
 	mSortCallback(NULL),
-	mPopupMenu(NULL),
 	mCommentTextView(NULL),
 	mNumDynamicWidthColumns(0),
 	mTotalStaticColumnWidth(0),
@@ -348,6 +347,13 @@ LLScrollListCtrl::~LLScrollListCtrl()
 	mItemList.clear();
     clearColumns(); //clears columns and deletes headers
 	delete mIsFriendSignal;
+
+	auto menu = mPopupMenuHandle.get();
+	if (menu)
+	{
+		menu->die();
+		mPopupMenuHandle.markDead();
+	}
 }
 
 
@@ -1997,17 +2003,23 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
 
 			// create the context menu from the XUI file and display it
 			std::string menu_name = is_group ? "menu_url_group.xml" : "menu_url_agent.xml";
-			delete mPopupMenu;
+			auto menu = mPopupMenuHandle.get();
+			if (menu)
+			{
+				menu->die();
+				mPopupMenuHandle.markDead();
+			}
 			llassert(LLMenuGL::sMenuContainer != NULL);
-			mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
+			menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
 				menu_name, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
-			if (mPopupMenu)
+			if (menu)
 			{
+				mPopupMenuHandle = menu->getHandle();
 				if (mIsFriendSignal)
 				{
 					bool isFriend = *(*mIsFriendSignal)(uuid);
-					LLView* addFriendButton = mPopupMenu->getChild<LLView>("add_friend");
-					LLView* removeFriendButton = mPopupMenu->getChild<LLView>("remove_friend");
+					LLView* addFriendButton = menu->getChild<LLView>("add_friend");
+					LLView* removeFriendButton = menu->getChild<LLView>("remove_friend");
 
 					if (addFriendButton && removeFriendButton)
 					{
@@ -2016,8 +2028,8 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
 					}
 				}
 
-				mPopupMenu->show(x, y);
-				LLMenuGL::showPopup(this, mPopupMenu, x, y);
+				menu->show(x, y);
+				LLMenuGL::showPopup(this, menu, x, y);
 				return TRUE;
 			}
 		}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 77d10fdec7..6f7d4768e1 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -526,7 +526,7 @@ private:
 
 	S32				mHighlightedItem;
 	class LLViewBorder*	mBorder;
-	LLContextMenu	*mPopupMenu;
+	LLHandle<LLContextMenu>	mPopupMenuHandle;
 	
 	LLView			*mCommentTextView;
 
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index 7e4aaa53bf..fcfdd64e6a 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -273,6 +273,12 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
 LLTextBase::~LLTextBase()
 {
 	mSegments.clear();
+	LLContextMenu* menu = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
+	if (menu)
+	{
+		menu->die();
+		mPopupMenuHandle.markDead();
+	}
 	delete mURLClickSignal;
 	delete mIsFriendSignal;
 	delete mIsObjectBlockedSignal;
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index b1f8b00cab..3d2a426913 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -257,7 +257,6 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
 	mMouseDownY(0),
 	mTabsToNextField(p.ignore_tab),
 	mPrevalidateFunc(p.prevalidate_callback()),
-	mContextMenu(NULL),
 	mShowContextMenu(p.show_context_menu),
 	mEnableTooltipPaste(p.enable_tooltip_paste),
 	mPassDelete(FALSE),
@@ -301,8 +300,13 @@ LLTextEditor::~LLTextEditor()
 	// Scrollbar is deleted by LLView
 	std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
 	mUndoStack.clear();
-	// context menu is owned by menu holder, not us
-	//delete mContextMenu;
+	// Mark the menu as dead or its retained in memory till shutdown.
+	LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+	if(menu)
+	{
+		menu->die();
+		mContextMenuHandle.markDead();
+	}
 }
 
 ////////////////////////////////////////////////////////////
@@ -2051,12 +2055,19 @@ void LLTextEditor::setEnabled(BOOL enabled)
 
 void LLTextEditor::showContextMenu(S32 x, S32 y)
 {
-	if (!mContextMenu)
+	LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+	if (!menu)
 	{
 		llassert(LLMenuGL::sMenuContainer != NULL);
-		mContextMenu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_text_editor.xml", 
+		menu = LLUICtrlFactory::createFromFile<LLContextMenu>("menu_text_editor.xml", 
 																				LLMenuGL::sMenuContainer, 
 																				LLMenuHolderGL::child_registry_t::instance());
+        if(!menu)
+        {
+            LL_WARNS() << "Failed to create menu for LLTextEditor: " << getName() << LL_ENDL;
+            return;
+        }
+		mContextMenuHandle = menu->getHandle();
 	}
 
 	// Route menu to this class
@@ -2102,11 +2113,11 @@ void LLTextEditor::showContextMenu(S32 x, S32 y)
 		}
 	}
 
-	mContextMenu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
-	mContextMenu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
-	mContextMenu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
-	mContextMenu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
-	mContextMenu->show(screen_x, screen_y, this);
+	menu->setItemVisible("Suggestion Separator", (use_spellcheck) && (!mSuggestionList.empty()));
+	menu->setItemVisible("Add to Dictionary", (use_spellcheck) && (is_misspelled));
+	menu->setItemVisible("Add to Ignore", (use_spellcheck) && (is_misspelled));
+	menu->setItemVisible("Spellcheck Separator", (use_spellcheck) && (is_misspelled));
+	menu->show(screen_x, screen_y, this);
 }
 
 
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 1a10d2fd1e..f3939248c2 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -329,7 +329,7 @@ private:
 	keystroke_signal_t mKeystrokeSignal;
 	LLTextValidate::validate_func_t mPrevalidateFunc;
 
-	LLContextMenu* mContextMenu;
+	LLHandle<LLContextMenu> mContextMenuHandle;
 }; // end class LLTextEditor
 
 // Build time optimization, generate once in .cpp file
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index 5150df25f2..2707f7a15c 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -127,7 +127,12 @@ LLToolBar::LLToolBar(const LLToolBar::Params& p)
 
 LLToolBar::~LLToolBar()
 {
-	delete mPopupMenuHandle.get();
+	auto menu = mPopupMenuHandle.get();
+	if (menu)
+	{
+		menu->die();
+		mPopupMenuHandle.markDead();
+	}
 	delete mButtonAddSignal;
 	delete mButtonEnterSignal;
 	delete mButtonLeaveSignal;
diff --git a/indra/newview/lllistcontextmenu.cpp b/indra/newview/lllistcontextmenu.cpp
index 6bda8b1d0d..77185411c5 100644
--- a/indra/newview/lllistcontextmenu.cpp
+++ b/indra/newview/lllistcontextmenu.cpp
@@ -51,6 +51,7 @@ LLListContextMenu::~LLListContextMenu()
 	if (!mMenuHandle.isDead())
 	{
 		mMenuHandle.get()->die();
+		mMenuHandle.markDead();
 	}
 }
 
@@ -59,13 +60,8 @@ void LLListContextMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32
 	LLContextMenu* menup = mMenuHandle.get();
 	if (menup)
 	{
-		//preventing parent (menu holder) from deleting already "dead" context menus on exit
-		LLView* parent = menup->getParent();
-		if (parent)
-		{
-			parent->removeChild(menup);
-		}
-		delete menup;
+		menup->die();
+		mMenuHandle.markDead();
 		mUUIDs.clear();
 	}
 
-- 
cgit v1.2.3


From 5456af4c8ce8c2bef7d544cd8a928ea399832756 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 16:28:34 -0400
Subject: Fix various menu leaks and lazy creation in chiclets, bump floater,
 media controls, and the mini map

---
 indra/newview/llchiclet.cpp     | 66 ++++++++++++++++++++++++++++++-----------
 indra/newview/llchiclet.h       |  6 ++--
 indra/newview/llfloaterbump.cpp | 35 ++++++++++++++--------
 indra/newview/llfloaterbump.h   |  2 +-
 indra/newview/llmediactrl.cpp   | 50 ++++++++++++++++++-------------
 indra/newview/llmediactrl.h     |  4 +--
 indra/newview/llnetmap.cpp      | 28 ++++++++++-------
 indra/newview/llnetmap.h        |  2 +-
 8 files changed, 126 insertions(+), 67 deletions(-)

diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index 0f187b0ecf..cc4f4536a4 100644
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -67,7 +67,6 @@ LLSysWellChiclet::LLSysWellChiclet(const Params& p)
 	, mMaxDisplayedCount(p.max_displayed_count)
 	, mIsNewMessagesState(false)
 	, mFlashToLitTimer(NULL)
-	, mContextMenu(NULL)
 {
 	LLButton::Params button_params = p.button;
 	mButton = LLUICtrlFactory::create<LLButton>(button_params);
@@ -79,6 +78,12 @@ LLSysWellChiclet::LLSysWellChiclet(const Params& p)
 LLSysWellChiclet::~LLSysWellChiclet()
 {
 	mFlashToLitTimer->unset();
+	LLContextMenu* menu = static_cast<LLContextMenu*>(mContextMenuHandle.get());
+	if (menu)
+	{
+		menu->die();
+		mContextMenuHandle.markDead();
+	}
 }
 
 void LLSysWellChiclet::setCounter(S32 counter)
@@ -145,14 +150,16 @@ void LLSysWellChiclet::updateWidget(bool is_window_empty)
 // virtual
 BOOL LLSysWellChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
 {
-	if(!mContextMenu)
+	LLContextMenu* menu_avatar = mContextMenuHandle.get();
+	if(!menu_avatar)
 	{
 		createMenu();
+		menu_avatar = mContextMenuHandle.get();
 	}
-	if (mContextMenu)
+	if (menu_avatar)
 	{
-		mContextMenu->show(x, y);
-		LLMenuGL::showPopup(this, mContextMenu, x, y);
+		menu_avatar->show(x, y);
+		LLMenuGL::showPopup(this, menu_avatar, x, y);
 	}
 	return TRUE;
 }
@@ -192,7 +199,7 @@ bool LLNotificationChiclet::enableMenuItem(const LLSD& user_data)
 
 void LLNotificationChiclet::createMenu()
 {
-	if(mContextMenu)
+	if(mContextMenuHandle.get())
 	{
 		LL_WARNS() << "Menu already exists" << LL_ENDL;
 		return;
@@ -207,10 +214,14 @@ void LLNotificationChiclet::createMenu()
 		boost::bind(&LLNotificationChiclet::enableMenuItem, this, _2));
 
 	llassert(LLMenuGL::sMenuContainer != NULL);
-	mContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>
+	LLContextMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>
 		("menu_notification_well_button.xml",
 		 LLMenuGL::sMenuContainer,
 		 LLViewerMenuHolderGL::child_registry_t::instance());
+	if (menu)
+	{
+		mContextMenuHandle = menu->getHandle();
+	}
 }
 
 /*virtual*/
@@ -309,10 +320,19 @@ LLIMChiclet::LLIMChiclet(const LLIMChiclet::Params& p)
 , mDefaultWidth(p.rect().getWidth())
 , mNewMessagesIcon(NULL)
 , mChicletButton(NULL)
-, mPopupMenu(NULL)
 {
 }
 
+LLIMChiclet::~LLIMChiclet()
+{
+	auto menu = mPopupMenuHandle.get();
+	if (menu)
+	{
+		menu->die();
+		mPopupMenuHandle.markDead();
+	}
+}
+
 /* virtual*/
 BOOL LLIMChiclet::postBuild()
 {
@@ -364,16 +384,18 @@ void LLIMChiclet::setToggleState(bool toggle)
 
 BOOL LLIMChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
 {
-	if(!mPopupMenu)
+	auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+	if(!menu)
 	{
 		createPopupMenu();
+		menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
 	}
 
-	if (mPopupMenu)
+	if (menu)
 	{
 		updateMenuItems();
-		mPopupMenu->arrangeAndClear();
-		LLMenuGL::showPopup(this, mPopupMenu, x, y);
+		menu->arrangeAndClear();
+		LLMenuGL::showPopup(this, menu, x, y);
 	}
 
 	return TRUE;
@@ -381,15 +403,16 @@ BOOL LLIMChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
 
 void LLIMChiclet::hidePopupMenu()
 {
-	if (mPopupMenu)
+	auto menu = mPopupMenuHandle.get();
+	if (menu)
 	{
-		mPopupMenu->setVisible(FALSE);
+		menu->setVisible(FALSE);
 	}
 }
 
 bool LLIMChiclet::canCreateMenu()
 {
-	if(mPopupMenu)
+	if(mPopupMenuHandle.get())
 	{
 		LL_WARNS() << "Menu already exists" << LL_ENDL;
 		return false;
@@ -1107,8 +1130,13 @@ void LLScriptChiclet::createPopupMenu()
 	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 	registrar.add("ScriptChiclet.Action", boost::bind(&LLScriptChiclet::onMenuItemClicked, this, _2));
 
-	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
+	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
 		("menu_script_chiclet.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	if (menu)
+	{
+		mPopupMenuHandle = menu->getHandle();
+	}
+
 }
 
 //////////////////////////////////////////////////////////////////////////
@@ -1185,8 +1213,12 @@ void LLInvOfferChiclet::createPopupMenu()
 	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 	registrar.add("InvOfferChiclet.Action", boost::bind(&LLInvOfferChiclet::onMenuItemClicked, this, _2));
 
-	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
+	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
 		("menu_inv_offer_chiclet.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	if (menu)
+	{
+		mPopupMenuHandle = menu->getHandle();
+	}
 }
 
 // EOF
diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h
index aceedda07e..58a797218f 100644
--- a/indra/newview/llchiclet.h
+++ b/indra/newview/llchiclet.h
@@ -252,7 +252,7 @@ public:
 	{};
 
 	
-	virtual ~LLIMChiclet() {};
+	virtual ~LLIMChiclet();
 
 	/**
 	 * It is used for default setting up of chicklet:click handler, etc.  
@@ -325,7 +325,7 @@ protected:
 
 	bool canCreateMenu();
 
-	LLMenuGL* mPopupMenu;
+	LLHandle<LLUICtrl> mPopupMenuHandle;
 
 	bool mShowSpeaker;
 	bool mCounterEnabled;
@@ -519,7 +519,7 @@ protected:
 	bool mIsNewMessagesState;
 
 	LLFlashTimer* mFlashToLitTimer;
-	LLContextMenu* mContextMenu;
+	LLHandle<LLContextMenu> mContextMenuHandle;
 };
 
 class LLNotificationChiclet : public LLSysWellChiclet
diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp
index 33e4c7cd5f..307ab8c4d1 100644
--- a/indra/newview/llfloaterbump.cpp
+++ b/indra/newview/llfloaterbump.cpp
@@ -69,6 +69,12 @@ LLFloaterBump::LLFloaterBump(const LLSD& key)
 // Destroys the object
 LLFloaterBump::~LLFloaterBump()
 {
+	auto menu = mPopupMenuHandle.get();
+	if (menu)
+	{
+		menu->die();
+		mPopupMenuHandle.markDead();
+	}
 }
 
 BOOL LLFloaterBump::postBuild()
@@ -77,11 +83,15 @@ BOOL LLFloaterBump::postBuild()
 	mList->setAllowMultipleSelection(false);
 	mList->setRightMouseDownCallback(boost::bind(&LLFloaterBump::onScrollListRightClicked, this, _1, _2, _3));
 
-	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_avatar_other.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-	mPopupMenu->setItemVisible(std::string("Normal"), false);
-	mPopupMenu->setItemVisible(std::string("Always use impostor"), false);
-	mPopupMenu->setItemVisible(std::string("Never use impostor"), false);
-	mPopupMenu->setItemVisible(std::string("Impostor seperator"), false);
+	LLContextMenu* menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_avatar_other.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	if (menu)
+	{
+		mPopupMenuHandle = menu->getHandle();
+		menu->setItemVisible(std::string("Normal"), false);
+		menu->setItemVisible(std::string("Always use impostor"), false);
+		menu->setItemVisible(std::string("Never use impostor"), false);
+		menu->setItemVisible(std::string("Impostor seperator"), false);
+	}
 
 	return TRUE;
 }
@@ -176,18 +186,19 @@ void LLFloaterBump::onScrollListRightClicked(LLUICtrl* ctrl, S32 x, S32 y)
 	if (!gMeanCollisionList.empty())
 	{
 		LLScrollListItem* item = mList->hitItem(x, y);
-		if (item && mPopupMenu)
+		auto menu = mPopupMenuHandle.get();
+		if (item && menu)
 		{
 			mItemUUID = item->getUUID();
-			mPopupMenu->buildDrawLabels();
-			mPopupMenu->updateParent(LLMenuGL::sMenuContainer);
+			menu->buildDrawLabels();
+			menu->updateParent(LLMenuGL::sMenuContainer);
 
 			std::string mute_msg = (LLMuteList::getInstance()->isMuted(mItemUUID, mNames[mItemUUID])) ? "UnmuteAvatar" : "MuteAvatar";
-			mPopupMenu->getChild<LLUICtrl>("Avatar Mute")->setValue(LLTrans::getString(mute_msg));
-			mPopupMenu->setItemEnabled(std::string("Zoom In"), bool(gObjectList.findObject(mItemUUID)));
+			menu->getChild<LLUICtrl>("Avatar Mute")->setValue(LLTrans::getString(mute_msg));
+			menu->setItemEnabled(std::string("Zoom In"), bool(gObjectList.findObject(mItemUUID)));
 
-			((LLContextMenu*)mPopupMenu)->show(x, y);
-			LLMenuGL::showPopup(ctrl, mPopupMenu, x, y);
+			menu->show(x, y);
+			LLMenuGL::showPopup(ctrl, menu, x, y);
 		}
 	}
 }
diff --git a/indra/newview/llfloaterbump.h b/indra/newview/llfloaterbump.h
index ce52c75255..d2f9fabdd3 100644
--- a/indra/newview/llfloaterbump.h
+++ b/indra/newview/llfloaterbump.h
@@ -68,7 +68,7 @@ private:
 	virtual ~LLFloaterBump();
 
 	LLScrollListCtrl* mList;
-	LLMenuGL* mPopupMenu;
+	LLHandle<LLContextMenu> mPopupMenuHandle;
 	LLUUID mItemUUID;
 
 	typedef std::map<LLUUID, std::string> uuid_map_t;
diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp
index 9142aadab9..36ac1bdf97 100644
--- a/indra/newview/llmediactrl.cpp
+++ b/indra/newview/llmediactrl.cpp
@@ -106,7 +106,6 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) :
 	mTrusted(p.trusted_content),
 	mWindowShade(NULL),
 	mHoverTextChanged(false),
-	mContextMenu(NULL),
     mAllowFileDownload(false)
 {
 	{
@@ -151,6 +150,13 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) :
 
 LLMediaCtrl::~LLMediaCtrl()
 {
+	auto menu = mContextMenuHandle.get();
+	if (menu)
+	{
+		menu->die();
+		mContextMenuHandle.markDead();
+	}
+
 	if (mMediaSource)
 	{
 		mMediaSource->remObserver( this );
@@ -336,15 +342,33 @@ BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask )
 		setFocus( TRUE );
 	}
 
-	if (mContextMenu)
+	auto menu = mContextMenuHandle.get();
+	if (!menu)
+	{
+		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registar;
+		registar.add("Open.WebInspector", boost::bind(&LLMediaCtrl::onOpenWebInspector, this));
+
+		// stinson 05/05/2014 : use this as the parent of the context menu if the static menu
+		// container has yet to be created
+		LLPanel* menuParent = (LLMenuGL::sMenuContainer != NULL) ? dynamic_cast<LLPanel*>(LLMenuGL::sMenuContainer) : dynamic_cast<LLPanel*>(this);
+		llassert(menuParent != NULL);
+		menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
+			"menu_media_ctrl.xml", menuParent, LLViewerMenuHolderGL::child_registry_t::instance());
+		if (menu)
+		{
+			mContextMenuHandle = menu->getHandle();
+		}
+	}
+
+	if (menu)
 	{
 		// hide/show debugging options
 		bool media_plugin_debugging_enabled = gSavedSettings.getBOOL("MediaPluginDebugging");
-		mContextMenu->setItemVisible("open_webinspector", media_plugin_debugging_enabled );
-		mContextMenu->setItemVisible("debug_separator", media_plugin_debugging_enabled );
+		menu->setItemVisible("open_webinspector", media_plugin_debugging_enabled );
+		menu->setItemVisible("debug_separator", media_plugin_debugging_enabled );
 
-		mContextMenu->show(x, y);
-		LLMenuGL::showPopup(this, mContextMenu, x, y);
+		menu->show(x, y);
+		LLMenuGL::showPopup(this, menu, x, y);
 	}
 
 	return TRUE;
@@ -409,15 +433,6 @@ void LLMediaCtrl::onFocusLost()
 //
 BOOL LLMediaCtrl::postBuild ()
 {
-	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registar;
-	registar.add("Open.WebInspector", boost::bind(&LLMediaCtrl::onOpenWebInspector, this));
-
-	// stinson 05/05/2014 : use this as the parent of the context menu if the static menu
-	// container has yet to be created
-	LLPanel* menuParent = (LLMenuGL::sMenuContainer != NULL) ? dynamic_cast<LLPanel*>(LLMenuGL::sMenuContainer) : dynamic_cast<LLPanel*>(this);
-	llassert(menuParent != NULL);
-	mContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>(
-		"menu_media_ctrl.xml", menuParent, LLViewerMenuHolderGL::child_registry_t::instance());
 	setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChanged, this, _2));
 
 	return TRUE;
@@ -1230,11 +1245,6 @@ void LLMediaCtrl::setTrustedContent(bool trusted)
 	}
 }
 
-void LLMediaCtrl::updateContextMenuParent(LLView* pNewParent)
-{
-	mContextMenu->updateParent(pNewParent);
-}
-
 bool LLMediaCtrl::wantsKeyUpKeyDown() const
 {
     return true;
diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h
index bc4cbaae68..487c654adc 100644
--- a/indra/newview/llmediactrl.h
+++ b/indra/newview/llmediactrl.h
@@ -174,8 +174,6 @@ public:
 
 		LLUUID getTextureID() {return mMediaTextureID;}
 
-		void updateContextMenuParent(LLView* pNewParent);
-
         // The Browser windows want keyup and keydown events. Overridden from LLFocusableElement to return true.
         virtual bool    wantsKeyUpKeyDown() const;
         virtual bool    wantsReturnKey() const;
@@ -220,7 +218,7 @@ public:
 			mTextureHeight;
 
 		class LLWindowShade* mWindowShade;
-		LLContextMenu* mContextMenu;
+		LLHandle<LLContextMenu> mContextMenuHandle;
 };
 
 #endif // LL_LLMediaCtrl_H
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 1240ce7c0f..245fec30c9 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -93,8 +93,7 @@ LLNetMap::LLNetMap (const Params & p)
 	mObjectImagep(),
 	mClosestAgentToCursor(),
 	mClosestAgentAtLastRightClick(),
-	mToolTipMsg(),
-	mPopupMenu(NULL)
+	mToolTipMsg()
 {
 	mScale = gSavedSettings.getF32("MiniMapScale");
 	mPixelsPerMeter = mScale / REGION_WIDTH_METERS;
@@ -103,6 +102,12 @@ LLNetMap::LLNetMap (const Params & p)
 
 LLNetMap::~LLNetMap()
 {
+	auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+	if (menu)
+	{
+		menu->die();
+		mPopupMenuHandle.markDead();
+	}
 }
 
 BOOL LLNetMap::postBuild()
@@ -112,7 +117,8 @@ BOOL LLNetMap::postBuild()
 	registrar.add("Minimap.Zoom", boost::bind(&LLNetMap::handleZoom, this, _2));
 	registrar.add("Minimap.Tracker", boost::bind(&LLNetMap::handleStopTracking, this, _2));
 
-	mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	mPopupMenuHandle = menu->getHandle();
 	return TRUE;
 }
 
@@ -859,12 +865,13 @@ BOOL LLNetMap::handleMouseUp( S32 x, S32 y, MASK mask )
 
 BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
 {
-	if (mPopupMenu)
+	auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+	if (menu)
 	{
-		mPopupMenu->buildDrawLabels();
-		mPopupMenu->updateParent(LLMenuGL::sMenuContainer);
-		mPopupMenu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0));
-		LLMenuGL::showPopup(this, mPopupMenu, x, y);
+		menu->buildDrawLabels();
+		menu->updateParent(LLMenuGL::sMenuContainer);
+		menu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0));
+		LLMenuGL::showPopup(this, menu, x, y);
 	}
 	return TRUE;
 }
@@ -990,9 +997,10 @@ void LLNetMap::handleZoom(const LLSD& userdata)
 
 void LLNetMap::handleStopTracking (const LLSD& userdata)
 {
-	if (mPopupMenu)
+	auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+	if (menu)
 	{
-		mPopupMenu->setItemEnabled ("Stop Tracking", false);
+		menu->setItemEnabled ("Stop Tracking", false);
 		LLTracker::stopTracking (LLTracker::isTracking(NULL));
 	}
 }
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
index 1f7e7d68c6..0adb78d2c8 100644
--- a/indra/newview/llnetmap.h
+++ b/indra/newview/llnetmap.h
@@ -134,7 +134,7 @@ private:
 	void handleZoom(const LLSD& userdata);
 	void handleStopTracking (const LLSD& userdata);
 
-	LLMenuGL*		mPopupMenu;
+	LLHandle<LLView>		mPopupMenuHandle;
 	uuid_vec_t		gmSelected;
 };
 
-- 
cgit v1.2.3


From 22dc6de993cfc3de497edbce998a2bad719809d0 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 19:41:46 -0400
Subject: Fix trivial memory leaks in note card processing, chat history, and
 task inventory

---
 indra/newview/llchathistory.cpp     |  5 +----
 indra/newview/llpreviewnotecard.cpp |  3 ++-
 indra/newview/llviewerobject.cpp    | 27 +++++++++++++++------------
 3 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index f4e1943ea2..42283f0238 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -1103,10 +1103,7 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
 
 LLSD LLChatHistory::getValue() const
 {
-  LLSD* text=new LLSD(); 
-  text->assign(mEditor->getText());
-  return *text;
-    
+	return LLSD(mEditor->getText());
 }
 
 LLChatHistory::~LLChatHistory()
diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp
index 3fd4f51559..33656566d1 100644
--- a/indra/newview/llpreviewnotecard.cpp
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -255,7 +255,7 @@ void LLPreviewNotecard::loadAsset()
 			else
 			{
 				LLHost source_sim = LLHost();
-				LLSD* user_data = new LLSD();
+				LLSD* user_data = nullptr;
 				if (mObjectUUID.notNull())
 				{
 					LLViewerObject *objectp = gObjectList.findObject(mObjectUUID);
@@ -274,6 +274,7 @@ void LLPreviewNotecard::loadAsset()
 						mAssetStatus = PREVIEW_ASSET_LOADED;
 						return;
 					}
+					user_data = new LLSD();
 					user_data->with("taskid", mObjectUUID).with("itemid", mItemUUID);
 				}
 				else
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index aad6c14b4d..b2e2f23ca7 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -3233,36 +3233,39 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
         return;
     }
 
-    LLFilenameAndTask* ft = new LLFilenameAndTask;
-    ft->mTaskID = task_id;
     // we can receive multiple task updates simultaneously, make sure we will not rewrite newer with older update
-    msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, ft->mSerial);
+    S16 serial = 0;
+    msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial);
 
-    if (ft->mSerial == object->mInventorySerialNum
-        && ft->mSerial < object->mExpectedInventorySerialNum)
+    if (serial == object->mInventorySerialNum
+        && serial < object->mExpectedInventorySerialNum)
     {
         // Loop Protection.
         // We received same serial twice.
         // Viewer did some changes to inventory that couldn't be saved server side
         // or something went wrong to cause serial to be out of sync.
         // Drop xfer and restart after some time, assign server's value as expected
-        LL_WARNS() << "Task inventory serial might be out of sync, server serial: " << ft->mSerial << " client expected serial: " << object->mExpectedInventorySerialNum << LL_ENDL;
-        object->mExpectedInventorySerialNum = ft->mSerial;
+        LL_WARNS() << "Task inventory serial might be out of sync, server serial: " << serial << " client expected serial: " << object->mExpectedInventorySerialNum << LL_ENDL;
+        object->mExpectedInventorySerialNum = serial;
         object->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_DESYNC);
     }
-    else if (ft->mSerial < object->mExpectedInventorySerialNum)
+    else if (serial < object->mExpectedInventorySerialNum)
     {
         // Out of date message, record to current serial for loop protection, but do not load it
         // Drop xfer and restart after some time
-        if (ft->mSerial < object->mInventorySerialNum)
+        if (serial < object->mInventorySerialNum)
         {
             LL_WARNS() << "Task serial decreased. Potentially out of order packet or desync." << LL_ENDL;
         }
-        object->mInventorySerialNum = ft->mSerial;
+        object->mInventorySerialNum = serial;
         object->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED);
     }
-    else if (ft->mSerial >= object->mExpectedInventorySerialNum)
+    else if (serial >= object->mExpectedInventorySerialNum)
     {
+        LLFilenameAndTask* ft = new LLFilenameAndTask;
+        ft->mTaskID = task_id;
+        ft->mSerial = serial;
+        
         // We received version we expected or newer. Load it.
         object->mInventorySerialNum = ft->mSerial;
         object->mExpectedInventorySerialNum = ft->mSerial;
@@ -3297,7 +3300,7 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
             object->mRegionp->getHost(),
             TRUE,
             &LLViewerObject::processTaskInvFile,
-            (void**)ft,
+            (void**)ft, // This takes ownership of ft
             LLXferManager::HIGH_PRIORITY);
         if (object->mInvRequestState == INVENTORY_XFER)
         {
-- 
cgit v1.2.3


From a15410468b49125af36672fa20845da65f216dcc Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 19:42:14 -0400
Subject: Fix use after free in LLWorld::removeRegion

---
 indra/newview/llworld.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index 8abb49fba8..95cb98cad2 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -293,13 +293,13 @@ void LLWorld::removeRegion(const LLHost &host)
 
 	mRegionRemovedSignal(regionp);
 
-	delete regionp;
-
 	updateWaterObjects();
 
 	//double check all objects of this region are removed.
 	gObjectList.clearAllMapObjectsInRegion(regionp) ;
 	//llassert_always(!gObjectList.hasMapObjectInRegion(regionp)) ;
+
+	delete regionp; // Delete last to prevent use after free
 }
 
 
-- 
cgit v1.2.3


From a40e2b2af2e0b636954573f3348bced6af126f83 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 19:42:39 -0400
Subject: Fix use after free of LLUUID in script notifications

---
 indra/newview/llscriptfloater.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/indra/newview/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp
index da912ef3d4..6a27ff3047 100644
--- a/indra/newview/llscriptfloater.cpp
+++ b/indra/newview/llscriptfloater.cpp
@@ -460,10 +460,11 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id)
 
 		if(it != mNotifications.end())
 		{
+			LLUUID old_id = it->first; // copy LLUUID to prevent use after free when it is erased below
 			LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
 			if (NULL != chiclet_panelp)
 			{
-				LLIMChiclet * chicletp = chiclet_panelp->findChiclet<LLIMChiclet>(it->first);
+				LLIMChiclet * chicletp = chiclet_panelp->findChiclet<LLIMChiclet>(old_id);
 				if (NULL != chicletp)
 				{
 					// Pass the new_message icon state further.
@@ -472,14 +473,14 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id)
 				}
 			}
 
-			LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>("script_floater", it->first);
+			LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>("script_floater", old_id);
 			if (floater)
 			{
 				// Generate chiclet with a "new message" indicator if a docked window was opened but not in focus. See EXT-3142.
 				set_new_message |= !floater->hasFocus();
 			}
 
-			removeNotification(it->first);
+			removeNotification(old_id);
 		}
 	}
 
-- 
cgit v1.2.3


From 37766d9297fd692cc70490c898d647a11e04098b Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 19:42:49 -0400
Subject: Fix use after free in material manager

---
 indra/newview/llmaterialmgr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llmaterialmgr.cpp b/indra/newview/llmaterialmgr.cpp
index 6e1e6506d9..a52f7244f3 100644
--- a/indra/newview/llmaterialmgr.cpp
+++ b/indra/newview/llmaterialmgr.cpp
@@ -660,8 +660,8 @@ void LLMaterialMgr::processGetQueue()
 		{
 			material_queue_t::iterator itMaterial = loopMaterial++;
 			materialsData.append((*itMaterial).asLLSD());
-			materials.erase(itMaterial);
 			markGetPending(region_id, *itMaterial);
+			materials.erase(itMaterial);
 		}
 		if (materials.empty())
 		{
-- 
cgit v1.2.3


From 8e4cfe8aed308675e1507321f4daffc7994fa1e3 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 19:43:01 -0400
Subject: Fix use after free in inventory object deletion

---
 indra/newview/llinventorymodel.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 216a9f4c94..b50e8431a1 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -1691,11 +1691,11 @@ void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, boo
 	// Can't have links to links, so there's no need for this update
 	// if the item removed is a link. Can also skip if source of the
 	// update is getting broken link info separately.
-	obj = NULL; // delete obj
 	if (fix_broken_links && !is_link_type)
 	{
 		updateLinkedObjectsFromPurge(id);
 	}
+	obj = nullptr; // delete obj
 	if (do_notify_observers)
 	{
 		notifyObservers();
-- 
cgit v1.2.3


From 2745465c732d3c0bcaba6e560d69d3fc8adcff28 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 22:15:07 -0400
Subject: Fix leak of item pairs during LLFlatListView destruction

---
 indra/llui/llflatlistview.cpp | 11 +++++++++++
 indra/llui/llflatlistview.h   |  1 +
 2 files changed, 12 insertions(+)

diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp
index 5e00bf7f45..b13e7389cc 100644
--- a/indra/llui/llflatlistview.cpp
+++ b/indra/llui/llflatlistview.cpp
@@ -505,6 +505,17 @@ LLFlatListView::LLFlatListView(const LLFlatListView::Params& p)
 	}
 };
 
+LLFlatListView::~LLFlatListView()
+{
+	for (pairs_iterator_t it = mItemPairs.begin(); it != mItemPairs.end(); ++it)
+	{
+		mItemsPanel->removeChild((*it)->first);
+		(*it)->first->die();
+		delete *it;
+	}
+	mItemPairs.clear();
+}
+
 // virtual
 void LLFlatListView::draw()
 {
diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h
index 230ea200d8..d47c1cf333 100644
--- a/indra/llui/llflatlistview.h
+++ b/indra/llui/llflatlistview.h
@@ -299,6 +299,7 @@ public:
 
 	virtual S32	notify(const LLSD& info) ;
 
+	virtual ~LLFlatListView();
 protected:
 
 	/** Pairs LLpanel representing a single item LLPanel and LLSD associated with it */
-- 
cgit v1.2.3


From 59062cec1cf108863f1101b193b7dfe89e79cb28 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 22:16:12 -0400
Subject: Fix use after free of inventory offer in
 LLGroupNoticeNotificationListItem::onClickAttachment

This is due to forceResponse deleting the inventory offer upon completion
---
 indra/newview/llnotificationlistitem.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp
index 6a79a0c68c..f86edfd0cf 100644
--- a/indra/newview/llnotificationlistitem.cpp
+++ b/indra/newview/llnotificationlistitem.cpp
@@ -521,8 +521,6 @@ void LLGroupNoticeNotificationListItem::close()
 void LLGroupNoticeNotificationListItem::onClickAttachment()
 {
     if (mInventoryOffer != NULL) {
-        mInventoryOffer->forceResponse(IOR_ACCEPT);
-
         static const LLUIColor textColor = LLUIColorTable::instance().getColor(
             "GroupNotifyDimmedTextColor");
         mAttachmentTextBox->setColor(textColor);
@@ -532,7 +530,7 @@ void LLGroupNoticeNotificationListItem::onClickAttachment()
         if (!isAttachmentOpenable(mInventoryOffer->mType)) {
             LLNotifications::instance().add("AttachmentSaved", LLSD(), LLSD());
         }
-
+        mInventoryOffer->forceResponse(IOR_ACCEPT);
         mInventoryOffer = NULL;
     }
 }
-- 
cgit v1.2.3


From d0f0d77474ac3630a2d1fca2f819ef67bdfff0fb Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 22:16:39 -0400
Subject: Fix leak of tex layer objects during LLLocalTextureObject destruction

---
 indra/llappearance/lllocaltextureobject.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/indra/llappearance/lllocaltextureobject.cpp b/indra/llappearance/lllocaltextureobject.cpp
index 3f564ec3de..0481326e9e 100644
--- a/indra/llappearance/lllocaltextureobject.cpp
+++ b/indra/llappearance/lllocaltextureobject.cpp
@@ -76,6 +76,7 @@ LLLocalTextureObject::LLLocalTextureObject(const LLLocalTextureObject& lto) :
 
 LLLocalTextureObject::~LLLocalTextureObject()
 {
+	delete_and_clear(mTexLayers);
 }
 
 LLGLTexture* LLLocalTextureObject::getImage() const
-- 
cgit v1.2.3


From 5326e3a206d8fb2ed79c909bb37a34f848b77672 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 22:17:34 -0400
Subject: Fix leak of mRoot during LLAvatarAppearance destruction

---
 indra/llappearance/llavatarappearance.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index 2d6d2a10d2..232123a635 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -305,7 +305,12 @@ LLAvatarAppearance::~LLAvatarAppearance()
 		}
 	}
 
-	if (mRoot) mRoot->removeAllChildren();
+	if (mRoot) 
+	{
+		mRoot->removeAllChildren();
+		delete mRoot;
+		mRoot = nullptr;
+	}
 	mJointMap.clear();
 
 	clearSkeleton();
-- 
cgit v1.2.3


From 589a167147fd4a316f4cf588bbb2aaa103c9d3bb Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Wed, 19 Oct 2022 22:19:22 -0400
Subject: Fix leak of LLFolderViewModel sorter and filter with unique_ptr

---
 indra/llui/llfolderviewmodel.h | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h
index 093e213be3..dd5eb47a63 100644
--- a/indra/llui/llfolderviewmodel.h
+++ b/indra/llui/llfolderviewmodel.h
@@ -419,21 +419,15 @@ public:
 		mFilter(filter)
 	{}
 
-	virtual ~LLFolderViewModel() 
-	{
-		delete mSorter;
-		mSorter = NULL;
-		delete mFilter;
-		mFilter = NULL;
-	}
+	virtual ~LLFolderViewModel() {}
 
 	virtual SortType& getSorter()					 { return *mSorter; }
 	virtual const SortType& getSorter() const 		 { return *mSorter; }
-	virtual void setSorter(const SortType& sorter) 	 { mSorter = new SortType(sorter); requestSortAll(); }
+	virtual void setSorter(const SortType& sorter) 	 { mSorter.reset(new SortType(sorter)); requestSortAll(); }
 
 	virtual FilterType& getFilter() 				 { return *mFilter; }
 	virtual const FilterType& getFilter() const		 { return *mFilter; }
-	virtual void setFilter(const FilterType& filter) { mFilter = new FilterType(filter); }
+	virtual void setFilter(const FilterType& filter) { mFilter.reset(new FilterType(filter)); }
 
 	// By default, we assume the content is available. If a network fetch mechanism is implemented for the model,
 	// this method needs to be overloaded and return the relevant fetch status.
@@ -471,8 +465,8 @@ public:
 	}
 
 protected:
-	SortType*		mSorter;
-	FilterType*		mFilter;
+	std::unique_ptr<SortType>		mSorter;
+	std::unique_ptr<FilterType>		mFilter;
 };
 
 #endif // LLFOLDERVIEWMODEL_H
-- 
cgit v1.2.3


From 58d62656620be44360861ef60630da1845c307be Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Thu, 20 Oct 2022 22:52:41 +0300
Subject: SL-18423 Post-merge: restore RyeMutt's fix from SL-18412

---
 indra/newview/llnetmap.cpp | 54 +++++++++++++++++++++++++++-------------------
 indra/newview/llnetmap.h   |  2 +-
 2 files changed, 33 insertions(+), 23 deletions(-)

diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index 1f84e14877..0ba3c3d691 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -118,10 +118,12 @@ LLNetMap::LLNetMap (const Params & p)
 
 LLNetMap::~LLNetMap()
 {
-	if (mPopupMenu)
-	{
-        mPopupMenu->die();
-	}
+    auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+    if (menu)
+    {
+        menu->die();
+        mPopupMenuHandle.markDead();
+    }
 }
 
 BOOL LLNetMap::postBuild()
@@ -137,9 +139,9 @@ BOOL LLNetMap::postBuild()
     commitRegistrar.add("Minimap.MapOrientation.Set", boost::bind(&LLNetMap::setMapOrientation, this, _2));
     commitRegistrar.add("Minimap.AboutLand", boost::bind(&LLNetMap::popupShowAboutLand, this, _2));
 
-    mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_mini_map.xml", gMenuHolder,
-                                                                          LLViewerMenuHolderGL::child_registry_t::instance());
-    mPopupMenu->setItemEnabled("Re-center map", false);
+    LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_mini_map.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+    mPopupMenuHandle = menu->getHandle();
+    menu->setItemEnabled("Re-center map", false);
 	return TRUE;
 }
 
@@ -208,8 +210,12 @@ void LLNetMap::draw()
         mCentering = false;
     }
 
-    bool can_recenter_map = !(centered || mCentering || auto_centering);
-    mPopupMenu->setItemEnabled("Re-center map", can_recenter_map);
+    auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+    if (menu)
+    {
+        bool can_recenter_map = !(centered || mCentering || auto_centering);
+        menu->setItemEnabled("Re-center map", can_recenter_map);
+    }
     updateAboutLandPopupButton();
 
 	// Prepare a scissor region
@@ -597,14 +603,15 @@ void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color,
 
 bool LLNetMap::isMouseOnPopupMenu()
 {
-    if (!mPopupMenu->isOpen())
+    auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+    if (!menu || !menu->isOpen())
     {
         return false;
     }
 
     S32 popup_x;
     S32 popup_y;
-    LLUI::getInstance()->getMousePositionLocal(mPopupMenu, &popup_x, &popup_y);
+    LLUI::getInstance()->getMousePositionLocal(menu, &popup_x, &popup_y);
     // *NOTE: Tolerance is larger than it needs to be because the context menu is offset from the mouse when the menu is opened from certain
     // directions. This may be a quirk of LLMenuGL::showPopup. -Cosmic,2022-03-22
     constexpr S32 tolerance = 10;
@@ -615,7 +622,7 @@ bool LLNetMap::isMouseOnPopupMenu()
     {
         for (S32 sign_y = -1; sign_y <= 1; sign_y += 2)
         {
-            if (mPopupMenu->pointInView(popup_x + (sign_x * tolerance), popup_y + (sign_y * tolerance)))
+            if (menu->pointInView(popup_x + (sign_x * tolerance), popup_y + (sign_y * tolerance)))
             {
                 return true;
             }
@@ -626,7 +633,8 @@ bool LLNetMap::isMouseOnPopupMenu()
 
 void LLNetMap::updateAboutLandPopupButton()
 {
-    if (!mPopupMenu->isOpen())
+    auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+    if (!menu || !menu->isOpen())
     {
         return;
     }
@@ -634,7 +642,7 @@ void LLNetMap::updateAboutLandPopupButton()
     LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(mPopupWorldPos);
     if (!region)
     {
-        mPopupMenu->setItemEnabled("About Land", false);
+        menu->setItemEnabled("About Land", false);
     }
     else
     {
@@ -649,7 +657,7 @@ void LLNetMap::updateAboutLandPopupButton()
             {
                 valid_parcel = hover_parcel->getOwnerID().notNull();
             }
-            mPopupMenu->setItemEnabled("About Land", valid_parcel);
+            menu->setItemEnabled("About Land", valid_parcel);
         }
     }
 }
@@ -1045,13 +1053,14 @@ BOOL LLNetMap::handleMouseUp(S32 x, S32 y, MASK mask)
 
 BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
 {
-	if (mPopupMenu)
+    auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+    if (menu)
 	{
 		mPopupWorldPos = viewPosToGlobal(x, y);
-        mPopupMenu->buildDrawLabels();
-        mPopupMenu->updateParent(LLMenuGL::sMenuContainer);
-        mPopupMenu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0));
-		LLMenuGL::showPopup(this, mPopupMenu, x, y);
+        menu->buildDrawLabels();
+        menu->updateParent(LLMenuGL::sMenuContainer);
+        menu->setItemEnabled("Stop Tracking", LLTracker::isTracking(0));
+		LLMenuGL::showPopup(this, menu, x, y);
 	}
 	return TRUE;
 }
@@ -1184,9 +1193,10 @@ void LLNetMap::setZoom(const LLSD &userdata)
 
 void LLNetMap::handleStopTracking (const LLSD& userdata)
 {
-	if (mPopupMenu)
+    auto menu = static_cast<LLMenuGL*>(mPopupMenuHandle.get());
+    if (menu)
 	{
-        mPopupMenu->setItemEnabled ("Stop Tracking", false);
+        menu->setItemEnabled ("Stop Tracking", false);
 		LLTracker::stopTracking (LLTracker::isTracking(NULL));
 	}
 }
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
index 20d0828ab1..75c1abc4ed 100644
--- a/indra/newview/llnetmap.h
+++ b/indra/newview/llnetmap.h
@@ -162,7 +162,7 @@ private:
     void setMapOrientation(const LLSD& userdata);
     void popupShowAboutLand(const LLSD& userdata);
 
-    LLMenuGL*       mPopupMenu;
+    LLHandle<LLView> mPopupMenuHandle;
 	uuid_vec_t		gmSelected;
 };
 
-- 
cgit v1.2.3


From 8de9beeb6f1d2d3c3e6f5fde396e4cde0a54d36a Mon Sep 17 00:00:00 2001
From: Mnikolenko Productengine <mnikolenko@productengine.com>
Date: Mon, 24 Oct 2022 21:46:14 +0300
Subject: SL-18388 Searchable debug settings UI

---
 indra/llui/llscrolllistctrl.cpp                    |  10 +-
 indra/llui/llscrolllistctrl.h                      |   4 +-
 indra/newview/app_settings/settings.xml            |  11 +
 indra/newview/llfloatersettingsdebug.cpp           | 272 ++++++++++++++++-----
 indra/newview/llfloatersettingsdebug.h             |  21 +-
 .../default/xui/en/floater_settings_debug.xml      | 134 +++++++---
 6 files changed, 343 insertions(+), 109 deletions(-)

diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 167593bd52..3922a94390 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -1313,14 +1313,14 @@ LLScrollListItem* LLScrollListCtrl::getItemByLabel(const std::string& label, BOO
 }
 
 
-BOOL LLScrollListCtrl::selectItemByPrefix(const std::string& target, BOOL case_sensitive)
+BOOL LLScrollListCtrl::selectItemByPrefix(const std::string& target, BOOL case_sensitive, S32 column)
 {
-	return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive);
+	return selectItemByPrefix(utf8str_to_wstring(target), case_sensitive, column);
 }
 
 // Selects first enabled item that has a name where the name's first part matched the target string.
 // Returns false if item not found.
-BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sensitive)
+BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sensitive, S32 column)
 {
 	BOOL found = FALSE;
 
@@ -1335,7 +1335,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen
 		{
 			LLScrollListItem* item = *iter;
 			// Only select enabled items with matching names
-			LLScrollListCell* cellp = item->getColumn(getSearchColumn());
+			LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column);
 			BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getValue().asString()[0]) : FALSE;
 			if (select)
 			{
@@ -1358,7 +1358,7 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen
 			LLScrollListItem* item = *iter;
 
 			// Only select enabled items with matching names
-			LLScrollListCell* cellp = item->getColumn(getSearchColumn());
+			LLScrollListCell* cellp = item->getColumn(column == -1 ? getSearchColumn() : column);
 			if (!cellp)
 			{
 				continue;
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index 6f7d4768e1..a908dbc968 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -261,8 +261,8 @@ public:
 	virtual LLScrollListItem* addSimpleElement(const std::string& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD());
 
 	BOOL			selectItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 );		// FALSE if item not found
-	BOOL			selectItemByPrefix(const std::string& target, BOOL case_sensitive = TRUE);
-	BOOL			selectItemByPrefix(const LLWString& target, BOOL case_sensitive = TRUE);
+	BOOL			selectItemByPrefix(const std::string& target, BOOL case_sensitive = TRUE, S32 column = -1);
+	BOOL			selectItemByPrefix(const LLWString& target, BOOL case_sensitive = TRUE, S32 column = -1);
 	LLScrollListItem*  getItemByLabel( const std::string& item, BOOL case_sensitive = TRUE, S32 column = 0 );
 	const std::string	getSelectedItemLabel(S32 column = 0) const;
 	LLSD			getSelectedValue();
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 6861153d43..e9444efd43 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -16876,5 +16876,16 @@
     <key>Value</key>
     <string></string>
   </map>
+  <key>DebugSettingsHideDefault</key>
+  <map>
+    <key>Comment</key>
+    <string>Show non-default settings only in Debug Settings list</string>
+    <key>Persist</key>
+    <integer>0</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
 </map>
 </llsd>
diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp
index 186994c857..25cf8b2bf7 100644
--- a/indra/newview/llfloatersettingsdebug.cpp
+++ b/indra/newview/llfloatersettingsdebug.cpp
@@ -2,9 +2,9 @@
  * @file llfloatersettingsdebug.cpp
  * @brief floater for debugging internal viewer settings
  *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2022, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,8 +27,8 @@
 #include "llviewerprecompiledheaders.h"
 #include "llfloatersettingsdebug.h"
 #include "llfloater.h"
+#include "llfiltereditor.h"
 #include "lluictrlfactory.h"
-//#include "llfirstuse.h"
 #include "llcombobox.h"
 #include "llspinctrl.h"
 #include "llcolorswatch.h"
@@ -37,12 +37,11 @@
 
 
 LLFloaterSettingsDebug::LLFloaterSettingsDebug(const LLSD& key) 
-:	LLFloater(key)
+:   LLFloater(key),
+    mSettingList(NULL)
 {
-	mCommitCallbackRegistrar.add("SettingSelect",	boost::bind(&LLFloaterSettingsDebug::onSettingSelect, this,_1));
 	mCommitCallbackRegistrar.add("CommitSettings",	boost::bind(&LLFloaterSettingsDebug::onCommitSettings, this));
 	mCommitCallbackRegistrar.add("ClickDefault",	boost::bind(&LLFloaterSettingsDebug::onClickDefault, this));
-
 }
 
 LLFloaterSettingsDebug::~LLFloaterSettingsDebug()
@@ -50,59 +49,43 @@ LLFloaterSettingsDebug::~LLFloaterSettingsDebug()
 
 BOOL LLFloaterSettingsDebug::postBuild()
 {
-	LLComboBox* settings_combo = getChild<LLComboBox>("settings_combo");
+    enableResizeCtrls(true, false, true);
 
-	struct f : public LLControlGroup::ApplyFunctor
-	{
-		LLComboBox* combo;
-		f(LLComboBox* c) : combo(c) {}
-		virtual void apply(const std::string& name, LLControlVariable* control)
-		{
-			if (!control->isHiddenFromSettingsEditor())
-			{
-				combo->add(name, (void*)control);
-			}
-		}
-	} func(settings_combo);
+    mComment = getChild<LLTextEditor>("comment_text");
 
-	std::string key = getKey().asString();
-	if (key == "all" || key == "base")
-	{
-		gSavedSettings.applyToAll(&func);
-	}
-	if (key == "all" || key == "account")
-	{
-		gSavedPerAccountSettings.applyToAll(&func);
-	}
+    getChild<LLFilterEditor>("filter_input")->setCommitCallback(boost::bind(&LLFloaterSettingsDebug::setSearchFilter, this, _2));
+
+    mSettingList = getChild<LLScrollListCtrl>("setting_list");
+    mSettingList->setCommitOnSelectionChange(TRUE);
+    mSettingList->setCommitCallback(boost::bind(&LLFloaterSettingsDebug::onSettingSelect, this));
+
+    updateList();
+
+    gSavedSettings.getControl("DebugSettingsHideDefault")->getCommitSignal()->connect(boost::bind(&LLFloaterSettingsDebug::updateList, this, false));
 
-	settings_combo->sortByName();
-	settings_combo->updateSelection();
-	mComment = getChild<LLTextEditor>("comment_text");
 	return TRUE;
 }
 
 void LLFloaterSettingsDebug::draw()
 {
-	LLComboBox* settings_combo = getChild<LLComboBox>("settings_combo");
-	LLControlVariable* controlp = (LLControlVariable*)settings_combo->getCurrentUserdata();
-	updateControl(controlp);
+    LLScrollListItem* first_selected = mSettingList->getFirstSelected();
+    if (first_selected)
+    {
+        LLControlVariable* controlp = (LLControlVariable*)first_selected->getUserdata();
+        updateControl(controlp);
+    }
 
 	LLFloater::draw();
 }
 
-//static 
-void LLFloaterSettingsDebug::onSettingSelect(LLUICtrl* ctrl)
-{
-	LLComboBox* combo_box = (LLComboBox*)ctrl;
-	LLControlVariable* controlp = (LLControlVariable*)combo_box->getCurrentUserdata();
-
-	updateControl(controlp);
-}
-
 void LLFloaterSettingsDebug::onCommitSettings()
 {
-	LLComboBox* settings_combo = getChild<LLComboBox>("settings_combo");
-	LLControlVariable* controlp = (LLControlVariable*)settings_combo->getCurrentUserdata();
+    LLScrollListItem* first_selected = mSettingList->getFirstSelected();
+    if (!first_selected)
+    {
+        return;
+    }
+    LLControlVariable* controlp = (LLControlVariable*)first_selected->getUserdata();
 
 	if (!controlp)
 	{
@@ -176,19 +159,23 @@ void LLFloaterSettingsDebug::onCommitSettings()
 	  default:
 		break;
 	}
+    updateDefaultColumn(controlp);
 }
 
 // static
 void LLFloaterSettingsDebug::onClickDefault()
 {
-	LLComboBox* settings_combo = getChild<LLComboBox>("settings_combo");
-	LLControlVariable* controlp = (LLControlVariable*)settings_combo->getCurrentUserdata();
-
-	if (controlp)
-	{
-		controlp->resetToDefault(true);
-		updateControl(controlp);
-	}
+    LLScrollListItem* first_selected = mSettingList->getFirstSelected();
+    if (first_selected)
+    {
+        LLControlVariable* controlp = (LLControlVariable*)first_selected->getUserdata();
+        if (controlp)
+        {
+            controlp->resetToDefault(true);
+            updateDefaultColumn(controlp);
+            updateControl(controlp);
+        }
+    }
 }
 
 // we've switched controls, or doing per-frame update, so update spinners, etc.
@@ -207,21 +194,19 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)
 		return;
 	}
 
-	spinner1->setVisible(FALSE);
-	spinner2->setVisible(FALSE);
-	spinner3->setVisible(FALSE);
-	spinner4->setVisible(FALSE);
-	color_swatch->setVisible(FALSE);
-	getChildView("val_text")->setVisible( FALSE);
-	mComment->setText(LLStringUtil::null);
+    hideUIControls();
 
-	if (controlp)
+	if (controlp && !isSettingHidden(controlp))
 	{
 		eControlType type = controlp->type();
 
 		//hide combo box only for non booleans, otherwise this will result in the combo box closing every frame
 		getChildView("boolean_combo")->setVisible( type == TYPE_BOOLEAN);
-		
+        getChildView("default_btn")->setVisible(true);
+        getChildView("setting_name_txt")->setVisible(true);
+        getChild<LLTextBox>("setting_name_txt")->setText(controlp->getName());
+        getChild<LLTextBox>("setting_name_txt")->setToolTip(controlp->getName());
+        mComment->setVisible(true);
 
 		mComment->setText(controlp->getComment());
 		spinner1->setMaxValue(F32_MAX);
@@ -479,3 +464,166 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)
 	}
 
 }
+
+void LLFloaterSettingsDebug::updateList(bool skip_selection)
+{
+    std::string last_selected;
+    LLScrollListItem* item = mSettingList->getFirstSelected();
+    if (item)
+    {
+        LLScrollListCell* cell = item->getColumn(1);
+        if (cell)
+        {
+            last_selected = cell->getValue().asString();
+         }
+    }
+
+    mSettingList->deleteAllItems();
+    struct f : public LLControlGroup::ApplyFunctor
+    {
+        LLScrollListCtrl* setting_list;
+        LLFloaterSettingsDebug* floater;
+        std::string selected_setting;
+        bool skip_selection;
+        f(LLScrollListCtrl* list, LLFloaterSettingsDebug* floater, std::string setting, bool skip_selection) 
+            : setting_list(list), floater(floater), selected_setting(setting), skip_selection(skip_selection) {}
+        virtual void apply(const std::string& name, LLControlVariable* control)
+        {
+            if (!control->isHiddenFromSettingsEditor() && floater->matchesSearchFilter(name) && !floater->isSettingHidden(control))
+            {
+                LLSD row;
+
+                row["columns"][0]["column"] = "changed_setting";
+                row["columns"][0]["value"] = control->isDefault() ? "" : "*";
+
+                row["columns"][1]["column"] = "setting";
+                row["columns"][1]["value"] = name;
+
+                LLScrollListItem* item = setting_list->addElement(row, ADD_BOTTOM, (void*)control);
+                if (!floater->mSearchFilter.empty() && (selected_setting == name) && !skip_selection)
+                {
+                    std::string lower_name(name);
+                    LLStringUtil::toLower(lower_name);
+                    if (LLStringUtil::startsWith(lower_name, floater->mSearchFilter))
+                    {
+                        item->setSelected(true);
+                    }
+                }
+            }
+        }
+    } func(mSettingList, this, last_selected, skip_selection);
+
+    std::string key = getKey().asString();
+    if (key == "all" || key == "base")
+    {
+        gSavedSettings.applyToAll(&func);
+    }
+    if (key == "all" || key == "account")
+    {
+        gSavedPerAccountSettings.applyToAll(&func);
+    }
+
+
+    if (!mSettingList->isEmpty())
+    {
+        if (mSettingList->hasSelectedItem())
+        {
+            mSettingList->scrollToShowSelected();
+        }
+        else if (!mSettingList->hasSelectedItem() && !mSearchFilter.empty() && !skip_selection)
+        {
+            if (!mSettingList->selectItemByPrefix(mSearchFilter, false, 1))
+            {
+                mSettingList->selectFirstItem();
+            }
+            mSettingList->scrollToShowSelected();
+        }
+    }
+    else
+    {
+        LLSD row;
+
+        row["columns"][0]["column"] = "changed_setting";
+        row["columns"][0]["value"] = "";
+        row["columns"][1]["column"] = "setting";
+        row["columns"][1]["value"] = "No matching settings.";
+
+        mSettingList->addElement(row);
+        hideUIControls();
+    }
+}
+
+void LLFloaterSettingsDebug::onSettingSelect()
+{
+    LLScrollListItem* first_selected = mSettingList->getFirstSelected();
+    if (first_selected)
+    {
+        LLControlVariable* controlp = (LLControlVariable*)first_selected->getUserdata();
+        if (controlp)
+        {
+            updateControl(controlp);
+        }
+    }
+}
+
+void LLFloaterSettingsDebug::setSearchFilter(const std::string& filter)
+{
+    if(mSearchFilter == filter)
+        return;
+    mSearchFilter = filter;
+    LLStringUtil::toLower(mSearchFilter);
+    updateList();
+}
+
+bool LLFloaterSettingsDebug::matchesSearchFilter(std::string setting_name)
+{
+    // If the search filter is empty, everything passes.
+    if (mSearchFilter.empty()) return true;
+
+    LLStringUtil::toLower(setting_name);
+    std::string::size_type match_name = setting_name.find(mSearchFilter);
+
+    return (std::string::npos != match_name);
+}
+
+bool LLFloaterSettingsDebug::isSettingHidden(LLControlVariable* control)
+{
+    static LLCachedControl<bool> hide_default(gSavedSettings, "DebugSettingsHideDefault", false);
+    return hide_default && control->isDefault();
+}
+
+void LLFloaterSettingsDebug::updateDefaultColumn(LLControlVariable* control)
+{
+    if (isSettingHidden(control))
+    {
+        hideUIControls();
+        updateList(true);
+        return;
+    }
+
+    LLScrollListItem* item = mSettingList->getFirstSelected();
+    if (item)
+    {
+        LLScrollListCell* cell = item->getColumn(0);
+        if (cell)
+        {
+            std::string is_default = control->isDefault() ? "" : "*";
+            cell->setValue(is_default);
+        }
+    }
+}
+
+void LLFloaterSettingsDebug::hideUIControls()
+{
+    getChildView("val_spinner_1")->setVisible(false);
+    getChildView("val_spinner_2")->setVisible(false);
+    getChildView("val_spinner_3")->setVisible(false);
+    getChildView("val_spinner_4")->setVisible(false);
+    getChildView("val_color_swatch")->setVisible(false);
+    getChildView("val_text")->setVisible(false);
+    getChildView("default_btn")->setVisible(false);
+    getChildView("boolean_combo")->setVisible(false);
+    getChildView("setting_name_txt")->setVisible(false);
+    mComment->setVisible(false);
+}
+
diff --git a/indra/newview/llfloatersettingsdebug.h b/indra/newview/llfloatersettingsdebug.h
index f07e0557e3..888eaadcbd 100644
--- a/indra/newview/llfloatersettingsdebug.h
+++ b/indra/newview/llfloatersettingsdebug.h
@@ -2,9 +2,9 @@
  * @file llfloatersettingsdebug.h
  * @brief floater for debugging internal viewer settings
  *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * $LicenseInfo:firstyear=2022&license=viewerlgpl$
  * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2022, Linden Research, Inc.
  * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -30,6 +30,8 @@
 #include "llcontrol.h"
 #include "llfloater.h"
 
+class LLScrollListCtrl;
+
 class LLFloaterSettingsDebug 
 :	public LLFloater
 {
@@ -42,18 +44,31 @@ public:
 
 	void updateControl(LLControlVariable* control);
 
-	void onSettingSelect(LLUICtrl* ctrl);
 	void onCommitSettings();
 	void onClickDefault();
 
+    bool matchesSearchFilter(std::string setting_name);
+    bool isSettingHidden(LLControlVariable* control);
+
 private:
 	// key - selects which settings to show, one of:
 	// "all", "base", "account", "skin"
 	LLFloaterSettingsDebug(const LLSD& key);
 	virtual ~LLFloaterSettingsDebug();
+
+    void updateList(bool skip_selection = false);
+    void onSettingSelect();
+    void setSearchFilter(const std::string& filter);
+
+    void updateDefaultColumn(LLControlVariable* control);
+    void hideUIControls();
+
+    LLScrollListCtrl* mSettingList;
 	
 protected:
 	class LLTextEditor* mComment;
+
+    std::string mSearchFilter;
 };
 
 #endif //LLFLOATERDEBUGSETTINGS_H
diff --git a/indra/newview/skins/default/xui/en/floater_settings_debug.xml b/indra/newview/skins/default/xui/en/floater_settings_debug.xml
index 3ed2bd7206..e4fda5cd10 100644
--- a/indra/newview/skins/default/xui/en/floater_settings_debug.xml
+++ b/indra/newview/skins/default/xui/en/floater_settings_debug.xml
@@ -2,41 +2,79 @@
 <floater
  legacy_header_height="18"
  can_minimize="false"
- height="215"
+ height="360"
+ min_height="367"
  layout="topleft"
  name="settings_debug"
  help_topic="settings_debug"
  title="DEBUG SETTINGS"
- width="350">
-    <combo_box
-     allow_text_entry="true"
-     follows="top|left"
-     height="22"
-     layout="topleft"
-     left="15"
-     max_chars="255"
-     name="settings_combo"
-     top="30"
-     width="320">
-      <combo_box.commit_callback
-       function="SettingSelect" />
-    </combo_box>
-    <text_editor
-     enabled="false"
-     height="60"
-     layout="topleft"
-     left_delta="0"
-     name="comment_text"
-     top_pad="10"
-     width="320"
-     word_wrap="true" />
+ reuse_instance="true"
+ can_resize="true"
+ min_width="550"
+ width="570">
+  <filter_editor
+   follows="left|top|right"
+   height="23"
+   layout="topleft"
+   left="10"
+   right="-10"
+   label="Enter search text"
+   max_length_chars="300"
+   name="filter_input"
+   text_pad_left="10"
+   top="30" />
+  <scroll_list
+   column_padding="0"
+   draw_heading="true"
+   draw_stripes="false"
+   heading_height="23"
+   height="266"
+   layout="topleft"
+   search_column="1"
+   left="10"
+   follows="left|top|bottom"
+   name="setting_list"
+   top_pad="2"
+   width="300">
+    <scroll_list.columns
+     name="changed_setting"
+     relative_width="0.05"  />
+    <scroll_list.columns
+     label="Setting"
+     name="setting" />
+  </scroll_list>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="16"
+   layout="topleft"
+   name="setting_name_txt"
+   font="SansSerifSmallBold"
+   top_delta="8"
+   left_pad="10"
+   visible="false"
+   use_ellipses="true"
+   text_color="White"
+   width="240">
+    Debug setting name
+  </text>
+  <text_editor
+   enabled="false"
+   height="75"
+   layout="topleft"
+   visible="false"
+   name="comment_text"
+   follows="left|top"
+   width="240"
+   top_delta="20"
+   word_wrap="true" />
   <radio_group
    follows="top|left"
    height="30"
    layout="topleft"
-   left_delta="0"
    name="boolean_combo"
-   top_pad="10"
+   top_pad="15"
    visible="false"
    tab_stop="true" 
    width="100">
@@ -55,21 +93,25 @@
   </radio_group>
     <line_editor
      height="20"
+     follows="top|left"
      layout="topleft"
      left_delta="0"
      name="val_text"
      top_delta="0"
      visible="false"
-     width="300" >
+     width="220" >
       <line_editor.commit_callback
        function="CommitSettings" />
     </line_editor>
     <color_swatch
-     bottom="185"
+     top_delta="0"
+     left_delta="0"
+     follows="top|left"
      can_apply_immediately="true"
      height="55"
      name="val_color_swatch"
      label="Color"
+     visible="false"
      layout="topleft"
      width="37" >
       <color_swatch.commit_callback
@@ -79,10 +121,11 @@
      height="20"
      label="x"
      layout="topleft"
+     follows="top|left"
      left_delta="0"
      max_val="1e+007"
      name="val_spinner_1"
-     top_delta="10"
+     top_delta="5"
      visible="false"
      width="120" >
       <spinner.commit_callback
@@ -92,10 +135,11 @@
      height="20"
      label="x"
      layout="topleft"
-     left_pad="15"
+     follows="top|left"
+     left_delta="0"
      max_val="1e+007"
      name="val_spinner_2"
-     top_delta="0"
+     top_pad="10"
      visible="false"
      width="120">
       <spinner.commit_callback
@@ -105,10 +149,11 @@
      height="20"
      label="x"
      layout="topleft"
-     left="15"
+     follows="top|left"
+     left_delta="0"
      max_val="1e+007"
      name="val_spinner_3"
-     top="160"
+     top_pad="10"
      visible="false"
      width="120">
       <spinner.commit_callback
@@ -118,10 +163,11 @@
      height="20"
      label="x"
      layout="topleft"
-     left_pad="15"
+     follows="top|left"
+     left_delta="0"
      max_val="1e+007"
      name="val_spinner_4"
-     top_delta="0"
+     top_pad="10"
      visible="false"
      width="120" >
       <spinner.commit_callback
@@ -130,12 +176,26 @@
     <button
      height="22"
      label="Reset to default"
+     follows="left|top"
      layout="topleft"
-     left="15"
+     left_delta="0"
      name="default_btn"
-     top="186"
+     visible="false"
+     top_pad="10"
      width="150" >
       <button.commit_callback
        function="ClickDefault" />
     </button>
+    <check_box
+      control_name="DebugSettingsHideDefault"
+      height="16"
+      initial_value="true"
+      label="Show changed settings only"
+      layout="topleft"
+      top_pad="10"
+      left="10"
+      follows="left|bottom"
+      name="hide_default"
+      width="330">
+    </check_box>
 </floater>
-- 
cgit v1.2.3


From 228f5b2f120cf7e5691dcb174a2d4f34c5340ef4 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Tue, 25 Oct 2022 09:48:51 -0400
Subject: Restore LLUUID to a plain old data type in a post-c++11 world

---
 indra/llcommon/lluuid.cpp | 30 ------------------------------
 indra/llcommon/lluuid.h   |  8 ++++----
 2 files changed, 4 insertions(+), 34 deletions(-)

diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index acce8366ea..6f9e09a587 100644
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -1009,36 +1009,6 @@ LLUUID::LLUUID()
 	return !(word[0] | word[1] | word[2] | word[3]);
 }
 
-// Copy constructor
- LLUUID::LLUUID(const LLUUID& rhs)
-{
-	U32 *tmp = (U32 *)mData;
-	U32 *rhstmp = (U32 *)rhs.mData;
-	tmp[0] = rhstmp[0];
-	tmp[1] = rhstmp[1];
-	tmp[2] = rhstmp[2];
-	tmp[3] = rhstmp[3];
-}
-
- LLUUID::~LLUUID()
-{
-}
-
-// Assignment
- LLUUID& LLUUID::operator=(const LLUUID& rhs)
-{
-	// No need to check the case where this==&rhs.  The branch is slower than the write.
-	U32 *tmp = (U32 *)mData;
-	U32 *rhstmp = (U32 *)rhs.mData;
-	tmp[0] = rhstmp[0];
-	tmp[1] = rhstmp[1];
-	tmp[2] = rhstmp[2];
-	tmp[3] = rhstmp[3];
-	
-	return *this;
-}
-
-
  LLUUID::LLUUID(const char *in_string)
 {
 	if (!in_string || in_string[0] == 0)
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index 86a396ab06..c139c4eb4e 100644
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -55,10 +55,7 @@ public:
 	LLUUID();
 	explicit LLUUID(const char *in_string); // Convert from string.
 	explicit LLUUID(const std::string& in_string); // Convert from string.
-	LLUUID(const LLUUID &in);
-	LLUUID &operator=(const LLUUID &rhs);
-
-	~LLUUID();
+	~LLUUID() = default;
 
 	//
 	// MANIPULATORS
@@ -131,6 +128,9 @@ public:
 
 	U8 mData[UUID_BYTES];
 };
+static_assert(std::is_trivially_copyable<LLUUID>::value, "LLUUID must be trivial copy");
+static_assert(std::is_trivially_move_assignable<LLUUID>::value, "LLUUID must be trivial move");
+static_assert(std::is_standard_layout<LLUUID>::value, "LLUUID must be a standard layout type");
 
 typedef std::vector<LLUUID> uuid_vec_t;
 typedef std::set<LLUUID> uuid_set_t;
-- 
cgit v1.2.3


From 7b3bb0f9c9fd2649365b17fdcd415e366cf57745 Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <maximnproductengine@lindenlab.com>
Date: Tue, 25 Oct 2022 17:16:57 +0300
Subject: SL-17991 update tooltip over Clear History button

---
 indra/newview/skins/default/xui/en/panel_preferences_privacy.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml
index 2ec5cef640..ef08fdf7c4 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml
@@ -19,7 +19,7 @@
       follows="left|top"
       height="23"
       label="Clear History"
-      tool_tip="Clear login image, last location, teleport history, web and texture cache"
+      tool_tip="Clear search and teleport history, web and texture cache"
       layout="topleft"
       left="30"
       name="clear_cache"
-- 
cgit v1.2.3


From 3ac77d73380ced1a9ff940053f448d2794fcc512 Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Tue, 25 Oct 2022 19:49:37 +0300
Subject: SL-18438 Mac build fix

---
 indra/llmessage/tests/llcoproceduremanager_test.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/llmessage/tests/llcoproceduremanager_test.cpp b/indra/llmessage/tests/llcoproceduremanager_test.cpp
index 6424117ef3..a1a4ce0520 100644
--- a/indra/llmessage/tests/llcoproceduremanager_test.cpp
+++ b/indra/llmessage/tests/llcoproceduremanager_test.cpp
@@ -92,7 +92,7 @@ namespace tut
         Sync sync;
         int foo = 0;
         LLCoprocedureManager::instance().initializePool("PoolName");
-        LLUUID queueId = LLCoprocedureManager::instance().enqueueCoprocedure("PoolName", "ProcName",
+        LLCoprocedureManager::instance().enqueueCoprocedure("PoolName", "ProcName",
             [&foo, &sync] (LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t & ptr, const LLUUID & id) {
                 sync.bump();
                 foo = 1;
-- 
cgit v1.2.3


From b2c5973fbd504039e1ef9d8fe6d853857e00fcad Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Thu, 27 Oct 2022 02:35:52 +0300
Subject: DRTVWR-570 Mac build fix

---
 indra/newview/llaccountingcostmanager.cpp | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp
index e09527a34b..d3f988d715 100644
--- a/indra/newview/llaccountingcostmanager.cpp
+++ b/indra/newview/llaccountingcostmanager.cpp
@@ -96,11 +96,7 @@ void LLAccountingCostManager::accountingCostCoro(std::string url,
         LLSD dataToPost = LLSD::emptyMap();
         dataToPost[keystr.c_str()] = objectList;
 
-        LLAccountingCostObserver* observer = observerHandle.get();
-        LLUUID transactionId = observer->getTransactionID();
-        observer = NULL;
-
-
+        LLAccountingCostObserver* observer = NULL;
 
         LLSD results = httpAdapter->postAndSuspend(httpRequest, url, dataToPost);
 
-- 
cgit v1.2.3


From 91f9f2e9f789d0418107ed5d428e8f0be3a060e2 Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Thu, 27 Oct 2022 23:08:09 +0300
Subject: DRTVWR-570 Mac build fix: unused variables cleanup

---
 indra/newview/llappearancemgr.cpp             | 2 +-
 indra/newview/llenvironment.cpp               | 1 -
 indra/newview/llfloatercreatelandmark.cpp     | 1 -
 indra/newview/llfloateroutfitphotopreview.cpp | 1 -
 indra/newview/llfloaterscriptlimits.cpp       | 1 -
 indra/newview/llimview.cpp                    | 6 ++----
 indra/newview/llinventoryfilter.cpp           | 1 -
 indra/newview/llinventoryfunctions.cpp        | 3 ---
 indra/newview/llinventorymodel.cpp            | 1 -
 indra/newview/lloutfitgallery.cpp             | 2 +-
 indra/newview/lloutfitslist.cpp               | 3 +--
 indra/newview/llpanellandmedia.cpp            | 1 -
 indra/newview/llpanelplaces.cpp               | 1 -
 indra/newview/llpanelprofile.cpp              | 1 -
 indra/newview/llspeakers.cpp                  | 1 -
 indra/newview/llviewermenu.cpp                | 1 -
 indra/newview/llviewertexteditor.cpp          | 1 -
 indra/newview/llviewerwindow.cpp              | 1 -
 indra/newview/llvovolume.cpp                  | 6 +-----
 19 files changed, 6 insertions(+), 29 deletions(-)

diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 909f32cd21..3c93a9df7e 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -3987,7 +3987,7 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo
 		// existence of AIS as an indicator the fix is present. Does
 		// not actually use AIS to create the category.
 		inventory_func_type func = boost::bind(&LLAppearanceMgr::onOutfitFolderCreated,this,_1,show_panel);
-		LLUUID folder_id = gInventory.createNewCategory(
+		gInventory.createNewCategory(
 			parent_id,
 			LLFolderType::FT_OUTFIT,
 			new_folder_name,
diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp
index 1300cf3658..9a23702c38 100644
--- a/indra/newview/llenvironment.cpp
+++ b/indra/newview/llenvironment.cpp
@@ -684,7 +684,6 @@ namespace
             if (!injection->mBlendIn)
                 mix = 1.0 - mix;
             stringset_t dummy;
-            LLUUID cloud_noise_id = getCloudNoiseTextureId();
             F64 value = this->mSettings[injection->mKeyName].asReal();
             if (this->getCloudNoiseTextureId().isNull())
             {
diff --git a/indra/newview/llfloatercreatelandmark.cpp b/indra/newview/llfloatercreatelandmark.cpp
index 7def855d83..b82d8a29ba 100644
--- a/indra/newview/llfloatercreatelandmark.cpp
+++ b/indra/newview/llfloatercreatelandmark.cpp
@@ -316,7 +316,6 @@ void LLFloaterCreateLandmark::onSaveClicked()
 	LLStringUtil::trim(current_title_value);
 	LLStringUtil::trim(current_notes_value);
 
-	LLUUID item_id = mItem->getUUID();
 	LLUUID folder_id = mFolderCombo->getValue().asUUID();
 	bool change_parent = folder_id != mItem->getParentUUID();
 
diff --git a/indra/newview/llfloateroutfitphotopreview.cpp b/indra/newview/llfloateroutfitphotopreview.cpp
index 6c39db730c..ade258aef7 100644
--- a/indra/newview/llfloateroutfitphotopreview.cpp
+++ b/indra/newview/llfloateroutfitphotopreview.cpp
@@ -234,7 +234,6 @@ void LLFloaterOutfitPhotoPreview::updateImageID()
 	if(item)
 	{
 		mImageID = item->getAssetUUID();
-		LLPermissions perm(item->getPermissions());
 	}
 	else
 	{
diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp
index 3746b9b6c2..40fe11b309 100644
--- a/indra/newview/llfloaterscriptlimits.cpp
+++ b/indra/newview/llfloaterscriptlimits.cpp
@@ -421,7 +421,6 @@ void LLPanelScriptLimitsRegionMemory::setRegionDetails(LLSD content)
 	for(S32 i = 0; i < number_parcels; i++)
 	{
 		std::string parcel_name = content["parcels"][i]["name"].asString();
-		LLUUID parcel_id = content["parcels"][i]["id"].asUUID();
 		S32 number_objects = content["parcels"][i]["objects"].size();
 
 		S32 local_id = 0;
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 4d6ebf9cbb..afd68a6a38 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -2114,8 +2114,6 @@ void LLOutgoingCallDialog::show(const LLSD& key)
 
 	std::string callee_name = mPayload["session_name"].asString();
 
-	LLUUID session_id = mPayload["session_id"].asUUID();
-
 	if (callee_name == "anonymous") // obsolete? Likely was part of avaline support
 	{
 		callee_name = getString("anonymous");
@@ -2499,7 +2497,7 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload
 				}
 			}
 			
-			LLUUID new_session_id = gIMMgr->addSession(correct_session_name, type, session_id, true);
+			gIMMgr->addSession(correct_session_name, type, session_id, true);
 
 			std::string url = gAgent.getRegion()->getCapability(
 				"ChatSessionRequest");
@@ -2585,7 +2583,7 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response)
 			}
 			else
 			{
-				LLUUID new_session_id = gIMMgr->addSession(
+				gIMMgr->addSession(
 					payload["session_name"].asString(),
 					type,
 					session_id, true);
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index 707ff2b7b6..e3a6b2dc85 100644
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -437,7 +437,6 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLFolderViewModelItemInvent
 bool LLInventoryFilter::checkAgainstFilterType(const LLInventoryItem* item) const
 {
 	LLInventoryType::EType object_type = item->getInventoryType();
-	const LLUUID object_id = item->getUUID();
 
 	const U32 filterTypes = mFilterOps.mFilterTypes;
 
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 27edc8148e..2c8d372eff 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -1403,9 +1403,6 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol
                 LLNotificationsUtil::add("MerchantPasteFailed", subs);
                 return false;
             }
-            
-            // Get the parent folder of the moved item : we may have to update it
-            LLUUID src_folder = viewer_inv_item->getParentUUID();
 
             if (copy)
             {
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 06351dc540..87fd91b23a 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -2711,7 +2711,6 @@ void LLInventoryModel::buildParentChildMap()
 			// some accounts has pbroken inventory root folders
 			
 			std::string name = "My Inventory";
-			LLUUID prev_root_id = mRootFolderID;
 			for (parent_cat_map_t::const_iterator it = mParentChildCategoryTree.begin(),
 					 it_end = mParentChildCategoryTree.end(); it != it_end; ++it)
 			{
diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp
index f419e2e06d..5d139ff75f 100644
--- a/indra/newview/lloutfitgallery.cpp
+++ b/indra/newview/lloutfitgallery.cpp
@@ -1226,7 +1226,7 @@ void LLOutfitGallery::uploadOutfitImage(const std::vector<std::string>& filename
         checkRemovePhoto(outfit_id);
         std::string upload_pending_name = outfit_id.asString();
         std::string upload_pending_desc = "";
-        LLUUID photo_id = upload_new_resource(filename, // file
+        upload_new_resource(filename, // file
             upload_pending_name,
             upload_pending_desc,
             0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 7270580032..4171fd8822 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -823,8 +823,7 @@ void LLOutfitListBase::onOpen(const LLSD& info)
         mCategoriesObserver->addCategory(outfits,
             boost::bind(&LLOutfitListBase::refreshList, this, outfits));
 
-        const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
-
+        //const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
         // Start observing changes in Current Outfit category.
         //mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this));
 
diff --git a/indra/newview/llpanellandmedia.cpp b/indra/newview/llpanellandmedia.cpp
index 26cd3ff1c1..e379d67e37 100644
--- a/indra/newview/llpanellandmedia.cpp
+++ b/indra/newview/llpanellandmedia.cpp
@@ -179,7 +179,6 @@ void LLPanelLandMedia::refresh()
 		// enable/disable for text label for completeness
 		mMediaSizeCtrlLabel->setEnabled( can_change_media && allow_resize );
 
-		LLUUID tmp = parcel->getMediaID();
 		mMediaTextureCtrl->setImageAssetID ( parcel->getMediaID() );
 		mMediaTextureCtrl->setEnabled( can_change_media );
 
diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp
index 74ec576554..0f00231643 100644
--- a/indra/newview/llpanelplaces.cpp
+++ b/indra/newview/llpanelplaces.cpp
@@ -800,7 +800,6 @@ void LLPanelPlaces::onSaveButtonClicked()
 	LLStringUtil::trim(current_title_value);
 	LLStringUtil::trim(current_notes_value);
 
-	LLUUID item_id = mItem->getUUID();
 	LLUUID folder_id = mLandmarkInfo->getLandmarkFolder();
 	bool change_parent = folder_id != mItem->getParentUUID();
 
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index deebf0cd1b..708ff26ced 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -1055,7 +1055,6 @@ void LLPanelProfileSecondLife::resetData()
 
 void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data)
 {
-    LLUUID avatar_id = getAvatarId();
     const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId());
     if ((relationship != NULL || gAgent.isGodlike()) && !getSelfProfile())
     {
diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp
index ea671a130e..60bada8f58 100644
--- a/indra/newview/llspeakers.cpp
+++ b/indra/newview/llspeakers.cpp
@@ -982,7 +982,6 @@ void LLActiveSpeakerMgr::updateSpeakerList()
 	// clean up text only speakers
 	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
 	{
-		LLUUID speaker_id = speaker_it->first;
 		LLSpeaker* speakerp = speaker_it->second;
 		if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
 		{
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 3bff01625b..01e4734b3c 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -7651,7 +7651,6 @@ void handle_selected_texture_info(void*)
    		map_t::iterator it;
    		for (it = faces_per_texture.begin(); it != faces_per_texture.end(); ++it)
    		{
-   			LLUUID image_id = it->first;
    			U8 te = it->second[0];
    			LLViewerTexture* img = node->getObject()->getTEImage(te);
    			S32 height = img->getHeight();
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
index e2de7ac825..7abb42dd8a 100644
--- a/indra/newview/llviewertexteditor.cpp
+++ b/indra/newview/llviewertexteditor.cpp
@@ -1252,7 +1252,6 @@ bool LLViewerTextEditor::onCopyToInvDialog(const LLSD& notification, const LLSD&
 	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 	if( 0 == option )
 	{
-		LLUUID item_id = notification["payload"]["item_id"].asUUID();
 		llwchar wc = llwchar(notification["payload"]["item_wc"].asInteger());
 		LLInventoryItem* itemp = LLEmbeddedItems::getEmbeddedItemPtr(wc);
 		if (itemp)
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 588fb4eb1b..f4d8eb6035 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -1307,7 +1307,6 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi
                                                           TRUE /* pick_transparent */, 
                                                           FALSE /* pick_rigged */);
 
-					LLUUID object_id = pick_info.getObjectID();
 					S32 object_face = pick_info.mObjectFace;
 					std::string url = data;
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index d98a40d188..2e7ccc8334 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -856,10 +856,7 @@ void LLVOVolume::updateTextureVirtualSize(bool forced)
 	
 	if (isSculpted())
 	{
-		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
-		LLUUID id =  sculpt_params->getSculptTexture();
-		
-		updateSculptTexture();
+        updateSculptTexture();
 		
 		
 
@@ -1109,7 +1106,6 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
 				if (!getVolume()->isMeshAssetLoaded())
 				{ 
 					//load request not yet issued, request pipeline load this mesh
-					LLUUID asset_id = volume_params.getSculptID();
 					S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod);
 					if (available_lod != lod)
 					{
-- 
cgit v1.2.3


From fabfbb93e666326a9daad7c14e690a23e4567d23 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 28 Oct 2022 15:45:25 -0400
Subject: Harden LLDataPackerBinaryBuffer from performing invalid memcpy in
 case buffer is too small

---
 indra/llmessage/lldatapacker.cpp | 258 +++++++++++++++++++++++----------------
 1 file changed, 156 insertions(+), 102 deletions(-)

diff --git a/indra/llmessage/lldatapacker.cpp b/indra/llmessage/lldatapacker.cpp
index b6adc102d2..9f7768f78e 100644
--- a/indra/llmessage/lldatapacker.cpp
+++ b/indra/llmessage/lldatapacker.cpp
@@ -116,9 +116,8 @@ BOOL LLDataPacker::packFixed(const F32 value, const char *name,
 BOOL LLDataPacker::unpackFixed(F32 &value, const char *name,
 							   const BOOL is_signed, const U32 int_bits, const U32 frac_bits)
 {
-	//BOOL success = TRUE;
+	BOOL success = TRUE;
 	//LL_INFOS() << "unpackFixed:" << name << " int:" << int_bits << " frac:" << frac_bits << LL_ENDL;
-	BOOL ok = FALSE;
 	S32 unsigned_bits = int_bits + frac_bits;
 	S32 total_bits = unsigned_bits;
 
@@ -134,19 +133,19 @@ BOOL LLDataPacker::unpackFixed(F32 &value, const char *name,
 	if (total_bits <= 8)
 	{
 		U8 fixed_8;
-		ok = unpackU8(fixed_8, name);
+		success = unpackU8(fixed_8, name);
 		fixed_val = (F32)fixed_8;
 	}
 	else if (total_bits <= 16)
 	{
 		U16 fixed_16;
-		ok = unpackU16(fixed_16, name);
+		success = unpackU16(fixed_16, name);
 		fixed_val = (F32)fixed_16;
 	}
 	else if (total_bits <= 31)
 	{
 		U32 fixed_32;
-		ok = unpackU32(fixed_32, name);
+		success = unpackU32(fixed_32, name);
 		fixed_val = (F32)fixed_32;
 	}
 	else
@@ -164,7 +163,7 @@ BOOL LLDataPacker::unpackFixed(F32 &value, const char *name,
 	}
 	value = fixed_val;
 	//LL_INFOS() << "Value: " << value << LL_ENDL;
-	return ok;
+	return success;
 }
 
 BOOL LLDataPacker::unpackU16s(U16 *values, S32 count, const char *name)
@@ -238,37 +237,43 @@ BOOL LLDataPacker::unpackUUIDs(LLUUID *values, S32 count, const char *name)
 
 BOOL LLDataPackerBinaryBuffer::packString(const std::string& value, const char *name)
 {
-	BOOL success = TRUE;
 	S32 length = value.length()+1;
 
-	success &= verifyLength(length, name);
+	if (!verifyLength(length, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{
 		htolememcpy(mCurBufferp, value.c_str(), MVT_VARIABLE, length);  
 	}
 	mCurBufferp += length;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackString(std::string& value, const char *name)
 {
-	BOOL success = TRUE;
 	S32 length = (S32)strlen((char *)mCurBufferp) + 1; /*Flawfinder: ignore*/
 
-	success &= verifyLength(length, name);
+	if (!verifyLength(length, name))
+	{
+		return FALSE;
+	}
 
 	value = std::string((char*)mCurBufferp); // We already assume NULL termination calling strlen()
 	
 	mCurBufferp += length;
-	return success;
+	return TRUE;
 }
 
 BOOL LLDataPackerBinaryBuffer::packBinaryData(const U8 *value, S32 size, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(size + 4, name);
+	if (!verifyLength(size + 4, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
@@ -280,102 +285,117 @@ BOOL LLDataPackerBinaryBuffer::packBinaryData(const U8 *value, S32 size, const c
 		htolememcpy(mCurBufferp, value, MVT_VARIABLE, size);  
 	}
 	mCurBufferp += size;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackBinaryData(U8 *value, S32 &size, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(4, name);
-	htolememcpy(&size, mCurBufferp, MVT_S32, 4);
-	mCurBufferp += 4;
-	success &= verifyLength(size, name);
-	if (success)
+	if (!verifyLength(4, name))
 	{
-		htolememcpy(value, mCurBufferp, MVT_VARIABLE, size);
-		mCurBufferp += size;
+		LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << LL_ENDL;
+		return FALSE;
 	}
-	else
+
+	htolememcpy(&size, mCurBufferp, MVT_S32, 4);
+	mCurBufferp += 4;
+
+	if (!verifyLength(size, name))
 	{
 		LL_WARNS() << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << LL_ENDL;
-		success = FALSE;
+		return FALSE;
 	}
-	return success;
+
+	htolememcpy(value, mCurBufferp, MVT_VARIABLE, size);
+	mCurBufferp += size;
+
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(size, name);
+	if (!verifyLength(size, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, value, MVT_VARIABLE, size);  
 	}
 	mCurBufferp += size;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(size, name);
+	if (!verifyLength(size, name))
+	{
+		return FALSE;
+	}
 	htolememcpy(value, mCurBufferp, MVT_VARIABLE, size);
 	mCurBufferp += size;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packU8(const U8 value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(U8), name);
+	if (!verifyLength(sizeof(U8), name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{
 		*mCurBufferp = value;
 	}
 	mCurBufferp++;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackU8(U8 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(U8), name);
+	if (!verifyLength(sizeof(U8), name))
+	{
+		return FALSE;
+	}
 
 	value = *mCurBufferp;
 	mCurBufferp++;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packU16(const U16 value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(U16), name);
+	if (!verifyLength(sizeof(U16), name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, &value, MVT_U16, 2);  
 	}
 	mCurBufferp += 2;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackU16(U16 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(U16), name);
+	if (!verifyLength(sizeof(U16), name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(&value, mCurBufferp, MVT_U16, 2);
 	mCurBufferp += 2;
-	return success;
+	return TRUE;
 }
 
 BOOL LLDataPackerBinaryBuffer::packS16(const S16 value, const char *name)
@@ -404,134 +424,156 @@ BOOL LLDataPackerBinaryBuffer::unpackS16(S16 &value, const char *name)
 
 BOOL LLDataPackerBinaryBuffer::packU32(const U32 value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(U32), name);
+	if (!verifyLength(sizeof(U32), name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, &value, MVT_U32, 4);  
 	}
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackU32(U32 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(U32), name);
+	if (!verifyLength(sizeof(U32), name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(&value, mCurBufferp, MVT_U32, 4);
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packS32(const S32 value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(S32), name);
+	if (!verifyLength(sizeof(S32), name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, &value, MVT_S32, 4); 
 	}
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackS32(S32 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(S32), name);
+	if(!verifyLength(sizeof(S32), name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(&value, mCurBufferp, MVT_S32, 4);
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packF32(const F32 value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(F32), name);
+	if (!verifyLength(sizeof(F32), name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, &value, MVT_F32, 4); 
 	}
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackF32(F32 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(sizeof(F32), name);
+	if (!verifyLength(sizeof(F32), name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(&value, mCurBufferp, MVT_F32, 4);
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packColor4(const LLColor4 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(16, name);
+	if (!verifyLength(16, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16); 
 	}
 	mCurBufferp += 16;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackColor4(LLColor4 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(16, name);
+	if (!verifyLength(16, name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
 	mCurBufferp += 16;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packColor4U(const LLColor4U &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(4, name);
+	if (!verifyLength(4, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, value.mV, MVT_VARIABLE, 4);  
 	}
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackColor4U(LLColor4U &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(4, name);
+	if (!verifyLength(4, name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(value.mV, mCurBufferp, MVT_VARIABLE, 4);
 	mCurBufferp += 4;
-	return success;
+	return TRUE;
 }
 
 
 
 BOOL LLDataPackerBinaryBuffer::packVector2(const LLVector2 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(8, name);
+	if (!verifyLength(8, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
@@ -539,92 +581,106 @@ BOOL LLDataPackerBinaryBuffer::packVector2(const LLVector2 &value, const char *n
 		htolememcpy(mCurBufferp+4, &value.mV[1], MVT_F32, 4);  
 	}
 	mCurBufferp += 8;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackVector2(LLVector2 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(8, name);
+	if (!verifyLength(8, name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(&value.mV[0], mCurBufferp, MVT_F32, 4);
 	htolememcpy(&value.mV[1], mCurBufferp+4, MVT_F32, 4);
 	mCurBufferp += 8;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::packVector3(const LLVector3 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(12, name);
+	if (!verifyLength(12, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, value.mV, MVT_LLVector3, 12);  
 	}
 	mCurBufferp += 12;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackVector3(LLVector3 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(12, name);
+	if (!verifyLength(12, name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(value.mV, mCurBufferp, MVT_LLVector3, 12);
 	mCurBufferp += 12;
-	return success;
+	return TRUE;
 }
 
 BOOL LLDataPackerBinaryBuffer::packVector4(const LLVector4 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(16, name);
+	if (!verifyLength(16, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, value.mV, MVT_LLVector4, 16);  
 	}
 	mCurBufferp += 16;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackVector4(LLVector4 &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(16, name);
+	if (!verifyLength(16, name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
 	mCurBufferp += 16;
-	return success;
+	return TRUE;
 }
 
 BOOL LLDataPackerBinaryBuffer::packUUID(const LLUUID &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(16, name);
+	if (!verifyLength(16, name))
+	{
+		return FALSE;
+	}
 
 	if (mWriteEnabled) 
 	{ 
 		htolememcpy(mCurBufferp, value.mData, MVT_LLUUID, 16);  
 	}
 	mCurBufferp += 16;
-	return success;
+	return TRUE;
 }
 
 
 BOOL LLDataPackerBinaryBuffer::unpackUUID(LLUUID &value, const char *name)
 {
-	BOOL success = TRUE;
-	success &= verifyLength(16, name);
+	if (!verifyLength(16, name))
+	{
+		return FALSE;
+	}
 
 	htolememcpy(value.mData, mCurBufferp, MVT_LLUUID, 16);
 	mCurBufferp += 16;
-	return success;
+	return TRUE;
 }
 
 const LLDataPackerBinaryBuffer&	LLDataPackerBinaryBuffer::operator=(const LLDataPackerBinaryBuffer &a)
@@ -698,15 +754,13 @@ BOOL LLDataPackerAsciiBuffer::packString(const std::string& value, const char *n
 
 BOOL LLDataPackerAsciiBuffer::unpackString(std::string& value, const char *name)
 {
-	BOOL success = TRUE;
 	char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
-	BOOL res = getValueStr(name, valuestr, DP_BUFSIZE); // NULL terminated
-	if (!res) // 
+	if (!getValueStr(name, valuestr, DP_BUFSIZE))  // NULL terminated
 	{
 		return FALSE;
 	}
 	value = valuestr;
-	return success;
+	return TRUE;
 }
 
 
-- 
cgit v1.2.3


From 129301c8e9d48e29466597f6baee6d3566fac9dc Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 28 Oct 2022 15:46:42 -0400
Subject: Fix multiple leaks in the case of failure to deserialize animations

---
 indra/llcharacter/llkeyframemotion.cpp | 185 +++++++++++++++++----------------
 1 file changed, 97 insertions(+), 88 deletions(-)

diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
index ebf7454a61..403d5bcf49 100644
--- a/indra/llcharacter/llkeyframemotion.cpp
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -1229,7 +1229,7 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8
 BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, bool allow_invalid_joints)
 {
 	BOOL old_version = FALSE;
-	mJointMotionList = new LLKeyframeMotion::JointMotionList;
+	std::unique_ptr<LLKeyframeMotion::JointMotionList> joint_motion_list(new LLKeyframeMotion::JointMotionList);
 
 	//-------------------------------------------------------------------------
 	// get base priority
@@ -1272,16 +1272,16 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
                    << " for animation " << asset_id << LL_ENDL;
 		return FALSE;
 	}
-	mJointMotionList->mBasePriority = (LLJoint::JointPriority) temp_priority;
+	joint_motion_list->mBasePriority = (LLJoint::JointPriority) temp_priority;
 
-	if (mJointMotionList->mBasePriority >= LLJoint::ADDITIVE_PRIORITY)
+	if (joint_motion_list->mBasePriority >= LLJoint::ADDITIVE_PRIORITY)
 	{
-		mJointMotionList->mBasePriority = (LLJoint::JointPriority)((S32)LLJoint::ADDITIVE_PRIORITY-1);
-		mJointMotionList->mMaxPriority = mJointMotionList->mBasePriority;
+		joint_motion_list->mBasePriority = (LLJoint::JointPriority)((S32)LLJoint::ADDITIVE_PRIORITY-1);
+		joint_motion_list->mMaxPriority = joint_motion_list->mBasePriority;
 	}
-	else if (mJointMotionList->mBasePriority < LLJoint::USE_MOTION_PRIORITY)
+	else if (joint_motion_list->mBasePriority < LLJoint::USE_MOTION_PRIORITY)
 	{
-		LL_WARNS() << "bad animation base_priority " << mJointMotionList->mBasePriority
+		LL_WARNS() << "bad animation base_priority " << joint_motion_list->mBasePriority
                    << " for animation " << asset_id << LL_ENDL;
 		return FALSE;
 	}
@@ -1289,15 +1289,15 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 	//-------------------------------------------------------------------------
 	// get duration
 	//-------------------------------------------------------------------------
-	if (!dp.unpackF32(mJointMotionList->mDuration, "duration"))
+	if (!dp.unpackF32(joint_motion_list->mDuration, "duration"))
 	{
 		LL_WARNS() << "can't read duration"
                    << " for animation " << asset_id << LL_ENDL;
 		return FALSE;
 	}
 	
-	if (mJointMotionList->mDuration > MAX_ANIM_DURATION ||
-	    !llfinite(mJointMotionList->mDuration))
+	if (joint_motion_list->mDuration > MAX_ANIM_DURATION ||
+	    !llfinite(joint_motion_list->mDuration))
 	{
 		LL_WARNS() << "invalid animation duration"
                    << " for animation " << asset_id << LL_ENDL;
@@ -1307,14 +1307,14 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 	//-------------------------------------------------------------------------
 	// get emote (optional)
 	//-------------------------------------------------------------------------
-	if (!dp.unpackString(mJointMotionList->mEmoteName, "emote_name"))
+	if (!dp.unpackString(joint_motion_list->mEmoteName, "emote_name"))
 	{
 		LL_WARNS() << "can't read optional_emote_animation"
                    << " for animation " << asset_id << LL_ENDL;
 		return FALSE;
 	}
 
-	if(mJointMotionList->mEmoteName==mID.asString())
+	if(joint_motion_list->mEmoteName==mID.asString())
 	{
 		LL_WARNS() << "Malformed animation mEmoteName==mID"
                    << " for animation " << asset_id << LL_ENDL;
@@ -1324,23 +1324,23 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 	//-------------------------------------------------------------------------
 	// get loop
 	//-------------------------------------------------------------------------
-	if (!dp.unpackF32(mJointMotionList->mLoopInPoint, "loop_in_point") ||
-	    !llfinite(mJointMotionList->mLoopInPoint))
+	if (!dp.unpackF32(joint_motion_list->mLoopInPoint, "loop_in_point") ||
+	    !llfinite(joint_motion_list->mLoopInPoint))
 	{
 		LL_WARNS() << "can't read loop point"
                    << " for animation " << asset_id << LL_ENDL;
 		return FALSE;
 	}
 
-	if (!dp.unpackF32(mJointMotionList->mLoopOutPoint, "loop_out_point") ||
-	    !llfinite(mJointMotionList->mLoopOutPoint))
+	if (!dp.unpackF32(joint_motion_list->mLoopOutPoint, "loop_out_point") ||
+	    !llfinite(joint_motion_list->mLoopOutPoint))
 	{
 		LL_WARNS() << "can't read loop point"
                    << " for animation " << asset_id << LL_ENDL;
 		return FALSE;
 	}
 
-	if (!dp.unpackS32(mJointMotionList->mLoop, "loop"))
+	if (!dp.unpackS32(joint_motion_list->mLoop, "loop"))
 	{
 		LL_WARNS() << "can't read loop"
                    << " for animation " << asset_id << LL_ENDL;
@@ -1353,22 +1353,22 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 	if (female_land_anim == asset_id || formal_female_land_anim == asset_id)
 	{
 		LL_WARNS() << "Animation(" << asset_id << ") won't be looped." << LL_ENDL;
-		mJointMotionList->mLoop = FALSE;
+		joint_motion_list->mLoop = FALSE;
 	}
 
 	//-------------------------------------------------------------------------
 	// get easeIn and easeOut
 	//-------------------------------------------------------------------------
-	if (!dp.unpackF32(mJointMotionList->mEaseInDuration, "ease_in_duration") ||
-	    !llfinite(mJointMotionList->mEaseInDuration))
+	if (!dp.unpackF32(joint_motion_list->mEaseInDuration, "ease_in_duration") ||
+	    !llfinite(joint_motion_list->mEaseInDuration))
 	{
 		LL_WARNS() << "can't read easeIn"
                    << " for animation " << asset_id << LL_ENDL;
 		return FALSE;
 	}
 
-	if (!dp.unpackF32(mJointMotionList->mEaseOutDuration, "ease_out_duration") ||
-	    !llfinite(mJointMotionList->mEaseOutDuration))
+	if (!dp.unpackF32(joint_motion_list->mEaseOutDuration, "ease_out_duration") ||
+	    !llfinite(joint_motion_list->mEaseOutDuration))
 	{
 		LL_WARNS() << "can't read easeOut"
                    << " for animation " << asset_id << LL_ENDL;
@@ -1393,7 +1393,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 		return FALSE;
 	}
 	
-	mJointMotionList->mHandPose = (LLHandMotion::eHandPose)word;
+	joint_motion_list->mHandPose = (LLHandMotion::eHandPose)word;
 
 	//-------------------------------------------------------------------------
 	// get number of joint motions
@@ -1419,8 +1419,8 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 		return FALSE;
 	}
 
-	mJointMotionList->mJointMotionArray.clear();
-	mJointMotionList->mJointMotionArray.reserve(num_motions);
+	joint_motion_list->mJointMotionArray.clear();
+	joint_motion_list->mJointMotionArray.reserve(num_motions);
 	mJointStates.clear();
 	mJointStates.reserve(num_motions);
 
@@ -1431,7 +1431,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 	for(U32 i=0; i<num_motions; ++i)
 	{
 		JointMotion* joint_motion = new JointMotion;		
-		mJointMotionList->mJointMotionArray.push_back(joint_motion);
+		joint_motion_list->mJointMotionArray.push_back(joint_motion);
 		
 		std::string joint_name;
 		if (!dp.unpackString(joint_name, "joint_name"))
@@ -1503,9 +1503,9 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 		
 		joint_motion->mPriority = (LLJoint::JointPriority)joint_priority;
 		if (joint_priority != LLJoint::USE_MOTION_PRIORITY &&
-		    joint_priority > mJointMotionList->mMaxPriority)
+		    joint_priority > joint_motion_list->mMaxPriority)
 		{
-			mJointMotionList->mMaxPriority = (LLJoint::JointPriority)joint_priority;
+			joint_motion_list->mMaxPriority = (LLJoint::JointPriority)joint_priority;
 		}
 
 		joint_state->setPriority((LLJoint::JointPriority)joint_priority);
@@ -1556,9 +1556,9 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 					return FALSE;
 				}
 
-				time = U16_to_F32(time_short, 0.f, mJointMotionList->mDuration);
+				time = U16_to_F32(time_short, 0.f, joint_motion_list->mDuration);
 				
-				if (time < 0 || time > mJointMotionList->mDuration)
+				if (time < 0 || time > joint_motion_list->mDuration)
 				{
 					LL_WARNS() << "invalid frame time"
                                << " for animation " << asset_id << LL_ENDL;
@@ -1571,38 +1571,57 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			LLVector3 rot_angles;
 			U16 x, y, z;
 
-			BOOL success = TRUE;
-
 			if (old_version)
 			{
-				success = dp.unpackVector3(rot_angles, "rot_angles") && rot_angles.isFinite();
+				if (!dp.unpackVector3(rot_angles, "rot_angles"))
+				{
+					LL_WARNS() << "can't read rot_angles in rotation key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
+				if (!rot_angles.isFinite())
+				{
+					LL_WARNS() << "non-finite angle in rotation key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
 
 				LLQuaternion::Order ro = StringToOrder("ZYX");
 				rot_key.mRotation = mayaQ(rot_angles.mV[VX], rot_angles.mV[VY], rot_angles.mV[VZ], ro);
 			}
 			else
 			{
-				success &= dp.unpackU16(x, "rot_angle_x");
-				success &= dp.unpackU16(y, "rot_angle_y");
-				success &= dp.unpackU16(z, "rot_angle_z");
+				if (!dp.unpackU16(x, "rot_angle_x"))
+				{
+					LL_WARNS() << "can't read rot_angle_x in rotation key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
+				if (!dp.unpackU16(y, "rot_angle_y"))
+				{
+					LL_WARNS() << "can't read rot_angle_y in rotation key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
+				if (!dp.unpackU16(z, "rot_angle_z"))
+				{
+					LL_WARNS() << "can't read rot_angle_z in rotation key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
 
 				LLVector3 rot_vec;
 				rot_vec.mV[VX] = U16_to_F32(x, -1.f, 1.f);
 				rot_vec.mV[VY] = U16_to_F32(y, -1.f, 1.f);
 				rot_vec.mV[VZ] = U16_to_F32(z, -1.f, 1.f);
+
+				if(!rot_vec.isFinite())
+				{
+					LL_WARNS() << "non-finite angle in rotation key (" << k << ")"
+						<< " for animation " << asset_id << LL_ENDL;
+					return FALSE;
+				}				
 				rot_key.mRotation.unpackFromVector3(rot_vec);
 			}
 
-			if( !(rot_key.mRotation.isFinite()) )
-			{
-				LL_WARNS() << "non-finite angle in rotation key"
-                           << " for animation " << asset_id << LL_ENDL;
-				success = FALSE;
-			}
-			
-			if (!success)
+			if(!rot_key.mRotation.isFinite())
 			{
-				LL_WARNS() << "can't read rotation key (" << k << ")"
+				LL_WARNS() << "non-finite angle in rotation key (" << k << ")"
                            << " for animation " << asset_id << LL_ENDL;
 				return FALSE;
 			}
@@ -1655,14 +1674,16 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 					return FALSE;
 				}
 
-				pos_key.mTime = U16_to_F32(time_short, 0.f, mJointMotionList->mDuration);
+				pos_key.mTime = U16_to_F32(time_short, 0.f, joint_motion_list->mDuration);
 			}
 
-			BOOL success = TRUE;
-
 			if (old_version)
 			{
-				success = dp.unpackVector3(pos_key.mPosition, "pos");
+				if (!dp.unpackVector3(pos_key.mPosition, "pos"))
+				{
+					LL_WARNS() << "can't read pos in position key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
                 
                 //MAINT-6162
                 pos_key.mPosition.mV[VX] = llclamp( pos_key.mPosition.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
@@ -1674,25 +1695,30 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				U16 x, y, z;
 
-				success &= dp.unpackU16(x, "pos_x");
-				success &= dp.unpackU16(y, "pos_y");
-				success &= dp.unpackU16(z, "pos_z");
+				if (!dp.unpackU16(x, "pos_x"))
+				{
+					LL_WARNS() << "can't read pos_x in position key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
+				if (!dp.unpackU16(y, "pos_y"))
+				{
+					LL_WARNS() << "can't read pos_y in position key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
+				if (!dp.unpackU16(z, "pos_z"))
+				{
+					LL_WARNS() << "can't read pos_z in position key (" << k << ")" << LL_ENDL;
+					return FALSE;
+				}
 
 				pos_key.mPosition.mV[VX] = U16_to_F32(x, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
 				pos_key.mPosition.mV[VY] = U16_to_F32(y, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
 				pos_key.mPosition.mV[VZ] = U16_to_F32(z, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
 			}
 			
-			if( !(pos_key.mPosition.isFinite()) )
+			if(!pos_key.mPosition.isFinite())
 			{
 				LL_WARNS() << "non-finite position in key"
-                           << " for animation " << asset_id << LL_ENDL;
-				success = FALSE;
-			}
-			
-			if (!success)
-			{
-				LL_WARNS() << "can't read position key (" << k << ")"
                            << " for animation " << asset_id << LL_ENDL;
 				return FALSE;
 			}
@@ -1701,7 +1727,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 
 			if (is_pelvis)
 			{
-				mJointMotionList->mPelvisBBox.addPoint(pos_key.mPosition);
+				joint_motion_list->mPelvisBBox.addPoint(pos_key.mPosition);
 			}
 		}
 
@@ -1733,23 +1759,21 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 		for(S32 i = 0; i < num_constraints; ++i)
 		{
 			// read in constraint data
-			JointConstraintSharedData* constraintp = new JointConstraintSharedData;
+			std::unique_ptr<JointConstraintSharedData> constraintp(new JointConstraintSharedData);
 			U8 byte = 0;
 
 			if (!dp.unpackU8(byte, "chain_length"))
 			{
 				LL_WARNS() << "can't read constraint chain length"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 			constraintp->mChainLength = (S32) byte;
 
-			if((U32)constraintp->mChainLength > mJointMotionList->getNumJointMotions())
+			if((U32)constraintp->mChainLength > joint_motion_list->getNumJointMotions())
 			{
 				LL_WARNS() << "invalid constraint chain length"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1757,7 +1781,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint type"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 			
@@ -1765,7 +1788,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "invalid constraint type"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 			constraintp->mConstraintType = (EConstraintType)byte;
@@ -1776,7 +1798,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read source volume name"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1787,7 +1808,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "not a valid source constraint volume " << str
 						   << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1795,7 +1815,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint source offset"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 			
@@ -1803,7 +1822,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "non-finite constraint source offset"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 			
@@ -1811,7 +1829,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read target volume name"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1830,7 +1847,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 				{
 					LL_WARNS() << "not a valid target constraint volume " << str
 							   << " for animation " << asset_id << LL_ENDL;
-					delete constraintp;
 					return FALSE;
 				}
 			}
@@ -1839,7 +1855,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint target offset"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1847,7 +1862,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "non-finite constraint target offset"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 			
@@ -1855,7 +1869,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint target direction"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1863,7 +1876,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "non-finite constraint target direction"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1877,7 +1889,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint ease in start time"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1885,7 +1896,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint ease in stop time"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1893,7 +1903,6 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint ease out start time"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
@@ -1901,33 +1910,31 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 			{
 				LL_WARNS() << "can't read constraint ease out stop time"
                            << " for animation " << asset_id << LL_ENDL;
-				delete constraintp;
 				return FALSE;
 			}
 
-			mJointMotionList->mConstraints.push_front(constraintp);
-
-			constraintp->mJointStateIndices = new S32[constraintp->mChainLength + 1]; // note: mChainLength is size-limited - comes from a byte
-			
 			LLJoint* joint = mCharacter->findCollisionVolume(constraintp->mSourceConstraintVolume);
 			// get joint to which this collision volume is attached
 			if (!joint)
 			{
 				return FALSE;
 			}
+
+			constraintp->mJointStateIndices = new S32[constraintp->mChainLength + 1]; // note: mChainLength is size-limited - comes from a byte
+
 			for (S32 i = 0; i < constraintp->mChainLength + 1; i++)
 			{
 				LLJoint* parent = joint->getParent();
 				if (!parent)
 				{
 					LL_WARNS() << "Joint with no parent: " << joint->getName()
-                               << " Emote: " << mJointMotionList->mEmoteName
+                               << " Emote: " << joint_motion_list->mEmoteName
                                << " for animation " << asset_id << LL_ENDL;
 					return FALSE;
 				}
 				joint = parent;
 				constraintp->mJointStateIndices[i] = -1;
-				for (U32 j = 0; j < mJointMotionList->getNumJointMotions(); j++)
+				for (U32 j = 0; j < joint_motion_list->getNumJointMotions(); j++)
 				{
 					LLJoint* constraint_joint = getJoint(j);
 					
@@ -1948,14 +1955,16 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo
 				{
 					LL_WARNS() << "No joint index for constraint " << i
                                << " for animation " << asset_id << LL_ENDL;
-					delete constraintp;
 					return FALSE;
 				}
 			}
+
+			joint_motion_list->mConstraints.push_front(constraintp.release());
 		}
 	}
 
 	// *FIX: support cleanup of old keyframe data
+    mJointMotionList = joint_motion_list.release(); // release from unique_ptr to member;
 	LLKeyframeDataCache::addKeyframeData(getID(),  mJointMotionList);
 	mAssetStatus = ASSET_LOADED;
 
-- 
cgit v1.2.3


From 3dd874a99b81b6c392f1ab4c40015ba43f7801e6 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 28 Oct 2022 18:47:32 -0400
Subject: Fix memory leaks in lldir objc/mac

---
 indra/llfilesystem/lldir_mac.cpp       | 32 ++++++-------
 indra/llfilesystem/lldir_utils_objc.h  | 10 ++---
 indra/llfilesystem/lldir_utils_objc.mm | 82 +++++++++++++++++-----------------
 3 files changed, 60 insertions(+), 64 deletions(-)

diff --git a/indra/llfilesystem/lldir_mac.cpp b/indra/llfilesystem/lldir_mac.cpp
index 3bc4ee844e..9ad8e274b6 100644
--- a/indra/llfilesystem/lldir_mac.cpp
+++ b/indra/llfilesystem/lldir_mac.cpp
@@ -66,16 +66,16 @@ LLDir_Mac::LLDir_Mac()
 
     const std::string     secondLifeString = "SecondLife";
     
-    std::string *executablepathstr = getSystemExecutableFolder();
+    std::string executablepathstr = getSystemExecutableFolder();
 
     //NOTE:  LLINFOS/LLERRS will not output to log here.  The streams are not initialized.
     
-	if (executablepathstr)
+	if (!executablepathstr.empty())
 	{
 		// mExecutablePathAndName
-		mExecutablePathAndName = *executablepathstr;
+		mExecutablePathAndName = executablepathstr;
         
-        boost::filesystem::path executablepath(*executablepathstr);
+        boost::filesystem::path executablepath(executablepathstr);
         
 # ifndef BOOST_SYSTEM_NO_DEPRECATED
 #endif
@@ -83,8 +83,8 @@ LLDir_Mac::LLDir_Mac()
         mExecutableDir = executablepath.parent_path().string();
 		
 		// mAppRODataDir
-        std::string *resourcepath = getSystemResourceFolder();
-        mAppRODataDir = *resourcepath;
+        std::string resourcepath = getSystemResourceFolder();
+        mAppRODataDir = resourcepath;
 		
 		// *NOTE: When running in a dev tree, use the copy of
 		// skins in indra/newview/ rather than in the application bundle.  This
@@ -110,11 +110,11 @@ LLDir_Mac::LLDir_Mac()
 		}
 		
 		// mOSUserDir
-        std::string *appdir = getSystemApplicationSupportFolder();
+        std::string appdir = getSystemApplicationSupportFolder();
         std::string rootdir;
 
         //Create root directory
-        if (CreateDirectory(*appdir, secondLifeString, &rootdir))
+        if (CreateDirectory(appdir, secondLifeString, &rootdir))
         {
             
             // Save the full path to the folder
@@ -128,12 +128,10 @@ LLDir_Mac::LLDir_Mac()
         }
     
 		//mOSCacheDir
-        std::string *cachedir =  getSystemCacheFolder();
-
-        if (cachedir)
-		
+        std::string cachedir =  getSystemCacheFolder();
+        if (!cachedir.empty())
 		{
-            mOSCacheDir = *cachedir;
+            mOSCacheDir = cachedir;
             //TODO:  This changes from ~/Library/Cache/Secondlife to ~/Library/Cache/com.app.secondlife/Secondlife.  Last dir level could go away.
             CreateDirectory(mOSCacheDir, secondLifeString, NULL);
 		}
@@ -143,12 +141,10 @@ LLDir_Mac::LLDir_Mac()
 		
 		// mTempDir
         //Aura 120920 boost::filesystem::temp_directory_path() not yet implemented on mac. :(
-        std::string *tmpdir = getSystemTempFolder();
-        if (tmpdir)
+        std::string tmpdir = getSystemTempFolder();
+        if (!tmpdir.empty())
         {
-            
-            CreateDirectory(*tmpdir, secondLifeString, &mTempDir);
-            if (tmpdir) delete tmpdir;
+            CreateDirectory(tmpdir, secondLifeString, &mTempDir);
         }
 		
 		mWorkingDir = getCurPath();
diff --git a/indra/llfilesystem/lldir_utils_objc.h b/indra/llfilesystem/lldir_utils_objc.h
index 12019c4284..59dbeb4aec 100644
--- a/indra/llfilesystem/lldir_utils_objc.h
+++ b/indra/llfilesystem/lldir_utils_objc.h
@@ -33,11 +33,11 @@
 
 #include <iostream>
 
-std::string* getSystemTempFolder();
-std::string* getSystemCacheFolder();
-std::string* getSystemApplicationSupportFolder();
-std::string* getSystemResourceFolder();
-std::string* getSystemExecutableFolder();
+std::string getSystemTempFolder();
+std::string getSystemCacheFolder();
+std::string getSystemApplicationSupportFolder();
+std::string getSystemResourceFolder();
+std::string getSystemExecutableFolder();
 
 
 #endif // LL_LLDIR_UTILS_OBJC_H
diff --git a/indra/llfilesystem/lldir_utils_objc.mm b/indra/llfilesystem/lldir_utils_objc.mm
index da55a2f897..20540fb93c 100644
--- a/indra/llfilesystem/lldir_utils_objc.mm
+++ b/indra/llfilesystem/lldir_utils_objc.mm
@@ -30,75 +30,75 @@
 #include "lldir_utils_objc.h"
 #import <Cocoa/Cocoa.h>
 
-std::string* getSystemTempFolder()
+std::string getSystemTempFolder()
 {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    NSString * tempDir = NSTemporaryDirectory();
-    if (tempDir == nil)
-        tempDir = @"/tmp";
-    std::string *result = ( new std::string([tempDir UTF8String]) );
-    [pool release];
+    std::string result;
+    @autoreleasepool {
+        NSString * tempDir = NSTemporaryDirectory();
+        if (tempDir == nil)
+            tempDir = @"/tmp";
+        result = std::string([tempDir UTF8String]);
+    }
     
     return result;
 }
 
 //findSystemDirectory scoped exclusively to this file. 
-std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
+std::string findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
                                    NSSearchPathDomainMask domainMask)
 {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    
-    std::string *result = nil;
-    NSString *path = nil;
-    
-    // Search for the path
-    NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory,
-                                                         domainMask,
-                                                         YES);
-    if ([paths count])
-    {
-        path = [paths objectAtIndex:0];
-        //HACK:  Always attempt to create directory, ignore errors.
-        NSError *error = nil;
-
-        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
-
+    std::string result;
+    @autoreleasepool {
+        NSString *path = nil;
         
-        result = new std::string([path UTF8String]);        
+        // Search for the path
+        NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory,
+                                                             domainMask,
+                                                             YES);
+        if ([paths count])
+        {
+            path = [paths objectAtIndex:0];
+            //HACK:  Always attempt to create directory, ignore errors.
+            NSError *error = nil;
+            
+            [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
+            
+            
+            result = std::string([path UTF8String]);
+        }
     }
-    [pool release];
     return result;
 }
 
-std::string* getSystemExecutableFolder()
+std::string getSystemExecutableFolder()
 {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-    NSString *bundlePath = [[NSBundle mainBundle] executablePath];
-    std::string *result = (new std::string([bundlePath UTF8String]));  
-    [pool release];
+    std::string result;
+    @autoreleasepool {
+        NSString *bundlePath = [[NSBundle mainBundle] executablePath];
+        result = std::string([bundlePath UTF8String]);
+    }
 
     return result;
 }
 
-std::string* getSystemResourceFolder()
+std::string getSystemResourceFolder()
 {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-    NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
-    std::string *result = (new std::string([bundlePath UTF8String]));
-    [pool release];
+    std::string result;
+    @autoreleasepool {
+        NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
+        result = std::string([bundlePath UTF8String]);
+    }
     
     return result;
 }
 
-std::string* getSystemCacheFolder()
+std::string getSystemCacheFolder()
 {
     return findSystemDirectory (NSCachesDirectory,
                                 NSUserDomainMask);
 }
 
-std::string* getSystemApplicationSupportFolder()
+std::string getSystemApplicationSupportFolder()
 {
     return findSystemDirectory (NSApplicationSupportDirectory,
                                 NSUserDomainMask);
-- 
cgit v1.2.3


From 662dd44587796072b81b47f202597569f4e8c48d Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 28 Oct 2022 18:49:32 -0400
Subject: Fix leaks in mac filepicker code

---
 indra/newview/llfilepicker.cpp    |  10 +--
 indra/newview/llfilepicker.h      |   2 +-
 indra/newview/llfilepicker_mac.h  |   4 +-
 indra/newview/llfilepicker_mac.mm | 157 +++++++++++++++++++-------------------
 4 files changed, 88 insertions(+), 85 deletions(-)

diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 3669fb1eeb..e3a695fc79 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -586,9 +586,9 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename,
 
 #elif LL_DARWIN
 
-std::vector<std::string>* LLFilePicker::navOpenFilterProc(ELoadFilter filter) //(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode)
+std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadFilter filter) //(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode)
 {
-    std::vector<std::string> *allowedv = new std::vector< std::string >;
+    std::unique_ptr<std::vector<std::string>> allowedv(new std::vector< std::string >);
     switch(filter)
     {
         case FFLOAD_ALL:
@@ -661,9 +661,9 @@ bool	LLFilePicker::doNavChooseDialog(ELoadFilter filter)
     
 	gViewerWindow->getWindow()->beforeDialog();
     
-    std::vector<std::string> *allowed_types=navOpenFilterProc(filter);
+    std::unique_ptr<std::vector<std::string>> allowed_types = navOpenFilterProc(filter);
     
-    std::vector<std::string> *filev  = doLoadDialog(allowed_types, 
+    std::unique_ptr<std::vector<std::string>> filev  = doLoadDialog(allowed_types.get(),
                                                     mPickOptions);
 
 	gViewerWindow->getWindow()->afterDialog();
@@ -780,7 +780,7 @@ bool	LLFilePicker::doNavSaveDialog(ESaveFilter filter, const std::string& filena
 	gViewerWindow->getWindow()->beforeDialog();
 
 	// Run the dialog
-    std::string* filev = doSaveDialog(&namestring, 
+    std::unique_ptr<std::string> filev = doSaveDialog(&namestring, 
                  &type,
                  &creator,
                  &extension,
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index 04ba4416d7..73baeca1c0 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -167,7 +167,7 @@ private:
 	
 	bool doNavChooseDialog(ELoadFilter filter);
 	bool doNavSaveDialog(ESaveFilter filter, const std::string& filename);
-    std::vector<std::string>* navOpenFilterProc(ELoadFilter filter);
+    std::unique_ptr<std::vector<std::string>> navOpenFilterProc(ELoadFilter filter);
 #endif
 
 #if LL_GTK
diff --git a/indra/newview/llfilepicker_mac.h b/indra/newview/llfilepicker_mac.h
index e0b7e2e8ce..b2fb371afe 100644
--- a/indra/newview/llfilepicker_mac.h
+++ b/indra/newview/llfilepicker_mac.h
@@ -39,9 +39,9 @@
 #include <vector>
 
 //void modelessPicker();
-std::vector<std::string>* doLoadDialog(const std::vector<std::string>* allowed_types, 
+std::unique_ptr<std::vector<std::string>> doLoadDialog(const std::vector<std::string>* allowed_types,
                  unsigned int flags);
-std::string* doSaveDialog(const std::string* file, 
+std::unique_ptr<std::string> doSaveDialog(const std::string* file, 
                   const std::string* type,
                   const std::string* creator,
                   const std::string* extension,
diff --git a/indra/newview/llfilepicker_mac.mm b/indra/newview/llfilepicker_mac.mm
index 1438e4dc0a..0ae5fc3f77 100644
--- a/indra/newview/llfilepicker_mac.mm
+++ b/indra/newview/llfilepicker_mac.mm
@@ -29,104 +29,107 @@
 #include <iostream>
 #include "llfilepicker_mac.h"
 
-std::vector<std::string>* doLoadDialog(const std::vector<std::string>* allowed_types, 
+std::unique_ptr<std::vector<std::string>> doLoadDialog(const std::vector<std::string>* allowed_types,
                  unsigned int flags)
 {
-    int i, result;
-    
-    //Aura TODO:  We could init a small window and release it at the end of this routine
-    //for a modeless interface.
-    
-    NSOpenPanel *panel = [NSOpenPanel openPanel];
-    //NSString *fileName = nil;
-    NSMutableArray *fileTypes = nil;
-    
-    
-    if ( allowed_types && !allowed_types->empty()) 
-    {
-        fileTypes = [[NSMutableArray alloc] init];
+    std::unique_ptr<std::vector<std::string>> outfiles;
+
+    @autoreleasepool {
+        int i, result;
+        //Aura TODO:  We could init a small window and release it at the end of this routine
+        //for a modeless interface.
+        
+        NSOpenPanel *panel = [NSOpenPanel openPanel];
+        //NSString *fileName = nil;
+        NSMutableArray *fileTypes = nil;
         
-        for (i=0;i<allowed_types->size();++i)
+        if ( allowed_types && !allowed_types->empty())
         {
-            [fileTypes addObject: 
-             [NSString stringWithCString:(*allowed_types)[i].c_str() 
-                                encoding:[NSString defaultCStringEncoding]]];
+            fileTypes = [[[NSMutableArray alloc] init] autorelease];
+            
+            for (i=0;i<allowed_types->size();++i)
+            {
+                [fileTypes addObject:
+                 [NSString stringWithCString:(*allowed_types)[i].c_str()
+                                    encoding:[NSString defaultCStringEncoding]]];
+            }
         }
-    }
         
-    //[panel setMessage:@"Import one or more files or directories."];
-    [panel setAllowsMultipleSelection: ( (flags & F_MULTIPLE)?true:false ) ];
-    [panel setCanChooseDirectories: ( (flags & F_DIRECTORY)?true:false ) ];
-    [panel setCanCreateDirectories: true];
-    [panel setResolvesAliases: true];
-    [panel setCanChooseFiles: ( (flags & F_FILE)?true:false )];
-    [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ];
-    
-    std::vector<std::string>* outfiles = NULL; 
-    
-    if (fileTypes)
-    {
-        [panel setAllowedFileTypes:fileTypes];
-        result = [panel runModal];
-    }
-    else 
-    {
-        // I suggest it's better to open the last path and let this default to home dir as necessary
-        // for consistency with other OS X apps
-        //
-        //[panel setDirectoryURL: fileURLWithPath(NSHomeDirectory()) ];
-        result = [panel runModal];
-    }
-    
-    if (result == NSOKButton) 
-    {
-        NSArray *filesToOpen = [panel URLs];
-        int i, count = [filesToOpen count];
+        //[panel setMessage:@"Import one or more files or directories."];
+        [panel setAllowsMultipleSelection: ( (flags & F_MULTIPLE)?true:false ) ];
+        [panel setCanChooseDirectories: ( (flags & F_DIRECTORY)?true:false ) ];
+        [panel setCanCreateDirectories: true];
+        [panel setResolvesAliases: true];
+        [panel setCanChooseFiles: ( (flags & F_FILE)?true:false )];
+        [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ];
         
-        if (count > 0)
+        if (fileTypes)
+        {
+            [panel setAllowedFileTypes:fileTypes];
+            result = [panel runModal];
+        }
+        else
         {
-            outfiles = new std::vector<std::string>;
+            // I suggest it's better to open the last path and let this default to home dir as necessary
+            // for consistency with other OS X apps
+            //
+            //[panel setDirectoryURL: fileURLWithPath(NSHomeDirectory()) ];
+            result = [panel runModal];
         }
         
-        for (i=0; i<count; i++) {
-            NSString *aFile = [[filesToOpen objectAtIndex:i] path];
-            std::string *afilestr = new std::string([aFile UTF8String]);
-            outfiles->push_back(*afilestr);
+        if (result == NSOKButton)
+        {
+            NSArray *filesToOpen = [panel URLs];
+            int i, count = [filesToOpen count];
+            
+            if (count > 0)
+            {
+                outfiles.reset(new std::vector<std::string>);
+            }
+            
+            for (i=0; i<count; i++) {
+                NSString *aFile = [[filesToOpen objectAtIndex:i] path];
+                std::string afilestr = std::string([aFile UTF8String]);
+                outfiles->push_back(afilestr);
+            }
         }
     }
+
     return outfiles;
 }
 
 
-std::string* doSaveDialog(const std::string* file, 
+std::unique_ptr<std::string> doSaveDialog(const std::string* file,
                   const std::string* type,
                   const std::string* creator,
                   const std::string* extension,
                   unsigned int flags)
 {
-    NSSavePanel *panel = [NSSavePanel savePanel]; 
-    
-    NSString *extensionns = [NSString stringWithCString:extension->c_str() encoding:[NSString defaultCStringEncoding]];
-    NSArray *fileType = [extensionns componentsSeparatedByString:@","];
-    
-    //[panel setMessage:@"Save Image File"]; 
-    [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ];
-    [panel setCanSelectHiddenExtension:true]; 
-    [panel setAllowedFileTypes:fileType];
-    NSString *fileName = [NSString stringWithCString:file->c_str() encoding:[NSString defaultCStringEncoding]];
-    
-    std::string *outfile = NULL;
-    NSURL* url = [NSURL fileURLWithPath:fileName];
-    [panel setNameFieldStringValue: fileName];
-    [panel setDirectoryURL: url];
-    if([panel runModal] == 
-       NSFileHandlingPanelOKButton) 
-    {
-        NSURL* url = [panel URL];
-        NSString* p = [url path];
-        outfile = new std::string( [p UTF8String] );
-        // write the file 
-    } 
+    std::unique_ptr<std::string> outfile;
+    @autoreleasepool {
+        NSSavePanel *panel = [NSSavePanel savePanel];
+        
+        NSString *extensionns = [NSString stringWithCString:extension->c_str() encoding:[NSString defaultCStringEncoding]];
+        NSArray *fileType = [extensionns componentsSeparatedByString:@","];
+        
+        //[panel setMessage:@"Save Image File"];
+        [panel setTreatsFilePackagesAsDirectories: ( flags & F_NAV_SUPPORT ) ];
+        [panel setCanSelectHiddenExtension:true];
+        [panel setAllowedFileTypes:fileType];
+        NSString *fileName = [NSString stringWithCString:file->c_str() encoding:[NSString defaultCStringEncoding]];
+        
+        NSURL* url = [NSURL fileURLWithPath:fileName];
+        [panel setNameFieldStringValue: fileName];
+        [panel setDirectoryURL: url];
+        if([panel runModal] ==
+           NSFileHandlingPanelOKButton)
+        {
+            NSURL* url = [panel URL];
+            NSString* p = [url path];
+            outfile.reset(new std::string([p UTF8String]));
+            // write the file
+        }
+    }
     return outfile;
 }
 
-- 
cgit v1.2.3


From 97ea17e6e9fec4538868e3dc86b802c1117e012b Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 28 Oct 2022 18:50:38 -0400
Subject: Fix leak of copy and paste on mac

---
 indra/llwindow/llwindowmacosx-objc.h  |  2 +-
 indra/llwindow/llwindowmacosx-objc.mm | 45 ++++++++++++++++++-----------------
 indra/llwindow/llwindowmacosx.cpp     |  7 ++++--
 3 files changed, 29 insertions(+), 25 deletions(-)

diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h
index 43edc0110d..77024d3a9c 100644
--- a/indra/llwindow/llwindowmacosx-objc.h
+++ b/indra/llwindow/llwindowmacosx-objc.h
@@ -83,7 +83,7 @@ int createNSApp(int argc, const char **argv);
 void setupCocoa();
 bool pasteBoardAvailable();
 bool copyToPBoard(const unsigned short *str, unsigned int len);
-const unsigned short *copyFromPBoard();
+unsigned short *copyFromPBoard();
 CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY);
 short releaseImageCursor(CursorRef ref);
 short setImageCursor(CursorRef ref);
diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm
index 5ec9b017cf..acbcd1c281 100644
--- a/indra/llwindow/llwindowmacosx-objc.mm
+++ b/indra/llwindow/llwindowmacosx-objc.mm
@@ -64,13 +64,13 @@ void setupCocoa()
 
 bool copyToPBoard(const unsigned short *str, unsigned int len)
 {
-	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
-	NSPasteboard *pboard = [NSPasteboard generalPasteboard];
-	[pboard clearContents];
-	
-	NSArray *contentsToPaste = [[NSArray alloc] initWithObjects:[NSString stringWithCharacters:str length:len], nil];
-	[pool release];
-	return [pboard writeObjects:contentsToPaste];
+    @autoreleasepool {
+        NSPasteboard *pboard = [NSPasteboard generalPasteboard];
+        [pboard clearContents];
+        
+        NSArray *contentsToPaste = [[[NSArray alloc] initWithObjects:[NSString stringWithCharacters:str length:len], nil] autorelease];
+        return [pboard writeObjects:contentsToPaste];
+    }
 }
 
 bool pasteBoardAvailable()
@@ -79,22 +79,23 @@ bool pasteBoardAvailable()
 	return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
 }
 
-const unsigned short *copyFromPBoard()
+unsigned short *copyFromPBoard()
 {
-	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
-	NSPasteboard *pboard = [NSPasteboard generalPasteboard];
-	NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
-	NSString *str = NULL;
-	BOOL ok = [pboard canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
-	if (ok)
-	{
-		NSArray *objToPaste = [pboard readObjectsForClasses:classArray options:[NSDictionary dictionary]];
-		str = [objToPaste objectAtIndex:0];
-	}
-	unichar* temp = (unichar*)calloc([str length]+1, sizeof(unichar));
-	[str getCharacters:temp];
-	[pool release];
-	return temp;
+    @autoreleasepool {
+        NSPasteboard *pboard = [NSPasteboard generalPasteboard];
+        NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
+        NSString *str = NULL;
+        BOOL ok = [pboard canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
+        if (ok)
+        {
+            NSArray *objToPaste = [pboard readObjectsForClasses:classArray options:[NSDictionary dictionary]];
+            str = [objToPaste objectAtIndex:0];
+        }
+        NSUInteger str_len = [str length];
+        unichar* temp = (unichar*)calloc(str_len+1, sizeof(unichar));
+        [str getCharacters:temp range:NSMakeRange(0, str_len)];
+        return temp;
+    }
 }
 
 CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index c29131d60b..cf940bf68c 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1246,8 +1246,11 @@ BOOL LLWindowMacOSX::isClipboardTextAvailable()
 }
 
 BOOL LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
-{	
-	llutf16string str(copyFromPBoard());
+{
+    unsigned short* pboard_data = copyFromPBoard(); // must free returned data
+	llutf16string str(pboard_data);
+    free(pboard_data);
+
 	dst = utf16str_to_wstring(str);
 	if (dst != L"")
 	{
-- 
cgit v1.2.3


From d628a537f52b29dc1afd1dbea562f2abf48c7e4a Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 28 Oct 2022 18:51:11 -0400
Subject: Fix leaks in mac IME

---
 indra/newview/llappdelegate-objc.mm | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm
index 5214f4b838..1090888c1c 100644
--- a/indra/newview/llappdelegate-objc.mm
+++ b/indra/newview/llappdelegate-objc.mm
@@ -46,6 +46,7 @@
 
 - (void)dealloc
 {
+    [currentInputLanguage release];
     [super dealloc];
 }
 
@@ -199,12 +200,14 @@
 
 - (bool) romanScript
 {
-	// How to add support for new languages with the input window:
-	// Simply append this array with the language code (ja for japanese, ko for korean, zh for chinese, etc.)
-	NSArray *nonRomanScript = [[NSArray alloc] initWithObjects:@"ja", @"ko", @"zh-Hant", @"zh-Hans", nil];
-	if ([nonRomanScript containsObject:currentInputLanguage])
-    {
-        return false;
+    @autoreleasepool {
+        // How to add support for new languages with the input window:
+        // Simply append this array with the language code (ja for japanese, ko for korean, zh for chinese, etc.)
+        NSArray* nonRomanScript = @[@"ja", @"ko", @"zh-Hant", @"zh-Hans"];
+        if ([nonRomanScript containsObject:currentInputLanguage])
+        {
+            return false;
+        }
     }
     
     return true;
-- 
cgit v1.2.3


From 83466b301a34183a0d146d13bdc7973e02172588 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Fri, 28 Oct 2022 18:53:34 -0400
Subject: Clean up autorelease behavior in llwindowmac and additional leaks

---
 indra/llwindow/llwindowmacosx-objc.mm | 91 +++++++++++++++++------------------
 1 file changed, 45 insertions(+), 46 deletions(-)

diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm
index acbcd1c281..57c3d86295 100644
--- a/indra/llwindow/llwindowmacosx-objc.mm
+++ b/indra/llwindow/llwindowmacosx-objc.mm
@@ -49,14 +49,12 @@ void setupCocoa()
 	
 	if(!inited)
 	{
-		NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-		
-		// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
-		// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
-		// when init'ing the Cocoa App window.
-		[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
-		
-		[pool release];
+        @autoreleasepool {
+            // The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
+            // ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
+            // when init'ing the Cocoa App window.
+            [[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
+        }
 
 		inited = true;
 	}
@@ -100,19 +98,18 @@ unsigned short *copyFromPBoard()
 
 CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
 {
-	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-	// extra retain on the NSCursor since we want it to live for the lifetime of the app.
-	NSCursor *cursor =
-	[[[NSCursor alloc]
-	  initWithImage:
-	  [[[NSImage alloc] initWithContentsOfFile:
-		[NSString stringWithUTF8String:fullpath]
-		]autorelease]
-	  hotSpot:NSMakePoint(hotspotX, hotspotY)
-	  ]retain];
-	
-	[pool release];
+    NSCursor *cursor = nil;
+    @autoreleasepool {
+        // extra retain on the NSCursor since we want it to live for the lifetime of the app.
+        cursor =
+        [[[NSCursor alloc]
+          initWithImage:
+              [[[NSImage alloc] initWithContentsOfFile:
+                    [NSString stringWithUTF8String:fullpath]
+               ] autorelease]
+          hotSpot:NSMakePoint(hotspotX, hotspotY)
+         ] retain];
+    }
 	
 	return (CursorRef)cursor;
 }
@@ -179,10 +176,10 @@ OSErr releaseImageCursor(CursorRef ref)
 {
 	if( ref != NULL )
 	{
-		NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-		NSCursor *cursor = (NSCursor*)ref;
-		[cursor release];
-		[pool release];
+        @autoreleasepool {
+            NSCursor *cursor = (NSCursor*)ref;
+            [cursor autorelease];
+        }
 	}
 	else
 	{
@@ -196,10 +193,10 @@ OSErr setImageCursor(CursorRef ref)
 {
 	if( ref != NULL )
 	{
-		NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-		NSCursor *cursor = (NSCursor*)ref;
-		[cursor set];
-		[pool release];
+        @autoreleasepool {
+            NSCursor *cursor = (NSCursor*)ref;
+            [cursor set];
+        }
 	}
 	else
 	{
@@ -420,24 +417,26 @@ void requestUserAttention()
 
 long showAlert(std::string text, std::string title, int type)
 {
-    NSAlert *alert = [[NSAlert alloc] init];
-    
-    [alert setMessageText:[NSString stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]];
-    [alert setInformativeText:[NSString stringWithCString:text.c_str() encoding:[NSString defaultCStringEncoding]]];
-    if (type == 0)
-    {
-        [alert addButtonWithTitle:@"Okay"];
-    } else if (type == 1)
-    {
-        [alert addButtonWithTitle:@"Okay"];
-        [alert addButtonWithTitle:@"Cancel"];
-    } else if (type == 2)
-    {
-        [alert addButtonWithTitle:@"Yes"];
-        [alert addButtonWithTitle:@"No"];
+    long ret = 0;
+    @autoreleasepool {
+        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+        
+        [alert setMessageText:[NSString stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]];
+        [alert setInformativeText:[NSString stringWithCString:text.c_str() encoding:[NSString defaultCStringEncoding]]];
+        if (type == 0)
+        {
+            [alert addButtonWithTitle:@"Okay"];
+        } else if (type == 1)
+        {
+            [alert addButtonWithTitle:@"Okay"];
+            [alert addButtonWithTitle:@"Cancel"];
+        } else if (type == 2)
+        {
+            [alert addButtonWithTitle:@"Yes"];
+            [alert addButtonWithTitle:@"No"];
+        }
+        ret = [alert runModal];
     }
-    long ret = [alert runModal];
-    [alert dealloc];
     
     if (ret == NSAlertFirstButtonReturn)
     {
-- 
cgit v1.2.3


From d89033420ef05b9b0a5751c3f254ce802e90df0b Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sat, 29 Oct 2022 23:18:03 -0400
Subject: Fix RenderAppleUseMultGL debug setting for enabling threaded GL
 engine

---
 indra/llwindow/llwindowmacosx.cpp | 4 ++--
 indra/llwindow/llwindowmacosx.h   | 1 +
 indra/newview/llappviewer.cpp     | 5 +++++
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index cf940bf68c..2fe0ed469e 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -668,11 +668,11 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
 
 		if (cgl_err != kCGLNoError )
 		{
-			LL_DEBUGS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
+			LL_INFOS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
 		}
 		else
 		{
-			LL_DEBUGS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
+            LL_INFOS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
 		}
 	}
 	makeFirstResponder(mWindow, mGLView);
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
index b0f339e1db..851c860017 100644
--- a/indra/llwindow/llwindowmacosx.h
+++ b/indra/llwindow/llwindowmacosx.h
@@ -228,6 +228,7 @@ protected:
 	BOOL		mLanguageTextInputAllowed;
 	LLPreeditor*	mPreeditor;
 	
+public:
 	static BOOL	sUseMultGL;
 
 	friend class LLWindowManager;
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 4f3e0b08e4..5d509fa4ff 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -135,6 +135,10 @@
 #include "vlc/libvlc_version.h"
 #endif // LL_LINUX
 
+#if LL_DARWIN
+#include "llwindowmacosx.h"
+#endif
+
 // Third party library includes
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
@@ -560,6 +564,7 @@ static void settings_to_globals()
     LLWorldMapView::setScaleSetting(gSavedSettings.getF32("MapScale"));
 	
 #if LL_DARWIN
+    LLWindowMacOSX::sUseMultGL = gSavedSettings.getBOOL("RenderAppleUseMultGL");
 	gHiDPISupport = gSavedSettings.getBOOL("RenderHiDPI");
 #endif
 }
-- 
cgit v1.2.3


From 971fd6f8433b07bbd51ef83f2de518ef8b20d07f Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sat, 29 Oct 2022 23:18:35 -0400
Subject: Fix use of deprecated CGDisplayAvailableModes with
 CGDisplayCopyAllDisplayModes

---
 indra/llwindow/llwindowmacosx.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 2fe0ed469e..f924b17090 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1283,7 +1283,7 @@ LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_r
 {
 	if (!mSupportedResolutions)
 	{
-		CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
+		CFArrayRef modes = CGDisplayCopyAllDisplayModes(mDisplay, nullptr);
 
 		if(modes != NULL)
 		{
@@ -1322,6 +1322,7 @@ LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_r
 					}
 				}
 			}
+            CFRelease(modes);
 		}
 	}
 
-- 
cgit v1.2.3


From d0e07c770b978d57210a5403bc42cc48e700ef63 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sun, 30 Oct 2022 06:56:16 -0400
Subject: Fix checks for empty LLSD maps to use size and not emptyMap which is
 for creating an empty LLSDMap type.

---
 indra/llcommon/llerror.cpp              | 2 +-
 indra/llui/llmultislider.cpp            | 2 +-
 indra/llwindow/lldxhardware.cpp         | 2 +-
 indra/newview/llenvironment.cpp         | 2 +-
 indra/newview/llfloatermodelpreview.cpp | 2 +-
 indra/newview/llimprocessing.cpp        | 2 +-
 indra/newview/llpanelexperiencelog.cpp  | 2 +-
 indra/newview/llpanelobject.cpp         | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 919d2dabc4..56fb7c21ca 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -943,7 +943,7 @@ namespace LLError
             for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a)
             {
                 const LLSD& entry = *a;
-                if (entry.isMap() && !entry.emptyMap())
+                if (entry.isMap() && entry.size() != 0)
                 {
                     ELevel level = decodeLevel(entry["level"]);
 
diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp
index f89064d59a..604d246f12 100644
--- a/indra/llui/llmultislider.cpp
+++ b/indra/llui/llmultislider.cpp
@@ -92,7 +92,7 @@ LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
 	mMouseDownSignal(NULL),
 	mMouseUpSignal(NULL)
 {
-	mValue.emptyMap();
+	mValue = LLSD::emptyMap();
 	mCurSlider = LLStringUtil::null;
 
 	if (mOrientation == HORIZONTAL)
diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp
index 81e938edbe..391a377280 100644
--- a/indra/llwindow/lldxhardware.cpp
+++ b/indra/llwindow/lldxhardware.cpp
@@ -1098,7 +1098,7 @@ LLSD LLDXHardware::getDisplayInfo()
     }
 
 LCleanup:
-    if (ret.emptyMap())
+    if (!ret.isMap() || (ret.size() == 0))
     {
         LL_INFOS() << "Failed to get data, cleaning up" << LL_ENDL;
     }
diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp
index 1300cf3658..a01410e521 100644
--- a/indra/newview/llenvironment.cpp
+++ b/indra/newview/llenvironment.cpp
@@ -3086,7 +3086,7 @@ bool LLEnvironment::loadFromSettings()
         LL_INFOS("ENVIRONMENT") << "Unable to open previous session environment file " << user_filepath << LL_ENDL;
     }
 
-    if (!env_data.isMap() || env_data.emptyMap())
+    if (!env_data.isMap() || (env_data.size() == 0))
     {
         LL_DEBUGS("ENVIRONMENT") << "Empty map loaded from: " << user_filepath << LL_ENDL;
         return false;
diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp
index 66a245b779..6f8f73bca0 100644
--- a/indra/newview/llfloatermodelpreview.cpp
+++ b/indra/newview/llfloatermodelpreview.cpp
@@ -1740,7 +1740,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
 		childSetTextArg("download_weight", "[ST]", tbd);
 		childSetTextArg("server_weight", "[SIM]", tbd);
 		childSetTextArg("physics_weight", "[PH]", tbd);
-		if (!mModelPhysicsFee.isMap() || mModelPhysicsFee.emptyMap())
+		if (!mModelPhysicsFee.isMap() || (mModelPhysicsFee.size() == 0))
 		{
 			childSetTextArg("upload_fee", "[FEE]", tbd);
 		}
diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp
index 0524313a5c..57c0c7388e 100644
--- a/indra/newview/llimprocessing.cpp
+++ b/indra/newview/llimprocessing.cpp
@@ -1561,7 +1561,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url)
         return;
     }
 
-    if (messages.emptyArray())
+    if (messages.size() == 0)
     {
         // Nothing to process
         return;
diff --git a/indra/newview/llpanelexperiencelog.cpp b/indra/newview/llpanelexperiencelog.cpp
index 44b4728df7..e5c637938f 100644
--- a/indra/newview/llpanelexperiencelog.cpp
+++ b/indra/newview/llpanelexperiencelog.cpp
@@ -112,7 +112,7 @@ void LLPanelExperienceLog::refresh()
 	int items = 0;
 	bool moreItems = false;
 	LLSD events_to_save = events;
-	if (!events.emptyMap())
+	if (events.isMap() && events.size() != 0)
 	{
 		LLSD::map_const_iterator day = events.endMap();
 		do
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 0bfc1297d3..c04b402610 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -2119,7 +2119,7 @@ bool LLPanelObject::menuEnableItem(const LLSD& userdata)
     }
     else if (command == "params_paste")
     {
-        return mClipboardParams.isMap() && !mClipboardParams.emptyMap();
+        return mClipboardParams.isMap() && (mClipboardParams.size() != 0);
     }
     // copy options
     else if (command == "psr_copy")
-- 
cgit v1.2.3


From 8669f3f4c207e913bc2f6e39438d92d76b3aa1c4 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sun, 30 Oct 2022 06:59:54 -0400
Subject: Fix line editors deselecting when pressing capslock

---
 indra/llui/lllineeditor.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 33037b5001..4c7dd034fc 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -1567,7 +1567,7 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )
 				KEY_SHIFT != key &&
 				KEY_CONTROL != key &&
 				KEY_ALT != key &&
-				KEY_CAPSLOCK )
+				KEY_CAPSLOCK != key)
 			{
 				deselect();
 			}
-- 
cgit v1.2.3


From f3dd2bf95f942653123ec7a1011cf873fa7b4fb3 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sun, 30 Oct 2022 06:59:08 -0400
Subject: Fix menu checks for enabling object sit and touch to not traverse the
 entire menu holder to update labels

---
 indra/newview/llviewermenu.cpp | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 3573af40cb..fa2ec5fbec 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -2803,14 +2803,15 @@ void handle_object_show_original()
 }
 
 
-static void init_default_item_label(const std::string& item_name)
+static void init_default_item_label(LLUICtrl* ctrl)
 {
+	const std::string& item_name = ctrl->getName();
 	boost::unordered_map<std::string, LLStringExplicit>::iterator it = sDefaultItemLabels.find(item_name);
 	if (it == sDefaultItemLabels.end())
 	{
 		// *NOTE: This will not work for items of type LLMenuItemCheckGL because they return boolean value
 		//       (doesn't seem to matter much ATM).
-		LLStringExplicit default_label = gMenuHolder->childGetValue(item_name).asString();
+		LLStringExplicit default_label = ctrl->getValue().asString();
 		if (!default_label.empty())
 		{
 			sDefaultItemLabels.insert(std::pair<std::string, LLStringExplicit>(item_name, default_label));
@@ -2841,18 +2842,17 @@ bool enable_object_touch(LLUICtrl* ctrl)
 		new_value = obj->flagHandleTouch() || (parent && parent->flagHandleTouch());
 	}
 
-	std::string item_name = ctrl->getName();
-	init_default_item_label(item_name);
+	init_default_item_label(ctrl);
 
 	// Update label based on the node touch name if available.
 	LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode();
 	if (node && node->mValid && !node->mTouchName.empty())
 	{
-		gMenuHolder->childSetValue(item_name, node->mTouchName);
+		ctrl->setValue(node->mTouchName);
 	}
 	else
 	{
-		gMenuHolder->childSetValue(item_name, get_default_item_label(item_name));
+		ctrl->setValue(get_default_item_label(ctrl->getName()));
 	}
 
 	return new_value;
@@ -6591,20 +6591,18 @@ bool enable_object_sit(LLUICtrl* ctrl)
 	bool sitting_on_sel = sitting_on_selection();
 	if (!sitting_on_sel)
 	{
-		std::string item_name = ctrl->getName();
-
 		// init default labels
-		init_default_item_label(item_name);
+		init_default_item_label(ctrl);
 
 		// Update label
 		LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode();
 		if (node && node->mValid && !node->mSitName.empty())
 		{
-			gMenuHolder->childSetValue(item_name, node->mSitName);
+			ctrl->setValue(node->mSitName);
 		}
 		else
 		{
-			gMenuHolder->childSetValue(item_name, get_default_item_label(item_name));
+			ctrl->setValue(get_default_item_label(ctrl->getName()));
 		}
 	}
 	return !sitting_on_sel && is_object_sittable();
-- 
cgit v1.2.3


From 0c36fed11056511872afae14a39a88f5e46be0fc Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sun, 30 Oct 2022 18:05:34 -0400
Subject: Correct macOS png loader to use a default gamma of 2.2 as apple has
 done since OS 10.6

---
 indra/llimage/llpngwrapper.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp
index f7dc6272cf..cad7c00042 100644
--- a/indra/llimage/llpngwrapper.cpp
+++ b/indra/llimage/llpngwrapper.cpp
@@ -257,12 +257,7 @@ void LLPngWrapper::normalizeImage()
 		png_set_strip_16(mReadPngPtr);
 	}
 
-#if LL_DARWIN
-	const F64 SCREEN_GAMMA = 1.8;
-#else
 	const F64 SCREEN_GAMMA = 2.2;
-#endif
-
 	if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma))
 	{
 		png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma);
-- 
cgit v1.2.3


From 520ebb392bd06701fb6f6472cccebfaa8f13a03c Mon Sep 17 00:00:00 2001
From: Beq <beqjanus@gmail.com>
Date: Sat, 5 Nov 2022 22:37:14 +0000
Subject: [BUG-232834][BUG-232871] Alpha mask is being applied with emissive
 mask

This is the most local fix for this issue, addressing the specific unqualified use of HAS_ALPHA_MASK.
If we find other issues with alpha mask being applied incorrectly then, it may be better to fix higher up in llvieweshadermgr.cpp by reverting the changes from SL-17532.
For now, this way works for this specific bug without non-emissive side-effects.
---
 indra/newview/app_settings/shaders/class1/deferred/materialF.glsl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
index b26194f278..1280bc20a8 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
@@ -227,7 +227,7 @@ void main()
     vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy);
 	diffcol.rgb *= vertex_color.rgb;
 
-#ifdef HAS_ALPHA_MASK
+#if HAS_ALPHA_MASK && (DIFFUSE_ALPHA_MODE != DIFFUSE_ALPHA_MODE_EMISSIVE)
 #if DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND
     if (diffcol.a*vertex_color.a < minimum_alpha)
 #else
-- 
cgit v1.2.3


From 73ca604ad5aa5dc93198ab8778b135b7f01d5c7b Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Mon, 14 Nov 2022 22:39:03 +0200
Subject: SL-18618 Update libpng

---
 autobuild.xml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/autobuild.xml b/autobuild.xml
index 1a353fe2e9..f363b76706 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1418,9 +1418,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>c1c9e32e21f3c34d91ed045b2ca91f24</string>
+              <string>7a0059748d0b8733f2f9ce434cf604b8</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/87781/805801/libpng-1.6.8.563850-darwin64-563850.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/107514/937867/libpng-1.6.38.576621-darwin64-576621.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -1442,9 +1442,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>642e9cf95c8ccd0eb34f6d7a40df585a</string>
+              <string>3112013186ad60b0fc270a398d4dd499</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/87782/805831/libpng-1.6.8.563850-windows-563850.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/107513/937823/libpng-1.6.38.576621-windows-576621.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -1454,16 +1454,16 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>ce46aa0f171d97626c4a3940347cecd7</string>
+              <string>7c6bfcdb0d6162587cdbc436f595dd02</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/87780/805832/libpng-1.6.8.563850-windows64-563850.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/107512/937822/libpng-1.6.38.576621-windows64-576621.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
         <key>version</key>
-        <string>1.6.8.563850</string>
+        <string>1.6.38.576621</string>
       </map>
       <key>libuuid</key>
       <map>
-- 
cgit v1.2.3


From 5f44c2a5ffacc04e383635840f50ad3ddcf69dd4 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 17 Nov 2022 00:41:35 +0200
Subject: SL-15869 Do not account for login menu when checking if key
 combination is avaliable

Ex: Allow mapping actions to Ctrl+Alt+D
---
 indra/newview/llkeyconflict.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp
index d3ba18525b..60f8aca94c 100644
--- a/indra/newview/llkeyconflict.cpp
+++ b/indra/newview/llkeyconflict.cpp
@@ -171,8 +171,9 @@ bool LLKeyConflictHandler::isReservedByMenu(const KEY &key, const MASK &mask)
     {
         return false;
     }
-    return (gMenuBarView && gMenuBarView->hasAccelerator(key, mask))
-           || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(key, mask));
+    // At the moment controls are only applicable inworld,
+    // ignore gLoginMenuBarView
+    return gMenuBarView && gMenuBarView->hasAccelerator(key, mask);
 }
 
 // static
@@ -182,8 +183,7 @@ bool LLKeyConflictHandler::isReservedByMenu(const LLKeyData &data)
     {
         return false;
     }
-    return (gMenuBarView && gMenuBarView->hasAccelerator(data.mKey, data.mMask))
-           || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(data.mKey, data.mMask));
+    return gMenuBarView && gMenuBarView->hasAccelerator(data.mKey, data.mMask);
 }
 
 bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask)
-- 
cgit v1.2.3


From a8ded3bcc4703803f738df14a665016aa20d03a2 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 18 Nov 2022 14:43:54 +0200
Subject: SL-17135 Pull in apr-suit package

Same apr suit version, but with debug symbols
---
 autobuild.xml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/autobuild.xml b/autobuild.xml
index f363b76706..ae63d08f93 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -52,9 +52,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>b6357ef3a0ec37877a5831820f25094e</string>
+              <string>178b16ee9ff67986c8c14413ee68218e</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80557/759704/apr_suite-1.4.5.558565-darwin64-558565.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/107593/938535/apr_suite-1.4.5.576669-darwin64-576669.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -76,9 +76,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>cb48ac069440f6dcd564cfa9fd02a4c2</string>
+              <string>d2997cad03dbd0d70a060276b5671480</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80556/759710/apr_suite-1.4.5.558565-windows-558565.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/107594/938548/apr_suite-1.4.5.576669-windows-576669.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -88,16 +88,16 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>646dc3828d9c39fb1e77c4eec44ed739</string>
+              <string>ec24f5945faa8f13807b83eeaeb994f8</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80555/759709/apr_suite-1.4.5.558565-windows64-558565.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/107592/938547/apr_suite-1.4.5.576669-windows64-576669.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
         <key>version</key>
-        <string>1.4.5.558565</string>
+        <string>1.4.5.576669</string>
       </map>
       <key>boost</key>
       <map>
-- 
cgit v1.2.3


From e231b6d8d3bf3d65d94054e6259e5d33d24e5425 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 22 Nov 2022 01:43:14 +0200
Subject: SL-18689 Crash at LLTabContainer::selectNextTab()

FPE_NOOP at "idx = (idx + 1 ) % (S32)mTabList.size();"
---
 indra/llui/lltabcontainer.cpp | 5 +++++
 indra/newview/llstartup.cpp   | 2 --
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 0aa7a2d217..8c841540a5 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -1442,6 +1442,11 @@ void LLTabContainer::selectLastTab()
 
 void LLTabContainer::selectNextTab()
 {
+    if (mTabList.size() == 0)
+    {
+        return;
+    }
+
 	BOOL tab_has_focus = FALSE;
 	if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
 	{
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 054e9530d4..f2d73a4043 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -272,12 +272,10 @@ void show_first_run_dialog();
 bool first_run_dialog_callback(const LLSD& notification, const LLSD& response);
 void set_startup_status(const F32 frac, const std::string& string, const std::string& msg);
 bool login_alert_status(const LLSD& notification, const LLSD& response);
-void login_packet_failed(void**, S32 result);
 void use_circuit_callback(void**, S32 result);
 void register_viewer_callbacks(LLMessageSystem* msg);
 void asset_callback_nothing(const LLUUID&, LLAssetType::EType, void*, S32);
 bool callback_choose_gender(const LLSD& notification, const LLSD& response);
-void init_start_screen(S32 location_id);
 void release_start_screen();
 void reset_login();
 LLSD transform_cert_args(LLPointer<LLCertificate> cert);
-- 
cgit v1.2.3


From 87664fa35de33a8db00dd61ba91f5dc73afce14a Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 22 Nov 2022 23:08:47 +0200
Subject: SL-18219 Crash getting and sending render info on exit

There might be other causes for sendRenderInfoToRegion and getRenderInfoFromRegion, crashing, but in some cases viewer was shutting down
---
 indra/newview/llappviewer.cpp                  | 3 ++-
 indra/newview/llavatarrenderinfoaccountant.cpp | 3 +--
 indra/newview/llscenemonitor.cpp               | 2 +-
 indra/newview/llscenemonitor.h                 | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 5d509fa4ff..f307bdf2e8 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1730,7 +1730,8 @@ bool LLAppViewer::cleanup()
 	{
 		if (!isSecondInstance())
 		{
-			LLSceneMonitor::instance().dumpToFile(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv"));
+            std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv");
+			LLSceneMonitor::instance().dumpToFile(dump_path);
 		}
 		LLSceneMonitor::deleteSingleton();
 	}
diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp
index 275f17b02a..293c9d60a1 100644
--- a/indra/newview/llavatarrenderinfoaccountant.cpp
+++ b/indra/newview/llavatarrenderinfoaccountant.cpp
@@ -364,11 +364,10 @@ void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regi
 	}
 }
 
-// static
 // Called every frame - send render weight requests to every region
 void LLAvatarRenderInfoAccountant::idle()
 {
-	if (mRenderInfoScanTimer.hasExpired())
+	if (mRenderInfoScanTimer.hasExpired() && !LLApp::isExiting())
 	{
 		LL_DEBUGS("AvatarRenderInfo") << "Scanning regions for render info updates"
 									  << LL_ENDL;
diff --git a/indra/newview/llscenemonitor.cpp b/indra/newview/llscenemonitor.cpp
index 2e44dc1459..7089df677e 100644
--- a/indra/newview/llscenemonitor.cpp
+++ b/indra/newview/llscenemonitor.cpp
@@ -515,7 +515,7 @@ void LLSceneMonitor::fetchQueryResult()
 }
 
 //dump results to a file _scene_xmonitor_results.csv
-void LLSceneMonitor::dumpToFile(std::string file_name)
+void LLSceneMonitor::dumpToFile(const std::string &file_name)
 {
 	if (!hasResults()) return;
 
diff --git a/indra/newview/llscenemonitor.h b/indra/newview/llscenemonitor.h
index 7cd531bd34..f2e1ef69b9 100644
--- a/indra/newview/llscenemonitor.h
+++ b/indra/newview/llscenemonitor.h
@@ -61,7 +61,7 @@ public:
 	bool needsUpdate() const;
 	
 	const LLTrace::ExtendablePeriodicRecording* getRecording() const {return &mSceneLoadRecording;}
-	void dumpToFile(std::string file_name);
+	void dumpToFile(const std::string &file_name);
 	bool hasResults() const { return mSceneLoadRecording.getResults().getDuration() != S32Seconds(0);}
 
 	void reset();
-- 
cgit v1.2.3


From cfc2d2890e02898199337f46f364edc2fd3efcf8 Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <118780484+maxim-productengine@users.noreply.github.com>
Date: Thu, 1 Dec 2022 02:04:04 +0200
Subject: SL-18243 Add wear and unwear buttons on line items in Outfits floater

---
 indra/newview/llinventorylistitem.h                |   2 +-
 indra/newview/llwearableitemslist.cpp              | 101 +++++++++++++++++++--
 indra/newview/llwearableitemslist.h                |  23 ++++-
 .../skins/default/textures/icons/add_icon.png      | Bin 0 -> 3386 bytes
 .../skins/default/textures/icons/remove_icon.png   | Bin 0 -> 3446 bytes
 indra/newview/skins/default/textures/textures.xml  |   3 +
 .../skins/default/xui/en/outfit_accordion_tab.xml  |   1 +
 .../skins/default/xui/en/panel_outfits_wearing.xml |   1 +
 .../xui/en/widgets/wearable_outfit_list_item.xml   |  64 +++++++++++++
 9 files changed, 183 insertions(+), 12 deletions(-)
 create mode 100644 indra/newview/skins/default/textures/icons/add_icon.png
 create mode 100644 indra/newview/skins/default/textures/icons/remove_icon.png
 create mode 100644 indra/newview/skins/default/xui/en/widgets/wearable_outfit_list_item.xml

diff --git a/indra/newview/llinventorylistitem.h b/indra/newview/llinventorylistitem.h
index d4dd212cc3..cf713a6930 100644
--- a/indra/newview/llinventorylistitem.h
+++ b/indra/newview/llinventorylistitem.h
@@ -197,6 +197,7 @@ protected:
 	virtual BOOL handleToolTip( S32 x, S32 y, MASK mask);
 
 	const LLUUID mInventoryItemUUID;
+    bool mHovered;
 
 private:
 
@@ -221,7 +222,6 @@ private:
 	LLUIImagePtr	mSelectedImage;
 	LLUIImagePtr	mSeparatorImage;
 
-	bool			mHovered;
 	bool			mSelected;
 	bool			mSeparatorVisible;
 
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index bf4db81475..89b74ae962 100644
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -92,17 +92,77 @@ LLPanelWearableListItem::LLPanelWearableListItem(LLViewerInventoryItem* item, co
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////
+static LLWidgetNameRegistry::StaticRegistrar sRegisterPanelWearableOutfitItem(&typeid(LLPanelWearableOutfitItem::Params), "wearable_outfit_list_item");
+
+LLPanelWearableOutfitItem::Params::Params()
+:   add_btn("add_btn"),
+    remove_btn("remove_btn")
+{
+}
+
+BOOL LLPanelWearableOutfitItem::postBuild()
+{
+    LLPanelWearableListItem::postBuild();
+    
+    LLViewerInventoryItem* inv_item = getItem();
+    mShowWidgets &= (inv_item->getType() != LLAssetType::AT_BODYPART);
+    if(mShowWidgets)
+    {
+        addWidgetToRightSide("add_wearable");
+        addWidgetToRightSide("remove_wearable");
+
+        childSetAction("add_wearable", boost::bind(&LLPanelWearableOutfitItem::onAddWearable, this));
+        childSetAction("remove_wearable", boost::bind(&LLPanelWearableOutfitItem::onRemoveWearable, this));
+
+        setWidgetsVisible(false);
+        reshapeWidgets();
+    }
+    return TRUE;
+}
+
+BOOL LLPanelWearableOutfitItem::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+    if(!mShowWidgets)
+    {
+        return LLPanelWearableListItem::handleDoubleClick(x, y, mask);
+    }
+
+    if(LLAppearanceMgr::instance().isLinkedInCOF(mInventoryItemUUID))
+    {
+        onRemoveWearable();
+    }
+    else
+    {
+        onAddWearable();
+    }
+    return TRUE;
+}
+
+void LLPanelWearableOutfitItem::onAddWearable()
+{
+    setWidgetsVisible(false);
+    reshapeWidgets();
+    LLAppearanceMgr::instance().wearItemOnAvatar(mInventoryItemUUID, true, false);
+}
+
+void LLPanelWearableOutfitItem::onRemoveWearable()
+{
+    setWidgetsVisible(false);
+    reshapeWidgets();
+    LLAppearanceMgr::instance().removeItemFromAvatar(mInventoryItemUUID);
+}
 
 // static
 LLPanelWearableOutfitItem* LLPanelWearableOutfitItem::create(LLViewerInventoryItem* item,
-															 bool worn_indication_enabled)
+															 bool worn_indication_enabled,
+                                                             bool show_widgets)
 {
 	LLPanelWearableOutfitItem* list_item = NULL;
 	if (item)
 	{
-		const LLPanelInventoryListItemBase::Params& params = LLUICtrlFactory::getDefaultParams<LLPanelInventoryListItemBase>();
+		const LLPanelWearableOutfitItem::Params& params = LLUICtrlFactory::getDefaultParams<LLPanelWearableOutfitItem>();
 
-		list_item = new LLPanelWearableOutfitItem(item, worn_indication_enabled, params);
+		list_item = new LLPanelWearableOutfitItem(item, worn_indication_enabled, params, show_widgets);
 		list_item->initFromParams(params);
 		list_item->postBuild();
 	}
@@ -110,11 +170,23 @@ LLPanelWearableOutfitItem* LLPanelWearableOutfitItem::create(LLViewerInventoryIt
 }
 
 LLPanelWearableOutfitItem::LLPanelWearableOutfitItem(LLViewerInventoryItem* item,
-													 bool worn_indication_enabled,
-													 const LLPanelWearableOutfitItem::Params& params)
-: LLPanelInventoryListItemBase(item, params)
+                                                     bool worn_indication_enabled,
+                                                     const LLPanelWearableOutfitItem::Params& params,
+                                                     bool show_widgets)
+: LLPanelWearableListItem(item, params)
 , mWornIndicationEnabled(worn_indication_enabled)
+, mShowWidgets(show_widgets)
 {
+    if(mShowWidgets)
+    {
+        LLButton::Params button_params = params.add_btn;
+        applyXUILayout(button_params, this);
+        addChild(LLUICtrlFactory::create<LLButton>(button_params));
+
+        button_params = params.remove_btn;
+        applyXUILayout(button_params, this);
+        addChild(LLUICtrlFactory::create<LLButton>(button_params));
+    }
 }
 
 // virtual
@@ -127,11 +199,22 @@ void LLPanelWearableOutfitItem::updateItem(const std::string& name,
 	// We don't use get_is_item_worn() here because this update is triggered by
 	// an inventory observer upon link in COF beind added or removed so actual
 	// worn status of a linked item may still remain unchanged.
-	if (mWornIndicationEnabled && LLAppearanceMgr::instance().isLinkedInCOF(mInventoryItemUUID))
+    bool is_worn = LLAppearanceMgr::instance().isLinkedInCOF(mInventoryItemUUID);
+	if (mWornIndicationEnabled && is_worn)
 	{
 		search_label += LLTrans::getString("worn");
 		item_state = IS_WORN;
 	}
+    if(mShowWidgets)
+    {
+        setShowWidget("add_wearable", !is_worn);
+        setShowWidget("remove_wearable", is_worn);
+        if(mHovered)
+        {
+            setWidgetsVisible(true);
+            reshapeWidgets();
+        }
+    }
 
 	LLPanelInventoryListItemBase::updateItem(search_label, item_state);
 }
@@ -634,6 +717,7 @@ static const LLDefaultChildRegistry::Register<LLWearableItemsList> r("wearable_i
 LLWearableItemsList::Params::Params()
 :	standalone("standalone", true)
 ,	worn_indication_enabled("worn_indication_enabled", true)
+,   show_item_widgets("show_item_widgets", false)
 {}
 
 LLWearableItemsList::LLWearableItemsList(const LLWearableItemsList::Params& p)
@@ -649,6 +733,7 @@ LLWearableItemsList::LLWearableItemsList(const LLWearableItemsList::Params& p)
 	}
 	mWornIndicationEnabled = p.worn_indication_enabled;
 	setNoItemsCommentText(LLTrans::getString("LoadingData"));
+    mShowItemWidgets = p.show_item_widgets;
 }
 
 // virtual
@@ -665,7 +750,7 @@ LLPanel* LLWearableItemsList::createNewItem(LLViewerInventoryItem* item)
         return NULL;
     }
 
-    return LLPanelWearableOutfitItem::create(item, mWornIndicationEnabled);
+    return LLPanelWearableOutfitItem::create(item, mWornIndicationEnabled, mShowItemWidgets);
 }
 
 void LLWearableItemsList::updateList(const LLUUID& category_id)
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index ba8488b237..f7774a7086 100644
--- a/indra/newview/llwearableitemslist.h
+++ b/indra/newview/llwearableitemslist.h
@@ -72,12 +72,23 @@ protected:
  * Extends LLPanelInventoryListItemBase with handling
  * double click to wear the item.
  */
-class LLPanelWearableOutfitItem : public LLPanelInventoryListItemBase
+class LLPanelWearableOutfitItem : public LLPanelWearableListItem
 {
 	LOG_CLASS(LLPanelWearableOutfitItem);
 public:
+    struct Params : public LLInitParam::Block<Params, LLPanelWearableListItem::Params>
+    {
+        Optional<LLButton::Params>   add_btn, remove_btn;
+
+        Params();
+    };
+
+    BOOL postBuild();
+    BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+
 	static LLPanelWearableOutfitItem* create(LLViewerInventoryItem* item,
-											 bool worn_indication_enabled);
+											 bool worn_indication_enabled,
+                                             bool show_widgets);
 
 	/**
 	 * Updates item name and (worn) suffix.
@@ -85,12 +96,16 @@ public:
 	/*virtual*/ void updateItem(const std::string& name,
 								EItemState item_state = IS_DEFAULT);
 
+    void onAddWearable();
+    void onRemoveWearable();
+
 protected:
 	LLPanelWearableOutfitItem(LLViewerInventoryItem* item,
-							  bool worn_indication_enabled, const Params& params);
+							  bool worn_indication_enabled, const Params& params, bool show_widgets = false);
 
 private:
 	bool	mWornIndicationEnabled;
+    bool mShowWidgets;
 };
 
 class LLPanelDeletableWearableListItem : public LLPanelWearableListItem
@@ -442,6 +457,7 @@ public:
 	{
 		Optional<bool> standalone;
 		Optional<bool> worn_indication_enabled;
+        Optional<bool> show_item_widgets;
 
 		Params();
 	};
@@ -482,6 +498,7 @@ protected:
 
 	bool mIsStandalone;
 	bool mWornIndicationEnabled;
+    bool mShowItemWidgets;
 
 	ESortOrder		mSortOrder;
 
diff --git a/indra/newview/skins/default/textures/icons/add_icon.png b/indra/newview/skins/default/textures/icons/add_icon.png
new file mode 100644
index 0000000000..cb68ee8e16
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/add_icon.png differ
diff --git a/indra/newview/skins/default/textures/icons/remove_icon.png b/indra/newview/skins/default/textures/icons/remove_icon.png
new file mode 100644
index 0000000000..6e62ee33f4
Binary files /dev/null and b/indra/newview/skins/default/textures/icons/remove_icon.png differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 4429a1677e..1f2c0867c4 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -743,6 +743,9 @@ with the same filename but different name
   
   <texture name="Wearables_Divider" file_name="windows/Wearables_Divider.png" preload="false" />
 
+  <texture name="Add_Icon" file_name="icons/add_icon.png" preload="false" />
+  <texture name="Remove_Icon" file_name="icons/remove_icon.png" preload="false" />
+
   <texture name="Web_Profile_Off" file_name="icons/Web_Profile_Off.png" preload="false" />
 
   <texture name="WellButton_Lit" file_name="bottomtray/WellButton_Lit.png"  preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />
diff --git a/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml b/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml
index 2a24c74feb..d74dca8b95 100644
--- a/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml
+++ b/indra/newview/skins/default/xui/en/outfit_accordion_tab.xml
@@ -18,6 +18,7 @@
      follows="all"
      keep_one_selected="true"
      multi_select="true"
+     show_item_widgets="true"
      name="wearable_items_list"
      translate="false"
      standalone="false"
diff --git a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
index 42a7974316..b2dc975c6e 100644
--- a/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfits_wearing.xml
@@ -36,6 +36,7 @@
      left="3"
      multi_select="true"
      name="cof_items_list"
+     show_item_widgets="true"
      standalone="false"
      top="0"
      width="309"
diff --git a/indra/newview/skins/default/xui/en/widgets/wearable_outfit_list_item.xml b/indra/newview/skins/default/xui/en/widgets/wearable_outfit_list_item.xml
new file mode 100644
index 0000000000..cd84b91b1f
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/wearable_outfit_list_item.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<inventory_list_item
+  follows="top|right|left"
+  height="20"
+  name="inventory_item"
+  tab_stop="false" 
+  hover_image="ListItem_Over"
+  selected_image="ListItem_Select"
+  separator_image="Wearables_Divider" 
+  width="380">
+  <!-- DEFAULT style for inventory list item -->
+  <default_style
+   font="SansSerifSmall"
+   font.style="NORMAL" />
+
+  <!-- style for inventory list item WORN on avatar -->
+  <worn_style
+   font="SansSerifSmall"
+   font.style="BOLD"
+   color="EmphasisColor" />
+  <item_icon
+    height="16"
+    follows="top|left"
+    image_name="Inv_Object"
+    layout="topleft"
+    left="0"
+    name="item_icon"
+    top="0"
+    width="16" />
+  <item_name
+    follows="left|right"
+    height="20"
+    layout="topleft"
+    left="21"
+    parse_urls="false"
+    use_ellipses="true"
+    name="item_name"
+    text_color="white"
+    top="4"
+    value="..."
+    width="359" />
+    <add_btn
+     name="add_wearable"
+     layout="topleft"
+     follows="top|right"
+     image_unselected="Add_Icon"
+     image_selected="Add_Icon"
+     top="1"
+     left="0"
+     height="16"
+     width="16"
+     tab_stop="false" />
+    <remove_btn
+     name="remove_wearable"
+     layout="topleft"
+     follows="top|right"
+     image_unselected="Remove_Icon"
+     image_selected="Remove_Icon"
+     top="1"
+     left="26"
+     height="16"
+     width="16"
+     tab_stop="false" />
+</inventory_list_item>
-- 
cgit v1.2.3


From 112446804de888eef73f9f655d7c911da2d0a638 Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <mnikolenko@productengine.com>
Date: Sat, 3 Dec 2022 03:04:01 +0200
Subject: SL-18486 Complete Avatars floater is blank.

---
 indra/newview/llfloateravatar.cpp | 14 +++++++++-----
 indra/newview/llfloateravatar.h   |  3 +++
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/indra/newview/llfloateravatar.cpp b/indra/newview/llfloateravatar.cpp
index 31adf5b61e..f888d032ae 100644
--- a/indra/newview/llfloateravatar.cpp
+++ b/indra/newview/llfloateravatar.cpp
@@ -44,17 +44,21 @@ LLFloaterAvatar::LLFloaterAvatar(const LLSD& key)
 
 LLFloaterAvatar::~LLFloaterAvatar()
 {
-	LLMediaCtrl* avatar_picker = findChild<LLMediaCtrl>("avatar_picker_contents");
-	if (avatar_picker)
+	if (mAvatarPicker)
 	{
-		avatar_picker->navigateStop();
-		avatar_picker->clearCache();          //images are reloading each time already
-		avatar_picker->unloadMediaSource();
+		mAvatarPicker->navigateStop();
+        mAvatarPicker->clearCache();          //images are reloading each time already
+        mAvatarPicker->unloadMediaSource();
 	}
 }
 
 BOOL LLFloaterAvatar::postBuild()
 {
+    mAvatarPicker = findChild<LLMediaCtrl>("avatar_picker_contents");
+    if (mAvatarPicker)
+    {
+        mAvatarPicker->clearCache();
+    }
 	enableResizeCtrls(true, true, false);
 	return TRUE;
 }
diff --git a/indra/newview/llfloateravatar.h b/indra/newview/llfloateravatar.h
index cadc5e4028..76e9372709 100644
--- a/indra/newview/llfloateravatar.h
+++ b/indra/newview/llfloateravatar.h
@@ -29,6 +29,7 @@
 #define LL_FLOATER_AVATAR_H
 
 #include "llfloater.h"
+class LLMediaCtrl;
 
 class LLFloaterAvatar:
 	public LLFloater
@@ -38,6 +39,8 @@ private:
 	LLFloaterAvatar(const LLSD& key);
 	/*virtual*/	~LLFloaterAvatar();
 	/*virtual*/	BOOL postBuild();
+
+    LLMediaCtrl* mAvatarPicker;
 };
 
 #endif
-- 
cgit v1.2.3


From f7010e46af4205f2f14e07ee5d70c144e44aa7a5 Mon Sep 17 00:00:00 2001
From: akleshchev <117672381+akleshchev@users.noreply.github.com>
Date: Tue, 6 Dec 2022 04:37:07 +0200
Subject: SL-18778 Crash at LLVoiceClient::removeObserver (#25)

---
 indra/newview/llspeakingindicatormanager.cpp | 20 ++++++++++++++------
 indra/newview/llvoiceclient.cpp              | 15 ++++++++++++---
 indra/newview/llvoiceclient.h                |  2 --
 indra/newview/llvoicevivox.cpp               |  5 -----
 indra/newview/llvoicevivox.h                 |  2 --
 5 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp
index 5ca1d4b4a5..0111d8869c 100644
--- a/indra/newview/llspeakingindicatormanager.cpp
+++ b/indra/newview/llspeakingindicatormanager.cpp
@@ -51,6 +51,10 @@ class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, L
 	LLSINGLETON(SpeakingIndicatorManager);
 	~SpeakingIndicatorManager();
 	LOG_CLASS(SpeakingIndicatorManager);
+
+protected:
+    void                cleanupSingleton();
+
 public:
 
 	/**
@@ -183,12 +187,16 @@ SpeakingIndicatorManager::SpeakingIndicatorManager()
 
 SpeakingIndicatorManager::~SpeakingIndicatorManager()
 {
-	// Don't use LLVoiceClient::getInstance() here without check
-	// singleton MAY have already been destroyed.
-	if(LLVoiceClient::instanceExists())
-	{
-		LLVoiceClient::getInstance()->removeObserver(this);
-	}
+}
+
+void SpeakingIndicatorManager::cleanupSingleton()
+{
+    // Don't use LLVoiceClient::getInstance() here without a check,
+    // singleton MAY have already been destroyed.
+    if (LLVoiceClient::instanceExists())
+    {
+        LLVoiceClient::getInstance()->removeObserver(this);
+    }
 }
 
 void SpeakingIndicatorManager::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 2ef2d66f18..6bb987ede4 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -797,7 +797,10 @@ void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
 
 void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
 {
-	if (mVoiceModule && mVoiceModule->singletoneInstanceExists()) mVoiceModule->removeObserver(observer);
+    if (mVoiceModule)
+    {
+        mVoiceModule->removeObserver(observer);
+    }
 }
 
 void LLVoiceClient::addObserver(LLFriendObserver* observer)
@@ -807,7 +810,10 @@ void LLVoiceClient::addObserver(LLFriendObserver* observer)
 
 void LLVoiceClient::removeObserver(LLFriendObserver* observer)
 {
-	if (mVoiceModule && mVoiceModule->singletoneInstanceExists()) mVoiceModule->removeObserver(observer);
+    if (mVoiceModule)
+    {
+        mVoiceModule->removeObserver(observer);
+    }
 }
 
 void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
@@ -817,7 +823,10 @@ void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
 
 void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
 {
-	if (mVoiceModule && mVoiceModule->singletoneInstanceExists()) mVoiceModule->removeObserver(observer);
+    if (mVoiceModule)
+    {
+        mVoiceModule->removeObserver(observer);
+    }
 }
 
 std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index 246883b611..aa67502908 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -122,8 +122,6 @@ public:
 
 	virtual const LLVoiceVersionInfo& getVersion()=0;
 	
-	virtual bool singletoneInstanceExists()=0;
-	
 	/////////////////////
 	/// @name Tuning
 	//@{
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index ac6369e4e2..5a06877c38 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -5217,11 +5217,6 @@ void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle)
 	}
 }
 
-bool LLVivoxVoiceClient::singletoneInstanceExists()
-{
-	return LLVivoxVoiceClient::instanceExists();
-}
-
 void LLVivoxVoiceClient::leaveNonSpatialChannel()
 {
     LL_DEBUGS("Voice") << "Request to leave spacial channel." << LL_ENDL;
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index ebc3a62c35..0a785401c1 100644
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -73,8 +73,6 @@ public:
 
 	// Returns true if vivox has successfully logged in and is not in error state	
 	virtual bool isVoiceWorking() const;
-	
-	virtual bool singletoneInstanceExists();
 
 	/////////////////////
 	/// @name Tuning
-- 
cgit v1.2.3


From ab8dc07630728b3c5dc0a031eef28f5391734242 Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <maximnproductengine@lindenlab.com>
Date: Mon, 12 Dec 2022 13:13:48 +0200
Subject: SL-18826 limit teleport command usage

---
 indra/newview/llurldispatcher.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp
index a1670351f4..7c92e7ef98 100644
--- a/indra/newview/llurldispatcher.cpp
+++ b/indra/newview/llurldispatcher.cpp
@@ -263,7 +263,7 @@ public:
 	// inside the app, otherwise a malicious web page could
 	// cause a constant teleport loop.  JC
 	LLTeleportHandler() :
-		LLCommandHandler("teleport", UNTRUSTED_THROTTLE),
+		LLCommandHandler("teleport", UNTRUSTED_CLICK_ONLY),
 		LLEventAPI("LLTeleportHandler", "Low-level teleport API")
 	{
 		LLEventAPI::add("teleport",
-- 
cgit v1.2.3


From b4dd4271a1317c79aac4cf03a6612523e7a88ce4 Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Mon, 12 Dec 2022 20:22:50 +0200
Subject: SL-16874 Added tool tips to buttons displayed by llDialog()

---
 doc/contributions.txt                | 1 +
 indra/newview/lltoastnotifypanel.cpp | 1 +
 2 files changed, 2 insertions(+)

diff --git a/doc/contributions.txt b/doc/contributions.txt
index 04dc507783..f4cb450dd3 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -373,6 +373,7 @@ Charlie Sazaland
 Chaser Zaks
     BUG-225599
     BUG-227485
+    SL-16874
 Cherry Cheevers
 ChickyBabes Zuzu
 Chorazin Allen
diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp
index 024f25bc98..bf3f4c1e88 100644
--- a/indra/newview/lltoastnotifypanel.cpp
+++ b/indra/newview/lltoastnotifypanel.cpp
@@ -89,6 +89,7 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt
 	const LLFontGL* font = make_small_btn ? sFontSmall: sFont; // for block and ignore buttons in script dialog
 	p.name = form_element["name"].asString();
 	p.label = form_element["text"].asString();
+	p.tool_tip = form_element["text"].asString();
 	p.font = font;
 	p.rect.height = BTN_HEIGHT;
 	p.click_callback.function(boost::bind(&LLToastNotifyPanel::onClickButton, userdata));
-- 
cgit v1.2.3


From 07baa83ef273f41135b886363b34c423a566bffc Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 14 Dec 2022 20:49:56 +0200
Subject: SL-18384 Fix NSException for keyboard handling

Affects accent keys for diacritical marks
---
 indra/llwindow/llopenglview-objc.mm | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm
index 049226db65..7936245744 100644
--- a/indra/llwindow/llopenglview-objc.mm
+++ b/indra/llwindow/llopenglview-objc.mm
@@ -495,7 +495,12 @@ attributedStringInfo getSegments(NSAttributedString *str)
     // e.g. OS Window for upload something or Input Window...
     // mModifiers instance variable is for insertText: or insertText:replacementRange:  (by Pell Smit)
 	mModifiers = [theEvent modifierFlags];
-    unichar ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
+    NSString *str_no_modifiers = [theEvent charactersIgnoringModifiers];
+    unichar ch = 0;
+    if (str_no_modifiers.length)
+    {
+        ch = [str_no_modifiers characterAtIndex:0];
+    }
     bool acceptsText = mHasMarkedText ? false : callKeyDown(&eventData, keycode, mModifiers, ch);
 
     if (acceptsText &&
-- 
cgit v1.2.3


From 6f522084ed859507f13a6b04fe90eceb441f888e Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 10 Nov 2022 19:40:57 +0200
Subject: SL-18426 At log in only a part friends reported to chat as online

Server sends updates in bulk now, so notify per agent instead of per update
---
 indra/newview/llcallingcard.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
index 1ad2157df0..193d368b83 100644
--- a/indra/newview/llcallingcard.cpp
+++ b/indra/newview/llcallingcard.cpp
@@ -719,11 +719,12 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
 				// we were tracking someone who went offline
 				deleteTrackingData();
 			}
-		}
-		if(chat_notify)
-		{
-			// Look up the name of this agent for the notification
-			LLAvatarNameCache::get(agent_id,boost::bind(&on_avatar_name_cache_notify,_1, _2, online, payload));
+
+            if(chat_notify)
+            {
+                // Look up the name of this agent for the notification
+                LLAvatarNameCache::get(agent_id,boost::bind(&on_avatar_name_cache_notify,_1, _2, online, payload));
+            }
 		}
 
 		mModifyMask |= LLFriendObserver::ONLINE;
-- 
cgit v1.2.3


From 3fd86c2f23e288ec6754be0bf39bc452f6c0f3e1 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 5 Jan 2023 01:34:03 +0200
Subject: SL-18871 Debug setting's description is not scrollable

---
 indra/newview/llfloatersettingsdebug.cpp | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp
index 25cf8b2bf7..3c7f341613 100644
--- a/indra/newview/llfloatersettingsdebug.cpp
+++ b/indra/newview/llfloatersettingsdebug.cpp
@@ -208,7 +208,16 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)
         getChild<LLTextBox>("setting_name_txt")->setToolTip(controlp->getName());
         mComment->setVisible(true);
 
-		mComment->setText(controlp->getComment());
+        std::string old_text = mComment->getText();
+        std::string new_text = controlp->getComment();
+        // Don't setText if not nessesary, it will reset scroll
+        // This is a debug UI that reads from xml, there might
+        // be use cases where comment changes, but not the name
+        if (old_text != new_text)
+        {
+            mComment->setText(controlp->getComment());
+        }
+
 		spinner1->setMaxValue(F32_MAX);
 		spinner2->setMaxValue(F32_MAX);
 		spinner3->setMaxValue(F32_MAX);
-- 
cgit v1.2.3


From 73933fe778c5d29bfde2a3055b8ab21e5aa6b598 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 5 Jan 2023 18:21:53 +0200
Subject: SL-18894 The change of 'modify rights' is not recorded in IM history
 if the user is in DND mode

Revert of commit for SL-15401. Messages are supposed to handle 'mute' on their own.
---
 indra/newview/llcallingcard.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
index 193d368b83..df79043b00 100644
--- a/indra/newview/llcallingcard.cpp
+++ b/indra/newview/llcallingcard.cpp
@@ -650,8 +650,7 @@ void LLAvatarTracker::processChange(LLMessageSystem* msg)
 		{
 			if(mBuddyInfo.find(agent_id) != mBuddyInfo.end())
 			{
-                if (((mBuddyInfo[agent_id]->getRightsGrantedFrom() ^  new_rights) & LLRelationship::GRANT_MODIFY_OBJECTS)
-                    && !gAgent.isDoNotDisturb())
+                if (((mBuddyInfo[agent_id]->getRightsGrantedFrom() ^  new_rights) & LLRelationship::GRANT_MODIFY_OBJECTS))
 				{
 					LLSD args;
 					args["NAME"] = LLSLURL("agent", agent_id, "displayname").getSLURLString();
-- 
cgit v1.2.3


From d6f5e5bc9424b9d45f6eeeca5d894d46dc91b279 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Sat, 7 Jan 2023 01:10:22 +0200
Subject: SL-18911 [MAC] My Land Holdings floater crashes when not empty

---
 indra/newview/llfloaterlandholdings.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llfloaterlandholdings.cpp b/indra/newview/llfloaterlandholdings.cpp
index 8633fe4e5e..2f13c940ca 100644
--- a/indra/newview/llfloaterlandholdings.cpp
+++ b/indra/newview/llfloaterlandholdings.cpp
@@ -89,7 +89,7 @@ BOOL LLFloaterLandHoldings::postBuild()
 		LLUIString areastr = getString("area_string");
 		areastr.setArg("[AREA]", llformat("%d", gAgent.mGroups.at(i).mContribution));
 		element["columns"][1]["column"] = "area";
-		element["columns"][1]["value"] = areastr;
+		element["columns"][1]["value"] = areastr.getString();
 		element["columns"][1]["font"] = "SANSSERIF";
 
 		grant_list->addElement(element);
-- 
cgit v1.2.3


From 30678472ee9d25a738d71181c2b887f629cae790 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Sat, 7 Jan 2023 18:59:39 +0200
Subject: Revert "SL-18911 [MAC] My Land Holdings floater crashes when not
 empty"

This reverts commit d6f5e5bc9424b9d45f6eeeca5d894d46dc91b279.
---
 indra/newview/llfloaterlandholdings.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/llfloaterlandholdings.cpp b/indra/newview/llfloaterlandholdings.cpp
index 2f13c940ca..8633fe4e5e 100644
--- a/indra/newview/llfloaterlandholdings.cpp
+++ b/indra/newview/llfloaterlandholdings.cpp
@@ -89,7 +89,7 @@ BOOL LLFloaterLandHoldings::postBuild()
 		LLUIString areastr = getString("area_string");
 		areastr.setArg("[AREA]", llformat("%d", gAgent.mGroups.at(i).mContribution));
 		element["columns"][1]["column"] = "area";
-		element["columns"][1]["value"] = areastr.getString();
+		element["columns"][1]["value"] = areastr;
 		element["columns"][1]["font"] = "SANSSERIF";
 
 		grant_list->addElement(element);
-- 
cgit v1.2.3


From 007939f0a76fd2f596d4d4252578af3d30234b33 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 11 Jan 2023 07:44:12 +0200
Subject: SL-18939 Cannot copy inventory folders with copiable links

---
 indra/newview/llinventorybridge.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 7793b71f56..7a3b913ed5 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -2140,6 +2140,7 @@ bool LLItemBridge::isItemCopyable(bool can_copy_as_link) const
 
     static LLCachedControl<bool> inventory_linking(gSavedSettings, "InventoryLinking", true);
     return (can_copy_as_link && inventory_linking)
+        || (mIsLink && inventory_linking)
         || item->getPermissions().allowCopyBy(gAgent.getID());
 }
 
@@ -2346,6 +2347,12 @@ BOOL LLFolderBridge::isUpToDate() const
 
 bool LLFolderBridge::isItemCopyable(bool can_copy_as_link) const
 {
+    if (can_copy_as_link && !LLFolderType::lookupIsProtectedType(getPreferredType()))
+    {
+        // Can copy and paste unprotected folders as links
+        return true;
+    }
+
 	// Folders are copyable if items in them are, recursively, copyable.
 	
 	// Get the content of the folder
-- 
cgit v1.2.3


From b3201e75b1908dd5186d6561b6706f2cc07c92c3 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 11 Jan 2023 16:03:33 +0200
Subject: SL-18945 Links have no 'cut' option

Links can be drag and dropped so they should be movable via 'cut' as well
---
 indra/newview/llinventorybridge.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 7a3b913ed5..db347f7096 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -831,6 +831,12 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
 			{
 				disabled_items.push_back(std::string("Find Original"));
 			}
+
+            items.push_back(std::string("Cut"));
+            if (!isItemMovable() || !isItemRemovable())
+            {
+                disabled_items.push_back(std::string("Cut"));
+            }
 		}
 		else
 		{
-- 
cgit v1.2.3


From 71657b66f99301a602c7915b70b8b395902bf6ac Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Fri, 27 Jan 2023 22:04:11 +0200
Subject: Revert "SL-18581 Don't show the starter avatar toolbar button for
 NUX"

This reverts commit 4d429b7ea31f51f653e0e2ad6b5799a515e28334.
---
 indra/newview/lltoolbarview.cpp | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp
index 752fc6f3f3..01d799dcd5 100644
--- a/indra/newview/lltoolbarview.cpp
+++ b/indra/newview/lltoolbarview.cpp
@@ -44,7 +44,6 @@
 #include "llagent.h"  // HACK for destinations guide on startup
 #include "llfloaterreg.h"  // HACK for destinations guide on startup
 #include "llviewercontrol.h"  // HACK for destinations guide on startup
-#include "llinventorymodel.h" // HACK to disable starter avatars button for NUX
 
 #include <boost/foreach.hpp>
 
@@ -320,22 +319,6 @@ bool LLToolBarView::loadToolbars(bool force_default)
 			}
 		}
 	}
-
-    // SL-18581: Don't show the starter avatar toolbar button for NUX users
-    LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS));
-    if (gAgent.isFirstLogin()
-        && my_outfits_cat != NULL
-        && my_outfits_cat->getDescendentCount() > 0)
-    {
-        for (S32 i = LLToolBarEnums::TOOLBAR_FIRST; i <= LLToolBarEnums::TOOLBAR_LAST; i++)
-        {
-            if (mToolbars[i])
-            {
-                mToolbars[i]->removeCommand(LLCommandId("avatar"));
-            }
-        }
-    }
-
 	mToolbarsLoaded = true;
 	return true;
 }
-- 
cgit v1.2.3


From 8d21d29bd7fa038db632ff90fb0e1207d0713ca2 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Thu, 2 Feb 2023 14:33:39 -0500
Subject: Increment viewer version to 6.6.10 following promotion of DRTVWR-570

---
 indra/newview/VIEWER_VERSION.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index f5199477c5..0673dfa857 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-6.6.9
+6.6.10
-- 
cgit v1.2.3