diff --git a/.hgignore b/.hgignore
index 403b73df6d28a41d669ab8abd0236a1c3df808d0..501f9e6abe6fbe80f07c8688e75e09f855c29104 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,5 +1,6 @@
 syntax: glob
 
+
 # WinMerge temp files
 *.bak
 # Compiled python bytecode
@@ -67,3 +68,4 @@ glob:indra/newview/filters.xml
 glob:indra/newview/avatar_icons_cache.txt
 glob:indra/newview/avatar_lad.log
 glob:*.diff
+*.rej
diff --git a/.hgtags b/.hgtags
index 456d1d540f5b4d3d7acf102caf3e851c28146ad8..6ebdae7707f8fb6bf906e221b3c5d159b0c95d5b 100644
--- a/.hgtags
+++ b/.hgtags
@@ -271,18 +271,31 @@ e9c82fca5ae6fb8a8af29012d78fb194a29323f3 3.2.9-beta1
 a01ef9bed28627f4ca543fbc1d70c79cc297a90f DRTVWR-118_3.2.9-beta2
 a01ef9bed28627f4ca543fbc1d70c79cc297a90f 3.2.9-beta2
 987425b1acf4752379b2e1eb20944b4b35d67a85 3.2.8-beta2
+d5f263687f43f278107363365938f0a214920a4b DRTVWR-119
+d5f263687f43f278107363365938f0a214920a4b 3.3.0-beta1
+5e8d2662f38a66eca6c591295f5880d47afc73f7 viewer-release-candidate
+5e8d2662f38a66eca6c591295f5880d47afc73f7 3.3.0-release
 d5f263687f43f278107363365938f0a214920a4b 3.3.0-start
 dffd0457ee0745de65bf95f0642a5c9e46b8e2f0 viewer-beta-candidate
 d5f263687f43f278107363365938f0a214920a4b DRTVWR-119
 d5f263687f43f278107363365938f0a214920a4b 3.3.0-beta1
 5e8d2662f38a66eca6c591295f5880d47afc73f7 viewer-release-candidate
 5e8d2662f38a66eca6c591295f5880d47afc73f7 3.3.0-release
-dffd0457ee0745de65bf95f0642a5c9e46b8e2f0 viewer-beta-candidate
-3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 viewer-beta-candidate
-3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 viewer-beta-candidate
+28b95a6a28dca3338d9a1f4f204b96678df9f6a5 viewer-beta-candidate
+b43cd25be49e3984ff5361cefad020e069131d98 3.3.1-start
+3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 DRTVWR-125
+3e2fca4ed1a0dc9fe6d8a6664e71098bb035a367 3.3.1-start
+28b95a6a28dca3338d9a1f4f204b96678df9f6a5 3.3.1-beta1
 1dc545e44617975da2a4a32fe303386c687a6ca1 viewer-beta-candidate
 1dc545e44617975da2a4a32fe303386c687a6ca1 3.3.1-beta2
 1dc545e44617975da2a4a32fe303386c687a6ca1 DRTVWR-139
 5e8d2662f38a66eca6c591295f5880d47afc73f7 viewer-release-candidate
 c623bbc854b6f7ee1b33a3718f76715046aa2937 viewer-release-candidate
 c623bbc854b6f7ee1b33a3718f76715046aa2937 3.3.1-release
+d29a260119f8d5a5d168e25fed0c7ea6b3f40161 3.3.2-beta1
+675668bd24d3bea570814f71762a2a806f7e1b8d 3.3.2-beta2
+c623bbc854b6f7ee1b33a3718f76715046aa2937 viewer-release-candidate
+675668bd24d3bea570814f71762a2a806f7e1b8d viewer-release-candidate
+675668bd24d3bea570814f71762a2a806f7e1b8d 3.3.2-release
+675668bd24d3bea570814f71762a2a806f7e1b8d viewer-release-candidate
+15e90b52dc0297921b022b90d10d797436b8a1bd viewer-release-candidate
diff --git a/BuildParams b/BuildParams
index 1c39dd7cc754b0fb7ed0c1e1aa0fb43064c6c37d..dccca834f1de53ca1d1d441a1f5a264d872ab929 100644
--- a/BuildParams
+++ b/BuildParams
@@ -35,6 +35,7 @@ viewer-development.build_debug_release_separately = true
 # Notifications - to configure email notices, add a setting like this:
 # <username>_<reponame>.email = <email-address>
 
+
 # =================================================================
 # Canonical viewer integration builds - Oz Linden
 # =================================================================
@@ -180,4 +181,35 @@ simon_viewer-dev-private.email_status_this_is_os = false
 vir-project-1.viewer_channel = "Second Life Release"
 vir-project-1.login_channel = "Second Life Release"
 
+# ========================================
+# THX-1138 / Runway projects
+# ========================================
+viewer-thx1138-runway-shared.viewer_channel = "Project Viewer - THX-1138 Runway"
+viewer-thx1138-runway-shared.login_channel = "Project Viewer - THX-1138 Runway"
+viewer-thx1138-runway-shared.viewer_grid = uma
+viewer-thx1138-runway-shared.build_debug_release_separately = true
+viewer-thx1138-runway-shared.build_CYGWIN_Debug = false
+viewer-thx1138-runway-shared.build_viewer_update_version_manager = false
+
+viewer-thx1138.viewer_channel = "Project Viewer - THX-1138"
+viewer-thx1138.login_channel = "Project Viewer - THX-1138"
+viewer-thx1138.viewer_grid = uma
+viewer-thx1138.build_debug_release_separately = true
+viewer-thx1138.build_CYGWIN_Debug = false
+viewer-thx1138.build_viewer_update_version_manager = false
+
+runway-merge.viewer_channel = "Project Viewer - Runway Merge"
+runway-merge.login_channel = "Project Viewer - Runway Merge"
+runway-merge.viewer_grid = agni
+runway-merge.build_debug_release_separately = true
+runway-merge.build_CYGWIN_Debug = false
+runway-merge.build_viewer_update_version_manager = false
+
+runway.viewer_channel = "Project Viewer - Runway"
+runway.login_channel = "Project Viewer - Runway"
+runway.viewer_grid = agni
+runway.build_debug_release_separately = true
+runway.build_CYGWIN_Debug = false
+runway.build_viewer_update_version_manager = false
+
 # eof
diff --git a/autobuild.xml b/autobuild.xml
index 9914be68678a06e049e85f7a5fe424102645eb94..d58c4a350cbcca0e5a9998e8f8d2d7306b7625fd 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -90,9 +90,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>9868bfa0b6954e4884c49c6f30068c80</string>
+              <string>1fe7c2121916b2c5ff75073742442e2a</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.4.2-darwin-20110217.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-apr/rev/257886/arch/Darwin/installer/apr_suite-1.4.5-darwin-20120523.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin</string>
@@ -102,9 +102,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>ff62946c518a247c86e1066c1e9a5855</string>
+              <string>eac7926a7caa121144c911434a01b680</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.4.2-linux-20110309.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-apr/rev/257886/arch/Linux/installer/apr_suite-1.4.5-linux-20120523.tar.bz2</string>
             </map>
             <key>name</key>
             <string>linux</string>
@@ -114,9 +114,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>73785c200a5b4ef74a1230b028bb680d</string>
+              <string>8789d952a33ce3e8b87be8cbf94bc6ba</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/apr_suite-1.4.2-windows-20110217.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-apr/rev/257886/arch/CYGWIN/installer/apr_suite-1.4.5-windows-20120523.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -1206,9 +1206,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>26aa7c367ffadd573f61a6a96f820f80</string>
+              <string>4a98d727561cd1f4ac5ee02907411df1</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/245988/arch/Darwin/installer/llqtwebkit-4.7.1-darwin-20111201.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/250147/arch/Darwin/installer/llqtwebkit-4.7.1-darwin-20120228.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin</string>
@@ -1218,9 +1218,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>c05a33ee8b6f253b5a744596dfc3707d</string>
+              <string>f50e5f0cc880c55b3f0f7e67dc8f7221</string>
               <key>url</key>
-              <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-linux-qt4.6-20101013.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/250147/arch/Linux/installer/llqtwebkit-4.7.1-linux-20120228.tar.bz2</string>
             </map>
             <key>name</key>
             <string>linux</string>
@@ -1230,9 +1230,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>270db8568a0c4bab266d98e1a820aec4</string>
+              <string>5e3cd6af397e853a963a6de40d440ff4</string>
               <key>url</key>
-              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/245988/arch/CYGWIN/installer/llqtwebkit-4.7.1-windows-20111201.tar.bz2</string>
+              <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llqtwebkit/rev/250147/arch/CYGWIN/installer/llqtwebkit-4.7.1-windows-20120228.tar.bz2</string>
             </map>
             <key>name</key>
             <string>windows</string>
diff --git a/build.sh b/build.sh
index c7c89fe3c2bdc40376b04230c94f1e8defe4937a..8ca3208087f6a4729a1f8a991e86f08832a40a26 100755
--- a/build.sh
+++ b/build.sh
@@ -132,10 +132,6 @@ if test -f scripts/update_version_files.py ; then
   end_section UpdateVer
 fi
 
-# Now retrieve the version for use in the version manager
-# First three parts only, $revision will be appended automatically.
-build_viewer_update_version_manager_version=`python scripts/get_version.py --viewer-version | sed 's/\.[0-9]*$//'`
-
 if [ -z "$AUTOBUILD" ]
 then
   export autobuild_dir="$here/../../../autobuild/bin/"
@@ -177,9 +173,6 @@ eval "$("$AUTOBUILD" source_environment)"
 env|sort
 
 
-# Install packages.
-"$AUTOBUILD" install --skip-license-check
-
 # Now run the build
 succeeded=true
 build_processes=
@@ -195,10 +188,19 @@ do
   begin_section "Do$variant"
   build_dir=`build_dir_$arch $variant`
   build_dir_stubs="$build_dir/win_setup/$variant"
+
+  begin_section "PreClean"
   rm -rf "$build_dir"
+  end_section "PreClean"
+
   mkdir -p "$build_dir"
   mkdir -p "$build_dir/tmp"
-  #export TMP="$build_dir/tmp"
+
+  # Install packages.
+  begin_section "AutobuildInstall" 
+  "$AUTOBUILD" install --verbose --skip-license-check
+  end_section "AutobuildInstall" 
+
   if pre_build "$variant" "$build_dir" >> "$build_log" 2>&1
   then
     if $build_link_parallel
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 3851e62cfbdae7aa8137087a910a1adc6ae8fc21..18df538d2a9f66e246f1824ae10ed75993369c52 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -234,7 +234,7 @@ Borg Capalini
 Boroondas Gupte
 	OPEN-29
 	OPEN-39
-	OPEN-39
+	OPEN-54
 	OPEN-99
 	SNOW-278
 	SNOW-503
@@ -396,6 +396,8 @@ Frontera Thor
 Fury Rosewood
 Gaberoonie Zanzibar
 Ganymedes Costagravas
+Geenz Spad
+	STORM-1823
 Gene Frostbite
 GeneJ Composer
 Geneko Nemeth
@@ -622,6 +624,8 @@ Jonathan Yap
 	STORM-1796
 	STORM-1807
 	STORM-1808
+	STORM-637
+	STORM-1822
 	STORM-1809
 	STORM-1793
 	STORM-1810
@@ -868,6 +872,7 @@ Nicky Perian
 	OPEN-1
 	STORM-1087
 	STORM-1090
+	STORM-1828
 Nicoladie Gymnast
 Nounouch Hapmouche
 	VWR-238
@@ -1155,9 +1160,9 @@ Tofu Buzzard
 	CTS-411
 	STORM-546
 	VWR-24509
-    STORM-1684
 	SH-2477
 	STORM-1684
+	STORM-1819
 Tony Kembia
 Torben Trautman
 TouchaHoney Perhaps
@@ -1187,6 +1192,8 @@ Unlikely Quintessa
 UsikuFarasi Kanarik
 Vadim Bigbear
 	VWR-2681
+Vaalith Jinn
+    STORM-64
 Vector Hastings
 	VWR-8726
 Veritas Raymaker
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 4b1bf49d07313f1656ba48eaf8f2373b5241b022..1cebb53a07df035e68d4334f2290a06dea2fc361 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -66,7 +66,6 @@ if (VIEWER)
   add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger)
   add_subdirectory(${LIBS_OPEN_PREFIX}llplugin)
   add_subdirectory(${LIBS_OPEN_PREFIX}llui)
-  add_subdirectory(${LIBS_OPEN_PREFIX}llxuixml)
   add_subdirectory(${LIBS_OPEN_PREFIX}viewer_components)
 
   # Legacy C++ tests. Build always, run if LL_TESTS is true.
diff --git a/indra/cmake/JsonCpp.cmake b/indra/cmake/JsonCpp.cmake
index 499b00fb444328bfc46f2b2493a7de4b3216744d..7ad73e56830a50869ef90bd988db47240d06235b 100644
--- a/indra/cmake/JsonCpp.cmake
+++ b/indra/cmake/JsonCpp.cmake
@@ -18,5 +18,5 @@ else (STANDALONE)
   elseif (LINUX)
     set(JSONCPP_LIBRARIES libjson_linux-gcc-4.1.3_libmt.a)
   endif (WINDOWS)
-  set(JSONCPP_INCLUDE_DIRS "${LIBS_PREBUILT_DIR}/include/jsoncpp" "${LIBS_PREBUILT_DIR}/include/json")
+  set(JSONCPP_INCLUDE_DIR "${LIBS_PREBUILT_DIR}/include/jsoncpp" "${LIBS_PREBUILT_DIR}/include/json")
 endif (STANDALONE)
diff --git a/indra/cmake/LLTestCommand.cmake b/indra/cmake/LLTestCommand.cmake
index b5a0580a904f6d93ff8c765e02e4e031555fed40..f75c23a5de26796831a7f142fad4b1ccaf2e027b 100644
--- a/indra/cmake/LLTestCommand.cmake
+++ b/indra/cmake/LLTestCommand.cmake
@@ -9,6 +9,9 @@ MACRO(LL_TEST_COMMAND OUTVAR LD_LIBRARY_PATH)
   FOREACH(dir ${LD_LIBRARY_PATH})
     LIST(APPEND value "-l${dir}")
   ENDFOREACH(dir)
+  # Enough different tests want to be able to find CMake's PYTHON_EXECUTABLE
+  # that we should just pop it into the environment for everybody.
+  LIST(APPEND value "-DPYTHON=${PYTHON_EXECUTABLE}")
   LIST(APPEND value ${ARGN})
   SET(${OUTVAR} ${value})
 ##IF(LL_TEST_VERBOSE)
diff --git a/indra/cmake/Linking.cmake b/indra/cmake/Linking.cmake
index c5f9e2c57911298aa37a712c91ad04e12ce6e37a..47f944f9a5dd377ac5ba12b04ab107a07b7e2d1d 100644
--- a/indra/cmake/Linking.cmake
+++ b/indra/cmake/Linking.cmake
@@ -13,7 +13,7 @@ elseif (LINUX)
   set(EXE_STAGING_DIR ${CMAKE_BINARY_DIR}/sharedlibs/bin)
 elseif (DARWIN)
   set(SHARED_LIB_STAGING_DIR ${CMAKE_BINARY_DIR}/sharedlibs)
-  set(EXE_STAGING_DIR "${CMAKE_BINARY_DIR}/sharedlibs/\$(CONFIGURATION)")
+  set(EXE_STAGING_DIR "${CMAKE_BINARY_DIR}/sharedlibs")
 endif (WINDOWS)
 
 # Autobuild packages must provide 'release' versions of libraries, but may provide versions for
@@ -33,7 +33,14 @@ else(WINDOWS OR DARWIN)
   set(AUTOBUILD_LIBS_INSTALL_DIRS ${AUTOBUILD_INSTALL_DIR}/lib/${CMAKE_BUILD_TYPE_LOWER})
 endif(WINDOWS OR DARWIN)
 
-list(APPEND AUTOBUILD_LIBS_INSTALL_DIRS ${ARCH_PREBUILT_DIRS_RELEASE})
+if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release")
+  # When we're building something other than Release, append the
+  # packages/lib/release directory to deal with autobuild packages that don't
+  # provide (e.g.) lib/debug libraries.
+  list(APPEND AUTOBUILD_LIBS_INSTALL_DIRS ${ARCH_PREBUILT_DIRS_RELEASE})
+  message(STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}, extending AUTOBUILD_LIBS_INSTALL_DIRS")
+endif (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release")
+message(STATUS "For ${CMAKE_BUILD_TYPE}, AUTOBUILD_LIBS_INSTALL_DIRS: ${AUTOBUILD_LIBS_INSTALL_DIRS}")
 link_directories(${AUTOBUILD_LIBS_INSTALL_DIRS})
 
 if (LINUX)
diff --git a/indra/cmake/VisualLeakDetector.cmake b/indra/cmake/VisualLeakDetector.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..d3ba554e462621cbc6bff6d0d670f0a89055f01e
--- /dev/null
+++ b/indra/cmake/VisualLeakDetector.cmake
@@ -0,0 +1,15 @@
+# -*- cmake -*-
+
+if (VIEWER)
+
+  set(INCLUDE_VLD_CMAKE OFF CACHE BOOL "Build the Windows viewer with Visual Leak Detector turned on or off")
+
+  if (INCLUDE_VLD_CMAKE)
+
+    if (WINDOWS)
+      add_definitions(-DINCLUDE_VLD=1)
+    endif (WINDOWS)
+
+  endif (INCLUDE_VLD_CMAKE)
+
+endif (VIEWER)
diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake
index 91b49e75d737973b81bed22728f15e63685acef3..d9df78bfc8fdec62c4261b6c74208c047e999e51 100644
--- a/indra/cmake/WebKitLibPlugin.cmake
+++ b/indra/cmake/WebKitLibPlugin.cmake
@@ -70,9 +70,10 @@ elseif (LINUX)
         QtNetwork
         QtGui
         QtCore
-        qgif
-        qjpeg
-        jpeg
+        jscore
+#        qgif
+#        qjpeg
+#        jpeg
         fontconfig
         X11
         Xrender
diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py
index ce2d1e0386b4a3ca3d36623d0b5be6b5a974e491..a2ef61c8fd5037172d77b7314458fd50673beb36 100644
--- a/indra/cmake/run_build_test.py
+++ b/indra/cmake/run_build_test.py
@@ -46,6 +46,7 @@ $/LicenseInfo$
 
 import os
 import sys
+import signal
 import subprocess
 
 def main(command, libpath=[], vars={}):
@@ -113,6 +114,33 @@ def main(command, libpath=[], vars={}):
     sys.stdout.flush()
     return subprocess.call(command)
 
+# swiped from vita, sigh, seems like a Bad Idea to introduce dependency
+def translate_rc(rc):
+    """
+    Accept an rc encoded as for subprocess.Popen.returncode:
+    None means still running
+    int >= 0 means terminated voluntarily with specified rc
+    int <  0 means terminated by signal (-rc)
+
+    Return a string explaining the outcome. In case of a signal, try to
+    name the corresponding symbol from the 'signal' module.
+    """
+    if rc is None:
+        return "still running"
+    
+    if rc >= 0:
+        return "terminated with rc %s" % rc
+
+    # Negative rc means the child was terminated by signal -rc.
+    rc = -rc
+    for attr in dir(signal):
+        if attr.startswith('SIG') and getattr(signal, attr) == rc:
+            strc = attr
+            break
+    else:
+        strc = str(rc)
+    return "terminated by signal %s" % strc
+
 if __name__ == "__main__":
     from optparse import OptionParser
     parser = OptionParser(usage="usage: %prog [options] command args...")
@@ -140,5 +168,5 @@ if __name__ == "__main__":
               vars=dict([(pair.split('=', 1) + [""])[:2] for pair in opts.vars]))
     if rc not in (None, 0):
         print >>sys.stderr, "Failure running: %s" % " ".join(args)
-        print >>sys.stderr, "Error: %s" % rc
+        print >>sys.stderr, "Error %s: %s" % (rc, translate_rc(rc))
     sys.exit((rc < 0) and 255 or rc)
diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
index 48e876429dfaac10b7e3085c3d4eb1324aa3b7ec..36c5b6782618b63a5c35d2ba0174caa898071712 100644
--- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
+++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp
@@ -54,6 +54,11 @@ static const char USAGE[] = "\n"
 " -o, --output <file1 .. file2> OR <type>\n"
 "        List of image files to create (assumes same order as for input files)\n"
 "        OR 3 letters file type extension to convert each input file into.\n"
+" -load, --load_size <n>\n"
+"        Portion of the input file to load, in bytes."
+"        If (load == 0), it will load the whole file."
+"        If (load == -1), it will load the size relevant to reach the requested discard level (see -d)."
+"        Only valid for j2c images. Default is 0 (load whole file).\n"
 " -r, --region <x0, y0, x1, y1>\n"
 "        Crop region applied to the input files in pixels.\n"
 "        Only used for j2c images. Default is no region cropping.\n"
@@ -104,22 +109,52 @@ void output_image_stats(LLPointer<LLImageFormatted> image, const std::string &fi
 	// Print out some statistical data on the image
 	std::cout << "Image stats for : " << filename << ", extension : " << image->getExtension() << std::endl;
 
-	std::cout << "    with : " << (int)(image->getWidth())       << ", height : " << (int)(image->getHeight())       << std::endl;
-	std::cout << "    comp : " << (int)(image->getComponents())  << ", levels : " << (int)(image->getDiscardLevel()) << std::endl;
-	std::cout << "    head : " << (int)(image->calcHeaderSize()) << ",   data : " << (int)(image->getDataSize())     << std::endl;
+	std::cout << "    with : " << (int)(image->getWidth())       << ", height : " << (int)(image->getHeight())   << std::endl;
+	std::cout << "    comp : " << (int)(image->getComponents())  << ", levels : " << (int)(image->getLevels())   << std::endl;
+	std::cout << "    head : " << (int)(image->calcHeaderSize()) << ",   data : " << (int)(image->getDataSize()) << std::endl;
 
 	return;
 }
 
 // Load an image from file and return a raw (decompressed) instance of its data
-LLPointer<LLImageRaw> load_image(const std::string &src_filename, int discard_level, int* region, bool output_stats)
+LLPointer<LLImageRaw> load_image(const std::string &src_filename, int discard_level, int* region, int load_size, bool output_stats)
 {
 	LLPointer<LLImageFormatted> image = create_image(src_filename);
 	
-	// This just loads the image file stream into a buffer. No decoding done.
-	if (!image->load(src_filename))
+	// We support partial loading only for j2c images
+	if (image->getCodec() == IMG_CODEC_J2C)
 	{
-		return NULL;
+		// Load the header
+		if (!image->load(src_filename, 600))
+		{
+			return NULL;
+		}
+		S32 h = ((LLImageJ2C*)(image.get()))->calcHeaderSize();
+		S32 d = (load_size > 0 ? ((LLImageJ2C*)(image.get()))->calcDiscardLevelBytes(load_size) : 0);
+		S8  r = ((LLImageJ2C*)(image.get()))->getRawDiscardLevel();
+		std::cout << "Merov debug : header = " << h << ", load_size = " << load_size << ", discard level = " << d << ", raw discard level = " << r << std::endl;
+		for (d = 0; d < MAX_DISCARD_LEVEL; d++)
+		{
+			S32 data_size = ((LLImageJ2C*)(image.get()))->calcDataSize(d);
+			std::cout << "Merov debug : discard_level = " << d << ", data_size = " << data_size << std::endl;
+		}
+		if (load_size < 0)
+		{
+			load_size = (discard_level != -1 ? ((LLImageJ2C*)(image.get()))->calcDataSize(discard_level) : 0);
+		}
+		// Load the requested byte range
+		if (!image->load(src_filename, load_size))
+		{
+			return NULL;
+		}
+	}
+	else 
+	{
+		// This just loads the image file stream into a buffer. No decoding done.
+		if (!image->load(src_filename))
+		{
+			return NULL;
+		}
 	}
 	
 	if(	(image->getComponents() != 3) && (image->getComponents() != 4) )
@@ -310,6 +345,7 @@ int main(int argc, char** argv)
 	bool image_stats = false;
 	int* region = NULL;
 	int discard_level = -1;
+	int load_size = 0;
 	int precincts_size = -1;
 	int blocks_size = -1;
 	int levels = 0;
@@ -396,6 +432,22 @@ int main(int argc, char** argv)
 				discard_level = llclamp(discard_level,0,5);
 			}
 		}
+		else if (!strcmp(argv[arg], "--load_size") || !strcmp(argv[arg], "-load"))
+		{
+			std::string value_str;
+			if ((arg + 1) < argc)
+			{
+				value_str = argv[arg+1];
+			}
+			if (((arg + 1) >= argc) || (value_str[0] == '-'))
+			{
+				std::cout << "No valid --load_size argument given, load_size ignored" << std::endl;
+			}
+			else
+			{
+				load_size = atoi(value_str.c_str());
+			}
+		}
 		else if (!strcmp(argv[arg], "--precincts") || !strcmp(argv[arg], "-p"))
 		{
 			std::string value_str;
@@ -510,7 +562,7 @@ int main(int argc, char** argv)
 	for (; in_file != in_end; ++in_file, ++out_file)
 	{
 		// Load file
-		LLPointer<LLImageRaw> raw_image = load_image(*in_file, discard_level, region, image_stats);
+		LLPointer<LLImageRaw> raw_image = load_image(*in_file, discard_level, region, load_size, image_stats);
 		if (!raw_image)
 		{
 			std::cout << "Error: Image " << *in_file << " could not be loaded" << std::endl;
diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt
index 1180460f4b8ff2563e1cd580497ecc1efca64302..633ad84159e68403bdc123152dd50509ecf851e6 100644
--- a/indra/integration_tests/llui_libtest/CMakeLists.txt
+++ b/indra/integration_tests/llui_libtest/CMakeLists.txt
@@ -18,7 +18,6 @@ include(LLWindow)
 include(LLUI)
 include(LLVFS)        # ugh, needed for LLDir
 include(LLXML)
-include(LLXUIXML)
 include(Linking)
 # include(Tut)
 
@@ -32,7 +31,6 @@ include_directories(
     ${LLVFS_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
-    ${LLXUIXML_INCLUDE_DIRS}
     )
 
 set(llui_libtest_SOURCE_FILES
diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py
index c33a03034aa3144f9407cb4eb2b8d116c004b7d4..a4fb77357c68a7397607e02bfa8127a3a1b7ea9d 100644
--- a/indra/lib/python/indra/util/llmanifest.py
+++ b/indra/lib/python/indra/util/llmanifest.py
@@ -41,6 +41,14 @@ import tarfile
 import errno
 import subprocess
 
+class ManifestError(RuntimeError):
+    """Use an exception more specific than generic Python RuntimeError"""
+    pass
+
+class MissingError(ManifestError):
+    """You specified a file that doesn't exist"""
+    pass
+
 def path_ancestors(path):
     drive, path = os.path.splitdrive(os.path.normpath(path))
     result = []
@@ -180,6 +188,9 @@ def usage(srctree=""):
             arg['description'] % nd)
 
 def main():
+##  import itertools
+##  print ' '.join((("'%s'" % item) if ' ' in item else item)
+##                 for item in itertools.chain([sys.executable], sys.argv))
     option_names = [arg['name'] + '=' for arg in ARGUMENTS]
     option_names.append('help')
     options, remainder = getopt.getopt(sys.argv[1:], "", option_names)
@@ -385,7 +396,7 @@ class LLManifest(object):
         child.stdout.close()
         status = child.wait()
         if status:
-            raise RuntimeError(
+            raise ManifestError(
                 "Command %s returned non-zero status (%s) \noutput:\n%s"
                 % (command, status, output) )
         return output
@@ -395,7 +406,7 @@ class LLManifest(object):
           a) verify that you really have created it
           b) schedule it for cleanup"""
         if not os.path.exists(path):
-            raise RuntimeError, "Should be something at path " + path
+            raise ManifestError, "Should be something at path " + path
         self.created_paths.append(path)
 
     def put_in_file(self, contents, dst):
@@ -550,7 +561,7 @@ class LLManifest(object):
             except (IOError, os.error), why:
                 errors.append((srcname, dstname, why))
         if errors:
-            raise RuntimeError, errors
+            raise ManifestError, errors
 
 
     def cmakedirs(self, path):
@@ -598,11 +609,10 @@ class LLManifest(object):
 
     def check_file_exists(self, path):
         if not os.path.exists(path) and not os.path.islink(path):
-            raise RuntimeError("Path %s doesn't exist" % (
-                os.path.normpath(os.path.join(os.getcwd(), path)),))
+            raise MissingError("Path %s doesn't exist" % (os.path.abspath(path),))
 
 
-    wildcard_pattern = re.compile('\*')
+    wildcard_pattern = re.compile(r'\*')
     def expand_globs(self, src, dst):
         src_list = glob.glob(src)
         src_re, d_template = self.wildcard_regex(src.replace('\\', '/'),
@@ -615,7 +625,7 @@ class LLManifest(object):
         sys.stdout.write("Processing %s => %s ... " % (src, dst))
         sys.stdout.flush()
         if src == None:
-            raise RuntimeError("No source file, dst is " + dst)
+            raise ManifestError("No source file, dst is " + dst)
         if dst == None:
             dst = src
         dst = os.path.join(self.get_dst_prefix(), dst)
@@ -637,13 +647,23 @@ class LLManifest(object):
                 else:
                     count += self.process_file(src, dst)
             return count
-        try:
-            count = try_path(os.path.join(self.get_src_prefix(), src))
-        except RuntimeError:
+
+        for pfx in self.get_src_prefix(), self.get_artwork_prefix(), self.get_build_prefix():
             try:
-                count = try_path(os.path.join(self.get_artwork_prefix(), src))
-            except RuntimeError:
-                count = try_path(os.path.join(self.get_build_prefix(), src))
+                count = try_path(os.path.join(pfx, src))
+            except MissingError:
+                # If src isn't a wildcard, and if that file doesn't exist in
+                # this pfx, try next pfx.
+                count = 0
+                continue
+
+            # Here try_path() didn't raise MissingError. Did it process any files?
+            if count:
+                break
+            # Even though try_path() didn't raise MissingError, it returned 0
+            # files. src is probably a wildcard meant for some other pfx. Loop
+            # back to try the next.
+
         print "%d files" % count
 
     def do(self, *actions):
diff --git a/indra/linux_updater/CMakeLists.txt b/indra/linux_updater/CMakeLists.txt
index 00a78b2a8f7d7a0184cd5ec78a402f71a4ae9872..4377a6333c37e53f993bfd5fd71f89df58b1c547 100644
--- a/indra/linux_updater/CMakeLists.txt
+++ b/indra/linux_updater/CMakeLists.txt
@@ -10,14 +10,14 @@ include(UI)
 include(LLCommon)
 include(LLVFS)
 include(LLXML)
-include(LLXUIXML)
+include(LLUI)
 include(Linking)
 
 include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLVFS_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
-    ${LLXUIXML_INCLUDE_DIRS}
+    ${LLUI_INCLUDE_DIRS}
     ${CURL_INCLUDE_DIRS}
     ${CARES_INCLUDE_DIRS}
     ${OPENSSL_INCLUDE_DIRS}
@@ -42,7 +42,7 @@ target_link_libraries(linux-updater
     ${CRYPTO_LIBRARIES}
     ${UI_LIBRARIES}
     ${LLXML_LIBRARIES}
-    ${LLXUIXML_LIBRARIES}
+    ${LLUI_LIBRARIES}
     ${LLVFS_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
     )
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 0a3eaec5c53ad155d59cb68764e35f40b7e6b257..dd7b8c6eb830206f757b441f4f27d18f6cfa8273 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -61,7 +61,10 @@ set(llcommon_SOURCE_FILES
     llformat.cpp
     llframetimer.cpp
     llheartbeat.cpp
+    llinitparam.cpp
     llinstancetracker.cpp
+    llleap.cpp
+    llleaplistener.cpp
     llliveappconfig.cpp
     lllivefile.cpp
     lllog.cpp
@@ -74,13 +77,14 @@ set(llcommon_SOURCE_FILES
     llmortician.cpp
     lloptioninterface.cpp
     llptrto.cpp 
-    llprocesslauncher.cpp
+    llprocess.cpp
     llprocessor.cpp
     llqueuedthread.cpp
     llrand.cpp
     llrefcount.cpp
     llrun.cpp
     llsd.cpp
+    llsdparam.cpp
     llsdserialize.cpp
     llsdserialize_xml.cpp
     llsdutil.cpp
@@ -88,6 +92,7 @@ set(llcommon_SOURCE_FILES
     llsingleton.cpp
     llstat.cpp
     llstacktrace.cpp
+    llstreamqueue.cpp
     llstreamtools.cpp
     llstring.cpp
     llstringtable.cpp
@@ -173,9 +178,12 @@ set(llcommon_HEADER_FILES
     llheartbeat.h
     llhttpstatuscodes.h
     llindexedqueue.h
+    llinitparam.h
     llinstancetracker.h
     llkeythrottle.h
     lllazy.h
+    llleap.h
+    llleaplistener.h
     lllistenerwrapper.h
     lllinkedqueue.h
     llliveappconfig.h
@@ -196,7 +204,7 @@ set(llcommon_HEADER_FILES
     llpointer.h
     llpreprocessor.h
     llpriqueuemap.h
-    llprocesslauncher.h
+    llprocess.h
     llprocessor.h
     llptrskiplist.h
     llptrskipmap.h
@@ -204,10 +212,12 @@ set(llcommon_HEADER_FILES
     llqueuedthread.h
     llrand.h
     llrefcount.h
+    llregistry.h
     llrun.h
     llrefcount.h
     llsafehandle.h
     llsd.h
+    llsdparam.h
     llsdserialize.h
     llsdserialize_xml.h
     llsdutil.h
@@ -216,11 +226,13 @@ set(llcommon_HEADER_FILES
     llsingleton.h
     llskiplist.h
     llskipmap.h
+    llsortedvector.h
     llstack.h
     llstacktrace.h
     llstat.h
     llstatenums.h
     llstl.h
+    llstreamqueue.h
     llstreamtools.h
     llstrider.h
     llstring.h
@@ -230,6 +242,7 @@ set(llcommon_HEADER_FILES
     llthreadsafequeue.h
     lltimer.h
     lltreeiterators.h
+    lltypeinfolookup.h
     lluri.h
     lluuid.h
     lluuidhashmap.h
@@ -317,8 +330,7 @@ if (LL_TESTS)
   LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
-  LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}"
-                          "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/setpython.py")
+  LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")                          
   LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
@@ -326,6 +338,9 @@ if (LL_TESTS)
   LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
+  LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
 
   # *TODO - reenable these once tcmalloc libs no longer break the build.
   #ADD_BUILD_TEST(llallocator llcommon)
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index e4381dbbd64de5372d288cff8114a928b5349747..7e6eee0f3ccc679cc87fcddc140c5bff02f2cda7 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -654,9 +654,7 @@ namespace LLError
 		g.invalidateCallSites();
 		s.tagLevelMap[tag_name] = level;
 	}
-}
 
-namespace {
 	LLError::ELevel decodeLevel(std::string name)
 	{
 		static LevelMap level_names;
@@ -681,7 +679,9 @@ namespace {
 		
 		return i->second;
 	}
-	
+}
+
+namespace {
 	void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level)
 	{
 		LLSD::array_const_iterator i, end;
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index ed9de002f5662418b62346bf321e52f454725b79..d53a819d88359801fc35f29f389dc961b2b77743 100644
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -1,4 +1,4 @@
-/** 
+/**
  * @file llerrorcontrol.h
  * @date   December 2006
  * @brief error message system control
@@ -6,21 +6,21 @@
  * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2010, 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$
  */
@@ -38,7 +38,7 @@ class LLSD;
 	This is the part of the LLError namespace that manages the messages
 	produced by the logging.  The logging support is defined in llerror.h.
 	Most files do not need to include this.
-	
+
 	These implementations are in llerror.cpp.
 */
 
@@ -72,7 +72,7 @@ namespace LLError
 		Settings that control what is logged.
 		Setting a level means log messages at that level or above.
 	*/
-	
+
 	LL_COMMON_API void setPrintLocation(bool);
 	LL_COMMON_API void setDefaultLevel(LLError::ELevel);
 	LL_COMMON_API ELevel getDefaultLevel();
@@ -80,7 +80,8 @@ namespace LLError
 	LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel);
 	LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel);
 	LL_COMMON_API void setTagLevel(const std::string& file_name, LLError::ELevel);
-	
+
+	LL_COMMON_API LLError::ELevel decodeLevel(std::string name);
 	LL_COMMON_API void configure(const LLSD&);
 		// the LLSD can configure all of the settings
 		// usually read automatically from the live errorlog.xml file
@@ -100,31 +101,31 @@ namespace LLError
 		// (by, for example, setting a class level to LEVEL_NONE), will keep
 		// the that message from causing the fatal funciton to be invoked.
 
-    LL_COMMON_API FatalFunction getFatalFunction();
-        // Retrieve the previously-set FatalFunction
-
-    /// temporarily override the FatalFunction for the duration of a
-    /// particular scope, e.g. for unit tests
-    class LL_COMMON_API OverrideFatalFunction
-    {
-    public:
-        OverrideFatalFunction(const FatalFunction& func):
-            mPrev(getFatalFunction())
-        {
-            setFatalFunction(func);
-        }
-        ~OverrideFatalFunction()
-        {
-            setFatalFunction(mPrev);
-        }
-
-    private:
-        FatalFunction mPrev;
-    };
+	LL_COMMON_API FatalFunction getFatalFunction();
+		// Retrieve the previously-set FatalFunction
+
+	/// temporarily override the FatalFunction for the duration of a
+	/// particular scope, e.g. for unit tests
+	class LL_COMMON_API OverrideFatalFunction
+	{
+	public:
+		OverrideFatalFunction(const FatalFunction& func):
+			mPrev(getFatalFunction())
+		{
+			setFatalFunction(func);
+		}
+		~OverrideFatalFunction()
+		{
+			setFatalFunction(mPrev);
+		}
+
+	private:
+		FatalFunction mPrev;
+	};
 
 	typedef std::string (*TimeFunction)();
 	LL_COMMON_API std::string utcTime();
-	
+
 	LL_COMMON_API void setTimeFunction(TimeFunction);
 		// The function is use to return the current time, formatted for
 		// display by those error recorders that want the time included.
@@ -136,19 +137,27 @@ namespace LLError
 		// An object that handles the actual output or error messages.
 	public:
 		virtual ~Recorder();
-		
+
 		virtual void recordMessage(LLError::ELevel, const std::string& message) = 0;
 			// use the level for better display, not for filtering
-			
+
 		virtual bool wantsTime(); // default returns false
 			// override and return true if the recorder wants the time string
 			// included in the text of the message
 	};
-	
+
+	/**
+	 * @NOTE: addRecorder() conveys ownership to the underlying Settings
+	 * object -- when destroyed, it will @em delete the passed Recorder*!
+	 */
 	LL_COMMON_API void addRecorder(Recorder*);
+	/**
+	 * @NOTE: removeRecorder() reclaims ownership of the Recorder*: its
+	 * lifespan becomes the caller's problem.
+	 */
 	LL_COMMON_API void removeRecorder(Recorder*);
 		// each error message is passed to each recorder via recordMessage()
-	
+
 	LL_COMMON_API void logToFile(const std::string& filename);
 	LL_COMMON_API void logToFixedBuffer(LLLineBuffer*);
 		// Utilities to add recorders for logging to a file or a fixed buffer
@@ -166,10 +175,9 @@ namespace LLError
 	class Settings;
 	LL_COMMON_API Settings* saveAndResetSettings();
 	LL_COMMON_API void restoreSettings(Settings *);
-		
+
 	LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
 	LL_COMMON_API int shouldLogCallCount();
-	
 };
 
 #endif // LL_LLERRORCONTROL_H
diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp
index 902eaa3b72a7d1ee8dece713b7d15c87bff45cc6..950fcd6e8392e52c437c647b15c2522fa07b3976 100644
--- a/indra/llcommon/llerrorthread.cpp
+++ b/indra/llcommon/llerrorthread.cpp
@@ -112,13 +112,8 @@ void LLErrorThread::run()
 #if !LL_WINDOWS
 	U32 last_sig_child_count = 0;
 #endif
-	while (1)
+	while (! (LLApp::isError() || LLApp::isStopped()))
 	{
-		if (LLApp::isError() || LLApp::isStopped())
-		{
-			// The application has stopped running, time to take action (maybe)
-			break;
-		}
 #if !LL_WINDOWS
 		// Check whether or not the main thread had a sig child we haven't handled.
 		U32 current_sig_child_count = LLApp::getSigChildCount();
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index c32a776c3f591f81e22fbabaf88af90d61266d7d..c51d042a3d312d908d5c8b56cd47341ee43dad51 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -1,4 +1,4 @@
-/** 
+/**
  * @file llfile.cpp
  * @author Michael Schlachter
  * @date 2006-03-23
@@ -8,60 +8,194 @@
  * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2010, 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$
  */
 
 #if LL_WINDOWS
 #include <windows.h>
+#include <stdlib.h>                 // Windows errno
+#else
+#include <errno.h>
 #endif
 
 #include "linden_common.h"
 #include "llfile.h"
 #include "llstring.h"
 #include "llerror.h"
+#include "stringize.h"
 
 using namespace std;
 
+static std::string empty;
+
+// Many of the methods below use OS-level functions that mess with errno. Wrap
+// variants of strerror() to report errors.
+
+#if LL_WINDOWS
+// On Windows, use strerror_s().
+std::string strerr(int errn)
+{
+	char buffer[256];
+	strerror_s(buffer, errn);       // infers sizeof(buffer) -- love it!
+	return buffer;
+}
+
+#else
+// On Posix we want to call strerror_r(), but alarmingly, there are two
+// different variants. The one that returns int always populates the passed
+// buffer (except in case of error), whereas the other one always returns a
+// valid char* but might or might not populate the passed buffer. How do we
+// know which one we're getting? Define adapters for each and let the compiler
+// select the applicable adapter.
+
+// strerror_r() returns char*
+std::string message_from(int /*orig_errno*/, const char* /*buffer*/, size_t /*bufflen*/,
+						 const char* strerror_ret)
+{
+	return strerror_ret;
+}
+
+// strerror_r() returns int
+std::string message_from(int orig_errno, const char* buffer, size_t bufflen,
+						 int strerror_ret)
+{
+	if (strerror_ret == 0)
+	{
+		return buffer;
+	}
+	// Here strerror_r() has set errno. Since strerror_r() has already failed,
+	// seems like a poor bet to call it again to diagnose its own error...
+	int stre_errno = errno;
+	if (stre_errno == ERANGE)
+	{
+		return STRINGIZE("strerror_r() can't explain errno " << orig_errno
+						 << " (" << bufflen << "-byte buffer too small)");
+	}
+	if (stre_errno == EINVAL)
+	{
+		return STRINGIZE("unknown errno " << orig_errno);
+	}
+	// Here we don't even understand the errno from strerror_r()!
+	return STRINGIZE("strerror_r() can't explain errno " << orig_errno
+					 << " (error " << stre_errno << ')');
+}
+
+std::string strerr(int errn)
+{
+	char buffer[256];
+	// Select message_from() function matching the strerror_r() we have on hand.
+	return message_from(errn, buffer, sizeof(buffer),
+						strerror_r(errn, buffer, sizeof(buffer)));
+}
+#endif	// ! LL_WINDOWS
+
+// On either system, shorthand call just infers global 'errno'.
+std::string strerr()
+{
+	return strerr(errno);
+}
+
+int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
+{
+	if (rc < 0)
+	{
+		// Capture errno before we start emitting output
+		int errn = errno;
+		// For certain operations, a particular errno value might be
+		// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
+		// EEXIST. Don't warn if caller explicitly says this errno is okay.
+		if (errn != accept)
+		{
+			LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename
+							   << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
+		}
+#if 0 && LL_WINDOWS                 // turn on to debug file-locking problems
+		// If the problem is "Permission denied," maybe it's because another
+		// process has the file open. Try to find out.
+		if (errn == EACCES)         // *not* EPERM
+		{
+			// Only do any of this stuff (before LL_ENDL) if it will be logged.
+			LL_DEBUGS("LLFile") << empty;
+			const char* TEMP = getenv("TEMP");
+			if (! TEMP)
+			{
+				LL_CONT << "No $TEMP, not running 'handle'";
+			}
+			else
+			{
+				std::string tf(TEMP);
+				tf += "\\handle.tmp";
+				// http://technet.microsoft.com/en-us/sysinternals/bb896655
+				std::string cmd(STRINGIZE("handle \"" << filename
+										  // "openfiles /query /v | fgrep -i \"" << filename
+										  << "\" > \"" << tf << '"'));
+				LL_CONT << cmd;
+				if (system(cmd.c_str()) != 0)
+				{
+					LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
+				}
+				else
+				{
+					std::ifstream inf(tf);
+					std::string line;
+					while (std::getline(inf, line))
+					{
+						LL_CONT << '\n' << line;
+					}
+				}
+				LLFile::remove(tf);
+			}
+			LL_CONT << LL_ENDL;
+		}
+#endif  // LL_WINDOWS hack to identify processes holding file open
+	}
+	return rc;
+}
+
 // static
 int	LLFile::mkdir(const std::string& dirname, int perms)
 {
-#if LL_WINDOWS	
+#if LL_WINDOWS
 	// permissions are ignored on Windows
 	std::string utf8dirname = dirname;
 	llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
-	return _wmkdir(utf16dirname.c_str());
+	int rc = _wmkdir(utf16dirname.c_str());
 #else
-	return ::mkdir(dirname.c_str(), (mode_t)perms);
+	int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
 #endif
+	// We often use mkdir() to ensure the existence of a directory that might
+	// already exist. Don't spam the log if it does.
+	return warnif("mkdir", dirname, rc, EEXIST);
 }
 
 // static
 int	LLFile::rmdir(const std::string& dirname)
 {
-#if LL_WINDOWS	
+#if LL_WINDOWS
 	// permissions are ignored on Windows
 	std::string utf8dirname = dirname;
 	llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
-	return _wrmdir(utf16dirname.c_str());
+	int rc = _wrmdir(utf16dirname.c_str());
 #else
-	return ::rmdir(dirname.c_str());
+	int rc = ::rmdir(dirname.c_str());
 #endif
+	return warnif("rmdir", dirname, rc);
 }
 
 // static
@@ -108,10 +242,11 @@ int	LLFile::remove(const std::string& filename)
 #if	LL_WINDOWS
 	std::string utf8filename = filename;
 	llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
-	return _wremove(utf16filename.c_str());
+	int rc = _wremove(utf16filename.c_str());
 #else
-	return ::remove(filename.c_str());
+	int rc = ::remove(filename.c_str());
 #endif
+	return warnif("remove", filename, rc);
 }
 
 int	LLFile::rename(const std::string& filename, const std::string& newname)
@@ -121,10 +256,11 @@ int	LLFile::rename(const std::string& filename, const std::string& newname)
 	std::string utf8newname = newname;
 	llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
 	llutf16string utf16newname = utf8str_to_utf16str(utf8newname);
-	return _wrename(utf16filename.c_str(),utf16newname.c_str());
+	int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
 #else
-	return ::rename(filename.c_str(),newname.c_str());
+	int rc = ::rename(filename.c_str(),newname.c_str());
 #endif
+	return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc);
 }
 
 int	LLFile::stat(const std::string& filename, llstat* filestatus)
@@ -132,23 +268,26 @@ int	LLFile::stat(const std::string& filename, llstat* filestatus)
 #if LL_WINDOWS
 	std::string utf8filename = filename;
 	llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
-	return _wstat(utf16filename.c_str(),filestatus);
+	int rc = _wstat(utf16filename.c_str(),filestatus);
 #else
-	return ::stat(filename.c_str(),filestatus);
+	int rc = ::stat(filename.c_str(),filestatus);
 #endif
+	// We use stat() to determine existence (see isfile(), isdir()).
+	// Don't spam the log if the subject pathname doesn't exist.
+	return warnif("stat", filename, rc, ENOENT);
 }
 
 bool LLFile::isdir(const std::string& filename)
 {
 	llstat st;
-	
+
 	return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
 }
 
 bool LLFile::isfile(const std::string& filename)
 {
 	llstat st;
-	
+
 	return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
 }
 
@@ -260,7 +399,7 @@ void llifstream::open(const std::string& _Filename,	/* Flawfinder: ignore */
 	ios_base::openmode _Mode,
 	int _Prot)
 {	// open a C stream with specified mode
-	
+
 	LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::in, _Prot);
 	if(filep == NULL)
 	{
@@ -280,7 +419,7 @@ bool llifstream::is_open() const
 	return false;
 }
 llifstream::~llifstream()
-{	
+{
 	if (_ShouldClose)
 	{
 		close();
@@ -309,7 +448,7 @@ bool llofstream::is_open() const
 
 void llofstream::open(const std::string& _Filename,	/* Flawfinder: ignore */
 	ios_base::openmode _Mode,
-	int _Prot)	
+	int _Prot)
 {	// open a C stream with specified mode
 
 	LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::out, _Prot);
@@ -340,14 +479,14 @@ void llofstream::close()
 
 llofstream::llofstream(const std::string& _Filename,
 	std::ios_base::openmode _Mode,
-	int _Prot) 
+	int _Prot)
 		: std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)
 {	// construct with named file and specified mode
 	open(_Filename, _Mode , _Prot);	/* Flawfinder: ignore */
 }
 
 llofstream::~llofstream()
-{	
+{
 	// destroy the object
 	if (_ShouldClose)
 	{
diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llcommon/llinitparam.cpp
similarity index 100%
rename from indra/llxuixml/llinitparam.cpp
rename to indra/llcommon/llinitparam.cpp
diff --git a/indra/llxuixml/llinitparam.h b/indra/llcommon/llinitparam.h
similarity index 98%
rename from indra/llxuixml/llinitparam.h
rename to indra/llcommon/llinitparam.h
index 4ab1d891a366b168e52a52ef91985c8029c813e6..99983a19cb8925843453489259c8bda126660aab 100644
--- a/indra/llxuixml/llinitparam.h
+++ b/indra/llcommon/llinitparam.h
@@ -35,6 +35,7 @@
 #include <boost/shared_ptr.hpp>
 
 #include "llerror.h"
+#include "lltypeinfolookup.h"
 
 namespace LLInitParam
 {
@@ -205,7 +206,7 @@ namespace LLInitParam
 		mutable std::string	mValueName;
 	};
 
-	class Parser
+	class LL_COMMON_API Parser
 	{
 		LOG_CLASS(Parser);
 
@@ -227,9 +228,9 @@ namespace LLInitParam
 		typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&);
 		typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)>	parser_inspect_func_t;
 
-		typedef std::map<const std::type_info*, parser_read_func_t, CompareTypeID>		parser_read_func_map_t;
-		typedef std::map<const std::type_info*, parser_write_func_t, CompareTypeID>		parser_write_func_map_t;
-		typedef std::map<const std::type_info*, parser_inspect_func_t, CompareTypeID>	parser_inspect_func_map_t;
+		typedef LLTypeInfoLookup<parser_read_func_t>		parser_read_func_map_t;
+		typedef LLTypeInfoLookup<parser_write_func_t>		parser_write_func_map_t;
+		typedef LLTypeInfoLookup<parser_inspect_func_t>		parser_inspect_func_map_t;
 
 		Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map)
 		:	mParseSilently(false),
@@ -301,7 +302,7 @@ namespace LLInitParam
 	class Param;
 
 	// various callbacks and constraints associated with an individual param
-	struct ParamDescriptor
+	struct LL_COMMON_API ParamDescriptor
 	{
 		struct UserData
 		{
@@ -341,7 +342,7 @@ namespace LLInitParam
 	typedef boost::shared_ptr<ParamDescriptor> ParamDescriptorPtr;
 
 	// each derived Block class keeps a static data structure maintaining offsets to various params
-	class BlockDescriptor
+	class LL_COMMON_API BlockDescriptor
 	{
 	public:
 		BlockDescriptor();
@@ -369,7 +370,7 @@ namespace LLInitParam
 		class BaseBlock*				mCurrentBlockPtr;		// pointer to block currently being constructed
 	};
 
-	class BaseBlock
+	class LL_COMMON_API BaseBlock
 	{
 	public:
 		//TODO: implement in terms of owned_ptr
@@ -566,7 +567,7 @@ namespace LLInitParam
 		static bool equals(const BaseBlock::Lazy<T>& a, const BaseBlock::Lazy<T>& b) { return !a.empty() || !b.empty(); }
 	};
 
-	class Param
+	class LL_COMMON_API Param
 	{
 	public:
 		void setProvided(bool is_provided = true)
@@ -2062,8 +2063,8 @@ namespace LLInitParam
 		
 
 		// block param interface
-		bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
-		void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
+		LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
+		LL_COMMON_API void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
 		bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
 		{
 			//TODO: implement LLSD params as schema type Any
diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h
index 34d841a4e01ddc58a383a1618f05185a4dfbdcd4..403df08990e48c3db81e81ad037ec91a74223e98 100644
--- a/indra/llcommon/llinstancetracker.h
+++ b/indra/llcommon/llinstancetracker.h
@@ -167,8 +167,9 @@ public:
 
 	static T* getInstance(const KEY& k)
 	{
-		typename InstanceMap::const_iterator found = getMap_().find(k);
-		return (found == getMap_().end()) ? NULL : found->second;
+		const InstanceMap& map(getMap_());
+		typename InstanceMap::const_iterator found = map.find(k);
+		return (found == map.end()) ? NULL : found->second;
 	}
 
 	static instance_iter beginInstances() 
@@ -239,8 +240,20 @@ class LLInstanceTracker<T, T*> : public LLInstanceTrackerBase
 
 public:
 
-	/// for completeness of analogy with the generic implementation
-	static T* getInstance(T* k) { return k; }
+	/**
+	 * Does a particular instance still exist? Of course, if you already have
+	 * a T* in hand, you need not call getInstance() to @em locate the
+	 * instance -- unlike the case where getInstance() accepts some kind of
+	 * key. Nonetheless this method is still useful to @em validate a
+	 * particular T*, since each instance's destructor removes itself from the
+	 * underlying set.
+	 */
+	static T* getInstance(T* k)
+	{
+		const InstanceSet& set(getSet_());
+		typename InstanceSet::const_iterator found = set.find(k);
+		return (found == set.end())? NULL : *found;
+	}
 	static S32 instanceCount() { return getSet_().size(); }
 
 	class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag>
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0a57ef1c48c57d32674e0e7f8f4ec4c377847581
--- /dev/null
+++ b/indra/llcommon/llleap.cpp
@@ -0,0 +1,459 @@
+/**
+ * @file   llleap.cpp
+ * @author Nat Goodspeed
+ * @date   2012-02-20
+ * @brief  Implementation for llleap.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llleap.h"
+// STL headers
+#include <sstream>
+#include <algorithm>
+// std headers
+// external library headers
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/tokenizer.hpp>
+// other Linden headers
+#include "llerror.h"
+#include "llstring.h"
+#include "llprocess.h"
+#include "llevents.h"
+#include "stringize.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llerrorcontrol.h"
+#include "lltimer.h"
+#include "lluuid.h"
+#include "llleaplistener.h"
+
+#if LL_MSVC
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+LLLeap::LLLeap() {}
+LLLeap::~LLLeap() {}
+
+class LLLeapImpl: public LLLeap
+{
+    LOG_CLASS(LLLeap);
+public:
+    // Called only by LLLeap::create()
+    LLLeapImpl(const std::string& desc, const std::vector<std::string>& plugin):
+        // We might reassign mDesc in the constructor body if it's empty here.
+        mDesc(desc),
+        // We expect multiple LLLeapImpl instances. Definitely tweak
+        // mDonePump's name for uniqueness.
+        mDonePump("LLLeap", true),
+        // Troubling thought: what if one plugin intentionally messes with
+        // another plugin? LLEventPump names are in a single global namespace.
+        // Try to make that more difficult by generating a UUID for the reply-
+        // pump name -- so it should NOT need tweaking for uniqueness.
+        mReplyPump(LLUUID::generateNewID().asString()),
+        mExpect(0),
+        mPrevFatalFunction(LLError::getFatalFunction()),
+        // Instantiate a distinct LLLeapListener for this plugin. (Every
+        // plugin will want its own collection of managed listeners, etc.)
+        // Pass it a callback to our connect() method, so it can send events
+        // from a particular LLEventPump to the plugin without having to know
+        // this class or method name.
+        mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2)))
+    {
+        // Rule out empty vector
+        if (plugin.empty())
+        {
+            throw Error("no plugin command");
+        }
+
+        // Don't leave desc empty either, but in this case, if we weren't
+        // given one, we'll fake one.
+        if (desc.empty())
+        {
+            mDesc = LLProcess::basename(plugin[0]);
+            // how about a toLower() variant that returns the transformed string?!
+            std::string desclower(mDesc);
+            LLStringUtil::toLower(desclower);
+            // If we're running a Python script, use the script name for the
+            // desc instead of just 'python'. Arguably we should check for
+            // more different interpreters as well, but there's a reason to
+            // notice Python specially: we provide Python LLSD serialization
+            // support, so there's a pretty good reason to implement plugins
+            // in that language.
+            if (plugin.size() >= 2 && (desclower == "python" || desclower == "python.exe"))
+            {
+                mDesc = LLProcess::basename(plugin[1]);
+            }
+        }
+
+        // Listen for child "termination" right away to catch launch errors.
+        mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1));
+
+        // Okay, launch child.
+        LLProcess::Params params;
+        params.desc = mDesc;
+        std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end());
+        params.executable = *pi++;
+        for ( ; pi != pend; ++pi)
+        {
+            params.args.add(*pi);
+        }
+        params.files.add(LLProcess::FileParam("pipe")); // stdin
+        params.files.add(LLProcess::FileParam("pipe")); // stdout
+        params.files.add(LLProcess::FileParam("pipe")); // stderr
+        params.postend = mDonePump.getName();
+        mChild = LLProcess::create(params);
+        // If that didn't work, no point in keeping this LLLeap object.
+        if (! mChild)
+        {
+            throw Error(STRINGIZE("failed to run " << mDesc));
+        }
+
+        // Okay, launch apparently worked. Change our mDonePump listener.
+        mDonePump.stopListening("LLLeap");
+        mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::done, this, _1));
+
+        // Child might pump large volumes of data through either stdout or
+        // stderr. Don't bother copying all that data into notification event.
+        LLProcess::ReadPipe
+            &childout(mChild->getReadPipe(LLProcess::STDOUT)),
+            &childerr(mChild->getReadPipe(LLProcess::STDERR));
+        childout.setLimit(20);
+        childerr.setLimit(20);
+
+        // Serialize any event received on mReplyPump to our child's stdin.
+        mStdinConnection = connect(mReplyPump, "LLLeap");
+
+        // Listening on stdout is stateful. In general, we're either waiting
+        // for the length prefix or waiting for the specified length of data.
+        // We address that with two different listener methods -- one of which
+        // is blocked at any given time.
+        mStdoutConnection = childout.getPump()
+            .listen("prefix", boost::bind(&LLLeapImpl::rstdout, this, _1));
+        mStdoutDataConnection = childout.getPump()
+            .listen("data",   boost::bind(&LLLeapImpl::rstdoutData, this, _1));
+        mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
+
+        // Log anything sent up through stderr. When a typical program
+        // encounters an error, it writes its error message to stderr and
+        // terminates with nonzero exit code. In particular, the Python
+        // interpreter behaves that way. More generally, though, a plugin
+        // author can log whatever s/he wants to the viewer log using stderr.
+        mStderrConnection = childerr.getPump()
+            .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1));
+
+        // For our lifespan, intercept any LL_ERRS so we can notify plugin
+        LLError::setFatalFunction(boost::bind(&LLLeapImpl::fatalFunction, this, _1));
+
+        // Send child a preliminary event reporting our own reply-pump name --
+        // which would otherwise be pretty tricky to guess!
+        wstdin(mReplyPump.getName(),
+               LLSDMap
+               ("command", mListener->getName())
+               // Include LLLeap features -- this may be important for child to
+               // construct (or recognize) current protocol.
+               ("features", LLLeapListener::getFeatures()));
+    }
+
+    // Normally we'd expect to arrive here only via done()
+    virtual ~LLLeapImpl()
+    {
+        LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL;
+        // Restore original FatalFunction
+        LLError::setFatalFunction(mPrevFatalFunction);
+    }
+
+    // Listener for failed launch attempt
+    bool bad_launch(const LLSD& data)
+    {
+        LL_WARNS("LLLeap") << data["string"].asString() << LL_ENDL;
+        return false;
+    }
+
+    // Listener for child-process termination
+    bool done(const LLSD& data)
+    {
+        // Log the termination
+        LL_INFOS("LLLeap") << data["string"].asString() << LL_ENDL;
+
+        // Any leftover data at this moment are because protocol was not
+        // satisfied. Possibly the child was interrupted in the middle of
+        // sending a message, possibly the child didn't flush stdout before
+        // terminating, possibly it's just garbage. Log its existence but
+        // discard it.
+        LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
+        if (childout.size())
+        {
+            LLProcess::ReadPipe::size_type
+                peeklen((std::min)(LLProcess::ReadPipe::size_type(50), childout.size()));
+            LL_WARNS("LLLeap") << "Discarding final " << childout.size() << " bytes: "
+                               << childout.peek(0, peeklen) << "..." << LL_ENDL;
+        }
+
+        // Kill this instance. MUST BE LAST before return!
+        delete this;
+        return false;
+    }
+
+    // Listener for events on mReplyPump: send to child stdin
+    bool wstdin(const std::string& pump, const LLSD& data)
+    {
+        LLSD packet(LLSDMap("pump", pump)("data", data));
+
+        std::ostringstream buffer;
+        buffer << LLSDNotationStreamer(packet);
+
+/*==========================================================================*|
+        // DEBUGGING ONLY: don't copy str() if we can avoid it.
+        std::string strdata(buffer.str());
+        if (std::size_t(buffer.tellp()) != strdata.length())
+        {
+            LL_ERRS("LLLeap") << "tellp() -> " << buffer.tellp() << " != "
+                              << "str().length() -> " << strdata.length() << LL_ENDL;
+        }
+        // DEBUGGING ONLY: reading back is terribly inefficient.
+        std::istringstream readback(strdata);
+        LLSD echo;
+        LLPointer<LLSDParser> parser(new LLSDNotationParser());
+        S32 parse_status(parser->parse(readback, echo, strdata.length()));
+        if (parse_status == LLSDParser::PARSE_FAILURE)
+        {
+            LL_ERRS("LLLeap") << "LLSDNotationParser() cannot parse output of "
+                              << "LLSDNotationStreamer()" << LL_ENDL;
+        }
+        if (! llsd_equals(echo, packet))
+        {
+            LL_ERRS("LLLeap") << "LLSDNotationParser() produced different LLSD "
+                              << "than passed to LLSDNotationStreamer()" << LL_ENDL;
+        }
+|*==========================================================================*/
+
+        LL_DEBUGS("EventHost") << "Sending: " << buffer.tellp() << ':';
+        std::string::size_type truncate(80);
+        if (buffer.tellp() <= truncate)
+        {
+            LL_CONT << buffer.str();
+        }
+        else
+        {
+            LL_CONT << buffer.str().substr(0, truncate) << "...";
+        }
+        LL_CONT << LL_ENDL;
+
+        LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
+        childin.get_ostream() << buffer.tellp() << ':' << buffer.str() << std::flush;
+        return false;
+    }
+
+    // Initial state of stateful listening on child stdout: wait for a length
+    // prefix, followed by ':'.
+    bool rstdout(const LLSD& data)
+    {
+        LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
+        // It's possible we got notified of a couple digit characters without
+        // seeing the ':' -- unlikely, but still. Until we see ':', keep
+        // waiting.
+        if (childout.contains(':'))
+        {
+            std::istream& childstream(childout.get_istream());
+            // Saw ':', read length prefix and store in mExpect.
+            size_t expect;
+            childstream >> expect;
+            int colon(childstream.get());
+            if (colon != ':')
+            {
+                // Protocol failure. Clear out the rest of the pending data in
+                // childout (well, up to a max length) to log what was wrong.
+                LLProcess::ReadPipe::size_type
+                    readlen((std::min)(childout.size(), LLProcess::ReadPipe::size_type(80)));
+                bad_protocol(STRINGIZE(expect << char(colon) << childout.read(readlen)));
+            }
+            else
+            {
+                // Saw length prefix, saw colon, life is good. Now wait for
+                // that length of data to arrive.
+                mExpect = expect;
+                LL_DEBUGS("LLLeap") << "got length, waiting for "
+                                    << mExpect << " bytes of data" << LL_ENDL;
+                // Block calls to this method; resetting mBlocker unblocks
+                // calls to the other method.
+                mBlocker.reset(new LLEventPump::Blocker(mStdoutConnection));
+                // Go check if we've already received all the advertised data.
+                if (childout.size())
+                {
+                    LLSD updata(data);
+                    updata["len"] = LLSD::Integer(childout.size());
+                    rstdoutData(updata);
+                }
+            }
+        }
+        else if (childout.contains('\n'))
+        {
+            // Since this is the initial listening state, this is where we'd
+            // arrive if the child isn't following protocol at all -- say
+            // because the user specified 'ls' or some darn thing.
+            bad_protocol(childout.getline());
+        }
+        return false;
+    }
+
+    // State in which we listen on stdout for the specified length of data to
+    // arrive.
+    bool rstdoutData(const LLSD& data)
+    {
+        LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
+        // Until we've accumulated the promised length of data, keep waiting.
+        if (childout.size() >= mExpect)
+        {
+            // Ready to rock and roll.
+            LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got "
+                                << childout.size() << ", parsing LLSD" << LL_ENDL;
+            LLSD data;
+            LLPointer<LLSDParser> parser(new LLSDNotationParser());
+            S32 parse_status(parser->parse(childout.get_istream(), data, mExpect));
+            if (parse_status == LLSDParser::PARSE_FAILURE)
+            {
+                bad_protocol("unparseable LLSD data");
+            }
+            else if (! (data.isMap() && data["pump"].isString() && data.has("data")))
+            {
+                // we got an LLSD object, but it lacks required keys
+                bad_protocol("missing 'pump' or 'data'");
+            }
+            else
+            {
+                // The LLSD object we got from our stream contains the keys we
+                // need.
+                LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
+                // Block calls to this method; resetting mBlocker unblocks calls
+                // to the other method.
+                mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
+                // Go check for any more pending events in the buffer.
+                if (childout.size())
+                {
+                    LLSD updata(data);
+                    data["len"] = LLSD::Integer(childout.size());
+                    rstdout(updata);
+                }
+            }
+        }
+        return false;
+    }
+
+    void bad_protocol(const std::string& data)
+    {
+        LL_WARNS("LLLeap") << mDesc << ": invalid protocol: " << data << LL_ENDL;
+        // No point in continuing to run this child.
+        mChild->kill();
+    }
+
+    // Listen on child stderr and log everything that arrives
+    bool rstderr(const LLSD& data)
+    {
+        LLProcess::ReadPipe& childerr(mChild->getReadPipe(LLProcess::STDERR));
+        // We might have gotten a notification involving only a partial line
+        // -- or multiple lines. Read all complete lines; stop when there's
+        // only a partial line left.
+        while (childerr.contains('\n'))
+        {
+            // DO NOT make calls with side effects in a logging statement! If
+            // that log level is suppressed, your side effects WON'T HAPPEN.
+            std::string line(childerr.getline());
+            // Log the received line. Prefix it with the desc so we know which
+            // plugin it's from. This method name rstderr() is intentionally
+            // chosen to further qualify the log output.
+            LL_INFOS("LLLeap") << mDesc << ": " << line << LL_ENDL;
+        }
+        // What if child writes a final partial line to stderr?
+        if (data["eof"].asBoolean() && childerr.size())
+        {
+            std::string rest(childerr.read(childerr.size()));
+            // Read all remaining bytes and log.
+            LL_INFOS("LLLeap") << mDesc << ": " << rest << LL_ENDL;
+        }
+        return false;
+    }
+
+    void fatalFunction(const std::string& error)
+    {
+        // Notify plugin
+        LLSD event;
+        event["type"] = "error";
+        event["error"] = error;
+        mReplyPump.post(event);
+
+        // All the above really accomplished was to buffer the serialized
+        // event in our WritePipe. Have to pump mainloop a couple times to
+        // really write it out there... but time out in case we can't write.
+        LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
+        LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
+        LLSD nop;
+        F64 until(LLTimer::getElapsedSeconds() + 2);
+        while (childin.size() && LLTimer::getElapsedSeconds() < until)
+        {
+            mainloop.post(nop);
+        }
+
+        // forward the call to the previous FatalFunction
+        mPrevFatalFunction(error);
+    }
+
+private:
+    /// We always want to listen on mReplyPump with wstdin(); under some
+    /// circumstances we'll also echo other LLEventPumps to the plugin.
+    LLBoundListener connect(LLEventPump& pump, const std::string& listener)
+    {
+        // Serialize any event received on the specified LLEventPump to our
+        // child's stdin, suitably enriched with the pump name on which it was
+        // received.
+        return pump.listen(listener,
+                           boost::bind(&LLLeapImpl::wstdin, this, pump.getName(), _1));
+    }
+
+    std::string mDesc;
+    LLEventStream mDonePump;
+    LLEventStream mReplyPump;
+    LLProcessPtr mChild;
+    LLTempBoundListener
+        mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection;
+    boost::scoped_ptr<LLEventPump::Blocker> mBlocker;
+    LLProcess::ReadPipe::size_type mExpect;
+    LLError::FatalFunction mPrevFatalFunction;
+    boost::scoped_ptr<LLLeapListener> mListener;
+};
+
+// This must follow the declaration of LLLeapImpl, so it may as well be last.
+LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc)
+{
+    // If caller is willing to permit exceptions, just instantiate.
+    if (exc)
+        return new LLLeapImpl(desc, plugin);
+
+    // Caller insists on suppressing LLLeap::Error. Very well, catch it.
+    try
+    {
+        return new LLLeapImpl(desc, plugin);
+    }
+    catch (const LLLeap::Error&)
+    {
+        return NULL;
+    }
+}
+
+LLLeap* LLLeap::create(const std::string& desc, const std::string& plugin, bool exc)
+{
+    // Use LLStringUtil::getTokens() to parse the command line
+    return create(desc,
+                  LLStringUtil::getTokens(plugin,
+                                          " \t\r\n", // drop_delims
+                                          "",        // no keep_delims
+                                          "\"'",     // either kind of quotes
+                                          "\\"),     // backslash escape
+                  exc);
+}
diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h
new file mode 100644
index 0000000000000000000000000000000000000000..1a1ad23d3925a333aeed1cb57c42c00484df5e80
--- /dev/null
+++ b/indra/llcommon/llleap.h
@@ -0,0 +1,80 @@
+/**
+ * @file   llleap.h
+ * @author Nat Goodspeed
+ * @date   2012-02-20
+ * @brief  Class that implements "LLSD Event API Plugin"
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLLEAP_H)
+#define LL_LLLEAP_H
+
+#include "llinstancetracker.h"
+#include <string>
+#include <vector>
+#include <stdexcept>
+
+/**
+ * LLSD Event API Plugin class. Because instances are managed by
+ * LLInstanceTracker, you can instantiate LLLeap and forget the instance
+ * unless you need it later. Each instance manages an LLProcess; when the
+ * child process terminates, LLLeap deletes itself. We don't require a unique
+ * LLInstanceTracker key.
+ *
+ * The fact that a given LLLeap instance vanishes when its child process
+ * terminates makes it problematic to store an LLLeap* anywhere. Any stored
+ * LLLeap* pointer should be validated before use by
+ * LLLeap::getInstance(LLLeap*) (see LLInstanceTracker).
+ */
+class LL_COMMON_API LLLeap: public LLInstanceTracker<LLLeap>
+{
+public:
+    /**
+     * Pass a brief string description, mostly for logging purposes. The desc
+     * need not be unique, but obviously the clearer we can make it, the
+     * easier these things will be to debug. The strings are the command line
+     * used to launch the desired plugin process.
+     *
+     * Pass exc=false to suppress LLLeap::Error exception. Obviously in that
+     * case the caller cannot discover the nature of the error, merely that an
+     * error of some kind occurred (because create() returned NULL). Either
+     * way, the error is logged.
+     */
+    static LLLeap* create(const std::string& desc, const std::vector<std::string>& plugin,
+                          bool exc=true);
+
+    /**
+     * Pass a brief string description, mostly for logging purposes. The desc
+     * need not be unique, but obviously the clearer we can make it, the
+     * easier these things will be to debug. Pass a command-line string
+     * to launch the desired plugin process.
+     *
+     * Pass exc=false to suppress LLLeap::Error exception. Obviously in that
+     * case the caller cannot discover the nature of the error, merely that an
+     * error of some kind occurred (because create() returned NULL). Either
+     * way, the error is logged.
+     */
+    static LLLeap* create(const std::string& desc, const std::string& plugin,
+                          bool exc=true);
+
+    /**
+     * Exception thrown for invalid create() arguments, e.g. no plugin
+     * program. This is more resiliant than an LL_ERRS failure, because the
+     * string(s) passed to create() might come from an external source. This
+     * way the caller can catch LLLeap::Error and try to recover.
+     */
+    struct Error: public std::runtime_error
+    {
+        Error(const std::string& what): std::runtime_error(what) {}
+    };
+
+    virtual ~LLLeap();
+
+protected:
+    LLLeap();
+};
+
+#endif /* ! defined(LL_LLLEAP_H) */
diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa5730f112e2be3eac4aeec79d8a4e49373da179
--- /dev/null
+++ b/indra/llcommon/llleaplistener.cpp
@@ -0,0 +1,287 @@
+/**
+ * @file   llleaplistener.cpp
+ * @author Nat Goodspeed
+ * @date   2012-03-16
+ * @brief  Implementation for llleaplistener.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llleaplistener.h"
+// STL headers
+// std headers
+// external library headers
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "lluuid.h"
+#include "llsdutil.h"
+#include "stringize.h"
+
+/*****************************************************************************
+*   LEAP FEATURE STRINGS
+*****************************************************************************/
+/**
+ * Implement "getFeatures" command. The LLSD map thus obtained is intended to
+ * be machine-readable (read: easily-parsed, if parsing be necessary) and to
+ * highlight the differences between this version of the LEAP protocol and
+ * the baseline version. A client may thus determine whether or not the
+ * running viewer supports some recent feature of interest.
+ *
+ * This method is defined at the top of this implementation file so it's easy
+ * to find, easy to spot, easy to update as we enhance the LEAP protocol.
+ */
+/*static*/ LLSD LLLeapListener::getFeatures()
+{
+    static LLSD features;
+    if (features.isUndefined())
+    {
+        features = LLSD::emptyMap();
+
+        // This initial implementation IS the baseline LEAP protocol; thus the
+        // set of differences is empty; thus features is initially empty.
+//      features["featurename"] = "value";
+    }
+
+    return features;
+}
+
+LLLeapListener::LLLeapListener(const ConnectFunc& connect):
+    // Each LEAP plugin has an instance of this listener. Make the command
+    // pump name difficult for other such plugins to guess.
+    LLEventAPI(LLUUID::generateNewID().asString(),
+               "Operations relating to the LLSD Event API Plugin (LEAP) protocol"),
+    mConnect(connect)
+{
+    LLSD need_name(LLSDMap("name", LLSD()));
+    add("newpump",
+        "Instantiate a new LLEventPump named like [\"name\"] and listen to it.\n"
+        "If [\"type\"] == \"LLEventQueue\", make LLEventQueue, else LLEventStream.\n"
+        "Events sent through new LLEventPump will be decorated with [\"pump\"]=name.\n"
+        "Returns actual name in [\"name\"] (may be different if collision).",
+        &LLLeapListener::newpump,
+        need_name);
+    add("killpump",
+        "Delete LLEventPump [\"name\"] created by \"newpump\".\n"
+        "Returns [\"status\"] boolean indicating whether such a pump existed.",
+        &LLLeapListener::killpump,
+        need_name);
+    LLSD need_source_listener(LLSDMap("source", LLSD())("listener", LLSD()));
+    add("listen",
+        "Listen to an existing LLEventPump named [\"source\"], with listener name\n"
+        "[\"listener\"].\n"
+        "By default, send events on [\"source\"] to the plugin, decorated\n"
+        "with [\"pump\"]=[\"source\"].\n"
+        "If [\"dest\"] specified, send undecorated events on [\"source\"] to the\n"
+        "LLEventPump named [\"dest\"].\n"
+        "Returns [\"status\"] boolean indicating whether the connection was made.",
+        &LLLeapListener::listen,
+        need_source_listener);
+    add("stoplistening",
+        "Disconnect a connection previously established by \"listen\".\n"
+        "Pass same [\"source\"] and [\"listener\"] arguments.\n"
+        "Returns [\"status\"] boolean indicating whether such a listener existed.",
+        &LLLeapListener::stoplistening,
+        need_source_listener);
+    add("ping",
+        "No arguments, just a round-trip sanity check.",
+        &LLLeapListener::ping);
+    add("getAPIs",
+        "Enumerate all LLEventAPI instances by name and description.",
+        &LLLeapListener::getAPIs);
+    add("getAPI",
+        "Get name, description, dispatch key and operations for LLEventAPI [\"api\"].",
+        &LLLeapListener::getAPI,
+        LLSD().with("api", LLSD()));
+    add("getFeatures",
+        "Return an LLSD map of feature strings (deltas from baseline LEAP protocol)",
+        static_cast<void (LLLeapListener::*)(const LLSD&) const>(&LLLeapListener::getFeatures));
+    add("getFeature",
+        "Return the feature value with key [\"feature\"]",
+        &LLLeapListener::getFeature,
+        LLSD().with("feature", LLSD()));
+}
+
+LLLeapListener::~LLLeapListener()
+{
+    // We'd have stored a map of LLTempBoundListener instances, save that the
+    // operation of inserting into a std::map necessarily copies the
+    // value_type, and Bad Things would happen if you copied an
+    // LLTempBoundListener. (Destruction of the original would disconnect the
+    // listener, invalidating every stored connection.)
+    BOOST_FOREACH(ListenersMap::value_type& pair, mListeners)
+    {
+        pair.second.disconnect();
+    }
+}
+
+void LLLeapListener::newpump(const LLSD& request)
+{
+    Response reply(LLSD(), request);
+
+    std::string name = request["name"];
+    LLSD const & type = request["type"];
+
+    LLEventPump * new_pump = NULL;
+    if (type.asString() == "LLEventQueue")
+    {
+        new_pump = new LLEventQueue(name, true); // tweak name for uniqueness
+    }
+    else
+    {
+        if (! (type.isUndefined() || type.asString() == "LLEventStream"))
+        {
+            reply.warn(STRINGIZE("unknown 'type' " << type << ", using LLEventStream"));
+        }
+        new_pump = new LLEventStream(name, true); // tweak name for uniqueness
+    }
+
+    name = new_pump->getName();
+
+    mEventPumps.insert(name, new_pump);
+
+    // Now listen on this new pump with our plugin listener
+    std::string myname("llleap");
+    saveListener(name, myname, mConnect(*new_pump, myname));
+
+    reply["name"] = name;
+}
+
+void LLLeapListener::killpump(const LLSD& request)
+{
+    Response reply(LLSD(), request);
+
+    std::string name = request["name"];
+    // success == (nonzero number of entries were erased)
+    reply["status"] = bool(mEventPumps.erase(name));
+}
+
+void LLLeapListener::listen(const LLSD& request)
+{
+    Response reply(LLSD(), request);
+
+    std::string source_name = request["source"];
+    std::string dest_name = request["dest"];
+    std::string listener_name = request["listener"];
+
+    LLEventPump & source = LLEventPumps::instance().obtain(source_name);
+
+    reply["status"] = false;
+    if (mListeners.find(ListenersMap::key_type(source_name, listener_name)) == mListeners.end())
+    {
+        try
+        {
+            if (request["dest"].isDefined())
+            {
+                // If we're asked to connect the "source" pump to a
+                // specific "dest" pump, find dest pump and connect it.
+                LLEventPump & dest = LLEventPumps::instance().obtain(dest_name);
+                saveListener(source_name, listener_name,
+                             source.listen(listener_name,
+                                           boost::bind(&LLEventPump::post, &dest, _1)));
+            }
+            else
+            {
+                // "dest" unspecified means to direct events on "source"
+                // to our plugin listener.
+                saveListener(source_name, listener_name, mConnect(source, listener_name));
+            }
+            reply["status"] = true;
+        }
+        catch (const LLEventPump::DupListenerName &)
+        {
+            // pass - status already set to false
+        }
+    }
+}
+
+void LLLeapListener::stoplistening(const LLSD& request)
+{
+    Response reply(LLSD(), request);
+
+    std::string source_name = request["source"];
+    std::string listener_name = request["listener"];
+
+    ListenersMap::iterator finder =
+        mListeners.find(ListenersMap::key_type(source_name, listener_name));
+
+    reply["status"] = false;
+    if(finder != mListeners.end())
+    {
+        reply["status"] = true;
+        finder->second.disconnect();
+        mListeners.erase(finder);
+    }
+}
+
+void LLLeapListener::ping(const LLSD& request) const
+{
+    // do nothing, default reply suffices
+    Response(LLSD(), request);
+}
+
+void LLLeapListener::getAPIs(const LLSD& request) const
+{
+    Response reply(LLSD(), request);
+
+    for (LLEventAPI::instance_iter eai(LLEventAPI::beginInstances()),
+             eaend(LLEventAPI::endInstances());
+         eai != eaend; ++eai)
+    {
+        LLSD info;
+        info["desc"] = eai->getDesc();
+        reply[eai->getName()] = info;
+    }
+}
+
+void LLLeapListener::getAPI(const LLSD& request) const
+{
+    Response reply(LLSD(), request);
+
+    LLEventAPI* found = LLEventAPI::getInstance(request["api"]);
+    if (found)
+    {
+        reply["name"] = found->getName();
+        reply["desc"] = found->getDesc();
+        reply["key"] = found->getDispatchKey();
+        LLSD ops;
+        for (LLEventAPI::const_iterator oi(found->begin()), oend(found->end());
+             oi != oend; ++oi)
+        {
+            ops.append(found->getMetadata(oi->first));
+        }
+        reply["ops"] = ops;
+    }
+}
+
+void LLLeapListener::getFeatures(const LLSD& request) const
+{
+    // Merely constructing and destroying a Response object suffices here.
+    // Giving it a name would only produce fatal 'unreferenced variable'
+    // warnings.
+    Response(getFeatures(), request);
+}
+
+void LLLeapListener::getFeature(const LLSD& request) const
+{
+    Response reply(LLSD(), request);
+
+    LLSD::String feature_name(request["feature"]);
+    LLSD features(getFeatures());
+    if (features[feature_name].isDefined())
+    {
+        reply["feature"] = features[feature_name];
+    }
+}
+
+void LLLeapListener::saveListener(const std::string& pump_name,
+                                  const std::string& listener_name,
+                                  const LLBoundListener& listener)
+{
+    mListeners.insert(ListenersMap::value_type(ListenersMap::key_type(pump_name, listener_name),
+                                               listener));
+}
diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h
new file mode 100644
index 0000000000000000000000000000000000000000..2193d81b9ead98d05e395ae9580d81455a785087
--- /dev/null
+++ b/indra/llcommon/llleaplistener.h
@@ -0,0 +1,73 @@
+/**
+ * @file   llleaplistener.h
+ * @author Nat Goodspeed
+ * @date   2012-03-16
+ * @brief  LLEventAPI supporting LEAP plugins
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLLEAPLISTENER_H)
+#define LL_LLLEAPLISTENER_H
+
+#include "lleventapi.h"
+#include <map>
+#include <string>
+#include <boost/function.hpp>
+#include <boost/ptr_container/ptr_map.hpp>
+
+/// Listener class implementing LLLeap query/control operations.
+/// See https://jira.lindenlab.com/jira/browse/DEV-31978.
+class LLLeapListener: public LLEventAPI
+{
+public:
+    /**
+     * Decouple LLLeap by dependency injection. Certain LLLeapListener
+     * operations must be able to cause LLLeap to listen on a specified
+     * LLEventPump with the LLLeap listener that wraps incoming events in an
+     * outer (pump=, data=) map and forwards them to the plugin. Very well,
+     * define the signature for a function that will perform that, and make
+     * our constructor accept such a function.
+     */
+    typedef boost::function<LLBoundListener(LLEventPump&, const std::string& listener)>
+            ConnectFunc;
+    LLLeapListener(const ConnectFunc& connect);
+    ~LLLeapListener();
+
+    static LLSD getFeatures();
+
+private:
+    void newpump(const LLSD&);
+    void killpump(const LLSD&);
+    void listen(const LLSD&);
+    void stoplistening(const LLSD&);
+    void ping(const LLSD&) const;
+    void getAPIs(const LLSD&) const;
+    void getAPI(const LLSD&) const;
+    void getFeatures(const LLSD&) const;
+    void getFeature(const LLSD&) const;
+
+    void saveListener(const std::string& pump_name, const std::string& listener_name,
+                      const LLBoundListener& listener);
+
+    ConnectFunc mConnect;
+
+    // In theory, listen() could simply call the relevant LLEventPump's
+    // listen() method, stoplistening() likewise. Lifespan issues make us
+    // capture the LLBoundListener objects: when this object goes away, all
+    // those listeners should be disconnected. But what if the client listens,
+    // stops, listens again on the same LLEventPump with the same listener
+    // name? Merely collecting LLBoundListeners wouldn't adequately track
+    // that. So capture the latest LLBoundListener for this LLEventPump name
+    // and listener name.
+    typedef std::map<std::pair<std::string, std::string>, LLBoundListener> ListenersMap;
+    ListenersMap mListeners;
+    // Similar lifespan reasoning applies to LLEventPumps instantiated by
+    // newpump() operations.
+    typedef boost::ptr_map<std::string, LLEventPump> EventPumpsMap;
+    EventPumpsMap mEventPumps;
+};
+
+#endif /* ! defined(LL_LLLEAPLISTENER_H) */
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9667e4e03360594aca8b2a21d5bb6ae6c72cd079
--- /dev/null
+++ b/indra/llcommon/llprocess.cpp
@@ -0,0 +1,1313 @@
+/** 
+ * @file llprocess.cpp
+ * @brief Utility class for launching, terminating, and tracking the state of processes.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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$
+ */
+
+#include "linden_common.h"
+#include "llprocess.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llsingleton.h"
+#include "llstring.h"
+#include "stringize.h"
+#include "llapr.h"
+#include "apr_signal.h"
+#include "llevents.h"
+
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/asio/buffers_iterator.hpp>
+#include <iostream>
+#include <stdexcept>
+#include <limits>
+#include <algorithm>
+#include <vector>
+#include <typeinfo>
+#include <utility>
+
+/*****************************************************************************
+*   Helpers
+*****************************************************************************/
+static const char* whichfile_[] = { "stdin", "stdout", "stderr" };
+static std::string empty;
+static LLProcess::Status interpret_status(int status);
+static std::string getDesc(const LLProcess::Params& params);
+
+static std::string whichfile(LLProcess::FILESLOT index)
+{
+	if (index < LL_ARRAY_SIZE(whichfile_))
+		return whichfile_[index];
+	return STRINGIZE("file slot " << index);
+}
+
+/**
+ * Ref-counted "mainloop" listener. As long as there are still outstanding
+ * LLProcess objects, keep listening on "mainloop" so we can keep polling APR
+ * for process status.
+ */
+class LLProcessListener
+{
+	LOG_CLASS(LLProcessListener);
+public:
+	LLProcessListener():
+		mCount(0)
+	{}
+
+	void addPoll(const LLProcess&)
+	{
+		// Unconditionally increment mCount. If it was zero before
+		// incrementing, listen on "mainloop".
+		if (mCount++ == 0)
+		{
+			LL_DEBUGS("LLProcess") << "listening on \"mainloop\"" << LL_ENDL;
+			mConnection = LLEventPumps::instance().obtain("mainloop")
+				.listen("LLProcessListener", boost::bind(&LLProcessListener::tick, this, _1));
+		}
+	}
+
+	void dropPoll(const LLProcess&)
+	{
+		// Unconditionally decrement mCount. If it's zero after decrementing,
+		// stop listening on "mainloop".
+		if (--mCount == 0)
+		{
+			LL_DEBUGS("LLProcess") << "disconnecting from \"mainloop\"" << LL_ENDL;
+			mConnection.disconnect();
+		}
+	}
+
+private:
+	/// called once per frame by the "mainloop" LLEventPump
+	bool tick(const LLSD&)
+	{
+		// Tell APR to sense whether each registered LLProcess is still
+		// running and call handle_status() appropriately. We should be able
+		// to get the same info from an apr_proc_wait(APR_NOWAIT) call; but at
+		// least in APR 1.4.2, testing suggests that even with APR_NOWAIT,
+		// apr_proc_wait() blocks the caller. We can't have that in the
+		// viewer. Hence the callback rigmarole. (Once we update APR, it's
+		// probably worth testing again.) Also -- although there's an
+		// apr_proc_other_child_refresh() call, i.e. get that information for
+		// one specific child, it accepts an 'apr_other_child_rec_t*' that's
+		// mentioned NOWHERE else in the documentation or header files! I
+		// would use the specific call in LLProcess::getStatus() if I knew
+		// how. As it is, each call to apr_proc_other_child_refresh_all() will
+		// call callbacks for ALL still-running child processes. That's why we
+		// centralize such calls, using "mainloop" to ensure it happens once
+		// per frame, and refcounting running LLProcess objects to remain
+		// registered only while needed.
+		LL_DEBUGS("LLProcess") << "calling apr_proc_other_child_refresh_all()" << LL_ENDL;
+		apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+		return false;
+	}
+
+	/// If this object is destroyed before mCount goes to zero, stop
+	/// listening on "mainloop" anyway.
+	LLTempBoundListener mConnection;
+	unsigned mCount;
+};
+static LLProcessListener sProcessListener;
+
+/*****************************************************************************
+*   WritePipe and ReadPipe
+*****************************************************************************/
+LLProcess::BasePipe::~BasePipe() {}
+const LLProcess::BasePipe::size_type
+	  // use funky syntax to call max() to avoid blighted max() macros
+	  LLProcess::BasePipe::npos((std::numeric_limits<LLProcess::BasePipe::size_type>::max)());
+
+class WritePipeImpl: public LLProcess::WritePipe
+{
+	LOG_CLASS(WritePipeImpl);
+public:
+	WritePipeImpl(const std::string& desc, apr_file_t* pipe):
+		mDesc(desc),
+		mPipe(pipe),
+		// Essential to initialize our std::ostream with our special streambuf!
+		mStream(&mStreambuf)
+	{
+		mConnection = LLEventPumps::instance().obtain("mainloop")
+			.listen(LLEventPump::inventName("WritePipe"),
+					boost::bind(&WritePipeImpl::tick, this, _1));
+
+#if ! LL_WINDOWS
+		// We can't count on every child process reading everything we try to
+		// write to it. And if the child terminates with WritePipe data still
+		// pending, unless we explicitly suppress it, Posix will hit us with
+		// SIGPIPE. That would terminate the viewer, boom. "Ignoring" it means
+		// APR gets the correct errno, passes it back to us, we log it, etc.
+		signal(SIGPIPE, SIG_IGN);
+#endif
+	}
+
+	virtual std::ostream& get_ostream() { return mStream; }
+	virtual size_type size() const { return mStreambuf.size(); }
+
+	bool tick(const LLSD&)
+	{
+		typedef boost::asio::streambuf::const_buffers_type const_buffer_sequence;
+		// If there's anything to send, try to send it.
+		std::size_t total(mStreambuf.size()), consumed(0);
+		if (total)
+		{
+			const_buffer_sequence bufs = mStreambuf.data();
+			// In general, our streambuf might contain a number of different
+			// physical buffers; iterate over those.
+			bool keepwriting = true;
+			for (const_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end());
+				 bufi != bufend && keepwriting; ++bufi)
+			{
+				// http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents
+				// Although apr_file_write() accepts const void*, we
+				// manipulate const char* so we can increment the pointer.
+				const char* remainptr = boost::asio::buffer_cast<const char*>(*bufi);
+				std::size_t remainlen = boost::asio::buffer_size(*bufi);
+				while (remainlen)
+				{
+					// Tackle the current buffer in discrete chunks. On
+					// Windows, we've observed strange failures when trying to
+					// write big lengths (~1 MB) in a single operation. Even a
+					// 32K chunk seems too large. At some point along the way
+					// apr_file_write() returns 11 (Resource temporarily
+					// unavailable, i.e. EAGAIN) and says it wrote 0 bytes --
+					// even though it did write the chunk! Our next write
+					// attempt retries with the same chunk, resulting in the
+					// chunk being duplicated at the child end. Using smaller
+					// chunks is empirically more reliable.
+					std::size_t towrite((std::min)(remainlen, std::size_t(4*1024)));
+					apr_size_t written(towrite);
+					apr_status_t err = apr_file_write(mPipe, remainptr, &written);
+					// EAGAIN is exactly what we want from a nonblocking pipe.
+					// Rather than waiting for data, it should return immediately.
+					if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err)))
+					{
+						LL_WARNS("LLProcess") << "apr_file_write(" << towrite << ") on " << mDesc
+											  << " got " << err << ":" << LL_ENDL;
+						ll_apr_warn_status(err);
+					}
+
+					// 'written' is modified to reflect the number of bytes actually
+					// written. Make sure we consume those later. (Don't consume them
+					// now, that would invalidate the buffer iterator sequence!)
+					consumed += written;
+					// don't forget to advance to next chunk of current buffer
+					remainptr += written;
+					remainlen -= written;
+
+					char msgbuf[512];
+					LL_DEBUGS("LLProcess") << "wrote " << written << " of " << towrite
+										   << " bytes to " << mDesc
+										   << " (original " << total << "),"
+										   << " code " << err << ": "
+										   << apr_strerror(err, msgbuf, sizeof(msgbuf))
+										   << LL_ENDL;
+
+					// The parent end of this pipe is nonblocking. If we weren't able
+					// to write everything we wanted, don't keep banging on it -- that
+					// won't change until the child reads some. Wait for next tick().
+					if (written < towrite)
+					{
+						keepwriting = false; // break outer loop over buffers too
+						break;
+					}
+				} // next chunk of current buffer
+			}     // next buffer
+			// In all, we managed to write 'consumed' bytes. Remove them from the
+			// streambuf so we don't keep trying to send them. This could be
+			// anywhere from 0 up to mStreambuf.size(); anything we haven't yet
+			// sent, we'll try again later.
+			mStreambuf.consume(consumed);
+		}
+
+		return false;
+	}
+
+private:
+	std::string mDesc;
+	apr_file_t* mPipe;
+	LLTempBoundListener mConnection;
+	boost::asio::streambuf mStreambuf;
+	std::ostream mStream;
+};
+
+class ReadPipeImpl: public LLProcess::ReadPipe
+{
+	LOG_CLASS(ReadPipeImpl);
+public:
+	ReadPipeImpl(const std::string& desc, apr_file_t* pipe, LLProcess::FILESLOT index):
+		mDesc(desc),
+		mPipe(pipe),
+		mIndex(index),
+		// Essential to initialize our std::istream with our special streambuf!
+		mStream(&mStreambuf),
+		mPump("ReadPipe", true),    // tweak name as needed to avoid collisions
+		mLimit(0),
+		mEOF(false)
+	{
+		mConnection = LLEventPumps::instance().obtain("mainloop")
+			.listen(LLEventPump::inventName("ReadPipe"),
+					boost::bind(&ReadPipeImpl::tick, this, _1));
+	}
+
+	// Much of the implementation is simply connecting the abstract virtual
+	// methods with implementation data concealed from the base class.
+	virtual std::istream& get_istream() { return mStream; }
+	virtual std::string getline() { return LLProcess::getline(mStream); }
+	virtual LLEventPump& getPump() { return mPump; }
+	virtual void setLimit(size_type limit) { mLimit = limit; }
+	virtual size_type getLimit() const { return mLimit; }
+	virtual size_type size() const { return mStreambuf.size(); }
+
+	virtual std::string read(size_type len)
+	{
+		// Read specified number of bytes into a buffer.
+		size_type readlen((std::min)(size(), len));
+		// Formally, &buffer[0] is invalid for a vector of size() 0. Exit
+		// early in that situation.
+		if (! readlen)
+			return "";
+		// Make a buffer big enough.
+		std::vector<char> buffer(readlen);
+		mStream.read(&buffer[0], readlen);
+		// Since we've already clamped 'readlen', we can think of no reason
+		// why mStream.read() should read fewer than 'readlen' bytes.
+		// Nonetheless, use the actual retrieved length.
+		return std::string(&buffer[0], mStream.gcount());
+	}
+
+	virtual std::string peek(size_type offset=0, size_type len=npos) const
+	{
+		// Constrain caller's offset and len to overlap actual buffer content.
+		std::size_t real_offset = (std::min)(mStreambuf.size(), std::size_t(offset));
+		size_type	want_end	= (len == npos)? npos : (real_offset + len);
+		std::size_t real_end	= (std::min)(mStreambuf.size(), std::size_t(want_end));
+		boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+		return std::string(boost::asio::buffers_begin(cbufs) + real_offset,
+						   boost::asio::buffers_begin(cbufs) + real_end);
+	}
+
+	virtual size_type find(const std::string& seek, size_type offset=0) const
+	{
+		// If we're passing a string of length 1, use find(char), which can
+		// use an O(n) std::find() rather than the O(n^2) std::search().
+		if (seek.length() == 1)
+		{
+			return find(seek[0], offset);
+		}
+
+		// If offset is beyond the whole buffer, can't even construct a valid
+		// iterator range; can't possibly find the string we seek.
+		if (offset > mStreambuf.size())
+		{
+			return npos;
+		}
+
+		boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+		boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type>
+			begin(boost::asio::buffers_begin(cbufs)),
+			end	 (boost::asio::buffers_end(cbufs)),
+			found(std::search(begin + offset, end, seek.begin(), seek.end()));
+		return (found == end)? npos : (found - begin);
+	}
+
+	virtual size_type find(char seek, size_type offset=0) const
+	{
+		// If offset is beyond the whole buffer, can't even construct a valid
+		// iterator range; can't possibly find the char we seek.
+		if (offset > mStreambuf.size())
+		{
+			return npos;
+		}
+
+		boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+		boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type>
+			begin(boost::asio::buffers_begin(cbufs)),
+			end	 (boost::asio::buffers_end(cbufs)),
+			found(std::find(begin + offset, end, seek));
+		return (found == end)? npos : (found - begin);
+	}
+
+	bool tick(const LLSD&)
+	{
+		// Once we've hit EOF, skip all the rest of this.
+		if (mEOF)
+			return false;
+
+		typedef boost::asio::streambuf::mutable_buffers_type mutable_buffer_sequence;
+		// Try, every time, to read into our streambuf. In fact, we have no
+		// idea how much data the child might be trying to send: keep trying
+		// until we're convinced we've temporarily exhausted the pipe.
+		enum PipeState { RETRY, EXHAUSTED, CLOSED };
+		PipeState state = RETRY;
+		std::size_t committed(0);
+		do
+		{
+			// attempt to read an arbitrary size
+			mutable_buffer_sequence bufs = mStreambuf.prepare(4096);
+			// In general, the mutable_buffer_sequence returned by prepare() might
+			// contain a number of different physical buffers; iterate over those.
+			std::size_t tocommit(0);
+			for (mutable_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end());
+				 bufi != bufend; ++bufi)
+			{
+				// http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents
+				std::size_t toread(boost::asio::buffer_size(*bufi));
+				apr_size_t gotten(toread);
+				apr_status_t err = apr_file_read(mPipe,
+												 boost::asio::buffer_cast<void*>(*bufi),
+												 &gotten);
+				// EAGAIN is exactly what we want from a nonblocking pipe.
+				// Rather than waiting for data, it should return immediately.
+				if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err)))
+				{
+					// Handle EOF specially: it's part of normal-case processing.
+					if (err == APR_EOF)
+					{
+						LL_DEBUGS("LLProcess") << "EOF on " << mDesc << LL_ENDL;
+					}
+					else
+					{
+						LL_WARNS("LLProcess") << "apr_file_read(" << toread << ") on " << mDesc
+											  << " got " << err << ":" << LL_ENDL;
+						ll_apr_warn_status(err);
+					}
+					// Either way, though, we won't need any more tick() calls.
+					mConnection.disconnect();
+					// Ignore any subsequent calls we might get anyway.
+					mEOF = true;
+					state = CLOSED; // also break outer retry loop
+					break;
+				}
+
+				// 'gotten' was modified to reflect the number of bytes actually
+				// received. Make sure we commit those later. (Don't commit them
+				// now, that would invalidate the buffer iterator sequence!)
+				tocommit += gotten;
+				LL_DEBUGS("LLProcess") << "filled " << gotten << " of " << toread
+									   << " bytes from " << mDesc << LL_ENDL;
+
+				// The parent end of this pipe is nonblocking. If we weren't even
+				// able to fill this buffer, don't loop to try to fill the next --
+				// that won't change until the child writes more. Wait for next
+				// tick().
+				if (gotten < toread)
+				{
+					// break outer retry loop too
+					state = EXHAUSTED;
+					break;
+				}
+			}
+
+			// Don't forget to "commit" the data!
+			mStreambuf.commit(tocommit);
+			committed += tocommit;
+
+			// state is changed from RETRY when we can't fill any one buffer
+			// of the mutable_buffer_sequence established by the current
+			// prepare() call -- whether due to error or not enough bytes.
+			// That is, if state is still RETRY, we've filled every physical
+			// buffer in the mutable_buffer_sequence. In that case, for all we
+			// know, the child might have still more data pending -- go for it!
+		} while (state == RETRY);
+
+		// Once we recognize that the pipe is closed, make one more call to
+		// listener. The listener might be waiting for a particular substring
+		// to arrive, or a particular length of data or something. The event
+		// with "eof" == true announces that nothing further will arrive, so
+		// use it or lose it.
+		if (committed || state == CLOSED)
+		{
+			// If we actually received new data, publish it on our LLEventPump
+			// as advertised. Constrain it by mLimit. But show listener the
+			// actual accumulated buffer size, regardless of mLimit.
+			size_type datasize((std::min)(mLimit, size_type(mStreambuf.size())));
+			mPump.post(LLSDMap
+					   ("data", peek(0, datasize))
+					   ("len", LLSD::Integer(mStreambuf.size()))
+					   ("slot", LLSD::Integer(mIndex))
+					   ("name", whichfile(mIndex))
+					   ("desc", mDesc)
+					   ("eof", state == CLOSED));
+		}
+
+		return false;
+	}
+
+private:
+	std::string mDesc;
+	apr_file_t* mPipe;
+	LLProcess::FILESLOT mIndex;
+	LLTempBoundListener mConnection;
+	boost::asio::streambuf mStreambuf;
+	std::istream mStream;
+	LLEventStream mPump;
+	size_type mLimit;
+	bool mEOF;
+};
+
+/*****************************************************************************
+*   LLProcess itself
+*****************************************************************************/
+/// Need an exception to avoid constructing an invalid LLProcess object, but
+/// internal use only
+struct LLProcessError: public std::runtime_error
+{
+	LLProcessError(const std::string& msg): std::runtime_error(msg) {}
+};
+
+LLProcessPtr LLProcess::create(const LLSDOrParams& params)
+{
+	try
+	{
+		return LLProcessPtr(new LLProcess(params));
+	}
+	catch (const LLProcessError& e)
+	{
+		LL_WARNS("LLProcess") << e.what() << LL_ENDL;
+
+		// If caller is requesting an event on process termination, send one
+		// indicating bad launch. This may prevent someone waiting forever for
+		// a termination post that can't arrive because the child never
+		// started.
+		if (params.postend.isProvided())
+		{
+			LLEventPumps::instance().obtain(params.postend)
+				.post(LLSDMap
+					  // no "id"
+					  ("desc", getDesc(params))
+					  ("state", LLProcess::UNSTARTED)
+					  // no "data"
+					  ("string", e.what())
+					 );
+		}
+
+		return LLProcessPtr();
+	}
+}
+
+/// Call an apr function returning apr_status_t. On failure, log warning and
+/// throw LLProcessError mentioning the function call that produced that
+/// result.
+#define chkapr(func)                            \
+	if (ll_apr_warn_status(func))				\
+		throw LLProcessError(#func " failed")
+
+LLProcess::LLProcess(const LLSDOrParams& params):
+	mAutokill(params.autokill),
+	mPipes(NSLOTS)
+{
+	// Hmm, when you construct a ptr_vector with a size, it merely reserves
+	// space, it doesn't actually make it that big. Explicitly make it bigger.
+	// Because of ptr_vector's odd semantics, have to push_back(0) the right
+	// number of times! resize() wants to default-construct new BasePipe
+	// instances, which fails because it's pure virtual. But because of the
+	// constructor call, these push_back() calls should require no new
+	// allocation.
+	for (size_t i = 0; i < mPipes.capacity(); ++i)
+		mPipes.push_back(0);
+
+	if (! params.validateBlock(true))
+	{
+		throw LLProcessError(STRINGIZE("not launched: failed parameter validation\n"
+									   << LLSDNotationStreamer(params)));
+	}
+
+	mPostend = params.postend;
+
+	apr_procattr_t *procattr = NULL;
+	chkapr(apr_procattr_create(&procattr, gAPRPoolp));
+
+	// IQA-490, CHOP-900: On Windows, ask APR to jump through hoops to
+	// constrain the set of handles passed to the child process. Before we
+	// changed to APR, the Windows implementation of LLProcessLauncher called
+	// CreateProcess(bInheritHandles=FALSE), meaning to pass NO open handles
+	// to the child process. Now that we support pipes, though, we must allow
+	// apr_proc_create() to pass bInheritHandles=TRUE. But without taking
+	// special pains, that causes trouble in a number of ways, due to the fact
+	// that the viewer is constantly opening and closing files -- most of
+	// which CreateProcess() passes to every child process!
+#if ! defined(APR_HAS_PROCATTR_CONSTRAIN_HANDLE_SET)
+	// Our special preprocessor symbol isn't even defined -- wrong APR
+	LL_WARNS("LLProcess") << "This version of APR lacks Linden "
+						  << "apr_procattr_constrain_handle_set() extension" << LL_ENDL;
+#else
+	chkapr(apr_procattr_constrain_handle_set(procattr, 1));
+#endif
+
+	// For which of stdin, stdout, stderr should we create a pipe to the
+	// child? In the viewer, there are only a couple viable
+	// apr_procattr_io_set() alternatives: inherit the viewer's own stdxxx
+	// handle (APR_NO_PIPE, e.g. for stdout, stderr), or create a pipe that's
+	// blocking on the child end but nonblocking at the viewer end
+	// (APR_CHILD_BLOCK).
+	// Other major options could include explicitly creating a single APR pipe
+	// and passing it as both stdout and stderr (apr_procattr_child_out_set(),
+	// apr_procattr_child_err_set()), or accepting a filename, opening it and
+	// passing that apr_file_t (simple <, >, 2> redirect emulation).
+	std::vector<apr_int32_t> select;
+	BOOST_FOREACH(const FileParam& fparam, params.files)
+	{
+		// Every iteration, we're going to append an item to 'select'. At the
+		// top of the loop, its size() is, in effect, an index. Use that to
+		// pick a string description for messages.
+		std::string which(whichfile(FILESLOT(select.size())));
+		if (fparam.type().empty())  // inherit our file descriptor
+		{
+			select.push_back(APR_NO_PIPE);
+		}
+		else if (fparam.type() == "pipe") // anonymous pipe
+		{
+			if (! fparam.name().empty())
+			{
+				LL_WARNS("LLProcess") << "For " << params.executable()
+									  << ": internal names for reusing pipes ('"
+									  << fparam.name() << "' for " << which
+									  << ") are not yet supported -- creating distinct pipe"
+									  << LL_ENDL;
+			}
+			// The viewer can't block for anything: the parent end MUST be
+			// nonblocking. As the APR documentation itself points out, it
+			// makes very little sense to set nonblocking I/O for the child
+			// end of a pipe: only a specially-written child could deal with
+			// that.
+			select.push_back(APR_CHILD_BLOCK);
+		}
+		else
+		{
+			throw LLProcessError(STRINGIZE("For " << params.executable()
+										   << ": unsupported FileParam for " << which
+										   << ": type='" << fparam.type()
+										   << "', name='" << fparam.name() << "'"));
+		}
+	}
+	// By default, pass APR_NO_PIPE for unspecified slots.
+	while (select.size() < NSLOTS)
+	{
+		select.push_back(APR_NO_PIPE);
+	}
+	chkapr(apr_procattr_io_set(procattr, select[STDIN], select[STDOUT], select[STDERR]));
+
+	// Thumbs down on implicitly invoking the shell to invoke the child. From
+	// our point of view, the other major alternative to APR_PROGRAM_PATH
+	// would be APR_PROGRAM_ENV: still copy environment, but require full
+	// executable pathname. I don't see a downside to searching the PATH,
+	// though: if our caller wants (e.g.) a specific Python interpreter, s/he
+	// can still pass the full pathname.
+	chkapr(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));
+	// YES, do extra work if necessary to report child exec() failures back to
+	// parent process.
+	chkapr(apr_procattr_error_check_set(procattr, 1));
+	// Do not start a non-autokill child in detached state. On Posix
+	// platforms, this setting attempts to daemonize the new child, closing
+	// std handles and the like, and that's a bit more detachment than we
+	// want. autokill=false just means not to implicitly kill the child when
+	// the parent terminates!
+//	chkapr(apr_procattr_detach_set(procattr, params.autokill? 0 : 1));
+
+	if (params.autokill)
+	{
+#if ! defined(APR_HAS_PROCATTR_AUTOKILL_SET)
+		// Our special preprocessor symbol isn't even defined -- wrong APR
+		LL_WARNS("LLProcess") << "This version of APR lacks Linden apr_procattr_autokill_set() extension" << LL_ENDL;
+#elif ! APR_HAS_PROCATTR_AUTOKILL_SET
+		// Symbol is defined, but to 0: expect apr_procattr_autokill_set() to
+		// return APR_ENOTIMPL.
+#else   // APR_HAS_PROCATTR_AUTOKILL_SET nonzero
+		ll_apr_warn_status(apr_procattr_autokill_set(procattr, 1));
+#endif
+	}
+
+	// In preparation for calling apr_proc_create(), we collect a number of
+	// const char* pointers obtained from std::string::c_str(). Turns out
+	// LLInitParam::Block's helpers Optional, Mandatory, Multiple et al.
+	// guarantee that converting to the wrapped type (std::string in our
+	// case), e.g. by calling operator(), returns a reference to *the same
+	// instance* of the wrapped type that's stored in our Block subclass.
+	// That's important! We know 'params' persists throughout this method
+	// call; but without that guarantee, we'd have to assume that converting
+	// one of its members to std::string might return a different (temp)
+	// instance. Capturing the c_str() from a temporary std::string is Bad Bad
+	// Bad. But armed with this knowledge, when you see params.cwd().c_str(),
+	// grit your teeth and smile and carry on.
+
+	if (params.cwd.isProvided())
+	{
+		chkapr(apr_procattr_dir_set(procattr, params.cwd().c_str()));
+	}
+
+	// create an argv vector for the child process
+	std::vector<const char*> argv;
+
+	// Add the executable path. See above remarks about c_str().
+	argv.push_back(params.executable().c_str());
+
+	// Add arguments. See above remarks about c_str().
+	BOOST_FOREACH(const std::string& arg, params.args)
+	{
+		argv.push_back(arg.c_str());
+	}
+
+	// terminate with a null pointer
+	argv.push_back(NULL);
+
+	// Launch! The NULL would be the environment block, if we were passing
+	// one. Hand-expand chkapr() macro so we can fill in the actual command
+	// string instead of the variable names.
+	if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr,
+										   gAPRPoolp)))
+	{
+		throw LLProcessError(STRINGIZE(params << " failed"));
+	}
+
+	// arrange to call status_callback()
+	apr_proc_other_child_register(&mProcess, &LLProcess::status_callback, this, mProcess.in,
+								  gAPRPoolp);
+	// and make sure we poll it once per "mainloop" tick
+	sProcessListener.addPoll(*this);
+	mStatus.mState = RUNNING;
+
+	mDesc = STRINGIZE(getDesc(params) << " (" << mProcess.pid << ')');
+	LL_INFOS("LLProcess") << mDesc << ": launched " << params << LL_ENDL;
+
+	// Unless caller explicitly turned off autokill (child should persist),
+	// take steps to terminate the child. This is all suspenders-and-belt: in
+	// theory our destructor should kill an autokill child, but in practice
+	// that doesn't always work (e.g. VWR-21538).
+	if (params.autokill)
+	{
+/*==========================================================================*|
+		// NO: There may be an APR bug, not sure -- but at least on Mac, when
+		// gAPRPoolp is destroyed, OUR process receives SIGTERM! Apparently
+		// either our own PID is getting into the list of processes to kill()
+		// (unlikely), or somehow one of those PIDs is getting zeroed first,
+		// so that kill() sends SIGTERM to the whole process group -- this
+		// process included. I'd have to build and link with a debug version
+		// of APR to know for sure. It's too bad: this mechanism would be just
+		// right for dealing with static autokill LLProcessPtr variables,
+		// which aren't destroyed until after APR is no longer available.
+
+		// Tie the lifespan of this child process to the lifespan of our APR
+		// pool: on destruction of the pool, forcibly kill the process. Tell
+		// APR to try SIGTERM and wait 3 seconds. If that didn't work, use
+		// SIGKILL.
+		apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT);
+|*==========================================================================*/
+
+		// On Windows, associate the new child process with our Job Object.
+		autokill();
+	}
+
+	// Instantiate the proper pipe I/O machinery
+	// want to be able to point to apr_proc_t::in, out, err by index
+	typedef apr_file_t* apr_proc_t::*apr_proc_file_ptr;
+	static apr_proc_file_ptr members[] =
+		{ &apr_proc_t::in, &apr_proc_t::out, &apr_proc_t::err };
+	for (size_t i = 0; i < NSLOTS; ++i)
+	{
+		if (select[i] != APR_CHILD_BLOCK)
+			continue;
+		std::string desc(STRINGIZE(mDesc << ' ' << whichfile(FILESLOT(i))));
+		apr_file_t* pipe(mProcess.*(members[i]));
+		if (i == STDIN)
+		{
+			mPipes.replace(i, new WritePipeImpl(desc, pipe));
+		}
+		else
+		{
+			mPipes.replace(i, new ReadPipeImpl(desc, pipe, FILESLOT(i)));
+		}
+		LL_DEBUGS("LLProcess") << "Instantiating " << typeid(mPipes[i]).name()
+							   << "('" << desc << "')" << LL_ENDL;
+	}
+}
+
+// Helper to obtain a description string, given a Params block
+static std::string getDesc(const LLProcess::Params& params)
+{
+	// If caller specified a description string, by all means use it.
+	if (params.desc.isProvided())
+		return params.desc;
+
+	// Caller didn't say. Use the executable name -- but use just the filename
+	// part. On Mac, for instance, full pathnames get cumbersome.
+	return LLProcess::basename(params.executable);
+}
+
+//static
+std::string LLProcess::basename(const std::string& path)
+{
+	// If there are Linden utility functions to manipulate pathnames, I
+	// haven't found them -- and for this usage, Boost.Filesystem seems kind
+	// of heavyweight.
+	std::string::size_type delim = path.find_last_of("\\/");
+	// If path contains no pathname delimiters, return the whole thing.
+	if (delim == std::string::npos)
+		return path;
+
+	// Return just the part beyond the last delimiter.
+	return path.substr(delim + 1);
+}
+
+LLProcess::~LLProcess()
+{
+	// In the Linden viewer, there's at least one static LLProcessPtr. Its
+	// destructor will be called *after* ll_cleanup_apr(). In such a case,
+	// unregistering is pointless (and fatal!) -- and kill(), which also
+	// relies on APR, is impossible.
+	if (! gAPRPoolp)
+		return;
+
+	// Only in state RUNNING are we registered for callback. In UNSTARTED we
+	// haven't yet registered. And since receiving the callback is the only
+	// way we detect child termination, we only change from state RUNNING at
+	// the same time we unregister.
+	if (mStatus.mState == RUNNING)
+	{
+		// We're still registered for a callback: unregister. Do it before
+		// we even issue the kill(): even if kill() somehow prompted an
+		// instantaneous callback (unlikely), this object is going away! Any
+		// information updated in this object by such a callback is no longer
+		// available to any consumer anyway.
+		apr_proc_other_child_unregister(this);
+		// One less LLProcess to poll for
+		sProcessListener.dropPoll(*this);
+	}
+
+	if (mAutokill)
+	{
+		kill("destructor");
+	}
+}
+
+bool LLProcess::kill(const std::string& who)
+{
+	if (isRunning())
+	{
+		LL_INFOS("LLProcess") << who << " killing " << mDesc << LL_ENDL;
+
+#if LL_WINDOWS
+		int sig = -1;
+#else  // Posix
+		int sig = SIGTERM;
+#endif
+
+		ll_apr_warn_status(apr_proc_kill(&mProcess, sig));
+	}
+
+	return ! isRunning();
+}
+
+bool LLProcess::isRunning() const
+{
+	return getStatus().mState == RUNNING;
+}
+
+LLProcess::Status LLProcess::getStatus() const
+{
+	return mStatus;
+}
+
+std::string LLProcess::getStatusString() const
+{
+	return getStatusString(getStatus());
+}
+
+std::string LLProcess::getStatusString(const Status& status) const
+{
+	return getStatusString(mDesc, status);
+}
+
+//static
+std::string LLProcess::getStatusString(const std::string& desc, const Status& status)
+{
+	if (status.mState == UNSTARTED)
+		return desc + " was never launched";
+
+	if (status.mState == RUNNING)
+		return desc + " running";
+
+	if (status.mState == EXITED)
+		return STRINGIZE(desc << " exited with code " << status.mData);
+
+	if (status.mState == KILLED)
+#if LL_WINDOWS
+		return STRINGIZE(desc << " killed with exception " << std::hex << status.mData);
+#else
+		return STRINGIZE(desc << " killed by signal " << status.mData
+						 << " (" << apr_signal_description_get(status.mData) << ")");
+#endif
+
+	return STRINGIZE(desc << " in unknown state " << status.mState << " (" << status.mData << ")");
+}
+
+// Classic-C-style APR callback
+void LLProcess::status_callback(int reason, void* data, int status)
+{
+	// Our only role is to bounce this static method call back into object
+	// space.
+	static_cast<LLProcess*>(data)->handle_status(reason, status);
+}
+
+#define tabent(symbol) { symbol, #symbol }
+static struct ReasonCode
+{
+	int code;
+	const char* name;
+} reasons[] =
+{
+	tabent(APR_OC_REASON_DEATH),
+	tabent(APR_OC_REASON_UNWRITABLE),
+	tabent(APR_OC_REASON_RESTART),
+	tabent(APR_OC_REASON_UNREGISTER),
+	tabent(APR_OC_REASON_LOST),
+	tabent(APR_OC_REASON_RUNNING)
+};
+#undef tabent
+
+// Object-oriented callback
+void LLProcess::handle_status(int reason, int status)
+{
+	{
+		// This odd appearance of LL_DEBUGS is just to bracket a lookup that will
+		// only be performed if in fact we're going to produce the log message.
+		LL_DEBUGS("LLProcess") << empty;
+		std::string reason_str;
+		BOOST_FOREACH(const ReasonCode& rcp, reasons)
+		{
+			if (reason == rcp.code)
+			{
+				reason_str = rcp.name;
+				break;
+			}
+		}
+		if (reason_str.empty())
+		{
+			reason_str = STRINGIZE("unknown reason " << reason);
+		}
+		LL_CONT << mDesc << ": handle_status(" << reason_str << ", " << status << ")" << LL_ENDL;
+	}
+
+	if (! (reason == APR_OC_REASON_DEATH || reason == APR_OC_REASON_LOST))
+	{
+		// We're only interested in the call when the child terminates.
+		return;
+	}
+
+	// Somewhat oddly, APR requires that you explicitly unregister even when
+	// it already knows the child has terminated. We must pass the same 'data'
+	// pointer as for the register() call, which was our 'this'.
+	apr_proc_other_child_unregister(this);
+	// don't keep polling for a terminated process
+	sProcessListener.dropPoll(*this);
+	// We overload mStatus.mState to indicate whether the child is registered
+	// for APR callback: only RUNNING means registered. Track that we've
+	// unregistered. We know the child has terminated; might be EXITED or
+	// KILLED; refine below.
+	mStatus.mState = EXITED;
+
+	// Make last-gasp calls for each of the ReadPipes we have on hand. Since
+	// they're listening on "mainloop", we can be sure they'll eventually
+	// collect all pending data from the child. But we want to be able to
+	// guarantee to our consumer that by the time we post on the "postend"
+	// LLEventPump, our ReadPipes are already buffering all the data there
+	// will ever be from the child. That lets the "postend" listener decide
+	// what to do with that final data.
+	for (size_t i = 0; i < mPipes.size(); ++i)
+	{
+		std::string error;
+		ReadPipeImpl* ppipe = getPipePtr<ReadPipeImpl>(error, FILESLOT(i));
+		if (ppipe)
+		{
+			static LLSD trivial;
+			ppipe->tick(trivial);
+		}
+	}
+
+//	wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
+	// It's just wrong to call apr_proc_wait() here. The only way APR knows to
+	// call us with APR_OC_REASON_DEATH is that it's already reaped this child
+	// process, so calling wait() will only produce "huh?" from the OS. We
+	// must rely on the status param passed in, which unfortunately comes
+	// straight from the OS wait() call, which means we have to decode it by
+	// hand.
+	mStatus = interpret_status(status);
+	LL_INFOS("LLProcess") << getStatusString() << LL_ENDL;
+
+	// If caller requested notification on child termination, send it.
+	if (! mPostend.empty())
+	{
+		LLEventPumps::instance().obtain(mPostend)
+			.post(LLSDMap
+				  ("id",     getProcessID())
+				  ("desc",   mDesc)
+				  ("state",  mStatus.mState)
+				  ("data",   mStatus.mData)
+				  ("string", getStatusString())
+				 );
+	}
+}
+
+LLProcess::id LLProcess::getProcessID() const
+{
+	return mProcess.pid;
+}
+
+LLProcess::handle LLProcess::getProcessHandle() const
+{
+#if LL_WINDOWS
+	return mProcess.hproc;
+#else
+	return mProcess.pid;
+#endif
+}
+
+std::string LLProcess::getPipeName(FILESLOT) const
+{
+	// LLProcess::FileParam::type "npipe" is not yet implemented
+	return "";
+}
+
+template<class PIPETYPE>
+PIPETYPE* LLProcess::getPipePtr(std::string& error, FILESLOT slot)
+{
+	if (slot >= NSLOTS)
+	{
+		error = STRINGIZE(mDesc << " has no slot " << slot);
+		return NULL;
+	}
+	if (mPipes.is_null(slot))
+	{
+		error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a monitored pipe");
+		return NULL;
+	}
+	// Make sure we dynamic_cast in pointer domain so we can test, rather than
+	// accepting runtime's exception.
+	PIPETYPE* ppipe = dynamic_cast<PIPETYPE*>(&mPipes[slot]);
+	if (! ppipe)
+	{
+		error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a " << typeid(PIPETYPE).name());
+		return NULL;
+	}
+
+	error.clear();
+	return ppipe;
+}
+
+template <class PIPETYPE>
+PIPETYPE& LLProcess::getPipe(FILESLOT slot)
+{
+	std::string error;
+	PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
+	if (! wp)
+	{
+		throw NoPipe(error);
+	}
+	return *wp;
+}
+
+template <class PIPETYPE>
+boost::optional<PIPETYPE&> LLProcess::getOptPipe(FILESLOT slot)
+{
+	std::string error;
+	PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
+	if (! wp)
+	{
+		LL_DEBUGS("LLProcess") << error << LL_ENDL;
+		return boost::optional<PIPETYPE&>();
+	}
+	return *wp;
+}
+
+LLProcess::WritePipe& LLProcess::getWritePipe(FILESLOT slot)
+{
+	return getPipe<WritePipe>(slot);
+}
+
+boost::optional<LLProcess::WritePipe&> LLProcess::getOptWritePipe(FILESLOT slot)
+{
+	return getOptPipe<WritePipe>(slot);
+}
+
+LLProcess::ReadPipe& LLProcess::getReadPipe(FILESLOT slot)
+{
+	return getPipe<ReadPipe>(slot);
+}
+
+boost::optional<LLProcess::ReadPipe&> LLProcess::getOptReadPipe(FILESLOT slot)
+{
+	return getOptPipe<ReadPipe>(slot);
+}
+
+//static
+std::string LLProcess::getline(std::istream& in)
+{
+	std::string line;
+	std::getline(in, line);
+	// Blur the distinction between "\r\n" and plain "\n". std::getline() will
+	// have eaten the "\n", but we could still end up with a trailing "\r".
+	std::string::size_type lastpos = line.find_last_not_of("\r");
+	if (lastpos != std::string::npos)
+	{
+		// Found at least one character that's not a trailing '\r'. SKIP OVER
+		// IT and erase the rest of the line.
+		line.erase(lastpos+1);
+	}
+	return line;
+}
+
+std::ostream& operator<<(std::ostream& out, const LLProcess::Params& params)
+{
+	if (params.cwd.isProvided())
+	{
+		out << "cd " << LLStringUtil::quote(params.cwd) << ": ";
+	}
+	out << LLStringUtil::quote(params.executable);
+	BOOST_FOREACH(const std::string& arg, params.args)
+	{
+		out << ' ' << LLStringUtil::quote(arg);
+	}
+	return out;
+}
+
+/*****************************************************************************
+*   Windows specific
+*****************************************************************************/
+#if LL_WINDOWS
+
+static std::string WindowsErrorString(const std::string& operation);
+
+void LLProcess::autokill()
+{
+	// hopefully now handled by apr_procattr_autokill_set()
+}
+
+LLProcess::handle LLProcess::isRunning(handle h, const std::string& desc)
+{
+	// This direct Windows implementation is because we have no access to the
+	// apr_proc_t struct: we expect it's been destroyed.
+	if (! h)
+		return 0;
+
+	DWORD waitresult = WaitForSingleObject(h, 0);
+	if(waitresult == WAIT_OBJECT_0)
+	{
+		// the process has completed.
+		if (! desc.empty())
+		{
+			DWORD status = 0;
+			if (! GetExitCodeProcess(h, &status))
+			{
+				LL_WARNS("LLProcess") << desc << " terminated, but "
+									  << WindowsErrorString("GetExitCodeProcess()") << LL_ENDL;
+			}
+			{
+				LL_INFOS("LLProcess") << getStatusString(desc, interpret_status(status))
+									  << LL_ENDL;
+			}
+		}
+		CloseHandle(h);
+		return 0;
+	}
+
+	return h;
+}
+
+static LLProcess::Status interpret_status(int status)
+{
+	LLProcess::Status result;
+
+	// This bit of code is cribbed from apr/threadproc/win32/proc.c, a
+	// function (unfortunately static) called why_from_exit_code():
+	/* See WinNT.h STATUS_ACCESS_VIOLATION and family for how
+	 * this class of failures was determined
+	 */
+	if ((status & 0xFFFF0000) == 0xC0000000)
+	{
+		result.mState = LLProcess::KILLED;
+	}
+	else
+	{
+		result.mState = LLProcess::EXITED;
+	}
+	result.mData = status;
+
+	return result;
+}
+
+/// GetLastError()/FormatMessage() boilerplate
+static std::string WindowsErrorString(const std::string& operation)
+{
+	int result = GetLastError();
+
+	LPTSTR error_str = 0;
+	if (FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+					   NULL,
+					   result,
+					   0,
+					   (LPTSTR)&error_str,
+					   0,
+					   NULL)
+		!= 0) 
+	{
+		// convert from wide-char string to multi-byte string
+		char message[256];
+		wcstombs(message, error_str, sizeof(message));
+		message[sizeof(message)-1] = 0;
+		LocalFree(error_str);
+		// convert to std::string to trim trailing whitespace
+		std::string mbsstr(message);
+		mbsstr.erase(mbsstr.find_last_not_of(" \t\r\n"));
+		return STRINGIZE(operation << " failed (" << result << "): " << mbsstr);
+	}
+	return STRINGIZE(operation << " failed (" << result
+					 << "), but FormatMessage() did not explain");
+}
+
+/*****************************************************************************
+*   Posix specific
+*****************************************************************************/
+#else // Mac and linux
+
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+void LLProcess::autokill()
+{
+	// What we ought to do here is to:
+	// 1. create a unique process group and run all autokill children in that
+	//    group (see https://jira.secondlife.com/browse/SWAT-563);
+	// 2. figure out a way to intercept control when the viewer exits --
+	//    gracefully or not; 
+	// 3. when the viewer exits, kill off the aforementioned process group.
+
+	// It's point 2 that's troublesome. Although I've seen some signal-
+	// handling logic in the Posix viewer code, I haven't yet found any bit of
+	// code that's run no matter how the viewer exits (a try/finally for the
+	// whole process, as it were).
+}
+
+// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
+static bool reap_pid(pid_t pid, LLProcess::Status* pstatus=NULL)
+{
+	LLProcess::Status dummy;
+	if (! pstatus)
+	{
+		// If caller doesn't want to see Status, give us a target anyway so we
+		// don't have to have a bunch of conditionals.
+		pstatus = &dummy;
+	}
+
+	int status = 0;
+	pid_t wait_result = ::waitpid(pid, &status, WNOHANG);
+	if (wait_result == pid)
+	{
+		*pstatus = interpret_status(status);
+		return true;
+	}
+	if (wait_result == 0)
+	{
+		pstatus->mState = LLProcess::RUNNING;
+		pstatus->mData	= 0;
+		return false;
+	}
+
+	// Clear caller's Status block; caller must interpret UNSTARTED to mean
+	// "if this PID was ever valid, it no longer is."
+	*pstatus = LLProcess::Status();
+
+	// We've dealt with the success cases: we were able to reap the child
+	// (wait_result == pid) or it's still running (wait_result == 0). It may
+	// be that the child terminated but didn't hang around long enough for us
+	// to reap. In that case we still have no Status to report, but we can at
+	// least state that it's not running.
+	if (wait_result == -1 && errno == ECHILD)
+	{
+		// No such process -- this may mean we're ignoring SIGCHILD.
+		return true;
+	}
+
+	// Uh, should never happen?!
+	LL_WARNS("LLProcess") << "LLProcess::reap_pid(): waitpid(" << pid << ") returned "
+						  << wait_result << "; not meaningful?" << LL_ENDL;
+	// If caller is looping until this pid terminates, and if we can't find
+	// out, better to break the loop than to claim it's still running.
+	return true;
+}
+
+LLProcess::id LLProcess::isRunning(id pid, const std::string& desc)
+{
+	// This direct Posix implementation is because we have no access to the
+	// apr_proc_t struct: we expect it's been destroyed.
+	if (! pid)
+		return 0;
+
+	// Check whether the process has exited, and reap it if it has.
+	LLProcess::Status status;
+	if(reap_pid(pid, &status))
+	{
+		// the process has exited.
+		if (! desc.empty())
+		{
+			std::string statstr(desc + " apparently terminated: no status available");
+			// We don't just pass UNSTARTED to getStatusString() because, in
+			// the context of reap_pid(), that state has special meaning.
+			if (status.mState != UNSTARTED)
+			{
+				statstr = getStatusString(desc, status);
+			}
+			LL_INFOS("LLProcess") << statstr << LL_ENDL;
+		}
+		return 0;
+	}
+
+	return pid;
+}
+
+static LLProcess::Status interpret_status(int status)
+{
+	LLProcess::Status result;
+
+	if (WIFEXITED(status))
+	{
+		result.mState = LLProcess::EXITED;
+		result.mData  = WEXITSTATUS(status);
+	}
+	else if (WIFSIGNALED(status))
+	{
+		result.mState = LLProcess::KILLED;
+		result.mData  = WTERMSIG(status);
+	}
+	else                            // uh, shouldn't happen?
+	{
+		result.mState = LLProcess::EXITED;
+		result.mData  = status;     // someone else will have to decode
+	}
+
+	return result;
+}
+
+#endif  // Posix
diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h
new file mode 100644
index 0000000000000000000000000000000000000000..51010966f9d7dd48001606624b1a57fa6ac27e86
--- /dev/null
+++ b/indra/llcommon/llprocess.h
@@ -0,0 +1,546 @@
+/** 
+ * @file llprocess.h
+ * @brief Utility class for launching, terminating, and tracking child processes.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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$
+ */
+ 
+#ifndef LL_LLPROCESS_H
+#define LL_LLPROCESS_H
+
+#include "llinitparam.h"
+#include "llsdparam.h"
+#include "apr_thread_proc.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/optional.hpp>
+#include <boost/noncopyable.hpp>
+#include <iosfwd>                   // std::ostream
+#include <stdexcept>
+
+#if LL_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>                // HANDLE (eye roll)
+#elif LL_LINUX
+#if defined(Status)
+#undef Status
+#endif
+#endif
+
+class LLEventPump;
+
+class LLProcess;
+/// LLProcess instances are created on the heap by static factory methods and
+/// managed by ref-counted pointers.
+typedef boost::shared_ptr<LLProcess> LLProcessPtr;
+
+/**
+ * LLProcess handles launching an external process with specified command line
+ * arguments. It also keeps track of whether the process is still running, and
+ * can kill it if required.
+ *
+ * In discussing LLProcess, we use the term "parent" to refer to this process
+ * (the process invoking LLProcess), versus "child" to refer to the process
+ * spawned by LLProcess.
+ *
+ * LLProcess relies on periodic post() calls on the "mainloop" LLEventPump: an
+ * LLProcess object's Status won't update until the next "mainloop" tick. For
+ * instance, the Second Life viewer's main loop already posts to an
+ * LLEventPump by that name once per iteration. See
+ * indra/llcommon/tests/llprocess_test.cpp for an example of waiting for
+ * child-process termination in a standalone test context.
+ */
+class LL_COMMON_API LLProcess: public boost::noncopyable
+{
+	LOG_CLASS(LLProcess);
+public:
+	/**
+	 * Specify what to pass for each of child stdin, stdout, stderr.
+	 * @see LLProcess::Params::files.
+	 */
+	struct FileParam: public LLInitParam::Block<FileParam>
+	{
+		/**
+		 * type of file handle to pass to child process
+		 *
+		 * - "" (default): let the child inherit the same file handle used by
+		 *   this process. For instance, if passed as stdout, child stdout
+		 *   will be interleaved with stdout from this process. In this case,
+		 *   @a name is moot and should be left "".
+		 *
+		 * - "file": open an OS filesystem file with the specified @a name.
+		 *   <i>Not yet implemented.</i>
+		 *
+		 * - "pipe" or "tpipe" or "npipe": depends on @a name
+		 *
+		 *   - @a name.empty(): construct an OS pipe used only for this slot
+		 *     of the forthcoming child process.
+		 *
+		 *   - ! @a name.empty(): in a global registry, find or create (using
+		 *     the specified @a name) an OS pipe. The point of the (purely
+		 *     internal) @a name is that passing the same @a name in more than
+		 *     one slot for a given LLProcess -- or for slots in different
+		 *     LLProcess instances -- means the same pipe. For example, you
+		 *     might pass the same @a name value as both stdout and stderr to
+		 *     make the child process produce both on the same actual pipe. Or
+		 *     you might pass the same @a name as the stdout for one LLProcess
+		 *     and the stdin for another to connect the two child processes.
+		 *     Use LLProcess::getPipeName() to generate a unique name
+		 *     guaranteed not to already exist in the registry. <i>Not yet
+		 *     implemented.</i>
+		 *
+		 *   The difference between "pipe", "tpipe" and "npipe" is as follows.
+		 *
+		 *   - "pipe": direct LLProcess to monitor the parent end of the pipe,
+		 *     pumping nonblocking I/O every frame. The expectation (at least
+		 *     for stdout or stderr) is that the caller will listen for
+		 *     incoming data and consume it as it arrives. It's important not
+		 *     to neglect such a pipe, because it's buffered in memory. If you
+		 *     suspect the child may produce a great volume of output between
+		 *     frames, consider directing the child to write to a filesystem
+		 *     file instead, then read the file later.
+		 *
+		 *   - "tpipe": do not engage LLProcess machinery to monitor the
+		 *     parent end of the pipe. A "tpipe" is used only to connect
+		 *     different child processes. As such, it makes little sense to
+		 *     pass an empty @a name. <i>Not yet implemented.</i>
+		 *
+		 *   - "npipe": like "tpipe", but use an OS named pipe with a
+		 *     generated name. Note that @a name is the @em internal name of
+		 *     the pipe in our global registry -- it doesn't necessarily have
+		 *     anything to do with the pipe's name in the OS filesystem. Use
+		 *     LLProcess::getPipeName() to obtain the named pipe's OS
+		 *     filesystem name, e.g. to pass it as the @a name to another
+		 *     LLProcess instance using @a type "file". This supports usage
+		 *     like bash's &lt;(subcommand...) or &gt;(subcommand...)
+		 *     constructs. <i>Not yet implemented.</i>
+		 *
+		 * In all cases the open mode (read, write) is determined by the child
+		 * slot you're filling. Child stdin means select the "read" end of a
+		 * pipe, or open a filesystem file for reading; child stdout or stderr
+		 * means select the "write" end of a pipe, or open a filesystem file
+		 * for writing.
+		 *
+		 * Confusion such as passing the same pipe as the stdin of two
+		 * processes (rather than stdout for one and stdin for the other) is
+		 * explicitly permitted: it's up to the caller to construct meaningful
+		 * LLProcess pipe graphs.
+		 */
+		Optional<std::string> type;
+		Optional<std::string> name;
+
+		FileParam(const std::string& tp="", const std::string& nm=""):
+			type("type"),
+			name("name")
+		{
+			// If caller wants to specify values, use explicit assignment to
+			// set them rather than initialization.
+			if (! tp.empty()) type = tp;
+			if (! nm.empty()) name = nm;
+		}
+	};
+
+	/// Param block definition
+	struct Params: public LLInitParam::Block<Params>
+	{
+		Params():
+			executable("executable"),
+			args("args"),
+			cwd("cwd"),
+			autokill("autokill", true),
+			files("files"),
+			postend("postend"),
+			desc("desc")
+		{}
+
+		/// pathname of executable
+		Mandatory<std::string> executable;
+		/**
+		 * zero or more additional command-line arguments. Arguments are
+		 * passed through as exactly as we can manage, whitespace and all.
+		 * @note On Windows we manage this by implicitly double-quoting each
+		 * argument while assembling the command line.
+		 */
+		Multiple<std::string> args;
+		/// current working directory, if need it changed
+		Optional<std::string> cwd;
+		/// implicitly kill process on destruction of LLProcess object
+		/// (default true)
+		Optional<bool> autokill;
+		/**
+		 * Up to three FileParam items: for child stdin, stdout, stderr.
+		 * Passing two FileParam entries means default treatment for stderr,
+		 * and so forth.
+		 *
+		 * @note LLInitParam::Block permits usage like this:
+		 * @code
+		 * LLProcess::Params params;
+		 * ...
+		 * params.files
+		 *     .add(LLProcess::FileParam()) // stdin
+		 *     .add(LLProcess::FileParam().type("pipe") // stdout
+		 *     .add(LLProcess::FileParam().type("file").name("error.log"));
+		 * @endcode
+		 *
+		 * @note While it's theoretically plausible to pass additional open
+		 * file handles to a child specifically written to expect them, our
+		 * underlying implementation doesn't yet support that.
+		 */
+		Multiple<FileParam, AtMost<3> > files;
+		/**
+		 * On child-process termination, if this LLProcess object still
+		 * exists, post LLSD event to LLEventPump with specified name (default
+		 * no event). Event contains at least:
+		 *
+		 * - "id" as obtained from getProcessID()
+		 * - "desc" short string description of child (executable + pid)
+		 * - "state" @c state enum value, from Status.mState
+		 * - "data"	 if "state" is EXITED, exit code; if KILLED, on Posix,
+		 *   signal number
+		 * - "string" English text describing "state" and "data" (e.g. "exited
+		 *   with code 0")
+		 */
+		Optional<std::string> postend;
+		/**
+		 * Description of child process for logging purposes. It need not be
+		 * unique; the logged description string will contain the PID as well.
+		 * If this is omitted, a description will be derived from the
+		 * executable name.
+		 */
+		Optional<std::string> desc;
+	};
+	typedef LLSDParamAdapter<Params> LLSDOrParams;
+
+	/**
+	 * Factory accepting either plain LLSD::Map or Params block.
+	 * MAY RETURN DEFAULT-CONSTRUCTED LLProcessPtr if params invalid!
+	 */
+	static LLProcessPtr create(const LLSDOrParams& params);
+	virtual ~LLProcess();
+
+	/// Is child process still running?
+	bool isRunning() const;
+
+	/**
+	 * State of child process
+	 */
+	enum state
+	{
+		UNSTARTED,					///< initial value, invisible to consumer
+		RUNNING,					///< child process launched
+		EXITED,						///< child process terminated voluntarily
+		KILLED						///< child process terminated involuntarily
+	};
+
+	/**
+	 * Status info
+	 */
+	struct Status
+	{
+		Status():
+			mState(UNSTARTED),
+			mData(0)
+		{}
+
+		state mState;				///< @see state
+		/**
+		 * - for mState == EXITED: mData is exit() code
+		 * - for mState == KILLED: mData is signal number (Posix)
+		 * - otherwise: mData is undefined
+		 */
+		int mData;
+	};
+
+	/// Status query
+	Status getStatus() const;
+	/// English Status string query, for logging etc.
+	std::string getStatusString() const;
+	/// English Status string query for previously-captured Status
+	std::string getStatusString(const Status& status) const;
+	/// static English Status string query
+	static std::string getStatusString(const std::string& desc, const Status& status);
+
+	// Attempt to kill the process -- returns true if the process is no longer running when it returns.
+	// Note that even if this returns false, the process may exit some time after it's called.
+	bool kill(const std::string& who="");
+
+#if LL_WINDOWS
+	typedef int id;                 ///< as returned by getProcessID()
+	typedef HANDLE handle;          ///< as returned by getProcessHandle()
+#else
+	typedef pid_t id;
+	typedef pid_t handle;
+#endif
+	/**
+	 * Get an int-like id value. This is primarily intended for a human reader
+	 * to differentiate processes.
+	 */
+	id getProcessID() const;
+	/**
+	 * Get a "handle" of a kind that you might pass to platform-specific API
+	 * functions to engage features not directly supported by LLProcess.
+	 */
+	handle getProcessHandle() const;
+
+	/**
+	 * Test if a process (@c handle obtained from getProcessHandle()) is still
+	 * running. Return same nonzero @c handle value if still running, else
+	 * zero, so you can test it like a bool. But if you want to update a
+	 * stored variable as a side effect, you can write code like this:
+	 * @code
+	 * hchild = LLProcess::isRunning(hchild);
+	 * @endcode
+	 * @note This method is intended as a unit-test hook, not as the first of
+	 * a whole set of operations supported on freestanding @c handle values.
+	 * New functionality should be added as nonstatic members operating on
+	 * the same data as getProcessHandle().
+	 *
+	 * In particular, if child termination is detected by static isRunning()
+	 * rather than by nonstatic isRunning(), the LLProcess object won't be
+	 * aware of the child's changed status and may encounter OS errors trying
+	 * to obtain it. static isRunning() is only intended for after the
+	 * launching LLProcess object has been destroyed.
+	 */
+	static handle isRunning(handle, const std::string& desc="");
+
+	/// Provide symbolic access to child's file slots
+	enum FILESLOT { STDIN=0, STDOUT=1, STDERR=2, NSLOTS=3 };
+
+	/**
+	 * For a pipe constructed with @a type "npipe", obtain the generated OS
+	 * filesystem name for the specified pipe. Otherwise returns the empty
+	 * string. @see LLProcess::FileParam::type
+	 */
+	std::string getPipeName(FILESLOT) const;
+
+	/// base of ReadPipe, WritePipe
+	class LL_COMMON_API BasePipe
+	{
+	public:
+		virtual ~BasePipe() = 0;
+
+		typedef std::size_t size_type;
+		static const size_type npos;
+
+		/**
+		 * Get accumulated buffer length.
+		 *
+		 * For WritePipe, is there still pending data to send to child?
+		 *
+		 * For ReadPipe, we often need to refrain from actually reading the
+		 * std::istream returned by get_istream() until we've accumulated
+		 * enough data to make it worthwhile. For instance, if we're expecting
+		 * a number from the child, but the child happens to flush "12" before
+		 * emitting "3\n", get_istream() >> myint could return 12 rather than
+		 * 123!
+		 */
+		virtual size_type size() const = 0;
+	};
+
+	/// As returned by getWritePipe() or getOptWritePipe()
+	class WritePipe: public BasePipe
+	{
+	public:
+		/**
+		 * Get ostream& on which to write to child's stdin.
+		 *
+		 * @usage
+		 * @code
+		 * myProcess->getWritePipe().get_ostream() << "Hello, child!" << std::endl;
+		 * @endcode
+		 */
+		virtual std::ostream& get_ostream() = 0;
+	};
+
+	/// As returned by getReadPipe() or getOptReadPipe()
+	class ReadPipe: public BasePipe
+	{
+	public:
+		/**
+		 * Get istream& on which to read from child's stdout or stderr.
+		 *
+		 * @usage
+		 * @code
+		 * std::string stuff;
+		 * myProcess->getReadPipe().get_istream() >> stuff;
+		 * @endcode
+		 *
+		 * You should be sure in advance that the ReadPipe in question can
+		 * fill the request. @see getPump()
+		 */
+		virtual std::istream& get_istream() = 0;
+
+		/**
+		 * Like std::getline(get_istream(), line), but trims off trailing '\r'
+		 * to make calling code less platform-sensitive.
+		 */
+		virtual std::string getline() = 0;
+
+		/**
+		 * Like get_istream().read(buffer, n), but returns std::string rather
+		 * than requiring caller to construct a buffer, etc.
+		 */
+		virtual std::string read(size_type len) = 0;
+
+		/**
+		 * Peek at accumulated buffer data without consuming it. Optional
+		 * parameters give you substr() functionality.
+		 *
+		 * @note You can discard buffer data using get_istream().ignore(n).
+		 */
+		virtual std::string peek(size_type offset=0, size_type len=npos) const = 0;
+
+		/**
+		 * Detect presence of a substring (or char) in accumulated buffer data
+		 * without retrieving it. Optional offset allows you to search from
+		 * specified position.
+		 */
+		template <typename SEEK>
+		bool contains(SEEK seek, size_type offset=0) const
+		{ return find(seek, offset) != npos; }
+
+		/**
+		 * Search for a substring in accumulated buffer data without
+		 * retrieving it. Returns size_type position at which found, or npos
+		 * meaning not found. Optional offset allows you to search from
+		 * specified position.
+		 */
+		virtual size_type find(const std::string& seek, size_type offset=0) const = 0;
+
+		/**
+		 * Search for a char in accumulated buffer data without retrieving it.
+		 * Returns size_type position at which found, or npos meaning not
+		 * found. Optional offset allows you to search from specified
+		 * position.
+		 */
+		virtual size_type find(char seek, size_type offset=0) const = 0;
+
+		/**
+		 * Get LLEventPump& on which to listen for incoming data. The posted
+		 * LLSD::Map event will contain:
+		 *
+		 * - "data" part of pending data; see setLimit()
+		 * - "len" entire length of pending data, regardless of setLimit()
+		 * - "slot" this ReadPipe's FILESLOT, e.g. LLProcess::STDOUT
+		 * - "name" e.g. "stdout"
+		 * - "desc" e.g. "SLPlugin (pid) stdout"
+		 * - "eof" @c true means there no more data will arrive on this pipe,
+		 *   therefore no more events on this pump
+		 *
+		 * If the child sends "abc", and this ReadPipe posts "data"="abc", but
+		 * you don't consume it by reading the std::istream returned by
+		 * get_istream(), and the child next sends "def", ReadPipe will post
+		 * "data"="abcdef".
+		 */
+		virtual LLEventPump& getPump() = 0;
+
+		/**
+		 * Set maximum length of buffer data that will be posted in the LLSD
+		 * announcing arrival of new data from the child. If you call
+		 * setLimit(5), and the child sends "abcdef", the LLSD event will
+		 * contain "data"="abcde". However, you may still read the entire
+		 * "abcdef" from get_istream(): this limit affects only the size of
+		 * the data posted with the LLSD event. If you don't call this method,
+		 * @em no data will be posted: the default is 0 bytes.
+		 */
+		virtual void setLimit(size_type limit) = 0;
+
+		/**
+		 * Query the current setLimit() limit.
+		 */
+		virtual size_type getLimit() const = 0;
+	};
+
+	/// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to
+	/// create a pipe at the corresponding FILESLOT.
+	struct NoPipe: public std::runtime_error
+	{
+		NoPipe(const std::string& what): std::runtime_error(what) {}
+	};
+
+	/**
+	 * Get a reference to the (only) WritePipe for this LLProcess. @a slot, if
+	 * specified, must be STDIN. Throws NoPipe if you did not request a "pipe"
+	 * for child stdin. Use this method when you know how you created the
+	 * LLProcess in hand.
+	 */
+	WritePipe& getWritePipe(FILESLOT slot=STDIN);
+
+	/**
+	 * Get a boost::optional<WritePipe&> to the (only) WritePipe for this
+	 * LLProcess. @a slot, if specified, must be STDIN. The return value is
+	 * empty if you did not request a "pipe" for child stdin. Use this method
+	 * for inspecting an LLProcess you did not create.
+	 */
+	boost::optional<WritePipe&> getOptWritePipe(FILESLOT slot=STDIN);
+
+	/**
+	 * Get a reference to one of the ReadPipes for this LLProcess. @a slot, if
+	 * specified, must be STDOUT or STDERR. Throws NoPipe if you did not
+	 * request a "pipe" for child stdout or stderr. Use this method when you
+	 * know how you created the LLProcess in hand.
+	 */
+	ReadPipe& getReadPipe(FILESLOT slot);
+
+	/**
+	 * Get a boost::optional<ReadPipe&> to one of the ReadPipes for this
+	 * LLProcess. @a slot, if specified, must be STDOUT or STDERR. The return
+	 * value is empty if you did not request a "pipe" for child stdout or
+	 * stderr. Use this method for inspecting an LLProcess you did not create.
+	 */
+	boost::optional<ReadPipe&> getOptReadPipe(FILESLOT slot);
+
+	/// little utilities that really should already be somewhere else in the
+	/// code base
+	static std::string basename(const std::string& path);
+	static std::string getline(std::istream&);
+
+private:
+	/// constructor is private: use create() instead
+	LLProcess(const LLSDOrParams& params);
+	void autokill();
+	// Classic-C-style APR callback
+	static void status_callback(int reason, void* data, int status);
+	// Object-oriented callback
+	void handle_status(int reason, int status);
+	// implementation for get[Opt][Read|Write]Pipe()
+	template <class PIPETYPE>
+	PIPETYPE& getPipe(FILESLOT slot);
+	template <class PIPETYPE>
+	boost::optional<PIPETYPE&> getOptPipe(FILESLOT slot);
+	template <class PIPETYPE>
+	PIPETYPE* getPipePtr(std::string& error, FILESLOT slot);
+
+	std::string mDesc;
+	std::string mPostend;
+	apr_proc_t mProcess;
+	bool mAutokill;
+	Status mStatus;
+	// explicitly want this ptr_vector to be able to store NULLs
+	typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector;
+	PipeVector mPipes;
+};
+
+/// for logging
+LL_COMMON_API std::ostream& operator<<(std::ostream&, const LLProcess::Params&);
+
+#endif // LL_LLPROCESS_H
diff --git a/indra/llcommon/llprocesslauncher.cpp b/indra/llcommon/llprocesslauncher.cpp
deleted file mode 100644
index 10950181fd30bf0f182d19e5954a3847eb8ec1a7..0000000000000000000000000000000000000000
--- a/indra/llcommon/llprocesslauncher.cpp
+++ /dev/null
@@ -1,357 +0,0 @@
-/** 
- * @file llprocesslauncher.cpp
- * @brief Utility class for launching, terminating, and tracking the state of processes.
- *
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, 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$
- */
- 
-#include "linden_common.h"
-
-#include "llprocesslauncher.h"
-
-#include <iostream>
-#if LL_DARWIN || LL_LINUX
-// not required or present on Win32
-#include <sys/wait.h>
-#endif
-
-LLProcessLauncher::LLProcessLauncher()
-{
-#if LL_WINDOWS
-	mProcessHandle = 0;
-#else
-	mProcessID = 0;
-#endif
-}
-
-LLProcessLauncher::~LLProcessLauncher()
-{
-	kill();
-}
-
-void LLProcessLauncher::setExecutable(const std::string &executable)
-{
-	mExecutable = executable;
-}
-
-void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
-{
-	mWorkingDir = dir;
-}
-
-const std::string& LLProcessLauncher::getExecutable() const
-{
-	return mExecutable;
-}
-
-void LLProcessLauncher::clearArguments()
-{
-	mLaunchArguments.clear();
-}
-
-void LLProcessLauncher::addArgument(const std::string &arg)
-{
-	mLaunchArguments.push_back(arg);
-}
-
-void LLProcessLauncher::addArgument(const char *arg)
-{
-	mLaunchArguments.push_back(std::string(arg));
-}
-
-#if LL_WINDOWS
-
-int LLProcessLauncher::launch(void)
-{
-	// If there was already a process associated with this object, kill it.
-	kill();
-	orphan();
-
-	int result = 0;
-	
-	PROCESS_INFORMATION pinfo;
-	STARTUPINFOA sinfo;
-	memset(&sinfo, 0, sizeof(sinfo));
-	
-	std::string args = mExecutable;
-	for(int i = 0; i < (int)mLaunchArguments.size(); i++)
-	{
-		args += " ";
-		args += mLaunchArguments[i];
-	}
-	
-	// So retarded.  Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
-	char *args2 = new char[args.size() + 1];
-	strcpy(args2, args.c_str());
-
-	const char * working_directory = 0;
-	if(!mWorkingDir.empty()) working_directory = mWorkingDir.c_str();
-	if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, working_directory, &sinfo, &pinfo ) )
-	{
-		result = GetLastError();
-
-		LPTSTR error_str = 0;
-		if(
-			FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-				NULL,
-				result,
-				0,
-				(LPTSTR)&error_str,
-				0,
-				NULL) 
-			!= 0) 
-		{
-			char message[256];
-			wcstombs(message, error_str, 256);
-			message[255] = 0;
-			llwarns << "CreateProcessA failed: " << message << llendl;
-			LocalFree(error_str);
-		}
-
-		if(result == 0)
-		{
-			// Make absolutely certain we return a non-zero value on failure.
-			result = -1;
-		}
-	}
-	else
-	{
-		// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
-		// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
-		mProcessHandle = pinfo.hProcess;
-		CloseHandle(pinfo.hThread); // stops leaks - nothing else
-	}		
-	
-	delete[] args2;
-	
-	return result;
-}
-
-bool LLProcessLauncher::isRunning(void)
-{
-	if(mProcessHandle != 0)		
-	{
-		DWORD waitresult = WaitForSingleObject(mProcessHandle, 0);
-		if(waitresult == WAIT_OBJECT_0)
-		{
-			// the process has completed.
-			mProcessHandle = 0;
-		}			
-	}
-
-	return (mProcessHandle != 0);
-}
-bool LLProcessLauncher::kill(void)
-{
-	bool result = true;
-	
-	if(mProcessHandle != 0)
-	{
-		TerminateProcess(mProcessHandle,0);
-
-		if(isRunning())
-		{
-			result = false;
-		}
-	}
-	
-	return result;
-}
-
-void LLProcessLauncher::orphan(void)
-{
-	// Forget about the process
-	mProcessHandle = 0;
-}
-
-// static 
-void LLProcessLauncher::reap(void)
-{
-	// No actions necessary on Windows.
-}
-
-#else // Mac and linux
-
-#include <signal.h>
-#include <fcntl.h>
-#include <errno.h>
-
-static std::list<pid_t> sZombies;
-
-// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
-static bool reap_pid(pid_t pid)
-{
-	bool result = false;
-	
-	pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
-	if(wait_result == pid)
-	{
-		result = true;
-	}
-	else if(wait_result == -1)
-	{
-		if(errno == ECHILD)
-		{
-			// No such process -- this may mean we're ignoring SIGCHILD.
-			result = true;
-		}
-	}
-	
-	return result;
-}
-
-int LLProcessLauncher::launch(void)
-{
-	// If there was already a process associated with this object, kill it.
-	kill();
-	orphan();
-	
-	int result = 0;
-	int current_wd = -1;
-	
-	// create an argv vector for the child process
-	const char ** fake_argv = new const char *[mLaunchArguments.size() + 2];  // 1 for the executable path, 1 for the NULL terminator
-
-	int i = 0;
-	
-	// add the executable path
-	fake_argv[i++] = mExecutable.c_str();
-	
-	// and any arguments
-	for(int j=0; j < mLaunchArguments.size(); j++)
-		fake_argv[i++] = mLaunchArguments[j].c_str();
-	
-	// terminate with a null pointer
-	fake_argv[i] = NULL;
-	
-	if(!mWorkingDir.empty())
-	{
-		// save the current working directory
-		current_wd = ::open(".", O_RDONLY);
-	
-		// and change to the one the child will be executed in
-		if (::chdir(mWorkingDir.c_str()))
-		{
-			// chdir failed
-		}
-	}
-		
- 	// flush all buffers before the child inherits them
- 	::fflush(NULL);
-
-	pid_t id = vfork();
-	if(id == 0)
-	{
-		// child process
-		
-		::execv(mExecutable.c_str(), (char * const *)fake_argv);
-		
-		// If we reach this point, the exec failed.
-		// Use _exit() instead of exit() per the vfork man page.
-		_exit(0);
-	}
-
-	// parent process
-	
-	if(current_wd >= 0)
-	{
-		// restore the previous working directory
-		if (::fchdir(current_wd))
-		{
-			// chdir failed
-		}
-		::close(current_wd);
-	}
-	
-	delete[] fake_argv;
-	
-	mProcessID = id;
-
-	return result;
-}
-
-bool LLProcessLauncher::isRunning(void)
-{
-	if(mProcessID != 0)
-	{
-		// Check whether the process has exited, and reap it if it has.
-		if(reap_pid(mProcessID))
-		{
-			// the process has exited.
-			mProcessID = 0;
-		}
-	}
-	
-	return (mProcessID != 0);
-}
-
-bool LLProcessLauncher::kill(void)
-{
-	bool result = true;
-	
-	if(mProcessID != 0)
-	{
-		// Try to kill the process.  We'll do approximately the same thing whether the kill returns an error or not, so we ignore the result.
-		(void)::kill(mProcessID, SIGTERM);
-		
-		// This will have the side-effect of reaping the zombie if the process has exited.
-		if(isRunning())
-		{
-			result = false;
-		}
-	}
-	
-	return result;
-}
-
-void LLProcessLauncher::orphan(void)
-{
-	// Disassociate the process from this object
-	if(mProcessID != 0)
-	{	
-		// We may still need to reap the process's zombie eventually
-		sZombies.push_back(mProcessID);
-	
-		mProcessID = 0;
-	}
-}
-
-// static 
-void LLProcessLauncher::reap(void)
-{
-	// Attempt to real all saved process ID's.
-	
-	std::list<pid_t>::iterator iter = sZombies.begin();
-	while(iter != sZombies.end())
-	{
-		if(reap_pid(*iter))
-		{
-			iter = sZombies.erase(iter);
-		}
-		else
-		{
-			iter++;
-		}
-	}
-}
-
-#endif
diff --git a/indra/llcommon/llprocesslauncher.h b/indra/llcommon/llprocesslauncher.h
deleted file mode 100644
index 954c2491472eb2e17b11122fe7d95eeb53348fff..0000000000000000000000000000000000000000
--- a/indra/llcommon/llprocesslauncher.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/** 
- * @file llprocesslauncher.h
- * @brief Utility class for launching, terminating, and tracking the state of processes.
- *
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, 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$
- */
- 
-#ifndef LL_LLPROCESSLAUNCHER_H
-#define LL_LLPROCESSLAUNCHER_H
-
-#if LL_WINDOWS
-#include <windows.h>
-#endif
-
-
-/*
-	LLProcessLauncher handles launching external processes with specified command line arguments.
-	It also keeps track of whether the process is still running, and can kill it if required.
-*/
-
-class LL_COMMON_API LLProcessLauncher
-{
-	LOG_CLASS(LLProcessLauncher);
-public:
-	LLProcessLauncher();
-	virtual ~LLProcessLauncher();
-	
-	void setExecutable(const std::string &executable);
-	void setWorkingDirectory(const std::string &dir);
-
-	const std::string& getExecutable() const;
-
-	void clearArguments();
-	void addArgument(const std::string &arg);
-	void addArgument(const char *arg);
-		
-	int launch(void);
-	bool isRunning(void);
-	
-	// Attempt to kill the process -- returns true if the process is no longer running when it returns.
-	// Note that even if this returns false, the process may exit some time after it's called.
-	bool kill(void);
-	
-	// Use this if you want the external process to continue execution after the LLProcessLauncher instance controlling it is deleted.
-	// Normally, the destructor will attempt to kill the process and wait for termination.
-	// This should only be used if the viewer is about to exit -- otherwise, the child process will become a zombie after it exits.
-	void orphan(void);	
-	
-	// This needs to be called periodically on Mac/Linux to clean up zombie processes.
-	static void reap(void);
-	
-	// Accessors for platform-specific process ID
-#if LL_WINDOWS
-	HANDLE getProcessHandle() { return mProcessHandle; };
-#else
-	pid_t getProcessID() { return mProcessID; };
-#endif	
-	
-private:
-	std::string mExecutable;
-	std::string mWorkingDir;
-	std::vector<std::string> mLaunchArguments;
-	
-#if LL_WINDOWS
-	HANDLE mProcessHandle;
-#else
-	pid_t mProcessID;
-#endif
-};
-
-#endif // LL_LLPROCESSLAUNCHER_H
diff --git a/indra/llxuixml/llregistry.h b/indra/llcommon/llregistry.h
similarity index 94%
rename from indra/llxuixml/llregistry.h
rename to indra/llcommon/llregistry.h
index 36ce6a97b720c75d347d972948f16fee80ce8a2f..36d7f7a44c89617b6f34c0a11698eb6b06367a54 100644
--- a/indra/llxuixml/llregistry.h
+++ b/indra/llcommon/llregistry.h
@@ -31,6 +31,7 @@
 
 #include <boost/type_traits.hpp>
 #include "llsingleton.h"
+#include "lltypeinfolookup.h"
 
 template <typename T>
 class LLRegistryDefaultComparator
@@ -38,6 +39,24 @@ class LLRegistryDefaultComparator
 	bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; }
 };
 
+template <typename KEY, typename VALUE>
+struct LLRegistryMapSelector
+{
+    typedef std::map<KEY, VALUE> type;
+};
+
+template <typename VALUE>
+struct LLRegistryMapSelector<std::type_info*, VALUE>
+{
+    typedef LLTypeInfoLookup<VALUE> type;
+};
+
+template <typename VALUE>
+struct LLRegistryMapSelector<const std::type_info*, VALUE>
+{
+    typedef LLTypeInfoLookup<VALUE> type;
+};
+
 template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
 class LLRegistry
 {
@@ -53,7 +72,7 @@ public:
 	{
 		friend class LLRegistry<KEY, VALUE, COMPARATOR>;
 	public:
-		typedef typename std::map<KEY, VALUE> registry_map_t;
+		typedef typename LLRegistryMapSelector<KEY, VALUE>::type registry_map_t;
 
 		bool add(ref_const_key_t key, ref_const_value_t value)
 		{
diff --git a/indra/llui/llsdparam.cpp b/indra/llcommon/llsdparam.cpp
similarity index 100%
rename from indra/llui/llsdparam.cpp
rename to indra/llcommon/llsdparam.cpp
diff --git a/indra/llui/llsdparam.h b/indra/llcommon/llsdparam.h
similarity index 96%
rename from indra/llui/llsdparam.h
rename to indra/llcommon/llsdparam.h
index 3dfc6d020ee33ada34bf74a17145ef8f85533786..6ef5debd7b74eddf82a4d961e5491faab62f7758 100644
--- a/indra/llui/llsdparam.h
+++ b/indra/llcommon/llsdparam.h
@@ -31,7 +31,7 @@
 #include "llinitparam.h"
 #include "boost/function.hpp"
 
-struct LLParamSDParserUtilities
+struct LL_COMMON_API LLParamSDParserUtilities
 {
 	static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range);
 
@@ -40,7 +40,7 @@ struct LLParamSDParserUtilities
 	static void readSDValues(read_sd_cb_t cb, const LLSD& sd);
 };
 
-class LLParamSDParser 
+class LL_COMMON_API LLParamSDParser 
 :	public LLInitParam::Parser
 {
 LOG_CLASS(LLParamSDParser);
@@ -92,7 +92,7 @@ private:
 };
 
 
-extern LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR;
+extern LL_COMMON_API LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR;
 template<typename T>
 class LLSDParamAdapter : public T
 {
diff --git a/indra/llcommon/llsortedvector.h b/indra/llcommon/llsortedvector.h
new file mode 100644
index 0000000000000000000000000000000000000000..391b82ee4461033d6bb57a1acca4c41762831d96
--- /dev/null
+++ b/indra/llcommon/llsortedvector.h
@@ -0,0 +1,152 @@
+/**
+ * @file   llsortedvector.h
+ * @author Nat Goodspeed
+ * @date   2012-04-08
+ * @brief  LLSortedVector class wraps a vector that we maintain in sorted
+ *         order so we can perform binary-search lookups.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLSORTEDVECTOR_H)
+#define LL_LLSORTEDVECTOR_H
+
+#include <vector>
+#include <algorithm>
+
+/**
+ * LLSortedVector contains a std::vector<std::pair> that we keep sorted on the
+ * first of the pair. This makes insertion somewhat more expensive than simple
+ * std::vector::push_back(), but allows us to use binary search for lookups.
+ * It's intended for small aggregates where lookup is far more performance-
+ * critical than insertion; in such cases a binary search on a small, sorted
+ * std::vector can be more performant than a std::map lookup.
+ */
+template <typename KEY, typename VALUE>
+class LLSortedVector
+{
+public:
+    typedef LLSortedVector<KEY, VALUE> self;
+    typedef KEY key_type;
+    typedef VALUE mapped_type;
+    typedef std::pair<key_type, mapped_type> value_type;
+    typedef std::vector<value_type> PairVector;
+    typedef typename PairVector::iterator iterator;
+    typedef typename PairVector::const_iterator const_iterator;
+
+    /// Empty
+    LLSortedVector() {}
+
+    /// Fixed initial size
+    LLSortedVector(std::size_t size):
+        mVector(size)
+    {}
+
+    /// Bulk load
+    template <typename ITER>
+    LLSortedVector(ITER begin, ITER end):
+        mVector(begin, end)
+    {
+        // Allow caller to dump in a bunch of (pairs convertible to)
+        // value_type if desired, but make sure we sort afterwards.
+        std::sort(mVector.begin(), mVector.end());
+    }
+
+    /// insert(key, value)
+    std::pair<iterator, bool> insert(const key_type& key, const mapped_type& value)
+    {
+        return insert(value_type(key, value));
+    }
+
+    /// insert(value_type)
+    std::pair<iterator, bool> insert(const value_type& pair)
+    {
+        typedef std::pair<iterator, bool> iterbool;
+        iterator found = std::lower_bound(mVector.begin(), mVector.end(), pair,
+                                          less<value_type>());
+        // have to check for end() before it's even valid to dereference
+        if (found == mVector.end())
+        {
+            std::size_t index(mVector.size());
+            mVector.push_back(pair);
+            // don't forget that push_back() invalidates 'found'
+            return iterbool(mVector.begin() + index, true);
+        }
+        if (found->first == pair.first)
+        {
+            return iterbool(found, false);
+        }
+        // remember that insert() invalidates 'found' -- save index
+        std::size_t index(found - mVector.begin());
+        mVector.insert(found, pair);
+        // okay, convert from index back to iterator
+        return iterbool(mVector.begin() + index, true);
+    }
+
+    iterator begin() { return mVector.begin(); }
+    iterator end()   { return mVector.end(); }
+    const_iterator begin() const { return mVector.begin(); }
+    const_iterator end()   const { return mVector.end(); }
+
+    bool empty() const { return mVector.empty(); }
+    std::size_t size() const { return mVector.size(); }
+
+    /// find
+    iterator find(const key_type& key)
+    {
+        iterator found = std::lower_bound(mVector.begin(), mVector.end(),
+                                          value_type(key, mapped_type()),
+                                          less<value_type>());
+        if (found == mVector.end() || found->first != key)
+            return mVector.end();
+        return found;
+    }
+
+    const_iterator find(const key_type& key) const
+    {
+        return const_cast<self*>(this)->find(key);
+    }
+
+private:
+    // Define our own 'less' comparator so we can specialize without messing
+    // with std::less.
+    template <typename T>
+    struct less: public std::less<T> {};
+
+    // Specialize 'less' for an LLSortedVector::value_type involving
+    // std::type_info*. This is one of LLSortedVector's foremost use cases. We
+    // specialize 'less' rather than just defining a specific comparator
+    // because LLSortedVector should be usable for other key_types as well.
+    template <typename T>
+    struct less< std::pair<std::type_info*, T> >:
+        public std::binary_function<std::pair<std::type_info*, T>,
+                                    std::pair<std::type_info*, T>,
+                                    bool>
+    {
+        bool operator()(const std::pair<std::type_info*, T>& lhs,
+                        const std::pair<std::type_info*, T>& rhs) const
+        {
+            return lhs.first->before(*rhs.first);
+        }
+    };
+
+    // Same as above, but with const std::type_info*.
+    template <typename T>
+    struct less< std::pair<const std::type_info*, T> >:
+        public std::binary_function<std::pair<const std::type_info*, T>,
+                                    std::pair<const std::type_info*, T>,
+                                    bool>
+    {
+        bool operator()(const std::pair<const std::type_info*, T>& lhs,
+                        const std::pair<const std::type_info*, T>& rhs) const
+        {
+            return lhs.first->before(*rhs.first);
+        }
+    };
+
+    PairVector mVector;
+};
+
+#endif /* ! defined(LL_LLSORTEDVECTOR_H) */
diff --git a/indra/llcommon/llstreamqueue.cpp b/indra/llcommon/llstreamqueue.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1116a2b6a2cacfd76e7f563606cdfaf76b822a54
--- /dev/null
+++ b/indra/llcommon/llstreamqueue.cpp
@@ -0,0 +1,24 @@
+/**
+ * @file   llstreamqueue.cpp
+ * @author Nat Goodspeed
+ * @date   2012-01-05
+ * @brief  Implementation for llstreamqueue.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llstreamqueue.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+
+// As of this writing, llstreamqueue.h is entirely template-based, therefore
+// we don't strictly need a corresponding .cpp file. However, our CMake test
+// macro assumes one. Here it is.
+bool llstreamqueue_cpp_ignored = true;
diff --git a/indra/llcommon/llstreamqueue.h b/indra/llcommon/llstreamqueue.h
new file mode 100644
index 0000000000000000000000000000000000000000..0726bad1757679f14f5099e27459799d6ccb3dfc
--- /dev/null
+++ b/indra/llcommon/llstreamqueue.h
@@ -0,0 +1,240 @@
+/**
+ * @file   llstreamqueue.h
+ * @author Nat Goodspeed
+ * @date   2012-01-04
+ * @brief  Definition of LLStreamQueue
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLSTREAMQUEUE_H)
+#define LL_LLSTREAMQUEUE_H
+
+#include <string>
+#include <list>
+#include <iosfwd>                   // std::streamsize
+#include <boost/iostreams/categories.hpp>
+
+/**
+ * This class is a growable buffer between a producer and consumer. It serves
+ * as a queue usable with Boost.Iostreams -- hence, a "stream queue."
+ *
+ * This is especially useful for buffering nonblocking I/O. For instance, we
+ * want application logic to be able to serialize LLSD to a std::ostream. We
+ * may write more data than the destination pipe can handle all at once, but
+ * it's imperative NOT to block the application-level serialization call. So
+ * we buffer it instead. Successive frames can try nonblocking writes to the
+ * destination pipe until all buffered data has been sent.
+ *
+ * Similarly, we want application logic be able to deserialize LLSD from a
+ * std::istream. Again, we must not block that deserialize call waiting for
+ * more data to arrive from the input pipe! Instead we build up a buffer over
+ * a number of frames, using successive nonblocking reads, until we have
+ * "enough" data to be able to present it through a std::istream.
+ *
+ * @note The use cases for this class overlap somewhat with those for the
+ * LLIOPipe/LLPumpIO hierarchies, and indeed we considered using those. This
+ * class has two virtues over the older machinery:
+ *
+ * # It's vastly simpler -- way fewer concepts. It's not clear to me whether
+ *   there were ever LLIOPipe/etc. use cases that demanded all the fanciness
+ *   rolled in, or whether they were simply overdesigned. In any case, no
+ *   remaining Lindens will admit to familiarity with those classes -- and
+ *   they're sufficiently obtuse that it would take considerable learning
+ *   curve to figure out how to use them properly. The bottom line is that
+ *   current management is not keen on any more engineers climbing that curve.
+ * # This class is designed around available components such as std::string,
+ *   std::list, Boost.Iostreams. There's less proprietary code.
+ */
+template <typename Ch>
+class LLGenericStreamQueue
+{
+public:
+    LLGenericStreamQueue():
+        mSize(0),
+        mClosed(false)
+    {}
+
+    /**
+     * Boost.Iostreams Source Device facade for use with other Boost.Iostreams
+     * functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
+     * 1.48 Iostreams concepts; instead it behaves as both a Sink and a
+     * Source. This is its Source facade.
+     */
+    struct Source
+    {
+        typedef Ch char_type;
+        typedef boost::iostreams::source_tag category;
+
+        /// Bind the underlying LLGenericStreamQueue
+        Source(LLGenericStreamQueue& sq):
+            mStreamQueue(sq)
+        {}
+
+        // Read up to n characters from the underlying data source into the
+        // buffer s, returning the number of characters read; return -1 to
+        // indicate EOF
+        std::streamsize read(Ch* s, std::streamsize n)
+        {
+            return mStreamQueue.read(s, n);
+        }
+
+        LLGenericStreamQueue& mStreamQueue;
+    };
+
+    /**
+     * Boost.Iostreams Sink Device facade for use with other Boost.Iostreams
+     * functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
+     * 1.48 Iostreams concepts; instead it behaves as both a Sink and a
+     * Source. This is its Sink facade.
+     */
+    struct Sink
+    {
+        typedef Ch char_type;
+        typedef boost::iostreams::sink_tag category;
+
+        /// Bind the underlying LLGenericStreamQueue
+        Sink(LLGenericStreamQueue& sq):
+            mStreamQueue(sq)
+        {}
+
+        /// Write up to n characters from the buffer s to the output sequence,
+        /// returning the number of characters written
+        std::streamsize write(const Ch* s, std::streamsize n)
+        {
+            return mStreamQueue.write(s, n);
+        }
+
+        /// Send EOF to consumer
+        void close()
+        {
+            mStreamQueue.close();
+        }
+
+        LLGenericStreamQueue& mStreamQueue;
+    };
+
+    /// Present Boost.Iostreams Source facade
+    Source asSource() { return Source(*this); }
+    /// Present Boost.Iostreams Sink facade
+    Sink   asSink()   { return Sink(*this); }
+
+    /// append data to buffer
+    std::streamsize write(const Ch* s, std::streamsize n)
+    {
+        // Unclear how often we might be asked to write 0 bytes -- perhaps a
+        // naive caller responding to an unready nonblocking read. But if we
+        // do get such a call, don't add a completely empty BufferList entry.
+        if (n == 0)
+            return n;
+        // We could implement this using a single std::string object, a la
+        // ostringstream. But the trouble with appending to a string is that
+        // you might have to recopy all previous contents to grow its size. If
+        // we want this to scale to large data volumes, better to allocate
+        // individual pieces.
+        mBuffer.push_back(string(s, n));
+        mSize += n;
+        return n;
+    }
+
+    /**
+     * Inform this LLGenericStreamQueue that no further data are forthcoming.
+     * For our purposes, close() is strictly a producer-side operation;
+     * there's little point in closing the consumer side.
+     */
+    void close()
+    {
+        mClosed = true;
+    }
+
+    /// consume data from buffer
+    std::streamsize read(Ch* s, std::streamsize n)
+    {
+        // read() is actually a convenience method for peek() followed by
+        // skip().
+        std::streamsize got(peek(s, n));
+        // We can only skip() as many characters as we can peek(); ignore
+        // skip() return here.
+        skip(n);
+        return got;
+    }
+
+    /// Retrieve data from buffer without consuming. Like read(), return -1 on
+    /// EOF.
+    std::streamsize peek(Ch* s, std::streamsize n) const;
+
+    /// Consume data from buffer without retrieving. Unlike read() and peek(),
+    /// at EOF we simply skip 0 characters.
+    std::streamsize skip(std::streamsize n);
+
+    /// How many characters do we currently have buffered?
+    std::streamsize size() const
+    {
+        return mSize;
+    }
+
+private:
+    typedef std::basic_string<Ch> string;
+    typedef std::list<string> BufferList;
+    BufferList mBuffer;
+    std::streamsize mSize;
+    bool mClosed;
+};
+
+template <typename Ch>
+std::streamsize LLGenericStreamQueue<Ch>::peek(Ch* s, std::streamsize n) const
+{
+    // Here we may have to build up 'n' characters from an arbitrary
+    // number of individual BufferList entries.
+    typename BufferList::const_iterator bli(mBuffer.begin()), blend(mBuffer.end());
+    // Indicate EOF if producer has closed the pipe AND we've exhausted
+    // all previously-buffered data.
+    if (mClosed && bli == blend)
+    {
+        return -1;
+    }
+    // Here either producer hasn't yet closed, or we haven't yet exhausted
+    // remaining data.
+    std::streamsize needed(n), got(0);
+    // Loop until either we run out of BufferList entries or we've
+    // completely satisfied the request.
+    for ( ; bli != blend && needed; ++bli)
+    {
+        std::streamsize chunk(std::min(needed, std::streamsize(bli->length())));
+        std::copy(bli->begin(), bli->begin() + chunk, s);
+        needed -= chunk;
+        s      += chunk;
+        got    += chunk;
+    }
+    return got;
+}
+
+template <typename Ch>
+std::streamsize LLGenericStreamQueue<Ch>::skip(std::streamsize n)
+{
+    typename BufferList::iterator bli(mBuffer.begin()), blend(mBuffer.end());
+    std::streamsize toskip(n), skipped(0);
+    while (bli != blend && toskip >= bli->length())
+    {
+        std::streamsize chunk(bli->length());
+        typename BufferList::iterator zap(bli++);
+        mBuffer.erase(zap);
+        mSize   -= chunk;
+        toskip  -= chunk;
+        skipped += chunk;
+    }
+    if (bli != blend && toskip)
+    {
+        bli->erase(bli->begin(), bli->begin() + toskip);
+        mSize   -= toskip;
+        skipped += toskip;
+    }
+    return skipped;
+}
+
+typedef LLGenericStreamQueue<char>    LLStreamQueue;
+typedef LLGenericStreamQueue<wchar_t> LLWStreamQueue;
+
+#endif /* ! defined(LL_LLSTREAMQUEUE_H) */
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index e7fe6568085e8b0691f160bf312359f6f7822c42..fa0eb9f72c7a501b92bf26d6c2accc25d9993138 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -912,22 +912,24 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions);
 template<> 
 void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims)
 {
-	std::string currToken;
-	std::string::size_type begIdx, endIdx;
-
-	begIdx = instr.find_first_not_of (delims);
-	while (begIdx != std::string::npos)
+	// Starting at offset 0, scan forward for the next non-delimiter. We're
+	// done when the only characters left in 'instr' are delimiters.
+	for (std::string::size_type begIdx, endIdx = 0;
+		 (begIdx = instr.find_first_not_of (delims, endIdx)) != std::string::npos; )
 	{
+		// Found a non-delimiter. After that, find the next delimiter.
 		endIdx = instr.find_first_of (delims, begIdx);
 		if (endIdx == std::string::npos)
 		{
+			// No more delimiters: this token extends to the end of the string.
 			endIdx = instr.length();
 		}
 
-		currToken = instr.substr(begIdx, endIdx - begIdx);
+		// extract the token between begIdx and endIdx; substr() needs length
+		std::string currToken(instr.substr(begIdx, endIdx - begIdx));
 		LLStringUtil::trim (currToken);
 		tokens.push_back(currToken);
-		begIdx = instr.find_first_not_of (delims, endIdx);
+		// next scan past delimiters starts at endIdx
 	}
 }
 
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 7e41e787b52814a97d590cf171463627b6602250..09733e8e2a16cb3d8eb0b11175f9a89b84282b61 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -40,6 +40,7 @@
 #endif
 
 #include <string.h>
+#include <boost/scoped_ptr.hpp>
 
 #if LL_SOLARIS
 // stricmp and strnicmp do not exist on Solaris:
@@ -237,40 +238,77 @@ private:
 	static std::string sLocale;
 
 public:
-	typedef typename std::basic_string<T>::size_type size_type;
+	typedef std::basic_string<T> string_type;
+	typedef typename string_type::size_type size_type;
 	
 public:
 	/////////////////////////////////////////////////////////////////////////////////////////
 	// Static Utility functions that operate on std::strings
 
-	static const std::basic_string<T> null;
+	static const string_type null;
 	
 	typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
-	LL_COMMON_API static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
-	LL_COMMON_API static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
-	LL_COMMON_API static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
-	LL_COMMON_API static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
-	LL_COMMON_API static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
-	LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
-	LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
+	/// considers any sequence of delims as a single field separator
+	LL_COMMON_API static void getTokens(const string_type& instr,
+										std::vector<string_type >& tokens,
+										const string_type& delims);
+	/// like simple scan overload, but returns scanned vector
+	static std::vector<string_type> getTokens(const string_type& instr,
+											  const string_type& delims);
+	/// add support for keep_delims and quotes (either could be empty string)
+	static void getTokens(const string_type& instr,
+						  std::vector<string_type>& tokens,
+						  const string_type& drop_delims,
+						  const string_type& keep_delims,
+						  const string_type& quotes=string_type());
+	/// like keep_delims-and-quotes overload, but returns scanned vector
+	static std::vector<string_type> getTokens(const string_type& instr,
+											  const string_type& drop_delims,
+											  const string_type& keep_delims,
+											  const string_type& quotes=string_type());
+	/// add support for escapes (could be empty string)
+	static void getTokens(const string_type& instr,
+						  std::vector<string_type>& tokens,
+						  const string_type& drop_delims,
+						  const string_type& keep_delims,
+						  const string_type& quotes,
+						  const string_type& escapes);
+	/// like escapes overload, but returns scanned vector
+	static std::vector<string_type> getTokens(const string_type& instr,
+											  const string_type& drop_delims,
+											  const string_type& keep_delims,
+											  const string_type& quotes,
+											  const string_type& escapes);
+
+	LL_COMMON_API static void formatNumber(string_type& numStr, string_type decimals);
+	LL_COMMON_API static bool formatDatetime(string_type& replacement, string_type token, string_type param, S32 secFromEpoch);
+	LL_COMMON_API static S32 format(string_type& s, const format_map_t& substitutions);
+	LL_COMMON_API static S32 format(string_type& s, const LLSD& substitutions);
+	LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const format_map_t& substitutions);
+	LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const LLSD& substitutions);
 	LL_COMMON_API static void setLocale (std::string inLocale);
 	LL_COMMON_API static std::string getLocale (void);
 	
-	static bool isValidIndex(const std::basic_string<T>& string, size_type i)
+	static bool isValidIndex(const string_type& string, size_type i)
 	{
 		return !string.empty() && (0 <= i) && (i <= string.size());
 	}
 
-	static void	trimHead(std::basic_string<T>& string);
-	static void	trimTail(std::basic_string<T>& string);
-	static void	trim(std::basic_string<T>& string)	{ trimHead(string); trimTail(string); }
-	static void truncate(std::basic_string<T>& string, size_type count);
+	static bool contains(const string_type& string, T c, size_type i=0)
+	{
+		return string.find(c, i) != string_type::npos;
+	}
 
-	static void	toUpper(std::basic_string<T>& string);
-	static void	toLower(std::basic_string<T>& string);
+	static void	trimHead(string_type& string);
+	static void	trimTail(string_type& string);
+	static void	trim(string_type& string)	{ trimHead(string); trimTail(string); }
+	static void truncate(string_type& string, size_type count);
+
+	static void	toUpper(string_type& string);
+	static void	toLower(string_type& string);
 	
 	// True if this is the head of s.
-	static BOOL	isHead( const std::basic_string<T>& string, const T* s ); 
+	static BOOL	isHead( const string_type& string, const T* s ); 
 
 	/**
 	 * @brief Returns true if string starts with substr
@@ -278,8 +316,8 @@ public:
 	 * If etither string or substr are empty, this method returns false.
 	 */
 	static bool startsWith(
-		const std::basic_string<T>& string,
-		const std::basic_string<T>& substr);
+		const string_type& string,
+		const string_type& substr);
 
 	/**
 	 * @brief Returns true if string ends in substr
@@ -287,19 +325,32 @@ public:
 	 * If etither string or substr are empty, this method returns false.
 	 */
 	static bool endsWith(
-		const std::basic_string<T>& string,
-		const std::basic_string<T>& substr);
+		const string_type& string,
+		const string_type& substr);
 
-	static void	addCRLF(std::basic_string<T>& string);
-	static void	removeCRLF(std::basic_string<T>& string);
+	static void	addCRLF(string_type& string);
+	static void	removeCRLF(string_type& string);
 
-	static void	replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
-	static void	replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
-	static void	replaceChar( std::basic_string<T>& string, T target, T replacement );
-	static void replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement );
+	static void	replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab );
+	static void	replaceNonstandardASCII( string_type& string, T replacement );
+	static void	replaceChar( string_type& string, T target, T replacement );
+	static void replaceString( string_type& string, string_type target, string_type replacement );
 	
-	static BOOL	containsNonprintable(const std::basic_string<T>& string);
-	static void	stripNonprintable(std::basic_string<T>& string);
+	static BOOL	containsNonprintable(const string_type& string);
+	static void	stripNonprintable(string_type& string);
+
+	/**
+	 * Double-quote an argument string if needed, unless it's already
+	 * double-quoted. Decide whether it's needed based on the presence of any
+	 * character in @a triggers (default space or double-quote). If we quote
+	 * it, escape any embedded double-quote with the @a escape string (default
+	 * backslash).
+	 *
+	 * Passing triggers="" means always quote, unless it's already double-quoted.
+	 */
+	static string_type quote(const string_type& str,
+							 const string_type& triggers=" \"",
+							 const string_type& escape="\\");
 
 	/**
 	 * @brief Unsafe way to make ascii characters. You should probably
@@ -308,18 +359,18 @@ public:
 	 * The 2 and 4 byte std::string probably work, so LLWStringUtil::_makeASCII
 	 * should work.
 	 */
-	static void _makeASCII(std::basic_string<T>& string);
+	static void _makeASCII(string_type& string);
 
 	// Conversion to other data types
-	static BOOL	convertToBOOL(const std::basic_string<T>& string, BOOL& value);
-	static BOOL	convertToU8(const std::basic_string<T>& string, U8& value);
-	static BOOL	convertToS8(const std::basic_string<T>& string, S8& value);
-	static BOOL	convertToS16(const std::basic_string<T>& string, S16& value);
-	static BOOL	convertToU16(const std::basic_string<T>& string, U16& value);
-	static BOOL	convertToU32(const std::basic_string<T>& string, U32& value);
-	static BOOL	convertToS32(const std::basic_string<T>& string, S32& value);
-	static BOOL	convertToF32(const std::basic_string<T>& string, F32& value);
-	static BOOL	convertToF64(const std::basic_string<T>& string, F64& value);
+	static BOOL	convertToBOOL(const string_type& string, BOOL& value);
+	static BOOL	convertToU8(const string_type& string, U8& value);
+	static BOOL	convertToS8(const string_type& string, S8& value);
+	static BOOL	convertToS16(const string_type& string, S16& value);
+	static BOOL	convertToU16(const string_type& string, U16& value);
+	static BOOL	convertToU32(const string_type& string, U32& value);
+	static BOOL	convertToS32(const string_type& string, S32& value);
+	static BOOL	convertToF32(const string_type& string, F32& value);
+	static BOOL	convertToF64(const string_type& string, F64& value);
 
 	/////////////////////////////////////////////////////////////////////////////////////////
 	// Utility functions for working with char*'s and strings
@@ -327,24 +378,24 @@ public:
 	// Like strcmp but also handles empty strings. Uses
 	// current locale.
 	static S32		compareStrings(const T* lhs, const T* rhs);
-	static S32		compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+	static S32		compareStrings(const string_type& lhs, const string_type& rhs);
 	
 	// case insensitive version of above. Uses current locale on
 	// Win32, and falls back to a non-locale aware comparison on
 	// Linux.
 	static S32		compareInsensitive(const T* lhs, const T* rhs);
-	static S32		compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+	static S32		compareInsensitive(const string_type& lhs, const string_type& rhs);
 
 	// Case sensitive comparison with good handling of numbers.  Does not use current locale.
 	// a.k.a. strdictcmp()
-	static S32		compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
+	static S32		compareDict(const string_type& a, const string_type& b);
 
 	// Case *in*sensitive comparison with good handling of numbers.  Does not use current locale.
 	// a.k.a. strdictcmp()
-	static S32		compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b);
+	static S32		compareDictInsensitive(const string_type& a, const string_type& b);
 
 	// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
-	static BOOL		precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
+	static BOOL		precedesDict( const string_type& a, const string_type& b );
 
 	// A replacement for strncpy.
 	// If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
@@ -352,7 +403,7 @@ public:
 	static void		copy(T* dst, const T* src, size_type dst_size);
 	
 	// Copies src into dst at a given offset.  
-	static void		copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
+	static void		copyInto(string_type& dst, const string_type& src, size_type offset);
 	
 	static bool		isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
 
@@ -362,7 +413,7 @@ public:
 #endif
 
 private:
-	LL_COMMON_API static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
+	LL_COMMON_API static size_type getSubstitution(const string_type& instr, size_type& start, std::vector<string_type >& tokens);
 };
 
 template<class T> const std::basic_string<T> LLStringUtilBase<T>::null;
@@ -636,10 +687,325 @@ namespace LLStringFn
 ////////////////////////////////////////////////////////////
 // NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
 // There is no LLWStringUtil::format implementation currently.
-// Calling thse for anything other than LLStringUtil will produce link errors.
+// Calling these for anything other than LLStringUtil will produce link errors.
 
 ////////////////////////////////////////////////////////////
 
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr, const string_type& delims)
+{
+	std::vector<string_type> tokens;
+	getTokens(instr, tokens, delims);
+	return tokens;
+}
+
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr,
+							   const string_type& drop_delims,
+							   const string_type& keep_delims,
+							   const string_type& quotes)
+{
+	std::vector<string_type> tokens;
+	getTokens(instr, tokens, drop_delims, keep_delims, quotes);
+	return tokens;
+}
+
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr,
+							   const string_type& drop_delims,
+							   const string_type& keep_delims,
+							   const string_type& quotes,
+							   const string_type& escapes)
+{
+	std::vector<string_type> tokens;
+	getTokens(instr, tokens, drop_delims, keep_delims, quotes, escapes);
+	return tokens;
+}
+
+namespace LLStringUtilBaseImpl
+{
+
+/**
+ * Input string scanner helper for getTokens(), or really any other
+ * character-parsing routine that may have to deal with escape characters.
+ * This implementation defines the concept (also an interface, should you
+ * choose to implement the concept by subclassing) and provides trivial
+ * implementations for a string @em without escape processing.
+ */
+template <class T>
+struct InString
+{
+	typedef std::basic_string<T> string_type;
+	typedef typename string_type::const_iterator const_iterator;
+
+	InString(const_iterator b, const_iterator e):
+		mIter(b),
+		mEnd(e)
+	{}
+	virtual ~InString() {}
+
+	bool done() const { return mIter == mEnd; }
+	/// Is the current character (*mIter) escaped? This implementation can
+	/// answer trivially because it doesn't support escapes.
+	virtual bool escaped() const { return false; }
+	/// Obtain the current character and advance @c mIter.
+	virtual T next() { return *mIter++; }
+	/// Does the current character match specified character?
+	virtual bool is(T ch) const { return (! done()) && *mIter == ch; }
+	/// Is the current character any one of the specified characters?
+	virtual bool oneof(const string_type& delims) const
+	{
+		return (! done()) && LLStringUtilBase<T>::contains(delims, *mIter);
+	}
+
+	/**
+	 * Scan forward from @from until either @a delim or end. This is primarily
+	 * useful for processing quoted substrings.
+	 *
+	 * If we do see @a delim, append everything from @from until (excluding)
+	 * @a delim to @a into, advance @c mIter to skip @a delim, and return @c
+	 * true.
+	 *
+	 * If we do not see @a delim, do not alter @a into or @c mIter and return
+	 * @c false. Do not pass GO, do not collect $200.
+	 *
+	 * @note The @c false case described above implements normal getTokens()
+	 * treatment of an unmatched open quote: treat the quote character as if
+	 * escaped, that is, simply collect it as part of the current token. Other
+	 * plausible behaviors directly affect the way getTokens() deals with an
+	 * unmatched quote: e.g. throwing an exception to treat it as an error, or
+	 * assuming a close quote beyond end of string (in which case return @c
+	 * true).
+	 */
+	virtual bool collect_until(string_type& into, const_iterator from, T delim)
+	{
+		const_iterator found = std::find(from, mEnd, delim);
+		// If we didn't find delim, change nothing, just tell caller.
+		if (found == mEnd)
+			return false;
+		// Found delim! Append everything between from and found.
+		into.append(from, found);
+		// advance past delim in input
+		mIter = found + 1;
+		return true;
+	}
+
+	const_iterator mIter, mEnd;
+};
+
+/// InString subclass that handles escape characters
+template <class T>
+class InEscString: public InString<T>
+{
+public:
+	typedef InString<T> super;
+	typedef typename super::string_type string_type;
+	typedef typename super::const_iterator const_iterator;
+	using super::done;
+	using super::mIter;
+	using super::mEnd;
+
+	InEscString(const_iterator b, const_iterator e, const string_type& escapes):
+		super(b, e),
+		mEscapes(escapes)
+	{
+		// Even though we've already initialized 'mIter' via our base-class
+		// constructor, set it again to check for initial escape char.
+		setiter(b);
+	}
+
+	/// This implementation uses the answer cached by setiter().
+	virtual bool escaped() const { return mIsEsc; }
+	virtual T next()
+	{
+		// If we're looking at the escape character of an escape sequence,
+		// skip that character. This is the one time we can modify 'mIter'
+		// without using setiter: for this one case we DO NOT CARE if the
+		// escaped character is itself an escape.
+		if (mIsEsc)
+			++mIter;
+		// If we were looking at an escape character, this is the escaped
+		// character; otherwise it's just the next character.
+		T result(*mIter);
+		// Advance mIter, checking for escape sequence.
+		setiter(mIter + 1);
+		return result;
+	}
+
+	virtual bool is(T ch) const
+	{
+		// Like base-class is(), except that an escaped character matches
+		// nothing.
+		return (! done()) && (! mIsEsc) && *mIter == ch;
+	}
+
+	virtual bool oneof(const string_type& delims) const
+	{
+		// Like base-class oneof(), except that an escaped character matches
+		// nothing.
+		return (! done()) && (! mIsEsc) && LLStringUtilBase<T>::contains(delims, *mIter);
+	}
+
+	virtual bool collect_until(string_type& into, const_iterator from, T delim)
+	{
+		// Deal with escapes in the characters we collect; that is, an escaped
+		// character must become just that character without the preceding
+		// escape. Collect characters in a separate string rather than
+		// directly appending to 'into' in case we do not find delim, in which
+		// case we're supposed to leave 'into' unmodified.
+		string_type collected;
+		// For scanning purposes, we're going to work directly with 'mIter'.
+		// Save its current value in case we fail to see delim.
+		const_iterator save_iter(mIter);
+		// Okay, set 'mIter', checking for escape.
+		setiter(from);
+		while (! done())
+		{
+			// If we see an unescaped delim, stop and report success.
+			if ((! mIsEsc) && *mIter == delim)
+			{
+				// Append collected chars to 'into'.
+				into.append(collected);
+				// Don't forget to advance 'mIter' past delim.
+				setiter(mIter + 1);
+				return true;
+			}
+			// We're not at end, and either we're not looking at delim or it's
+			// escaped. Collect this character and keep going.
+			collected.push_back(next());
+		}
+		// Here we hit 'mEnd' without ever seeing delim. Restore mIter and tell
+		// caller.
+		setiter(save_iter);
+		return false;
+	}
+
+private:
+	void setiter(const_iterator i)
+	{
+		mIter = i;
+
+		// Every time we change 'mIter', set 'mIsEsc' to be able to repetitively
+		// answer escaped() without having to rescan 'mEscapes'. mIsEsc caches
+		// contains(mEscapes, *mIter).
+
+		// We're looking at an escaped char if we're not already at end (that
+		// is, *mIter is even meaningful); if *mIter is in fact one of the
+		// specified escape characters; and if there's one more character
+		// following it. That is, if an escape character is the very last
+		// character of the input string, it loses its special meaning.
+		mIsEsc = (! done()) &&
+				LLStringUtilBase<T>::contains(mEscapes, *mIter) &&
+				(mIter+1) != mEnd;
+	}
+
+	const string_type mEscapes;
+	bool mIsEsc;
+};
+
+/// getTokens() implementation based on InString concept
+template <typename INSTRING, typename string_type>
+void getTokens(INSTRING& instr, std::vector<string_type>& tokens,
+			   const string_type& drop_delims, const string_type& keep_delims,
+			   const string_type& quotes)
+{
+	// There are times when we want to match either drop_delims or
+	// keep_delims. Concatenate them up front to speed things up.
+	string_type all_delims(drop_delims + keep_delims);
+	// no tokens yet
+	tokens.clear();
+
+	// try for another token
+	while (! instr.done())
+	{
+		// scan past any drop_delims
+		while (instr.oneof(drop_delims))
+		{
+			// skip this drop_delim
+			instr.next();
+			// but if that was the end of the string, done
+			if (instr.done())
+				return;
+		}
+		// found the start of another token: make a slot for it
+		tokens.push_back(string_type());
+		if (instr.oneof(keep_delims))
+		{
+			// *iter is a keep_delim, a token of exactly 1 character. Append
+			// that character to the new token and proceed.
+			tokens.back().push_back(instr.next());
+			continue;
+		}
+		// Here we have a non-delimiter token, which might consist of a mix of
+		// quoted and unquoted parts. Use bash rules for quoting: you can
+		// embed a quoted substring in the midst of an unquoted token (e.g.
+		// ~/"sub dir"/myfile.txt); you can ram two quoted substrings together
+		// to make a single token (e.g. 'He said, "'"Don't."'"'). We diverge
+		// from bash in that bash considers an unmatched quote an error. Our
+		// param signature doesn't allow for errors, so just pretend it's not
+		// a quote and embed it.
+		// At this level, keep scanning until we hit the next delimiter of
+		// either type (drop_delims or keep_delims).
+		while (! instr.oneof(all_delims))
+		{
+			// If we're looking at an open quote, search forward for
+			// a close quote, collecting characters along the way.
+			if (instr.oneof(quotes) &&
+				instr.collect_until(tokens.back(), instr.mIter+1, *instr.mIter))
+			{
+				// collect_until is cleverly designed to do exactly what we
+				// need here. No further action needed if it returns true.
+			}
+			else
+			{
+				// Either *iter isn't a quote, or there's no matching close
+				// quote: in other words, just an ordinary char. Append it to
+				// current token.
+				tokens.back().push_back(instr.next());
+			}
+			// having scanned that segment of this token, if we've reached the
+			// end of the string, we're done
+			if (instr.done())
+				return;
+		}
+	}
+}
+
+} // namespace LLStringUtilBaseImpl
+
+// static
+template <class T>
+void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
+									const string_type& drop_delims, const string_type& keep_delims,
+									const string_type& quotes)
+{
+	// Because this overload doesn't support escapes, use simple InString to
+	// manage input range.
+	LLStringUtilBaseImpl::InString<T> instring(string.begin(), string.end());
+	LLStringUtilBaseImpl::getTokens(instring, tokens, drop_delims, keep_delims, quotes);
+}
+
+// static
+template <class T>
+void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
+									const string_type& drop_delims, const string_type& keep_delims,
+									const string_type& quotes, const string_type& escapes)
+{
+	// This overload must deal with escapes. Delegate that to InEscString
+	// (unless there ARE no escapes).
+	boost::scoped_ptr< LLStringUtilBaseImpl::InString<T> > instrp;
+	if (escapes.empty())
+		instrp.reset(new LLStringUtilBaseImpl::InString<T>(string.begin(), string.end()));
+	else
+		instrp.reset(new LLStringUtilBaseImpl::InEscString<T>(string.begin(), string.end(), escapes));
+	LLStringUtilBaseImpl::getTokens(*instrp, tokens, drop_delims, keep_delims, quotes);
+}
 
 // static
 template<class T> 
@@ -669,7 +1035,7 @@ S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs)
 
 //static 
 template<class T> 
-S32 LLStringUtilBase<T>::compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+S32 LLStringUtilBase<T>::compareStrings(const string_type& lhs, const string_type& rhs)
 {
 	return LLStringOps::collate(lhs.c_str(), rhs.c_str());
 }
@@ -695,8 +1061,8 @@ S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
 	}
 	else
 	{
-		std::basic_string<T> lhs_string(lhs);
-		std::basic_string<T> rhs_string(rhs);
+		string_type lhs_string(lhs);
+		string_type rhs_string(rhs);
 		LLStringUtilBase<T>::toUpper(lhs_string);
 		LLStringUtilBase<T>::toUpper(rhs_string);
 		result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
@@ -706,10 +1072,10 @@ S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
 
 //static 
 template<class T> 
-S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+S32 LLStringUtilBase<T>::compareInsensitive(const string_type& lhs, const string_type& rhs)
 {
-	std::basic_string<T> lhs_string(lhs);
-	std::basic_string<T> rhs_string(rhs);
+	string_type lhs_string(lhs);
+	string_type rhs_string(rhs);
 	LLStringUtilBase<T>::toUpper(lhs_string);
 	LLStringUtilBase<T>::toUpper(rhs_string);
 	return LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
@@ -720,7 +1086,7 @@ S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, con
 
 //static 
 template<class T>
-S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+S32 LLStringUtilBase<T>::compareDict(const string_type& astr, const string_type& bstr)
 {
 	const T* a = astr.c_str();
 	const T* b = bstr.c_str();
@@ -761,7 +1127,7 @@ S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std
 
 // static
 template<class T>
-S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+S32 LLStringUtilBase<T>::compareDictInsensitive(const string_type& astr, const string_type& bstr)
 {
 	const T* a = astr.c_str();
 	const T* b = bstr.c_str();
@@ -796,7 +1162,7 @@ S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr
 // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
 // static 
 template<class T> 
-BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
+BOOL LLStringUtilBase<T>::precedesDict( const string_type& a, const string_type& b )
 {
 	if( a.size() && b.size() )
 	{
@@ -810,7 +1176,7 @@ BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)	
+void LLStringUtilBase<T>::toUpper(string_type& string)	
 { 
 	if( !string.empty() )
 	{ 
@@ -824,7 +1190,7 @@ void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
+void LLStringUtilBase<T>::toLower(string_type& string)
 { 
 	if( !string.empty() )
 	{ 
@@ -838,7 +1204,7 @@ void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
+void LLStringUtilBase<T>::trimHead(string_type& string)
 {			
 	if( !string.empty() )
 	{
@@ -853,7 +1219,7 @@ void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
+void LLStringUtilBase<T>::trimTail(string_type& string)
 {			
 	if( string.size() )
 	{
@@ -872,7 +1238,7 @@ void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
 // Replace line feeds with carriage return-line feed pairs.
 //static
 template<class T>
-void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
+void LLStringUtilBase<T>::addCRLF(string_type& string)
 {
 	const T LF = 10;
 	const T CR = 13;
@@ -914,7 +1280,7 @@ void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
 // Remove all carriage returns
 //static
 template<class T> 
-void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
+void LLStringUtilBase<T>::removeCRLF(string_type& string)
 {
 	const T CR = 13;
 
@@ -935,10 +1301,10 @@ void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
+void LLStringUtilBase<T>::replaceChar( string_type& string, T target, T replacement )
 {
 	size_type found_pos = 0;
-	while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos ) 
+	while( (found_pos = string.find(target, found_pos)) != string_type::npos ) 
 	{
 		string[found_pos] = replacement;
 		found_pos++; // avoid infinite defeat if target == replacement
@@ -947,10 +1313,10 @@ void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement )
+void LLStringUtilBase<T>::replaceString( string_type& string, string_type target, string_type replacement )
 {
 	size_type found_pos = 0;
-	while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
+	while( (found_pos = string.find(target, found_pos)) != string_type::npos )
 	{
 		string.replace( found_pos, target.length(), replacement );
 		found_pos += replacement.length(); // avoid infinite defeat if replacement contains target
@@ -959,7 +1325,7 @@ void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basi
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
+void LLStringUtilBase<T>::replaceNonstandardASCII( string_type& string, T replacement )
 {
 	const char LF = 10;
 	const S8 MIN = 32;
@@ -979,12 +1345,12 @@ void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string,
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab )
+void LLStringUtilBase<T>::replaceTabsWithSpaces( string_type& str, size_type spaces_per_tab )
 {
 	const T TAB = '\t';
 	const T SPACE = ' ';
 
-	std::basic_string<T> out_str;
+	string_type out_str;
 	// Replace tabs with spaces
 	for (size_type i = 0; i < str.length(); i++)
 	{
@@ -1003,7 +1369,7 @@ void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size
 
 //static
 template<class T> 
-BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& string)
+BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string)
 {
 	const char MIN = 32;
 	BOOL rv = FALSE;
@@ -1020,7 +1386,7 @@ BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& strin
 
 //static
 template<class T> 
-void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
+void LLStringUtilBase<T>::stripNonprintable(string_type& string)
 {
 	const char MIN = 32;
 	size_type j = 0;
@@ -1051,8 +1417,43 @@ void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
 	delete []c_string;
 }
 
+template<class T>
+std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str,
+												const string_type& triggers,
+												const string_type& escape)
+{
+	size_type len(str.length());
+	// If the string is already quoted, assume user knows what s/he's doing.
+	if (len >= 2 && str[0] == '"' && str[len-1] == '"')
+	{
+		return str;
+	}
+
+	// Not already quoted: do we need to? triggers.empty() is a special case
+	// meaning "always quote."
+	if ((! triggers.empty()) && str.find_first_of(triggers) == string_type::npos)
+	{
+		// no trigger characters, don't bother quoting
+		return str;
+	}
+
+	// For whatever reason, we must quote this string.
+	string_type result;
+	result.push_back('"');
+	for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci)
+	{
+		if (*ci == '"')
+		{
+			result.append(escape);
+		}
+		result.push_back(*ci);
+	}
+	result.push_back('"');
+	return result;
+}
+
 template<class T> 
-void LLStringUtilBase<T>::_makeASCII(std::basic_string<T>& string)
+void LLStringUtilBase<T>::_makeASCII(string_type& string)
 {
 	// Replace non-ASCII chars with LL_UNKNOWN_CHAR
 	for (size_type i = 0; i < string.length(); i++)
@@ -1082,7 +1483,7 @@ void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size )
 
 // static
 template<class T> 
-void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
+void LLStringUtilBase<T>::copyInto(string_type& dst, const string_type& src, size_type offset)
 {
 	if ( offset == dst.length() )
 	{
@@ -1092,7 +1493,7 @@ void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_s
 	}
 	else
 	{
-		std::basic_string<T> tail = dst.substr(offset);
+		string_type tail = dst.substr(offset);
 
 		dst = dst.substr(0, offset);
 		dst += src;
@@ -1103,7 +1504,7 @@ void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_s
 // True if this is the head of s.
 //static
 template<class T> 
-BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s ) 
+BOOL LLStringUtilBase<T>::isHead( const string_type& string, const T* s ) 
 { 
 	if( string.empty() )
 	{
@@ -1119,8 +1520,8 @@ BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s
 // static
 template<class T> 
 bool LLStringUtilBase<T>::startsWith(
-	const std::basic_string<T>& string,
-	const std::basic_string<T>& substr)
+	const string_type& string,
+	const string_type& substr)
 {
 	if(string.empty() || (substr.empty())) return false;
 	if(0 == string.find(substr)) return true;
@@ -1130,8 +1531,8 @@ bool LLStringUtilBase<T>::startsWith(
 // static
 template<class T> 
 bool LLStringUtilBase<T>::endsWith(
-	const std::basic_string<T>& string,
-	const std::basic_string<T>& substr)
+	const string_type& string,
+	const string_type& substr)
 {
 	if(string.empty() || (substr.empty())) return false;
 	std::string::size_type idx = string.rfind(substr);
@@ -1141,14 +1542,14 @@ bool LLStringUtilBase<T>::endsWith(
 
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
+BOOL LLStringUtilBase<T>::convertToBOOL(const string_type& string, BOOL& value)
 {
 	if( string.empty() )
 	{
 		return FALSE;
 	}
 
-	std::basic_string<T> temp( string );
+	string_type temp( string );
 	trim(temp);
 	if( 
 		(temp == "1") || 
@@ -1178,7 +1579,7 @@ BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& value) 
+BOOL LLStringUtilBase<T>::convertToU8(const string_type& string, U8& value) 
 {
 	S32 value32 = 0;
 	BOOL success = convertToS32(string, value32);
@@ -1191,7 +1592,7 @@ BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& va
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& value) 
+BOOL LLStringUtilBase<T>::convertToS8(const string_type& string, S8& value) 
 {
 	S32 value32 = 0;
 	BOOL success = convertToS32(string, value32);
@@ -1204,7 +1605,7 @@ BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& va
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16& value) 
+BOOL LLStringUtilBase<T>::convertToS16(const string_type& string, S16& value) 
 {
 	S32 value32 = 0;
 	BOOL success = convertToS32(string, value32);
@@ -1217,7 +1618,7 @@ BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16&
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16& value) 
+BOOL LLStringUtilBase<T>::convertToU16(const string_type& string, U16& value) 
 {
 	S32 value32 = 0;
 	BOOL success = convertToS32(string, value32);
@@ -1230,17 +1631,17 @@ BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16&
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32& value) 
+BOOL LLStringUtilBase<T>::convertToU32(const string_type& string, U32& value) 
 {
 	if( string.empty() )
 	{
 		return FALSE;
 	}
 
-	std::basic_string<T> temp( string );
+	string_type temp( string );
 	trim(temp);
 	U32 v;
-	std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+	std::basic_istringstream<T> i_stream((string_type)temp);
 	if(i_stream >> v)
 	{
 		value = v;
@@ -1250,17 +1651,17 @@ BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32&
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32& value) 
+BOOL LLStringUtilBase<T>::convertToS32(const string_type& string, S32& value) 
 {
 	if( string.empty() )
 	{
 		return FALSE;
 	}
 
-	std::basic_string<T> temp( string );
+	string_type temp( string );
 	trim(temp);
 	S32 v;
-	std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+	std::basic_istringstream<T> i_stream((string_type)temp);
 	if(i_stream >> v)
 	{
 		//TODO: figure out overflow and underflow reporting here
@@ -1277,7 +1678,7 @@ BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32&
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32& value) 
+BOOL LLStringUtilBase<T>::convertToF32(const string_type& string, F32& value) 
 {
 	F64 value64 = 0.0;
 	BOOL success = convertToF64(string, value64);
@@ -1290,17 +1691,17 @@ BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32&
 }
 
 template<class T> 
-BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
+BOOL LLStringUtilBase<T>::convertToF64(const string_type& string, F64& value)
 {
 	if( string.empty() )
 	{
 		return FALSE;
 	}
 
-	std::basic_string<T> temp( string );
+	string_type temp( string );
 	trim(temp);
 	F64 v;
-	std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+	std::basic_istringstream<T> i_stream((string_type)temp);
 	if(i_stream >> v)
 	{
 		//TODO: figure out overflow and underflow reporting here
@@ -1317,7 +1718,7 @@ BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64&
 }
 
 template<class T> 
-void LLStringUtilBase<T>::truncate(std::basic_string<T>& string, size_type count)
+void LLStringUtilBase<T>::truncate(string_type& string, size_type count)
 {
 	size_type cur_size = string.size();
 	string.resize(count < cur_size ? count : cur_size);
diff --git a/indra/llcommon/lltypeinfolookup.h b/indra/llcommon/lltypeinfolookup.h
new file mode 100644
index 0000000000000000000000000000000000000000..7510cc12ed7d473a96e97bf58633fd6c8bd4eeba
--- /dev/null
+++ b/indra/llcommon/lltypeinfolookup.h
@@ -0,0 +1,110 @@
+/**
+ * @file   lltypeinfolookup.h
+ * @author Nat Goodspeed
+ * @date   2012-04-08
+ * @brief  Template data structure like std::map<std::type_info*, T>
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLTYPEINFOLOOKUP_H)
+#define LL_LLTYPEINFOLOOKUP_H
+
+#include "llsortedvector.h"
+#include <typeinfo>
+
+/**
+ * LLTypeInfoLookup is specifically designed for use cases for which you might
+ * consider std::map<std::type_info*, VALUE>. We have several such data
+ * structures in the viewer. The trouble with them is that at least on Linux,
+ * you can't rely on always getting the same std::type_info* for a given type:
+ * different load modules will produce different std::type_info*.
+ * LLTypeInfoLookup contains a workaround to address this issue.
+ *
+ * Specifically, when we don't find the passed std::type_info*,
+ * LLTypeInfoLookup performs a linear search over registered entries to
+ * compare name() strings. Presuming that this succeeds, we cache the new
+ * (previously unrecognized) std::type_info* to speed future lookups.
+ *
+ * This worst-case fallback search (linear search with string comparison)
+ * should only happen the first time we look up a given type from a particular
+ * load module other than the one from which we initially registered types.
+ * (However, a lookup which wouldn't succeed anyway will always have
+ * worst-case performance.) This class is probably best used with less than a
+ * few dozen different types.
+ */
+template <typename VALUE>
+class LLTypeInfoLookup
+{
+public:
+    typedef LLTypeInfoLookup<VALUE> self;
+    typedef LLSortedVector<const std::type_info*, VALUE> vector_type;
+    typedef typename vector_type::key_type key_type;
+    typedef typename vector_type::mapped_type mapped_type;
+    typedef typename vector_type::value_type value_type;
+    typedef typename vector_type::iterator iterator;
+    typedef typename vector_type::const_iterator const_iterator;
+
+    LLTypeInfoLookup() {}
+
+    iterator begin() { return mVector.begin(); }
+    iterator end()   { return mVector.end(); }
+    const_iterator begin() const { return mVector.begin(); }
+    const_iterator end()   const { return mVector.end(); }
+    bool empty() const { return mVector.empty(); }
+    std::size_t size() const { return mVector.size(); }
+
+    std::pair<iterator, bool> insert(const std::type_info* key, const VALUE& value)
+    {
+        return insert(value_type(key, value));
+    }
+
+    std::pair<iterator, bool> insert(const value_type& pair)
+    {
+        return mVector.insert(pair);
+    }
+
+    // const find() forwards to non-const find(): this can alter mVector!
+    const_iterator find(const std::type_info* key) const
+    {
+        return const_cast<self*>(this)->find(key);
+    }
+
+    // non-const find() caches previously-unknown type_info* to speed future
+    // lookups.
+    iterator find(const std::type_info* key)
+    {
+        iterator found = mVector.find(key);
+        if (found != mVector.end())
+        {
+            // If LLSortedVector::find() found, great, we're done.
+            return found;
+        }
+        // Here we didn't find the passed type_info*. On Linux, though, even
+        // for the same type, typeid(sametype) produces a different type_info*
+        // when used in different load modules. So the fact that we didn't
+        // find the type_info* we seek doesn't mean this type isn't
+        // registered. Scan for matching name() string.
+        for (typename vector_type::iterator ti(mVector.begin()), tend(mVector.end());
+             ti != tend; ++ti)
+        {
+            if (std::string(ti->first->name()) == key->name())
+            {
+                // This unrecognized 'key' is for the same type as ti->first.
+                // To speed future lookups, insert a new entry that lets us
+                // look up ti->second using this same 'key'.
+                return insert(key, ti->second).first;
+            }
+        }
+        // We simply have never seen a type with this type_info* from any load
+        // module.
+        return mVector.end();
+    }
+
+private:
+    vector_type mVector;
+};
+
+#endif /* ! defined(LL_LLTYPEINFOLOOKUP_H) */
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 26ff1b5c55305354cfb483a29c3c64d0ab61b6c0..bfb30f900f08df192cdc3cba46278a46bc3851a5 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -29,7 +29,7 @@
 
 const S32 LL_VERSION_MAJOR = 3;
 const S32 LL_VERSION_MINOR = 3;
-const S32 LL_VERSION_PATCH = 1;
+const S32 LL_VERSION_PATCH = 3;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h
new file mode 100644
index 0000000000000000000000000000000000000000..a380b00a05150c20f02d0bd8a1f5d0f333d19288
--- /dev/null
+++ b/indra/llcommon/tests/StringVec.h
@@ -0,0 +1,37 @@
+/**
+ * @file   StringVec.h
+ * @author Nat Goodspeed
+ * @date   2012-02-24
+ * @brief  Extend TUT ensure_equals() to handle std::vector<std::string>
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_STRINGVEC_H)
+#define LL_STRINGVEC_H
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+typedef std::vector<std::string> StringVec;
+
+std::ostream& operator<<(std::ostream& out, const StringVec& strings)
+{
+    out << '(';
+    StringVec::const_iterator begin(strings.begin()), end(strings.end());
+    if (begin != end)
+    {
+        out << '"' << *begin << '"';
+        while (++begin != end)
+        {
+            out << ", \"" << *begin << '"';
+        }
+    }
+    out << ')';
+    return out;
+}
+
+#endif /* ! defined(LL_STRINGVEC_H) */
diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h
index dcdb2412beb8d0d5c4e41e176dff8c20c2e25dd1..9c5c18a1501af150f99523a4eed40e7e7f6a7f03 100644
--- a/indra/llcommon/tests/listener.h
+++ b/indra/llcommon/tests/listener.h
@@ -30,6 +30,8 @@
 #define LL_LISTENER_H
 
 #include "llsd.h"
+#include "llevents.h"
+#include "tests/StringVec.h"
 #include <iostream>
 
 /*****************************************************************************
@@ -133,24 +135,7 @@ struct Collect
         return false;
     }
     void clear() { result.clear(); }
-    typedef std::vector<std::string> StringList;
-    StringList result;
+    StringVec result;
 };
 
-std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings)
-{
-    out << '(';
-    Collect::StringList::const_iterator begin(strings.begin()), end(strings.end());
-    if (begin != end)
-    {
-        out << '"' << *begin << '"';
-        while (++begin != end)
-        {
-            out << ", \"" << *begin << '"';
-        }
-    }
-    out << ')';
-    return out;
-}
-
 #endif /* ! defined(LL_LISTENER_H) */
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index 09a20231dea2d309e74f58d97ab193fdd1604783..279a90e51b543b9e6c21568aef4ce9b230e6aae8 100644
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -1,4 +1,4 @@
-/** 
+/**
  * @file llerror_test.cpp
  * @date   December 2006
  * @brief error unit tests
@@ -6,21 +6,21 @@
  * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2010, 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$
  */
@@ -49,7 +49,7 @@ namespace
 	static bool fatalWasCalled;
 	void fatalCall(const std::string&) { fatalWasCalled = true; }
 }
-	
+
 namespace tut
 {
 	class TestRecorder : public LLError::Recorder
@@ -57,59 +57,65 @@ namespace tut
 	public:
 		TestRecorder() : mWantsTime(false) { }
 		~TestRecorder() { LLError::removeRecorder(this); }
-		
+
 		void recordMessage(LLError::ELevel level,
 						   const std::string& message)
 		{
 			mMessages.push_back(message);
 		}
-		
+
 		int countMessages()			{ return (int) mMessages.size(); }
 		void clearMessages()		{ mMessages.clear(); }
-		
+
 		void setWantsTime(bool t)	{ mWantsTime = t; }
 		bool wantsTime()			{ return mWantsTime; }
-		
+
 		std::string message(int n)
 		{
 			std::ostringstream test_name;
 			test_name << "testing message " << n << ", not enough messages";
-			
+
 			tut::ensure(test_name.str(), n < countMessages());
 			return mMessages[n];
 		}
-		
+
 	private:
 		typedef std::vector<std::string> MessageVector;
 		MessageVector mMessages;
-		
+
 		bool mWantsTime;
 	};
 
 	struct ErrorTestData
 	{
-		TestRecorder mRecorder;
+		// addRecorder() expects to be able to later delete the passed
+		// Recorder*. Even though removeRecorder() reclaims ownership, passing
+		// a pointer to a data member rather than a heap Recorder subclass
+		// instance would just be Wrong.
+		TestRecorder* mRecorder;
 		LLError::Settings* mPriorErrorSettings;
-		
-		ErrorTestData()
+
+		ErrorTestData():
+			mRecorder(new TestRecorder)
 		{
 			fatalWasCalled = false;
-			
+
 			mPriorErrorSettings = LLError::saveAndResetSettings();
 			LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
 			LLError::setFatalFunction(fatalCall);
-			LLError::addRecorder(&mRecorder);
+			LLError::addRecorder(mRecorder);
 		}
-		
+
 		~ErrorTestData()
 		{
-			LLError::removeRecorder(&mRecorder);
+			LLError::removeRecorder(mRecorder);
+			delete mRecorder;
 			LLError::restoreSettings(mPriorErrorSettings);
 		}
-		
+
 		void ensure_message_count(int expectedCount)
 		{
-			ensure_equals("message count", mRecorder.countMessages(), expectedCount);
+			ensure_equals("message count", mRecorder->countMessages(), expectedCount);
 		}
 
 		void ensure_message_contains(int n, const std::string& expectedText)
@@ -117,7 +123,7 @@ namespace tut
 			std::ostringstream test_name;
 			test_name << "testing message " << n;
 
-			ensure_contains(test_name.str(), mRecorder.message(n), expectedText);
+			ensure_contains(test_name.str(), mRecorder->message(n), expectedText);
 		}
 
 		void ensure_message_does_not_contain(int n, const std::string& expectedText)
@@ -125,22 +131,22 @@ namespace tut
 			std::ostringstream test_name;
 			test_name << "testing message " << n;
 
-			ensure_does_not_contain(test_name.str(), mRecorder.message(n), expectedText);
+			ensure_does_not_contain(test_name.str(), mRecorder->message(n), expectedText);
 		}
 	};
-	
+
 	typedef test_group<ErrorTestData>	ErrorTestGroup;
 	typedef ErrorTestGroup::object		ErrorTestObject;
-	
+
 	ErrorTestGroup errorTestGroup("error");
-	
+
 	template<> template<>
 	void ErrorTestObject::test<1>()
 		// basic test of output
 	{
 		llinfos << "test" << llendl;
 		llinfos << "bob" << llendl;
-		
+
 		ensure_message_contains(0, "test");
 		ensure_message_contains(1, "bob");
 	}
@@ -159,7 +165,7 @@ namespace
 };
 
 namespace tut
-{	
+{
 	template<> template<>
 	void ErrorTestObject::test<2>()
 		// messages are filtered based on default level
@@ -172,7 +178,7 @@ namespace tut
 		ensure_message_contains(3, "error");
 		ensure_message_contains(4, "four");
 		ensure_message_count(5);
-	
+
 		LLError::setDefaultLevel(LLError::LEVEL_INFO);
 		writeSome();
 		ensure_message_contains(5, "two");
@@ -180,20 +186,20 @@ namespace tut
 		ensure_message_contains(7, "error");
 		ensure_message_contains(8, "four");
 		ensure_message_count(9);
-		
+
 		LLError::setDefaultLevel(LLError::LEVEL_WARN);
 		writeSome();
 		ensure_message_contains(9, "three");
 		ensure_message_contains(10, "error");
 		ensure_message_contains(11, "four");
 		ensure_message_count(12);
-		
+
 		LLError::setDefaultLevel(LLError::LEVEL_ERROR);
 		writeSome();
 		ensure_message_contains(12, "error");
 		ensure_message_contains(13, "four");
 		ensure_message_count(14);
-		
+
 		LLError::setDefaultLevel(LLError::LEVEL_NONE);
 		writeSome();
 		ensure_message_count(14);
@@ -218,14 +224,14 @@ namespace tut
 	{
 		std::string thisFile = __FILE__;
 		std::string abbreviateFile = LLError::abbreviateFile(thisFile);
-		
+
 		ensure_ends_with("file name abbreviation",
 			abbreviateFile,
 			"llcommon/tests/llerror_test.cpp"
 			);
 		ensure_does_not_contain("file name abbreviation",
 			abbreviateFile, "indra");
-			
+
 		std::string someFile =
 #if LL_WINDOWS
 			"C:/amy/bob/cam.cpp"
@@ -234,12 +240,12 @@ namespace tut
 #endif
 			;
 		std::string someAbbreviation = LLError::abbreviateFile(someFile);
-		
+
 		ensure_equals("non-indra file abbreviation",
 			someAbbreviation, someFile);
 	}
 }
-	
+
 namespace
 {
 	std::string locationString(int line)
@@ -247,22 +253,22 @@ namespace
 		std::ostringstream location;
 		location << LLError::abbreviateFile(__FILE__)
 				 << "(" << line << ") : ";
-				 
+
 		return location.str();
 	}
-	
+
 	std::string writeReturningLocation()
 	{
 		llinfos << "apple" << llendl;	int this_line = __LINE__;
 		return locationString(this_line);
 	}
-	
+
 	std::string writeReturningLocationAndFunction()
 	{
 		llinfos << "apple" << llendl;	int this_line = __LINE__;
 		return locationString(this_line) + __FUNCTION__;
 	}
-	
+
 	std::string errorReturningLocation()
 	{
 		llerrs << "die" << llendl;	int this_line = __LINE__;
@@ -271,20 +277,20 @@ namespace
 }
 
 namespace tut
-{	
+{
 	template<> template<>
 	void ErrorTestObject::test<5>()
 		// file and line information in log messages
 	{
 		std::string location = writeReturningLocation();
 			// expecting default to not print location information
-		
+
 		LLError::setPrintLocation(true);
 		writeReturningLocation();
-		
+
 		LLError::setPrintLocation(false);
 		writeReturningLocation();
-		
+
 		ensure_message_does_not_contain(0, location);
 		ensure_message_contains(1, location);
 		ensure_message_does_not_contain(2, location);
@@ -297,7 +303,7 @@ namespace tut
 	existing log messages often do.)  The functions all return their C++
 	name so that test can be substantial mechanized.
  */
- 
+
 std::string logFromGlobal(bool id)
 {
 	llinfos << (id ? "logFromGlobal: " : "") << "hi" << llendl;
@@ -345,7 +351,7 @@ namespace
 			return "ClassWithNoLogType::logFromStatic";
 		}
 	};
-	
+
 	class ClassWithLogType {
 		LOG_CLASS(ClassWithLogType);
 	public:
@@ -360,13 +366,13 @@ namespace
 			return "ClassWithLogType::logFromStatic";
 		}
 	};
-	
+
 	std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); }
 	std::string logFromClassWithNoLogTypeMember(bool id) { ClassWithNoLogType c; return c.logFromMember(id); }
 	std::string logFromClassWithNoLogTypeStatic(bool id) { return ClassWithNoLogType::logFromStatic(id); }
 	std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); }
 	std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); }
-	
+
 	void ensure_has(const std::string& message,
 		const std::string& actual, const std::string& expected)
 	{
@@ -379,18 +385,18 @@ namespace
 			throw tut::failure(ss.str().c_str());
 		}
 	}
-	
+
 	typedef std::string (*LogFromFunction)(bool);
-	void testLogName(tut::TestRecorder& recorder, LogFromFunction f,
+	void testLogName(tut::TestRecorder* recorder, LogFromFunction f,
 		const std::string& class_name = "")
 	{
-		recorder.clearMessages();
+		recorder->clearMessages();
 		std::string name = f(false);
 		f(true);
-		
-		std::string messageWithoutName = recorder.message(0);
-		std::string messageWithName = recorder.message(1);
-		
+
+		std::string messageWithoutName = recorder->message(0);
+		std::string messageWithName = recorder->message(1);
+
 		ensure_has(name + " logged without name",
 			messageWithoutName, name);
 		ensure_has(name + " logged with name",
@@ -431,18 +437,18 @@ namespace
 		llinfos << "inside" << llendl;
 		return "moo";
 	}
-	
+
 	std::string outerLogger()
 	{
 		llinfos << "outside(" << innerLogger() << ")" << llendl;
 		return "bar";
 	}
-	
+
 	void uberLogger()
 	{
 		llinfos << "uber(" << outerLogger() << "," << innerLogger() << ")" << llendl;
 	}
-	
+
 	class LogWhileLogging
 	{
 	public:
@@ -461,11 +467,11 @@ namespace
 		LogWhileLogging l;
 		llinfos << "meta(" << l << ")" << llendl;
 	}
-	
+
 }
 
 namespace tut
-{			
+{
 	template<> template<>
 		// handle nested logging
 	void ErrorTestObject::test<7>()
@@ -474,31 +480,31 @@ namespace tut
 		ensure_message_contains(0, "inside");
 		ensure_message_contains(1, "outside(moo)");
 		ensure_message_count(2);
-		
+
 		uberLogger();
 		ensure_message_contains(2, "inside");
 		ensure_message_contains(3, "inside");
 		ensure_message_contains(4, "outside(moo)");
 		ensure_message_contains(5, "uber(bar,moo)");
 		ensure_message_count(6);
-		
+
 		metaLogger();
 		ensure_message_contains(6, "logging");
 		ensure_message_contains(7, "meta(baz)");
 		ensure_message_count(8);
 	}
-	
+
 	template<> template<>
 		// special handling of llerrs calls
 	void ErrorTestObject::test<8>()
 	{
 		LLError::setPrintLocation(false);
 		std::string location = errorReturningLocation();
-		
+
 		ensure_message_contains(0, location + "error");
 		ensure_message_contains(1, "die");
 		ensure_message_count(2);
-		
+
 		ensure("fatal callback called", fatalWasCalled);
 	}
 }
@@ -509,7 +515,7 @@ namespace
 	{
 		return "1947-07-08T03:04:05Z";
 	}
-	
+
 	void ufoSighting()
 	{
 		llinfos << "ufo" << llendl;
@@ -517,35 +523,35 @@ namespace
 }
 
 namespace tut
-{	
+{
 	template<> template<>
 		// time in output (for recorders that need it)
 	void ErrorTestObject::test<9>()
 	{
 		LLError::setTimeFunction(roswell);
 
-		mRecorder.setWantsTime(false);
+		mRecorder->setWantsTime(false);
 		ufoSighting();
 		ensure_message_contains(0, "ufo");
 		ensure_message_does_not_contain(0, roswell());
-		
-		mRecorder.setWantsTime(true);
+
+		mRecorder->setWantsTime(true);
 		ufoSighting();
 		ensure_message_contains(1, "ufo");
 		ensure_message_contains(1, roswell());
 	}
-	
+
 	template<> template<>
 		// output order
 	void ErrorTestObject::test<10>()
 	{
 		LLError::setPrintLocation(true);
 		LLError::setTimeFunction(roswell);
-		mRecorder.setWantsTime(true);
+		mRecorder->setWantsTime(true);
 		std::string locationAndFunction = writeReturningLocationAndFunction();
-		
+
 		ensure_equals("order is time type location function message",
-			mRecorder.message(0),
+			mRecorder->message(0),
 			roswell() + " INFO: " + locationAndFunction + ": apple");
 	}
 
@@ -553,30 +559,30 @@ namespace tut
 		// multiple recorders
 	void ErrorTestObject::test<11>()
 	{
-		TestRecorder altRecorder;
-		LLError::addRecorder(&altRecorder);
-		
+		TestRecorder* altRecorder(new TestRecorder);
+		LLError::addRecorder(altRecorder);
+
 		llinfos << "boo" << llendl;
 
 		ensure_message_contains(0, "boo");
-		ensure_equals("alt recorder count", altRecorder.countMessages(), 1);
-		ensure_contains("alt recorder message 0", altRecorder.message(0), "boo");
-		
+		ensure_equals("alt recorder count", altRecorder->countMessages(), 1);
+		ensure_contains("alt recorder message 0", altRecorder->message(0), "boo");
+
 		LLError::setTimeFunction(roswell);
 
-		TestRecorder anotherRecorder;
-		anotherRecorder.setWantsTime(true);
-		LLError::addRecorder(&anotherRecorder);
-		
+		TestRecorder* anotherRecorder(new TestRecorder);
+		anotherRecorder->setWantsTime(true);
+		LLError::addRecorder(anotherRecorder);
+
 		llinfos << "baz" << llendl;
 
 		std::string when = roswell();
-		
+
 		ensure_message_does_not_contain(1, when);
-		ensure_equals("alt recorder count", altRecorder.countMessages(), 2);
-		ensure_does_not_contain("alt recorder message 1", altRecorder.message(1), when);
-		ensure_equals("another recorder count", anotherRecorder.countMessages(), 1);
-		ensure_contains("another recorder message 0", anotherRecorder.message(0), when);
+		ensure_equals("alt recorder count", altRecorder->countMessages(), 2);
+		ensure_does_not_contain("alt recorder message 1", altRecorder->message(1), when);
+		ensure_equals("another recorder count", anotherRecorder->countMessages(), 1);
+		ensure_contains("another recorder message 0", anotherRecorder->message(0), when);
 	}
 }
 
@@ -610,10 +616,10 @@ namespace tut
 	{
 		LLError::setDefaultLevel(LLError::LEVEL_WARN);
 		LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO);
-		
+
 		TestAlpha::doAll();
 		TestBeta::doAll();
-		
+
 		ensure_message_contains(0, "aim west");
 		ensure_message_contains(1, "error");
 		ensure_message_contains(2, "ate eels");
@@ -623,7 +629,7 @@ namespace tut
 		ensure_message_contains(6, "big easy");
 		ensure_message_count(7);
 	}
-	
+
 	template<> template<>
 		// filtering by function, and that it will override class filtering
 	void ErrorTestObject::test<13>()
@@ -632,13 +638,13 @@ namespace tut
 		LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN);
 		LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG);
 		LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE);
-		
+
 		TestBeta::doAll();
 		ensure_message_contains(0, "buy iron");
 		ensure_message_contains(1, "bad word");
 		ensure_message_count(2);
 	}
-	
+
 	template<> template<>
 		// filtering by file
 		// and that it is overridden by both class and function filtering
@@ -652,7 +658,7 @@ namespace tut
 									LLError::LEVEL_NONE);
 		LLError::setFunctionLevel("TestBeta::doError",
 									LLError::LEVEL_NONE);
-		
+
 		TestAlpha::doAll();
 		TestBeta::doAll();
 		ensure_message_contains(0, "any idea");
@@ -660,7 +666,7 @@ namespace tut
 		ensure_message_contains(2, "bad word");
 		ensure_message_count(3);
 	}
-	
+
 	template<> template<>
 		// proper cached, efficient lookup of filtering
 	void ErrorTestObject::test<15>()
@@ -690,7 +696,7 @@ namespace tut
 		ensure_message_count(2);
 		ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
 	}
-	
+
 	template<> template<>
 		// configuration from LLSD
 	void ErrorTestObject::test<16>()
@@ -699,26 +705,26 @@ namespace tut
 		LLSD config;
 		config["print-location"] = true;
 		config["default-level"] = "DEBUG";
-		
+
 		LLSD set1;
 		set1["level"] = "WARN";
 		set1["files"][0] = this_file;
-		
+
 		LLSD set2;
 		set2["level"] = "INFO";
 		set2["classes"][0] = "TestAlpha";
-		
+
 		LLSD set3;
 		set3["level"] = "NONE";
 		set3["functions"][0] = "TestAlpha::doError";
 		set3["functions"][1] = "TestBeta::doError";
-		
+
 		config["settings"][0] = set1;
 		config["settings"][1] = set2;
 		config["settings"][2] = set3;
-		
+
 		LLError::configure(config);
-		
+
 		TestAlpha::doAll();
 		TestBeta::doAll();
 		ensure_message_contains(0, "any idea");
@@ -726,13 +732,13 @@ namespace tut
 		ensure_message_contains(1, "aim west");
 		ensure_message_contains(2, "bad word");
 		ensure_message_count(3);
-		
+
 		// make sure reconfiguring works
 		LLSD config2;
 		config2["default-level"] = "WARN";
-		
+
 		LLError::configure(config2);
-		
+
 		TestAlpha::doAll();
 		TestBeta::doAll();
 		ensure_message_contains(3, "aim west");
@@ -744,13 +750,13 @@ namespace tut
 		ensure_message_contains(8, "big easy");
 		ensure_message_count(9);
 	}
-}	
+}
 
 /* Tests left:
 	handling of classes without LOG_CLASS
 
-	live update of filtering from file	
-	
+	live update of filtering from file
+
 	syslog recorder
 	file recorder
 	cerr/stderr recorder
diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp
index b34d1c5fd34c266e14b8abc1595f8795408dc63f..454695ff9f2593d5954d80e7d2d6b5fa70de7def 100644
--- a/indra/llcommon/tests/llinstancetracker_test.cpp
+++ b/indra/llcommon/tests/llinstancetracker_test.cpp
@@ -35,6 +35,7 @@
 #include <vector>
 #include <set>
 #include <algorithm>                // std::sort()
+#include <stdexcept>
 // std headers
 // external library headers
 #include <boost/scoped_ptr.hpp>
@@ -42,6 +43,11 @@
 #include "../test/lltut.h"
 #include "wrapllerrs.h"
 
+struct Badness: public std::runtime_error
+{
+    Badness(const std::string& what): std::runtime_error(what) {}
+};
+
 struct Keyed: public LLInstanceTracker<Keyed, std::string>
 {
     Keyed(const std::string& name):
@@ -53,6 +59,17 @@ struct Keyed: public LLInstanceTracker<Keyed, std::string>
 
 struct Unkeyed: public LLInstanceTracker<Unkeyed>
 {
+    Unkeyed(const std::string& thrw="")
+    {
+        // LLInstanceTracker should respond appropriately if a subclass
+        // constructor throws an exception. Specifically, it should run
+        // LLInstanceTracker's destructor and remove itself from the
+        // underlying container.
+        if (! thrw.empty())
+        {
+            throw Badness(thrw);
+        }
+    }
 };
 
 /*****************************************************************************
@@ -95,6 +112,7 @@ namespace tut
     void object::test<2>()
     {
         ensure_equals(Unkeyed::instanceCount(), 0);
+        Unkeyed* dangling = NULL;
         {
             Unkeyed one;
             ensure_equals(Unkeyed::instanceCount(), 1);
@@ -107,7 +125,11 @@ namespace tut
                 ensure_equals(found, two.get());
             }
             ensure_equals(Unkeyed::instanceCount(), 1);
-        }
+            // store an unwise pointer to a temp Unkeyed instance
+            dangling = &one;
+        } // make that instance vanish
+        // check the now-invalid pointer to the destroyed instance
+        ensure("getInstance(T*) failed to track destruction", ! Unkeyed::getInstance(dangling));
         ensure_equals(Unkeyed::instanceCount(), 0);
     }
 
@@ -229,4 +251,49 @@ namespace tut
         }
         ensure(! what.empty());
     }
+
+    template<> template<>
+    void object::test<8>()
+    {
+        set_test_name("exception in subclass ctor");
+        typedef std::set<Unkeyed*> InstanceSet;
+        InstanceSet existing;
+        // We can't use the iterator-range InstanceSet constructor because
+        // beginInstances() returns an iterator that dereferences to an
+        // Unkeyed&, not an Unkeyed*.
+        for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+                                    ukend(Unkeyed::endInstances());
+             uki != ukend; ++uki)
+        {
+            existing.insert(&*uki);
+        }
+        Unkeyed* puk = NULL;
+        try
+        {
+            // We don't expect the assignment to take place because we expect
+            // Unkeyed to respond to the non-empty string param by throwing.
+            // We know the LLInstanceTracker base-class constructor will have
+            // run before Unkeyed's constructor, therefore the new instance
+            // will have added itself to the underlying set. The whole
+            // question is, when Unkeyed's constructor throws, will
+            // LLInstanceTracker's destructor remove it from the set? I
+            // realize we're testing the C++ implementation more than
+            // Unkeyed's implementation, but this seems an important point to
+            // nail down.
+            puk = new Unkeyed("throw");
+        }
+        catch (const Badness&)
+        {
+        }
+        // Ensure that every member of the new, updated set of Unkeyed
+        // instances was also present in the original set. If that's not true,
+        // it's because our new Unkeyed ended up in the updated set despite
+        // its constructor exception.
+        for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+                                    ukend(Unkeyed::endInstances());
+             uki != ukend; ++uki)
+        {
+            ensure("failed to remove instance", existing.find(&*uki) != existing.end());
+        }
+    }
 } // namespace tut
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b755e9ca5bd81d920e32bc7965a1402198f57fe
--- /dev/null
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -0,0 +1,694 @@
+/**
+ * @file   llleap_test.cpp
+ * @author Nat Goodspeed
+ * @date   2012-02-21
+ * @brief  Test for llleap.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llleap.h"
+// STL headers
+// std headers
+// external library headers
+#include <boost/assign/list_of.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "../test/namedtempfile.h"
+#include "../test/manageapr.h"
+#include "../test/catch_and_store_what_in.h"
+#include "wrapllerrs.h"
+#include "llevents.h"
+#include "llprocess.h"
+#include "stringize.h"
+#include "StringVec.h"
+#include <functional>
+
+using boost::assign::list_of;
+
+static ManageAPR manager;
+
+StringVec sv(const StringVec& listof) { return listof; }
+
+#if defined(LL_WINDOWS)
+#define sleep(secs) _sleep((secs) * 1000)
+#endif
+
+#if ! LL_WINDOWS
+const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data
+#else
+// "Then there's Windows... sigh." The "very large message" test is flaky in a
+// way that seems to point to either the OS (nonblocking writes to pipes) or
+// possibly the apr_file_write() function. Poring over log messages reveals
+// that at some point along the way apr_file_write() returns 11 (Resource
+// temporarily unavailable, i.e. EAGAIN) and says it wrote 0 bytes -- even
+// though it did write the chunk! Our next write attempt retries the same
+// chunk, resulting in the chunk being duplicated at the child end, corrupting
+// the data stream. Much as I would love to be able to fix it for real, such a
+// fix would appear to require distinguishing bogus EAGAIN returns from real
+// ones -- how?? Empirically this behavior is only observed when writing a
+// "very large message". To be able to move forward at all, try to bypass this
+// particular failure by adjusting the size of a "very large message" on
+// Windows.
+const size_t BUFFERED_LENGTH = 65336;
+#endif  // LL_WINDOWS
+
+void waitfor(const std::vector<LLLeap*>& instances, int timeout=60)
+{
+    int i;
+    for (i = 0; i < timeout; ++i)
+    {
+        // Every iteration, test whether any of the passed LLLeap instances
+        // still exist (are still running).
+        std::vector<LLLeap*>::const_iterator vli(instances.begin()), vlend(instances.end());
+        for ( ; vli != vlend; ++vli)
+        {
+            // getInstance() returns NULL if it's terminated/gone, non-NULL if
+            // it's still running
+            if (LLLeap::getInstance(*vli))
+                break;
+        }
+        // If we made it through all of 'instances' without finding one that's
+        // still running, we're done.
+        if (vli == vlend)
+        {
+/*==========================================================================*|
+            std::cout << instances.size() << " LLLeap instances terminated in "
+                      << i << " seconds, proceeding" << std::endl;
+|*==========================================================================*/
+            return;
+        }
+        // Found an instance that's still running. Wait and pump LLProcess.
+        sleep(1);
+        LLEventPumps::instance().obtain("mainloop").post(LLSD());
+    }
+    tut::ensure(STRINGIZE("at least 1 of " << instances.size()
+                          << " LLLeap instances timed out ("
+                          << timeout << " seconds) without terminating"),
+                i < timeout);
+}
+
+void waitfor(LLLeap* instance, int timeout=60)
+{
+    std::vector<LLLeap*> instances;
+    instances.push_back(instance);
+    waitfor(instances, timeout);
+}
+
+/*****************************************************************************
+*   TUT
+*****************************************************************************/
+namespace tut
+{
+    struct llleap_data
+    {
+        llleap_data():
+            reader(".py",
+                   // This logic is adapted from vita.viewerclient.receiveEvent()
+                   boost::lambda::_1 <<
+                   "import re\n"
+                   "import os\n"
+                   "import sys\n"
+                   "\n"
+                   // Don't forget that this Python script is written to some
+                   // temp directory somewhere! Its __file__ is useless in
+                   // finding indra/lib/python. Use our __FILE__, with
+                   // raw-string syntax to deal with Windows pathnames.
+                   "mydir = os.path.dirname(r'" << __FILE__ << "')\n"
+                   "try:\n"
+                   "    from llbase import llsd\n"
+                   "except ImportError:\n"
+                   // We expect mydir to be .../indra/llcommon/tests.
+                   "    sys.path.insert(0,\n"
+                   "        os.path.join(mydir, os.pardir, os.pardir, 'lib', 'python'))\n"
+                   "    from indra.base import llsd\n"
+                   "\n"
+                   "class ProtocolError(Exception):\n"
+                   "    def __init__(self, msg, data):\n"
+                   "        Exception.__init__(self, msg)\n"
+                   "        self.data = data\n"
+                   "\n"
+                   "class ParseError(ProtocolError):\n"
+                   "    pass\n"
+                   "\n"
+                   "def get():\n"
+                   "    hdr = ''\n"
+                   "    while ':' not in hdr and len(hdr) < 20:\n"
+                   "        hdr += sys.stdin.read(1)\n"
+                   "        if not hdr:\n"
+                   "            sys.exit(0)\n"
+                   "    if not hdr.endswith(':'):\n"
+                   "        raise ProtocolError('Expected len:data, got %r' % hdr, hdr)\n"
+                   "    try:\n"
+                   "        length = int(hdr[:-1])\n"
+                   "    except ValueError:\n"
+                   "        raise ProtocolError('Non-numeric len %r' % hdr[:-1], hdr[:-1])\n"
+                   "    parts = []\n"
+                   "    received = 0\n"
+                   "    while received < length:\n"
+                   "        parts.append(sys.stdin.read(length - received))\n"
+                   "        received += len(parts[-1])\n"
+                   "    data = ''.join(parts)\n"
+                   "    assert len(data) == length\n"
+                   "    try:\n"
+                   "        return llsd.parse(data)\n"
+                   //   Seems the old indra.base.llsd module didn't properly
+                   //   convert IndexError (from running off end of string) to
+                   //   LLSDParseError.
+                   "    except (IndexError, llsd.LLSDParseError), e:\n"
+                   "        msg = 'Bad received packet (%s)' % e\n"
+                   "        print >>sys.stderr, '%s, %s bytes:' % (msg, len(data))\n"
+                   "        showmax = 40\n"
+                   //       We've observed failures with very large packets;
+                   //       dumping the entire packet wastes time and space.
+                   //       But if the error states a particular byte offset,
+                   //       truncate to (near) that offset when dumping data.
+                   "        location = re.search(r' at (byte|index) ([0-9]+)', str(e))\n"
+                   "        if not location:\n"
+                   "            # didn't find offset, dump whole thing, no ellipsis\n"
+                   "            ellipsis = ''\n"
+                   "        else:\n"
+                   "            # found offset within error message\n"
+                   "            trunc = int(location.group(2)) + showmax\n"
+                   "            data = data[:trunc]\n"
+                   "            ellipsis = '... (%s more)' % (length - trunc)\n"
+                   "        offset = -showmax\n"
+                   "        for offset in xrange(0, len(data)-showmax, showmax):\n"
+                   "            print >>sys.stderr, '%04d: %r +' % \\\n"
+                   "                  (offset, data[offset:offset+showmax])\n"
+                   "        offset += showmax\n"
+                   "        print >>sys.stderr, '%04d: %r%s' % \\\n"
+                   "              (offset, data[offset:], ellipsis)\n"
+                   "        raise ParseError(msg, data)\n"
+                   "\n"
+                   "# deal with initial stdin message\n"
+                   // this will throw if the initial write to stdin doesn't
+                   // follow len:data protocol, or if we couldn't find 'pump'
+                   // in the dict
+                   "_reply = get()['pump']\n"
+                   "\n"
+                   "def replypump():\n"
+                   "    return _reply\n"
+                   "\n"
+                   "def put(req):\n"
+                   "    sys.stdout.write(':'.join((str(len(req)), req)))\n"
+                   "    sys.stdout.flush()\n"
+                   "\n"
+                   "def send(pump, data):\n"
+                   "    put(llsd.format_notation(dict(pump=pump, data=data)))\n"
+                   "\n"
+                   "def request(pump, data):\n"
+                   "    # we expect 'data' is a dict\n"
+                   "    data['reply'] = _reply\n"
+                   "    send(pump, data)\n"),
+            // Get the actual pathname of the NamedExtTempFile and trim off
+            // the ".py" extension. (We could cache reader.getName() in a
+            // separate member variable, but I happen to know getName() just
+            // returns a NamedExtTempFile member rather than performing any
+            // computation, so I don't mind calling it twice.) Then take the
+            // basename.
+            reader_module(LLProcess::basename(
+                              reader.getName().substr(0, reader.getName().length()-3))),
+            pPYTHON(getenv("PYTHON")),
+            PYTHON(pPYTHON? pPYTHON : "")
+        {
+            ensure("Set PYTHON to interpreter pathname", pPYTHON);
+        }
+        NamedExtTempFile reader;
+        const std::string reader_module;
+        const char* pPYTHON;
+        const std::string PYTHON;
+    };
+    typedef test_group<llleap_data> llleap_group;
+    typedef llleap_group::object object;
+    llleap_group llleapgrp("llleap");
+
+    template<> template<>
+    void object::test<1>()
+    {
+        set_test_name("multiple LLLeap instances");
+        NamedTempFile script("py",
+                             "import time\n"
+                             "time.sleep(1)\n");
+        std::vector<LLLeap*> instances;
+        instances.push_back(LLLeap::create(get_test_name(),
+                                           sv(list_of(PYTHON)(script.getName()))));
+        instances.push_back(LLLeap::create(get_test_name(),
+                                           sv(list_of(PYTHON)(script.getName()))));
+        // In this case we're simply establishing that two LLLeap instances
+        // can coexist without throwing exceptions or bombing in any other
+        // way. Wait for them to terminate.
+        waitfor(instances);
+    }
+
+    template<> template<>
+    void object::test<2>()
+    {
+        set_test_name("stderr to log");
+        NamedTempFile script("py",
+                             "import sys\n"
+                             "sys.stderr.write('''Hello from Python!\n"
+                             "note partial line''')\n");
+        CaptureLog log(LLError::LEVEL_INFO);
+        waitfor(LLLeap::create(get_test_name(),
+                               sv(list_of(PYTHON)(script.getName()))));
+        log.messageWith("Hello from Python!");
+        log.messageWith("note partial line");
+    }
+
+    template<> template<>
+    void object::test<3>()
+    {
+        set_test_name("bad stdout protocol");
+        NamedTempFile script("py",
+                             "print 'Hello from Python!'\n");
+        CaptureLog log(LLError::LEVEL_WARN);
+        waitfor(LLLeap::create(get_test_name(),
+                               sv(list_of(PYTHON)(script.getName()))));
+        ensure_contains("error log line",
+                        log.messageWith("invalid protocol"), "Hello from Python!");
+    }
+
+    template<> template<>
+    void object::test<4>()
+    {
+        set_test_name("leftover stdout");
+        NamedTempFile script("py",
+                             "import sys\n"
+                             // note lack of newline
+                             "sys.stdout.write('Hello from Python!')\n");
+        CaptureLog log(LLError::LEVEL_WARN);
+        waitfor(LLLeap::create(get_test_name(),
+                               sv(list_of(PYTHON)(script.getName()))));
+        ensure_contains("error log line",
+                        log.messageWith("Discarding"), "Hello from Python!");
+    }
+
+    template<> template<>
+    void object::test<5>()
+    {
+        set_test_name("bad stdout len prefix");
+        NamedTempFile script("py",
+                             "import sys\n"
+                             "sys.stdout.write('5a2:something')\n");
+        CaptureLog log(LLError::LEVEL_WARN);
+        waitfor(LLLeap::create(get_test_name(),
+                               sv(list_of(PYTHON)(script.getName()))));
+        ensure_contains("error log line",
+                        log.messageWith("invalid protocol"), "5a2:");
+    }
+
+    template<> template<>
+    void object::test<6>()
+    {
+        set_test_name("empty plugin vector");
+        std::string threw;
+        try
+        {
+            LLLeap::create("empty", StringVec());
+        }
+        CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+        ensure_contains("LLLeap::Error", threw, "no plugin");
+        // try the suppress-exception variant
+        ensure("bad launch returned non-NULL", ! LLLeap::create("empty", StringVec(), false));
+    }
+
+    template<> template<>
+    void object::test<7>()
+    {
+        set_test_name("bad launch");
+        // Synthesize bogus executable name
+        std::string BADPYTHON(PYTHON.substr(0, PYTHON.length()-1) + "x");
+        CaptureLog log;
+        std::string threw;
+        try
+        {
+            LLLeap::create("bad exe", BADPYTHON);
+        }
+        CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+        ensure_contains("LLLeap::create() didn't throw", threw, "failed");
+        log.messageWith("failed");
+        log.messageWith(BADPYTHON);
+        // try the suppress-exception variant
+        ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false));
+    }
+
+    // Generic self-contained listener: derive from this and override its
+    // call() method, then tell somebody to post on the pump named getName().
+    // Control will reach your call() override.
+    struct ListenerBase
+    {
+        // Pass the pump name you want; will tweak for uniqueness.
+        ListenerBase(const std::string& name):
+            mPump(name, true)
+        {
+            mPump.listen(name, boost::bind(&ListenerBase::call, this, _1));
+        }
+
+        virtual ~ListenerBase() {}  // pacify MSVC
+
+        virtual bool call(const LLSD& request)
+        {
+            return false;
+        }
+
+        LLEventPump& getPump() { return mPump; }
+        const LLEventPump& getPump() const { return mPump; }
+
+        std::string getName() const { return mPump.getName(); }
+        void post(const LLSD& data) { mPump.post(data); }
+
+        LLEventStream mPump;
+    };
+
+    // Mimic a dummy little LLEventAPI that merely sends a reply back to its
+    // requester on the "reply" pump.
+    struct AckAPI: public ListenerBase
+    {
+        AckAPI(): ListenerBase("AckAPI") {}
+
+        virtual bool call(const LLSD& request)
+        {
+            LLEventPumps::instance().obtain(request["reply"]).post("ack");
+            return false;
+        }
+    };
+
+    // Give LLLeap script a way to post success/failure.
+    struct Result: public ListenerBase
+    {
+        Result(): ListenerBase("Result") {}
+
+        virtual bool call(const LLSD& request)
+        {
+            mData = request;
+            return false;
+        }
+
+        void ensure() const
+        {
+            tut::ensure(std::string("never posted to ") + getName(), mData.isDefined());
+            // Post an empty string for success, non-empty string is failure message.
+            tut::ensure(mData, mData.asString().empty());
+        }
+
+        LLSD mData;
+    };
+
+    template<> template<>
+    void object::test<8>()
+    {
+        set_test_name("round trip");
+        AckAPI api;
+        Result result;
+        NamedTempFile script("py",
+                             boost::lambda::_1 <<
+                             "from " << reader_module << " import *\n"
+                             // make a request on our little API
+                             "request(pump='" << api.getName() << "', data={})\n"
+                             // wait for its response
+                             "resp = get()\n"
+                             "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
+                             "            else 'bad: ' + str(resp)\n"
+                             "send(pump='" << result.getName() << "', data=result)\n");
+        waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
+        result.ensure();
+    }
+
+    struct ReqIDAPI: public ListenerBase
+    {
+        ReqIDAPI(): ListenerBase("ReqIDAPI") {}
+
+        virtual bool call(const LLSD& request)
+        {
+            // free function from llevents.h
+            sendReply(LLSD(), request);
+            return false;
+        }
+    };
+
+    template<> template<>
+    void object::test<9>()
+    {
+        set_test_name("many small messages");
+        // It's not clear to me whether there's value in iterating many times
+        // over a send/receive loop -- I don't think that will exercise any
+        // interesting corner cases. This test first sends a large number of
+        // messages, then receives all the responses. The intent is to ensure
+        // that some of that data stream crosses buffer boundaries, loop
+        // iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
+        ReqIDAPI api;
+        Result result;
+        NamedTempFile script("py",
+                             boost::lambda::_1 <<
+                             "import sys\n"
+                             "from " << reader_module << " import *\n"
+                             // Note that since reader imports llsd, this
+                             // 'import *' gets us llsd too.
+                             "sample = llsd.format_notation(dict(pump='" <<
+                             api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
+                             // The whole packet has length prefix too: "len:data"
+                             "samplen = len(str(len(sample))) + 1 + len(sample)\n"
+                             // guess how many messages it will take to
+                             // accumulate BUFFERED_LENGTH
+                             "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
+                             "print >>sys.stderr, 'Sending %s requests' % count\n"
+                             "for i in xrange(count):\n"
+                             "    request('" << api.getName() << "', dict(reqid=i))\n"
+                             // The assumption in this specific test that
+                             // replies will arrive in the same order as
+                             // requests is ONLY valid because the API we're
+                             // invoking sends replies instantly. If the API
+                             // had to wait for some external event before
+                             // sending its reply, replies could arrive in
+                             // arbitrary order, and we'd have to tick them
+                             // off from a set.
+                             "result = ''\n"
+                             "for i in xrange(count):\n"
+                             "    resp = get()\n"
+                             "    if resp['data']['reqid'] != i:\n"
+                             "        result = 'expected reqid=%s in %s' % (i, resp)\n"
+                             "        break\n"
+                             "send(pump='" << result.getName() << "', data=result)\n");
+        waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
+                300);               // needs more realtime than most tests
+        result.ensure();
+    }
+
+    // This is the body of test<10>, extracted so we can run it over a number
+    // of large-message sizes.
+    void test_large_message(const std::string& PYTHON, const std::string& reader_module,
+                            const std::string& test_name, size_t size)
+    {
+        ReqIDAPI api;
+        Result result;
+        NamedTempFile script("py",
+                             boost::lambda::_1 <<
+                             "import sys\n"
+                             "from " << reader_module << " import *\n"
+                             // Generate a very large string value.
+                             "desired = int(sys.argv[1])\n"
+                             // 7 chars per item: 6 digits, 1 comma
+                             "count = int((desired - 50)/7)\n"
+                             "large = ''.join('%06d,' % i for i in xrange(count))\n"
+                             // Pass 'large' as reqid because we know the API
+                             // will echo reqid, and we want to receive it back.
+                             "request('" << api.getName() << "', dict(reqid=large))\n"
+                             "try:\n"
+                             "    resp = get()\n"
+                             "except ParseError, e:\n"
+                             "    # try to find where e.data diverges from expectation\n"
+                             // Normally we'd expect a 'pump' key in there,
+                             // too, with value replypump(). But Python
+                             // serializes keys in a different order than C++,
+                             // so incoming data start with 'data'.
+                             // Truthfully, though, if we get as far as 'pump'
+                             // before we find a difference, something's very
+                             // strange.
+                             "    expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
+                             "    chunk = 40\n"
+                             "    for offset in xrange(0, max(len(e.data), len(expect)), chunk):\n"
+                             "        if e.data[offset:offset+chunk] != \\\n"
+                             "           expect[offset:offset+chunk]:\n"
+                             "            print >>sys.stderr, 'Offset %06d: expect %r,\\n'\\\n"
+                             "                                '                  get %r' %\\\n"
+                             "                                (offset,\n"
+                             "                                 expect[offset:offset+chunk],\n"
+                             "                                 e.data[offset:offset+chunk])\n"
+                             "            break\n"
+                             "    else:\n"
+                             "        print >>sys.stderr, 'incoming data matches expect?!'\n"
+                             "    send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
+                             "    sys.exit(1)\n"
+                             "\n"
+                             "echoed = resp['data']['reqid']\n"
+                             "if echoed == large:\n"
+                             "    send('" << result.getName() << "', '')\n"
+                             "    sys.exit(0)\n"
+                             // Here we know echoed did NOT match; try to find where
+                             "for i in xrange(count):\n"
+                             "    start = 7*i\n"
+                             "    end   = 7*(i+1)\n"
+                             "    if end > len(echoed)\\\n"
+                             "    or echoed[start:end] != large[start:end]:\n"
+                             "        send('" << result.getName() << "',\n"
+                             "             'at offset %s, expected %r but got %r' %\n"
+                             "             (start, large[start:end], echoed[start:end]))\n"
+                             "sys.exit(1)\n");
+        waitfor(LLLeap::create(test_name,
+                               sv(list_of
+                                  (PYTHON)
+                                  (script.getName())
+                                  (stringize(size)))),
+                180);               // try a longer timeout
+        result.ensure();
+    }
+
+    struct TestLargeMessage: public std::binary_function<size_t, size_t, bool>
+    {
+        TestLargeMessage(const std::string& PYTHON_, const std::string& reader_module_,
+                         const std::string& test_name_):
+            PYTHON(PYTHON_),
+            reader_module(reader_module_),
+            test_name(test_name_)
+        {}
+
+        bool operator()(size_t left, size_t right) const
+        {
+            // We don't know whether upper_bound is going to pass the "sought
+            // value" as the left or the right operand. We pass 0 as the
+            // "sought value" so we can distinguish it. Of course that means
+            // the sequence we're searching must not itself contain 0!
+            size_t size;
+            bool success;
+            if (left)
+            {
+                size = left;
+                // Consider our return value carefully. Normal binary_search
+                // (or, in our case, upper_bound) expects a container sorted
+                // in ascending order, and defaults to the std::less
+                // comparator. Our container is in fact in ascending order, so
+                // return consistently with std::less. Here we were called as
+                // compare(item, sought). If std::less were called that way,
+                // 'true' would mean to move right (to higher numbers) within
+                // the sequence: the item being considered is less than the
+                // sought value. For us, that means that test_large_message()
+                // success should return 'true'.
+                success = true;
+            }
+            else
+            {
+                size = right;
+                // Here we were called as compare(sought, item). If std::less
+                // were called that way, 'true' would mean to move left (to
+                // lower numbers) within the sequence: the sought value is
+                // less than the item being considered. For us, that means
+                // test_large_message() FAILURE should return 'true', hence
+                // test_large_message() success should return 'false'.
+                success = false;
+            }
+
+            try
+            {
+                test_large_message(PYTHON, reader_module, test_name, size);
+                std::cout << "test_large_message(" << size << ") succeeded" << std::endl;
+                return success;
+            }
+            catch (const failure& e)
+            {
+                std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
+                return ! success;
+            }
+        }
+
+        const std::string PYTHON, reader_module, test_name;
+    };
+
+    // The point of this function is to try to find a size at which
+    // test_large_message() can succeed. We still want the overall test to
+    // fail; otherwise we won't get the coder's attention -- but if
+    // test_large_message() fails, try to find a plausible size at which it
+    // DOES work.
+    void test_or_split(const std::string& PYTHON, const std::string& reader_module,
+                       const std::string& test_name, size_t size)
+    {
+        try
+        {
+            test_large_message(PYTHON, reader_module, test_name, size);
+        }
+        catch (const failure& e)
+        {
+            std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
+            // If it still fails below 4K, give up: subdividing any further is
+            // pointless.
+            if (size >= 4096)
+            {
+                try
+                {
+                    // Recur with half the size
+                    size_t smaller(size/2);
+                    test_or_split(PYTHON, reader_module, test_name, smaller);
+                    // Recursive call will throw if test_large_message()
+                    // failed, therefore we only reach the line below if it
+                    // succeeded.
+                    std::cout << "but test_large_message(" << smaller << ") succeeded" << std::endl;
+
+                    // Binary search for largest size that works. But since
+                    // std::binary_search() only returns bool, actually use
+                    // std::upper_bound(), consistent with our desire to find
+                    // the LARGEST size that works. First generate a sorted
+                    // container of all the sizes we intend to try, from
+                    // 'smaller' (known to work) to 'size' (known to fail). We
+                    // could whomp up magic iterators to do this dynamically,
+                    // without actually instantiating a vector, but for a test
+                    // program this will do. At least preallocate the vector.
+                    // Per TestLargeMessage comments, it's important that this
+                    // vector not contain 0.
+                    std::vector<size_t> sizes;
+                    sizes.reserve((size - smaller)/4096 + 1);
+                    for (size_t sz(smaller), szend(size); sz < szend; sz += 4096)
+                        sizes.push_back(sz);
+                    // our comparator
+                    TestLargeMessage tester(PYTHON, reader_module, test_name);
+                    // Per TestLargeMessage comments, pass 0 as the sought value.
+                    std::vector<size_t>::const_iterator found =
+                        std::upper_bound(sizes.begin(), sizes.end(), 0, tester);
+                    if (found != sizes.end() && found != sizes.begin())
+                    {
+                        std::cout << "test_large_message(" << *(found - 1)
+                                  << ") is largest that succeeds" << std::endl;
+                    }
+                    else
+                    {
+                        std::cout << "cannot determine largest test_large_message(size) "
+                                  << "that succeeds" << std::endl;
+                    }
+                }
+                catch (const failure&)
+                {
+                    // The recursive test_or_split() call above has already
+                    // handled the exception. We don't want our caller to see
+                    // innermost exception; propagate outermost (below).
+                }
+            }
+            // In any case, because we reached here through failure of
+            // our original test_large_message(size) call, ensure failure
+            // propagates.
+            throw e;
+        }
+    }
+
+    template<> template<>
+    void object::test<10>()
+    {
+        set_test_name("very large message");
+        test_or_split(PYTHON, reader_module, get_test_name(), BUFFERED_LENGTH);
+    }
+} // namespace tut
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99186ed434a95187bc192c9cf599f328221da67e
--- /dev/null
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -0,0 +1,1262 @@
+/**
+ * @file   llprocess_test.cpp
+ * @author Nat Goodspeed
+ * @date   2011-12-19
+ * @brief  Test for llprocess.
+ * 
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llprocess.h"
+// STL headers
+#include <vector>
+#include <list>
+// std headers
+#include <fstream>
+// external library headers
+#include "llapr.h"
+#include "apr_thread_proc.h"
+#include <boost/foreach.hpp>
+#include <boost/function.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
+//#include <boost/lambda/lambda.hpp>
+//#include <boost/lambda/bind.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "../test/manageapr.h"
+#include "../test/namedtempfile.h"
+#include "../test/catch_and_store_what_in.h"
+#include "stringize.h"
+#include "llsdutil.h"
+#include "llevents.h"
+#include "wrapllerrs.h"
+
+#if defined(LL_WINDOWS)
+#define sleep(secs) _sleep((secs) * 1000)
+#define EOL "\r\n"
+#else
+#define EOL "\n"
+#include <sys/wait.h>
+#endif
+
+//namespace lambda = boost::lambda;
+
+// static instance of this manages APR init/cleanup
+static ManageAPR manager;
+
+/*****************************************************************************
+*   Helpers
+*****************************************************************************/
+
+#define ensure_equals_(left, right) \
+        ensure_equals(STRINGIZE(#left << " != " << #right), (left), (right))
+
+#define aprchk(expr) aprchk_(#expr, (expr))
+static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS)
+{
+    tut::ensure_equals(STRINGIZE(call << " => " << rv << ": " << manager.strerror(rv)),
+                       rv, expected);
+}
+
+/**
+ * Read specified file using std::getline(). It is assumed to be an error if
+ * the file is empty: don't use this function if that's an acceptable case.
+ * Last line will not end with '\n'; this is to facilitate the usual case of
+ * string compares with a single line of output.
+ * @param pathname The file to read.
+ * @param desc Optional description of the file for error message;
+ * defaults to "in <pathname>"
+ */
+static std::string readfile(const std::string& pathname, const std::string& desc="")
+{
+    std::string use_desc(desc);
+    if (use_desc.empty())
+    {
+        use_desc = STRINGIZE("in " << pathname);
+    }
+    std::ifstream inf(pathname.c_str());
+    std::string output;
+    tut::ensure(STRINGIZE("No output " << use_desc), std::getline(inf, output));
+    std::string more;
+    while (std::getline(inf, more))
+    {
+        output += '\n' + more;
+    }
+    return output;
+}
+
+/// Looping on LLProcess::isRunning() must now be accompanied by pumping
+/// "mainloop" -- otherwise the status won't update and you get an infinite
+/// loop.
+void yield(int seconds=1)
+{
+    // This function simulates waiting for another viewer frame
+    sleep(seconds);
+    LLEventPumps::instance().obtain("mainloop").post(LLSD());
+}
+
+void waitfor(LLProcess& proc, int timeout=60)
+{
+    int i = 0;
+    for ( ; i < timeout && proc.isRunning(); ++i)
+    {
+        yield();
+    }
+    tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
+                i < timeout);
+}
+
+void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
+{
+    int i = 0;
+    for ( ; i < timeout && LLProcess::isRunning(h, desc); ++i)
+    {
+        yield();
+    }
+    tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
+                i < timeout);
+}
+
+/**
+ * Construct an LLProcess to run a Python script.
+ */
+struct PythonProcessLauncher
+{
+    /**
+     * @param desc Arbitrary description for error messages
+     * @param script Python script, any form acceptable to NamedTempFile,
+     * typically either a std::string or an expression of the form
+     * (lambda::_1 << "script content with " << variable_data)
+     */
+    template <typename CONTENT>
+    PythonProcessLauncher(const std::string& desc, const CONTENT& script):
+        mDesc(desc),
+        mScript("py", script)
+    {
+        const char* PYTHON(getenv("PYTHON"));
+        tut::ensure("Set $PYTHON to the Python interpreter", PYTHON);
+
+        mParams.desc = desc + " script";
+        mParams.executable = PYTHON;
+        mParams.args.add(mScript.getName());
+    }
+
+    /// Launch Python script; verify that it launched
+    void launch()
+    {
+        mPy = LLProcess::create(mParams);
+        tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), mPy);
+    }
+
+    /// Run Python script and wait for it to complete.
+    void run()
+    {
+        launch();
+        // One of the irritating things about LLProcess is that
+        // there's no API to wait for the child to terminate -- but given
+        // its use in our graphics-intensive interactive viewer, it's
+        // understandable.
+        waitfor(*mPy);
+    }
+
+    /**
+     * Run a Python script using LLProcess, expecting that it will
+     * write to the file passed as its sys.argv[1]. Retrieve that output.
+     *
+     * Until January 2012, LLProcess provided distressingly few
+     * mechanisms for a child process to communicate back to its caller --
+     * not even its return code. We've introduced a convention by which we
+     * create an empty temp file, pass the name of that file to our child
+     * as sys.argv[1] and expect the script to write its output to that
+     * file. This function implements the C++ (parent process) side of
+     * that convention.
+     */
+    std::string run_read()
+    {
+        NamedTempFile out("out", ""); // placeholder
+        // pass name of this temporary file to the script
+        mParams.args.add(out.getName());
+        run();
+        // assuming the script wrote to that file, read it
+        return readfile(out.getName(), STRINGIZE("from " << mDesc << " script"));
+    }
+
+    LLProcess::Params mParams;
+    LLProcessPtr mPy;
+    std::string mDesc;
+    NamedTempFile mScript;
+};
+
+/// convenience function for PythonProcessLauncher::run()
+template <typename CONTENT>
+static void python(const std::string& desc, const CONTENT& script)
+{
+    PythonProcessLauncher py(desc, script);
+    py.run();
+}
+
+/// convenience function for PythonProcessLauncher::run_read()
+template <typename CONTENT>
+static std::string python_out(const std::string& desc, const CONTENT& script)
+{
+    PythonProcessLauncher py(desc, script);
+    return py.run_read();
+}
+
+/// Create a temporary directory and clean it up later.
+class NamedTempDir: public boost::noncopyable
+{
+public:
+    // Use python() function to create a temp directory: I've found
+    // nothing in either Boost.Filesystem or APR quite like Python's
+    // tempfile.mkdtemp().
+    // Special extra bonus: on Mac, mkdtemp() reports a pathname
+    // starting with /var/folders/something, whereas that's really a
+    // symlink to /private/var/folders/something. Have to use
+    // realpath() to compare properly.
+    NamedTempDir():
+        mPath(python_out("mkdtemp()",
+                         "from __future__ import with_statement\n"
+                         "import os.path, sys, tempfile\n"
+                         "with open(sys.argv[1], 'w') as f:\n"
+                         "    f.write(os.path.normcase(os.path.normpath(os.path.realpath(tempfile.mkdtemp()))))\n"))
+    {}
+
+    ~NamedTempDir()
+    {
+        aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp));
+    }
+
+    std::string getName() const { return mPath; }
+
+private:
+    std::string mPath;
+};
+
+/*****************************************************************************
+*   TUT
+*****************************************************************************/
+namespace tut
+{
+    struct llprocess_data
+    {
+        LLAPRPool pool;
+    };
+    typedef test_group<llprocess_data> llprocess_group;
+    typedef llprocess_group::object object;
+    llprocess_group llprocessgrp("llprocess");
+
+    struct Item
+    {
+        Item(): tries(0) {}
+        unsigned    tries;
+        std::string which;
+        std::string what;
+    };
+
+/*==========================================================================*|
+#define tabent(symbol) { symbol, #symbol }
+    static struct ReasonCode
+    {
+        int code;
+        const char* name;
+    } reasons[] =
+    {
+        tabent(APR_OC_REASON_DEATH),
+        tabent(APR_OC_REASON_UNWRITABLE),
+        tabent(APR_OC_REASON_RESTART),
+        tabent(APR_OC_REASON_UNREGISTER),
+        tabent(APR_OC_REASON_LOST),
+        tabent(APR_OC_REASON_RUNNING)
+    };
+#undef tabent
+|*==========================================================================*/
+
+    struct WaitInfo
+    {
+        WaitInfo(apr_proc_t* child_):
+            child(child_),
+            rv(-1),                 // we haven't yet called apr_proc_wait()
+            rc(0),
+            why(apr_exit_why_e(0))
+        {}
+        apr_proc_t* child;          // which subprocess
+        apr_status_t rv;            // return from apr_proc_wait()
+        int rc;                     // child's exit code
+        apr_exit_why_e why;         // APR_PROC_EXIT, APR_PROC_SIGNAL, APR_PROC_SIGNAL_CORE
+    };
+
+    void child_status_callback(int reason, void* data, int status)
+    {
+/*==========================================================================*|
+        std::string reason_str;
+        BOOST_FOREACH(const ReasonCode& rcp, reasons)
+        {
+            if (reason == rcp.code)
+            {
+                reason_str = rcp.name;
+                break;
+            }
+        }
+        if (reason_str.empty())
+        {
+            reason_str = STRINGIZE("unknown reason " << reason);
+        }
+        std::cout << "child_status_callback(" << reason_str << ")\n";
+|*==========================================================================*/
+
+        if (reason == APR_OC_REASON_DEATH || reason == APR_OC_REASON_LOST)
+        {
+            // Somewhat oddly, APR requires that you explicitly unregister
+            // even when it already knows the child has terminated.
+            apr_proc_other_child_unregister(data);
+
+            WaitInfo* wi(static_cast<WaitInfo*>(data));
+            // It's just wrong to call apr_proc_wait() here. The only way APR
+            // knows to call us with APR_OC_REASON_DEATH is that it's already
+            // reaped this child process, so calling wait() will only produce
+            // "huh?" from the OS. We must rely on the status param passed in,
+            // which unfortunately comes straight from the OS wait() call.
+//          wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
+            wi->rv = APR_CHILD_DONE; // fake apr_proc_wait() results
+#if defined(LL_WINDOWS)
+            wi->why = APR_PROC_EXIT;
+            wi->rc  = status;         // no encoding on Windows (no signals)
+#else  // Posix
+            if (WIFEXITED(status))
+            {
+                wi->why = APR_PROC_EXIT;
+                wi->rc  = WEXITSTATUS(status);
+            }
+            else if (WIFSIGNALED(status))
+            {
+                wi->why = APR_PROC_SIGNAL;
+                wi->rc  = WTERMSIG(status);
+            }
+            else                    // uh, shouldn't happen?
+            {
+                wi->why = APR_PROC_EXIT;
+                wi->rc  = status;   // someone else will have to decode
+            }
+#endif // Posix
+        }
+    }
+
+    template<> template<>
+    void object::test<1>()
+    {
+        set_test_name("raw APR nonblocking I/O");
+
+        // Create a script file in a temporary place.
+        NamedTempFile script("py",
+            "import sys" EOL
+            "import time" EOL
+            EOL
+            "time.sleep(2)" EOL
+            "print >>sys.stdout, 'stdout after wait'" EOL
+            "sys.stdout.flush()" EOL
+            "time.sleep(2)" EOL
+            "print >>sys.stderr, 'stderr after wait'" EOL
+            "sys.stderr.flush()" EOL
+            );
+
+        // Arrange to track the history of our interaction with child: what we
+        // fetched, which pipe it came from, how many tries it took before we
+        // got it.
+        std::vector<Item> history;
+        history.push_back(Item());
+
+        // Run the child process.
+        apr_procattr_t *procattr = NULL;
+        aprchk(apr_procattr_create(&procattr, pool.getAPRPool()));
+        aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK));
+        aprchk(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));
+
+        std::vector<const char*> argv;
+        apr_proc_t child;
+        argv.push_back("python");
+        // Have to have a named copy of this std::string so its c_str() value
+        // will persist.
+        std::string scriptname(script.getName());
+        argv.push_back(scriptname.c_str());
+        argv.push_back(NULL);
+
+        aprchk(apr_proc_create(&child, argv[0],
+                               &argv[0],
+                               NULL, // if we wanted to pass explicit environment
+                               procattr,
+                               pool.getAPRPool()));
+
+        // We do not want this child process to outlive our APR pool. On
+        // destruction of the pool, forcibly kill the process. Tell APR to try
+        // SIGTERM and wait 3 seconds. If that didn't work, use SIGKILL.
+        apr_pool_note_subprocess(pool.getAPRPool(), &child, APR_KILL_AFTER_TIMEOUT);
+
+        // arrange to call child_status_callback()
+        WaitInfo wi(&child);
+        apr_proc_other_child_register(&child, child_status_callback, &wi, child.in, pool.getAPRPool());
+
+        // TODO:
+        // Stuff child.in until it (would) block to verify EWOULDBLOCK/EAGAIN.
+        // Have child script clear it later, then write one more line to prove
+        // that it gets through.
+
+        // Monitor two different output pipes. Because one will be closed
+        // before the other, keep them in a list so we can drop whichever of
+        // them is closed first.
+        typedef std::pair<std::string, apr_file_t*> DescFile;
+        typedef std::list<DescFile> DescFileList;
+        DescFileList outfiles;
+        outfiles.push_back(DescFile("out", child.out));
+        outfiles.push_back(DescFile("err", child.err));
+
+        while (! outfiles.empty())
+        {
+            // This peculiar for loop is designed to let us erase(dfli). With
+            // a list, that invalidates only dfli itself -- but even so, we
+            // lose the ability to increment it for the next item. So at the
+            // top of every loop, while dfli is still valid, increment
+            // dflnext. Then before the next iteration, set dfli to dflnext.
+            for (DescFileList::iterator
+                     dfli(outfiles.begin()), dflnext(outfiles.begin()), dflend(outfiles.end());
+                 dfli != dflend; dfli = dflnext)
+            {
+                // Only valid to increment dflnext once we're sure it's not
+                // already at dflend.
+                ++dflnext;
+
+                char buf[4096];
+
+                apr_status_t rv = apr_file_gets(buf, sizeof(buf), dfli->second);
+                if (APR_STATUS_IS_EOF(rv))
+                {
+//                  std::cout << "(EOF on " << dfli->first << ")\n";
+//                  history.back().which = dfli->first;
+//                  history.back().what  = "*eof*";
+//                  history.push_back(Item());
+                    outfiles.erase(dfli);
+                    continue;
+                }
+                if (rv == EWOULDBLOCK || rv == EAGAIN)
+                {
+//                  std::cout << "(waiting; apr_file_gets(" << dfli->first << ") => " << rv << ": " << manager.strerror(rv) << ")\n";
+                    ++history.back().tries;
+                    continue;
+                }
+                aprchk_("apr_file_gets(buf, sizeof(buf), dfli->second)", rv);
+                // Is it even possible to get APR_SUCCESS but read 0 bytes?
+                // Hope not, but defend against that anyway.
+                if (buf[0])
+                {
+//                  std::cout << dfli->first << ": " << buf;
+                    history.back().which = dfli->first;
+                    history.back().what.append(buf);
+                    if (buf[strlen(buf) - 1] == '\n')
+                        history.push_back(Item());
+                    else
+                    {
+                        // Just for pretty output... if we only read a partial
+                        // line, terminate it.
+//                      std::cout << "...\n";
+                    }
+                }
+            }
+            // Do this once per tick, as we expect the viewer will
+            apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+            sleep(1);
+        }
+        apr_file_close(child.in);
+        apr_file_close(child.out);
+        apr_file_close(child.err);
+
+        // Okay, we've broken the loop because our pipes are all closed. If we
+        // haven't yet called wait, give the callback one more chance. This
+        // models the fact that unlike this small test program, the viewer
+        // will still be running.
+        if (wi.rv == -1)
+        {
+            std::cout << "last gasp apr_proc_other_child_refresh_all()\n";
+            apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+        }
+
+        if (wi.rv == -1)
+        {
+            std::cout << "child_status_callback(APR_OC_REASON_DEATH) wasn't called" << std::endl;
+            wi.rv = apr_proc_wait(wi.child, &wi.rc, &wi.why, APR_NOWAIT);
+        }
+//      std::cout << "child done: rv = " << rv << " (" << manager.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
+        aprchk_("apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT)", wi.rv, APR_CHILD_DONE);
+        ensure_equals_(wi.why, APR_PROC_EXIT);
+        ensure_equals_(wi.rc, 0);
+
+        // Beyond merely executing all the above successfully, verify that we
+        // obtained expected output -- and that we duly got control while
+        // waiting, proving the non-blocking nature of these pipes.
+        try
+        {
+            unsigned i = 0;
+            ensure("blocking I/O on child pipe (0)", history[i].tries);
+            ensure_equals_(history[i].which, "out");
+            ensure_equals_(history[i].what,  "stdout after wait" EOL);
+//          ++i;
+//          ensure_equals_(history[i].which, "out");
+//          ensure_equals_(history[i].what,  "*eof*");
+            ++i;
+            ensure("blocking I/O on child pipe (1)", history[i].tries);
+            ensure_equals_(history[i].which, "err");
+            ensure_equals_(history[i].what,  "stderr after wait" EOL);
+//          ++i;
+//          ensure_equals_(history[i].which, "err");
+//          ensure_equals_(history[i].what,  "*eof*");
+        }
+        catch (const failure&)
+        {
+            std::cout << "History:\n";
+            BOOST_FOREACH(const Item& item, history)
+            {
+                std::string what(item.what);
+                if ((! what.empty()) && what[what.length() - 1] == '\n')
+                {
+                    what.erase(what.length() - 1);
+                    if ((! what.empty()) && what[what.length() - 1] == '\r')
+                    {
+                        what.erase(what.length() - 1);
+                        what.append("\\r");
+                    }
+                    what.append("\\n");
+                }
+                std::cout << "  " << item.which << ": '" << what << "' ("
+                          << item.tries << " tries)\n";
+            }
+            std::cout << std::flush;
+            // re-raise same error; just want to enrich the output
+            throw;
+        }
+    }
+
+    template<> template<>
+    void object::test<2>()
+    {
+        set_test_name("setWorkingDirectory()");
+        // We want to test setWorkingDirectory(). But what directory is
+        // guaranteed to exist on every machine, under every OS? Have to
+        // create one. Naturally, ensure we clean it up when done.
+        NamedTempDir tempdir;
+        PythonProcessLauncher py(get_test_name(),
+                                 "from __future__ import with_statement\n"
+                                 "import os, sys\n"
+                                 "with open(sys.argv[1], 'w') as f:\n"
+                                 "    f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n");
+        // Before running, call setWorkingDirectory()
+        py.mParams.cwd = tempdir.getName();
+        ensure_equals("os.getcwd()", py.run_read(), tempdir.getName());
+    }
+
+    template<> template<>
+    void object::test<3>()
+    {
+        set_test_name("arguments");
+        PythonProcessLauncher py(get_test_name(),
+                                 "from __future__ import with_statement\n"
+                                 "import sys\n"
+                                 // note nonstandard output-file arg!
+                                 "with open(sys.argv[3], 'w') as f:\n"
+                                 "    for arg in sys.argv[1:]:\n"
+                                 "        print >>f, arg\n");
+        // We expect that PythonProcessLauncher has already appended
+        // its own NamedTempFile to mParams.args (sys.argv[0]).
+        py.mParams.args.add("first arg");          // sys.argv[1]
+        py.mParams.args.add("second arg");         // sys.argv[2]
+        // run_read() appends() one more argument, hence [3]
+        std::string output(py.run_read());
+        boost::split_iterator<std::string::const_iterator>
+            li(output, boost::first_finder("\n")), lend;
+        ensure("didn't get first arg", li != lend);
+        std::string arg(li->begin(), li->end());
+        ensure_equals(arg, "first arg");
+        ++li;
+        ensure("didn't get second arg", li != lend);
+        arg.assign(li->begin(), li->end());
+        ensure_equals(arg, "second arg");
+        ++li;
+        ensure("didn't get output filename?!", li != lend);
+        arg.assign(li->begin(), li->end());
+        ensure("output filename empty?!", ! arg.empty());
+        ++li;
+        ensure("too many args", li == lend);
+    }
+
+    template<> template<>
+    void object::test<4>()
+    {
+        set_test_name("exit(0)");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 "sys.exit(0)\n");
+        py.run();
+        ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+        ensure_equals("Status.mData",  py.mPy->getStatus().mData,  0);
+    }
+
+    template<> template<>
+    void object::test<5>()
+    {
+        set_test_name("exit(2)");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 "sys.exit(2)\n");
+        py.run();
+        ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+        ensure_equals("Status.mData",  py.mPy->getStatus().mData,  2);
+    }
+
+    template<> template<>
+    void object::test<6>()
+    {
+        set_test_name("syntax_error:");
+        PythonProcessLauncher py(get_test_name(),
+                                 "syntax_error:\n");
+        py.mParams.files.add(LLProcess::FileParam()); // inherit stdin
+        py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
+        py.mParams.files.add(LLProcess::FileParam().type("pipe")); // pipe for stderr
+        py.run();
+        ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+        ensure_equals("Status.mData",  py.mPy->getStatus().mData,  1);
+        std::istream& rpipe(py.mPy->getReadPipe(LLProcess::STDERR).get_istream());
+        std::vector<char> buffer(4096);
+        rpipe.read(&buffer[0], buffer.size());
+        std::streamsize got(rpipe.gcount());
+        ensure("Nothing read from stderr pipe", got);
+        std::string data(&buffer[0], got);
+        ensure("Didn't find 'SyntaxError:'", data.find("\nSyntaxError:") != std::string::npos);
+    }
+
+    template<> template<>
+    void object::test<7>()
+    {
+        set_test_name("explicit kill()");
+        PythonProcessLauncher py(get_test_name(),
+                                 "from __future__ import with_statement\n"
+                                 "import sys, time\n"
+                                 "with open(sys.argv[1], 'w') as f:\n"
+                                 "    f.write('ok')\n"
+                                 "# now sleep; expect caller to kill\n"
+                                 "time.sleep(120)\n"
+                                 "# if caller hasn't managed to kill by now, bad\n"
+                                 "with open(sys.argv[1], 'w') as f:\n"
+                                 "    f.write('bad')\n");
+        NamedTempFile out("out", "not started");
+        py.mParams.args.add(out.getName());
+        py.launch();
+        // Wait for the script to wake up and do its first write
+        int i = 0, timeout = 60;
+        for ( ; i < timeout; ++i)
+        {
+            yield();
+            if (readfile(out.getName(), "from kill() script") == "ok")
+                break;
+        }
+        // If we broke this loop because of the counter, something's wrong
+        ensure("script never started", i < timeout);
+        // script has performed its first write and should now be sleeping.
+        py.mPy->kill();
+        // wait for the script to terminate... one way or another.
+        waitfor(*py.mPy);
+#if LL_WINDOWS
+        ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+        ensure_equals("Status.mData",  py.mPy->getStatus().mData,  -1);
+#else
+        ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::KILLED);
+        ensure_equals("Status.mData",  py.mPy->getStatus().mData,  SIGTERM);
+#endif
+        // If kill() failed, the script would have woken up on its own and
+        // overwritten the file with 'bad'. But if kill() succeeded, it should
+        // not have had that chance.
+        ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+    }
+
+    template<> template<>
+    void object::test<8>()
+    {
+        set_test_name("implicit kill()");
+        NamedTempFile out("out", "not started");
+        LLProcess::handle phandle(0);
+        {
+            PythonProcessLauncher py(get_test_name(),
+                                     "from __future__ import with_statement\n"
+                                     "import sys, time\n"
+                                     "with open(sys.argv[1], 'w') as f:\n"
+                                     "    f.write('ok')\n"
+                                     "# now sleep; expect caller to kill\n"
+                                     "time.sleep(120)\n"
+                                     "# if caller hasn't managed to kill by now, bad\n"
+                                     "with open(sys.argv[1], 'w') as f:\n"
+                                     "    f.write('bad')\n");
+            py.mParams.args.add(out.getName());
+            py.launch();
+            // Capture handle for later
+            phandle = py.mPy->getProcessHandle();
+            // Wait for the script to wake up and do its first write
+            int i = 0, timeout = 60;
+            for ( ; i < timeout; ++i)
+            {
+                yield();
+                if (readfile(out.getName(), "from kill() script") == "ok")
+                    break;
+            }
+            // If we broke this loop because of the counter, something's wrong
+            ensure("script never started", i < timeout);
+            // Script has performed its first write and should now be sleeping.
+            // Destroy the LLProcess, which should kill the child.
+        }
+        // wait for the script to terminate... one way or another.
+        waitfor(phandle, "kill() script");
+        // If kill() failed, the script would have woken up on its own and
+        // overwritten the file with 'bad'. But if kill() succeeded, it should
+        // not have had that chance.
+        ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+    }
+
+    template<> template<>
+    void object::test<9>()
+    {
+        set_test_name("autokill=false");
+        NamedTempFile from("from", "not started");
+        NamedTempFile to("to", "");
+        LLProcess::handle phandle(0);
+        {
+            PythonProcessLauncher py(get_test_name(),
+                                     "from __future__ import with_statement\n"
+                                     "import sys, time\n"
+                                     "with open(sys.argv[1], 'w') as f:\n"
+                                     "    f.write('ok')\n"
+                                     "# wait for 'go' from test program\n"
+                                     "for i in xrange(60):\n"
+                                     "    time.sleep(1)\n"
+                                     "    with open(sys.argv[2]) as f:\n"
+                                     "        go = f.read()\n"
+                                     "    if go == 'go':\n"
+                                     "        break\n"
+                                     "else:\n"
+                                     "    with open(sys.argv[1], 'w') as f:\n"
+                                     "        f.write('never saw go')\n"
+                                     "    sys.exit(1)\n"
+                                     "# okay, saw 'go', write 'ack'\n"
+                                     "with open(sys.argv[1], 'w') as f:\n"
+                                     "    f.write('ack')\n");
+            py.mParams.args.add(from.getName());
+            py.mParams.args.add(to.getName());
+            py.mParams.autokill = false;
+            py.launch();
+            // Capture handle for later
+            phandle = py.mPy->getProcessHandle();
+            // Wait for the script to wake up and do its first write
+            int i = 0, timeout = 60;
+            for ( ; i < timeout; ++i)
+            {
+                yield();
+                if (readfile(from.getName(), "from autokill script") == "ok")
+                    break;
+            }
+            // If we broke this loop because of the counter, something's wrong
+            ensure("script never started", i < timeout);
+            // Now destroy the LLProcess, which should NOT kill the child!
+        }
+        // If the destructor killed the child anyway, give it time to die
+        yield(2);
+        // How do we know it's not terminated? By making it respond to
+        // a specific stimulus in a specific way.
+        {
+            std::ofstream outf(to.getName().c_str());
+            outf << "go";
+        } // flush and close.
+        // now wait for the script to terminate... one way or another.
+        waitfor(phandle, "autokill script");
+        // If the LLProcess destructor implicitly called kill(), the
+        // script could not have written 'ack' as we expect.
+        ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack");
+    }
+
+    template<> template<>
+    void object::test<10>()
+    {
+        set_test_name("'bogus' test");
+        CaptureLog recorder;
+        PythonProcessLauncher py(get_test_name(),
+                                 "print 'Hello world'\n");
+        py.mParams.files.add(LLProcess::FileParam("bogus"));
+        py.mPy = LLProcess::create(py.mParams);
+        ensure("should have rejected 'bogus'", ! py.mPy);
+        std::string message(recorder.messageWith("bogus"));
+        ensure_contains("did not name 'stdin'", message, "stdin");
+    }
+
+    template<> template<>
+    void object::test<11>()
+    {
+        set_test_name("'file' test");
+        // Replace this test with one or more real 'file' tests when we
+        // implement 'file' support
+        PythonProcessLauncher py(get_test_name(),
+                                 "print 'Hello world'\n");
+        py.mParams.files.add(LLProcess::FileParam());
+        py.mParams.files.add(LLProcess::FileParam("file"));
+        py.mPy = LLProcess::create(py.mParams);
+        ensure("should have rejected 'file'", ! py.mPy);
+    }
+
+    template<> template<>
+    void object::test<12>()
+    {
+        set_test_name("'tpipe' test");
+        // Replace this test with one or more real 'tpipe' tests when we
+        // implement 'tpipe' support
+        CaptureLog recorder;
+        PythonProcessLauncher py(get_test_name(),
+                                 "print 'Hello world'\n");
+        py.mParams.files.add(LLProcess::FileParam());
+        py.mParams.files.add(LLProcess::FileParam("tpipe"));
+        py.mPy = LLProcess::create(py.mParams);
+        ensure("should have rejected 'tpipe'", ! py.mPy);
+        std::string message(recorder.messageWith("tpipe"));
+        ensure_contains("did not name 'stdout'", message, "stdout");
+    }
+
+    template<> template<>
+    void object::test<13>()
+    {
+        set_test_name("'npipe' test");
+        // Replace this test with one or more real 'npipe' tests when we
+        // implement 'npipe' support
+        CaptureLog recorder;
+        PythonProcessLauncher py(get_test_name(),
+                                 "print 'Hello world'\n");
+        py.mParams.files.add(LLProcess::FileParam());
+        py.mParams.files.add(LLProcess::FileParam());
+        py.mParams.files.add(LLProcess::FileParam("npipe"));
+        py.mPy = LLProcess::create(py.mParams);
+        ensure("should have rejected 'npipe'", ! py.mPy);
+        std::string message(recorder.messageWith("npipe"));
+        ensure_contains("did not name 'stderr'", message, "stderr");
+    }
+
+    template<> template<>
+    void object::test<14>()
+    {
+        set_test_name("internal pipe name warning");
+        CaptureLog recorder;
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 "sys.exit(7)\n");
+        py.mParams.files.add(LLProcess::FileParam("pipe", "somename"));
+        py.run();                   // verify that it did launch anyway
+        ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+        ensure_equals("Status.mData",  py.mPy->getStatus().mData,  7);
+        std::string message(recorder.messageWith("not yet supported"));
+        ensure_contains("log message did not mention internal pipe name",
+                        message, "somename");
+    }
+
+    /*-------------- support for "get*Pipe() validation" test --------------*/
+#define TEST_getPipe(PROCESS, GETPIPE, GETOPTPIPE, VALID, NOPIPE, BADPIPE) \
+    do                                                                  \
+    {                                                                   \
+        std::string threw;                                              \
+        /* Both the following calls should work. */                     \
+        (PROCESS).GETPIPE(VALID);                                       \
+        ensure(#GETOPTPIPE "(" #VALID ") failed", (PROCESS).GETOPTPIPE(VALID)); \
+        /* pass obviously bogus PIPESLOT */                             \
+        CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(LLProcess::FILESLOT(4))); \
+        ensure_contains("didn't reject bad slot", threw, "no slot");    \
+        ensure_contains("didn't mention bad slot num", threw, "4");     \
+        EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(LLProcess::FILESLOT(4))); \
+        /* pass NOPIPE */                                               \
+        CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(NOPIPE));  \
+        ensure_contains("didn't reject non-pipe", threw, "not a monitored"); \
+        EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(NOPIPE));      \
+        /* pass BADPIPE: FILESLOT isn't empty but wrong direction */    \
+        CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(BADPIPE)); \
+        /* sneaky: GETPIPE is getReadPipe or getWritePipe */            \
+        /* so skip "get" to obtain ReadPipe or WritePipe  :-P  */       \
+        ensure_contains("didn't reject wrong pipe", threw, (#GETPIPE)+3); \
+        EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(BADPIPE));     \
+    } while (0)
+
+/// For expecting exceptions. Execute CODE, catch EXCEPTION, store its what()
+/// in std::string THREW, ensure it's not empty (i.e. EXCEPTION did happen).
+#define CATCH_IN(THREW, EXCEPTION, CODE)                                \
+    do                                                                  \
+    {                                                                   \
+        (THREW).clear();                                                \
+        try                                                             \
+        {                                                               \
+            CODE;                                                       \
+        }                                                               \
+        CATCH_AND_STORE_WHAT_IN(THREW, EXCEPTION)                       \
+        ensure("failed to throw " #EXCEPTION ": " #CODE, ! (THREW).empty()); \
+    } while (0)
+
+#define EXPECT_FAIL_WITH_LOG(EXPECT, CODE)                              \
+    do                                                                  \
+    {                                                                   \
+        CaptureLog recorder;                                            \
+        ensure(#CODE " succeeded", ! (CODE));                           \
+        recorder.messageWith(EXPECT);                                   \
+    } while (0)
+
+    template<> template<>
+    void object::test<15>()
+    {
+        set_test_name("get*Pipe() validation");
+        PythonProcessLauncher py(get_test_name(),
+                                 "print 'this output is expected'\n");
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for  stdin
+        py.mParams.files.add(LLProcess::FileParam());       // inherit stdout
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr
+        py.run();
+        TEST_getPipe(*py.mPy, getWritePipe, getOptWritePipe,
+                     LLProcess::STDIN,   // VALID
+                     LLProcess::STDOUT,  // NOPIPE
+                     LLProcess::STDERR); // BADPIPE
+        TEST_getPipe(*py.mPy, getReadPipe,  getOptReadPipe,
+                     LLProcess::STDERR,  // VALID
+                     LLProcess::STDOUT,  // NOPIPE
+                     LLProcess::STDIN);  // BADPIPE
+    }
+
+    template<> template<>
+    void object::test<16>()
+    {
+        set_test_name("talk to stdin/stdout");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys, time\n"
+                                 "print 'ok'\n"
+                                 "sys.stdout.flush()\n"
+                                 "# wait for 'go' from test program\n"
+                                 "go = sys.stdin.readline()\n"
+                                 "if go != 'go\\n':\n"
+                                 "    sys.exit('expected \"go\", saw %r' % go)\n"
+                                 "print 'ack'\n");
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+        py.launch();
+        LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+        int i, timeout = 60;
+        for (i = 0; i < timeout && py.mPy->isRunning() && childout.size() < 3; ++i)
+        {
+            yield();
+        }
+        ensure("script never started", i < timeout);
+        ensure_equals("bad wakeup from stdin/stdout script",
+                      childout.getline(), "ok");
+        // important to get the implicit flush from std::endl
+        py.mPy->getWritePipe().get_ostream() << "go" << std::endl;
+        for (i = 0; i < timeout && py.mPy->isRunning() && ! childout.contains("\n"); ++i)
+        {
+            yield();
+        }
+        ensure("script never replied", childout.contains("\n"));
+        ensure_equals("child didn't ack", childout.getline(), "ack");
+        ensure_equals("bad child termination", py.mPy->getStatus().mState, LLProcess::EXITED);
+        ensure_equals("bad child exit code",   py.mPy->getStatus().mData,  0);
+    }
+
+    struct EventListener: public boost::noncopyable
+    {
+        EventListener(LLEventPump& pump)
+        {
+            mConnection = 
+                pump.listen("EventListener", boost::bind(&EventListener::tick, this, _1));
+        }
+
+        bool tick(const LLSD& data)
+        {
+            mHistory.push_back(data);
+            return false;
+        }
+
+        std::list<LLSD> mHistory;
+        LLTempBoundListener mConnection;
+    };
+
+    static bool ack(std::ostream& out, const LLSD& data)
+    {
+        out << "continue" << std::endl;
+        return false;
+    }
+
+    template<> template<>
+    void object::test<17>()
+    {
+        set_test_name("listen for ReadPipe events");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 "sys.stdout.write('abc')\n"
+                                 "sys.stdout.flush()\n"
+                                 "sys.stdin.readline()\n"
+                                 "sys.stdout.write('def')\n"
+                                 "sys.stdout.flush()\n"
+                                 "sys.stdin.readline()\n"
+                                 "sys.stdout.write('ghi\\n')\n"
+                                 "sys.stdout.flush()\n"
+                                 "sys.stdin.readline()\n"
+                                 "sys.stdout.write('second line\\n')\n");
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+        py.launch();
+        std::ostream& childin(py.mPy->getWritePipe(LLProcess::STDIN).get_ostream());
+        LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+        // lift the default limit; allow event to carry (some of) the actual data
+        childout.setLimit(20);
+        // listen for incoming data on childout
+        EventListener listener(childout.getPump());
+        // also listen with a function that prompts the child to continue
+        // every time we see output
+        LLTempBoundListener connection(
+            childout.getPump().listen("ack", boost::bind(ack, boost::ref(childin), _1)));
+        int i, timeout = 60;
+        // wait through stuttering first line
+        for (i = 0; i < timeout && py.mPy->isRunning() && ! childout.contains("\n"); ++i)
+        {
+            yield();
+        }
+        ensure("couldn't get first line", i < timeout);
+        // disconnect from listener
+        listener.mConnection.disconnect();
+        // finish out the run
+        waitfor(*py.mPy);
+        // now verify history
+        std::list<LLSD>::const_iterator li(listener.mHistory.begin()),
+                                        lend(listener.mHistory.end());
+        ensure("no events", li != lend);
+        ensure_equals("history[0]", (*li)["data"].asString(), "abc");
+        ensure_equals("history[0] len", (*li)["len"].asInteger(), 3);
+        ++li;
+        ensure("only 1 event", li != lend);
+        ensure_equals("history[1]", (*li)["data"].asString(), "abcdef");
+        ensure_equals("history[0] len", (*li)["len"].asInteger(), 6);
+        ++li;
+        ensure("only 2 events", li != lend);
+        ensure_equals("history[2]", (*li)["data"].asString(), "abcdefghi" EOL);
+        ensure_equals("history[0] len", (*li)["len"].asInteger(), 9 + sizeof(EOL) - 1);
+        ++li;
+        // We DO NOT expect a whole new event for the second line because we
+        // disconnected.
+        ensure("more than 3 events", li == lend);
+    }
+
+    template<> template<>
+    void object::test<18>()
+    {
+        set_test_name("ReadPipe \"eof\" event");
+        PythonProcessLauncher py(get_test_name(),
+                                 "print 'Hello from Python!'\n");
+        py.mParams.files.add(LLProcess::FileParam()); // stdin
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+        py.launch();
+        LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+        EventListener listener(childout.getPump());
+        waitfor(*py.mPy);
+        // We can't be positive there will only be a single event, if the OS
+        // (or any other intervening layer) does crazy buffering. What we want
+        // to ensure is that there was exactly ONE event with "eof" true, and
+        // that it was the LAST event.
+        std::list<LLSD>::const_reverse_iterator rli(listener.mHistory.rbegin()),
+                                                rlend(listener.mHistory.rend());
+        ensure("no events", rli != rlend);
+        ensure("last event not \"eof\"", (*rli)["eof"].asBoolean());
+        while (++rli != rlend)
+        {
+            ensure("\"eof\" event not last", ! (*rli)["eof"].asBoolean());
+        }
+    }
+
+    template<> template<>
+    void object::test<19>()
+    {
+        set_test_name("setLimit()");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 "sys.stdout.write(sys.argv[1])\n");
+        std::string abc("abcdefghijklmnopqrstuvwxyz");
+        py.mParams.args.add(abc);
+        py.mParams.files.add(LLProcess::FileParam()); // stdin
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+        py.launch();
+        LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+        // listen for incoming data on childout
+        EventListener listener(childout.getPump());
+        // but set limit
+        childout.setLimit(10);
+        ensure_equals("getLimit() after setlimit(10)", childout.getLimit(), 10);
+        // okay, pump I/O to pick up output from child
+        waitfor(*py.mPy);
+        ensure("no events", ! listener.mHistory.empty());
+        // For all we know, that data could have arrived in several different
+        // bursts... probably not, but anyway, only check the last one.
+        ensure_equals("event[\"len\"]",
+                      listener.mHistory.back()["len"].asInteger(), abc.length());
+        ensure_equals("length of setLimit(10) data",
+                      listener.mHistory.back()["data"].asString().length(), 10);
+    }
+
+    template<> template<>
+    void object::test<20>()
+    {
+        set_test_name("peek() ReadPipe data");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 "sys.stdout.write(sys.argv[1])\n");
+        std::string abc("abcdefghijklmnopqrstuvwxyz");
+        py.mParams.args.add(abc);
+        py.mParams.files.add(LLProcess::FileParam()); // stdin
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+        py.launch();
+        LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+        // okay, pump I/O to pick up output from child
+        waitfor(*py.mPy);
+        // peek() with substr args
+        ensure_equals("peek()", childout.peek(), abc);
+        ensure_equals("peek(23)", childout.peek(23), abc.substr(23));
+        ensure_equals("peek(5, 3)", childout.peek(5, 3), abc.substr(5, 3));
+        ensure_equals("peek(27, 2)", childout.peek(27, 2), "");
+        ensure_equals("peek(23, 5)", childout.peek(23, 5), "xyz");
+        // contains() -- we don't exercise as thoroughly as find() because the
+        // contains() implementation is trivially (and visibly) based on find()
+        ensure("contains(\":\")", ! childout.contains(":"));
+        ensure("contains(':')",   ! childout.contains(':'));
+        ensure("contains(\"d\")", childout.contains("d"));
+        ensure("contains('d')",   childout.contains('d'));
+        ensure("contains(\"klm\")", childout.contains("klm"));
+        ensure("contains(\"klx\")", ! childout.contains("klx"));
+        // find()
+        ensure("find(\":\")", childout.find(":") == LLProcess::ReadPipe::npos);
+        ensure("find(':')",   childout.find(':') == LLProcess::ReadPipe::npos);
+        ensure_equals("find(\"d\")", childout.find("d"), 3);
+        ensure_equals("find('d')",   childout.find('d'), 3);
+        ensure_equals("find(\"d\", 3)", childout.find("d", 3), 3);
+        ensure_equals("find('d', 3)",   childout.find('d', 3), 3);
+        ensure("find(\"d\", 4)", childout.find("d", 4) == LLProcess::ReadPipe::npos);
+        ensure("find('d', 4)",   childout.find('d', 4) == LLProcess::ReadPipe::npos);
+        // The case of offset == end and offset > end are different. In the
+        // first case, we can form a valid (albeit empty) iterator range and
+        // search that. In the second, guard logic in the implementation must
+        // realize we can't form a valid iterator range.
+        ensure("find(\"d\", 26)", childout.find("d", 26) == LLProcess::ReadPipe::npos);
+        ensure("find('d', 26)",   childout.find('d', 26) == LLProcess::ReadPipe::npos);
+        ensure("find(\"d\", 27)", childout.find("d", 27) == LLProcess::ReadPipe::npos);
+        ensure("find('d', 27)",   childout.find('d', 27) == LLProcess::ReadPipe::npos);
+        ensure_equals("find(\"ghi\")", childout.find("ghi"), 6);
+        ensure_equals("find(\"ghi\", 6)", childout.find("ghi"), 6);
+        ensure("find(\"ghi\", 7)", childout.find("ghi", 7) == LLProcess::ReadPipe::npos);
+        ensure("find(\"ghi\", 26)", childout.find("ghi", 26) == LLProcess::ReadPipe::npos);
+        ensure("find(\"ghi\", 27)", childout.find("ghi", 27) == LLProcess::ReadPipe::npos);
+    }
+
+    template<> template<>
+    void object::test<21>()
+    {
+        set_test_name("bad postend");
+        std::string pumpname("postend");
+        EventListener listener(LLEventPumps::instance().obtain(pumpname));
+        LLProcess::Params params;
+        params.desc = get_test_name();
+        params.postend = pumpname;
+        LLProcessPtr child = LLProcess::create(params);
+        ensure("shouldn't have launched", ! child);
+        ensure_equals("number of postend events", listener.mHistory.size(), 1);
+        LLSD postend(listener.mHistory.front());
+        ensure("has id", ! postend.has("id"));
+        ensure_equals("desc", postend["desc"].asString(), std::string(params.desc));
+        ensure_equals("state", postend["state"].asInteger(), LLProcess::UNSTARTED);
+        ensure("has data", ! postend.has("data"));
+        std::string error(postend["string"]);
+        // All we get from canned parameter validation is a bool, so the
+        // "validation failed" message we ourselves generate can't mention
+        // "executable" by name. Just check that it's nonempty.
+        //ensure_contains("error", error, "executable");
+        ensure("string", ! error.empty());
+    }
+
+    template<> template<>
+    void object::test<22>()
+    {
+        set_test_name("good postend");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 "sys.exit(35)\n");
+        std::string pumpname("postend");
+        EventListener listener(LLEventPumps::instance().obtain(pumpname));
+        py.mParams.postend = pumpname;
+        py.launch();
+        LLProcess::id childid(py.mPy->getProcessID());
+        // Don't use waitfor(), which calls isRunning(); instead wait for an
+        // event on pumpname.
+        int i, timeout = 60;
+        for (i = 0; i < timeout && listener.mHistory.empty(); ++i)
+        {
+            yield();
+        }
+        ensure("no postend event", i < timeout);
+        ensure_equals("number of postend events", listener.mHistory.size(), 1);
+        LLSD postend(listener.mHistory.front());
+        ensure_equals("id",    postend["id"].asInteger(), childid);
+        ensure("desc empty", ! postend["desc"].asString().empty());
+        ensure_equals("state", postend["state"].asInteger(), LLProcess::EXITED);
+        ensure_equals("data",  postend["data"].asInteger(),  35);
+        std::string str(postend["string"]);
+        ensure_contains("string", str, "exited");
+        ensure_contains("string", str, "35");
+    }
+
+    struct PostendListener
+    {
+        PostendListener(LLProcess::ReadPipe& rpipe,
+                        const std::string& pumpname,
+                        const std::string& expect):
+            mReadPipe(rpipe),
+            mExpect(expect),
+            mTriggered(false)
+        {
+            LLEventPumps::instance().obtain(pumpname)
+                .listen("PostendListener", boost::bind(&PostendListener::postend, this, _1));
+        }
+
+        bool postend(const LLSD&)
+        {
+            mTriggered = true;
+            ensure_equals("postend listener", mReadPipe.read(mReadPipe.size()), mExpect);
+            return false;
+        }
+
+        LLProcess::ReadPipe& mReadPipe;
+        std::string mExpect;
+        bool mTriggered;
+    };
+
+    template<> template<>
+    void object::test<23>()
+    {
+        set_test_name("all data visible at postend");
+        PythonProcessLauncher py(get_test_name(),
+                                 "import sys\n"
+                                 // note, no '\n' in written data
+                                 "sys.stdout.write('partial line')\n");
+        std::string pumpname("postend");
+        py.mParams.files.add(LLProcess::FileParam()); // stdin
+        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+        py.mParams.postend = pumpname;
+        py.launch();
+        PostendListener listener(py.mPy->getReadPipe(LLProcess::STDOUT),
+                                 pumpname,
+                                 "partial line");
+        waitfor(*py.mPy);
+        ensure("postend never triggered", listener.mTriggered);
+    }
+} // namespace tut
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 72322c3b727a46b9f24a9527badfa76657eb2229..e625545763a2851a1e3de11f226a205a1cd5f98b 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -40,41 +40,15 @@ typedef U32 uint32_t;
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
-#include "llprocesslauncher.h"
+#include "llprocess.h"
 #endif
 
-#include <sstream>
-
-/*==========================================================================*|
-// Whoops, seems Linden's Boost package and the viewer are built with
-// different settings of VC's /Zc:wchar_t switch! Using Boost.Filesystem
-// pathname operations produces Windows link errors:
-// unresolved external symbol "private: static class std::codecvt<unsigned short,
-// char,int> const * & __cdecl boost::filesystem3::path::wchar_t_codecvt_facet()"
-// unresolved external symbol "void __cdecl boost::filesystem3::path_traits::convert()"
-// See:
-// http://boost.2283326.n4.nabble.com/filesystem-v3-unicode-and-std-codecvt-linker-error-td3455549.html
-// which points to:
-// http://msdn.microsoft.com/en-us/library/dh8che7s%28v=VS.100%29.aspx
-
-// As we're not trying to preserve compatibility with old Boost.Filesystem
-// code, but rather writing brand-new code, use the newest available
-// Filesystem API.
-#define BOOST_FILESYSTEM_VERSION 3
-#include "boost/filesystem.hpp"
-#include "boost/filesystem/v3/fstream.hpp"
-|*==========================================================================*/
 #include "boost/range.hpp"
 #include "boost/foreach.hpp"
 #include "boost/function.hpp"
 #include "boost/lambda/lambda.hpp"
 #include "boost/lambda/bind.hpp"
 namespace lambda = boost::lambda;
-/*==========================================================================*|
-// Aaaarrgh, Linden's Boost package doesn't even include Boost.Iostreams!
-#include "boost/iostreams/stream.hpp"
-#include "boost/iostreams/device/file_descriptor.hpp"
-|*==========================================================================*/
 
 #include "../llsd.h"
 #include "../llsdserialize.h"
@@ -82,236 +56,17 @@ namespace lambda = boost::lambda;
 #include "../llformat.h"
 
 #include "../test/lltut.h"
+#include "../test/manageapr.h"
+#include "../test/namedtempfile.h"
 #include "stringize.h"
 
+static ManageAPR manager;
+
 std::vector<U8> string_to_vector(const std::string& str)
 {
 	return std::vector<U8>(str.begin(), str.end());
 }
 
-#if ! LL_WINDOWS
-// We want to call strerror_r(), but alarmingly, there are two different
-// variants. The one that returns int always populates the passed buffer
-// (except in case of error), whereas the other one always returns a valid
-// char* but might or might not populate the passed buffer. How do we know
-// which one we're getting? Define adapters for each and let the compiler
-// select the applicable adapter.
-
-// strerror_r() returns char*
-std::string message_from(int /*orig_errno*/, const char* /*buffer*/, const char* strerror_ret)
-{
-    return strerror_ret;
-}
-
-// strerror_r() returns int
-std::string message_from(int orig_errno, const char* buffer, int strerror_ret)
-{
-    if (strerror_ret == 0)
-    {
-        return buffer;
-    }
-    // Here strerror_r() has set errno. Since strerror_r() has already failed,
-    // seems like a poor bet to call it again to diagnose its own error...
-    int stre_errno = errno;
-    if (stre_errno == ERANGE)
-    {
-        return STRINGIZE("strerror_r() can't explain errno " << orig_errno
-                         << " (buffer too small)");
-    }
-    if (stre_errno == EINVAL)
-    {
-        return STRINGIZE("unknown errno " << orig_errno);
-    }
-    // Here we don't even understand the errno from strerror_r()!
-    return STRINGIZE("strerror_r() can't explain errno " << orig_errno
-                     << " (error " << stre_errno << ')');
-}
-#endif  // ! LL_WINDOWS
-
-// boost::filesystem::temp_directory_path() isn't yet in Boost 1.45! :-(
-std::string temp_directory_path()
-{
-#if LL_WINDOWS
-    char buffer[4096];
-    GetTempPathA(sizeof(buffer), buffer);
-    return buffer;
-
-#else  // LL_DARWIN, LL_LINUX
-    static const char* vars[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR" };
-    BOOST_FOREACH(const char* var, vars)
-    {
-        const char* found = getenv(var);
-        if (found)
-            return found;
-    }
-    return "/tmp";
-#endif // LL_DARWIN, LL_LINUX
-}
-
-// Windows presents a kinda sorta compatibility layer. Code to the yucky
-// Windows names because they're less likely than the Posix names to collide
-// with any other names in this source.
-#if LL_WINDOWS
-#define _remove   DeleteFileA
-#else  // ! LL_WINDOWS
-#define _open     open
-#define _write    write
-#define _close    close
-#define _remove   remove
-#endif  // ! LL_WINDOWS
-
-// Create a text file with specified content "somewhere in the
-// filesystem," cleaning up when it goes out of scope.
-class NamedTempFile
-{
-public:
-    // Function that accepts an ostream ref and (presumably) writes stuff to
-    // it, e.g.:
-    // (lambda::_1 << "the value is " << 17 << '\n')
-    typedef boost::function<void(std::ostream&)> Streamer;
-
-    NamedTempFile(const std::string& ext, const std::string& content):
-        mPath(temp_directory_path())
-    {
-        createFile(ext, lambda::_1 << content);
-    }
-
-    // Disambiguate when passing string literal
-    NamedTempFile(const std::string& ext, const char* content):
-        mPath(temp_directory_path())
-    {
-        createFile(ext, lambda::_1 << content);
-    }
-
-    NamedTempFile(const std::string& ext, const Streamer& func):
-        mPath(temp_directory_path())
-    {
-        createFile(ext, func);
-    }
-
-    ~NamedTempFile()
-    {
-        _remove(mPath.c_str());
-    }
-
-    std::string getName() const { return mPath; }
-
-private:
-    void createFile(const std::string& ext, const Streamer& func)
-    {
-        // Silly maybe, but use 'ext' as the name prefix. Strip off a leading
-        // '.' if present.
-        int pfx_offset = ((! ext.empty()) && ext[0] == '.')? 1 : 0;
-
-#if ! LL_WINDOWS
-        // Make sure mPath ends with a directory separator, if it doesn't already.
-        if (mPath.empty() ||
-            ! (mPath[mPath.length() - 1] == '\\' || mPath[mPath.length() - 1] == '/'))
-        {
-            mPath.append("/");
-        }
-
-        // mkstemp() accepts and modifies a char* template string. Generate
-        // the template string, then copy to modifiable storage.
-        // mkstemp() requires its template string to end in six X's.
-        mPath += ext.substr(pfx_offset) + "XXXXXX";
-        // Copy to vector<char>
-        std::vector<char> pathtemplate(mPath.begin(), mPath.end());
-        // append a nul byte for classic-C semantics
-        pathtemplate.push_back('\0');
-        // std::vector promises that a pointer to the 0th element is the same
-        // as a pointer to a contiguous classic-C array
-        int fd(mkstemp(&pathtemplate[0]));
-        if (fd == -1)
-        {
-            // The documented errno values (http://linux.die.net/man/3/mkstemp)
-            // are used in a somewhat unusual way, so provide context-specific
-            // errors.
-            if (errno == EEXIST)
-            {
-                LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath
-                                         << "\") could not create unique file " << LL_ENDL;
-            }
-            if (errno == EINVAL)
-            {
-                LL_ERRS("NamedTempFile") << "bad mkstemp() file path template '"
-                                         << mPath << "'" << LL_ENDL;
-            }
-            // Shrug, something else
-            int mkst_errno = errno;
-            char buffer[256];
-            LL_ERRS("NamedTempFile") << "mkstemp(\"" << mPath << "\") failed: "
-                                     << message_from(mkst_errno, buffer,
-                                                     strerror_r(mkst_errno, buffer, sizeof(buffer)))
-                                     << LL_ENDL;
-        }
-        // mkstemp() seems to have worked! Capture the modified filename.
-        // Avoid the nul byte we appended.
-        mPath.assign(pathtemplate.begin(), (pathtemplate.end()-1));
-
-/*==========================================================================*|
-        // Define an ostream on the open fd. Tell it to close fd on destruction.
-        boost::iostreams::stream<boost::iostreams::file_descriptor_sink>
-            out(fd, boost::iostreams::close_handle);
-|*==========================================================================*/
-
-        // Write desired content.
-        std::ostringstream out;
-        // Stream stuff to it.
-        func(out);
-
-        std::string data(out.str());
-        int written(_write(fd, data.c_str(), data.length()));
-        int closed(_close(fd));
-        llassert_always(written == data.length() && closed == 0);
-
-#else // LL_WINDOWS
-        // GetTempFileName() is documented to require a MAX_PATH buffer.
-        char tempname[MAX_PATH];
-        // Use 'ext' as filename prefix, but skip leading '.' if any.
-        // The 0 param is very important: requests iterating until we get a
-        // unique name.
-        if (0 == GetTempFileNameA(mPath.c_str(), ext.c_str() + pfx_offset, 0, tempname))
-        {
-            // I always have to look up this call...  :-P
-            LPSTR msgptr;
-            FormatMessageA(
-                FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-                FORMAT_MESSAGE_FROM_SYSTEM |
-                FORMAT_MESSAGE_IGNORE_INSERTS,
-                NULL,
-                GetLastError(),
-                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                LPSTR(&msgptr),     // have to cast (char**) to (char*)
-                0, NULL );
-            LL_ERRS("NamedTempFile") << "GetTempFileName(\"" << mPath << "\", \""
-                                     << (ext.c_str() + pfx_offset) << "\") failed: "
-                                     << msgptr << LL_ENDL;
-            LocalFree(msgptr);
-        }
-        // GetTempFileName() appears to have worked! Capture the actual
-        // filename.
-        mPath = tempname;
-        // Open the file and stream content to it. Destructor will close.
-        std::ofstream out(tempname);
-        func(out);
-
-#endif  // LL_WINDOWS
-    }
-
-    void peep()
-    {
-        std::cout << "File '" << mPath << "' contains:\n";
-        std::ifstream reader(mPath.c_str());
-        std::string line;
-        while (std::getline(reader, line))
-            std::cout << line << '\n';
-        std::cout << "---\n";
-    }
-
-    std::string mPath;
-};
-
 namespace tut
 {
 	struct sd_xml_data
@@ -1783,7 +1538,7 @@ namespace tut
             const char* PYTHON(getenv("PYTHON"));
             ensure("Set $PYTHON to the Python interpreter", PYTHON);
 
-            NamedTempFile scriptfile(".py", script);
+            NamedTempFile scriptfile("py", script);
 
 #if LL_WINDOWS
             std::string q("\"");
@@ -1802,14 +1557,15 @@ namespace tut
             }
 
 #else  // LL_DARWIN, LL_LINUX
-            LLProcessLauncher py;
-            py.setExecutable(PYTHON);
-            py.addArgument(scriptfile.getName());
-            ensure_equals(STRINGIZE("Couldn't launch " << desc << " script"), py.launch(), 0);
+            LLProcess::Params params;
+            params.executable = PYTHON;
+            params.args.add(scriptfile.getName());
+            LLProcessPtr py(LLProcess::create(params));
+            ensure(STRINGIZE("Couldn't launch " << desc << " script"), py);
             // Implementing timeout would mean messing with alarm() and
             // catching SIGALRM... later maybe...
             int status(0);
-            if (waitpid(py.getProcessID(), &status, 0) == -1)
+            if (waitpid(py->getProcessID(), &status, 0) == -1)
             {
                 int waitpid_errno(errno);
                 ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
@@ -1888,12 +1644,12 @@ namespace tut
             "    else:\n"
             "        assert False, 'Too many data items'\n";
 
-        // Create a something.llsd file containing 'data' serialized to
+        // Create an llsdXXXXXX file containing 'data' serialized to
         // notation. It's important to separate with newlines because Python's
         // llsd module doesn't support parsing from a file stream, only from a
         // string, so we have to know how much of the file to read into a
         // string.
-        NamedTempFile file(".llsd",
+        NamedTempFile file("llsd",
                            // NamedTempFile's boost::function constructor
                            // takes a callable. To this callable it passes the
                            // std::ostream with which it's writing the
@@ -1926,7 +1682,7 @@ namespace tut
         // Create an empty data file. This is just a placeholder for our
         // script to write into. Create it to establish a unique name that
         // we know.
-        NamedTempFile file(".llsd", "");
+        NamedTempFile file("llsd", "");
 
         python("write Python notation",
                lambda::_1 <<
diff --git a/indra/llcommon/tests/llstreamqueue_test.cpp b/indra/llcommon/tests/llstreamqueue_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..050ad5c5bff287ddc7bdad0f7ed7f59997c9f123
--- /dev/null
+++ b/indra/llcommon/tests/llstreamqueue_test.cpp
@@ -0,0 +1,197 @@
+/**
+ * @file   llstreamqueue_test.cpp
+ * @author Nat Goodspeed
+ * @date   2012-01-05
+ * @brief  Test for llstreamqueue.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llstreamqueue.h"
+// STL headers
+#include <vector>
+// std headers
+// external library headers
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "stringize.h"
+
+/*****************************************************************************
+*   TUT
+*****************************************************************************/
+namespace tut
+{
+    struct llstreamqueue_data
+    {
+        llstreamqueue_data():
+            // we want a buffer with actual bytes in it, not an empty vector
+            buffer(10)
+        {}
+        // As LLStreamQueue is merely a typedef for
+        // LLGenericStreamQueue<char>, and no logic in LLGenericStreamQueue is
+        // specific to the <char> instantiation, we're comfortable for now
+        // testing only the narrow-char version.
+        LLStreamQueue strq;
+        // buffer for use in multiple tests
+        std::vector<char> buffer;
+    };
+    typedef test_group<llstreamqueue_data> llstreamqueue_group;
+    typedef llstreamqueue_group::object object;
+    llstreamqueue_group llstreamqueuegrp("llstreamqueue");
+
+    template<> template<>
+    void object::test<1>()
+    {
+        set_test_name("empty LLStreamQueue");
+        ensure_equals("brand-new LLStreamQueue isn't empty",
+                      strq.size(), 0);
+        ensure_equals("brand-new LLStreamQueue returns data",
+                      strq.asSource().read(&buffer[0], buffer.size()), 0);
+        strq.asSink().close();
+        ensure_equals("closed empty LLStreamQueue not at EOF",
+                      strq.asSource().read(&buffer[0], buffer.size()), -1);
+    }
+
+    template<> template<>
+    void object::test<2>()
+    {
+        set_test_name("one internal block, one buffer");
+        LLStreamQueue::Sink sink(strq.asSink());
+        ensure_equals("write(\"\")", sink.write("", 0), 0);
+        ensure_equals("0 write should leave LLStreamQueue empty (size())",
+                      strq.size(), 0);
+        ensure_equals("0 write should leave LLStreamQueue empty (peek())",
+                      strq.peek(&buffer[0], buffer.size()), 0);
+        // The meaning of "atomic" is that it must be smaller than our buffer.
+        std::string atomic("atomic");
+        ensure("test data exceeds buffer", atomic.length() < buffer.size());
+        ensure_equals(STRINGIZE("write(\"" << atomic << "\")"),
+                      sink.write(&atomic[0], atomic.length()), atomic.length());
+        ensure_equals("size() after write()", strq.size(), atomic.length());
+        size_t peeklen(strq.peek(&buffer[0], buffer.size()));
+        ensure_equals(STRINGIZE("peek(\"" << atomic << "\")"),
+                      peeklen, atomic.length());
+        ensure_equals(STRINGIZE("peek(\"" << atomic << "\") result"),
+                      std::string(buffer.begin(), buffer.begin() + peeklen), atomic);
+        ensure_equals("size() after peek()", strq.size(), atomic.length());
+        // peek() should not consume. Use a different buffer to prove it isn't
+        // just leftover data from the first peek().
+        std::vector<char> again(buffer.size());
+        peeklen = size_t(strq.peek(&again[0], again.size()));
+        ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again"),
+                      peeklen, atomic.length());
+        ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again result"),
+                      std::string(again.begin(), again.begin() + peeklen), atomic);
+        // now consume.
+        std::vector<char> third(buffer.size());
+        size_t readlen(strq.read(&third[0], third.size()));
+        ensure_equals(STRINGIZE("read(\"" << atomic << "\")"),
+                      readlen, atomic.length());
+        ensure_equals(STRINGIZE("read(\"" << atomic << "\") result"),
+                      std::string(third.begin(), third.begin() + readlen), atomic);
+        ensure_equals("peek() after read()", strq.peek(&buffer[0], buffer.size()), 0);
+        ensure_equals("size() after read()", strq.size(), 0);
+    }
+
+    template<> template<>
+    void object::test<3>()
+    {
+        set_test_name("basic skip()");
+        std::string lovecraft("lovecraft");
+        ensure("test data exceeds buffer", lovecraft.length() < buffer.size());
+        ensure_equals(STRINGIZE("write(\"" << lovecraft << "\")"),
+                      strq.write(&lovecraft[0], lovecraft.length()), lovecraft.length());
+        size_t peeklen(strq.peek(&buffer[0], buffer.size()));
+        ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\")"),
+                      peeklen, lovecraft.length());
+        ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\") result"),
+                      std::string(buffer.begin(), buffer.begin() + peeklen), lovecraft);
+        std::streamsize skip1(4);
+        ensure_equals(STRINGIZE("skip(" << skip1 << ")"), strq.skip(skip1), skip1);
+        ensure_equals("size() after skip()", strq.size(), lovecraft.length() - skip1);
+        size_t readlen(strq.read(&buffer[0], buffer.size()));
+        ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\")"),
+                      readlen, lovecraft.length() - skip1);
+        ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\") result"),
+                      std::string(buffer.begin(), buffer.begin() + readlen),
+                      lovecraft.substr(skip1));
+        ensure_equals("unconsumed", strq.read(&buffer[0], buffer.size()), 0);
+    }
+
+    template<> template<>
+    void object::test<4>()
+    {
+        set_test_name("skip() multiple blocks");
+        std::string blocks[] = { "books of ", "H.P. ", "Lovecraft" };
+        std::streamsize total(blocks[0].length() + blocks[1].length() + blocks[2].length());
+        std::streamsize leave(5);   // len("craft") above
+        std::streamsize skip(total - leave);
+        std::streamsize written(0);
+        BOOST_FOREACH(const std::string& block, blocks)
+        {
+            written += strq.write(&block[0], block.length());
+            ensure_equals("size() after write()", strq.size(), written);
+        }
+        std::streamsize skiplen(strq.skip(skip));
+        ensure_equals(STRINGIZE("skip(" << skip << ")"), skiplen, skip);
+        ensure_equals("size() after skip()", strq.size(), leave);
+        size_t readlen(strq.read(&buffer[0], buffer.size()));
+        ensure_equals("read(\"craft\")", readlen, leave);
+        ensure_equals("read(\"craft\") result",
+                      std::string(buffer.begin(), buffer.begin() + readlen), "craft");
+    }
+
+    template<> template<>
+    void object::test<5>()
+    {
+        set_test_name("concatenate blocks");
+        std::string blocks[] = { "abcd", "efghij", "klmnopqrs" };
+        BOOST_FOREACH(const std::string& block, blocks)
+        {
+            strq.write(&block[0], block.length());
+        }
+        std::vector<char> longbuffer(30);
+        std::streamsize readlen(strq.read(&longbuffer[0], longbuffer.size()));
+        ensure_equals("read() multiple blocks",
+                      readlen, blocks[0].length() + blocks[1].length() + blocks[2].length());
+        ensure_equals("read() multiple blocks result",
+                      std::string(longbuffer.begin(), longbuffer.begin() + readlen),
+                      blocks[0] + blocks[1] + blocks[2]);
+    }
+
+    template<> template<>
+    void object::test<6>()
+    {
+        set_test_name("split blocks");
+        std::string blocks[] = { "abcdefghijklm", "nopqrstuvwxyz" };
+        BOOST_FOREACH(const std::string& block, blocks)
+        {
+            strq.write(&block[0], block.length());
+        }
+        strq.close();
+        // We've already verified what strq.size() should be at this point;
+        // see above test named "skip() multiple blocks"
+        std::streamsize chksize(strq.size());
+        std::streamsize readlen(strq.read(&buffer[0], buffer.size()));
+        ensure_equals("read() 0", readlen, buffer.size());
+        ensure_equals("read() 0 result", std::string(buffer.begin(), buffer.end()), "abcdefghij");
+        chksize -= readlen;
+        ensure_equals("size() after read() 0", strq.size(), chksize);
+        readlen = strq.read(&buffer[0], buffer.size());
+        ensure_equals("read() 1", readlen, buffer.size());
+        ensure_equals("read() 1 result", std::string(buffer.begin(), buffer.end()), "klmnopqrst");
+        chksize -= readlen;
+        ensure_equals("size() after read() 1", strq.size(), chksize);
+        readlen = strq.read(&buffer[0], buffer.size());
+        ensure_equals("read() 2", readlen, chksize);
+        ensure_equals("read() 2 result",
+                      std::string(buffer.begin(), buffer.begin() + readlen), "uvwxyz");
+        ensure_equals("read() 3", strq.read(&buffer[0], buffer.size()), -1);
+    }
+} // namespace tut
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index 6a1cbf652a2ff9442db30eea5d317338a56e796d..93d3968dbfcfeb248dca90939cec82c233ef81da 100644
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -29,7 +29,11 @@
 #include "linden_common.h"
 #include "../test/lltut.h"
 
+#include <boost/assign/list_of.hpp>
 #include "../llstring.h"
+#include "StringVec.h"
+
+using boost::assign::list_of;
 
 namespace tut
 {
@@ -750,4 +754,118 @@ namespace tut
 		ensure("empty substr.", !LLStringUtil::endsWith(empty, value));
 		ensure("empty everything.", !LLStringUtil::endsWith(empty, empty));
 	}
+
+	template<> template<>
+	void string_index_object_t::test<41>()
+	{
+		set_test_name("getTokens(\"delims\")");
+		ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec());
+		ensure_equals("only delims",
+					  LLStringUtil::getTokens("   \r\n   ", " \r\n"), StringVec());
+		ensure_equals("sequence of delims",
+					  LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one"));
+		// nat considers this a dubious implementation side effect, but I'd
+		// hate to change it now...
+		ensure_equals("noncontiguous tokens",
+					  LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one"));
+		ensure_equals("space-padded tokens",
+					  LLStringUtil::getTokens(",    one  ,  two  ,", ","), list_of("one")("two"));
+		ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one"));
+	}
+
+	// Shorthand for verifying that getTokens() behaves the same when you
+	// don't pass a string of escape characters, when you pass an empty string
+	// (different overloads), and when you pass a string of characters that
+	// aren't actually present.
+	void ensure_getTokens(const std::string& desc,
+						  const std::string& string,
+						  const std::string& drop_delims,
+						  const std::string& keep_delims,
+						  const std::string& quotes,
+						  const std::vector<std::string>& expect)
+	{
+		ensure_equals(desc + " - no esc",
+					  LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes),
+					  expect);
+		ensure_equals(desc + " - empty esc",
+					  LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""),
+					  expect);
+		ensure_equals(desc + " - unused esc",
+					  LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"),
+					  expect);
+	}
+
+	void ensure_getTokens(const std::string& desc,
+						  const std::string& string,
+						  const std::string& drop_delims,
+						  const std::string& keep_delims,
+						  const std::vector<std::string>& expect)
+	{
+		ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect);
+	}
+
+	template<> template<>
+	void string_index_object_t::test<42>()
+	{
+		set_test_name("getTokens(\"delims\", etc.)");
+		// Signatures to test in this method:
+		// getTokens(string, drop_delims, keep_delims [, quotes [, escapes]])
+		// If you omit keep_delims, you get the older function (test above).
+
+		// cases like the getTokens(string, delims) tests above
+		ensure_getTokens("empty string", "", " ", "", StringVec());
+		ensure_getTokens("only delims",
+						 "   \r\n   ", " \r\n", "", StringVec());
+		ensure_getTokens("sequence of delims",
+						 ",,, one ,,,", ", ", "", list_of("one"));
+		// Note contrast with the case in the previous method
+		ensure_getTokens("noncontiguous tokens",
+						 ", ,, , one ,,,", ", ", "", list_of("one"));
+		ensure_getTokens("space-padded tokens",
+						 ",    one  ,  two  ,", ", ", "",
+                         list_of("one")("two"));
+		ensure_getTokens("no delims", "one", ",", "", list_of("one"));
+
+		// drop_delims vs. keep_delims
+		ensure_getTokens("arithmetic",
+						 " ab+def  / xx*  yy ", " ", "+-*/",
+						 list_of("ab")("+")("def")("/")("xx")("*")("yy"));
+
+		// quotes
+		ensure_getTokens("no quotes",
+						 "She said, \"Don't go.\"", " ", ",", "",
+						 list_of("She")("said")(",")("\"Don't")("go.\""));
+		ensure_getTokens("quotes",
+						 "She said, \"Don't go.\"", " ", ",", "\"",
+						 list_of("She")("said")(",")("Don't go."));
+		ensure_getTokens("quotes and delims",
+						 "run c:/'Documents and Settings'/someone", " ", "", "'",
+						 list_of("run")("c:/Documents and Settings/someone"));
+		ensure_getTokens("unmatched quote",
+						 "baby don't leave", " ", "", "'",
+						 list_of("baby")("don't")("leave"));
+		ensure_getTokens("adjacent quoted",
+						 "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'",
+						 list_of("abcdef \"ghijkl' mnopqr"));
+		ensure_getTokens("quoted empty string",
+						 "--set SomeVar ''", " ", "", "'",
+						 list_of("--set")("SomeVar")(""));
+
+		// escapes
+		// Don't use backslash as an escape for these tests -- you'll go nuts
+		// between the C++ string scanner and getTokens() escapes. Test with
+		// something else!
+		ensure_equals("escaped delims",
+					  LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"),
+					  list_of(" a")("-")("dog-gone phrase"));
+		ensure_equals("escaped quotes",
+					  LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"),
+					  list_of("say:")("this isn't working."));
+		ensure_equals("escaped escape",
+					  LLStringUtil::getTokens("want x^^2", " ", "", "", "^"),
+					  list_of("want")("x^2"));
+		ensure_equals("escape at end",
+					  LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"),
+					  list_of("it's up")("there^"));
+    }
 }
diff --git a/indra/llcommon/tests/setpython.py b/indra/llcommon/tests/setpython.py
deleted file mode 100644
index df7b90428e929614b0d45986e875801bf5d0e1f4..0000000000000000000000000000000000000000
--- a/indra/llcommon/tests/setpython.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/python
-"""\
-@file   setpython.py
-@author Nat Goodspeed
-@date   2011-07-13
-@brief  Set PYTHON environment variable for tests that care.
-
-$LicenseInfo:firstyear=2011&license=viewerlgpl$
-Copyright (c) 2011, Linden Research, Inc.
-$/LicenseInfo$
-"""
-
-import os
-import sys
-import subprocess
-
-if __name__ == "__main__":
-    os.environ["PYTHON"] = sys.executable
-    sys.exit(subprocess.call(sys.argv[1:]))
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index ffda84729bd3c6cf1c453001e6cc2a96602e5def..a4d3a4e0260e30b116edaed314d5ddc2e19a57d1 100644
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -29,7 +29,22 @@
 #if ! defined(LL_WRAPLLERRS_H)
 #define LL_WRAPLLERRS_H
 
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+#include <tut/tut.hpp>
 #include "llerrorcontrol.h"
+#include "stringize.h"
+#include <boost/bind.hpp>
+#include <boost/noncopyable.hpp>
+#include <list>
+#include <string>
+#include <stdexcept>
+
+// statically reference the function in test.cpp... it's short, we could
+// replicate, but better to reuse
+extern void wouldHaveCrashed(const std::string& message);
 
 struct WrapLL_ERRS
 {
@@ -70,4 +85,118 @@ struct WrapLL_ERRS
     LLError::FatalFunction mPriorFatal;
 };
 
+/**
+ * LLError::addRecorder() accepts ownership of the passed Recorder* -- it
+ * expects to be able to delete it later. CaptureLog isa Recorder whose
+ * pointer we want to be able to pass without any ownership implications.
+ * For such cases, instantiate a new RecorderProxy(yourRecorder) and pass
+ * that. Your heap RecorderProxy might later be deleted, but not yourRecorder.
+ */
+class RecorderProxy: public LLError::Recorder
+{
+public:
+    RecorderProxy(LLError::Recorder* recorder):
+        mRecorder(recorder)
+    {}
+
+    virtual void recordMessage(LLError::ELevel level, const std::string& message)
+    {
+        mRecorder->recordMessage(level, message);
+    }
+
+    virtual bool wantsTime()
+    {
+        return mRecorder->wantsTime();
+    }
+
+private:
+    LLError::Recorder* mRecorder;
+};
+
+/**
+ * Capture log messages. This is adapted (simplified) from the one in
+ * llerror_test.cpp.
+ */
+class CaptureLog : public LLError::Recorder, public boost::noncopyable
+{
+public:
+    CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG):
+        // Mostly what we're trying to accomplish by saving and resetting
+        // LLError::Settings is to bypass the default RecordToStderr and
+        // RecordToWinDebug Recorders. As these are visible only inside
+        // llerror.cpp, we can't just call LLError::removeRecorder() with
+        // each. For certain tests we need to produce, capture and examine
+        // DEBUG log messages -- but we don't want to spam the user's console
+        // with that output. If it turns out that saveAndResetSettings() has
+        // some bad effect, give up and just let the DEBUG level log messages
+        // display.
+        mOldSettings(LLError::saveAndResetSettings()),
+        mProxy(new RecorderProxy(this))
+    {
+        LLError::setFatalFunction(wouldHaveCrashed);
+        LLError::setDefaultLevel(level);
+        LLError::addRecorder(mProxy);
+    }
+
+    ~CaptureLog()
+    {
+        LLError::removeRecorder(mProxy);
+        delete mProxy;
+        LLError::restoreSettings(mOldSettings);
+    }
+
+    void recordMessage(LLError::ELevel level,
+                       const std::string& message)
+    {
+        mMessages.push_back(message);
+    }
+
+    /// Don't assume the message we want is necessarily the LAST log message
+    /// emitted by the underlying code; search backwards through all messages
+    /// for the sought string.
+    std::string messageWith(const std::string& search, bool required=true)
+    {
+        for (MessageList::const_reverse_iterator rmi(mMessages.rbegin()), rmend(mMessages.rend());
+             rmi != rmend; ++rmi)
+        {
+            if (rmi->find(search) != std::string::npos)
+                return *rmi;
+        }
+        // failed to find any such message
+        if (! required)
+            return std::string();
+
+        throw tut::failure(STRINGIZE("failed to find '" << search
+                                     << "' in captured log messages:\n"
+                                     << boost::ref(*this)));
+    }
+
+    std::ostream& streamto(std::ostream& out) const
+    {
+        MessageList::const_iterator mi(mMessages.begin()), mend(mMessages.end());
+        if (mi != mend)
+        {
+            // handle first message separately: it doesn't get a newline
+            out << *mi++;
+            for ( ; mi != mend; ++mi)
+            {
+                // every subsequent message gets a newline
+                out << '\n' << *mi;
+            }
+        }
+        return out;
+    }
+
+    typedef std::list<std::string> MessageList;
+    MessageList mMessages;
+    LLError::Settings* mOldSettings;
+    LLError::Recorder* mProxy;
+};
+
+inline
+std::ostream& operator<<(std::ostream& out, const CaptureLog& log)
+{
+    return log.streamto(out);
+}
+
 #endif /* ! defined(LL_WRAPLLERRS_H) */
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 56e01ac851e7a067bf740f3bb0a29440e003691a..6775b005f4f179e8beb94b57fd876ca6a81c26f7 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -48,11 +48,15 @@
 //static
 std::string LLImage::sLastErrorMessage;
 LLMutex* LLImage::sMutex = NULL;
+bool LLImage::sUseNewByteRange = false;
+S32  LLImage::sMinimalReverseByteRangePercent = 75;
 LLPrivateMemoryPool* LLImageBase::sPrivatePoolp = NULL ;
 
 //static
-void LLImage::initClass()
+void LLImage::initClass(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent)
 {
+	sUseNewByteRange = use_new_byte_range;
+    sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent;
 	sMutex = new LLMutex(NULL);
 
 	LLImageBase::createPrivatePool() ;
@@ -1334,7 +1338,8 @@ LLImageFormatted::LLImageFormatted(S8 codec)
 	  mCodec(codec),
 	  mDecoding(0),
 	  mDecoded(0),
-	  mDiscardLevel(-1)
+	  mDiscardLevel(-1),
+	  mLevels(0)
 {
 	mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
 }
@@ -1561,7 +1566,7 @@ void LLImageFormatted::appendData(U8 *data, S32 size)
 
 //----------------------------------------------------------------------------
 
-BOOL LLImageFormatted::load(const std::string &filename)
+BOOL LLImageFormatted::load(const std::string &filename, int load_size)
 {
 	resetLastError();
 
@@ -1580,14 +1585,19 @@ BOOL LLImageFormatted::load(const std::string &filename)
 		return FALSE;
 	}
 
+	// Constrain the load size to acceptable values
+	if ((load_size == 0) || (load_size > file_size))
+	{
+		load_size = file_size;
+	}
 	BOOL res;
-	U8 *data = allocateData(file_size);
-	apr_size_t bytes_read = file_size;
+	U8 *data = allocateData(load_size);
+	apr_size_t bytes_read = load_size;
 	apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
-	if (s != APR_SUCCESS || (S32) bytes_read != file_size)
+	if (s != APR_SUCCESS || (S32) bytes_read != load_size)
 	{
 		deleteData();
-		setLastError("Unable to read entire file",filename);
+		setLastError("Unable to read file",filename);
 		res = FALSE;
 	}
 	else
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 4469c9e86025e628d2b1100c6d8c85baaa604c93..46e6d1a901315fb622382dd20f0461c5a1116516 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -48,6 +48,8 @@ const S32 MAX_PRECINCT_SIZE = 2048;			// No reason to be bigger than MAX_IMAGE_S
 const S32 MIN_PRECINCT_SIZE = 4;			// Can't be smaller than MIN_BLOCK_SIZE
 const S32 MAX_BLOCK_SIZE = 64;				// Max total block size is 4096, hence 64x64 when using square blocks
 const S32 MIN_BLOCK_SIZE = 4;				// Min block dim is 4 according to jpeg2000 spec
+const S32 MIN_LAYER_SIZE = 2000;			// Size of the first quality layer (after header). Must be > to FIRST_PACKET_SIZE!!
+const S32 MAX_NB_LAYERS = 64;				// Max number of layers we'll entertain in SL (practical limit)
 
 const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2
 const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 2048
@@ -60,6 +62,7 @@ const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS; //2048 *
 // *TODO: change both to 1024 when SIM texture fetching is deprecated
 const S32 FIRST_PACKET_SIZE = 600;
 const S32 MAX_IMG_PACKET_SIZE = 1000;
+const S32 HTTP_PACKET_SIZE = 1496;
 
 // Base classes for images.
 // There are two major parts for the image:
@@ -89,15 +92,20 @@ typedef enum e_image_codec
 class LLImage
 {
 public:
-	static void initClass();
+	static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75);
 	static void cleanupClass();
 
 	static const std::string& getLastError();
 	static void setLastError(const std::string& message);
 	
+	static bool useNewByteRange() { return sUseNewByteRange; }
+    static S32  getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; }
+	
 protected:
 	static LLMutex* sMutex;
 	static std::string sLastErrorMessage;
+	static bool sUseNewByteRange;
+    static S32  sMinimalReverseByteRangePercent;
 };
 
 //============================================================================
@@ -294,7 +302,7 @@ public:
 	// getRawDiscardLevel() by default returns mDiscardLevel, but may be overridden (LLImageJ2C)
 	virtual S8  getRawDiscardLevel() { return mDiscardLevel; }
 	
-	BOOL load(const std::string& filename);
+	BOOL load(const std::string& filename, int load_size = 0);
 	BOOL save(const std::string& filename);
 
 	virtual BOOL updateData() = 0; // pure virtual
@@ -313,6 +321,8 @@ public:
 	BOOL isDecoded()  const { return mDecoded ? TRUE : FALSE; }
 	void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
 	S8 getDiscardLevel() const { return mDiscardLevel; }
+	S8 getLevels() const { return mLevels; }
+	void setLevels(S8 nlevels) { mLevels = nlevels; }
 
 	// setLastError needs to be deferred for J2C images since it may be called from a DLL
 	virtual void resetLastError();
@@ -325,7 +335,8 @@ protected:
 	S8 mCodec;
 	S8 mDecoding;
 	S8 mDecoded;  // unused, but changing LLImage layout requires recompiling static Mac/Linux libs. 2009-01-30 JC
-	S8 mDiscardLevel;
+	S8 mDiscardLevel;	// Current resolution level worked on. 0 = full res, 1 = half res, 2 = quarter res, etc...
+	S8 mLevels;			// Number of resolution levels in that image. Min is 1. 0 means unknown.
 	
 public:
 	static S32 sGlobalFormattedMemory;
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 8241746a74561b514bbe762ba9331a7b1ab6e37c..452aad25cb9f462d417e2f7cb8427a45e40a2adc 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -56,7 +56,7 @@ std::string LLImageJ2C::getEngineInfo()
 LLImageJ2C::LLImageJ2C() : 	LLImageFormatted(IMG_CODEC_J2C),
 							mMaxBytes(0),
 							mRawDiscardLevel(-1),
-							mRate(0.0f),
+							mRate(DEFAULT_COMPRESSION_RATE),
 							mReversible(FALSE),
 							mAreaUsedForDataSizeCalcs(0)
 {
@@ -142,6 +142,7 @@ BOOL LLImageJ2C::updateData()
 
 BOOL LLImageJ2C::initDecode(LLImageRaw &raw_image, int discard_level, int* region)
 {
+	setDiscardLevel(discard_level != -1 ? discard_level : 0);
 	return mImpl->initDecode(*this,raw_image,discard_level,region);
 }
 
@@ -261,19 +262,34 @@ S32 LLImageJ2C::calcHeaderSizeJ2C()
 //static
 S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
 {
-	// Note: this only provides an *estimate* of the size in bytes of an image level
-	// *TODO: find a way to read the true size (when available) and convey the fact
-	// that the result is an estimate in the other cases
-	if (rate <= 0.f) rate = .125f;
-	while (discard_level > 0)
+	// Note: This provides an estimation for the first to last quality layer of a given discard level
+	// This is however an efficient approximation, as the true discard level boundary would be
+	// in general too big for fast fetching.
+	// For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
+
+	// Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl().
+	S32 nb_layers = 1;
+	S32 surface = w*h;
+	S32 s = 64*64;
+	while (surface > s)
 	{
-		if (w < 1 || h < 1)
-			break;
-		w >>= 1;
-		h >>= 1;
-		discard_level--;
+		nb_layers++;
+		s *= 4;
 	}
-	S32 bytes = (S32)((F32)(w*h*comp)*rate);
+	F32 layer_factor =  3.0f * (7 - llclamp(nb_layers,1,6));
+	
+	// Compute w/pow(2,discard_level) and h/pow(2,discard_level)
+	w >>= discard_level;
+	h >>= discard_level;
+	w = llmax(w, 1);
+	h = llmax(h, 1);
+
+	// Temporary: compute both new and old range and pick one according to the settings TextureNewByteRange 
+	// *TODO: Take the old code out once we have enough tests done
+	S32 bytes;
+	S32 new_bytes = (S32) (sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor);
+	S32 old_bytes = (S32)((F32)(w*h*comp)*rate);
+	bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes);
 	bytes = llmax(bytes, calcHeaderSizeJ2C());
 	return bytes;
 }
@@ -283,15 +299,12 @@ S32 LLImageJ2C::calcHeaderSize()
 	return calcHeaderSizeJ2C();
 }
 
-
-// calcDataSize() returns how many bytes to read 
-// to load discard_level (including header and higher discard levels)
+// calcDataSize() returns how many bytes to read to load discard_level (including header)
 S32 LLImageJ2C::calcDataSize(S32 discard_level)
 {
 	discard_level = llclamp(discard_level, 0, MAX_DISCARD_LEVEL);
-
 	if ( mAreaUsedForDataSizeCalcs != (getHeight() * getWidth()) 
-		|| mDataSizes[0] == 0)
+		|| (mDataSizes[0] == 0))
 	{
 		mAreaUsedForDataSizeCalcs = getHeight() * getWidth();
 		
@@ -301,25 +314,6 @@ S32 LLImageJ2C::calcDataSize(S32 discard_level)
 			mDataSizes[level] = calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
 			level--;
 		}
-
-		/* This is technically a more correct way to calculate the size required
-		   for each discard level, since they should include the size needed for
-		   lower levels.   Unfortunately, this doesn't work well and will lead to 
-		   download stalls.  The true correct way is to parse the header.  This will
-		   all go away with http textures at some point.
-
-		// Calculate the size for each discard level.   Lower levels (higher quality)
-		// contain the cumulative size of higher levels		
-		S32 total_size = calcHeaderSizeJ2C();
-
-		S32 level = MAX_DISCARD_LEVEL;	// Start at the highest discard
-		while ( level >= 0 )
-		{	// Add in this discard level and all before it
-			total_size += calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), level, mRate);
-			mDataSizes[level] = total_size;
-			level--;
-		}
-		*/
 	}
 	return mDataSizes[discard_level];
 }
@@ -334,8 +328,9 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
 	}
 	while (1)
 	{
-		S32 bytes_needed = calcDataSize(discard_level); // virtual
-		if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes
+		S32 bytes_needed = calcDataSize(discard_level);
+		// Use TextureReverseByteRange percent (see settings.xml) of the optimal size to qualify as correct rendering for the given discard level
+		if (bytes >= (bytes_needed*LLImage::getReverseByteRangePercent()/100))
 		{
 			break;
 		}
@@ -348,11 +343,6 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
 	return discard_level;
 }
 
-void LLImageJ2C::setRate(F32 rate)
-{
-	mRate = rate;
-}
-
 void LLImageJ2C::setMaxBytes(S32 max_bytes)
 {
 	mMaxBytes = max_bytes;
@@ -474,6 +464,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester
 	addMetric("Perf Compression (kB/s)");
 
 	mRunBytesInDecompression = 0;
+	mRunBytesOutDecompression = 0;
 	mRunBytesInCompression = 0;
 
 	mTotalBytesInDecompression = 0;
@@ -483,6 +474,7 @@ LLImageCompressionTester::LLImageCompressionTester() : LLMetricPerformanceTester
 
 	mTotalTimeDecompression = 0.0f;
 	mTotalTimeCompression = 0.0f;
+	mRunTimeDecompression = 0.0f;
 }
 
 LLImageCompressionTester::~LLImageCompressionTester()
@@ -565,12 +557,17 @@ void LLImageCompressionTester::updateDecompressionStats(const S32 bytesIn, const
 	mTotalBytesInDecompression += bytesIn;
 	mRunBytesInDecompression += bytesIn;
 	mTotalBytesOutDecompression += bytesOut;
-	if (mRunBytesInDecompression > (1000000))
+	mRunBytesOutDecompression += bytesOut;
+	//if (mRunBytesInDecompression > (1000000))
+	if (mRunBytesOutDecompression > (10000000))
+	//if ((mTotalTimeDecompression - mRunTimeDecompression) >= (5.0f))
 	{
 		// Output everything
 		outputTestResults();
 		// Reset the decompression data of the run
 		mRunBytesInDecompression = 0;
+		mRunBytesOutDecompression = 0;
+		mRunTimeDecompression = mTotalTimeDecompression;
 	}
 }
 
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index 914174fc57fd092ddfdd2e436e79ebdebd87b4fa..ce8195940d7c43683bad9380c1b40f903a1aff73 100644
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -31,6 +31,9 @@
 #include "llassettype.h"
 #include "llmetricperformancetester.h"
 
+// JPEG2000 : compression rate used in j2c conversion.
+const F32 DEFAULT_COMPRESSION_RATE = 1.f/8.f;
+
 class LLImageJ2CImpl;
 class LLImageCompressionTester ;
 
@@ -67,12 +70,11 @@ public:
 
 	// Encode accessors
 	void setReversible(const BOOL reversible); // Use non-lossy?
-	void setRate(F32 rate);
 	void setMaxBytes(S32 max_bytes);
 	S32 getMaxBytes() const { return mMaxBytes; }
 
 	static S32 calcHeaderSizeJ2C();
-	static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = 0.f);
+	static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = DEFAULT_COMPRESSION_RATE);
 
 	static std::string getEngineInfo();
 
@@ -154,13 +156,15 @@ class LLImageCompressionTester : public LLMetricPerformanceTesterBasic
         U32 mTotalBytesOutDecompression;    // Total bytes produced by decompressor
         U32 mTotalBytesInCompression;       // Total bytes fed to compressor
         U32 mTotalBytesOutCompression;      // Total bytes produced by compressor
-		U32 mRunBytesInDecompression;		// Bytes fed to decompressor in this run
+        U32 mRunBytesInDecompression;		// Bytes fed to decompressor in this run
+        U32 mRunBytesOutDecompression;		// Bytes produced by the decompressor in this run
 		U32 mRunBytesInCompression;			// Bytes fed to compressor in this run
         //
         // Time
         //
         F32 mTotalTimeDecompression;        // Total time spent in computing decompression
         F32 mTotalTimeCompression;          // Total time spent in computing compression
+        F32 mRunTimeDecompression;          // Time in this run (we output every 5 sec in decompress)
     };
 
 #endif
diff --git a/indra/llimage/llimagepng.cpp b/indra/llimage/llimagepng.cpp
index 8d493ecde0b6a9937a9624f6aa44efd1bf220811..294f68b1220cf0ad761a8f4daad227147007c9e0 100644
--- a/indra/llimage/llimagepng.cpp
+++ b/indra/llimage/llimagepng.cpp
@@ -60,6 +60,12 @@ BOOL LLImagePNG::updateData()
 
 	// Decode the PNG data and extract sizing information
 	LLPngWrapper pngWrapper;
+	if (!pngWrapper.isValidPng(getData()))
+	{
+		setLastError("LLImagePNG data does not have a valid PNG header!");
+		return FALSE;
+	}
+
 	LLPngWrapper::ImageInfo infop;
 	if (! pngWrapper.readPng(getData(), NULL, &infop))
 	{
@@ -90,6 +96,12 @@ BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time)
 
 	// Decode the PNG data into the raw image
 	LLPngWrapper pngWrapper;
+	if (!pngWrapper.isValidPng(getData()))
+	{
+		setLastError("LLImagePNG data does not have a valid PNG header!");
+		return FALSE;
+	}
+
 	if (! pngWrapper.readPng(getData(), raw_image))
 	{
 		setLastError(pngWrapper.getErrorMessage());
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index c156ed0cef40411d153e9d825b27ff1e26a04b59..cf88de12b49ab8ff05512ccc98cd31fb592536dc 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -32,6 +32,7 @@
 #include "llmath.h"
 #include "llkdumem.h"
 
+#include "kdu_block_coding.h"
 
 class kdc_flow_control {
 	
@@ -244,7 +245,9 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod
 	mCodeStreamp->create(mInputp);
 
 	// Set the maximum number of bytes to use from the codestream
-	mCodeStreamp->set_max_bytes(max_bytes);
+	// *TODO: This seems to be wrong. The base class should have no idea of how j2c compression works so no
+	// good way of computing what's the byte range to be used.
+	mCodeStreamp->set_max_bytes(max_bytes,true);
 
 	//	If you want to flip or rotate the image for some reason, change
 	// the resolution, or identify a restricted region of interest, this is
@@ -291,8 +294,13 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECod
 		}
 	}
 
+	// Get the number of resolution levels in that image
+	mLevels = mCodeStreamp->get_min_dwt_levels();
+	
+	// Set the base dimensions
 	base.setSize(dims.size.x, dims.size.y, components);
-
+	base.setLevels(mLevels);
+	
 	if (!keep_codestream)
 	{
 		mCodeStreamp->destroy();
@@ -351,7 +359,8 @@ BOOL LLImageJ2CKDU::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int bloc
 	mLevels = levels;
 	if (mLevels != 0)
 	{
-		mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MIN_DECOMPOSITION_LEVELS);		
+		mLevels = llclamp(mLevels,MIN_DECOMPOSITION_LEVELS,MAX_DECOMPOSITION_LEVELS);
+		base.setLevels(mLevels);
 	}
 	return TRUE;
 }
@@ -364,6 +373,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 	// To regain control, we throw an exception, and catch it here.
 	try
 	{
+		// Merov : Test!! DO NOT COMMIT!!
+		//findDiscardLevelsBoundaries(base);
+
 		base.updateRawDiscardLevel();
 		setupCodeStream(base, TRUE, mode);
 
@@ -381,7 +393,7 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 			region_kdu->size.y = region[3] - region[1];
 		}
 		int discard = (discard_level != -1 ? discard_level : base.getRawDiscardLevel());
-		
+		//llinfos << "Merov debug : initDecode, discard used = " << discard << ", asked = " << discard_level << llendl;
 		// Apply loading restrictions
 		mCodeStreamp->apply_input_restrictions( first_channel, max_channel_count, discard, 0, region_kdu);
 		
@@ -394,12 +406,9 @@ BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
 
 		// Resize raw_image according to the image to be decoded
 		kdu_dims dims; mCodeStreamp->get_dims(0,dims);
-		// *TODO: Use the real number of levels read from the file throughout the code instead of relying on an infered value from dimensions
-		//S32 levels = mCodeStreamp->get_min_dwt_levels();
 		S32 channels = base.getComponents() - first_channel;
 		channels = llmin(channels,max_channel_count);
 		raw_image.resize(dims.size.x, dims.size.y, channels);
-		//llinfos << "j2c image dimension: width = " << dims.size.x << ", height = " << dims.size.y << ", channels = " << channels << ", levels = " << levels << llendl;
 
 		if (!mTileIndicesp)
 		{
@@ -583,12 +592,6 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 			comment.put_text(comment_text);
 		}
 
-		// Set codestream options
-		int num_layer_specs = 0;
-
-		kdu_long layer_bytes[64];
-		U32 max_bytes = 0;
-
 		if (num_components >= 3)
 		{
 			// Note that we always use YCC and not YUV
@@ -596,66 +599,51 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 			set_default_colour_weights(codestream.access_siz());
 		}
 
-		if (reversible)
+		// Set codestream options
+		int nb_layers = 0;
+		kdu_long layer_bytes[MAX_NB_LAYERS];
+		U32 max_bytes = (U32)(base.getWidth() * base.getHeight() * base.getComponents());
+
+		// Rate is the argument passed into the LLImageJ2C which specifies the target compression rate. The default is 8:1.
+		// *TODO: mRate is actually always 8:1 in the viewer. Test different values.
+		llassert (base.mRate > 0.f);
+		max_bytes = (U32)((F32)(max_bytes) * base.mRate);
+		
+		// This code is where we specify the target number of bytes for each quality layer.
+		// We're using a logarithmic spacing rule that fits with our way of fetching texture data.
+		// Note: For more info on this layers business, read kdu_codestream::flush() doc in kdu_compressed.h
+		layer_bytes[nb_layers++] = FIRST_PACKET_SIZE;
+		U32 i = MIN_LAYER_SIZE;
+		while ((i < max_bytes) && (nb_layers < (MAX_NB_LAYERS-1)))
 		{
-			codestream.access_siz()->parse_string("Creversible=yes");
-			// *TODO: we should use yuv in reversible mode and one level since those images are small. 
-			// Don't turn this on now though as both create problems on decoding for the moment
-			//codestream.access_siz()->parse_string("Clevels=1");
-			//codestream.access_siz()->parse_string("Cycc=no");
-			// If we're doing reversible (i.e. lossless compression), assumes we're not using quality layers.
-			// *TODO: this is incorrect and unecessary. Try using the regular layer setting.
-			codestream.access_siz()->parse_string("Clayers=1");
-			num_layer_specs = 1;
-			layer_bytes[0] = 0;
+			layer_bytes[nb_layers++] = i;
+			i *= 4;
 		}
-		else
+		// Note: for small images, we can have (max_bytes < FIRST_PACKET_SIZE), hence the test
+		if (layer_bytes[nb_layers-1] < max_bytes)
 		{
-			// Rate is the argument passed into the LLImageJ2C which
-			// specifies the target compression rate.  The default is 8:1.
-			// Possibly if max_bytes < 500, we should just use the default setting?
-			// *TODO: mRate is actually always 8:1 in the viewer. Test different values. Also force to reversible for small (< 500 bytes) textures.
-			if (base.mRate != 0.f)
-			{
-				max_bytes = (U32)(base.mRate*base.getWidth()*base.getHeight()*base.getComponents());
-			}
-			else
-			{
-				max_bytes = (U32)(base.getWidth()*base.getHeight()*base.getComponents()*0.125);
-			}
-
-			const U32 min_bytes = FIRST_PACKET_SIZE;
-			if (max_bytes > min_bytes)
-			{
-				U32 i;
-				// This code is where we specify the target number of bytes for
-				// each layer.  Not sure if we should do this for small images
-				// or not.  The goal is to have this roughly align with
-				// different quality levels that we decode at.
-				for (i = min_bytes; i < max_bytes; i*=4)
-				{
-					if (i == min_bytes * 4)
-					{
-						i = 2000;
-					}
-					layer_bytes[num_layer_specs] = i;
-					num_layer_specs++;
-				}
-				layer_bytes[num_layer_specs] = max_bytes;
-				num_layer_specs++;
+			// Set the last quality layer so to fit the preset compression ratio
+			layer_bytes[nb_layers++] = max_bytes;
+		}
 
-				std::string layer_string = llformat("Clayers=%d",num_layer_specs);
-				codestream.access_siz()->parse_string(layer_string.c_str());
-			}
-			else
+		if (reversible)
+		{
+			// Use 0 for a last quality layer for reversible images so all remaining code blocks will be flushed
+			// Hack: KDU encoding for reversible images has a bug for small images that leads to j2c images that 
+			// cannot be open or are very blurry. Avoiding that last layer prevents the problem to happen.
+			if ((base.getWidth() >= 32) || (base.getHeight() >= 32))
 			{
-				layer_bytes[0] = min_bytes;
-				num_layer_specs = 1;
-				std::string layer_string = llformat("Clayers=%d",num_layer_specs);
-				codestream.access_siz()->parse_string(layer_string.c_str());
+				layer_bytes[nb_layers++] = 0;
 			}
+			codestream.access_siz()->parse_string("Creversible=yes");
+			// *TODO: we should use yuv in reversible mode
+			// Don't turn this on now though as it creates problems on decoding for the moment
+			//codestream.access_siz()->parse_string("Cycc=no");
 		}
 		
+		std::string layer_string = llformat("Clayers=%d",nb_layers);
+		codestream.access_siz()->parse_string(layer_string.c_str());
+		
 		// Set up data ordering, markers, etc... if precincts or blocks specified
 		if ((mBlocksSize != -1) || (mPrecinctsSize != -1))
 		{
@@ -669,23 +657,26 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 				std::string blocks_string = llformat("Cblk={%d,%d}",mBlocksSize,mBlocksSize);
 				codestream.access_siz()->parse_string(blocks_string.c_str());
 			}
-			std::string ordering_string = llformat("Corder=RPCL");
+			std::string ordering_string = llformat("Corder=LRCP");
 			codestream.access_siz()->parse_string(ordering_string.c_str());
 			std::string PLT_string = llformat("ORGgen_plt=yes");
 			codestream.access_siz()->parse_string(PLT_string.c_str());
 			std::string Parts_string = llformat("ORGtparts=R");
 			codestream.access_siz()->parse_string(Parts_string.c_str());
 		}
+		
+		// Set the number of wavelets subresolutions (aka levels) 
 		if (mLevels != 0)
 		{
 			std::string levels_string = llformat("Clevels=%d",mLevels);
 			codestream.access_siz()->parse_string(levels_string.c_str());
 		}
 		
+		// Complete the encode settings
 		codestream.access_siz()->finalize_all();
 		codestream.change_appearance(transpose,vflip,hflip);
 
-		// Now we are ready for sample data processing.
+		// Now we are ready for sample data processing
 		kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream);
 		bool done = false;
 		while (!done)
@@ -702,7 +693,7 @@ BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
 		}
 
 		// Produce the compressed output
-		codestream.flush(layer_bytes,num_layer_specs);
+		codestream.flush(layer_bytes,nb_layers);
 
 		// Cleanup
 		delete tile;
@@ -750,6 +741,207 @@ BOOL LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
 	}
 }
 
+/*****************************************************************************/
+/* STATIC                        copy_block                                  */
+/*****************************************************************************/
+
+static void copy_block(kdu_block *in, kdu_block *out)
+{
+	if (in->K_max_prime != out->K_max_prime)
+    { 
+		std::cout << "Cannot copy blocks belonging to subbands with different quantization parameters." << std::endl; 
+		return;
+	}
+	if ((in->size.x != out->size.x) || (in->size.y != out->size.y))  
+    { 
+		std::cout << "Cannot copy code-blocks with different dimensions." << std::endl; 
+		return;
+	}
+	out->missing_msbs = in->missing_msbs;
+	if (out->max_passes < (in->num_passes+2))        // Gives us enough to round up
+		out->set_max_passes(in->num_passes+2,false); // to the next whole bit-plane
+	out->num_passes = in->num_passes;
+	int num_bytes = 0;
+	for (int z=0; z < in->num_passes; z++)
+    {
+		num_bytes += (out->pass_lengths[z] = in->pass_lengths[z]);
+		out->pass_slopes[z] = in->pass_slopes[z];
+    }
+	
+    // Just copy compressed code-bytes. Block transcoding not supported.
+	if (out->max_bytes < num_bytes)
+		out->set_max_bytes(num_bytes,false);
+	memcpy(out->byte_buffer,in->byte_buffer,(size_t) num_bytes);
+}
+
+/*****************************************************************************/
+/* STATIC                        copy_tile                                   */
+/*****************************************************************************/
+
+static void
+copy_tile(kdu_tile tile_in, kdu_tile tile_out, int tnum_in, int tnum_out,
+		  kdu_params *siz_in, kdu_params *siz_out, int skip_components,
+		  int &num_blocks)
+{
+	int num_components = tile_out.get_num_components();
+	int new_tpart=0, next_tpart = 1;
+	
+	for (int c=0; c < num_components; c++)
+    {
+		kdu_tile_comp comp_in, comp_out;
+		comp_in = tile_in.access_component(c);
+		comp_out = tile_out.access_component(c);
+		int num_resolutions = comp_out.get_num_resolutions();
+		//std::cout << "    Copying tile : num_resolutions = " << num_resolutions << std::endl;
+		for (int r=0; r < num_resolutions; r++)
+        {
+			kdu_resolution res_in;  res_in = comp_in.access_resolution(r);
+			kdu_resolution res_out; res_out = comp_out.access_resolution(r);
+			int b, min_band;
+			int num_bands = res_in.get_valid_band_indices(min_band);
+			std::cout << "        Copying tile : num_bands = " << num_bands << std::endl;
+			for (b=min_band; num_bands > 0; num_bands--, b++)
+            {
+				kdu_subband band_in;  band_in = res_in.access_subband(b);
+				kdu_subband band_out; band_out = res_out.access_subband(b);
+				kdu_dims blocks_in;  band_in.get_valid_blocks(blocks_in);
+				kdu_dims blocks_out; band_out.get_valid_blocks(blocks_out);
+				if ((blocks_in.size.x != blocks_out.size.x) ||
+					(blocks_in.size.y != blocks_out.size.y))
+                { 
+					std::cout << "Transcoding operation cannot proceed: Code-block partitions for the input and output code-streams do not agree." << std::endl;
+					return;
+				}
+				kdu_coords idx;
+				//std::cout << "            Copying tile : block indices, x = " << blocks_out.size.x << " and y = " << blocks_out.size.y << std::endl;
+				for (idx.y=0; idx.y < blocks_out.size.y; idx.y++)
+				{
+					for (idx.x=0; idx.x < blocks_out.size.x; idx.x++)
+					{
+						kdu_block *in =
+						band_in.open_block(idx+blocks_in.pos,&new_tpart);
+						for (; next_tpart <= new_tpart; next_tpart++)
+							siz_out->copy_from(siz_in,tnum_in,tnum_out,next_tpart,
+											   skip_components);
+						kdu_block *out = band_out.open_block(idx+blocks_out.pos);
+						copy_block(in,out);
+						band_in.close_block(in);
+						band_out.close_block(out);
+						num_blocks++;
+					}
+				}
+            }
+        }
+    }
+}
+
+// Find the block boundary for each discard level in the input image.
+// We parse the input blocks and copy them in a temporary output stream.
+// For the moment, we do nothing more that parsing the raw list of blocks and outputing result.
+void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base)
+{
+	// We need the number of levels in that image before starting.
+	getMetadata(base);
+	
+	for (int discard_level = 0; discard_level < mLevels; discard_level++)
+	{
+		//std::cout << "Parsing discard level = " << discard_level << std::endl;
+		// Create the input codestream object.
+		setupCodeStream(base, TRUE, MODE_FAST);
+		mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL);
+		mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true);
+		siz_params *siz_in = mCodeStreamp->access_siz();
+	
+		// Create the output codestream object.
+		siz_params siz;
+		siz.copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
+		siz.set(Scomponents,0,0,mCodeStreamp->get_num_components());
+	
+		U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents();
+		max_output_size = (max_output_size < 1000 ? 1000 : max_output_size);
+		U8 *output_buffer = new U8[max_output_size];
+		U32 output_size = 0; // Address updated by LLKDUMemTarget to give the final compressed buffer size
+		LLKDUMemTarget output(output_buffer, output_size, max_output_size);
+		kdu_codestream codestream_out; 
+		codestream_out.create(&siz,&output);
+		//codestream_out.share_buffering(*mCodeStreamp);
+		siz_params *siz_out = codestream_out.access_siz();
+		siz_out->copy_from(siz_in,-1,-1,-1,0,discard_level,false,false,false);
+		codestream_out.access_siz()->finalize_all(-1);
+	
+		// Set up rate control variables
+		kdu_long max_bytes = KDU_LONG_MAX;
+		kdu_params *cod = siz_out->access_cluster(COD_params);
+		int total_layers;  cod->get(Clayers,0,0,total_layers);
+		kdu_long *layer_bytes = new kdu_long[total_layers];
+		int nel, non_empty_layers = 0;
+	
+		// Now ready to perform the transfer of compressed data between streams
+		int flush_counter = INT_MAX;
+		kdu_dims tile_indices_in;  
+		mCodeStreamp->get_valid_tiles(tile_indices_in);
+		kdu_dims tile_indices_out; 
+		codestream_out.get_valid_tiles(tile_indices_out);
+		assert((tile_indices_in.size.x == tile_indices_out.size.x) &&
+			   (tile_indices_in.size.y == tile_indices_out.size.y));
+		int num_blocks=0;
+	
+		kdu_coords idx;
+		//std::cout << "Parsing tiles : x = " << tile_indices_out.size.x << " to y = " << tile_indices_out.size.y << std::endl;
+		for (idx.y=0; idx.y < tile_indices_out.size.y; idx.y++)
+		{
+			for (idx.x=0; idx.x < tile_indices_out.size.x; idx.x++)
+			{
+				kdu_tile tile_in = mCodeStreamp->open_tile(idx+tile_indices_in.pos);
+				int tnum_in = tile_in.get_tnum();
+				int tnum_out = idx.x + idx.y*tile_indices_out.size.x;
+				siz_out->copy_from(siz_in,tnum_in,tnum_out,0,0,discard_level,false,false,false);
+				siz_out->finalize_all(tnum_out);
+				// Note: do not open the output tile without first copying any tile-specific code-stream parameters
+				kdu_tile tile_out = codestream_out.open_tile(idx+tile_indices_out.pos);
+				assert(tnum_out == tile_out.get_tnum());
+				copy_tile(tile_in,tile_out,tnum_in,tnum_out,siz_in,siz_out,0,num_blocks);
+				tile_in.close();
+				tile_out.close();
+				flush_counter--;
+				if ((flush_counter <= 0) && codestream_out.ready_for_flush())
+				{
+					flush_counter = INT_MAX;
+					nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
+					non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
+				}
+			}
+		}
+	
+		// Generate the output code-stream
+		if (codestream_out.ready_for_flush())
+		{
+			nel = codestream_out.trans_out(max_bytes,layer_bytes,total_layers);
+			non_empty_layers = (nel > non_empty_layers)?nel:non_empty_layers;
+		}
+		if (non_empty_layers > total_layers)
+			non_empty_layers = total_layers; // Can happen if a tile has more layers
+	
+		// Print out stats
+		std::cout << "Code stream parsing for discard level = " << discard_level << std::endl;
+		std::cout << "    Total compressed memory in  = " << mCodeStreamp->get_compressed_data_memory() << " bytes" << std::endl;
+		std::cout << "    Total compressed memory out = " << codestream_out.get_compressed_data_memory() << " bytes" << std::endl;
+		//std::cout << "    Output contains " << total_layers << " quality layers" << std::endl;		
+		std::cout << "    Transferred " << num_blocks << " code-blocks from in to out" << std::endl;
+		//std::cout << "    Read " << mCodeStreamp->get_num_tparts() << " tile-part(s) from a total of " << (int) tile_indices_in.area() << " tile(s)" << std::endl;
+		std::cout << "    Total bytes read = " << mCodeStreamp->get_total_bytes() << std::endl;
+		//std::cout << "    Wrote " << codestream_out.get_num_tparts() << " tile-part(s) in a total of " << (int) tile_indices_out.area() << " tile(s)" << std::endl;
+		std::cout << "    Total bytes written = " << codestream_out.get_total_bytes() << std::endl;
+		std::cout << "-------------" << std::endl;
+	
+		// Clean-up
+		cleanupCodeStream();
+		codestream_out.destroy();
+		delete[] output_buffer;	
+	}
+	return;
+}
+
 void set_default_colour_weights(kdu_params *siz)
 {
 	kdu_params *cod = siz->access_cluster(COD_params);
diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h
index 1489dbf7043ba08a21a30152ebc0fb121a535233..9ab0b9e4a7bcfe9402ebb81956c3cb9b8e50a83a 100644
--- a/indra/llkdu/llimagej2ckdu.h
+++ b/indra/llkdu/llimagej2ckdu.h
@@ -60,6 +60,7 @@ protected:
 								BOOL reversible=FALSE);
 	/*virtual*/ BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL);
 	/*virtual*/ BOOL initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0);
+	void findDiscardLevelsBoundaries(LLImageJ2C &base);
 
 private:
 	BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL);
diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp
index ab60ab6d502d05ee3bb0dcb21d43bf4eac151095..beee99a522967af6159b9241c898b076dad827a5 100644
--- a/indra/llkdu/tests/llimagej2ckdu_test.cpp
+++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp
@@ -29,6 +29,7 @@
 // Class to test 
 #include "llimagej2ckdu.h"
 #include "llkdumem.h"
+#include "kdu_block_coding.h"
 // Tut header
 #include "lltut.h"
 
@@ -86,7 +87,7 @@ void LLImageFormatted::resetLastError() { }
 void LLImageFormatted::sanityCheck() { }
 void LLImageFormatted::setLastError(const std::string& , const std::string& ) { }
 
-LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C) { }
+LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C), mRate(DEFAULT_COMPRESSION_RATE) { }
 LLImageJ2C::~LLImageJ2C() { }
 S32 LLImageJ2C::calcDataSize(S32 ) { return 0; }
 S32 LLImageJ2C::calcDiscardLevelBytes(S32 ) { return 0; }
@@ -107,16 +108,25 @@ bool LLKDUMemIn::get(int, kdu_line_buf&, int) { return false; }
 
 // Stub Kakadu Library calls
 kdu_tile_comp kdu_tile::access_component(int ) { kdu_tile_comp a; return a; }
+kdu_block_encoder::kdu_block_encoder() { }
+kdu_block_decoder::kdu_block_decoder() { }
+void kdu_block::set_max_passes(int , bool ) { }
+void kdu_block::set_max_bytes(int , bool ) { }
+void kdu_block::set_max_samples(int ) { }
 void kdu_tile::close(kdu_thread_env* ) { }
 int kdu_tile::get_num_components() { return 0; }
 bool kdu_tile::get_ycc() { return false; }
 void kdu_tile::set_components_of_interest(int , const int* ) { }
+int kdu_tile::get_tnum() { return 0; }
 kdu_resolution kdu_tile_comp::access_resolution() { kdu_resolution a; return a; }
+kdu_resolution kdu_tile_comp::access_resolution(int ) { kdu_resolution a; return a; }
 int kdu_tile_comp::get_bit_depth(bool ) { return 8; }
 bool kdu_tile_comp::get_reversible() { return false; }
+int kdu_tile_comp::get_num_resolutions() { return 1; }
 kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; }
 void kdu_resolution::get_dims(kdu_dims& ) { }
 int kdu_resolution::which() { return 0; }
+int kdu_resolution::get_valid_band_indices(int &) { return 1; }
 kdu_decoder::kdu_decoder(kdu_subband , kdu_sample_allocator*, bool , float, int, kdu_thread_env*, kdu_thread_queue*) { }
 kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { }
 kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { }
@@ -124,6 +134,7 @@ kdu_params::~kdu_params() { }
 void kdu_params::set(const char* , int , int , bool ) { }
 void kdu_params::set(const char* , int , int , int ) { }
 void kdu_params::finalize_all(bool ) { }
+void kdu_params::finalize_all(int, bool ) { }
 void kdu_params::copy_from(kdu_params*, int, int, int, int, int, bool, bool, bool) { }
 bool kdu_params::parse_string(const char*) { return false; }
 bool kdu_params::get(const char*, int, int, bool&, bool, bool, bool) { return false; }
@@ -135,6 +146,7 @@ void kdu_codestream::set_fast() { }
 void kdu_codestream::set_fussy() { }
 void kdu_codestream::get_dims(int, kdu_dims&, bool ) { }
 int kdu_codestream::get_min_dwt_levels() { return 5; }
+int kdu_codestream::get_max_tile_layers() { return 1; }
 void kdu_codestream::change_appearance(bool, bool, bool) { }
 void kdu_codestream::get_tile_dims(kdu_coords, int, kdu_dims&, bool ) { }
 void kdu_codestream::destroy() { }
@@ -148,9 +160,18 @@ void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { }
 void kdu_codestream::flush(kdu_long *, int , kdu_uint16 *, bool, bool, double, kdu_thread_env*) { }
 void kdu_codestream::set_resilient(bool ) { }
 int kdu_codestream::get_num_components(bool ) { return 0; }
+kdu_long kdu_codestream::get_total_bytes(bool ) { return 0; }
+kdu_long kdu_codestream::get_compressed_data_memory(bool ) {return 0; }
+void kdu_codestream::share_buffering(kdu_codestream ) { }
+int kdu_codestream::get_num_tparts() { return 0; }
+int kdu_codestream::trans_out(kdu_long, kdu_long*, int, bool, kdu_thread_env* ) { return 0; }
+bool kdu_codestream::ready_for_flush(kdu_thread_env*) { return false; }
 siz_params* kdu_codestream::access_siz() { return NULL; }
 kdu_tile kdu_codestream::open_tile(kdu_coords , kdu_thread_env* ) { kdu_tile a; return a; }
 kdu_codestream_comment kdu_codestream::add_comment() { kdu_codestream_comment a; return a; }
+void kdu_subband::close_block(kdu_block*, kdu_thread_env*) { }
+void kdu_subband::get_valid_blocks(kdu_dims &indices) { }
+kdu_block* kdu_subband::open_block(kdu_coords, int*, kdu_thread_env*) { return NULL; }
 bool kdu_codestream_comment::put_text(const char*) { return false; }
 void kdu_customize_warnings(kdu_message*) { }
 void kdu_customize_errors(kdu_message*) { }
diff --git a/indra/llmath/llcoord.h b/indra/llmath/llcoord.h
index 1f617e649e96e693850193f8ed1c4a688aa6814d..9b76268afde6986ee08651cb129f301db31fc29a 100644
--- a/indra/llmath/llcoord.h
+++ b/indra/llmath/llcoord.h
@@ -26,6 +26,15 @@
 #ifndef LL_LLCOORD_H
 #define LL_LLCOORD_H
 
+template<typename> class LLCoord;
+struct LL_COORD_TYPE_GL;
+struct LL_COORD_TYPE_WINDOW;
+struct LL_COORD_TYPE_SCREEN;
+
+typedef LLCoord<LL_COORD_TYPE_GL> LLCoordGL;
+typedef LLCoord<LL_COORD_TYPE_WINDOW> LLCoordWindow;
+typedef LLCoord<LL_COORD_TYPE_SCREEN> LLCoordScreen;
+
 struct LLCoordCommon
 {
 	LLCoordCommon(S32 x, S32 y) : mX(x), mY(y) {}
@@ -45,7 +54,7 @@ public:
 
 	LLCoord():	mX(0), mY(0)
 	{}
-	LLCoord(S32 x, S32 y): mX(x), mY(y)
+	LLCoord(typename COORD_FRAME::value_t x, typename COORD_FRAME::value_t y): mX(x), mY(y)
 	{}
 
 	LLCoord(const LLCoordCommon& other)
@@ -58,10 +67,12 @@ public:
 		return COORD_FRAME::convertToCommon();
 	}
 
-	void set(S32 x, S32 y) { mX = x; mY = y;}
+	void set(typename COORD_FRAME::value_t x, typename COORD_FRAME::value_t y) { mX = x; mY = y;}
 	bool operator==(const self_t& other) const { return mX == other.mX && mY == other.mY; }
 	bool operator!=(const self_t& other) const { return !(*this == other); }
 
+	static const self_t& getTypedCoords(const COORD_FRAME& self) { return static_cast<const self_t&>(self); }
+	static self_t& getTypedCoords(COORD_FRAME& self) { return static_cast<self_t&>(self); }
 };
 
 struct LL_COORD_TYPE_GL 
@@ -70,13 +81,13 @@ struct LL_COORD_TYPE_GL
 
 	LLCoordCommon convertToCommon() const
 	{
-		const LLCoord<LL_COORD_TYPE_GL>& self = static_cast<const LLCoord<LL_COORD_TYPE_GL>&>(*this);
+		const LLCoordGL& self = LLCoordGL::getTypedCoords(*this);
 		return LLCoordCommon(self.mX, self.mY);
 	}
 
 	void convertFromCommon(const LLCoordCommon& from)
 	{
-		LLCoord<LL_COORD_TYPE_GL>& self = static_cast<LLCoord<LL_COORD_TYPE_GL>&>(*this);
+		LLCoordGL& self = LLCoordGL::getTypedCoords(*this);
 		self.mX = from.mX;
 		self.mY = from.mY;
 	}
@@ -98,8 +109,4 @@ struct LL_COORD_TYPE_SCREEN
 	void convertFromCommon(const LLCoordCommon& from);
 };
 
-typedef LLCoord<LL_COORD_TYPE_GL> LLCoordGL;
-typedef LLCoord<LL_COORD_TYPE_WINDOW> LLCoordWindow;
-typedef LLCoord<LL_COORD_TYPE_SCREEN> LLCoordScreen;
-
 #endif
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index 5161ee629167c2c7c4e9e29c730a53f1dc953af0..5ea9b58300fe68e53fd8f21b02cceaa847f8535c 100644
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -1527,7 +1527,8 @@ void LLCurl::cleanupClass()
 	delete sHandleMutexp ;
 	sHandleMutexp = NULL ;
 
-	llassert(Easy::sActiveHandles.empty());
+	// removed as per https://jira.secondlife.com/browse/SH-3115
+	//llassert(Easy::sActiveHandles.empty());
 }
 
 //static 
diff --git a/indra/llmessage/tests/llsdmessage_test.cpp b/indra/llmessage/tests/llsdmessage_test.cpp
index 0f2c0693031351ed7e34fcec041d1de2be90ae86..6871ac0d527f6175895b36e69a05d98ec1b052d8 100644
--- a/indra/llmessage/tests/llsdmessage_test.cpp
+++ b/indra/llmessage/tests/llsdmessage_test.cpp
@@ -42,6 +42,7 @@
 // external library headers
 // other Linden headers
 #include "../test/lltut.h"
+#include "../test/catch_and_store_what_in.h"
 #include "llsdserialize.h"
 #include "llevents.h"
 #include "stringize.h"
@@ -72,43 +73,14 @@ namespace tut
     template<> template<>
     void llsdmessage_object::test<1>()
     {
-        bool threw = false;
+        std::string threw;
         // This should fail...
         try
         {
             LLSDMessage localListener;
         }
-        catch (const LLEventPump::DupPumpName&)
-        {
-            threw = true;
-        }
-        catch (const std::runtime_error& ex)
-        {
-            // This clause is because on Linux, on the viewer side, for this
-            // one test program (though not others!), the
-            // LLEventPump::DupPumpName exception isn't caught by the clause
-            // above. Warn the user...
-            std::cerr << "Failed to catch " << typeid(ex).name() << std::endl;
-            // But if the expected exception was thrown, allow the test to
-            // succeed anyway. Not sure how else to handle this odd case.
-            if (std::string(typeid(ex).name()) == typeid(LLEventPump::DupPumpName).name())
-            {
-                threw = true;
-            }
-            else
-            {
-                // We don't even recognize this exception. Let it propagate
-                // out to TUT to fail the test.
-                throw;
-            }
-        }
-        catch (...)
-        {
-            std::cerr << "Utterly failed to catch expected exception!" << std::endl;
-            // This case is full of fail. We HAVE to address it.
-            throw;
-        }
-        ensure("second LLSDMessage should throw", threw);
+        CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::DupPumpName)
+        ensure("second LLSDMessage should throw", ! threw.empty());
     }
 
     template<> template<>
diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py
index f2c841532a95e9a021aeb7c1173af03ad898b0c1..5b9beb359b8ec729f6ecb8310d0a7d7da112b578 100644
--- a/indra/llmessage/tests/testrunner.py
+++ b/indra/llmessage/tests/testrunner.py
@@ -35,7 +35,7 @@ import re
 import errno
 import socket
 
-VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "1") # default to verbose
+VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "0") # default to quiet
 # Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if
 # that construct actually turns on verbosity...
 VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE)
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index 110fac0f2388abe4a39bb009cd660fe899e89e52..f10eaee5b48041d0f3ee66461259875846d6b3be 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -31,6 +31,7 @@
 #include "llpluginprocessparent.h"
 #include "llpluginmessagepipe.h"
 #include "llpluginmessageclasses.h"
+#include "stringize.h"
 
 #include "llapr.h"
 
@@ -134,7 +135,10 @@ LLPluginProcessParent::~LLPluginProcessParent()
 		mSharedMemoryRegions.erase(iter);
 	}
 	
-	mProcess.kill();
+	if (mProcess)
+	{
+		mProcess->kill();
+	}
 	killSockets();
 }
 
@@ -159,8 +163,8 @@ void LLPluginProcessParent::errorState(void)
 
 void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_dir, const std::string &plugin_filename, bool debug)
 {	
-	mProcess.setExecutable(launcher_filename);
-	mProcess.setWorkingDirectory(plugin_dir);
+	mProcessParams.executable = launcher_filename;
+	mProcessParams.cwd = plugin_dir;
 	mPluginFile = plugin_filename;
 	mPluginDir = plugin_dir;
 	mCPUUsage = 0.0f;
@@ -371,10 +375,8 @@ void LLPluginProcessParent::idle(void)
 				// Launch the plugin process.
 				
 				// Only argument to the launcher is the port number we're listening on
-				std::stringstream stream;
-				stream << mBoundPort;
-				mProcess.addArgument(stream.str());
-				if(mProcess.launch() != 0)
+				mProcessParams.args.add(stringize(mBoundPort));
+				if (! (mProcess = LLProcess::create(mProcessParams)))
 				{
 					errorState();
 				}
@@ -388,19 +390,18 @@ void LLPluginProcessParent::idle(void)
 						// The command we're constructing would look like this on the command line:
 						// osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell'
 
-						std::stringstream cmd;
-						
-						mDebugger.setExecutable("/usr/bin/osascript");
-						mDebugger.addArgument("-e");
-						mDebugger.addArgument("tell application \"Terminal\"");
-						mDebugger.addArgument("-e");
-						cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\"";
-						mDebugger.addArgument(cmd.str());
-						mDebugger.addArgument("-e");
-						mDebugger.addArgument("do script \"continue\" in win");
-						mDebugger.addArgument("-e");
-						mDebugger.addArgument("end tell");
-						mDebugger.launch();
+						LLProcess::Params params;
+						params.executable = "/usr/bin/osascript";
+						params.args.add("-e");
+						params.args.add("tell application \"Terminal\"");
+						params.args.add("-e");
+						params.args.add(STRINGIZE("set win to do script \"gdb -pid "
+												  << mProcess->getProcessID() << "\""));
+						params.args.add("-e");
+						params.args.add("do script \"continue\" in win");
+						params.args.add("-e");
+						params.args.add("end tell");
+						mDebugger = LLProcess::create(params);
 
 						#endif
 					}
@@ -470,7 +471,7 @@ void LLPluginProcessParent::idle(void)
 			break;
 			
 			case STATE_EXITING:
-				if(!mProcess.isRunning())
+				if (! mProcess->isRunning())
 				{
 					setState(STATE_CLEANUP);
 				}
@@ -498,7 +499,7 @@ void LLPluginProcessParent::idle(void)
 			break;
 			
 			case STATE_CLEANUP:
-				mProcess.kill();
+				mProcess->kill();
 				killSockets();
 				setState(STATE_DONE);
 			break;
@@ -1077,7 +1078,7 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
 {
 	bool result = false;
 	
-	if(!mProcess.isRunning())
+	if (! mProcess->isRunning())
 	{
 		LL_WARNS("Plugin") << "child exited" << LL_ENDL;
 		result = true;
diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h
index c66723f1753c7a7046e21b57a3a8c2789f7ccaa1..990fc5cbae3226503ca1f9278dbc291716511315 100644
--- a/indra/llplugin/llpluginprocessparent.h
+++ b/indra/llplugin/llpluginprocessparent.h
@@ -30,13 +30,14 @@
 #define LL_LLPLUGINPROCESSPARENT_H
 
 #include "llapr.h"
-#include "llprocesslauncher.h"
+#include "llprocess.h"
 #include "llpluginmessage.h"
 #include "llpluginmessagepipe.h"
 #include "llpluginsharedmemory.h"
 
 #include "lliosocket.h"
 #include "llthread.h"
+#include "llsd.h"
 
 class LLPluginProcessParentOwner
 {
@@ -139,26 +140,27 @@ private:
 	};
 	EState mState;
 	void setState(EState state);
-	
+
 	bool pluginLockedUp();
 	bool pluginLockedUpOrQuit();
 
 	bool accept();
-		
+
 	LLSocket::ptr_t mListenSocket;
 	LLSocket::ptr_t mSocket;
 	U32 mBoundPort;
-	
-	LLProcessLauncher mProcess;
-	
+
+	LLProcess::Params mProcessParams;
+	LLProcessPtr mProcess;
+
 	std::string mPluginFile;
 	std::string mPluginDir;
 
 	LLPluginProcessParentOwner *mOwner;
-	
+
 	typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType;
 	sharedMemoryRegionsType mSharedMemoryRegions;
-	
+
 	LLSD mMessageClassVersions;
 	std::string mPluginVersionString;
 	
@@ -171,7 +173,7 @@ private:
 	bool mBlocked;
 	bool mPolledInput;
 
-	LLProcessLauncher mDebugger;
+	LLProcessPtr mDebugger;
 	
 	F32 mPluginLaunchTimeout;		// Somewhat longer timeout for initial launch.
 	F32 mPluginLockupTimeout;		// If we don't receive a heartbeat in this many seconds, we declare the plugin locked up.
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 17131c9d8a0184543e3740ac9c4d7237458d8db7..c04a3f6b4182d5693bf14f926ed160260c475f56 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -43,7 +43,6 @@
 const F32 MIN_TEXTURE_LIFETIME = 10.f;
 
 //statics
-LLGLuint LLImageGL::sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS] = { 0 };
 
 U32 LLImageGL::sUniqueCount				= 0;
 U32 LLImageGL::sBindCount				= 0;
@@ -66,19 +65,10 @@ std::set<LLImageGL*> LLImageGL::sImageList;
 //****************************************************************************************************
 //-----------------------
 //debug use
-BOOL gAuditTexture = FALSE ;
-#define MAX_TEXTURE_LOG_SIZE 22 //2048 * 2048
-std::vector<S32> LLImageGL::sTextureLoadedCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
-std::vector<S32> LLImageGL::sTextureBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
-std::vector<S32> LLImageGL::sTextureCurBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
 S32 LLImageGL::sCurTexSizeBar = -1 ;
 S32 LLImageGL::sCurTexPickSize = -1 ;
-LLPointer<LLImageGL> LLImageGL::sHighlightTexturep = NULL;
-S32 LLImageGL::sMaxCatagories = 1 ;
+S32 LLImageGL::sMaxCategories = 1 ;
 
-std::vector<S32> LLImageGL::sTextureMemByCategory;
-std::vector<S32> LLImageGL::sTextureMemByCategoryBound ;
-std::vector<S32> LLImageGL::sTextureCurMemByCategoryBound ;
 //------------------------
 //****************************************************************************************************
 //End for texture auditing use only
@@ -176,49 +166,11 @@ BOOL is_little_endian()
 //static 
 void LLImageGL::initClass(S32 num_catagories) 
 {
-	sMaxCatagories = num_catagories ;
-
-	sTextureMemByCategory.resize(sMaxCatagories);
-	sTextureMemByCategoryBound.resize(sMaxCatagories) ;
-	sTextureCurMemByCategoryBound.resize(sMaxCatagories) ;
 }
 
 //static 
 void LLImageGL::cleanupClass() 
 {	
-	sTextureMemByCategory.clear() ;
-	sTextureMemByCategoryBound.clear() ;
-	sTextureCurMemByCategoryBound.clear() ;
-}
-
-//static 
-void LLImageGL::setHighlightTexture(S32 category) 
-{
-	const S32 dim = 128;
-	sHighlightTexturep = new LLImageGL() ;
-	LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
-	U8* data = image_raw->getData();
-	for (S32 i = 0; i<dim; i++)
-	{
-		for (S32 j = 0; j<dim; j++)
-		{
-			const S32 border = 2;
-			if (i<border || j<border || i>=(dim-border) || j>=(dim-border))
-			{
-				*data++ = 0xff;
-				*data++ = 0xff;
-				*data++ = 0xff;
-			}
-			else
-			{
-				*data++ = 0xff;
-				*data++ = 0xff;
-				*data++ = 0x00;
-			}
-		}
-	}
-	sHighlightTexturep->createGLTexture(0, image_raw, 0, TRUE, category);
-	image_raw = NULL;
 }
 
 //static
@@ -286,31 +238,11 @@ void LLImageGL::updateStats(F32 current_time)
 	sLastFrameTime = current_time;
 	sBoundTextureMemoryInBytes = sCurBoundTextureMemory;
 	sCurBoundTextureMemory = 0;
-
-	if(gAuditTexture)
-	{
-		for(U32 i = 0 ; i < sTextureCurBoundCounter.size() ; i++)
-		{
-			sTextureBoundCounter[i] = sTextureCurBoundCounter[i] ;
-			sTextureCurBoundCounter[i] = 0 ;
-		}
-		for(U32 i = 0 ; i < sTextureCurMemByCategoryBound.size() ; i++)
-		{
-			sTextureMemByCategoryBound[i] = sTextureCurMemByCategoryBound[i] ;
-			sTextureCurMemByCategoryBound[i] = 0 ;
-		}
-	}
 }
 
 //static
 S32 LLImageGL::updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category)
 {
-	if(gAuditTexture && ncomponents > 0 && category > -1)
-	{
-		sTextureCurBoundCounter[getTextureCounterIndex(mem / ncomponents)]++ ;
-		sTextureCurMemByCategoryBound[category] += mem ;
-	}
-	
 	LLImageGL::sCurBoundTextureMemory += mem ;
 	return LLImageGL::sCurBoundTextureMemory;
 }
@@ -1317,7 +1249,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
 		return TRUE ;
 	}
 
-	setCategory(category) ;
+	setCategory(category);
  	const U8* rawdata = imageraw->getData();
 	return createGLTexture(discard_level, rawdata, FALSE, usename);
 }
@@ -1395,11 +1327,6 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
 	{
 		sGlobalTextureMemoryInBytes -= mTextureMemory;
 
-		if(gAuditTexture)
-		{
-			decTextureCounter(mTextureMemory, mComponents, mCategory) ;
-		}
-
 		LLImageGL::deleteTextures(1, &old_name);
 
 		stop_glerror();
@@ -1409,10 +1336,6 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
 	sGlobalTextureMemoryInBytes += mTextureMemory;
 	mTexelsInGLTexture = getWidth() * getHeight() ;
 
-	if(gAuditTexture)
-	{
-		incTextureCounter(mTextureMemory, mComponents, mCategory) ;
-	}
 	// mark this as bound at this point, so we don't throw it out immediately
 	mLastBindTime = sLastFrameTime;
 	return TRUE;
@@ -1569,22 +1492,29 @@ void LLImageGL::destroyGLTexture()
 	{
 		if(mTextureMemory)
 		{
-			if(gAuditTexture)
-			{
-				decTextureCounter(mTextureMemory, mComponents, mCategory) ;
-			}
 			sGlobalTextureMemoryInBytes -= mTextureMemory;
 			mTextureMemory = 0;
 		}
 		
 		LLImageGL::deleteTextures(1, &mTexName);			
-		mTexName = 0;
 		mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
+		mTexName = 0;		
 		mGLTextureCreated = FALSE ;
-	}
+	}	
 }
 
-
+//force to invalidate the gl texture, most likely a sculpty texture
+void LLImageGL::forceToInvalidateGLTexture()
+{
+	if (mTexName != 0)
+	{
+		destroyGLTexture();
+	}
+	else
+	{
+		mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
+	}
+}
 
 //----------------------------------------------------------------------------
 
@@ -2002,70 +1932,6 @@ BOOL LLImageGL::getMask(const LLVector2 &tc)
 	return res;
 }
 
-void LLImageGL::setCategory(S32 category) 
-{
-#if 0 //turn this off temporarily because it is not in use now.
-	if(!gAuditTexture)
-	{
-		return ;
-	}
-	if(mCategory != category)
-	{		
-		if(mCategory > -1)
-		{
-			sTextureMemByCategory[mCategory] -= mTextureMemory ;
-		}
-		if(category > -1 && category < sMaxCatagories)
-		{
-			sTextureMemByCategory[category] += mTextureMemory ;		
-			mCategory = category;
-		}
-		else
-		{
-			mCategory = -1 ;
-		}
-	}
-#endif
-}
-
-//for debug use 
-//val is a "power of two" number
-S32 LLImageGL::getTextureCounterIndex(U32 val) 
-{
-	//index range is [0, MAX_TEXTURE_LOG_SIZE].
-	if(val < 2)
-	{
-		return 0 ;
-	}
-	else if(val >= (1 << MAX_TEXTURE_LOG_SIZE))
-	{
-		return MAX_TEXTURE_LOG_SIZE ;
-	}
-	else
-	{
-		S32 ret = 0 ;
-		while(val >>= 1)
-		{
-			++ret;
-		}
-		return ret ;
-	}
-}
-
-//static
-void LLImageGL::incTextureCounter(U32 val, S32 ncomponents, S32 category) 
-{
-	sTextureLoadedCounter[getTextureCounterIndex(val)]++ ;
-	sTextureMemByCategory[category] += (S32)val * ncomponents ;
-}
-
-//static
-void LLImageGL::decTextureCounter(U32 val, S32 ncomponents, S32 category) 
-{
-	sTextureLoadedCounter[getTextureCounterIndex(val)]-- ;
-	sTextureMemByCategory[category] += (S32)val * ncomponents ;
-}
-
 void LLImageGL::setCurTexSizebar(S32 index, BOOL set_pick_size)
 {
 	sCurTexSizeBar = index ;
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index e23005fe29d53d3d9590977d11d03d6b4d98b9b0..f34b9fa91a9302c638a953c31d53eeec2d2dc20f 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -103,8 +103,8 @@ public:
 	static void setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression = true);
 
 	BOOL createGLTexture() ;
-	BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE, 
-		S32 category = sMaxCatagories - 1);
+	BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0, BOOL to_create = TRUE,
+		S32 category = sMaxCategories-1);
 	BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0);
 	void setImage(const LLImageRaw* imageraw);
 	void setImage(const U8* data_in, BOOL data_hasmips = FALSE);
@@ -115,6 +115,7 @@ public:
 	// Read back a raw image for this discard level, if it exists
 	BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const;
 	void destroyGLTexture();
+	void forceToInvalidateGLTexture();
 
 	void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE);
 	void setComponents(S8 ncomponents) { mComponents = ncomponents; }
@@ -237,8 +238,6 @@ public:
 	static S32 sCount;
 	
 	static F32 sLastFrameTime;
-	
-	static LLGLuint sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS]; // Currently bound texture ID
 
 	// Global memory statistics
 	static S32 sGlobalTextureMemoryInBytes;		// Tracks main memory texmem
@@ -260,9 +259,10 @@ public:
 public:
 	static void initClass(S32 num_catagories) ;
 	static void cleanupClass() ;
-private:
-	static S32 sMaxCatagories ;
 
+private:
+	static S32 sMaxCategories;
+	
 	//the flag to allow to call readBackRaw(...).
 	//can be removed if we do not use that function at all.
 	static BOOL sAllowReadBackRaw ;
@@ -272,39 +272,22 @@ private:
 //****************************************************************************************************
 private:
 	S32 mCategory ;
-public:		
-	void setCategory(S32 category) ;
-	S32  getCategory()const {return mCategory ;}
-
+public:
+	void setCategory(S32 category) {mCategory = category;}
+	S32  getCategory()const {return mCategory;}
+	
 	//for debug use: show texture size distribution 
 	//----------------------------------------
-	static LLPointer<LLImageGL> sHighlightTexturep; //default texture to replace normal textures
-	static std::vector<S32> sTextureLoadedCounter ;
-	static std::vector<S32> sTextureBoundCounter ;
-	static std::vector<S32> sTextureCurBoundCounter ;
 	static S32 sCurTexSizeBar ;
 	static S32 sCurTexPickSize ;
 
-	static void setHighlightTexture(S32 category) ;
-	static S32 getTextureCounterIndex(U32 val) ;
-	static void incTextureCounter(U32 val, S32 ncomponents, S32 category) ;
-	static void decTextureCounter(U32 val, S32 ncomponents, S32 category) ;
 	static void setCurTexSizebar(S32 index, BOOL set_pick_size = TRUE) ;
 	static void resetCurTexSizebar();
-	//----------------------------------------
 
-	//for debug use: show texture category distribution 
-	//----------------------------------------		
-	
-	static std::vector<S32> sTextureMemByCategory;
-	static std::vector<S32> sTextureMemByCategoryBound ;
-	static std::vector<S32> sTextureCurMemByCategoryBound ;
-	//----------------------------------------	
 //****************************************************************************************************
 //End of definitions for texture auditing use only
 //****************************************************************************************************
 
 };
 
-extern BOOL gAuditTexture;
 #endif // LL_LLIMAGEGL_H
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index b0ddacbb05c8dd872ffbfde26501f61639dc0f45..93bac4c779e765bc4142eb0ed525833a5b1c9d83 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -246,14 +246,6 @@ bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
 	}
 
 	//in audit, replace the selected texture by the default one.
-	if(gAuditTexture && for_rendering && LLImageGL::sCurTexPickSize > 0)
-	{
-		if(texture->getWidth() * texture->getHeight() == LLImageGL::sCurTexPickSize)
-		{
-			gl_tex->updateBindStats(gl_tex->mTextureMemory);
-			return bind(LLImageGL::sHighlightTexturep.get());
-		}
-	}
 	if ((mCurrTexture != gl_tex->getTexName()) || forceBind)
 	{
 		activate();
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 7d384450e67e16da8728b9b431b9111c3de2319a..5a6e6cab3e41f5388bceaa854ad552d4590fdc5c 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1062,8 +1062,9 @@ void LLShaderMgr::initAttribsAndUniforms()
 	mReservedUniforms.push_back("proj_shadow_res");
 	mReservedUniforms.push_back("depth_cutoff");
 	mReservedUniforms.push_back("norm_cutoff");
+	mReservedUniforms.push_back("shadow_target_width");
 	
-	llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_NORM_CUTOFF+1);
+	llassert(mReservedUniforms.size() == LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH+1);
 
 	mReservedUniforms.push_back("tc_scale");
 	mReservedUniforms.push_back("rcp_screen_res");
diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h
index e28bda6de2d4b39a6b4af2d27f20659eecfc28df..f792faa8f0f43e7100a974fe9e0f486ac259ca60 100644
--- a/indra/llrender/llshadermgr.h
+++ b/indra/llrender/llshadermgr.h
@@ -130,6 +130,7 @@ public:
 		DEFERRED_PROJ_SHADOW_RES,
 		DEFERRED_DEPTH_CUTOFF,
 		DEFERRED_NORM_CUTOFF,
+		DEFERRED_SHADOW_TARGET_WIDTH,
 
 		FXAA_TC_SCALE,
 		FXAA_RCP_SCREEN_RES,
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 772f173f1748e9edabc3281e2ed6b43feb0803d0..20c3456a56a816ac3fda9c3edf0eb17fdefc033d 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -12,7 +12,6 @@ include(LLRender)
 include(LLWindow)
 include(LLVFS)
 include(LLXML)
-include(LLXUIXML)
 
 include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
@@ -24,7 +23,6 @@ include_directories(
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLVFS_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
-    ${LLXUIXML_INCLUDE_DIRS}
     )
 
 set(llui_SOURCE_FILES
@@ -83,7 +81,6 @@ set(llui_SOURCE_FILES
     llscrolllistcolumn.cpp
     llscrolllistctrl.cpp
     llscrolllistitem.cpp
-    llsdparam.cpp
     llsearcheditor.cpp
     llslider.cpp
     llsliderctrl.cpp
@@ -100,11 +97,13 @@ set(llui_SOURCE_FILES
     lltextutil.cpp
     lltextvalidate.cpp
     lltimectrl.cpp
+    lltrans.cpp
     lltransutil.cpp
     lltoggleablemenu.cpp
     lltoolbar.cpp
     lltooltip.cpp
     llui.cpp
+    lluicolor.cpp
     lluicolortable.cpp
     lluictrl.cpp
     lluictrlfactory.cpp
@@ -121,6 +120,7 @@ set(llui_SOURCE_FILES
     llview.cpp
     llviewquery.cpp
     llwindowshade.cpp
+    llxuiparser.cpp
     )
     
 set(llui_HEADER_FILES
@@ -189,7 +189,6 @@ set(llui_HEADER_FILES
     llscrolllistcolumn.h
     llscrolllistctrl.h
     llscrolllistitem.h
-    llsdparam.h
     llsliderctrl.h
     llslider.h
     llspinctrl.h
@@ -208,6 +207,7 @@ set(llui_HEADER_FILES
     lltoggleablemenu.h
     lltoolbar.h
     lltooltip.h
+    lltrans.h
     lltransutil.h
     lluicolortable.h
     lluiconstants.h
@@ -215,6 +215,7 @@ set(llui_HEADER_FILES
     lluictrl.h
     lluifwd.h
     llui.h
+    lluicolor.h
     lluiimage.h
     lluistring.h
     llundo.h
@@ -228,6 +229,7 @@ set(llui_HEADER_FILES
     llview.h
     llviewquery.h
     llwindowshade.h
+    llxuiparser.h
     )
 
 set_source_files_properties(${llui_HEADER_FILES}
diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp
index 6910b962a1932ce09c9e31bc5f120a3e460ece1b..14173fdbb0a6f9fdbb228896fd113b411875f50a 100644
--- a/indra/llui/llclipboard.cpp
+++ b/indra/llui/llclipboard.cpp
@@ -34,109 +34,113 @@
 #include "llview.h"
 #include "llwindow.h"
 
-// Global singleton
-LLClipboard gClipboard;
-
-
-LLClipboard::LLClipboard()
+LLClipboard::LLClipboard() :
+	mGeneration(0)
 {
-	mSourceItem = NULL;
+	reset();
 }
 
-
 LLClipboard::~LLClipboard()
 {
+	reset();
 }
 
-
-void LLClipboard::copyFromSubstring(const LLWString &src, S32 pos, S32 len, const LLUUID& source_id )
+void LLClipboard::reset()
 {
-	mSourceID = source_id;
-	mString = src.substr(pos, len);
-	LLView::getWindow()->copyTextToClipboard( mString );
+	// Increment the clipboard count
+	mGeneration++;
+	// Clear the clipboard
+	mObjects.clear();
+	mCutMode = false;
+	mString = LLWString();
 }
 
-void LLClipboard::copyFromString(const LLWString &src, const LLUUID& source_id )
+// Copy the input uuid to the LL clipboard
+bool LLClipboard::copyToClipboard(const LLUUID& src, const LLAssetType::EType type)
 {
-	mSourceID = source_id;
-	mString = src;
-	LLView::getWindow()->copyTextToClipboard( mString );
+	reset();
+	return addToClipboard(src, type);
 }
 
-const LLWString& LLClipboard::getPasteWString( LLUUID* source_id )
+// Add the input uuid to the LL clipboard
+// Convert the uuid to string and concatenate that string to the system clipboard if legit
+bool LLClipboard::addToClipboard(const LLUUID& src, const LLAssetType::EType type)
 {
-	if( mSourceID.notNull() )
+	bool res = false;
+	if (src.notNull())
 	{
-		LLWString temp_string;
-		LLView::getWindow()->pasteTextFromClipboard(temp_string);
-
-		if( temp_string != mString )
+		res = true;
+		if (LLAssetType::lookupIsAssetIDKnowable(type))
 		{
-			mSourceID.setNull();
-			mString = temp_string;
+			LLWString source = utf8str_to_wstring(src.asString());
+			res = addToClipboard(source, 0, source.size());
+		}
+		if (res)
+		{
+			mObjects.push_back(src);
+			mGeneration++;
 		}
 	}
-	else
-	{
-		LLView::getWindow()->pasteTextFromClipboard(mString);
-	}
+	return res;
+}
 
-	if( source_id )
+bool LLClipboard::pasteFromClipboard(std::vector<LLUUID>& inv_objects) const
+{
+	bool res = false;
+	S32 count = mObjects.size();
+	if (count > 0)
 	{
-		*source_id = mSourceID;
+		res = true;
+		inv_objects.clear();
+		for (S32 i = 0; i < count; i++)
+		{
+			inv_objects.push_back(mObjects[i]);
+		}
 	}
-
-	return mString;
+	return res;
 }
 
+// Returns true if the LL Clipboard has pasteable items in it
+bool LLClipboard::hasContents() const
+{
+	return (mObjects.size() > 0);
+}
 
-BOOL LLClipboard::canPasteString() const
+// Returns true if the input uuid is in the list of clipboard objects
+bool LLClipboard::isOnClipboard(const LLUUID& object) const
 {
-	return LLView::getWindow()->isClipboardTextAvailable();
+	std::vector<LLUUID>::const_iterator iter = std::find(mObjects.begin(), mObjects.end(), object);
+	return (iter != mObjects.end());
 }
 
+// Copy the input string to the LL and the system clipboard
+bool LLClipboard::copyToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary)
+{
+	return addToClipboard(src, pos, len, use_primary);
+}
 
-void LLClipboard::copyFromPrimarySubstring(const LLWString &src, S32 pos, S32 len, const LLUUID& source_id )
+// Concatenate the input string to the LL and the system clipboard
+bool LLClipboard::addToClipboard(const LLWString &src, S32 pos, S32 len, bool use_primary)
 {
-	mSourceID = source_id;
 	mString = src.substr(pos, len);
-	LLView::getWindow()->copyTextToPrimary( mString );
+	return (use_primary ? LLView::getWindow()->copyTextToPrimary(mString) : LLView::getWindow()->copyTextToClipboard(mString));
 }
 
-
-const LLWString& LLClipboard::getPastePrimaryWString( LLUUID* source_id )
+// Copy the System clipboard to the output string.
+// Manage the LL Clipboard / System clipboard consistency
+bool LLClipboard::pasteFromClipboard(LLWString &dst, bool use_primary)
 {
-	if( mSourceID.notNull() )
-	{
-		LLWString temp_string;
-		LLView::getWindow()->pasteTextFromPrimary(temp_string);
-
-		if( temp_string != mString )
-		{
-			mSourceID.setNull();
-			mString = temp_string;
-		}
-	}
-	else
-	{
-		LLView::getWindow()->pasteTextFromPrimary(mString);
-	}
-
-	if( source_id )
+	bool res = (use_primary ? LLView::getWindow()->pasteTextFromPrimary(dst) : LLView::getWindow()->pasteTextFromClipboard(dst));
+	if (res)
 	{
-		*source_id = mSourceID;
+		mString = dst;
 	}
-
-	return mString;
+	return res;
 }
 
-
-BOOL LLClipboard::canPastePrimaryString() const
+// Return true if there's something on the System clipboard
+bool LLClipboard::isTextAvailable(bool use_primary) const
 {
-	return LLView::getWindow()->isPrimaryTextAvailable();
+	return (use_primary ? LLView::getWindow()->isPrimaryTextAvailable() : LLView::getWindow()->isClipboardTextAvailable());
 }
 
-void LLClipboard::setSourceObject(const LLUUID& source_id, LLAssetType::EType type) 
-{
-	mSourceItem = new LLInventoryObject (source_id, LLUUID::null, type, "");
-}
diff --git a/indra/llui/llclipboard.h b/indra/llui/llclipboard.h
index 9371b9428447ae2c8deb3ba3238d726dca41a7cb..fd2e7610df4bda53d4809748478a2479fa2b84a8 100644
--- a/indra/llui/llclipboard.h
+++ b/indra/llui/llclipboard.h
@@ -27,46 +27,68 @@
 #ifndef LL_LLCLIPBOARD_H
 #define LL_LLCLIPBOARD_H
 
+#include <boost/function.hpp>
 
 #include "llstring.h"
 #include "lluuid.h"
 #include "stdenums.h"
+#include "llsingleton.h"
+#include "llassettype.h"
 #include "llinventory.h"
 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLClipboard
+//
+// This class is used to cut/copy/paste text strings and inventory items around 
+// the world. Use LLClipboard::instance().method() to use its methods.
+// Note that the text and UUIDs are loosely coupled only. There are few cases
+// where the viewer does offer a serialized version of the UUID on the clipboard.
+// In those case, the text is overridden when copying/cutting the item. 
+// In all other cases, the text and the UUIDs are very much independent.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-class LLClipboard
+class LLClipboard : public LLSingleton<LLClipboard>
 {
 public:
 	LLClipboard();
 	~LLClipboard();
+	
+	// Clears the clipboard
+	void reset();
+	// Returns the state of the clipboard so client can know if it has been modified (comparing with tracked state)
+	int	getGeneration() const { return mGeneration; }
 
-	/* We support two flavors of clipboard.  The default is the explicitly
-	   copy-and-pasted clipboard.  The second is the so-called 'primary' clipboard
-	   which is implicitly copied upon selection on platforms which expect this
-	   (i.e. X11/Linux). */
-
-	void		copyFromSubstring(const LLWString &copy_from, S32 pos, S32 len, const LLUUID& source_id = LLUUID::null );
-	void		copyFromString(const LLWString &copy_from, const LLUUID& source_id = LLUUID::null );
-	BOOL		canPasteString() const;
-	const LLWString&	getPasteWString(LLUUID* source_id = NULL);
+	// Text strings management:
+	// ------------------------
+	// We support two flavors of text clipboards. The default is the explicitly
+	// copy-and-pasted clipboard. The second is the so-called 'primary' clipboard
+	// which is implicitly copied upon selection on platforms which expect this
+	// (i.e. X11/Linux, Mac).
+	bool copyToClipboard(const LLWString& src, S32 pos, S32 len, bool use_primary = false);
+	bool addToClipboard(const LLWString& src, S32 pos, S32 len, bool use_primary = false);
+	bool pasteFromClipboard(LLWString& dst, bool use_primary = false);
+	bool isTextAvailable(bool use_primary = false) const;
+	
+	// Object list management:
+	// -----------------------
+	// Clears and adds one single object to the clipboard
+	bool copyToClipboard(const LLUUID& src, const LLAssetType::EType type = LLAssetType::AT_NONE);
+	// Adds one object to the current list of objects on the clipboard
+	bool addToClipboard(const LLUUID& src, const LLAssetType::EType type = LLAssetType::AT_NONE);
+	// Gets a copy of the objects on the clipboard
+	bool pasteFromClipboard(std::vector<LLUUID>& inventory_objects) const;
+	
+	bool hasContents() const;										// True if the clipboard has pasteable objects
+	bool isOnClipboard(const LLUUID& object) const;					// True if the input object uuid is on the clipboard
 
-	void		copyFromPrimarySubstring(const LLWString &copy_from, S32 pos, S32 len, const LLUUID& source_id = LLUUID::null );
-	BOOL		canPastePrimaryString() const;
-	const LLWString&	getPastePrimaryWString(LLUUID* source_id = NULL);	
+	bool isCutMode() const { return mCutMode; }
+	void setCutMode(bool mode) { mCutMode = mode; mGeneration++; }
 
-	// Support clipboard for object known only by their uuid and asset type
-	void		  setSourceObject(const LLUUID& source_id, LLAssetType::EType type);
-	const LLInventoryObject* getSourceObject() { return mSourceItem; }
-	
 private:
-	LLUUID      mSourceID;
-	LLWString	mString;
-	LLInventoryObject* mSourceItem;
+	std::vector<LLUUID> mObjects;		// Objects on the clipboard. Can be empty while mString contains something licit (e.g. text from chat)
+	LLWString mString;					// The text string. If mObjects is not empty, this string is reflecting them (UUIDs for the moment) if the asset type is knowable.
+	bool mCutMode;						// This is a convenience flag for the viewer.
+	int mGeneration;					// Incremented when the clipboard changes so that interested parties can check for changes on the clipboard.	
 };
 
-
-// Global singleton
-extern LLClipboard gClipboard;
-
-
 #endif  // LL_LLCLIPBOARD_H
diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp
index e01e331acf3f8e5eb2d88467653e457dbf2e84f0..e08ccb0b78c36e1b376d04c87f0a86b6c6444b62 100644
--- a/indra/llui/llcontainerview.cpp
+++ b/indra/llui/llcontainerview.cpp
@@ -196,24 +196,24 @@ void LLContainerView::arrange(S32 width, S32 height, BOOL called_from_parent)
 	if (total_height < height)
 		total_height = height;
 	
+	LLRect my_rect = getRect();
 	if (followsTop())
 	{
-		// HACK: casting away const. Should use setRect or some helper function instead.
-		const_cast<LLRect&>(getRect()).mBottom = getRect().mTop - total_height;
+		my_rect.mBottom = my_rect.mTop - total_height;
 	}
 	else
 	{
-		// HACK: casting away const. Should use setRect or some helper function instead.
-		const_cast<LLRect&>(getRect()).mTop = getRect().mBottom + total_height;
+		my_rect.mTop = my_rect.mBottom + total_height;
 	}
-	// HACK: casting away const. Should use setRect or some helper function instead.
-		const_cast<LLRect&>(getRect()).mRight = getRect().mLeft + width;
+
+	my_rect.mRight = my_rect.mLeft + width;
+	setRect(my_rect);
 
 	top = total_height;
 	if (mShowLabel)
-		{
-			top -= 20;
-		}
+	{
+		top -= 20;
+	}
 	
 	bottom = top;
 
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 22b20969fc5799ddf202980385d3961e1c749beb..8ca1e685a96922a823d5b182590f53ea90b7cc01 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -68,10 +68,10 @@ namespace LLInitParam
 {
 	void TypeValues<LLFloaterEnums::EOpenPositioning>::declareValues()
 	{
-		declare("none",       LLFloaterEnums::OPEN_POSITIONING_NONE);
-		declare("cascading",  LLFloaterEnums::OPEN_POSITIONING_CASCADING);
-		declare("centered",   LLFloaterEnums::OPEN_POSITIONING_CENTERED);
-		declare("specified",  LLFloaterEnums::OPEN_POSITIONING_SPECIFIED);
+		declare("relative",   LLFloaterEnums::POSITIONING_RELATIVE);
+		declare("cascading",  LLFloaterEnums::POSITIONING_CASCADING);
+		declare("centered",   LLFloaterEnums::POSITIONING_CENTERED);
+		declare("specified",  LLFloaterEnums::POSITIONING_SPECIFIED);
 	}
 }
 
@@ -177,9 +177,7 @@ LLFloater::Params::Params()
 	save_visibility("save_visibility", false),
 	can_dock("can_dock", false),
 	show_title("show_title", true),
-	open_positioning("open_positioning", LLFloaterEnums::OPEN_POSITIONING_NONE),
-	specified_left("specified_left"),
-	specified_bottom("specified_bottom"),
+	positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE),
 	header_height("header_height", 0),
 	legacy_header_height("legacy_header_height", 0),
 	close_image("close_image"),
@@ -249,9 +247,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p)
 	mCanClose(p.can_close),
 	mDragOnLeft(p.can_drag_on_left),
 	mResizable(p.can_resize),
-	mOpenPositioning(p.open_positioning),
-	mSpecifiedLeft(p.specified_left),
-	mSpecifiedBottom(p.specified_bottom),
+	mPositioning(p.positioning),
 	mMinWidth(p.min_width),
 	mMinHeight(p.min_height),
 	mHeaderHeight(p.header_height),
@@ -547,10 +543,18 @@ LLFloater::~LLFloater()
 
 void LLFloater::storeRectControl()
 {
-	if( mRectControl.size() > 1 )
+	if (!mRectControl.empty())
 	{
 		getControlGroup()->setRect( mRectControl, getRect() );
 	}
+	if (!mPosXControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE)
+	{
+		getControlGroup()->setF32( mPosXControl, mPosition.mX );
+	}
+	if (!mPosYControl.empty() && mPositioning == LLFloaterEnums::POSITIONING_RELATIVE)
+	{
+		getControlGroup()->setF32( mPosYControl, mPosition.mY );
+	}
 }
 
 void LLFloater::storeVisibilityControl()
@@ -569,23 +573,6 @@ void LLFloater::storeDockStateControl()
 	}
 }
 
-LLRect LLFloater::getSavedRect() const
-{
-	LLRect rect;
-
-	if (mRectControl.size() > 1)
-	{
-		rect = getControlGroup()->getRect(mRectControl);
-	}
-
-	return rect;
-}
-
-bool LLFloater::hasSavedRect() const
-{
-	return !getSavedRect().isEmpty();
-}
-
 // static
 std::string LLFloater::getControlName(const std::string& name, const LLSD& key)
 {
@@ -863,7 +850,7 @@ void LLFloater::applyControlsAndPosition(LLFloater* other)
 	{
 		if (!applyRectControl())
 		{
-			applyPositioning(other);
+			applyPositioning(other, true);
 		}
 	}
 }
@@ -872,29 +859,68 @@ bool LLFloater::applyRectControl()
 {
 	bool saved_rect = false;
 
+	LLRect screen_rect = calcScreenRect();
+	mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
+	
 	LLFloater* last_in_group = LLFloaterReg::getLastFloaterInGroup(mInstanceName);
 	if (last_in_group && last_in_group != this)
 	{
 		// other floaters in our group, position ourselves relative to them and don't save the rect
 		mRectControl.clear();
-		mOpenPositioning = LLFloaterEnums::OPEN_POSITIONING_CASCADE_GROUP;
+		mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP;
 	}
-	else if (mRectControl.size() > 1)
+	else
 	{
-		// If we have a saved rect, use it
-		const LLRect& rect = getControlGroup()->getRect(mRectControl);
-		saved_rect = rect.notEmpty();
-		if (saved_rect)
+		bool rect_specified = false;
+		if (!mRectControl.empty())
 		{
-			setOrigin(rect.mLeft, rect.mBottom);
-
-			if (mResizable)
+			// If we have a saved rect, use it
+			const LLRect& rect = getControlGroup()->getRect(mRectControl);
+			if (rect.notEmpty()) saved_rect = true;
+			if (saved_rect)
 			{
-				reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
+				setOrigin(rect.mLeft, rect.mBottom);
+
+				if (mResizable)
+				{
+					reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
+				}
+				mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+				LLRect screen_rect = calcScreenRect();
+				mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
+				rect_specified = true;
 			}
 		}
+
+		LLControlVariablePtr x_control = getControlGroup()->getControl(mPosXControl);
+		LLControlVariablePtr y_control = getControlGroup()->getControl(mPosYControl);
+		if (x_control.notNull() 
+			&& y_control.notNull()
+			&& !x_control->isDefault()
+			&& !y_control->isDefault())
+		{
+			mPosition.mX = x_control->getValue().asReal();
+			mPosition.mY = y_control->getValue().asReal();
+			mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+			applyRelativePosition();
+
+			saved_rect = true;
+		}
+
+		// remember updated position
+		if (rect_specified)
+		{
+			storeRectControl();
+		}
 	}
 
+	if (saved_rect)
+	{
+		// propagate any derived positioning data back to settings file
+		storeRectControl();
+	}
+
+
 	return saved_rect;
 }
 
@@ -911,50 +937,56 @@ bool LLFloater::applyDockState()
 	return docked;
 }
 
-void LLFloater::applyPositioning(LLFloater* other)
+void LLFloater::applyPositioning(LLFloater* other, bool on_open)
 {
 	// Otherwise position according to the positioning code
-	switch (mOpenPositioning)
+	switch (mPositioning)
 	{
-	case LLFloaterEnums::OPEN_POSITIONING_CENTERED:
+	case LLFloaterEnums::POSITIONING_CENTERED:
 		center();
 		break;
 
-	case LLFloaterEnums::OPEN_POSITIONING_SPECIFIED:
-		{
-			// Translate relative to snap rect
-			setOrigin(mSpecifiedLeft, mSpecifiedBottom);
-			const LLRect& snap_rect = gFloaterView->getSnapRect();
-			translate(snap_rect.mLeft, snap_rect.mBottom);
-			translateIntoRect(snap_rect);
-		}
+	case LLFloaterEnums::POSITIONING_SPECIFIED:
 		break;
 
-	case LLFloaterEnums::OPEN_POSITIONING_CASCADE_GROUP:
-	case LLFloaterEnums::OPEN_POSITIONING_CASCADING:
-		if (other != NULL && other != this)
+	case LLFloaterEnums::POSITIONING_CASCADING:
+		if (!on_open)
 		{
-			stackWith(*other);
+			applyRelativePosition();
 		}
-		else
+		// fall through
+	case LLFloaterEnums::POSITIONING_CASCADE_GROUP:
+		if (on_open)
 		{
-			static const U32 CASCADING_FLOATER_HOFFSET = 0;
-			static const U32 CASCADING_FLOATER_VOFFSET = 0;
+			if (other != NULL && other != this)
+			{
+				stackWith(*other);
+			}
+			else
+			{
+				static const U32 CASCADING_FLOATER_HOFFSET = 0;
+				static const U32 CASCADING_FLOATER_VOFFSET = 0;
 			
-			const LLRect& snap_rect = gFloaterView->getSnapRect();
+				const LLRect& snap_rect = gFloaterView->getSnapRect();
 
-			const S32 horizontal_offset = CASCADING_FLOATER_HOFFSET;
-			const S32 vertical_offset = snap_rect.getHeight() - CASCADING_FLOATER_VOFFSET;
+				const S32 horizontal_offset = CASCADING_FLOATER_HOFFSET;
+				const S32 vertical_offset = snap_rect.getHeight() - CASCADING_FLOATER_VOFFSET;
 
-			S32 rect_height = getRect().getHeight();
-			setOrigin(horizontal_offset, vertical_offset - rect_height);
+				S32 rect_height = getRect().getHeight();
+				setOrigin(horizontal_offset, vertical_offset - rect_height);
 
-			translate(snap_rect.mLeft, snap_rect.mBottom);
-			translateIntoRect(snap_rect);
+				translate(snap_rect.mLeft, snap_rect.mBottom);
+			}
+			setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
 		}
 		break;
 
-	case LLFloaterEnums::OPEN_POSITIONING_NONE:
+	case LLFloaterEnums::POSITIONING_RELATIVE:
+		{
+			applyRelativePosition();
+
+			break;
+		}
 	default:
 		// Do nothing
 		break;
@@ -1072,7 +1104,9 @@ void LLFloater::handleReshape(const LLRect& new_rect, bool by_user)
 	if (by_user && !isMinimized())
 	{
 		storeRectControl();
-		mOpenPositioning = LLFloaterEnums::OPEN_POSITIONING_NONE;
+		mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
+		LLRect screen_rect = calcScreenRect();
+		mPosition = LLCoordGL(screen_rect.getCenterX(), screen_rect.getCenterY()).convert();
 	}
 
 	// if not minimized, adjust all snapped dependents to new shape
@@ -1250,6 +1284,7 @@ void LLFloater::setMinimized(BOOL minimize)
 
 		// Reshape *after* setting mMinimized
 		reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE );
+		applyPositioning(NULL, false);
 	}
 
 	make_ui_sound("UISndWindowClose");
@@ -1590,7 +1625,7 @@ void LLFloater::setDocked(bool docked, bool pop_on_undock)
 		if (mDocked)
 		{
 			setMinimized(FALSE);
-			mOpenPositioning = LLFloaterEnums::OPEN_POSITIONING_NONE;
+			mPositioning = LLFloaterEnums::POSITIONING_RELATIVE;
 		}
 
 		updateTitleButtons();
@@ -1624,7 +1659,7 @@ void LLFloater::onClickTearOff(LLFloater* self)
 		self->openFloater(self->getKey());
 		
 		// only force position for floaters that don't have that data saved
-		if (self->mRectControl.size() <= 1)
+		if (self->mRectControl.empty())
 		{
 			new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight());
 			self->setRect(new_rect);
@@ -2164,19 +2199,14 @@ LLFloaterView::LLFloaterView (const Params& p)
 	mSnapOffsetBottom(0),
 	mSnapOffsetRight(0)
 {
+	mSnapView = getHandle();
 }
 
 // By default, adjust vertical.
 void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
 {
-	S32 old_right = mLastSnapRect.mRight;
-	S32 old_top = mLastSnapRect.mTop;
-
 	LLView::reshape(width, height, called_from_parent);
 
-	S32 new_right = getSnapRect().mRight;
-	S32 new_top = getSnapRect().mTop;
-
 	mLastSnapRect = getSnapRect();
 
 	for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
@@ -2189,35 +2219,39 @@ void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
 			continue;
 		}
 
-		if (!floaterp->isMinimized())
+		if (!floaterp->isMinimized() && floaterp->getCanDrag())
 		{
-			LLRect r = floaterp->getRect();
+			LLRect old_rect = floaterp->getRect();
+			floaterp->applyPositioning(NULL, false);
+			LLRect new_rect = floaterp->getRect();
 
-			// Compute absolute distance from each edge of screen
-			S32 left_offset = llabs(r.mLeft - 0);
-			S32 right_offset = llabs(old_right - r.mRight);
+			//LLRect r = floaterp->getRect();
 
-			S32 top_offset = llabs(old_top - r.mTop);
-			S32 bottom_offset = llabs(r.mBottom - 0);
+			//// Compute absolute distance from each edge of screen
+			//S32 left_offset = llabs(r.mLeft - 0);
+			//S32 right_offset = llabs(old_right - r.mRight);
 
-			S32 translate_x = 0;
-			S32 translate_y = 0;
+			//S32 top_offset = llabs(old_top - r.mTop);
+			//S32 bottom_offset = llabs(r.mBottom - 0);
 
-			if (left_offset > right_offset)
-			{
-				translate_x = new_right - old_right;
-			}
+			S32 translate_x = new_rect.mLeft - old_rect.mLeft;
+			S32 translate_y = new_rect.mBottom - old_rect.mBottom;
 
-			if (top_offset < bottom_offset)
-			{
-				translate_y = new_top - old_top;
-			}
+			//if (left_offset > right_offset)
+			//{
+			//	translate_x = new_right - old_right;
+			//}
+
+			//if (top_offset < bottom_offset)
+			//{
+			//	translate_y = new_top - old_top;
+			//}
 
 			// don't reposition immovable floaters
-			if (floaterp->getCanDrag())
-			{
-				floaterp->translate(translate_x, translate_y);
-			}
+			//if (floaterp->getCanDrag())
+			//{
+			//	floaterp->translate(translate_x, translate_y);
+			//}
 			BOOST_FOREACH(LLHandle<LLFloater> dependent_floater, floaterp->mDependents)
 			{
 				if (dependent_floater.get())
@@ -2913,9 +2947,11 @@ void LLFloater::setInstanceName(const std::string& name)
 		std::string ctrl_name = getControlName(mInstanceName, mKey);
 
 		// save_rect and save_visibility only apply to registered floaters
-		if (!mRectControl.empty())
+		if (mSaveRect)
 		{
 			mRectControl = LLFloaterReg::declareRectControl(ctrl_name);
+			mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name);
+			mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name);
 		}
 		if (!mVisibilityControl.empty())
 		{
@@ -2972,7 +3008,10 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
 	LLPanel::initFromParams(p);
 
 	// override any follows flags
-	setFollows(FOLLOWS_NONE);
+	if (mPositioning != LLFloaterEnums::POSITIONING_SPECIFIED)
+	{
+		setFollows(FOLLOWS_NONE);
+	}
 
 	mTitle = p.title;
 	mShortTitle = p.short_title;
@@ -2991,14 +3030,9 @@ void LLFloater::initFromParams(const LLFloater::Params& p)
 	mSingleInstance = p.single_instance;
 	mReuseInstance = p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance;
 
-	mOpenPositioning = p.open_positioning;
-	mSpecifiedLeft = p.specified_left;
-	mSpecifiedBottom = p.specified_bottom;
+	mPositioning = p.positioning;
 
-	if (p.save_rect && mRectControl.empty())
-	{
-		mRectControl = "t"; // flag to build mRectControl name once mInstanceName is set
-	}
+	mSaveRect = p.save_rect;
 	if (p.save_visibility)
 	{
 		mVisibilityControl = "t"; // flag to build mVisibilityControl name once mInstanceName is set
@@ -3113,7 +3147,7 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str
 		params.rect.left.set(0);
 	}
 	params.from_xui = true;
-	applyXUILayout(params, parent);
+	applyXUILayout(params, parent, parent == gFloaterView ? gFloaterView->getSnapRect() : parent->getLocalRect());
  	initFromParams(params);
 
 	initFloater(params);
@@ -3272,8 +3306,27 @@ void LLFloater::stackWith(LLFloater& other)
 	next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight());
 	
 	setShape(next_rect);
+
+	if (!other.getHost())
+	{
+		other.mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP;
+		other.setFollows(FOLLOWS_LEFT | FOLLOWS_TOP);
+	}
 }
 
+void LLFloater::applyRelativePosition()
+{
+	LLRect snap_rect = gFloaterView->getSnapRect();
+	LLRect floater_view_screen_rect = gFloaterView->calcScreenRect();
+	snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom);
+	LLRect floater_screen_rect = calcScreenRect();
+
+	LLCoordGL new_center = mPosition.convert();
+	LLCoordGL cur_center(floater_screen_rect.getCenterX(), floater_screen_rect.getCenterY());
+	translate(new_center.mX - cur_center.mX, new_center.mY - cur_center.mY);
+}
+
+
 LLCoordFloater::LLCoordFloater(F32 x, F32 y, LLFloater& floater)
 :	coord_t((S32)x, (S32)y)
 {
@@ -3306,9 +3359,12 @@ bool LLCoordFloater::operator==(const LLCoordFloater& other) const
 
 LLCoordCommon LL_COORD_FLOATER::convertToCommon() const
 {
-	const LLCoordFloater& self = static_cast<const LLCoordFloater&>(*this);
+	const LLCoordFloater& self = static_cast<const LLCoordFloater&>(LLCoordFloater::getTypedCoords(*this));
 
 	LLRect snap_rect = gFloaterView->getSnapRect();
+	LLRect floater_view_screen_rect = gFloaterView->calcScreenRect();
+	snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom);
+
 	LLFloater* floaterp = mFloater.get();
 	S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0;
 	S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0;
@@ -3348,8 +3404,12 @@ LLCoordCommon LL_COORD_FLOATER::convertToCommon() const
 
 void LL_COORD_FLOATER::convertFromCommon(const LLCoordCommon& from)
 {
-	LLCoordFloater& self = static_cast<LLCoordFloater&>(*this);
+	LLCoordFloater& self = static_cast<LLCoordFloater&>(LLCoordFloater::getTypedCoords(*this));
 	LLRect snap_rect = gFloaterView->getSnapRect();
+	LLRect floater_view_screen_rect = gFloaterView->calcScreenRect();
+	snap_rect.translate(floater_view_screen_rect.mLeft, floater_view_screen_rect.mBottom);
+
+
 	LLFloater* floaterp = mFloater.get();
 	S32 floater_width = floaterp ? floaterp->getRect().getWidth() : 0;
 	S32 floater_height = floaterp ? floaterp->getRect().getHeight() : 0;
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index a7cc9ae961782ef39f3f1e4c1439d44fc9adc88a..64d6dcea0444afeffcd7de5758e1a40fe3953884 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -64,12 +64,12 @@ namespace LLFloaterEnums
 {
 	enum EOpenPositioning
 	{
-		OPEN_POSITIONING_NONE,
-		OPEN_POSITIONING_CASCADING,
-		OPEN_POSITIONING_CASCADE_GROUP,
-		OPEN_POSITIONING_CENTERED,
-		OPEN_POSITIONING_SPECIFIED,
-		OPEN_POSITIONING_COUNT
+		POSITIONING_RELATIVE,
+		POSITIONING_CASCADING,
+		POSITIONING_CASCADE_GROUP,
+		POSITIONING_CENTERED,
+		POSITIONING_SPECIFIED,
+		POSITIONING_COUNT
 	};
 }
 
@@ -163,10 +163,7 @@ public:
 								can_dock,
 								show_title;
 		
-		Optional<LLFloaterEnums::EOpenPositioning>	open_positioning;
-		Optional<S32>								specified_left;
-		Optional<S32>								specified_bottom;
-
+		Optional<LLFloaterEnums::EOpenPositioning>	positioning;
 		
 		Optional<S32>			header_height,
 								legacy_header_height; // HACK see initFromXML()
@@ -272,8 +269,6 @@ public:
 	BOOL			isResizable() const				{ return mResizable; }
 	void			setResizeLimits( S32 min_width, S32 min_height );
 	void			getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; }
-	LLRect			getSavedRect() const;
-	bool			hasSavedRect() const;
 
 	static std::string		getControlName(const std::string& name, const LLSD& key);
 	static LLControlGroup*	getControlGroup();
@@ -355,7 +350,7 @@ public:
 		
 	void			enableResizeCtrls(bool enable, bool width = true, bool height = true);
 
-	bool			isPositioning(LLFloaterEnums::EOpenPositioning p) const { return (p == mOpenPositioning); }
+	bool			isPositioning(LLFloaterEnums::EOpenPositioning p) const { return (p == mPositioning); }
 protected:
 	void			applyControlsAndPosition(LLFloater* other);
 
@@ -363,7 +358,9 @@ protected:
 
 	virtual bool	applyRectControl();
 	bool			applyDockState();
-	void			applyPositioning(LLFloater* other);
+	void			applyPositioning(LLFloater* other, bool on_open);
+	void			applyRelativePosition();
+
 	void			storeRectControl();
 	void			storeVisibilityControl();
 	void			storeDockStateControl();
@@ -427,7 +424,10 @@ public:
 	commit_signal_t* mMinimizeSignal;
 
 protected:
+	bool			mSaveRect;
 	std::string		mRectControl;
+	std::string		mPosXControl;
+	std::string		mPosYControl;
 	std::string		mVisibilityControl;
 	std::string		mDocStateControl;
 	LLSD			mKey;				// Key used for retrieving instances; set (for now) by LLFLoaterReg
@@ -453,9 +453,7 @@ private:
 	BOOL			mDragOnLeft;
 	BOOL			mResizable;
 
-	LLFloaterEnums::EOpenPositioning	mOpenPositioning;
-	S32									mSpecifiedLeft;
-	S32									mSpecifiedBottom;
+	LLFloaterEnums::EOpenPositioning	mPositioning;
 	LLCoordFloater	mPosition;
 	
 	S32				mMinWidth;
diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp
index e144b68f5ebb00ae3df2316233d359ff0092f8f9..9115eb71740760af6522fb80f6170b31f46b595a 100644
--- a/indra/llui/llfloaterreg.cpp
+++ b/indra/llui/llfloaterreg.cpp
@@ -96,7 +96,9 @@ LLFloater* LLFloaterReg::getLastFloaterCascading()
 		{
 			LLFloater* inst = *iter;
 
-			if (inst->getVisible() && inst->isPositioning(LLFloaterEnums::OPEN_POSITIONING_CASCADING))
+			if (inst->getVisible() 
+				&& (inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADING)
+					|| inst->isPositioning(LLFloaterEnums::POSITIONING_CASCADE_GROUP)))
 			{
 				if (candidate_rect.mTop > inst->getRect().mTop)
 				{
@@ -358,9 +360,7 @@ void LLFloaterReg::restoreVisibleInstances()
 //static
 std::string LLFloaterReg::getRectControlName(const std::string& name)
 {
-	std::string res = std::string("floater_rect_") + name;
-	LLStringUtil::replaceChar( res, ' ', '_' );
-	return res;
+	return std::string("floater_rect_") + getBaseControlName(name);
 }
 
 //static
@@ -368,19 +368,48 @@ std::string LLFloaterReg::declareRectControl(const std::string& name)
 {
 	std::string controlname = getRectControlName(name);
 	LLFloater::getControlGroup()->declareRect(controlname, LLRect(),
-												 llformat("Window Position and Size for %s", name.c_str()),
+												 llformat("Window Size for %s", name.c_str()),
 												 TRUE);
 	return controlname;
 }
 
+std::string LLFloaterReg::declarePosXControl(const std::string& name)
+{
+	std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_x";
+	LLFloater::getControlGroup()->declareF32(controlname, 
+											10.f,
+											llformat("Window X Position for %s", name.c_str()),
+											TRUE);
+	return controlname;
+}
+
+std::string LLFloaterReg::declarePosYControl(const std::string& name)
+{
+	std::string controlname = std::string("floater_pos_") + getBaseControlName(name) + "_y";
+	LLFloater::getControlGroup()->declareF32(controlname,
+											10.f,
+											llformat("Window Y Position for %s", name.c_str()),
+											TRUE);
+
+	return controlname;
+}
+
+
 //static
 std::string LLFloaterReg::getVisibilityControlName(const std::string& name)
 {
-	std::string res = std::string("floater_vis_") + name;
+	return std::string("floater_vis_") + getBaseControlName(name);
+}
+
+//static 
+std::string LLFloaterReg::getBaseControlName(const std::string& name)
+{
+	std::string res(name);
 	LLStringUtil::replaceChar( res, ' ', '_' );
 	return res;
 }
 
+
 //static
 std::string LLFloaterReg::declareVisibilityControl(const std::string& name)
 {
diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h
index 534cf8b40ab882a85629aa155af26b1b2031672b..a1e1f8a98835ffa6bd8fd257a8a5799268b5e6ce 100644
--- a/indra/llui/llfloaterreg.h
+++ b/indra/llui/llfloaterreg.h
@@ -115,9 +115,11 @@ public:
 	// Control Variables
 	static std::string getRectControlName(const std::string& name);
 	static std::string declareRectControl(const std::string& name);
+	static std::string declarePosXControl(const std::string& name);
+	static std::string declarePosYControl(const std::string& name);
 	static std::string getVisibilityControlName(const std::string& name);
 	static std::string declareVisibilityControl(const std::string& name);
-
+	static std::string getBaseControlName(const std::string& name);
 	static std::string declareDockStateControl(const std::string& name);
 	static std::string getDockStateControlName(const std::string& name);
 
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index ae262f794ea80f13a526cf1364540530633798f2..4c730286da86a6b0a6dbbfca12216aa211db5f75 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -113,7 +113,26 @@ S32 LLLayoutPanel::getLayoutDim() const
 					? getRect().getWidth()
 					: getRect().getHeight()));
 }
- 
+
+S32 LLLayoutPanel::getTargetDim() const
+{
+	return mTargetDim;
+}
+
+void LLLayoutPanel::setTargetDim(S32 value)
+{
+	LLRect new_rect(getRect());
+	if (mOrientation == LLLayoutStack::HORIZONTAL)
+	{
+		new_rect.mRight = new_rect.mLeft + value;
+	}
+	else
+	{
+		new_rect.mTop = new_rect.mBottom + value;
+	}
+	setShape(new_rect, true);
+}
+
 S32 LLLayoutPanel::getVisibleDim() const
 {
 	F32 min_dim = getRelevantMinDim();
@@ -172,12 +191,15 @@ void LLLayoutPanel::handleReshape(const LLRect& new_rect, bool by_user)
 	LLLayoutStack* stackp = dynamic_cast<LLLayoutStack*>(getParent());
 	if (stackp)
 	{
-		stackp->mNeedsLayout = true;
 		if (by_user)
-		{
-			// tell layout stack to account for new shape
+		{	// tell layout stack to account for new shape
+			
+			// make sure that panels have already been auto resized
+			stackp->updateLayout();
+			// now apply requested size to panel
 			stackp->updatePanelRect(this, new_rect);
 		}
+		stackp->mNeedsLayout = true;
 	}
 	LLPanel::handleReshape(new_rect, by_user);
 }
@@ -241,7 +263,6 @@ void LLLayoutStack::draw()
 			drawChild(panelp, 0, 0, !clip_rect.isEmpty());
 		}
 	}
-	mAnimatedThisFrame = false;
 }
 
 void LLLayoutStack::removeChild(LLView* view)
@@ -310,7 +331,7 @@ void LLLayoutStack::updateLayout()
 
 	if (!mNeedsLayout) return;
 
-	bool animation_in_progress = animatePanels();
+	bool continue_animating = animatePanels();
 	F32 total_visible_fraction = 0.f;
 	S32 space_to_distribute = (mOrientation == HORIZONTAL)
 							? getRect().getWidth()
@@ -415,7 +436,7 @@ void LLLayoutStack::updateLayout()
 
 	// clear animation flag at end, since panel resizes will set it
 	// and leave it set if there is any animation in progress
-	mNeedsLayout = animation_in_progress;
+	mNeedsLayout = continue_animating;
 } // end LLLayoutStack::updateLayout
 
 LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const
@@ -488,6 +509,7 @@ void LLLayoutStack::updateClass()
 	for (instance_iter it = beginInstances(); it != endInstances(); ++it)
 	{
 		it->updateLayout();
+		it->mAnimatedThisFrame = false;
 	}
 }
 
@@ -557,7 +579,7 @@ void LLLayoutStack::normalizeFractionalSizes()
 
 bool LLLayoutStack::animatePanels()
 {
-	bool animation_in_progress = false;
+	bool continue_animating = false;
 	
 	//
 	// animate visibility
@@ -577,14 +599,15 @@ bool LLLayoutStack::animatePanels()
 					}
 				}
 				
-				animation_in_progress = true;
+				mAnimatedThisFrame = true;
+				continue_animating = true;
 			}
 			else
 			{
 				if (panelp->mVisibleAmt != 1.f)
 				{
 					panelp->mVisibleAmt = 1.f;
-					animation_in_progress = true;
+					mAnimatedThisFrame = true;
 				}
 			}
 		}
@@ -601,14 +624,15 @@ bool LLLayoutStack::animatePanels()
 					}
 				}
 
-				animation_in_progress = true;
+				continue_animating = true;
+				mAnimatedThisFrame = true;
 			}
 			else
 			{
 				if (panelp->mVisibleAmt != 0.f)
 				{
 					panelp->mVisibleAmt = 0.f;
-					animation_in_progress = true;
+					mAnimatedThisFrame = true;
 				}
 			}
 		}
@@ -616,22 +640,31 @@ bool LLLayoutStack::animatePanels()
 		F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f;
 		if (panelp->mCollapseAmt != collapse_state)
 		{
-			if (!mAnimatedThisFrame)
+			if (mAnimate)
 			{
-				panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
-			}
-			animation_in_progress = true;
+				if (!mAnimatedThisFrame)
+				{
+					panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLCriticalDamp::getInterpolant(mCloseTimeConstant));
+				}
 			
-			if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f)
+				if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f)
+				{
+					panelp->mCollapseAmt = collapse_state;
+				}
+
+				mAnimatedThisFrame = true;
+				continue_animating = true;
+			}
+			else
 			{
 				panelp->mCollapseAmt = collapse_state;
+				mAnimatedThisFrame = true;
 			}
 		}
 	}
 
-	mAnimatedThisFrame = true;
-
-	return animation_in_progress;
+	if (mAnimatedThisFrame) mNeedsLayout = true;
+	return continue_animating;
 }
 
 void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& new_rect )
diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h
index d32caec5f92ac3a40a5c3c7237624264c71e1b93..648cd5fdce202d209758295b4f72da4228ff1569 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -155,6 +155,8 @@ public:
 	void setVisible(BOOL visible);
 
 	S32 getLayoutDim() const;
+	S32 getTargetDim() const;
+	void setTargetDim(S32 value);
 	S32 getMinDim() const { return llmax(0, mMinDim); }
 	void setMinDim(S32 value) { mMinDim = value; }
 
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 7e84814c518ac965ab3afafd81e92f29d464c717..d0fbf4b91370d6c7f46c2e3addb9306cca2cbaa5 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -1047,7 +1047,7 @@ void LLLineEditor::cut()
 		// Prepare for possible rollback
 		LLLineEditorRollback rollback( this );
 
-		gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
+		LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
 		deleteSelection();
 
 		// Validate new string and rollback the if needed.
@@ -1078,13 +1078,13 @@ void LLLineEditor::copy()
 	{
 		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
 		S32 length = llabs( mSelectionStart - mSelectionEnd );
-		gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
+		LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length );
 	}
 }
 
 BOOL LLLineEditor::canPaste() const
 {
-	return !mReadOnly && gClipboard.canPasteString(); 
+	return !mReadOnly && LLClipboard::instance().isTextAvailable(); 
 }
 
 void LLLineEditor::paste()
@@ -1115,14 +1115,7 @@ void LLLineEditor::pasteHelper(bool is_primary)
 	if (can_paste_it)
 	{
 		LLWString paste;
-		if (is_primary)
-		{
-			paste = gClipboard.getPastePrimaryWString();
-		}
-		else 
-		{
-			paste = gClipboard.getPasteWString();
-		}
+		LLClipboard::instance().pasteFromClipboard(paste, is_primary);
 
 		if (!paste.empty())
 		{
@@ -1209,13 +1202,13 @@ void LLLineEditor::copyPrimary()
 	{
 		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
 		S32 length = llabs( mSelectionStart - mSelectionEnd );
-		gClipboard.copyFromPrimarySubstring( mText.getWString(), left_pos, length );
+		LLClipboard::instance().copyToClipboard( mText.getWString(), left_pos, length, true);
 	}
 }
 
 BOOL LLLineEditor::canPastePrimary() const
 {
-	return !mReadOnly && gClipboard.canPastePrimaryString(); 
+	return !mReadOnly && LLClipboard::instance().isTextAvailable(true); 
 }
 
 void LLLineEditor::updatePrimary()
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index 8aa548b9749f4446e7c68f072f93583346fef815..09480968a67cd39d63a83e641ef712968e7fec73 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -399,6 +399,7 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par
 :	mName(p.name),
 	mType(p.type),
 	mMessage(p.value),
+	mFooter(p.footer.value),
 	mLabel(p.label),
 	mIcon(p.icon),
 	mURL(p.url.value),
@@ -870,6 +871,16 @@ std::string LLNotification::getMessage() const
 	return message;
 }
 
+std::string LLNotification::getFooter() const
+{
+	if (!mTemplatep)
+		return std::string();
+
+	std::string footer = mTemplatep->mFooter;
+	LLStringUtil::format(footer, mSubstitutions);
+	return footer;
+}
+
 std::string LLNotification::getLabel() const
 {
 	std::string label = mTemplatep->mLabel;
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index 3df2efcac3a66b63d7dbb092091f84c377bccdf1..4ae02b943fa415c691005db26ee61b753ab37ce4 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -509,6 +509,7 @@ public:
 
 	std::string getType() const;
 	std::string getMessage() const;
+	std::string getFooter() const;
 	std::string getLabel() const;
 	std::string getURL() const;
 	S32 getURLOption() const;
diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h
index fb50c9c1233b8de79e8d3e27c62b94dce617a35a..72973789db246efdb3c5eab3194ee6c6c4a58978 100644
--- a/indra/llui/llnotificationtemplate.h
+++ b/indra/llui/llnotificationtemplate.h
@@ -167,6 +167,17 @@ struct LLNotificationTemplate
 		{}
 	};
 
+	struct Footer : public LLInitParam::Block<Footer>
+	{
+		Mandatory<std::string> value;
+
+		Footer()
+		:	value("value")
+		{
+			addSynonym(value, "");
+		}
+	};
+
 	struct Params : public LLInitParam::Block<Params>
 	{
 		Mandatory<std::string>			name;
@@ -184,7 +195,8 @@ struct LLNotificationTemplate
 		Optional<FormRef>				form_ref;
 		Optional<ENotificationPriority, 
 			NotificationPriorityValues> priority;
-		Multiple<Tag>		tags;
+		Multiple<Tag>					tags;
+		Optional<Footer>				footer;
 
 
 		Params()
@@ -202,7 +214,8 @@ struct LLNotificationTemplate
 			url("url"),
 			unique("unique"),
 			form_ref(""),
-			tags("tag")
+			tags("tag"),
+			footer("footer")
 		{}
 
 	};
@@ -231,6 +244,8 @@ struct LLNotificationTemplate
     // The text used to display the notification. Replaceable parameters
     // are enclosed in square brackets like this [].
     std::string mMessage;
+    // The text used to display the notification, but under the form.
+    std::string mFooter;
 	// The label for the notification; used for 
 	// certain classes of notification (those with a window and a window title). 
 	// Also used when a notification pops up underneath the current one.
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
index 20bed050ada1f67d11bca643b3de360070e0635b..9b7e30bb048733ce8e8b193dc40918a1571f1c36 100644
--- a/indra/llui/llscrollcontainer.cpp
+++ b/indra/llui/llscrollcontainer.cpp
@@ -389,10 +389,17 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height
 		{
 			*show_h_scrollbar = TRUE;
 			*visible_height -= scrollbar_size;
-			// Note: Do *not* recompute *show_v_scrollbar here because with
-			// the (- scrollbar_size) we just did we will always add a vertical scrollbar
-			// even if the height of the items is actually less than the visible size.
-			// Fear not though: there's enough calcVisibleSize() calls to add a vertical slider later.
+
+			// The view inside the scroll container should not be extended
+			// to container's full height to ensure the correct computation
+			// of *show_v_scrollbar after subtracting horizontal scrollbar_size.
+
+			// Must retest now that visible_height has changed
+			if( !*show_v_scrollbar && ((doc_height - *visible_height) > 1) )
+			{
+				*show_v_scrollbar = TRUE;
+				*visible_width -= scrollbar_size;
+			}
 		}
 	}
 }
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
index 3aa79cc255b47d5bd4d7c158fca21f92fe6d1d65..d87c95b3d759f2348b6657c833c13c398f9cbbb4 100644
--- a/indra/llui/llscrollcontainer.h
+++ b/indra/llui/llscrollcontainer.h
@@ -91,7 +91,7 @@ public:
 	void			setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; }
 	LLRect			getVisibleContentRect();
 	LLRect			getContentWindowRect();
-	const LLRect&	getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
+	virtual const LLRect	getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; }
 	void			pageUp(S32 overlap = 0);
 	void			pageDown(S32 overlap = 0);
 	void			goToTop();
@@ -116,6 +116,9 @@ public:
 	
 	bool autoScroll(S32 x, S32 y);
 
+protected:
+	LLView*		mScrolledView;
+
 private:
 	// internal scrollbar handlers
 	virtual void scrollHorizontal( S32 new_pos );
@@ -124,7 +127,6 @@ private:
 	void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const;
 
 	LLScrollbar* mScrollbar[SCROLLBAR_COUNT];
-	LLView*		mScrolledView;
 	S32			mSize;
 	BOOL		mIsOpaque;
 	LLUIColor	mBackgroundColor;
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 466fac33ea3c3f1e876717e28630c4d3a35825f3..b3e1b63db58a4d788001458509c34c8aacc87b2e 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -2504,7 +2504,7 @@ void	LLScrollListCtrl::copy()
 	{
 		buffer += (*itor)->getContentsCSV() + "\n";
 	}
-	gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length());
+	LLClipboard::instance().copyToClipboard(utf8str_to_wstring(buffer), 0, buffer.length());
 }
 
 // virtual
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index ec4db14790521de961a946989b71761972d37901..04cce7878e4611170a8164aeecef09afce4f5791 100644
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -272,7 +272,7 @@ LLRect LLStatBar::getRequiredRect()
 	{
 		if (mDisplayHistory)
 		{
-			rect.mTop = 67;
+			rect.mTop = 35 + mStatp->getNumBins();
 		}
 		else
 		{
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 3409b6817d28340d0163709730f91af3d170a98e..9720dded6c658e5844fe04a949a5997b4060d742 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -1332,7 +1332,7 @@ void LLTextEditor::cut()
 	}
 	S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
 	S32 length = llabs( mSelectionStart - mSelectionEnd );
-	gClipboard.copyFromSubstring( getWText(), left_pos, length, mSourceID );
+	LLClipboard::instance().copyToClipboard( getWText(), left_pos, length);
 	deleteSelection( FALSE );
 
 	onKeyStroke();
@@ -1352,12 +1352,12 @@ void LLTextEditor::copy()
 	}
 	S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
 	S32 length = llabs( mSelectionStart - mSelectionEnd );
-	gClipboard.copyFromSubstring(getWText(), left_pos, length, mSourceID);
+	LLClipboard::instance().copyToClipboard(getWText(), left_pos, length);
 }
 
 BOOL LLTextEditor::canPaste() const
 {
-	return !mReadOnly && gClipboard.canPasteString();
+	return !mReadOnly && LLClipboard::instance().isTextAvailable();
 }
 
 // paste from clipboard
@@ -1393,16 +1393,8 @@ void LLTextEditor::pasteHelper(bool is_primary)
 		return;
 	}
 
-	LLUUID source_id;
 	LLWString paste;
-	if (is_primary)
-	{
-		paste = gClipboard.getPastePrimaryWString(&source_id);
-	}
-	else 
-	{
-		paste = gClipboard.getPasteWString(&source_id);
-	}
+	LLClipboard::instance().pasteFromClipboard(paste, is_primary);
 
 	if (paste.empty())
 	{
@@ -1475,12 +1467,12 @@ void LLTextEditor::copyPrimary()
 	}
 	S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
 	S32 length = llabs( mSelectionStart - mSelectionEnd );
-	gClipboard.copyFromPrimarySubstring(getWText(), left_pos, length, mSourceID);
+	LLClipboard::instance().copyToClipboard(getWText(), left_pos, length, true);
 }
 
 BOOL LLTextEditor::canPastePrimary() const
 {
-	return !mReadOnly && gClipboard.canPastePrimaryString();
+	return !mReadOnly && LLClipboard::instance().isTextAvailable(true);
 }
 
 void LLTextEditor::updatePrimary()
diff --git a/indra/llxuixml/lltrans.cpp b/indra/llui/lltrans.cpp
similarity index 100%
rename from indra/llxuixml/lltrans.cpp
rename to indra/llui/lltrans.cpp
diff --git a/indra/llxuixml/lltrans.h b/indra/llui/lltrans.h
similarity index 100%
rename from indra/llxuixml/lltrans.h
rename to indra/llui/lltrans.h
diff --git a/indra/llxuixml/lluicolor.cpp b/indra/llui/lluicolor.cpp
similarity index 100%
rename from indra/llxuixml/lluicolor.cpp
rename to indra/llui/lluicolor.cpp
diff --git a/indra/llxuixml/lluicolor.h b/indra/llui/lluicolor.h
similarity index 100%
rename from indra/llxuixml/lluicolor.h
rename to indra/llui/lluicolor.h
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 421166dcd4dcf7a983118827e1c601be629a7b87..54843227b74c4f99263a5dcbd4d70d7dfba9629a 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -1835,7 +1835,10 @@ const LLCtrlQuery & LLView::getFocusRootsQuery()
 
 void	LLView::setShape(const LLRect& new_rect, bool by_user)
 {
-	handleReshape(new_rect, by_user);
+	if (new_rect != getRect())
+	{
+		handleReshape(new_rect, by_user);
+	}
 }
 
 void LLView::handleReshape(const LLRect& new_rect, bool by_user)
@@ -2225,145 +2228,163 @@ static bool get_last_child_rect(LLView* parent, LLRect *rect)
 }
 
 //static
-void LLView::applyXUILayout(LLView::Params& p, LLView* parent)
+void LLView::applyXUILayout(LLView::Params& p, LLView* parent, LLRect layout_rect)
 {
+	if (!parent) return;
+
 	const S32 VPAD = 4;
 	const S32 MIN_WIDGET_HEIGHT = 10;
 	
 	// *NOTE:  This will confuse export of floater/panel coordinates unless
 	// the default is also "topleft".  JC
-	if (p.layout().empty() && parent)
+	if (p.layout().empty())
 	{
 		p.layout = parent->getLayout();
 	}
 
-	if (parent)
+	if (layout_rect.isEmpty())
 	{
-		LLRect parent_rect = parent->getLocalRect();
-		// overwrite uninitialized rect params, using context
-		LLRect default_rect = parent->getLocalRect();
+		layout_rect = parent->getLocalRect();
+	}
 
-		bool layout_topleft = (p.layout() == "topleft");
+	// overwrite uninitialized rect params, using context
+	LLRect default_rect = parent->getLocalRect();
 
-		// convert negative or centered coordinates to parent relative values
-		// Note: some of this logic matches the logic in TypedParam<LLRect>::setValueFromBlock()
-		if (p.rect.left.isProvided() && p.rect.left < 0) p.rect.left = p.rect.left + parent_rect.getWidth();
-		if (p.rect.right.isProvided() && p.rect.right < 0) p.rect.right = p.rect.right + parent_rect.getWidth();
-		if (p.rect.bottom.isProvided() && p.rect.bottom < 0) p.rect.bottom = p.rect.bottom + parent_rect.getHeight();
-		if (p.rect.top.isProvided() && p.rect.top < 0) p.rect.top = p.rect.top + parent_rect.getHeight();
+	bool layout_topleft = (p.layout() == "topleft");
 
+	// convert negative or centered coordinates to parent relative values
+	// Note: some of this logic matches the logic in TypedParam<LLRect>::setValueFromBlock()
+	if (p.rect.left.isProvided()) 
+	{
+		p.rect.left = p.rect.left + ((p.rect.left >= 0) ? layout_rect.mLeft : layout_rect.mRight);
+	}
+	if (p.rect.right.isProvided())
+	{
+		p.rect.right = p.rect.right + ((p.rect.right >= 0) ? layout_rect.mLeft : layout_rect.mRight);
+	}
+	if (p.rect.bottom.isProvided()) 
+	{
+		p.rect.bottom = p.rect.bottom + ((p.rect.bottom >= 0) ? layout_rect.mBottom : layout_rect.mTop);
 		if (layout_topleft)
 		{
 			//invert top to bottom
-			if (p.rect.top.isProvided()) p.rect.top = parent_rect.getHeight() - p.rect.top;
-			if (p.rect.bottom.isProvided()) p.rect.bottom = parent_rect.getHeight() - p.rect.bottom;
+			p.rect.bottom = layout_rect.mBottom + layout_rect.mTop - p.rect.bottom;
 		}
-
-		// DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels
-		if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0)
+	}
+	if (p.rect.top.isProvided())
+	{
+		p.rect.top = p.rect.top + ((p.rect.top >= 0) ? layout_rect.mBottom : layout_rect.mTop);
+		if (layout_topleft)
 		{
-			p.rect.height = MIN_WIDGET_HEIGHT;
+			//invert top to bottom
+			p.rect.top = layout_rect.mBottom + layout_rect.mTop - p.rect.top;
 		}
+	}
+
+	// DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels
+	if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0)
+	{
+		p.rect.height = MIN_WIDGET_HEIGHT;
+	}
 
-		default_rect.translate(0, default_rect.getHeight());
+	default_rect.translate(0, default_rect.getHeight());
 
-		// If there was a recently constructed child, use its rectangle
-		get_last_child_rect(parent, &default_rect);
+	// If there was a recently constructed child, use its rectangle
+	get_last_child_rect(parent, &default_rect);
 
-		if (layout_topleft)
+	if (layout_topleft)
+	{
+		// Invert the sense of bottom_delta for topleft layout
+		if (p.bottom_delta.isProvided())
 		{
-			// Invert the sense of bottom_delta for topleft layout
-			if (p.bottom_delta.isProvided())
-			{
-				p.bottom_delta = -p.bottom_delta;
-			}
-			else if (p.top_pad.isProvided()) 
-			{
-				p.bottom_delta = -(p.rect.height + p.top_pad);
-			}
-			else if (p.top_delta.isProvided())
-			{
-				p.bottom_delta =
-					-(p.top_delta + p.rect.height - default_rect.getHeight());
-			}
-			else if (!p.left_delta.isProvided()
-					 && !p.left_pad.isProvided())
-			{
-				// set default position is just below last rect
-				p.bottom_delta.set(-(p.rect.height + VPAD), false);
-			}
-			else
-			{
-				p.bottom_delta.set(0, false);
-			}
-	
-			// default to same left edge
-			if (!p.left_delta.isProvided())
-			{
-				p.left_delta.set(0, false);
-			}
-			if (p.left_pad.isProvided())
-			{
-				// left_pad is based on prior widget's right edge
-				p.left_delta.set(p.left_pad + default_rect.getWidth(), false);
-			}
-			
-			default_rect.translate(p.left_delta, p.bottom_delta);				
+			p.bottom_delta = -p.bottom_delta;
 		}
-		else
-		{	
-			// set default position is just below last rect
-			if (!p.bottom_delta.isProvided())
-			{
-				p.bottom_delta.set(-(p.rect.height + VPAD), false);
-			}
-			if (!p.left_delta.isProvided())
-			{
-				p.left_delta.set(0, false);
-			}
-			default_rect.translate(p.left_delta, p.bottom_delta);
+		else if (p.top_pad.isProvided()) 
+		{
+			p.bottom_delta = -(p.rect.height + p.top_pad);
 		}
-
-		// this handles case where *both* x and x_delta are provided
-		// ignore x in favor of default x + x_delta
-		if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false);
-		if (p.left_delta.isProvided()) p.rect.left.set(0, false);
-
-		// selectively apply rectangle defaults, making sure that
-		// params are not flagged as having been "provided"
-		// as rect params are overconstrained and rely on provided flags
-		if (!p.rect.left.isProvided())
+		else if (p.top_delta.isProvided())
 		{
-			p.rect.left.set(default_rect.mLeft, false);
-			//HACK: get around the fact that setting a rect param component value won't invalidate the existing rect object value
-			p.rect.paramChanged(p.rect.left, true);
+			p.bottom_delta =
+				-(p.top_delta + p.rect.height - default_rect.getHeight());
 		}
-		if (!p.rect.bottom.isProvided())
+		else if (!p.left_delta.isProvided()
+					&& !p.left_pad.isProvided())
 		{
-			p.rect.bottom.set(default_rect.mBottom, false);
-			p.rect.paramChanged(p.rect.bottom, true);
+			// set default position is just below last rect
+			p.bottom_delta.set(-(p.rect.height + VPAD), false);
 		}
-		if (!p.rect.top.isProvided())
+		else
 		{
-			p.rect.top.set(default_rect.mTop, false);
-			p.rect.paramChanged(p.rect.top, true);
+			p.bottom_delta.set(0, false);
 		}
-		if (!p.rect.right.isProvided())
+	
+		// default to same left edge
+		if (!p.left_delta.isProvided())
 		{
-			p.rect.right.set(default_rect.mRight, false);
-			p.rect.paramChanged(p.rect.right, true);
-
+			p.left_delta.set(0, false);
 		}
-		if (!p.rect.width.isProvided())
+		if (p.left_pad.isProvided())
 		{
-			p.rect.width.set(default_rect.getWidth(), false);
-			p.rect.paramChanged(p.rect.width, true);
+			// left_pad is based on prior widget's right edge
+			p.left_delta.set(p.left_pad + default_rect.getWidth(), false);
 		}
-		if (!p.rect.height.isProvided())
+			
+		default_rect.translate(p.left_delta, p.bottom_delta);				
+	}
+	else
+	{	
+		// set default position is just below last rect
+		if (!p.bottom_delta.isProvided())
 		{
-			p.rect.height.set(default_rect.getHeight(), false);
-			p.rect.paramChanged(p.rect.height, true);
+			p.bottom_delta.set(-(p.rect.height + VPAD), false);
 		}
+		if (!p.left_delta.isProvided())
+		{
+			p.left_delta.set(0, false);
+		}
+		default_rect.translate(p.left_delta, p.bottom_delta);
+	}
+
+	// this handles case where *both* x and x_delta are provided
+	// ignore x in favor of default x + x_delta
+	if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false);
+	if (p.left_delta.isProvided()) p.rect.left.set(0, false);
+
+	// selectively apply rectangle defaults, making sure that
+	// params are not flagged as having been "provided"
+	// as rect params are overconstrained and rely on provided flags
+	if (!p.rect.left.isProvided())
+	{
+		p.rect.left.set(default_rect.mLeft, false);
+		//HACK: get around the fact that setting a rect param component value won't invalidate the existing rect object value
+		p.rect.paramChanged(p.rect.left, true);
+	}
+	if (!p.rect.bottom.isProvided())
+	{
+		p.rect.bottom.set(default_rect.mBottom, false);
+		p.rect.paramChanged(p.rect.bottom, true);
+	}
+	if (!p.rect.top.isProvided())
+	{
+		p.rect.top.set(default_rect.mTop, false);
+		p.rect.paramChanged(p.rect.top, true);
+	}
+	if (!p.rect.right.isProvided())
+	{
+		p.rect.right.set(default_rect.mRight, false);
+		p.rect.paramChanged(p.rect.right, true);
+
+	}
+	if (!p.rect.width.isProvided())
+	{
+		p.rect.width.set(default_rect.getWidth(), false);
+		p.rect.paramChanged(p.rect.width, true);
+	}
+	if (!p.rect.height.isProvided())
+	{
+		p.rect.height.set(default_rect.getHeight(), false);
+		p.rect.paramChanged(p.rect.height, true);
 	}
 }
 
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index fd19309a56e4706da4c4a54eae698f0174a08179..1c353495107b1f09426693ed6aabdd76195f7661 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -505,7 +505,7 @@ public:
 
 	// Set up params after XML load before calling new(),
 	// usually to adjust layout.
-	static void applyXUILayout(Params& p, LLView* parent);
+	static void applyXUILayout(Params& p, LLView* parent, LLRect layout_rect = LLRect());
 
 	// For re-export of floaters and panels, convert the coordinate system
 	// to be top-left based.
diff --git a/indra/llxuixml/llxuiparser.cpp b/indra/llui/llxuiparser.cpp
similarity index 100%
rename from indra/llxuixml/llxuiparser.cpp
rename to indra/llui/llxuiparser.cpp
diff --git a/indra/llxuixml/llxuiparser.h b/indra/llui/llxuiparser.h
similarity index 100%
rename from indra/llxuixml/llxuiparser.h
rename to indra/llui/llxuiparser.h
diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp
index c75df868918d1f67350fdd0d5c5ac711be0a0709..cb3b7abb1437a78c44f85f015e4396370e00690f 100644
--- a/indra/llui/tests/llurlentry_stub.cpp
+++ b/indra/llui/tests/llurlentry_stub.cpp
@@ -105,28 +105,6 @@ LLStyle::Params::Params()
 
 namespace LLInitParam
 {
-	Param::Param(BaseBlock* enclosing_block)
-	:	mIsProvided(false)
-	{
-		const U8* my_addr = reinterpret_cast<const U8*>(this);
-		const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
-		mEnclosingBlockOffset = (U16)(my_addr - block_addr);
-	}
-
-	void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptorPtr in_param, const char* char_name){}
-	void BaseBlock::addSynonym(Param& param, const std::string& synonym) {}
-	param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;}
-	
-	void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
-	{
-		descriptor.mCurrentBlockPtr = this;
-	}
-	bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name){ return true; }
-	void BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const LLInitParam::BaseBlock* diff_block) const {}
-	bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_value, S32 max_value) const { return true; }
-	bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; }
-	bool BaseBlock::validateBlock(bool emit_errors) const { return true; }
-
 	ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color)
 	:	super_t(color)
 	{}
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index c1fb050206aadf943a43cedcc7b5f9c46a7b6985..8f0a48018fb26132f0ca1b5257a1110850dce4d3 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -70,21 +70,6 @@ S32 LLUIImage::getHeight() const
 	return 0;
 }
 
-namespace LLInitParam
-{
-	BlockDescriptor::BlockDescriptor() {}
-	ParamDescriptor::ParamDescriptor(param_handle_t p, 
-						merge_func_t merge_func, 
-						deserialize_func_t deserialize_func, 
-						serialize_func_t serialize_func,
-						validation_func_t validation_func,
-						inspect_func_t inspect_func,
-						S32 min_count,
-						S32 max_count){}
-	ParamDescriptor::~ParamDescriptor() {}
-
-}
-
 namespace tut
 {
 	struct LLUrlEntryData
diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp
index 7183413463fabf21b587d1fc2446a0b7de6dbd61..963473c92ab0d2f18c7bcb47a9ff7a7f1a793cf5 100644
--- a/indra/llui/tests/llurlmatch_test.cpp
+++ b/indra/llui/tests/llurlmatch_test.cpp
@@ -63,40 +63,6 @@ S32 LLUIImage::getHeight() const
 
 namespace LLInitParam
 {
-	BlockDescriptor::BlockDescriptor() {}
-	ParamDescriptor::ParamDescriptor(param_handle_t p, 
-						merge_func_t merge_func, 
-						deserialize_func_t deserialize_func, 
-						serialize_func_t serialize_func,
-						validation_func_t validation_func,
-						inspect_func_t inspect_func,
-						S32 min_count,
-						S32 max_count){}
-	ParamDescriptor::~ParamDescriptor() {}
-
-	void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptorPtr in_param, const char* char_name){}
-	param_handle_t BaseBlock::getHandleFromParam(const Param* param) const {return 0;}
-	void BaseBlock::addSynonym(Param& param, const std::string& synonym) {}
-
-	void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
-	{
-		descriptor.mCurrentBlockPtr = this;
-	}
-
-	Param::Param(BaseBlock* enclosing_block)
-	:	mIsProvided(false)
-	{
-		const U8* my_addr = reinterpret_cast<const U8*>(this);
-		const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
-		mEnclosingBlockOffset = 0x7FFFffff & ((U32)(my_addr - block_addr));
-	}
-
-	bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name){ return true; }
-	void BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const LLInitParam::BaseBlock* diff_block) const {}
-	bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const { return true; }
-	bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) { return true; }
-	bool BaseBlock::validateBlock(bool emit_errors) const { return true; }
-
 	ParamValue<LLUIColor, TypeValues<LLUIColor> >::ParamValue(const LLUIColor& color)
 	:	super_t(color)
 	{}
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
index 6834b34387c2b68e7b60bcf6c8caf8e10670a9d1..5b7424acbb69dcf2762f78236ef68e96342c1e87 100644
--- a/indra/llwindow/llwindow.cpp
+++ b/indra/llwindow/llwindow.cpp
@@ -192,6 +192,21 @@ BOOL LLWindow::setSize(LLCoordScreen size)
 	return setSizeImpl(size);
 }
 
+BOOL LLWindow::setSize(LLCoordWindow size)
+{
+	//HACK: we are inconsistently using minimum window dimensions
+	// in this case, we are constraining the inner "client" rect and other times
+	// we constrain the outer "window" rect
+	// There doesn't seem to be a good way to do this consistently without a bunch of platform
+	// specific code
+	if (!getMaximized())
+	{
+		size.mX = llmax(size.mX, mMinWindowWidth);
+		size.mY = llmax(size.mY, mMinWindowHeight);
+	}
+	return setSizeImpl(size);
+}
+
 
 // virtual
 void LLWindow::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately)
@@ -440,7 +455,7 @@ BOOL LLWindowManager::isWindowValid(LLWindow *window)
 //coordinate conversion utility funcs that forward to llwindow
 LLCoordCommon LL_COORD_TYPE_WINDOW::convertToCommon() const
 {
-	const LLCoordWindow& self = static_cast<const LLCoordWindow&>(*this);
+	const LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this);
 
 	LLWindow* windowp = &(*LLWindow::beginInstances());
 	LLCoordGL out;
@@ -450,7 +465,7 @@ LLCoordCommon LL_COORD_TYPE_WINDOW::convertToCommon() const
 
 void LL_COORD_TYPE_WINDOW::convertFromCommon(const LLCoordCommon& from)
 {
-	LLCoordWindow& self = static_cast<LLCoordWindow&>(*this);
+	LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this);
 
 	LLWindow* windowp = &(*LLWindow::beginInstances());
 	LLCoordGL from_gl(from);
@@ -459,7 +474,7 @@ void LL_COORD_TYPE_WINDOW::convertFromCommon(const LLCoordCommon& from)
 
 LLCoordCommon LL_COORD_TYPE_SCREEN::convertToCommon() const
 {
-	const LLCoordScreen& self = static_cast<const LLCoordScreen&>(*this);
+	const LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this);
 
 	LLWindow* windowp = &(*LLWindow::beginInstances());
 	LLCoordGL out;
@@ -469,7 +484,7 @@ LLCoordCommon LL_COORD_TYPE_SCREEN::convertToCommon() const
 
 void LL_COORD_TYPE_SCREEN::convertFromCommon(const LLCoordCommon& from)
 {
-	LLCoordScreen& self = static_cast<LLCoordScreen&>(*this);
+	LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this);
 
 	LLWindow* windowp = &(*LLWindow::beginInstances());
 	LLCoordGL from_gl(from);
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index d2971581d2f28b648031b5a50f2a1baba9ba6ace..4da87f4e068d64d90f5d039b8e9f3ca0fe890db5 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -73,6 +73,7 @@ public:
 	virtual BOOL getSize(LLCoordWindow *size) = 0;
 	virtual BOOL setPosition(LLCoordScreen position) = 0;
 	BOOL setSize(LLCoordScreen size);
+	BOOL setSize(LLCoordWindow size);
 	virtual void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true);
 	virtual BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) = 0;
 	virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
@@ -172,6 +173,7 @@ protected:
 	virtual BOOL canDelete();
 
 	virtual BOOL setSizeImpl(LLCoordScreen size) = 0;
+	virtual BOOL setSizeImpl(LLCoordWindow size) = 0;
 
 protected:
 	LLWindowCallbacks*	mCallbacks;
diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h
index d4a778cb85bddf569f34e98025f53b57e5fdedcb..1f767f4c97d65716942ba819684b4fc217478951 100644
--- a/indra/llwindow/llwindowheadless.h
+++ b/indra/llwindow/llwindowheadless.h
@@ -47,6 +47,7 @@ public:
 	/*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;};
 	/*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;};
 	/*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;};
+	/*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;};
 	/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;};
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;};
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;};
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index c952f8bbcf504cf0172d66dbb20513abd4642144..32bb84cba59171dfe2450ea77801103682e094e8 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1266,6 +1266,31 @@ BOOL LLWindowMacOSX::setSizeImpl(const LLCoordScreen size)
 	return TRUE;
 }
 
+BOOL LLWindowMacOSX::setSizeImpl(const LLCoordWindow size)
+{
+	Rect client_rect;
+	if (mWindow)
+	{
+		OSStatus err = GetWindowBounds(mWindow, kWindowContentRgn, &client_rect);
+		if (err == noErr)
+		{
+			client_rect.right = client_rect.left + size.mX;
+			client_rect.bottom = client_rect.top + size.mY;
+			err = SetWindowBounds(mWindow, kWindowContentRgn, &client_rect);
+		}
+		if (err == noErr)
+		{
+			return TRUE;
+		}
+		else
+		{
+			llinfos << "Error setting size" << err << llendl;
+			return FALSE;
+		}
+	}
+	return FALSE;
+}
+
 void LLWindowMacOSX::swapBuffers()
 {
 	aglSwapBuffers(mContext);
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
index 073f294b543722340288fa6b0793e80464ce8ad2..52ba8b3bf3eb7f43c7044f08317aacc920887482 100644
--- a/indra/llwindow/llwindowmacosx.h
+++ b/indra/llwindow/llwindowmacosx.h
@@ -59,6 +59,7 @@ public:
 	/*virtual*/ BOOL getSize(LLCoordWindow *size);
 	/*virtual*/ BOOL setPosition(LLCoordScreen position);
 	/*virtual*/ BOOL setSizeImpl(LLCoordScreen size);
+	/*virtual*/ BOOL setSizeImpl(LLCoordWindow size);
 	/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL);
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index 5f5baceef838cfeb1f7525edc562523f4333542e..3d33af9d9bc97289a4f67030576d2f3f75613983 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -981,6 +981,25 @@ BOOL LLWindowSDL::setSizeImpl(const LLCoordScreen size)
 	return FALSE;
 }
 
+BOOL LLWindowSDL::setSizeImpl(const LLCoordWindow size)
+{
+	if(mWindow)
+	{
+		// Push a resize event onto SDL's queue - we'll handle it
+		// when it comes out again.
+		SDL_Event event;
+		event.type = SDL_VIDEORESIZE;
+		event.resize.w = size.mX;
+		event.resize.h = size.mY;
+		SDL_PushEvent(&event); // copied into queue
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
 void LLWindowSDL::swapBuffers()
 {
 	if (mWindow)
diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h
index 59719e404625d1c80e465f04641ec85d0bacda67..4e2a269ea33b4587c903662e55106c05e22d8a8d 100644
--- a/indra/llwindow/llwindowsdl.h
+++ b/indra/llwindow/llwindowsdl.h
@@ -64,6 +64,7 @@ public:
 	/*virtual*/ BOOL getSize(LLCoordWindow *size);
 	/*virtual*/ BOOL setPosition(LLCoordScreen position);
 	/*virtual*/ BOOL setSizeImpl(LLCoordScreen size);
+	/*virtual*/ BOOL setSizeImpl(LLCoordWindow size);
 	/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL);
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index e953d6ea12ca442722db841f1da910d87f8ae360..e07fbddb944536c073a4848e6f4e294070992f3e 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -872,10 +872,30 @@ BOOL LLWindowWin32::setSizeImpl(const LLCoordScreen size)
 		return FALSE;
 	}
 
+	WINDOWPLACEMENT placement;
+	placement.length = sizeof(WINDOWPLACEMENT);
+
+	if (!GetWindowPlacement(mWindowHandle, &placement)) return FALSE;
+
+	placement.showCmd = SW_RESTORE;
+
+	if (!SetWindowPlacement(mWindowHandle, &placement)) return FALSE;
+
 	moveWindow(position, size);
 	return TRUE;
 }
 
+BOOL LLWindowWin32::setSizeImpl(const LLCoordWindow size)
+{
+	RECT window_rect = {0, 0, size.mX, size.mY };
+	DWORD dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+	DWORD dw_style = WS_OVERLAPPEDWINDOW;
+
+	AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
+
+	return setSizeImpl(LLCoordScreen(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top));
+}
+
 // changing fullscreen resolution
 BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
 {
@@ -886,12 +906,12 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO
 	DWORD	current_refresh;
 	DWORD	dw_ex_style;
 	DWORD	dw_style;
-	RECT	window_rect;
+	RECT	window_rect = {0, 0, 0, 0};
 	S32 width = size.mX;
 	S32 height = size.mY;
 	BOOL auto_show = FALSE;
 
-	if (mhRC)
+	if (mhRC)	
 	{
 		auto_show = TRUE;
 		resetDisplayResolution();
@@ -986,7 +1006,8 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO
 			dw_ex_style = WS_EX_APPWINDOW;
 			dw_style = WS_POPUP;
 
-			// Move window borders out not to cover window contents
+			// Move window borders out not to cover window contents.
+			// This converts client rect to window rect, i.e. expands it by the window border size.
 			AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
 		}
 		// If it failed, we don't want to run fullscreen
@@ -1014,6 +1035,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO
 		dw_style = WS_OVERLAPPEDWINDOW;
 	}
 
+
 	// don't post quit messages when destroying old windows
 	mPostQuit = FALSE;
 
@@ -1799,6 +1821,10 @@ static LLFastTimer::DeclareTimer FTM_MOUSEHANDLER("Handle Mouse");
 
 LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param)
 {
+	// Ignore clicks not originated in the client area, i.e. mouse-up events not preceded with a WM_LBUTTONDOWN.
+	// This helps prevent avatar walking after maximizing the window by double-clicking the title bar.
+	static bool sHandleLeftMouseUp = true;
+
 	LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA);
 
 
@@ -2145,10 +2171,20 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
 			window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(FALSE));
 			return 0;
 
+		case WM_NCLBUTTONDOWN:
+			{
+				window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_NCLBUTTONDOWN");
+				// A click in a non-client area, e.g. title bar or window border.
+				sHandleLeftMouseUp = false;
+			}
+			break;
+
 		case WM_LBUTTONDOWN:
 			{
 				window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDOWN");
 				LLFastTimer t2(FTM_MOUSEHANDLER);
+				sHandleLeftMouseUp = true;
+
 				if (LLWinImm::isAvailable() && window_imp->mPreeditor)
 				{
 					window_imp->interruptLanguageTextInput();
@@ -2213,6 +2249,13 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
 			{
 				window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONUP");
 				LLFastTimer t2(FTM_MOUSEHANDLER);
+
+				if (!sHandleLeftMouseUp)
+				{
+					sHandleLeftMouseUp = true;
+					break;
+				}
+
 				//if (gDebugClicks)
 				//{
 				//	LL_INFOS("Window") << "WndProc left button up" << LL_ENDL;
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index b3602be8b7bad98b920b32c645a23ec345fad3aa..54c9ac4d4d182fd52349e67caeaf56fa92999b5c 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -58,6 +58,7 @@ public:
 	/*virtual*/ BOOL getSize(LLCoordWindow *size);
 	/*virtual*/ BOOL setPosition(LLCoordScreen position);
 	/*virtual*/ BOOL setSizeImpl(LLCoordScreen size);
+	/*virtual*/ BOOL setSizeImpl(LLCoordWindow size);
 	/*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL);
 	/*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
 	/*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
diff --git a/indra/llxuixml/CMakeLists.txt b/indra/llxuixml/CMakeLists.txt
deleted file mode 100644
index daed4de6ceb9dc28ef5518dd0e17c11b17335808..0000000000000000000000000000000000000000
--- a/indra/llxuixml/CMakeLists.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- cmake -*-
-
-project(llxuixml)
-
-include(00-Common)
-include(LLCommon)
-include(LLMath)
-include(LLXML)
-
-include_directories(
-    ${LLCOMMON_INCLUDE_DIRS}
-    ${LLMATH_INCLUDE_DIRS}
-    ${LLXML_INCLUDE_DIRS}
-    )
-
-set(llxuixml_SOURCE_FILES
-    llinitparam.cpp
-    lltrans.cpp
-    lluicolor.cpp
-    llxuiparser.cpp
-    )
-    
-set(llxuixml_HEADER_FILES
-    CMakeLists.txt
-
-    llinitparam.h
-    lltrans.h
-    llregistry.h
-    lluicolor.h
-    llxuiparser.h
-    )
-
-set_source_files_properties(${llxuixml_HEADER_FILES}
-                            PROPERTIES HEADER_FILE_ONLY TRUE)
-
-list(APPEND llxuixml_SOURCE_FILES ${llxuixml_HEADER_FILES})
-
-add_library (llxuixml ${llxuixml_SOURCE_FILES})
-# Libraries on which this library depends, needed for Linux builds
-# Sort by high-level to low-level
-target_link_libraries(llxuixml
-    llxml
-    llcommon
-    llmath
-    )
diff --git a/indra/lscript/lscript_byteformat.h b/indra/lscript/lscript_byteformat.h
index 7dd21bb1ad4f227c52b9c768e630f152ec6c1e05..a294def73418f26165796f8480a36f07ae2f0f86 100644
--- a/indra/lscript/lscript_byteformat.h
+++ b/indra/lscript/lscript_byteformat.h
@@ -529,6 +529,7 @@ typedef enum e_lscript_runtime_permissions
 	SCRIPT_PERMISSION_CHANGE_PERMISSIONS,
 	SCRIPT_PERMISSION_TRACK_CAMERA,
 	SCRIPT_PERMISSION_CONTROL_CAMERA,
+	SCRIPT_PERMISSION_TELEPORT,
 	SCRIPT_PERMISSION_EOF
 } LSCRIPTRunTimePermissions;
 
@@ -545,6 +546,7 @@ const U32 LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_EOF] =
 	(0x1 << 9),	//	SCRIPT_PERMISSION_CHANGE_PERMISSIONS
 	(0x1 << 10),//	SCRIPT_PERMISSION_TRACK_CAMERA
 	(0x1 << 11),//	SCRIPT_PERMISSION_CONTROL_CAMERA
+	(0x1 << 12),//	SCRIPT_PERMISSION_TELEPORT
 };
 
 // http_request string constants
diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l
index 4e103ae2ba4f4d164846a085eabd1b48b1b8f332..96b7e57e9781bd3cb766c96418a88547c7685667 100644
--- a/indra/lscript/lscript_compile/indra.l
+++ b/indra/lscript/lscript_compile/indra.l
@@ -212,7 +212,8 @@ extern "C" { int yyerror(const char *fmt, ...); }
 "PERMISSION_CHANGE_JOINTS"		{ count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_JOINTS]; return(INTEGER_CONSTANT); }
 "PERMISSION_CHANGE_PERMISSIONS"	{ count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_PERMISSIONS]; return(INTEGER_CONSTANT); }
 "PERMISSION_TRACK_CAMERA"		{ count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_TRACK_CAMERA]; return(INTEGER_CONSTANT); }
-"PERMISSION_CONTROL_CAMERA"			{ count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CONTROL_CAMERA]; return(INTEGER_CONSTANT); }
+"PERMISSION_CONTROL_CAMERA"		{ count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CONTROL_CAMERA]; return(INTEGER_CONSTANT); }
+"PERMISSION_TELEPORT"			{ count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_TELEPORT]; return(INTEGER_CONSTANT); }
 
 "INVENTORY_TEXTURE"					{ count(); yylval.ival = LLAssetType::AT_TEXTURE; return(INTEGER_CONSTANT); }
 "INVENTORY_SOUND"					{ count(); yylval.ival = LLAssetType::AT_SOUND; return(INTEGER_CONSTANT); }
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 3c9bde34b791bb47112736bada70b76850d1bc8b..0a267cbad010390fd0d8bc1f32980acbabeda914 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -30,7 +30,6 @@ include(LLUI)
 include(LLVFS)
 include(LLWindow)
 include(LLXML)
-include(LLXUIXML)
 include(LScript)
 include(Linking)
 include(NDOF)
@@ -41,12 +40,13 @@ include(UnixInstall)
 include(LLKDU)
 include(ViewerMiscLibs)
 include(LLLogin)
+include(VisualLeakDetector)
 include(GLOD)
 include(CMakeCopyIfDifferent)
 
 include_directories(
     ${DBUSGLIB_INCLUDE_DIRS}
-    ${JSONCPP_INCLUDE_DIRS}
+    ${JSONCPP_INCLUDE_DIR}
     ${GLOD_INCLUDE_DIR}
     ${LLAUDIO_INCLUDE_DIRS}
     ${LLCHARACTER_INCLUDE_DIRS}
@@ -65,7 +65,6 @@ include_directories(
     ${LLVFS_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
-    ${LLXUIXML_INCLUDE_DIRS}
     ${LSCRIPT_INCLUDE_DIRS}
     ${LSCRIPT_INCLUDE_DIRS}/lscript_compile
     ${LLLOGIN_INCLUDE_DIRS}
@@ -236,6 +235,7 @@ set(viewer_SOURCE_FILES
     llfloatertelehub.cpp
     llfloatertestinspectors.cpp
     llfloatertestlistview.cpp
+    llfloatertexturefetchdebugger.cpp
     llfloatertools.cpp
     llfloatertopobjects.cpp
     llfloatertos.cpp
@@ -287,7 +287,6 @@ set(viewer_SOURCE_FILES
     llinspectremoteobject.cpp
     llinspecttoast.cpp
     llinventorybridge.cpp
-    llinventoryclipboard.cpp
     llinventoryfilter.cpp
     llinventoryfunctions.cpp
     llinventoryicon.cpp
@@ -303,6 +302,7 @@ set(viewer_SOURCE_FILES
     lllistbrowser.cpp
     lllistcontextmenu.cpp
     lllistview.cpp
+    lllocalbitmaps.cpp
     lllocaltextureobject.cpp
     lllocationhistory.cpp
     lllocationinputctrl.cpp
@@ -489,6 +489,7 @@ set(viewer_SOURCE_FILES
     lltoastnotifypanel.cpp
     lltoastpanel.cpp
     lltoastscripttextbox.cpp
+    lltoastscriptquestion.cpp
     lltool.cpp
     lltoolbarview.cpp
     lltoolbrush.cpp
@@ -792,6 +793,7 @@ set(viewer_HEADER_FILES
     llfloatertelehub.h
     llfloatertestinspectors.h
     llfloatertestlistview.h
+    llfloatertexturefetchdebugger.h
     llfloatertools.h
     llfloatertopobjects.h
     llfloatertos.h
@@ -842,7 +844,6 @@ set(viewer_HEADER_FILES
     llinspectremoteobject.h
     llinspecttoast.h
     llinventorybridge.h
-    llinventoryclipboard.h
     llinventoryfilter.h
     llinventoryfunctions.h
     llinventoryicon.h
@@ -859,6 +860,7 @@ set(viewer_HEADER_FILES
     lllistbrowser.h
     lllistcontextmenu.h
     lllistview.h
+    lllocalbitmaps.h
     lllocaltextureobject.h
     lllocationhistory.h
     lllocationinputctrl.h
@@ -1036,6 +1038,7 @@ set(viewer_HEADER_FILES
     lltoastnotifypanel.h
     lltoastpanel.h
     lltoastscripttextbox.h
+    lltoastscriptquestion.h
     lltool.h
     lltoolbarview.h
     lltoolbrush.h
@@ -1745,7 +1748,6 @@ target_link_libraries(${VIEWER_BINARY_NAME}
     ${LLVFS_LIBRARIES}
     ${LLWINDOW_LIBRARIES}
     ${LLXML_LIBRARIES}
-    ${LLXUIXML_LIBRARIES}
     ${LSCRIPT_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml
index 15434f2b8f743f921c0f5555e47b4c0f039f3604..be79f919198612f0be1b54ad7b04d2b09a75a339 100644
--- a/indra/newview/app_settings/cmd_line.xml
+++ b/indra/newview/app_settings/cmd_line.xml
@@ -1,45 +1,101 @@
 <?xml version="1.0"?>
 <llsd>
   <map>
-    <key>help</key>
+    <!-- Please insert new keys in alphabetical order. -->
+    <key>analyzeperformance</key>
     <map>
       <key>desc</key>
-      <string>display this help message</string>
+      <string>When used in conjunction with logperformance, analyzes result of log against baseline.</string>
+      <key>map-to</key>
+      <string>AnalyzePerformance</string>
+    </map>
 
-      <key>short</key>
-      <string>h</string>
+    <key>autologin</key>
+    <map>
+      <key>desc</key>
+      <string>log in as last saved user</string>
+      <key>map-to</key>
+      <string>AutoLogin</string>
     </map>
 
-    <key>port</key>
+    <key>channel</key>
     <map>
       <key>count</key>
       <integer>1</integer>
-      <key>map-to</key>
-      <string>UserConnectionPort</string>
+      <!-- Special case. Not mapped to a setting. -->
     </map>
 
-    <key>drop</key>
+    <key>console</key>
     <map>
       <key>count</key>
       <integer>1</integer>
       <key>map-to</key>
-      <string>PacketDropPercentage</string>
+      <string>ShowConsoleWindow</string>
     </map>
 
-    <key>inbw</key>
+    <key>cooperative</key>
     <map>
+      <key>desc</key>
+      <string>Yield some idle time to local host.</string>
       <key>count</key>
       <integer>1</integer>
       <key>map-to</key>
-      <string>InBandwidth</string>
+      <string>YieldTime</string>
     </map>
 
-    <key>outbw</key>
+    <key>crashonstartup</key>
+    <map>
+      <key>desc</key>
+      <string>Crashes on startup. For QA use.</string>
+      <key>map-to</key>
+      <string>CrashOnStartup</string>
+    </map>
+
+    <key>debugsession</key>
+    <map>
+      <key>desc</key>
+      <string>Run as if RenderDebugGL is TRUE, but log errors until end of session.</string>
+      <key>map-to</key>
+      <string>DebugSession</string>
+    </map>
+
+    <key>debugviews</key>
+    <map>
+      <key>map-to</key>
+      <string>DebugViews</string>
+    </map>
+
+    <key>disablecrashlogger</key>
+    <map>
+      <key>desc</key>
+      <string>Disables the crash logger and lets the OS handle crashes</string>
+      <key>map-to</key>
+      <string>DisableCrashLogger</string>
+    </map>
+
+    <key>drop</key>
     <map>
       <key>count</key>
       <integer>1</integer>
       <key>map-to</key>
-      <string>OutBandwidth</string>
+      <string>PacketDropPercentage</string>
+    </map>
+
+    <key>god</key>
+    <map>
+      <key>desc</key>
+      <string>Log in a god if you have god access.</string>
+      <key>map-to</key>
+      <string>ConnectAsGod</string>
+    </map>
+
+    <key>graphicslevel</key>
+    <map>
+      <key>desc</key>
+      <string>Set the detail level. 
+0 - low, 1 - medium, 2 - high, 3 - ultra</string>
+      <key>count</key>
+      <integer>1</integer>
     </map>
 
     <key>grid</key>
@@ -52,16 +108,13 @@
       <string>CmdLineGridChoice</string>
     </map>
 
-    <key>loginuri</key>
+    <key>help</key>
     <map>
       <key>desc</key>
-      <string>login server and CGI script to use</string>
-      <key>count</key>
-      <integer>1</integer>
-      <key>compose</key>
-      <boolean>true</boolean>
-      <key>map-to</key>
-      <string>CmdLineLoginURI</string>
+      <string>display this help message</string>
+
+      <key>short</key>
+      <string>h</string>
     </map>
 
     <key>helperuri</key>
@@ -74,44 +127,73 @@
       <string>CmdLineHelperURI</string>
     </map>
 
-    <key>debugviews</key>
+    <key>ignorepixeldepth</key>
     <map>
+      <key>desc</key>
+      <string>Ignore pixel depth settings.</string>
       <key>map-to</key>
-      <string>DebugViews</string>
+      <string>IgnorePixelDepth</string>
     </map>
 
-    <key>skin</key>
+    <key>inbw</key>
+    <map>
+      <key>count</key>
+      <integer>1</integer>
+      <key>map-to</key>
+      <string>InBandwidth</string>
+    </map>
+
+    <key>leap</key>
     <map>
       <key>desc</key>
-      <string>ui/branding skin folder to use</string>
+      <string>command line to run an LLSD Event API Plugin</string>
       <key>count</key>
       <integer>1</integer>
+      <!-- you can specify multiple such plugins -->
+      <key>compose</key>
+      <boolean>true</boolean>
       <key>map-to</key>
-      <string>SkinFolder</string>
+      <string>LeapCommand</string>
     </map>
 
-    <key>autologin</key>
+    <key>logfile</key>
+    <map>
+      <key>count</key>
+      <integer>1</integer>
+      <key>map-to</key>
+      <string>UserLogFile</string>
+    </map>
+    
+    <key>login</key>
     <map>
       <key>desc</key>
-      <string>log in as last saved user</string>
+      <string>3 tokens: first, last and password</string>
+      <key>count</key>
+      <integer>3</integer>
       <key>map-to</key>
-      <string>AutoLogin</string>
+      <string>UserLoginInfo</string>
     </map>
 
-    <key>quitafter</key>
+    <key>loginpage</key>
     <map>
+      <key>desc</key>
+      <string>Login authentication page to use.</string>
       <key>count</key>
       <integer>1</integer>
       <key>map-to</key>
-      <string>QuitAfterSeconds</string>
+      <string>LoginPage</string>
     </map>
 
-    <key>logperformance</key>
+    <key>loginuri</key>
     <map>
       <key>desc</key>
-      <string>Log performance metrics for benchmarking</string>
+      <string>login server and CGI script to use</string>
+      <key>count</key>
+      <integer>1</integer>
+      <key>compose</key>
+      <boolean>true</boolean>
       <key>map-to</key>
-      <string>LogPerformance</string>
+      <string>CmdLineLoginURI</string>
     </map>
 
     <key>logmetrics</key>
@@ -123,29 +205,35 @@
       <key>map-to</key>
       <string>LogMetrics</string>
     </map>
-    
-    <key>analyzeperformance</key>
+
+    <key>logperformance</key>
     <map>
       <key>desc</key>
-      <string>When used in conjunction with logperformance, analyzes result of log against baseline.</string>
+      <string>Log performance metrics for benchmarking</string>
       <key>map-to</key>
-      <string>AnalyzePerformance</string>
+      <string>LogPerformance</string>
     </map>
 
-    <key>debugsession</key>
+    <key>multiple</key>		  
     <map>
       <key>desc</key>
-      <string>Run as if RenderDebugGL is TRUE, but log errors until end of session.</string>
+      <string>Allow multiple viewers.</string>
       <key>map-to</key>
-      <string>DebugSession</string>
+      <string>AllowMultipleViewers</string>
     </map>
 
-    <key>replaysession</key>
+    <key>noaudio</key>
+    <map>
+      <key>map-to</key>
+      <string>NoAudio</string>
+    </map>
+
+    <key>noinvlib</key>
     <map>
       <key>desc</key>
-      <string>After login, replay last recorded session and quit.</string>
+      <string>Do not request the inventory library.</string>
       <key>map-to</key>
-      <string>ReplaySession</string>
+      <string>NoInventoryLibrary</string>
     </map>
 
     <key>nonotifications</key>
@@ -156,22 +244,10 @@
       <string>IgnoreAllNotifications</string>      
     </map>    
 
-    <key>rotate</key>
-    <map>
-      <key>map-to</key>
-      <string>RotateRight</string>
-    </map>
-
-    <key>noaudio</key>
-    <map>
-      <key>map-to</key>
-      <string>NoAudio</string>
-    </map>
-
-    <key>nosound</key>
+    <key>nopreload</key>
     <map>
       <key>map-to</key>
-      <string>NoAudio</string>
+      <string>NoPreload</string>
     </map>
 
     <key>noprobe</key>
@@ -186,151 +262,134 @@
       <string>NoQuickTime</string>
     </map>
 
-    <key>nopreload</key>
+    <key>nosound</key>
     <map>
       <key>map-to</key>
-      <string>NoPreload</string>
+      <string>NoAudio</string>
     </map>
 
-    <key>purge</key>
+    <key>no-verify-ssl-cert</key>
     <map>
-      <key>desc</key>
-      <string>Delete files in the cache.</string>
       <key>map-to</key>
-      <string>PurgeCacheOnNextStartup</string>
+      <string>NoVerifySSLCert</string>
     </map>
 
-    <key>noinvlib</key>
+    <key>novoice</key>
     <map>
       <key>desc</key>
-      <string>Do not request the inventory library.</string>
+      <string>Disable voice.</string>
       <key>map-to</key>
-      <string>NoInventoryLibrary</string>
+      <string>CmdLineDisableVoice</string>
     </map>
 
-    <key>logfile</key>
+    <key>outbw</key>
     <map>
       <key>count</key>
       <integer>1</integer>
       <key>map-to</key>
-      <string>UserLogFile</string>
+      <string>OutBandwidth</string>
     </map>
 
-    <key>graphicslevel</key>
+    <key>port</key>
     <map>
-      <key>desc</key>
-      <string>Set the detail level. 
-0 - low, 1 - medium, 2 - high, 3 - ultra</string>
       <key>count</key>
       <integer>1</integer>
+      <key>map-to</key>
+      <string>UserConnectionPort</string>
     </map>
 
-    <key>setdefault</key>
+    <key>purge</key>
     <map>
       <key>desc</key>
-      <string>specify the value of a particular configuration variable which can be overridden by settings.xml.</string>
-      <key>count</key>
-      <integer>2</integer>
-      <!-- Special case. Mapped to settings procedurally. -->
+      <string>Delete files in the cache.</string>
+      <key>map-to</key>
+      <string>PurgeCacheOnNextStartup</string>
     </map>
 
-    <key>set</key>
+    <key>qa</key>
     <map>
       <key>desc</key>
-      <string>specify the value of a particular configuration variable that overrides all other settings.</string>
-      <key>count</key>
-      <integer>2</integer>
-      <key>compose</key>
-      <boolean>true</boolean>
-      <!-- Special case. Mapped to settings procedurally. -->
+      <string>Activated debugging menu in Advanced Settings.</string>
+      <key>map-to</key>
+      <string>QAMode</string>
     </map>
 
-    <key>settings</key>
+    <key>quitafter</key>
     <map>
-      <key>desc</key>
-      <string>Specify the filename of a configuration file.</string>
       <key>count</key>
       <integer>1</integer>
-      <!-- Special case. Mapped to settings procedurally. -->
+      <key>map-to</key>
+      <string>QuitAfterSeconds</string>
     </map>
-
-    <key>sessionsettings</key>
+    
+    <key>replaysession</key>
     <map>
       <key>desc</key>
-      <string>Specify the filename of a configuration file that contains temporary per-session configuration overrides.</string>
-      <key>count</key>
-      <integer>1</integer>
-      <!-- Special case. Mapped to settings procedurally. -->
+      <string>After login, replay last recorded session and quit.</string>
+      <key>map-to</key>
+      <string>ReplaySession</string>
     </map>
 
-    <key>usersessionsettings</key>
-    <map>
-      <key>desc</key>
-      <string>Specify the filename of a configuration file that contains temporary per-session configuration user overrides.</string>
-      <key>count</key>
-      <integer>1</integer>
-      <!-- Special case. Mapped to settings procedurally. -->
-    </map>
-    
-    <key>login</key>
+    <key>rotate</key>
     <map>
-      <key>desc</key>
-      <string>3 tokens: first, last and password</string>
-      <key>count</key>
-      <integer>3</integer>
       <key>map-to</key>
-      <string>UserLoginInfo</string>
+      <string>RotateRight</string>
     </map>
 
-    <key>god</key>
+    <key>safe</key>
     <map>
       <key>desc</key>
-      <string>Log in a god if you have god access.</string>
+      <string>Reset preferences, run in safe mode.</string>
       <key>map-to</key>
-      <string>ConnectAsGod</string>
+      <string>SafeMode</string>
     </map>
 
-    <key>console</key>
+    <key>sessionsettings</key>
     <map>
+      <key>desc</key>
+      <string>Specify the filename of a configuration file that contains temporary per-session configuration overrides.</string>
       <key>count</key>
       <integer>1</integer>
-      <key>map-to</key>
-      <string>ShowConsoleWindow</string>
+      <!-- Special case. Mapped to settings procedurally. -->
     </map>
 
-    <key>safe</key>
+    <key>set</key>
     <map>
       <key>desc</key>
-      <string>Reset preferences, run in safe mode.</string>
-      <key>map-to</key>
-      <string>SafeMode</string>
+      <string>specify the value of a particular configuration variable that overrides all other settings.</string>
+      <key>count</key>
+      <integer>2</integer>
+      <key>compose</key>
+      <boolean>true</boolean>
+      <!-- Special case. Mapped to settings procedurally. -->
     </map>
 
-    <key>multiple</key>		  
+    <key>setdefault</key>
     <map>
       <key>desc</key>
-      <string>Allow multiple viewers.</string>
-      <key>map-to</key>
-      <string>AllowMultipleViewers</string>
+      <string>specify the value of a particular configuration variable which can be overridden by settings.xml.</string>
+      <key>count</key>
+      <integer>2</integer>
+      <!-- Special case. Mapped to settings procedurally. -->
     </map>
 
-    <key>novoice</key>
+    <key>settings</key>
     <map>
       <key>desc</key>
-      <string>Disable voice.</string>
-      <key>map-to</key>
-      <string>CmdLineDisableVoice</string>
+      <string>Specify the filename of a configuration file.</string>
+      <key>count</key>
+      <integer>1</integer>
+      <!-- Special case. Mapped to settings procedurally. -->
     </map>
 
-    <key>url</key>
+    <key>skin</key>
     <map>
       <key>desc</key>
-      <string>Startup location</string>            
+      <string>ui/branding skin folder to use</string>
       <key>count</key>
       <integer>1</integer>
-      <key>last_option</key>
-      <boolean>true</boolean>
-      <!-- Special case. Not mapped to a setting. -->
+      <key>map-to</key>
+      <string>SkinFolder</string>
     </map>
 
     <key>slurl</key>
@@ -346,69 +405,24 @@
       <!-- Special case. Not mapped to a setting. -->
     </map>
 
-    <key>ignorepixeldepth</key>
-    <map>
-      <key>desc</key>
-      <string>Ignore pixel depth settings.</string>
-      <key>map-to</key>
-      <string>IgnorePixelDepth</string>
-    </map>
-
-    <key>cooperative</key>
+    <key>url</key>
     <map>
       <key>desc</key>
-      <string>Yield some idle time to local host.</string>
-      <key>count</key>
-      <integer>1</integer>
-      <key>map-to</key>
-      <string>YieldTime</string>
-    </map>
-
-    <key>no-verify-ssl-cert</key>
-    <map>
-      <key>map-to</key>
-      <string>NoVerifySSLCert</string>
-    </map>
-
-    <key>channel</key>
-    <map>
+      <string>Startup location</string>            
       <key>count</key>
       <integer>1</integer>
+      <key>last_option</key>
+      <boolean>true</boolean>
       <!-- Special case. Not mapped to a setting. -->
     </map>
 
-    <key>loginpage</key>
+    <key>usersessionsettings</key>
     <map>
       <key>desc</key>
-      <string>Login authentication page to use.</string>
+      <string>Specify the filename of a configuration file that contains temporary per-session configuration user overrides.</string>
       <key>count</key>
       <integer>1</integer>
-      <key>map-to</key>
-      <string>LoginPage</string>
-    </map>
-
-    <key>qa</key>
-    <map>
-      <key>desc</key>
-      <string>Activated debugging menu in Advanced Settings.</string>
-      <key>map-to</key>
-      <string>QAMode</string>
-    </map>
-
-    <key>crashonstartup</key>
-    <map>
-      <key>desc</key>
-      <string>Crashes on startup. For QA use.</string>
-      <key>map-to</key>
-      <string>CrashOnStartup</string>
-    </map>
-
-    <key>disablecrashlogger</key>
-    <map>
-      <key>desc</key>
-      <string>Disables the crash logger and lets the OS handle crashes</string>
-      <key>map-to</key>
-      <string>DisableCrashLogger</string>
+      <!-- Special case. Mapped to settings procedurally. -->
     </map>
   </map>
 </llsd>
diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini
index 82b43432eb32bcbbc2374e093af4d08e6158b90d..318b69438a761e4d4cff495aa80f535295517a85 100644
--- a/indra/newview/app_settings/keywords.ini
+++ b/indra/newview/app_settings/keywords.ini
@@ -91,6 +91,7 @@ PERMISSION_CHANGE_LINKS			Passed to llRequestPermissions library function to req
 # PERMISSION_CHANGE_PERMISSIONS Passed to llRequestPermissions library function to request permission to change permissions
 PERMISSION_TRACK_CAMERA			Passed to llRequestPermissions library function to request permission to track agent's camera
 PERMISSION_CONTROL_CAMERA		Passed to llRequestPermissions library function to request permission to change agent's camera
+PERMISSION_TELEPORT				Passed to llRequestPermissions library function to request permission to teleport agent
 
 DEBUG_CHANNEL		Chat channel reserved for debug and error messages from scripts
 PUBLIC_CHANNEL		Chat channel that broadcasts to all nearby users
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml
index a76eb3cd372d9dcf1875b042b0200ff0bae9b7db..64122bbb6ce9ac782faf15ba47ad80ad2e57583d 100644
--- a/indra/newview/app_settings/logcontrol.xml
+++ b/indra/newview/app_settings/logcontrol.xml
@@ -20,7 +20,7 @@
 					<key>tags</key>
 						<array>
 							<string>AppInit</string>
-              <string>Capabilities</string>
+							<string>Capabilities</string>
 							<string>SystemInfo</string>
 							<string>TextureCache</string>
 							<string>AppCache</string>
@@ -42,8 +42,10 @@
 						</array>
 					<key>tags</key>
 						<array>
-							<!-- sample entry for debugging a specific item	-->
-<!--						<string>Voice</string>		-->
+						<!-- sample entry for debugging specific items	
+						     <string>Avatar</string>
+						     <string>Voice</string>		
+						-->
 						</array>
 				</map>
 			</array>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index d9d6facfad2341029bd7d2d0cfa46afbc90ab92a..c9b4de014097f001ba1bf2e16f1312d3469a120c 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -335,17 +335,6 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
-  <key>AuditTexture</key>
-  <map>
-    <key>Comment</key>
-    <string>Enable texture auditting.</string>
-    <key>Persist</key>
-    <integer>1</integer>
-    <key>Type</key>
-    <string>Boolean</string>
-    <key>Value</key>
-    <integer>0</integer>
-  </map>
   <key>AutoAcceptNewInventory</key>
     <map>
       <key>Comment</key>
@@ -621,6 +610,28 @@
       <key>Value</key>
       <string>http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/[GRID_LOWERCASE]/avatars.html</string>
     </map>
+    <key>AvatarRotateThresholdSlow</key>
+    <map>
+      <key>Comment</key>
+      <string>Angle between avatar facing and camera facing at which avatar turns to face same direction as camera, when moving slowly (degrees)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <integer>60</integer>
+    </map>  
+    <key>AvatarRotateThresholdFast</key>
+    <map>
+      <key>Comment</key>
+      <string>Angle between avatar facing and camera facing at which avatar turns to face same direction as camera, when moving fast (degrees)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <integer>2</integer>
+    </map>  
     <key>AvatarBakedTextureUploadTimeout</key>
     <map>
       <key>Comment</key>
@@ -1914,7 +1925,7 @@
     <key>Type</key>
     <string>Boolean</string>
     <key>Value</key>
-    <integer>0</integer>
+    <integer>1</integer>
   </map>
     <key>DebugBeaconLineWidth</key>
     <map>
@@ -4281,17 +4292,6 @@
         <string>Boolean</string>
         <key>Value</key>
         <integer>0</integer>
-    </map>
-    <key>InventoryInboxToggleState</key>
-    <map>
-        <key>Comment</key>
-        <string>Stores the open/closed state of inventory Received items panel</string>
-        <key>Persist</key>
-        <integer>1</integer>
-        <key>Type</key>
-        <string>Boolean</string>
-        <key>Value</key>
-        <integer>0</integer>
     </map>
 	<key>InventoryLinking</key>
 	<map>
@@ -4590,6 +4590,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>LeapCommand</key>
+    <map>
+      <key>Comment</key>
+      <string>Zero or more command lines to run LLSD Event API Plugin programs.</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>LLSD</string>
+      <key>Value</key>
+      <array />
+    </map>
     <key>LSLFindCaseInsensitivity</key>
         <map>
         <key>Comment</key>
@@ -7153,7 +7164,7 @@
     <key>QAModeEventHostPort</key>
     <map>
       <key>Comment</key>
-      <string>Port on which lleventhost should listen</string>
+      <string>DEPRECATED: Port on which lleventhost should listen</string>
       <key>Persist</key>
       <integer>0</integer>
       <key>Type</key>
@@ -7172,6 +7183,17 @@
       <key>Value</key>
       <integer>-1</integer>
     </map>
+    <key>QAModeMetrics</key>
+    <map>
+      <key>Comment</key>
+      <string>"Enables QA features (logging, faster cycling) for metrics collector"</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
     <key>QuietSnapshotsToDisk</key>
     <map>
       <key>Comment</key>
@@ -8239,7 +8261,7 @@
     <key>Type</key>
     <string>U32</string>
     <key>Value</key>
-    <real>128</real>
+    <real>512</real>
   </map>
 
   <key>RenderSpecularResY</key>
@@ -8263,7 +8285,7 @@
     <key>Type</key>
     <string>F32</string>
     <key>Value</key>
-    <real>8</real>
+    <real>384</real>
   </map>
 
   <key>RenderDeferred</key>
@@ -9621,18 +9643,29 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
-    <key>ShowConsoleWindow</key>
-    <map>
-      <key>Comment</key>
-      <string>Show log in separate OS window</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>0</integer>
-    </map>
-    <key>NavBarShowCoordinates</key>
+  <key>ShowConsoleWindow</key>
+  <map>
+    <key>Comment</key>
+    <string>Show log in separate OS window</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+  <key>EnableVisualLeakDetector</key>
+  <map>
+    <key>Comment</key>
+    <string>EnableVisualLeakDetector</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>Boolean</string>
+    <key>Value</key>
+    <integer>0</integer>
+  </map>
+  <key>NavBarShowCoordinates</key>
     <map>
       <key>Comment</key>
       <string>Show coordinates in navigation bar</string>
@@ -10644,6 +10677,39 @@
       <key>Value</key>
       <real>20.0</real>
     </map>
+    <key>TexelPixelRatio</key>
+    <map>
+      <key>Comment</key>
+      <string>texel pixel ratio = texel / pixel</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>1.0</real>
+    </map>
+    <key>TextureCameraMotionThreshold</key>
+    <map>
+      <key>Comment</key>
+      <string>If the overall motion is lower than this value, textures will be loaded faster</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>0.2</real>
+    </map>
+    <key>TextureCameraMotionBoost</key>
+    <map>
+      <key>Comment</key>
+      <string>Progressive discard level decrement when the camera is still</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>3</integer>
+    </map>
     <key>TextureDecodeDisabled</key>
     <map>
       <key>Comment</key>
@@ -10677,6 +10743,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>TextureFetchDebuggerEnabled</key>
+    <map>
+      <key>Comment</key>
+      <string>Enable the texture fetching debugger if set</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
     <key>TextureLoadFullRes</key>
     <map>
       <key>Comment</key>
@@ -10699,6 +10776,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>TextureNewByteRange</key>
+    <map>
+      <key>Comment</key>
+      <string>Use the new more accurate byte range computation for j2c discard levels</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
     <key>TexturePickerShowFolders</key>
     <map>
       <key>Comment</key>
@@ -10721,6 +10809,17 @@
       <key>Value</key>
       <integer>2</integer>
     </map>
+    <key>TextureReverseByteRange</key>
+    <map>
+      <key>Comment</key>
+      <string>Minimal percent of the optimal byte range allowed to render a given discard level</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>50</integer>
+    </map>
     <key>ThrottleBandwidthKBPS</key>
     <map>
       <key>Comment</key>
@@ -12768,7 +12867,7 @@
     <key>WindowX</key>
     <map>
       <key>Comment</key>
-      <string>X coordinate of lower left corner of SL viewer window, relative to primary display (pixels)</string>
+      <string>X coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels)</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
@@ -12779,7 +12878,7 @@
     <key>WindowY</key>
     <map>
       <key>Comment</key>
-      <string>Y coordinate of lower left corner of SL viewer window, relative to primary display (pixels)</string>
+      <string>Y coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels)</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml
index 8cdd8ed838084dce4e1ff7a53f6fe2fa945d5d3b..143126b3345307e4d16083378d5ee539da0637d1 100644
--- a/indra/newview/app_settings/settings_per_account.xml
+++ b/indra/newview/app_settings/settings_per_account.xml
@@ -33,6 +33,28 @@
         <key>Value</key>
             <string />
         </map>
+    <key>InventoryInboxHeight</key>
+    <map>
+        <key>Comment</key>
+            <string>Inventory inbox panel height in Inventory floater.</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>S32</string>
+        <key>Value</key>
+            <integer>200</integer>
+    </map>
+    <key>InventoryInboxToggleState</key>
+    <map>
+        <key>Comment</key>
+            <string>Stores the open/closed state of inventory Received Items panel.</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>Boolean</string>
+        <key>Value</key>
+            <integer>0</integer>
+    </map>
     <key>DisplayDestinationsOnInitialRun</key>
         <map>
         <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
index 53a2a13392d9f134f1deadf578b418e666d53e66..4d01eeb64e581f09f23b19fa97722643e075d8fd 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl
@@ -128,7 +128,7 @@ void main()
 				
 				if (sa > 0.0)
 				{
-					sa = texture2D(lightFunc,vec2(sa, spec.a)).r * min(dist_atten*4.0, 1.0);
+					sa = 6 * texture2D(lightFunc, vec2(sa, spec.a)).r * min(dist_atten*4.0, 1.0);
 					sa *= noise;
 					col += da*sa*light_col[i].rgb*spec.rgb;
 				}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
index a5e04fba575c0070c30588aee812ff9a64fca780..19800a8b8e16be7369e3c23376124c79dd45c2b2 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl
@@ -109,7 +109,7 @@ void main()
 		float sa = dot(normalize(lv-normalize(pos)),norm);
 		if (sa > 0.0)
 		{
-			sa = texture2D(lightFunc, vec2(sa, spec.a)).r * min(dist_atten*4.0, 1.0);
+			sa = 6 * texture2D(lightFunc, vec2(sa, spec.a)).r * min(dist_atten*4.0, 1.0);
 			sa *= noise;
 			col += da*sa*color.rgb*spec.rgb;
 		}
diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl
index cf8cf8364a4a246186b8041ef2ba0bc51fefa4e8..bced4a557774cc6cc9037e43ea7097f0cba3a7a1 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskF.glsl
@@ -29,11 +29,11 @@ out vec4 frag_color;
 #define frag_color gl_FragColor
 #endif
 
-uniform float minimum_alpha;
-
 uniform sampler2D diffuseMap;
 
-VARYING vec4 post_pos;
+VARYING float pos_zd2;
+VARYING float pos_w;
+VARYING float target_pos_x;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 
@@ -41,12 +41,20 @@ void main()
 {
 	float alpha = diffuseLookup(vary_texcoord0.xy).a * vertex_color.a;
 
-	if (alpha < minimum_alpha)
+	if (alpha < 0.05) // treat as totally transparent
 	{
 		discard;
 	}
 
+	if (alpha < 0.88) // treat as semi-transparent
+	{
+	  if (fract(0.5*floor(target_pos_x / pos_w )) < 0.25)
+	  {
+	    discard;
+	  }
+	}
+
 	frag_color = vec4(1,1,1,1);
 	
-	gl_FragDepth = max(post_pos.z/post_pos.w*0.5+0.5, 0.0);
+	gl_FragDepth = max(pos_zd2/pos_w+0.5, 0.0);
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskV.glsl b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskV.glsl
index 7d3b06c56e7517c684c978abf34c808256257a14..c1f2d90712c42b0ba81a40b23a759b0122672ccb 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/shadowAlphaMaskV.glsl
@@ -25,12 +25,15 @@
 
 uniform mat4 texture_matrix0;
 uniform mat4 modelview_projection_matrix;
+uniform float shadow_target_width;
 
 ATTRIBUTE vec3 position;
 ATTRIBUTE vec4 diffuse_color;
 ATTRIBUTE vec2 texcoord0;
 
-VARYING vec4 post_pos;
+VARYING float pos_zd2;
+VARYING float pos_w;
+VARYING float target_pos_x;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
 
@@ -39,8 +42,11 @@ void passTextureIndex();
 void main()
 {
 	//transform vertex
-	vec4 pos = modelview_projection_matrix*vec4(position.xyz, 1.0);
-	post_pos = pos;
+	vec4 pre_pos = vec4(position.xyz, 1.0);
+	vec4 pos = modelview_projection_matrix * pre_pos;
+	target_pos_x = 0.5 * (shadow_target_width - 1.0) * pos.x;
+	pos_w = pos.w;
+	pos_zd2 = pos.z * 0.5;
 	
 	gl_Position = vec4(pos.x, pos.y, pos.w*0.5, pos.w);
 	
diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
index b5501c28203d1a72d43d7355f8d2034fbf9d405e..66e3cf6d13f4a152594985e240743358ee1e6217 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl
@@ -301,11 +301,11 @@ void main()
 			//
 			vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
 			float sa = dot(refnormpersp, sun_dir.xyz);
-			vec3 dumbshiny = vary_SunlitColor*texture2D(lightFunc, vec2(sa, spec.a)).r;
+			vec3 dumbshiny = vary_SunlitColor*(6 * texture2D(lightFunc, vec2(sa, spec.a)).r);
 			
 			// add the two types of shiny together
 			vec3 spec_contrib = dumbshiny * spec.rgb;
-			bloom = dot(spec_contrib, spec_contrib);
+			bloom = dot(spec_contrib, spec_contrib) / 4;
 			col += spec_contrib;
 
 			//add environmentmap
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
index 08f6ec63fe209cb479b34e6b33e1422ede9f98c8..8db4cb58cff03bf1a2788945ff622fea7c650269 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl
@@ -58,20 +58,22 @@ uniform float shadow_bias;
 
 uniform mat4 inv_proj;
 
-float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
+float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc)
 {
 	stc.xyz /= stc.w;
 	stc.z += shadow_bias;
+
+	stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here
 	
 	float cs = shadow2DRect(shadowMap, stc.xyz).x;
 	float shadow = cs;
 
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs);
-			
-	return shadow/5.0;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
+                        
+        return shadow*0.2;
 }
 
 
@@ -101,7 +103,7 @@ void main()
 
 			float w = 1.0;
 			w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
-			shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
+			shadow += pcfShadow(shadowMap3, lpos)*w;
 			weight += w;
 			shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
 		}
@@ -114,7 +116,7 @@ void main()
 			float w = 1.0;
 			w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
 			w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
-			shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
+			shadow += pcfShadow(shadowMap2, lpos)*w;
 			weight += w;
 		}
 
@@ -126,7 +128,7 @@ void main()
 			float w = 1.0;
 			w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
 			w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
-			shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
+			shadow += pcfShadow(shadowMap1, lpos)*w;
 			weight += w;
 		}
 
@@ -138,7 +140,7 @@ void main()
 			float w = 1.0;
 			w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
 				
-			shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
+			shadow += pcfShadow(shadowMap0, lpos)*w;
 			weight += w;
 		}
 		
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl
index aae6a070e294fd2b45fe953a30f3caa72f9a6df0..33958a501016ad83e07cc5bb1fa762019919d84d 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedF.glsl
@@ -71,20 +71,22 @@ vec4 getPosition(vec2 pos_screen)
 	return pos;
 }
 
-float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
+float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc)
 {
 	stc.xyz /= stc.w;
 	stc.z += shadow_bias;
+
+	stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here
 	
 	float cs = shadow2DRect(shadowMap, stc.xyz).x;
 	float shadow = cs;
 
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs);
-			
-	return shadow/5.0;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
+                        
+        return shadow*0.2;
 }
 
 
@@ -114,7 +116,7 @@ void main()
 
 			float w = 1.0;
 			w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
-			shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
+			shadow += pcfShadow(shadowMap3, lpos)*w;
 			weight += w;
 			shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
 		}
@@ -127,7 +129,7 @@ void main()
 			float w = 1.0;
 			w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
 			w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
-			shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
+			shadow += pcfShadow(shadowMap2, lpos)*w;
 			weight += w;
 		}
 
@@ -139,7 +141,7 @@ void main()
 			float w = 1.0;
 			w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
 			w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
-			shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
+			shadow += pcfShadow(shadowMap1, lpos)*w;
 			weight += w;
 		}
 
@@ -151,7 +153,7 @@ void main()
 			float w = 1.0;
 			w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
 				
-			shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
+			shadow += pcfShadow(shadowMap0, lpos)*w;
 			weight += w;
 		}
 		
diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl
index 931577359e5db39db990804c12684423b29e3487..ba6f3ace53f175bf91c683281f11208da5a60b6f 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/alphaNonIndexedNoColorF.glsl
@@ -70,20 +70,22 @@ vec4 getPosition(vec2 pos_screen)
 	return pos;
 }
 
-float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
+float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc)
 {
 	stc.xyz /= stc.w;
 	stc.z += shadow_bias;
+
+	stc.x = floor(stc.x + fract(stc.y*12345)); // add some chaotic jitter to X sample pos according to Y to disguise the snapping going on here
 	
 	float cs = shadow2DRect(shadowMap, stc.xyz).x;
 	float shadow = cs;
 
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(scl, -scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, scl, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-scl, -scl, 0.0)).x, cs);
-			
-	return shadow/5.0;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
+                        
+        return shadow*0.2;
 }
 
 
@@ -113,7 +115,7 @@ void main()
 
 			float w = 1.0;
 			w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
-			shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
+			shadow += pcfShadow(shadowMap3, lpos)*w;
 			weight += w;
 			shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
 		}
@@ -126,7 +128,7 @@ void main()
 			float w = 1.0;
 			w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
 			w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
-			shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
+			shadow += pcfShadow(shadowMap2, lpos)*w;
 			weight += w;
 		}
 
@@ -138,7 +140,7 @@ void main()
 			float w = 1.0;
 			w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
 			w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
-			shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
+			shadow += pcfShadow(shadowMap1, lpos)*w;
 			weight += w;
 		}
 
@@ -150,7 +152,7 @@ void main()
 			float w = 1.0;
 			w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
 				
-			shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
+			shadow += pcfShadow(shadowMap0, lpos)*w;
 			weight += w;
 		}
 		
diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
index 61a7f1e32fe59ea3580e228a05e388408826b4c6..a137bea30f1dc0202cf182c04e3003909fd1221e 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl
@@ -309,11 +309,11 @@ void main()
 			//
 			vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
 			float sa = dot(refnormpersp, sun_dir.xyz);
-			vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).r;
+			vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*(6 * texture2D(lightFunc, vec2(sa, spec.a)).r);
 
 			// add the two types of shiny together
 			vec3 spec_contrib = dumbshiny * spec.rgb;
-			bloom = dot(spec_contrib, spec_contrib);
+			bloom = dot(spec_contrib, spec_contrib) / 4;
 			col += spec_contrib;
 
 			//add environmentmap
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
index 8c4ccf9cb37265a2e938854a82539c005da73c4a..db3d7603591eedd0d0a52f62b54170380a71fc5a 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl
@@ -78,42 +78,42 @@ vec4 getPosition(vec2 pos_screen)
 	return pos;
 }
 
-float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
+float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
 {
 	stc.xyz /= stc.w;
 	stc.z += shadow_bias*scl;
-	
+
+	stc.x = floor(stc.x + fract(pos_screen.y*0.666666666)); // add some jitter to X sample pos according to Y to disguise the snapping going on here
+
 	float cs = shadow2DRect(shadowMap, stc.xyz).x;
 	float shadow = cs;
 
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs);
+	shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
+	shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
+	shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, 1.5, 0.0)).x;
+	shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, -1.5, 0.0)).x;
 			
-	return shadow/5.0;
-	
-	//return shadow;
+        return shadow*0.2;
 }
 
-float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl)
+float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
 {
 	stc.xyz /= stc.w;
 	stc.z += spot_shadow_bias*scl;
+	stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap
 	
 	float cs = shadow2D(shadowMap, stc.xyz).x;
 	float shadow = cs;
 
-	vec2 off = 1.5/proj_shadow_res;
+	vec2 off = 1.0/proj_shadow_res;
+	off.y *= 1.5;
 	
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs);
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs);
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs);
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs);
-				
-	return shadow/5.0;
-	
-	//return shadow;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x*2.0, off.y, 0.0)).x;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x*2.0, -off.y, 0.0)).x;
+
+        return shadow*0.2;
 }
 
 void main() 
@@ -166,7 +166,7 @@ void main()
 
 				float w = 1.0;
 				w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
-				shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
+				shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w;
 				weight += w;
 				shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
 			}
@@ -179,7 +179,7 @@ void main()
 				float w = 1.0;
 				w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
 				w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
-				shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
+				shadow += pcfShadow(shadowMap2, lpos, 0.5, pos_screen)*w;
 				weight += w;
 			}
 
@@ -191,7 +191,7 @@ void main()
 				float w = 1.0;
 				w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
 				w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
-				shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
+				shadow += pcfShadow(shadowMap1, lpos, 0.75, pos_screen)*w;
 				weight += w;
 			}
 
@@ -203,7 +203,7 @@ void main()
 				float w = 1.0;
 				w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
 				
-				shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
+				shadow += pcfShadow(shadowMap0, lpos, 1.0, pos_screen)*w;
 				weight += w;
 			}
 		
@@ -237,11 +237,11 @@ void main()
 	
 	//spotlight shadow 1
 	vec4 lpos = shadow_matrix[4]*spos;
-	frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8); 
+	frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen); 
 	
 	//spotlight shadow 2
 	lpos = shadow_matrix[5]*spos;
-	frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8); 
+	frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen); 
 
 	//frag_color.rgb = pos.xyz;
 	//frag_color.b = shadow;
diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
index 02075a7687760f52e4a56f13d714516c4e4cb08a..dfe108eb01e409e1e1aa329fc26a52c60bcf0220 100644
--- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
+++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl
@@ -90,7 +90,7 @@ vec2 getKern(int i)
 	kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750;
 	kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875;
 	kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000;
-	
+       
 	return kern[i];
 }
 
@@ -139,42 +139,42 @@ float calcAmbientOcclusion(vec4 pos, vec3 norm)
 	return min(ret, 1.0);
 }
 
-float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl)
+float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
 {
 	stc.xyz /= stc.w;
 	stc.z += shadow_bias*scl;
+
+	stc.x = floor(stc.x + fract(pos_screen.y*0.666666666));
 	
 	float cs = shadow2DRect(shadowMap, stc.xyz).x;
 	float shadow = cs;
-
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs);
-	shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs);
-			
-	return shadow/5.0;
 	
-	//return shadow;
+	shadow += shadow2DRect(shadowMap, stc.xyz+vec3(2.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(1.0, -1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-1.0, 1.5, 0.0)).x;
+        shadow += shadow2DRect(shadowMap, stc.xyz+vec3(-2.0, -1.5, 0.0)).x;
+                        
+        return shadow*0.2;
 }
 
-float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl)
+float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl, vec2 pos_screen)
 {
 	stc.xyz /= stc.w;
 	stc.z += spot_shadow_bias*scl;
+	stc.x = floor(proj_shadow_res.x * stc.x + fract(pos_screen.y*0.666666666)) / proj_shadow_res.x; // snap
 	
 	float cs = shadow2D(shadowMap, stc.xyz).x;
 	float shadow = cs;
 
-	vec2 off = 1.5/proj_shadow_res;
+	vec2 off = 1.0/proj_shadow_res;
+	off.y *= 1.5;
 	
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs);
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs);
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs);
-	shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs);
-	
-	return shadow/5.0;
-	
-	//return shadow;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x*2.0, off.y, 0.0)).x;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x;
+	shadow += shadow2D(shadowMap, stc.xyz+vec3(-off.x*2.0, -off.y, 0.0)).x;
+
+        return shadow*0.2;
 }
 
 void main() 
@@ -227,7 +227,7 @@ void main()
 
 				float w = 1.0;
 				w -= max(spos.z-far_split.z, 0.0)/transition_domain.z;
-				shadow += pcfShadow(shadowMap3, lpos, 0.25)*w;
+				shadow += pcfShadow(shadowMap3, lpos, 0.25, pos_screen)*w;
 				weight += w;
 				shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0);
 			}
@@ -240,7 +240,7 @@ void main()
 				float w = 1.0;
 				w -= max(spos.z-far_split.y, 0.0)/transition_domain.y;
 				w -= max(near_split.z-spos.z, 0.0)/transition_domain.z;
-				shadow += pcfShadow(shadowMap2, lpos, 0.75)*w;
+				shadow += pcfShadow(shadowMap2, lpos, 0.5, pos_screen)*w;
 				weight += w;
 			}
 
@@ -252,7 +252,7 @@ void main()
 				float w = 1.0;
 				w -= max(spos.z-far_split.x, 0.0)/transition_domain.x;
 				w -= max(near_split.y-spos.z, 0.0)/transition_domain.y;
-				shadow += pcfShadow(shadowMap1, lpos, 0.75)*w;
+				shadow += pcfShadow(shadowMap1, lpos, 0.75, pos_screen)*w;
 				weight += w;
 			}
 
@@ -264,7 +264,7 @@ void main()
 				float w = 1.0;
 				w -= max(near_split.x-spos.z, 0.0)/transition_domain.x;
 				
-				shadow += pcfShadow(shadowMap0, lpos, 1.0)*w;
+				shadow += pcfShadow(shadowMap0, lpos, 1.0, pos_screen)*w;
 				weight += w;
 			}
 		
@@ -298,11 +298,11 @@ void main()
 	
 	//spotlight shadow 1
 	vec4 lpos = shadow_matrix[4]*spos;
-	frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8); 
+	frag_color[2] = pcfShadow(shadowMap4, lpos, 0.8, pos_screen);
 	
 	//spotlight shadow 2
 	lpos = shadow_matrix[5]*spos;
-	frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8); 
+	frag_color[3] = pcfShadow(shadowMap5, lpos, 0.8, pos_screen);
 
 	//frag_color.rgb = pos.xyz;
 	//frag_color.b = shadow;
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index 283a28a0aa47b8c22401ff76c274afedbbf90a00..20936c6460bee17d344a1459ffde905572c114a4 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -110,22 +110,34 @@ export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
 #    fi
 #fi
 
-export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib:"${LD_LIBRARY_PATH}"'
-export SL_CMD='$LL_WRAPPER bin/do-not-directly-run-secondlife-bin'
-export SL_OPT="`cat etc/gridargs.dat` $@"
-
-# Run the program
-eval ${SL_ENV} ${SL_CMD} ${SL_OPT} || LL_RUN_ERR=runerr
+export LD_LIBRARY_PATH="$PWD/lib:${LD_LIBRARY_PATH}"
+
+# Have to deal specially with gridargs.dat; typical contents look like:
+# --channel "Second Life Developer"  --settings settings_developer.xml
+# Simply embedding $(<etc/gridargs.dat) into a command line treats each of
+# Second, Life and Developer as separate args -- no good. We need bash to
+# process quotes using eval.
+# First read it without scanning, then scan that string. Break quoted words
+# into a bash array. Note that if gridargs.dat is empty, or contains only
+# whitespace, the resulting gridargs array will be empty -- zero entries --
+# therefore "${gridargs[@]}" entirely vanishes from the command line below,
+# just as we want.
+eval gridargs=("$(<etc/gridargs.dat)")
+
+# Run the program.
+# Don't quote $LL_WRAPPER because, if empty, it should simply vanish from the
+# command line. But DO quote "$@": preserve separate args as individually
+# quoted. Similar remarks about the contents of gridargs.
+$LL_WRAPPER bin/do-not-directly-run-secondlife-bin "${gridargs[@]}" "$@"
+LL_RUN_ERR=$?
 
 # Handle any resulting errors
-if [ -n "$LL_RUN_ERR" ]; then
-	LL_RUN_ERR_MSG=""
-	if [ "$LL_RUN_ERR" = "runerr" ]; then
-		# generic error running the binary
-		echo '*** Bad shutdown. ***'
-		if [ "`uname -m`" = "x86_64" ]; then
-			echo
-			cat << EOFMARKER
+if [ $LL_RUN_ERR -ne 0 ]; then
+	# generic error running the binary
+	echo '*** Bad shutdown ($LL_RUN_ERR). ***'
+	if [ "$(uname -m)" = "x86_64" ]; then
+		echo
+		cat << EOFMARKER
 You are running the Second Life Viewer on a x86_64 platform.  The
 most common problems when launching the Viewer (particularly
 'bin/do-not-directly-run-secondlife-bin: not found' and 'error while
@@ -134,10 +146,8 @@ distribution's 32-bit compatibility packages.
 For example, on Ubuntu and other Debian-based Linuxes you might run:
 $ sudo apt-get install ia32-libs ia32-libs-gtk ia32-libs-kde ia32-libs-sdl
 EOFMARKER
-		fi
 	fi
 fi
-	
 
 echo
 echo '*******************************************************'
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 3870a3be2ecc71d3d5e122e1b5f21ff6c196bbd1..336760475354c6f094422367d596295088a49cd3 100755
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -3253,6 +3253,10 @@ void LLAgent::processControlRelease(LLMessageSystem *msg, void **)
 void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
 {
 	gAgentQueryManager.mNumPendingQueries--;
+	if (gAgentQueryManager.mNumPendingQueries == 0)
+	{
+		selfStopPhase("fetch_texture_cache_entries");
+	}
 
 	if (!isAgentAvatarValid() || gAgentAvatarp->isDead())
 	{
@@ -3302,13 +3306,12 @@ void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void *
 					else
 					{
 						// no cache of this bake. request upload.
-						gAgentAvatarp->requestLayerSetUpload(baked_index);
+						gAgentAvatarp->invalidateComposite(gAgentAvatarp->getLayerSet(baked_index),TRUE);
 					}
 				}
 			}
 		}
 	}
-
 	llinfos << "Received cached texture response for " << num_results << " textures." << llendl;
 	gAgentAvatarp->outputRezTiming("Fetched agent wearables textures from cache. Will now load them");
 
@@ -3775,7 +3778,15 @@ void LLAgent::sendAgentSetAppearance()
 		return;
 	}
 
-	llinfos << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << llendl;
+	if (!gAgentWearables.changeInProgress())
+	{
+		// Change is fully resolved, can close some open phases.
+		gAgentAvatarp->getPhases().stopPhase("process_initial_wearables_update");
+		gAgentAvatarp->getPhases().stopPhase("wear_inventory_category");
+	}
+	
+	gAgentAvatarp->sendAppearanceChangeMetrics();
+	LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << LL_ENDL;
 	//dumpAvatarTEs( "sendAgentSetAppearance()" );
 
 	LLMessageSystem* msg = gMessageSystem;
@@ -3822,14 +3833,14 @@ void LLAgent::sendAgentSetAppearance()
 	// only update cache entries if we have all our baked textures
 	if (textures_current)
 	{
-		llinfos << "TAT: Sending cached texture data" << llendl;
+		LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL;
 		for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
 		{
 			BOOL generate_valid_hash = TRUE;
 			if (isAgentAvatarValid() && !gAgentAvatarp->isBakedTextureFinal((LLVOAvatarDefines::EBakedTextureIndex)baked_index))
 			{
 				generate_valid_hash = FALSE;
-				llinfos << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << llendl;
+				LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << LL_ENDL;
 			}
 
 			const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash);
diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp
index a8d2222c030cbf9107d45137b543c7bb389d1365..a4c0b056ac192f5384cb81e41fad73520279fb1d 100644
--- a/indra/newview/llagentlistener.cpp
+++ b/indra/newview/llagentlistener.cpp
@@ -126,6 +126,17 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
 		"[\"obj_uuid\"]: id of object to look at, use this or [\"position\"] to indicate the target\n"
 		"[\"position\"]: region position {x, y, z} where to find closest object or avatar to look at",
         &LLAgentListener::lookAt);
+    add("getGroups",
+        "Send information about the agent's groups on [\"reply\"]:\n"
+        "[\"groups\"]: array of group information\n"
+        "[\"id\"]: group id\n"
+        "[\"name\"]: group name\n"
+        "[\"insignia\"]: group insignia texture id\n"
+        "[\"notices\"]: boolean indicating if this user accepts notices from this group\n"
+        "[\"display\"]: boolean indicating if this group is listed in the user's profile\n"
+        "[\"contrib\"]: user's land contribution to this group\n",
+        &LLAgentListener::getGroups,
+        LLSDMap("reply", LLSD()));
 }
 
 void LLAgentListener::requestTeleport(LLSD const & event_data) const
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index dd02a74a381f764d8ba787c04b2ae311186b3b7c..e441f21f904374bc0530159c901b8080464dd4c2 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -955,6 +955,8 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs
 
 	if (isAgentAvatarValid())
 	{
+		//gAgentAvatarp->clearPhases(); // reset phase timers for outfit loading.
+		gAgentAvatarp->getPhases().startPhase("process_initial_wearables_update");
 		gAgentAvatarp->outputRezTiming("Received initial wearables update");
 	}
 
@@ -1448,7 +1450,16 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 	{
 		gAgentAvatarp->setCompositeUpdatesEnabled(TRUE);
 		gAgentAvatarp->updateVisualParams();
-		gAgentAvatarp->invalidateAll();
+
+		// If we have not yet declouded, we may want to use
+		// baked texture UUIDs sent from the first objectUpdate message
+		// don't overwrite these. If we have already declouded, we've saved
+		// these ids as the last known good textures and can invalidate without
+		// re-clouding.
+		if (!gAgentAvatarp->getIsCloud())
+		{
+			gAgentAvatarp->invalidateAll();
+		}
 	}
 
 	// Start rendering & update the server
@@ -1630,10 +1641,11 @@ void LLAgentWearables::queryWearableCache()
 	{
 		if (isAgentAvatarValid())
 		{
+			selfStartPhase("fetch_texture_cache_entries");
 			gAgentAvatarp->outputRezTiming("Fetching textures from cache");
 		}
 
-		llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl;
+		LL_INFOS("Avatar") << gAgentAvatarp->avString() << "Requesting texture cache entry for " << num_queries << " baked textures" << LL_ENDL;
 		gMessageSystem->sendReliable(gAgent.getRegion()->getHost());
 		gAgentQueryManager.mNumPendingQueries++;
 		gAgentQueryManager.mWearablesCacheQueryID++;
@@ -2081,6 +2093,11 @@ boost::signals2::connection LLAgentWearables::addLoadedCallback(loaded_callback_
 	return mLoadedSignal.connect(cb);
 }
 
+bool LLAgentWearables::changeInProgress() const
+{
+	return mCOFChangeInProgress;
+}
+
 void LLAgentWearables::notifyLoadingStarted()
 {
 	mCOFChangeInProgress = true;
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 01cae3ffd86b1cb59b611655b2bf89d7c5cb434f..5932be21c639be6626ed70fdf3fcac6d55ef6888 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -233,6 +233,7 @@ public:
 	typedef boost::signals2::signal<void()>	loaded_signal_t;
 	boost::signals2::connection				addLoadedCallback(loaded_callback_t cb);
 
+	bool									changeInProgress() const;
 	void									notifyLoadingStarted();
 	void									notifyLoadingFinished();
 
diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp
index 8cba54347e3141f744f8dc32970b407e53dc50ca..e2417cdddb3528d17d68897dae5e8f547744e366 100644
--- a/indra/newview/llagentwearablesfetch.cpp
+++ b/indra/newview/llagentwearablesfetch.cpp
@@ -89,6 +89,7 @@ LLInitialWearablesFetch::LLInitialWearablesFetch(const LLUUID& cof_id) :
 {
 	if (isAgentAvatarValid())
 	{
+		gAgentAvatarp->getPhases().startPhase("initial_wearables_fetch");
 		gAgentAvatarp->outputRezTiming("Initial wearables fetch started");
 	}
 }
@@ -107,6 +108,7 @@ void LLInitialWearablesFetch::done()
 	doOnIdleOneTime(boost::bind(&LLInitialWearablesFetch::processContents,this));
 	if (isAgentAvatarValid())
 	{
+		gAgentAvatarp->getPhases().stopPhase("initial_wearables_fetch");
 		gAgentAvatarp->outputRezTiming("Initial wearables fetch done");
 	}
 }
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 33f5373d7e80627cc3e3ad9581040968b2a89dc0..faadfb4b87bcdf86d4797913205964daecbd62bc 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -50,6 +50,11 @@
 #include "llviewerregion.h"
 #include "llwearablelist.h"
 
+std::string self_av_string()
+{
+	return gAgentAvatarp->avString();
+}
+
 // RAII thingy to guarantee that a variable gets reset when the Setter
 // goes out of scope.  More general utility would be handy - TODO:
 // check boost.
@@ -156,6 +161,10 @@ public:
 	{
 		mCatID = cat_id;
 		mAppend = append;
+
+		LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
+		
+		selfStartPhase("wear_inventory_category_callback");
 	}
 	void fire(const LLUUID& item_id)
 	{
@@ -167,13 +176,16 @@ public:
 		 * after the last item has fired the event and dereferenced it -- if all
 		 * the events actually fire!
 		 */
+		LL_DEBUGS("Avatar") << self_av_string() << " fired on copied item, id " << item_id << LL_ENDL;
 	}
 
 protected:
 	~LLWearInventoryCategoryCallback()
 	{
-		llinfos << "done all inventory callbacks" << llendl;
+		LL_INFOS("Avatar") << self_av_string() << "done all inventory callbacks" << LL_ENDL;
 		
+		selfStopPhase("wear_inventory_category_callback");
+
 		// Is the destructor called by ordinary dereference, or because the app's shutting down?
 		// If the inventory callback manager goes away, we're shutting down, no longer want the callback.
 		if( LLInventoryCallbackManager::is_instantiated() )
@@ -182,7 +194,7 @@ protected:
 		}
 		else
 		{
-			llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
+			llwarns << self_av_string() << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
 		}
 	}
 
@@ -212,11 +224,14 @@ LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit
 	mFireCount(0),
 	mUpdateBaseOrder(update_base_outfit_ordering)
 {
+	selfStartPhase("update_appearance_on_destroy");
 }
 
 LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
 {
-	llinfos << "done update appearance on destroy" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL;
+
+	selfStopPhase("update_appearance_on_destroy");
 	
 	if (!LLApp::isExiting())
 	{
@@ -229,7 +244,7 @@ void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
 	LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item);
 	const std::string item_name = item ? item->getName() : "ITEM NOT FOUND";
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << LL_ENDL;
 #endif
 	mFireCount++;
 }
@@ -339,11 +354,16 @@ LLWearableHoldingPattern::LLWearableHoldingPattern():
 			 
 	}
 	sActiveHoldingPatterns.insert(this);
+	selfStartPhase("holding_pattern");
 }
 
 LLWearableHoldingPattern::~LLWearableHoldingPattern()
 {
 	sActiveHoldingPatterns.erase(this);
+	if (isMostRecent())
+	{
+		selfStopPhase("holding_pattern");
+	}
 }
 
 bool LLWearableHoldingPattern::isMostRecent()
@@ -390,9 +410,10 @@ void LLWearableHoldingPattern::checkMissingWearables()
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway why don't we actually skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
-		
+
 	std::vector<S32> found_by_type(LLWearableType::WT_COUNT,0);
 	std::vector<S32> requested_by_type(LLWearableType::WT_COUNT,0);
 	for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
@@ -408,7 +429,7 @@ void LLWearableHoldingPattern::checkMissingWearables()
 	{
 		if (requested_by_type[type] > found_by_type[type])
 		{
-			llwarns << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl;
+			llwarns << self_av_string() << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl;
 		}
 		if (found_by_type[type] > 0)
 			continue;
@@ -425,11 +446,13 @@ void LLWearableHoldingPattern::checkMissingWearables()
 			mTypesToRecover.insert(type);
 			mTypesToLink.insert(type);
 			recoverMissingWearable((LLWearableType::EType)type);
-			llwarns << "need to replace " << type << llendl; 
+			llwarns << self_av_string() << "need to replace " << type << llendl; 
 		}
 	}
 
 	resetTime(60.0F);
+
+	selfStartPhase("get_missing_wearables");
 	if (!pollMissingWearables())
 	{
 		doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
@@ -445,13 +468,14 @@ void LLWearableHoldingPattern::onAllComplete()
 
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway need to skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 
 	// Activate all gestures in this folder
 	if (mGestItems.count() > 0)
 	{
-		llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Activating " << mGestItems.count() << " gestures" << LL_ENDL;
 		
 		LLGestureMgr::instance().activateGestures(mGestItems);
 		
@@ -468,13 +492,13 @@ void LLWearableHoldingPattern::onAllComplete()
 	}
 
 	// Update wearables.
-	llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Updating agent wearables with " << mResolved << " wearable items " << LL_ENDL;
 	LLAppearanceMgr::instance().updateAgentWearables(this, false);
 	
 	// Update attachments to match those requested.
 	if (isAgentAvatarValid())
 	{
-		llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Updating " << mObjItems.count() << " attachments" << LL_ENDL;
 		LLAgentWearables::userUpdateAttachments(mObjItems);
 	}
 
@@ -492,9 +516,12 @@ void LLWearableHoldingPattern::onAllComplete()
 
 void LLWearableHoldingPattern::onFetchCompletion()
 {
+	selfStopPhase("get_wearables");
+		
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 
 	checkMissingWearables();
@@ -505,7 +532,8 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 
 	bool completed = isFetchCompleted();
@@ -514,14 +542,14 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
 
 	if (done)
 	{
-		llinfos << "polling, done status: " << completed << " timed out " << timed_out
-				<< " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
+		LL_INFOS("Avatar") << self_av_string() << "polling, done status: " << completed << " timed out " << timed_out
+				<< " elapsed " << mWaitTime.getElapsedTimeF32() << LL_ENDL;
 
 		mFired = true;
 		
 		if (timed_out)
 		{
-			llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
+			llwarns << self_av_string() << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
 		}
 
 		onFetchCompletion();
@@ -543,6 +571,7 @@ public:
 		if (!mHolder->isMostRecent())
 		{
 			llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+			// runway skip here?
 		}
 
 		llinfos << "Recovered item link for type " << mType << llendl;
@@ -569,12 +598,12 @@ public:
 			}
 			else
 			{
-				llwarns << "inventory item not found for recovered wearable" << llendl;
+				llwarns << self_av_string() << "inventory item not found for recovered wearable" << llendl;
 			}
 		}
 		else
 		{
-			llwarns << "inventory link not found for recovered wearable" << llendl;
+			llwarns << self_av_string() << "inventory link not found for recovered wearable" << llendl;
 		}
 	}
 private:
@@ -596,10 +625,11 @@ public:
 	{
 		if (!mHolder->isMostRecent())
 		{
-			llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+			// runway skip here?
+			llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 		}
 
-		llinfos << "Recovered item for type " << mType << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Recovered item for type " << mType << LL_ENDL;
 		LLViewerInventoryItem *itemp = gInventory.getItem(item_id);
 		mWearable->setItemID(item_id);
 		LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder);
@@ -626,7 +656,8 @@ void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 	
 		// Try to recover by replacing missing wearable with a new one.
@@ -665,7 +696,7 @@ void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
 		if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable))
 		{
 			// Wearable link that was never resolved; remove links to it from COF
-			llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl;
+			LL_INFOS("Avatar") << self_av_string() << "removing link for unresolved item " << data.mItemID.asString() << LL_ENDL;
 			LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
 		}
 	}
@@ -675,7 +706,8 @@ bool LLWearableHoldingPattern::pollMissingWearables()
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		// runway skip here?
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 	
 	bool timed_out = isTimedOut();
@@ -684,15 +716,17 @@ bool LLWearableHoldingPattern::pollMissingWearables()
 
 	if (!done)
 	{
-		llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size()
+		LL_INFOS("Avatar") << self_av_string() << "polling missing wearables, waiting for items " << mTypesToRecover.size()
 				<< " links " << mTypesToLink.size()
 				<< " wearables, timed out " << timed_out
 				<< " elapsed " << mWaitTime.getElapsedTimeF32()
-				<< " done " << done << llendl;
+				<< " done " << done << LL_ENDL;
 	}
 
 	if (done)
 	{
+		selfStopPhase("get_missing_wearables");
+
 		gAgentAvatarp->debugWearablesLoaded();
 
 		// BAP - if we don't call clearCOFLinksForMissingWearables()
@@ -722,14 +756,14 @@ void LLWearableHoldingPattern::handleLateArrivals()
 	}
 	if (!isMostRecent())
 	{
-		llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl;
+		llwarns << self_av_string() << "Late arrivals not handled - outfit change no longer valid" << llendl;
 	}
 	if (!mIsAllComplete)
 	{
-		llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl;
+		llwarns << self_av_string() << "Late arrivals not handled - in middle of missing wearables processing" << llendl;
 	}
 
-	llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << LL_ENDL;
 
 	// Update mFoundList using late-arriving wearables.
 	std::set<LLWearableType::EType> replaced_types;
@@ -805,19 +839,19 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
 {
 	if (!isMostRecent())
 	{
-		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
+		llwarns << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 	}
 	
 	mResolved += 1;  // just counting callbacks, not successes.
-	llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "resolved " << mResolved << "/" << getFoundList().size() << LL_ENDL;
 	if (!wearable)
 	{
-		llwarns << "no wearable found" << llendl;
+		llwarns << self_av_string() << "no wearable found" << llendl;
 	}
 
 	if (mFired)
 	{
-		llwarns << "called after holder fired" << llendl;
+		llwarns << self_av_string() << "called after holder fired" << llendl;
 		if (wearable)
 		{
 			mLateArrivals.insert(wearable);
@@ -843,7 +877,7 @@ void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
 			// Failing this means inventory or asset server are corrupted in a way we don't handle.
 			if ((data.mWearableType >= LLWearableType::WT_COUNT) || (wearable->getType() != data.mWearableType))
 			{
-				llwarns << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl;
+				llwarns << self_av_string() << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl;
 				break;
 			}
 
@@ -1391,8 +1425,8 @@ void LLAppearanceMgr::filterWearableItems(
 
 // Create links to all listed items.
 void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
-								  LLInventoryModel::item_array_t& items,
-								  LLPointer<LLInventoryCallback> cb)
+							  LLInventoryModel::item_array_t& items,
+							  LLPointer<LLInventoryCallback> cb)
 {
 	for (S32 i=0; i<items.count(); i++)
 	{
@@ -1408,7 +1442,7 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
 		const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid);
 		const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND";
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-		llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << LL_ENDL;
 #endif
 	}
 }
@@ -1416,7 +1450,7 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
 void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 {
 	LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
-	llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "starting, cat '" << (pcat ? pcat->getName() : "[UNKNOWN]") << "'" << LL_ENDL;
 
 	const LLUUID cof = getCOF();
 
@@ -1478,26 +1512,26 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 	gInventory.notifyObservers();
 
 	// Create links to new COF contents.
-	llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "creating LLUpdateAppearanceOnDestroy" << LL_ENDL;
 	LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(!append);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking body items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking body items" << LL_ENDL;
 #endif
 	linkAll(cof, body_items, link_waiter);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking wear items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking wear items" << LL_ENDL;
 #endif
 	linkAll(cof, wear_items, link_waiter);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking obj items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking obj items" << LL_ENDL;
 #endif
 	linkAll(cof, obj_items, link_waiter);
 
 #ifndef LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "Linking gesture items" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "Linking gesture items" << LL_ENDL;
 #endif
 	linkAll(cof, gest_items, link_waiter);
 
@@ -1506,7 +1540,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 	{
 		createBaseOutfitLink(category, link_waiter);
 	}
-	llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl;
+	LL_DEBUGS("Avatar") << self_av_string() << "waiting for LLUpdateAppearanceOnDestroy" << LL_ENDL;
 }
 
 void LLAppearanceMgr::updatePanelOutfitName(const std::string& name)
@@ -1663,7 +1697,7 @@ void LLAppearanceMgr::enforceItemRestrictions()
 			 ++it)
 		{
 			LLViewerInventoryItem *item = *it;
-			llinfos << "purging duplicate or excess item " << item->getName() << llendl;
+			LL_DEBUGS("Avatar") << self_av_string() << "purging duplicate or excess item " << item->getName() << LL_ENDL;
 			gInventory.purgeObject(item->getUUID());
 		}
 		gInventory.notifyObservers();
@@ -1678,9 +1712,11 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
 		return;
 	}
 
+	LLVOAvatar::ScopedPhaseSetter(gAgentAvatarp,"update_appearance_from_cof");
+	
 	BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF);
 
-	llinfos << "starting" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
 
 	//checking integrity of the COF in terms of ordering of wearables, 
 	//checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state)
@@ -1772,12 +1808,14 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
 		}
 	}
 
+	selfStartPhase("get_wearables");
+
 	for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin();
 		 it != holder->getFoundList().end(); ++it)
 	{
 		LLFoundData& found = *it;
 
-		lldebugs << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
+		lldebugs << self_av_string() << "waiting for onWearableAssetFetch callback, asset " << found.mAssetID.asString() << llendl;
 
 		// Fetch the wearables about to be worn.
 		LLWearableList::instance().getAsset(found.mAssetID,
@@ -1849,11 +1887,15 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
 {
 	if(!category) return;
 
+	selfClearPhases();
+	selfStartPhase("wear_inventory_category");
+
 	gAgentWearables.notifyLoadingStarted();
 
-	llinfos << "wearInventoryCategory( " << category->getName()
-			 << " )" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName()
+			 << " )" << LL_ENDL;
 
+	selfStartPhase("wear_inventory_category_fetch");
 	callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
 														   &LLAppearanceMgr::instance(),
 														   category->getUUID(), copy, append));
@@ -1861,7 +1903,9 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
 
 void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append)
 {
-	llinfos << "starting" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
+
+	selfStopPhase("wear_inventory_category_fetch");
 	
 	// We now have an outfit ready to be copied to agent inventory. Do
 	// it, and wear that outfit normally.
@@ -1944,8 +1988,8 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
 	// wearables being dirty.
 	if(!category) return;
 
-	llinfos << "wearInventoryCategoryOnAvatar( " << category->getName()
-			 << " )" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategoryOnAvatar '" << category->getName()
+			 << "'" << LL_ENDL;
 			 	
 	if (gAgentCamera.cameraCustomizeAvatar())
 	{
@@ -1958,7 +2002,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
 
 void LLAppearanceMgr::wearOutfitByName(const std::string& name)
 {
-	llinfos << "Wearing category " << name << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL;
 	//inc_busy_count();
 
 	LLInventoryModel::cat_array_t cat_array;
@@ -2281,7 +2325,7 @@ const std::string OTHER_GESTURES_FOLDER = "Other Gestures";
 
 void LLAppearanceMgr::copyLibraryGestures()
 {
-	llinfos << "Copying library gestures" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "Copying library gestures" << LL_ENDL;
 
 	// Copy gestures
 	LLUUID lib_gesture_cat_id =
@@ -2337,11 +2381,11 @@ void LLAppearanceMgr::copyLibraryGestures()
 		LLUUID cat_id = findDescendentCategoryIDByName(lib_gesture_cat_id,folder_name);
 		if (cat_id.isNull())
 		{
-			llwarns << "failed to find gesture folder for " << folder_name << llendl;
+			llwarns << self_av_string() << "failed to find gesture folder for " << folder_name << llendl;
 		}
 		else
 		{
-			llinfos << "initiating fetch and copy for " << folder_name << " cat_id " << cat_id << llendl;
+			LL_DEBUGS("Avatar") << self_av_string() << "initiating fetch and copy for " << folder_name << " cat_id " << cat_id << LL_ENDL;
 			callAfterCategoryFetch(cat_id,
 								   boost::bind(&LLAppearanceMgr::shallowCopyCategory,
 											   &LLAppearanceMgr::instance(),
@@ -2355,7 +2399,7 @@ void LLAppearanceMgr::autopopulateOutfits()
 	// If this is the very first time the user has logged into viewer2+ (from a legacy viewer, or new account)
 	// then auto-populate outfits from the library into the My Outfits folder.
 
-	llinfos << "avatar fully visible" << llendl;
+	LL_INFOS("Avatar") << self_av_string() << "avatar fully visible" << LL_ENDL;
 
 	static bool check_populate_my_outfits = true;
 	if (check_populate_my_outfits && 
@@ -2731,7 +2775,7 @@ void LLAppearanceMgr::dumpCat(const LLUUID& cat_id, const std::string& msg)
 }
 
 void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items,
-										const std::string& msg)
+									const std::string& msg)
 {
 	for (S32 i=0; i<items.count(); i++)
 	{
@@ -2742,9 +2786,8 @@ void LLAppearanceMgr::dumpItemArray(const LLInventoryModel::item_array_t& items,
 		{
 			asset_id = linked_item->getAssetUUID();
 		}
-		llinfos << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << msg << " " << i <<" " << (item ? item->getName() : "(nullitem)") << " " << asset_id.asString() << LL_ENDL;
 	}
-	llinfos << llendl;
 }
 
 LLAppearanceMgr::LLAppearanceMgr():
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 4d94c90cc6282a158e7a2a1d7f2a16a75f391343..ed04b5bf383bf5bbbc640dd539c39da912f70e9c 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -93,6 +93,7 @@
 #include "llsecondlifeurls.h"
 #include "llupdaterservice.h"
 #include "llcallfloater.h"
+#include "llfloatertexturefetchdebugger.h"
 
 // Linden library includes
 #include "llavatarnamecache.h"
@@ -111,6 +112,8 @@
 #include "llnotifications.h"
 #include "llnotificationsutil.h"
 
+#include "llleap.h"
+
 // Third party library includes
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
@@ -124,7 +127,6 @@
 #endif
 
 #include "llapr.h"
-#include "apr_dso.h"
 #include <boost/lexical_cast.hpp>
 
 #include "llviewerkeyboard.h"
@@ -161,6 +163,7 @@
 #include "llcontainerview.h"
 #include "lltooltip.h"
 
+#include "llsdutil.h"
 #include "llsdserialize.h"
 
 #include "llworld.h"
@@ -561,7 +564,6 @@ static void settings_modify()
 	LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4]
 	gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession;
 	gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline");
-	gAuditTexture = gSavedSettings.getBOOL("AuditTexture");
 }
 
 class LLFastTimerLogThread : public LLThread
@@ -732,12 +734,12 @@ bool LLAppViewer::init()
 	
 	{
 		// Viewer metrics initialization
-		static LLCachedControl<bool> metrics_submode(gSavedSettings,
-													 "QAModeMetrics",
-													 false,
-													 "Enables QA features (logging, faster cycling) for metrics collector");
+		//static LLCachedControl<bool> metrics_submode(gSavedSettings,
+		//											 "QAModeMetrics",
+		//											 false,
+		//											 "Enables QA features (logging, faster cycling) for metrics collector");
 
-		if (metrics_submode)
+		if (gSavedSettings.getBOOL("QAModeMetrics"))
 		{
 			app_metrics_qa_mode = true;
 			app_metrics_interval = METRICS_INTERVAL_QA;
@@ -1048,11 +1050,38 @@ bool LLAppViewer::init()
 
 
 	gGLActive = FALSE;
+
+	// Iterate over --leap command-line options. But this is a bit tricky: if
+	// there's only one, it won't be an array at all.
+	LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand"));
+	LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL;
+	if (LeapCommand.isDefined() && ! LeapCommand.isArray())
+	{
+		// If LeapCommand is actually a scalar value, make an array of it.
+		// Have to do it in two steps because LeapCommand.append(LeapCommand)
+		// trashes content! :-P
+		LLSD item(LeapCommand);
+		LeapCommand.append(item);
+	}
+	BOOST_FOREACH(const std::string& leap, llsd::inArray(LeapCommand))
+	{
+		LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL;
+		// We don't have any better description of this plugin than the
+		// user-specified command line. Passing "" causes LLLeap to derive a
+		// description from the command line itself.
+		// Suppress LLLeap::Error exception: trust LLLeap's own logging. We
+		// don't consider any one --leap command mission-critical, so if one
+		// fails, log it, shrug and carry on.
+		LLLeap::create("", leap, false); // exception=false
+	}
+
 	if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0)
 	{
-		loadEventHostModule(gSavedSettings.getS32("QAModeEventHostPort"));
+		LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: "
+							 << "lleventhost no longer supported as a dynamic library"
+							 << LL_ENDL;
 	}
-	
+
 	LLViewerMedia::initClass();
 	LL_INFOS("InitInfo") << "Viewer media initialized." << LL_ENDL ;
 
@@ -1229,7 +1258,7 @@ bool LLAppViewer::mainLoop()
 			if(mem_leak_instance)
 			{
 				mem_leak_instance->idle() ;				
-			}			
+			}							
 
             // canonical per-frame event
             mainloop.post(newFrame);
@@ -1350,13 +1379,11 @@ bool LLAppViewer::mainLoop()
 					ms_sleep(500);
 				}
 
-				static const F64 FRAME_SLOW_THRESHOLD = 0.5; //2 frames per seconds				
 				const F64 max_idle_time = llmin(.005*10.0*gFrameTimeSeconds, 0.005); // 5 ms a second
 				idleTimer.reset();
-				bool is_slow = (frameTimer.getElapsedTimeF64() > FRAME_SLOW_THRESHOLD) ;
 				S32 total_work_pending = 0;
 				S32 total_io_pending = 0;	
-				while(!is_slow)//do not unpause threads if the frame rates are very low.
+				while(1)
 				{
 					S32 work_pending = 0;
 					S32 io_pending = 0;
@@ -1416,6 +1443,17 @@ bool LLAppViewer::mainLoop()
 					LLLFSThread::sLocal->pause(); 
 				}									
 
+				//texture fetching debugger
+				if(LLTextureFetchDebugger::isEnabled())
+				{
+					LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
+						LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
+					if(tex_fetch_debugger_instance)
+					{
+						tex_fetch_debugger_instance->idle() ;				
+					}
+				}
+
 				if ((LLStartUp::getStartupState() >= STATE_CLEANUP) &&
 					(frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD))
 				{
@@ -1524,17 +1562,24 @@ bool LLAppViewer::cleanup()
 		gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp");
 	}
 
-	// *TODO - generalize this and move DSO wrangling to a helper class -brad
-	std::set<struct apr_dso_handle_t *>::const_iterator i;
-	for(i = mPlugins.begin(); i != mPlugins.end(); ++i)
 	{
-		int (*ll_plugin_stop_func)(void) = NULL;
-		apr_status_t rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_stop_func, *i, "ll_plugin_stop");
-		ll_plugin_stop_func();
-
-		rv = apr_dso_unload(*i);
-	}
-	mPlugins.clear();
+		// Kill off LLLeap objects. We can find them all because LLLeap is derived
+		// from LLInstanceTracker. But collect instances first: LLInstanceTracker
+		// specifically forbids adding/deleting instances while iterating.
+		std::vector<LLLeap*> leaps;
+		leaps.reserve(LLLeap::instanceCount());
+		for (LLLeap::instance_iter li(LLLeap::beginInstances()), lend(LLLeap::endInstances());
+			 li != lend; ++li)
+		{
+			leaps.push_back(&*li);
+		}
+		// Okay, now trash them all. We don't have to NULL or erase the entry
+		// in 'leaps' because the whole vector is going away momentarily.
+		BOOST_FOREACH(LLLeap* leap, leaps)
+		{
+			delete leap;
+		}
+	} // destroy 'leaps'
 
 	//flag all elements as needing to be destroyed immediately
 	// to ensure shutdown order
@@ -1959,7 +2004,7 @@ bool LLAppViewer::initThreads()
 	static const bool enable_threads = true;
 #endif
 
-	LLImage::initClass();
+	LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange"));
 
 	LLVFSThread::initClass(enable_threads && false);
 	LLLFSThread::initClass(enable_threads && false);
@@ -4207,6 +4252,7 @@ void LLAppViewer::idle()
 			// The 5-second interval is nice for this purpose.  If the object debug
 			// bit moves or is disabled, please give this a suitable home.
 			LLViewerAssetStatsFF::record_fps_main(gFPSClamped);
+			LLViewerAssetStatsFF::record_avatar_stats();
 		}
 	}
 
@@ -4254,7 +4300,8 @@ void LLAppViewer::idle()
 		static LLTimer report_interval;
 
 		// *TODO:  Add configuration controls for this
-		if (report_interval.getElapsedTimeF32() >= app_metrics_interval)
+		F32 seconds = report_interval.getElapsedTimeF32();
+		if (seconds >= app_metrics_interval)
 		{
 			metricsSend(! gDisconnected);
 			report_interval.reset();
@@ -4964,87 +5011,6 @@ void LLAppViewer::handleLoginComplete()
 	writeDebugInfo();
 }
 
-// *TODO - generalize this and move DSO wrangling to a helper class -brad
-void LLAppViewer::loadEventHostModule(S32 listen_port)
-{
-	std::string dso_name =
-#if LL_WINDOWS
-	    "lleventhost.dll";
-#elif LL_DARWIN
-	    "liblleventhost.dylib";
-#else
-	    "liblleventhost.so";
-#endif
-
-	std::string dso_path = gDirUtilp->findFile(dso_name,
-		gDirUtilp->getAppRODataDir(),
-		gDirUtilp->getExecutableDir());
-
-	if(dso_path == "")
-	{
-		llerrs << "QAModeEventHost requested but module \"" << dso_name << "\" not found!" << llendl;
-		return;
-	}
-
-	LL_INFOS("eventhost") << "Found lleventhost at '" << dso_path << "'" << LL_ENDL;
-#if ! defined(LL_WINDOWS)
-	{
-		std::string outfile("/tmp/lleventhost.file.out");
-		std::string command("file '" + dso_path + "' > '" + outfile + "' 2>&1");
-		int rc = system(command.c_str());
-		if (rc != 0)
-		{
-			LL_WARNS("eventhost") << command << " ==> " << rc << ':' << LL_ENDL;
-		}
-		else
-		{
-			LL_INFOS("eventhost") << command << ':' << LL_ENDL;
-		}
-		{
-			std::ifstream reader(outfile.c_str());
-			std::string line;
-			while (std::getline(reader, line))
-			{
-				size_t len = line.length();
-				if (len && line[len-1] == '\n')
-					line.erase(len-1);
-				LL_INFOS("eventhost") << line << LL_ENDL;
-			}
-		}
-		remove(outfile.c_str());
-	}
-#endif // LL_WINDOWS
-
-	apr_dso_handle_t * eventhost_dso_handle = NULL;
-	apr_pool_t * eventhost_dso_memory_pool = NULL;
-
-	//attempt to load the shared library
-	apr_pool_create(&eventhost_dso_memory_pool, NULL);
-	apr_status_t rv = apr_dso_load(&eventhost_dso_handle,
-		dso_path.c_str(),
-		eventhost_dso_memory_pool);
-	llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle));
-	llassert_always(eventhost_dso_handle != NULL);
-
-	int (*ll_plugin_start_func)(LLSD const &) = NULL;
-	rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_start_func, eventhost_dso_handle, "ll_plugin_start");
-
-	llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle));
-	llassert_always(ll_plugin_start_func != NULL);
-
-	LLSD args;
-	args["listen_port"] = listen_port;
-
-	int status = ll_plugin_start_func(args);
-
-	if(status != 0)
-	{
-		llerrs << "problem loading eventhost plugin, status: " << status << llendl;
-	}
-
-	mPlugins.insert(eventhost_dso_handle);
-}
-
 void LLAppViewer::launchUpdater()
 {
 		LLSD query_map = LLSD::emptyMap();
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 71a7868191ae95cee19acb390ce02d445faba688..f7d019ccbacfd5ba235832687f32a243f21d62b0 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -41,8 +41,6 @@ class LLTextureFetch;
 class LLWatchdogTimeout;
 class LLUpdaterService;
 
-struct apr_dso_handle_t;
-
 class LLAppViewer : public LLApp
 {
 public:
@@ -220,8 +218,6 @@ private:
 
     void sendLogoutRequest();
     void disconnectViewer();
-
-	void loadEventHostModule(S32 listen_port);
 	
 	// *FIX: the app viewer class should be some sort of singleton, no?
 	// Perhaps its child class is the singleton and this should be an abstract base.
@@ -270,8 +266,6 @@ private:
 
     LLAllocator mAlloc;
 
-	std::set<struct apr_dso_handle_t*> mPlugins;
-
 	LLFrameTimer mMemCheckTimer;
 	
 	boost::scoped_ptr<LLUpdaterService> mUpdater;
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 6931b55c4c5773faefc273537fa22ff4c07e1da0..bad60a9757af3e116170e330d8d1891f136b4d80 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -26,6 +26,10 @@
 
 #include "llviewerprecompiledheaders.h"
 
+#ifdef INCLUDE_VLD
+#include "vld.h"
+#endif
+
 #include "llappviewerwin32.h"
 
 #include "llmemtype.h"
@@ -105,6 +109,14 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)
 {
+#ifdef INCLUDE_VLD
+	// only works for debug builds (hard coded into vld.h)
+	#ifdef _DEBUG
+		// start with Visual Leak Detector turned off
+		VLDGlobalDisable();
+	#endif // _DEBUG
+#endif // INCLUDE_VLD
+
 	LLMemType mt1(LLMemType::MTYPE_STARTUP);
 
 	const S32 MAX_HEAPS = 255;
diff --git a/indra/newview/llassetuploadqueue.cpp b/indra/newview/llassetuploadqueue.cpp
index 7e50098a173d847e51284c4129b88a63b8daf41b..f943759bb89c66fe1e27d633b60ea195ad307637 100644
--- a/indra/newview/llassetuploadqueue.cpp
+++ b/indra/newview/llassetuploadqueue.cpp
@@ -123,7 +123,9 @@ public:
 			for(LLSD::array_const_iterator line	= compile_errors.beginArray();
 				line < compile_errors.endArray(); line++)
 			{
-				mSupplier->log(line->asString());
+				std::string str = line->asString();
+				str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
+				mSupplier->log(str);
 				llinfos << content["errors"] << llendl;
 			}
 		}
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index 267e0f03b995aca98e482744363359564efa04b0..fdd4565e50dbc3e88c99d6786895b47222ef2dbf 100755
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -744,6 +744,11 @@ void LLAvatarActions::shareWithAvatars()
 
 	LLFloaterAvatarPicker* picker =
 		LLFloaterAvatarPicker::show(boost::bind(give_inventory, _1, _2), TRUE, FALSE);
+	if (!picker)
+	{
+		return;
+	}
+
 	picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable));
 	picker->openFriendsTab();
 	LLNotificationsUtil::add("ShareNotification");
diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp
index b1cd83a1fba65a706b50e09e48290f711b2a6bb4..706bc42ea07cac99f442f40438bfd7fa9bd58e31 100644
--- a/indra/newview/llavatarpropertiesprocessor.cpp
+++ b/indra/newview/llavatarpropertiesprocessor.cpp
@@ -33,6 +33,7 @@
 #include "llagentpicksinfo.h"
 #include "lldateutil.h"
 #include "llviewergenericmessage.h"
+#include "llstartup.h"
 
 // Linden library includes
 #include "llavatarconstants.h"	// AVATAR_TRANSACTED, etc.
@@ -113,6 +114,14 @@ void LLAvatarPropertiesProcessor::sendGenericRequest(const LLUUID& avatar_id, EA
 
 void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequest(const LLUUID& avatar_id)
 {
+	// this is the startup state when send_complete_agent_movement() message is sent.
+	// Before this, the AvatarPropertiesRequest message  
+	// won't work so don't bother trying
+	if (LLStartUp::getStartupState() <= STATE_AGENT_SEND)
+	{
+		return;
+	}
+
 	if (isPendingRequest(avatar_id, APT_PROPERTIES))
 	{
 		// waiting for a response, don't re-request
diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp
index 65c61c4a8bdf456c6b41dc7f9a2c505d1b525ad8..17d403bbe1040b492658123af45a8e606e99037a 100644
--- a/indra/newview/llcommandlineparser.cpp
+++ b/indra/newview/llcommandlineparser.cpp
@@ -342,6 +342,15 @@ bool LLCommandLineParser::parseCommandLine(int argc, char **argv)
     return parseAndStoreResults(clp);
 }
 
+// TODO:
+// - Break out this funky parsing logic into separate method
+// - Unit-test it with tests like LLStringUtil::getTokens() (the command-line
+//   overload that supports quoted tokens)
+// - Unless this logic offers significant semantic benefits, replace it with
+//   LLStringUtil::getTokens(). This would fix a known bug: you cannot --set a
+//   string-valued variable to the empty string, because empty strings are
+//   eliminated below.
+
 bool LLCommandLineParser::parseCommandLineString(const std::string& str)
 {
     // Split the string content into tokens
diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp
index a42677845ecc0b7c00254bcb580aba3bc531fdc4..4f5f9e22b69ddfe6ef1256d37bd945ec07b6789b 100644
--- a/indra/newview/llcompilequeue.cpp
+++ b/indra/newview/llcompilequeue.cpp
@@ -100,6 +100,7 @@ BOOL LLFloaterScriptQueue::postBuild()
 {
 	childSetAction("close",onCloseBtn,this);
 	getChildView("close")->setEnabled(FALSE);
+	setVisible(true);
 	return TRUE;
 }
 
@@ -158,7 +159,6 @@ void LLFloaterScriptQueue::addObject(const LLUUID& id)
 
 BOOL LLFloaterScriptQueue::start()
 {
-	//llinfos << "LLFloaterCompileQueue::start()" << llendl;
 	std::string buffer;
 
 	LLSelectMgr *mgr = LLSelectMgr::getInstance();
@@ -178,7 +178,7 @@ BOOL LLFloaterScriptQueue::start()
 	args["[COUNT]"] = llformat ("%d", mObjectIDs.count());
 	buffer = getString ("Starting", args);
 	
-	getChild<LLScrollListCtrl>("queue output")->setCommentText(buffer);
+	getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
 
 	return nextObject();
 }
@@ -211,7 +211,7 @@ BOOL LLFloaterScriptQueue::nextObject()
 	if(isDone() && !mDone)
 	{
 		mDone = true;
-		getChild<LLScrollListCtrl>("queue output")->setCommentText(getString("Done"));
+		getChild<LLScrollListCtrl>("queue output")->addSimpleElement(getString("Done"), ADD_BOTTOM);
 		getChildView("close")->setEnabled(TRUE);
 	}
 	return successful_start;
@@ -277,7 +277,7 @@ public:
 			return;
 		}
 
-		queue->getChild<LLScrollListCtrl>("queue output")->setCommentText(message);
+		queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM);
 	}
 		
 private:
@@ -464,7 +464,7 @@ void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
 	}
 	if(queue && (buffer.size() > 0)) 
 	{
-		queue->getChild<LLScrollListCtrl>("queue output")->setCommentText(buffer);
+		queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
 	}
 	delete data;
 }
@@ -637,7 +637,7 @@ void LLFloaterResetQueue::handleInventory(LLViewerObject* viewer_obj,
 				LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
 				std::string buffer;
 				buffer = getString("Resetting") + (": ") + item->getName();
-				getChild<LLScrollListCtrl>("queue output")->setCommentText(buffer);
+				getChild<LLScrollListCtrl>("queue output")->addSimpleElement(buffer, ADD_BOTTOM);
 				LLMessageSystem* msg = gMessageSystem;
 				msg->newMessageFast(_PREHASH_ScriptReset);
 				msg->nextBlockFast(_PREHASH_AgentData);
@@ -690,7 +690,7 @@ void LLFloaterRunQueue::handleInventory(LLViewerObject* viewer_obj,
 				LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");
 				std::string buffer;
 				buffer = getString("Running") + (": ") + item->getName();
-				list->setCommentText(buffer);
+				list->addSimpleElement(buffer, ADD_BOTTOM);
 
 				LLMessageSystem* msg = gMessageSystem;
 				msg->newMessageFast(_PREHASH_SetScriptRunning);
@@ -745,7 +745,7 @@ void LLFloaterNotRunQueue::handleInventory(LLViewerObject* viewer_obj,
 				LLScrollListCtrl* list = getChild<LLScrollListCtrl>("queue output");
 				std::string buffer;
 				buffer = getString("NotRunning") + (": ") +item->getName();
-				list->setCommentText(buffer);
+				list->addSimpleElement(buffer, ADD_BOTTOM);
 	
 				LLMessageSystem* msg = gMessageSystem;
 				msg->newMessageFast(_PREHASH_SetScriptRunning);
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
index 7d3170cb76504149753aa9243920a28980fa0230..29b1d23d7df24737563a50305aeba8f86510c506 100644
--- a/indra/newview/lldebugview.cpp
+++ b/indra/newview/lldebugview.cpp
@@ -68,8 +68,6 @@ LLDebugView::~LLDebugView()
 	gDebugView = NULL;
 	gTextureView = NULL;
 	gSceneView = NULL;
-	gTextureSizeView = NULL;
-	gTextureCategoryView = NULL;
 }
 
 void LLDebugView::init()
@@ -117,35 +115,11 @@ void LLDebugView::init()
 	LLTextureView::Params tvp;
 	tvp.name("gTextureView");
 	tvp.rect(r);
-	tvp.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
+	tvp.follows.flags(FOLLOWS_TOP|FOLLOWS_LEFT);
 	tvp.visible(false);
 	gTextureView = LLUICtrlFactory::create<LLTextureView>(tvp);
 	addChild(gTextureView);
 	//gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE);
-
-
-	if(gAuditTexture)
-	{
-		r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100);
-		LLTextureSizeView::Params tsv ;
-		tsv.name("gTextureSizeView");
-		tsv.rect(r);
-		tsv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
-		tsv.visible(false);
-		gTextureSizeView = LLUICtrlFactory::create<LLTextureSizeView>(tsv);
-		addChild(gTextureSizeView);
-		gTextureSizeView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_SIZE) ;
-
-		r.set(150, rect.getHeight() - 50, 900 + LLViewerTexture::getTotalNumOfCategories() * 30, 100);
-		LLTextureSizeView::Params tcv ;
-		tcv.name("gTextureCategoryView");
-		tcv.rect(r);
-		tcv.follows.flags(FOLLOWS_BOTTOM|FOLLOWS_LEFT);
-		tcv.visible(false);
-		gTextureCategoryView = LLUICtrlFactory::create<LLTextureSizeView>(tcv);
-		gTextureCategoryView->setType(LLTextureSizeView::TEXTURE_MEM_OVER_CATEGORY);
-		addChild(gTextureCategoryView);
-	}
 }
 
 void LLDebugView::draw()
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index a93b2b71de394c7198ebff27f33b26c7fb281787..bf8338e5f273d1794ccb61c6b13ebee3731c3d53 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -216,12 +216,14 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 		return TRUE;
 	}
 
+#if 0 //THIS CAUSES MAINT-1092
 	bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mWaterDis.isComplete();
 
 	if (use_fbo)
 	{
 		gPipeline.mWaterDis.bindTarget();
 	}
+#endif
 
 	LLGLSLShader::bindNoShader();
 	LLVertexBuffer::unbind();
@@ -256,10 +258,12 @@ BOOL LLViewerDynamicTexture::updateAllInstances()
 		}
 	}
 
+#if 0
 	if (use_fbo)
 	{
 		gPipeline.mWaterDis.flush();
 	}
+#endif
 
 	return ret;
 }
diff --git a/indra/newview/llexternaleditor.cpp b/indra/newview/llexternaleditor.cpp
index ed1d7e860a0cfa672fe78857a6b8861059c5169f..9480e54809a910861bcba2db5a22c103e66d4c25 100644
--- a/indra/newview/llexternaleditor.cpp
+++ b/indra/newview/llexternaleditor.cpp
@@ -29,6 +29,9 @@
 
 #include "lltrans.h"
 #include "llui.h"
+#include "llprocess.h"
+#include "llsdutil.h"
+#include <boost/foreach.hpp>
 
 // static
 const std::string LLExternalEditor::sFilenameMarker = "%s";
@@ -45,19 +48,8 @@ LLExternalEditor::EErrorCode LLExternalEditor::setCommand(const std::string& env
 		return EC_NOT_SPECIFIED;
 	}
 
-	// Add the filename marker if missing.
-	if (cmd.find(sFilenameMarker) == std::string::npos)
-	{
-		cmd += " \"" + sFilenameMarker + "\"";
-		llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl;
-	}
-
 	string_vec_t tokens;
-	if (tokenize(tokens, cmd) < 2) // 2 = bin + at least one arg (%s)
-	{
-		llwarns << "Error parsing editor command" << llendl;
-		return EC_PARSE_ERROR;
-	}
+	tokenize(tokens, cmd);
 
 	// Check executable for existence.
 	std::string bin_path = tokens[0];
@@ -68,51 +60,48 @@ LLExternalEditor::EErrorCode LLExternalEditor::setCommand(const std::string& env
 	}
 
 	// Save command.
-	mProcess.setExecutable(bin_path);
-	mArgs.clear();
+	mProcessParams = LLProcess::Params();
+	mProcessParams.executable = bin_path;
 	for (size_t i = 1; i < tokens.size(); ++i)
 	{
-		if (i > 1) mArgs += " ";
-		mArgs += "\"" + tokens[i] + "\"";
+		mProcessParams.args.add(tokens[i]);
 	}
-	llinfos << "Setting command [" << bin_path << " " << mArgs << "]" << llendl;
+
+	// Add the filename marker if missing.
+	if (cmd.find(sFilenameMarker) == std::string::npos)
+	{
+		mProcessParams.args.add(sFilenameMarker);
+		llinfos << "Adding the filename marker (" << sFilenameMarker << ")" << llendl;
+	}
+
+	llinfos << "Setting command [" << mProcessParams << "]" << llendl;
 
 	return EC_SUCCESS;
 }
 
 LLExternalEditor::EErrorCode LLExternalEditor::run(const std::string& file_path)
 {
-	std::string args = mArgs;
-	if (mProcess.getExecutable().empty() || args.empty())
+	if (std::string(mProcessParams.executable).empty() || mProcessParams.args.empty())
 	{
 		llwarns << "Editor command not set" << llendl;
 		return EC_NOT_SPECIFIED;
 	}
 
-	// Substitute the filename marker in the command with the actual passed file name.
-	LLStringUtil::replaceString(args, sFilenameMarker, file_path);
-
-	// Split command into separate tokens.
-	string_vec_t tokens;
-	tokenize(tokens, args);
+	// Copy params block so we can replace sFilenameMarker
+	LLProcess::Params params;
+	params.executable = mProcessParams.executable;
 
-	// Set process arguments taken from the command.
-	mProcess.clearArguments();
-	for (string_vec_t::const_iterator arg_it = tokens.begin(); arg_it != tokens.end(); ++arg_it)
-	{
-		mProcess.addArgument(*arg_it);
-	}
-
-	// Run the editor.
-	llinfos << "Running editor command [" << mProcess.getExecutable() + " " + args << "]" << llendl;
-	int result = mProcess.launch();
-	if (result == 0)
+	// Substitute the filename marker in the command with the actual passed file name.
+	BOOST_FOREACH(const std::string& arg, mProcessParams.args)
 	{
-		// Prevent killing the process in destructor (will add it to the zombies list).
-		mProcess.orphan();
+		std::string fixed(arg);
+		LLStringUtil::replaceString(fixed, sFilenameMarker, file_path);
+		params.args.add(fixed);
 	}
 
-	return result == 0 ? EC_SUCCESS : EC_FAILED_TO_RUN;
+	// Run the editor. Prevent killing the process in destructor.
+	params.autokill = false;
+	return LLProcess::create(params) ? EC_SUCCESS : EC_FAILED_TO_RUN;
 }
 
 // static
@@ -130,6 +119,12 @@ std::string LLExternalEditor::getErrorMessage(EErrorCode code)
 	return LLTrans::getString("Unknown");
 }
 
+// TODO:
+// - Unit-test this with tests like LLStringUtil::getTokens() (the
+//   command-line overload that supports quoted tokens)
+// - Unless there are significant semantic differences, eliminate this method
+//   and use LLStringUtil::getTokens() instead.
+
 // static
 size_t LLExternalEditor::tokenize(string_vec_t& tokens, const std::string& str)
 {
diff --git a/indra/newview/llexternaleditor.h b/indra/newview/llexternaleditor.h
index ef5db56c6ee056e6ed1ee560c20ae2e1abe51e92..fd2c25020cbabca33d3b20c03b36a4a64c9f596e 100644
--- a/indra/newview/llexternaleditor.h
+++ b/indra/newview/llexternaleditor.h
@@ -27,7 +27,7 @@
 #ifndef LL_LLEXTERNALEDITOR_H
 #define LL_LLEXTERNALEDITOR_H
 
-#include <llprocesslauncher.h>
+#include "llprocess.h"
 
 /**
  * Usage:
@@ -97,9 +97,7 @@ private:
 	 */
 	static const std::string sSetting;
 
-
-	std::string			mArgs;
-	LLProcessLauncher	mProcess;
+	LLProcess::Params		mProcessParams;
 };
 
 #endif // LL_LLEXTERNALEDITOR_H
diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp
index f4b6dc2c816190673274924519e8705ef46afaf0..575b613ccfcce44e41e09484073f2a19f2ffce0f 100644
--- a/indra/newview/llfavoritesbar.cpp
+++ b/indra/newview/llfavoritesbar.cpp
@@ -39,7 +39,7 @@
 
 #include "llagent.h"
 #include "llclipboard.h"
-#include "llinventoryclipboard.h"
+#include "llclipboard.h"
 #include "llinventorybridge.h"
 #include "llinventoryfunctions.h"
 #include "llfloatersidepanelcontainer.h"
@@ -1118,7 +1118,7 @@ BOOL LLFavoritesBarCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
 }
 void copy_slurl_to_clipboard_cb(std::string& slurl)
 {
-	gClipboard.copyFromString(utf8str_to_wstring(slurl));
+	LLClipboard::instance().copyToClipboard(utf8str_to_wstring(slurl),0,slurl.size());
 
 	LLSD args;
 	args["SLURL"] = slurl;
@@ -1187,7 +1187,7 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata)
 	}
 	else if (action == "copy")
 	{
-		LLInventoryClipboard::instance().store(mSelectedItemID);
+		LLClipboard::instance().copyToClipboard(mSelectedItemID, LLAssetType::AT_LANDMARK);
 	}
 	else if (action == "paste")
 	{
@@ -1211,13 +1211,13 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata)
 
 BOOL LLFavoritesBarCtrl::isClipboardPasteable() const
 {
-	if (!LLInventoryClipboard::instance().hasContents())
+	if (!LLClipboard::instance().hasContents())
 	{
 		return FALSE;
 	}
 
 	LLDynamicArray<LLUUID> objects;
-	LLInventoryClipboard::instance().retrieve(objects);
+	LLClipboard::instance().pasteFromClipboard(objects);
 	S32 count = objects.count();
 	for(S32 i = 0; i < count; i++)
 	{
@@ -1246,7 +1246,7 @@ void LLFavoritesBarCtrl::pastFromClipboard() const
 	{
 		LLInventoryItem* item = NULL;
 		LLDynamicArray<LLUUID> objects;
-		LLInventoryClipboard::instance().retrieve(objects);
+		LLClipboard::instance().pasteFromClipboard(objects);
 		S32 count = objects.count();
 		LLUUID parent_id(mFavoriteFolderId);
 		for(S32 i = 0; i < count; i++)
diff --git a/indra/newview/llfloaterbuycontents.cpp b/indra/newview/llfloaterbuycontents.cpp
index a7388d21a37d2277440f25b2f331b76d9984e24f..bca4b5e447188a64526b73266771923ac3e2b194 100644
--- a/indra/newview/llfloaterbuycontents.cpp
+++ b/indra/newview/llfloaterbuycontents.cpp
@@ -210,7 +210,9 @@ void LLFloaterBuyContents::inventoryChanged(LLViewerObject* obj,
 		LLSD row;
 
 		BOOL item_is_multi = FALSE;
-		if ( inv_item->getFlags() & LLInventoryItemFlags::II_FLAGS_LANDMARK_VISITED )
+		if ((inv_item->getFlags() & LLInventoryItemFlags::II_FLAGS_LANDMARK_VISITED
+			|| inv_item->getFlags() & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS)
+			&& !(inv_item->getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK))
 		{
 			item_is_multi = TRUE;
 		}
diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp
index d495f20a9a861a3a843b19b9a6c58e4fdadcd152..56051ff68458f5cbd589ae833028116bbad3be41 100644
--- a/indra/newview/llfloatergesture.cpp
+++ b/indra/newview/llfloatergesture.cpp
@@ -32,7 +32,7 @@
 #include "llinventorybridge.h"
 #include "llinventoryfunctions.h"
 #include "llinventorymodel.h"
-#include "llinventoryclipboard.h"
+#include "llclipboard.h"
 
 #include "llagent.h"
 #include "llappearancemgr.h"
@@ -90,6 +90,12 @@ public:
 		if(mFloater)
 		{
 			mFloater->addGesture(inv_item,NULL,mFloater->getChild<LLScrollListCtrl>("gesture_list"));
+
+			// EXP-1909 (Pasted gesture displayed twice)
+			// The problem is that addGesture is called here for the second time for the same item (which is copied)
+			// First time addGesture is called from LLFloaterGestureObserver::changed(), which is a callback for inventory
+			// change. So we need to refresh the gesture list to avoid duplicates.
+			mFloater->refreshAll();
 		}
 	}
 };
@@ -391,11 +397,11 @@ bool LLFloaterGesture::isActionEnabled(const LLSD& command)
 	std::string command_name = command.asString();
 	if("paste" == command_name)
 	{
-		if(!LLInventoryClipboard::instance().hasContents())
+		if(!LLClipboard::instance().hasContents())
 			return false;
 
 		LLDynamicArray<LLUUID> ids;
-		LLInventoryClipboard::instance().retrieve(ids);
+		LLClipboard::instance().pasteFromClipboard(ids);
 		for(LLDynamicArray<LLUUID>::iterator it = ids.begin(); it != ids.end(); it++)
 		{
 			LLInventoryItem* item = gInventory.getItem(*it);
@@ -490,27 +496,26 @@ void LLFloaterGesture::onActivateBtnClick()
 void LLFloaterGesture::onCopyPasteAction(const LLSD& command)
 {
 	std::string command_name  = command.asString();
-	// since we select this comman inventory item had  already arrived .
+	// Since we select this command, the inventory items must have already arrived
 	if("copy_gesture" == command_name)
 	{
 		uuid_vec_t ids;
 		getSelectedIds(ids);
-		// make sure that clopboard is empty
-		LLInventoryClipboard::instance().reset();
+		// Make sure the clipboard is empty
+		LLClipboard::instance().reset();
 		for(uuid_vec_t::iterator it = ids.begin(); it != ids.end(); it++)
 		{
 			LLInventoryItem* item = gInventory.getItem(*it);
 			if(item  && item->getInventoryType() == LLInventoryType::IT_GESTURE)
 			{
-				LLInventoryClipboard::instance().add(item->getUUID());
+				LLClipboard::instance().addToClipboard(item->getUUID(),LLAssetType::AT_GESTURE);
 			}
 		}
 	}
 	else if ("paste" == command_name)
 	{
-		LLInventoryClipboard& clipbord = LLInventoryClipboard::instance();
 		LLDynamicArray<LLUUID> ids;
-		clipbord.retrieve(ids);
+		LLClipboard::instance().pasteFromClipboard(ids);
 		if(ids.empty() || !gInventory.isCategoryComplete(mGestureFolderID))
 			return;
 		LLInventoryCategory* gesture_dir = gInventory.getCategory(mGestureFolderID);
@@ -530,11 +535,11 @@ void LLFloaterGesture::onCopyPasteAction(const LLSD& command)
 						gesture_dir->getUUID(), getString("copy_name", string_args), cb);
 			}
 		}
-		clipbord.reset();
+		LLClipboard::instance().reset();
 	}
 	else if ("copy_uuid" == command_name)
 	{
-		gClipboard.copyFromString(utf8str_to_wstring(mGestureList->getCurrentID().asString()), mGestureList->getCurrentID());
+		LLClipboard::instance().copyToClipboard(mGestureList->getCurrentID(),LLAssetType::AT_GESTURE);
 	}
 }
 
diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp
index a34e0353ec5d9cd9593a20a858a5c54894b7050c..fb905eae116cc672db3507463f7e2b651c3c5a30 100644
--- a/indra/newview/llfloatergodtools.cpp
+++ b/indra/newview/llfloatergodtools.cpp
@@ -1123,8 +1123,12 @@ bool LLPanelObjectTools::callbackSimWideDeletes( const LLSD& notification, const
 
 void LLPanelObjectTools::onClickSet()
 {
+	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelObjectTools::callbackAvatarID, this, _1,_2));
 	// grandparent is a floater, which can have a dependent
-	gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLPanelObjectTools::callbackAvatarID, this, _1,_2)));
+	if (picker)
+	{
+		gFloaterView->getParentFloater(this)->addDependentFloater(picker);
+	}
 }
 
 void LLPanelObjectTools::onClickSetBySelection(void* data)
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 95da8ff948bd25b016d9499e7772e0b99db617d0..ee18c95b34745f00afbb1bda96268608aa953f58 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -2739,7 +2739,12 @@ void LLPanelLandAccess::onCommitAny(LLUICtrl *ctrl, void *userdata)
 
 void LLPanelLandAccess::onClickAddAccess()
 {
-	gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLPanelLandAccess::callbackAvatarCBAccess, this, _1)) );
+	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
+		boost::bind(&LLPanelLandAccess::callbackAvatarCBAccess, this, _1));
+	if (picker)
+	{
+		gFloaterView->getParentFloater(this)->addDependentFloater(picker);
+	}
 }
 
 void LLPanelLandAccess::callbackAvatarCBAccess(const uuid_vec_t& ids)
@@ -2783,7 +2788,12 @@ void LLPanelLandAccess::onClickRemoveAccess(void* data)
 // static
 void LLPanelLandAccess::onClickAddBanned()
 {
-	gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned, this, _1)));
+	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
+		boost::bind(&LLPanelLandAccess::callbackAvatarCBBanned, this, _1));
+	if (picker)
+	{
+		gFloaterView->getParentFloater(this)->addDependentFloater(picker);
+	}
 }
 
 // static
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 676059779c74df64dedfc690d02b8d4cf5a2fd95..17850ff35d382fc8550fc6dc67792bf1a87c58ee 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -650,7 +650,10 @@ void LLPanelRegionGeneralInfo::onClickKick()
 	// in order to set up floater dependency
 	LLFloater* parent_floater = gFloaterView->getParentFloater(this);
 	LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionGeneralInfo::onKickCommit, this, _1), FALSE, TRUE);
-	parent_floater->addDependentFloater(child_floater);
+	if (child_floater)
+	{
+		parent_floater->addDependentFloater(child_floater);
+	}
 }
 
 void LLPanelRegionGeneralInfo::onKickCommit(const uuid_vec_t& ids)
@@ -1470,7 +1473,10 @@ void LLPanelEstateInfo::onClickKickUser()
 	// in order to set up floater dependency
 	LLFloater* parent_floater = gFloaterView->getParentFloater(this);
 	LLFloater* child_floater = LLFloaterAvatarPicker::show(boost::bind(&LLPanelEstateInfo::onKickUserCommit, this, _1), FALSE, TRUE);
-	parent_floater->addDependentFloater(child_floater);
+	if (child_floater)
+	{
+		parent_floater->addDependentFloater(child_floater);
+	}
 }
 
 void LLPanelEstateInfo::onKickUserCommit(const uuid_vec_t& ids)
@@ -1891,6 +1897,26 @@ void LLPanelEstateInfo::sendEstateAccessDelta(U32 flags, const LLUUID& agent_or_
 	gAgent.sendReliableMessage();
 }
 
+// static
+void LLPanelEstateInfo::updateEstateOwnerName(const std::string& name)
+{
+	LLPanelEstateInfo* panelp = LLFloaterRegionInfo::getPanelEstate();
+	if (panelp)
+	{
+		panelp->setOwnerName(name);
+	}
+}
+
+// static
+void LLPanelEstateInfo::updateEstateName(const std::string& name)
+{
+	LLPanelEstateInfo* panelp = LLFloaterRegionInfo::getPanelEstate();
+	if (panelp)
+	{
+		panelp->getChildRef<LLTextBox>("estate_name").setText(name);
+	}
+}
+
 void LLPanelEstateInfo::updateControls(LLViewerRegion* region)
 {
 	BOOL god = gAgent.isGodlike();
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
index ae45949b4a46018046b2ff2863bc0ceeb96e34a3..e36ef4604b23d950e8b1b303eef68d586a647e5d 100644
--- a/indra/newview/llfloaterregioninfo.h
+++ b/indra/newview/llfloaterregioninfo.h
@@ -294,6 +294,9 @@ public:
 	
 	void updateControls(LLViewerRegion* region);
 	
+	static void updateEstateName(const std::string& name);
+	static void updateEstateOwnerName(const std::string& name);
+
 	virtual bool refreshFromRegion(LLViewerRegion* region);
 	virtual bool estateUpdate(LLMessageSystem* msg);
 	
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
index c08848b1ea4f1fc59c652c5446692c2d4a5e2404..3ec1e372eb1bd2eae68d599bdc52f9a08763f36e 100644
--- a/indra/newview/llfloaterreporter.cpp
+++ b/indra/newview/llfloaterreporter.cpp
@@ -285,7 +285,11 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)
 
 void LLFloaterReporter::onClickSelectAbuser()
 {
-	gFloaterView->getParentFloater(this)->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLFloaterReporter::callbackAvatarID, this, _1, _2), FALSE, TRUE ));
+	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterReporter::callbackAvatarID, this, _1, _2), FALSE, TRUE );
+	if (picker)
+	{
+		gFloaterView->getParentFloater(this)->addDependentFloater(picker);
+	}
 }
 
 void LLFloaterReporter::callbackAvatarID(const uuid_vec_t& ids, const std::vector<LLAvatarName> names)
diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp
index 3434841d09b52d3d3e288079b86b9cc715b94b08..64c0dfa0236b7ed1fe48bc4bc01e173fb1aaa42d 100644
--- a/indra/newview/llfloatersellland.cpp
+++ b/indra/newview/llfloatersellland.cpp
@@ -392,8 +392,12 @@ void LLFloaterSellLandUI::onChangeValue(LLUICtrl *ctrl, void *userdata)
 
 void LLFloaterSellLandUI::doSelectAgent()
 {
+	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterSellLandUI::callbackAvatarPick, this, _1, _2), FALSE, TRUE);
 	// grandparent is a floater, in order to set up dependency
-	addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(&LLFloaterSellLandUI::callbackAvatarPick, this, _1, _2), FALSE, TRUE));
+	if (picker)
+	{
+		addDependentFloater(picker);
+	}
 }
 
 void LLFloaterSellLandUI::callbackAvatarPick(const uuid_vec_t& ids, const std::vector<LLAvatarName> names)
diff --git a/indra/newview/llfloatertexturefetchdebugger.cpp b/indra/newview/llfloatertexturefetchdebugger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b34b720557148beb70c7c214cf96a3f9f62f790
--- /dev/null
+++ b/indra/newview/llfloatertexturefetchdebugger.cpp
@@ -0,0 +1,390 @@
+/** 
+ * @file llfloatertexturefetchdebugger.cpp
+ * @brief LLFloaterTextureFetchDebugger class definition
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertexturefetchdebugger.h"
+
+#include "lluictrlfactory.h"
+#include "llbutton.h"
+#include "llspinctrl.h"
+#include "llresmgr.h"
+
+#include "llmath.h"
+#include "llviewerwindow.h"
+#include "llappviewer.h"
+#include "lltexturefetch.h"
+#include "llviewercontrol.h"
+
+LLFloaterTextureFetchDebugger::LLFloaterTextureFetchDebugger(const LLSD& key)
+	: LLFloater(key),
+	mDebugger(NULL)
+{
+	setTitle("Texture Fetching Debugger Floater");
+	
+	mCommitCallbackRegistrar.add("TexFetchDebugger.ChangeTexelPixelRatio",	boost::bind(&LLFloaterTextureFetchDebugger::onChangeTexelPixelRatio, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Start",	boost::bind(&LLFloaterTextureFetchDebugger::onClickStart, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Clear",	boost::bind(&LLFloaterTextureFetchDebugger::onClickClear, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Close",	boost::bind(&LLFloaterTextureFetchDebugger::onClickClose, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.CacheRead",	boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheRead, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.CacheWrite",	boost::bind(&LLFloaterTextureFetchDebugger::onClickCacheWrite, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.HTTPLoad",	boost::bind(&LLFloaterTextureFetchDebugger::onClickHTTPLoad, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.Decode",	boost::bind(&LLFloaterTextureFetchDebugger::onClickDecode, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.GLTexture",	boost::bind(&LLFloaterTextureFetchDebugger::onClickGLTexture, this));
+
+	mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisCache",	boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisCache, this));
+	mCommitCallbackRegistrar.add("TexFetchDebugger.RefetchVisHTTP",	boost::bind(&LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP, this));
+}
+//----------------------------------------------
+
+BOOL LLFloaterTextureFetchDebugger::postBuild(void) 
+{	
+	mDebugger = LLAppViewer::getTextureFetch()->getFetchDebugger();
+
+	//set states for buttons
+	mButtonStateMap["start_btn"] = true;
+	mButtonStateMap["close_btn"] = true;
+	mButtonStateMap["clear_btn"] = true;
+	mButtonStateMap["cacheread_btn"] = false;
+	mButtonStateMap["cachewrite_btn"] = false;
+	mButtonStateMap["http_btn"] = false;
+	mButtonStateMap["decode_btn"] = false;
+	mButtonStateMap["gl_btn"] = false;
+
+	mButtonStateMap["refetchviscache_btn"] = true;
+	mButtonStateMap["refetchvishttp_btn"] = true;
+
+	updateButtons();
+
+	getChild<LLUICtrl>("texel_pixel_ratio")->setValue(gSavedSettings.getF32("TexelPixelRatio"));
+
+	return TRUE ;
+}
+
+LLFloaterTextureFetchDebugger::~LLFloaterTextureFetchDebugger()
+{
+	//stop everything
+	mDebugger->stopDebug();
+}
+
+void LLFloaterTextureFetchDebugger::updateButtons()
+{
+	for(std::map<std::string, bool>::iterator iter = mButtonStateMap.begin(); iter != mButtonStateMap.end(); ++iter)
+	{
+		if(iter->second)
+		{
+			childEnable(iter->first.c_str());
+		}
+		else
+		{
+			childDisable(iter->first.c_str());
+		}
+	}
+}
+
+void LLFloaterTextureFetchDebugger::disableButtons()
+{
+	childDisable("start_btn");
+	childDisable("clear_btn");
+	childDisable("cacheread_btn");
+	childDisable("cachewrite_btn");
+	childDisable("http_btn");
+	childDisable("decode_btn");
+	childDisable("gl_btn");
+	childDisable("refetchviscache_btn");
+	childDisable("refetchvishttp_btn");
+}
+
+void LLFloaterTextureFetchDebugger::idle()
+{	
+	LLTextureFetchDebugger::e_debug_state state = mDebugger->getState();
+	
+	if(mDebugger->update())
+	{
+		switch(state)
+		{
+		case LLTextureFetchDebugger::IDLE:
+			break;
+		case LLTextureFetchDebugger::READ_CACHE:
+			mButtonStateMap["cachewrite_btn"] = true;
+			mButtonStateMap["decode_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::WRITE_CACHE:
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::DECODING:
+			mButtonStateMap["gl_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::HTTP_FETCHING:
+			mButtonStateMap["cacheread_btn"] = true;
+			mButtonStateMap["cachewrite_btn"] = true;
+			mButtonStateMap["decode_btn"] = true;
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::GL_TEX:
+			updateButtons();
+			break;
+		case LLTextureFetchDebugger::REFETCH_VIS_CACHE:
+			updateButtons();
+		case LLTextureFetchDebugger::REFETCH_VIS_HTTP:
+			updateButtons();
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+//----------------------
+void LLFloaterTextureFetchDebugger::onChangeTexelPixelRatio()
+{
+	gSavedSettings.setF32("TexelPixelRatio", getChild<LLUICtrl>("texel_pixel_ratio")->getValue().asReal());
+}
+
+void LLFloaterTextureFetchDebugger::onClickStart()
+{
+	disableButtons();
+
+	mDebugger->startDebug();
+
+	mButtonStateMap["start_btn"] = false;
+	mButtonStateMap["cacheread_btn"] = true;
+	mButtonStateMap["http_btn"] = true;
+	updateButtons();
+}
+
+void LLFloaterTextureFetchDebugger::onClickClose()
+{
+	setVisible(FALSE);
+	
+	//stop everything
+	mDebugger->stopDebug();
+}
+
+void LLFloaterTextureFetchDebugger::onClickClear()
+{
+	mButtonStateMap["start_btn"] = true;
+	mButtonStateMap["close_btn"] = true;
+	mButtonStateMap["clear_btn"] = true;
+	mButtonStateMap["cacheread_btn"] = false;
+	mButtonStateMap["cachewrite_btn"] = false;
+	mButtonStateMap["http_btn"] = false;
+	mButtonStateMap["decode_btn"] = false;
+	mButtonStateMap["gl_btn"] = false;
+	mButtonStateMap["refetchviscache_btn"] = true;
+	mButtonStateMap["refetchvishttp_btn"] = true;
+	updateButtons();
+
+	//stop everything
+	mDebugger->stopDebug();
+	mDebugger->clearHistory();
+}
+
+void LLFloaterTextureFetchDebugger::onClickCacheRead()
+{
+	disableButtons();
+
+	mDebugger->debugCacheRead();
+}
+
+void LLFloaterTextureFetchDebugger::onClickCacheWrite()
+{
+	disableButtons();
+
+	mDebugger->debugCacheWrite();
+}
+
+void LLFloaterTextureFetchDebugger::onClickHTTPLoad()
+{
+	disableButtons();
+
+	mDebugger->debugHTTP();
+}
+
+void LLFloaterTextureFetchDebugger::onClickDecode()
+{
+	disableButtons();
+
+	mDebugger->debugDecoder();
+}
+
+void LLFloaterTextureFetchDebugger::onClickGLTexture()
+{
+	disableButtons();
+
+	mDebugger->debugGLTextureCreation();
+}
+
+void LLFloaterTextureFetchDebugger::onClickRefetchVisCache()
+{
+	disableButtons();
+
+	mDebugger->debugRefetchVisibleFromCache();
+}
+
+void LLFloaterTextureFetchDebugger::onClickRefetchVisHTTP()
+{
+	disableButtons();
+
+	mDebugger->debugRefetchVisibleFromHTTP();
+}
+
+void LLFloaterTextureFetchDebugger::draw()
+{
+	//total number of fetched textures
+	{
+		getChild<LLUICtrl>("total_num_fetched_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumFetchedTextures()));
+	}
+
+	//total number of fetching requests
+	{
+		getChild<LLUICtrl>("total_num_fetching_requests_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumFetchingRequests()));
+	}
+
+	//total number of cache hits
+	{
+		getChild<LLUICtrl>("total_num_cache_hits_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumCacheHits()));
+	}
+
+	//total number of visible textures
+	{
+		getChild<LLUICtrl>("total_num_visible_tex_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumVisibleFetchedTextures()));
+	}
+
+	//total number of visible texture fetching requests
+	{
+		getChild<LLUICtrl>("total_num_visible_tex_fetch_req_label")->setTextArg("[NUM]", llformat("%d", mDebugger->getNumVisibleFetchingRequests()));
+	}
+
+	//total number of fetched data
+	{
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getFetchedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getDecodedData() >> 10));
+		getChild<LLUICtrl>("total_fetched_data_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getFetchedPixels() / 1000000.f));
+	}
+
+	//total number of visible fetched data
+	{		
+		getChild<LLUICtrl>("total_fetched_vis_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getVisibleFetchedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_vis_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getVisibleDecodedData() >> 10));
+	}
+
+	//total number of rendered fetched data
+	{
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[SIZE1]", llformat("%d", mDebugger->getRenderedData() >> 10));		
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[SIZE2]", llformat("%d", mDebugger->getRenderedDecodedData() >> 10));
+		getChild<LLUICtrl>("total_fetched_rendered_data_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRenderedPixels() / 1000000.f));
+	}
+
+	//total time on cache readings
+	if(mDebugger->getCacheReadTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_cache_read_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_cache_read_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getCacheReadTime()));
+	}
+
+	//total time on cache writings
+	if(mDebugger->getCacheWriteTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_cache_write_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_cache_write_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getCacheWriteTime()));
+	}
+
+	//total time on decoding
+	if(mDebugger->getDecodeTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_decode_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_decode_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getDecodeTime()));
+	}
+
+	//total time on gl texture creation
+	if(mDebugger->getGLCreationTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_gl_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_gl_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getGLCreationTime()));
+	}
+
+	//total time on HTTP fetching
+	if(mDebugger->getHTTPTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_http_label")->setTextArg("[TIME]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getHTTPTime()));
+	}
+
+	//total time on entire fetching
+	{
+		getChild<LLUICtrl>("total_time_fetch_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getTotalFetchingTime()));
+	}
+
+	//total time on refetching visible textures from cache
+	if(mDebugger->getRefetchVisCacheTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[TIME]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisCacheTime()));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10));
+		getChild<LLUICtrl>("total_time_refetch_vis_cache_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f));
+	}
+
+	//total time on refetching visible textures from http
+	if(mDebugger->getRefetchVisHTTPTime() < 0.f)
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[TIME]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", std::string("----"));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", std::string("----"));
+	}
+	else
+	{
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[TIME]", llformat("%.3f", mDebugger->getRefetchVisHTTPTime()));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[SIZE]", llformat("%d", mDebugger->getRefetchedData() >> 10));
+		getChild<LLUICtrl>("total_time_refetch_vis_http_label")->setTextArg("[PIXEL]", llformat("%.3f", mDebugger->getRefetchedPixels() / 1000000.f));
+	}
+
+	LLFloater::draw();
+}
diff --git a/indra/newview/llfloatertexturefetchdebugger.h b/indra/newview/llfloatertexturefetchdebugger.h
new file mode 100644
index 0000000000000000000000000000000000000000..33012c6a3dcf67978bd3e49df7fc6173df2ae04c
--- /dev/null
+++ b/indra/newview/llfloatertexturefetchdebugger.h
@@ -0,0 +1,71 @@
+/** 
+ * @file llfloatertexturefetchdebugger.h
+ * @brief texture fetching debugger window, debug use only
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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$
+ */
+
+#ifndef LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
+#define LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
+
+#include "llfloater.h"
+class LLTextureFetchDebugger;
+
+class LLFloaterTextureFetchDebugger : public LLFloater
+{
+	friend class LLFloaterReg;
+public:
+	/// initialize all the callbacks for the menu
+
+	virtual BOOL postBuild() ;
+	virtual void draw() ;
+	
+	void onChangeTexelPixelRatio();
+	
+	void onClickStart();
+	void onClickClear();
+	void onClickClose();
+
+	void onClickCacheRead();
+	void onClickCacheWrite();
+	void onClickHTTPLoad();
+	void onClickDecode();
+	void onClickGLTexture();
+
+	void onClickRefetchVisCache();
+	void onClickRefetchVisHTTP();
+public:
+	void idle() ;
+
+private:	
+	LLFloaterTextureFetchDebugger(const LLSD& key);
+	virtual ~LLFloaterTextureFetchDebugger();
+
+	void updateButtons();
+	void disableButtons();
+
+private:	
+	LLTextureFetchDebugger* mDebugger;
+	std::map<std::string, bool> mButtonStateMap;
+};
+
+#endif // LL_FLOATER_TEXTURE_FETCH_DEBUGGER__H
diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp
index 030fed0575deaa9508652140c417745934463da1..227720bee34975370dd10a45d2b79c79e7885327 100644
--- a/indra/newview/llfloatervoiceeffect.cpp
+++ b/indra/newview/llfloatervoiceeffect.cpp
@@ -145,7 +145,9 @@ void LLFloaterVoiceEffect::refreshEffectList()
 		for (voice_effect_list_t::const_iterator it = template_list.begin(); it != template_list.end(); ++it)
 		{
 			const LLUUID& effect_id = it->second;
-			std::string effect_name = getString("effect_" + it->first); // will throw an error if the effect is not listed in the XML
+
+			std::string localized_effect = "effect_" + it->first;
+			std::string effect_name = hasString(localized_effect) ? getString(localized_effect) : it->first;  // XML contains localized effects names
 
 			LLSD effect_properties = effect_interface->getVoiceEffectProperties(effect_id);
 
diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp
index 3b5c3663fbc772bca84ba27a47d1ad1a70ac2a31..3fe2518de67230cb185fda3cc036366796cfd5aa 100644
--- a/indra/newview/llfloaterwebcontent.cpp
+++ b/indra/newview/llfloaterwebcontent.cpp
@@ -169,7 +169,7 @@ void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y,
 void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height)
 {
 	// Make sure the layout of the browser control is updated, so this calculation is correct.
-	LLLayoutStack::updateClass();
+	getChild<LLLayoutStack>("stack1")->updateLayout();
 
 	// TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc.
 	LLCoordWindow window_size;
@@ -258,7 +258,7 @@ void LLFloaterWebContent::open_media(const Params& p)
 
 	if (!p.preferred_media_size().isEmpty())
 	{
-		LLLayoutStack::updateClass();
+		getChild<LLLayoutStack>("stack1")->updateLayout();
 		LLRect browser_rect = mWebBrowser->calcScreenRect();
 		LLCoordWindow window_size;
 		getWindow()->getSize(&window_size);
diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp
index 618fd7fc31fc517353ef31215317a2be373a4797..1fa194ab1918b41bb8d9a1c01ff1c1113ef2bf8c 100644
--- a/indra/newview/llfolderview.cpp
+++ b/indra/newview/llfolderview.cpp
@@ -30,7 +30,7 @@
 
 #include "llcallbacklist.h"
 #include "llinventorybridge.h"
-#include "llinventoryclipboard.h" // *TODO: remove this once hack below gone.
+#include "llclipboard.h" // *TODO: remove this once hack below gone.
 #include "llinventoryfilter.h"
 #include "llinventoryfunctions.h"
 #include "llinventorymodelbackgroundfetch.h"
@@ -164,6 +164,33 @@ void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
 void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
 { }
 
+///----------------------------------------------------------------------------
+/// Class LLFolderViewScrollContainer
+///----------------------------------------------------------------------------
+
+// virtual
+const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const
+{
+	LLRect rect = LLRect::null;
+	if (mScrolledView)
+	{
+		LLFolderView* folder_view = dynamic_cast<LLFolderView*>(mScrolledView);
+		if (folder_view)
+		{
+			S32 height = folder_view->mRunningHeight;
+
+			rect = mScrolledView->getRect();
+			rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height);
+		}
+	}
+
+	return rect;
+}
+
+LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer::Params& p)
+:	LLScrollContainer(p)
+{}
+
 ///----------------------------------------------------------------------------
 /// Class LLFolderView
 ///----------------------------------------------------------------------------
@@ -429,8 +456,8 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen
 		}
 		else
 		{
-			folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
-									(folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
+			folderp->setVisible((show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
+								 (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))));
 		}
 
 		if (folderp->getVisible())
@@ -535,6 +562,7 @@ void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
 	{
 		width = scroll_rect.getWidth();
 	}
+
 	LLView::reshape(width, height, called_from_parent);
 	mReshapeSignal(mSelectedItems, FALSE);
 }
@@ -769,7 +797,7 @@ void LLFolderView::sanitizeSelection()
 	// if nothing selected after prior constraints...
 	if (mSelectedItems.empty())
 	{
-		// ...select first available parent of original selection, or "My Inventory" otherwise
+		// ...select first available parent of original selection
 		LLFolderViewItem* new_selection = NULL;
 		if (original_selected_item)
 		{
@@ -943,6 +971,9 @@ void LLFolderView::draw()
 			// We should call this method to also notify parent about required rect.
 			// See EXT-7564, EXT-7047.
 			arrangeFromRoot();
+			LLUI::popMatrix();
+			LLUI::pushMatrix();
+			LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
 		}
 	}
 
@@ -1014,6 +1045,24 @@ bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector<LLFol
 	return false;
 }
 
+// static
+void LLFolderView::removeCutItems()
+{
+	// There's no item in "cut" mode on the clipboard -> exit
+	if (!LLClipboard::instance().isCutMode())
+		return;
+
+	// Get the list of clipboard item uuids and iterate through them
+	LLDynamicArray<LLUUID> objects;
+	LLClipboard::instance().pasteFromClipboard(objects);
+	for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin();
+		 iter != objects.end();
+		 ++iter)
+	{
+		gInventory.removeObject(*iter);
+	}
+}
+
 void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response)
 {
 	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
@@ -1293,7 +1342,7 @@ BOOL LLFolderView::canCopy() const
 void LLFolderView::copy()
 {
 	// *NOTE: total hack to clear the inventory clipboard
-	LLInventoryClipboard::instance().reset();
+	LLClipboard::instance().reset();
 	S32 count = mSelectedItems.size();
 	if(getVisible() && getEnabled() && (count > 0))
 	{
@@ -1334,7 +1383,7 @@ BOOL LLFolderView::canCut() const
 void LLFolderView::cut()
 {
 	// clear the inventory clipboard
-	LLInventoryClipboard::instance().reset();
+	LLClipboard::instance().reset();
 	S32 count = mSelectedItems.size();
 	if(getVisible() && getEnabled() && (count > 0))
 	{
@@ -1348,6 +1397,7 @@ void LLFolderView::cut()
 				listener->cutToClipboard();
 			}
 		}
+		LLFolderView::removeCutItems();
 	}
 	mSearchString.clear();
 }
@@ -1961,19 +2011,13 @@ void LLFolderView::deleteAllChildren()
 
 void LLFolderView::scrollToShowSelection()
 {
-	// If items are filtered while background fetch is in progress
-	// scrollbar resets to the first filtered item. See EXT-3981.
-	// However we allow scrolling for folder views with mAutoSelectOverride
-	// (used in Places SP) as an exception because the selection in them
-	// is not reset during items filtering. See STORM-133.
-	if ( (!LLInventoryModelBackgroundFetch::instance().folderFetchActive() || mAutoSelectOverride)
-			&& mSelectedItems.size() )
+	if ( mSelectedItems.size() )
 	{
 		mNeedsScroll = TRUE;
 	}
 }
 
-// If the parent is scroll containter, scroll it to make the selection
+// If the parent is scroll container, scroll it to make the selection
 // is maximally visible.
 void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect)
 {
@@ -2108,10 +2152,10 @@ bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata)
 		removeSelectedItems();
 		return true;
 	}
-
-	if ("copy" == action)
-	{	
-		LLInventoryClipboard::instance().reset();
+	if (("copy" == action) || ("cut" == action))
+	{
+		// Clear the clipboard before we start adding things on it
+		LLClipboard::instance().reset();
 	}
 
 	static const std::string change_folder_string = "change_folder_type_";
@@ -2192,46 +2236,56 @@ void LLFolderView::doIdle()
 		arrangeAll();
 	}
 
+	if (mFilter->isModified() && mFilter->isNotDefault())
+	{
+		mNeedsAutoSelect = TRUE;
+	}
 	mFilter->clearModified();
-	BOOL filter_modified_and_active = mCompletedFilterGeneration < mFilter->getCurrentGeneration() && 
-										mFilter->isNotDefault();
-	mNeedsAutoSelect = mFilter->hasFilterString() &&
-							!(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture());
 
-	// filter to determine visiblity before arranging
+	// filter to determine visibility before arranging
 	filterFromRoot();
 
 	// automatically show matching items, and select first one if we had a selection
-	// do this every frame until user puts keyboard focus into the inventory window
-	// signaling the end of the automatic update
-	// only do this when mNeedsFilter is set, meaning filtered items have
-	// potentially changed
 	if (mNeedsAutoSelect)
 	{
 		LLFastTimer t3(FTM_AUTO_SELECT);
 		// select new item only if a filtered item not currently selected
 		LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back();
-		if ((selected_itemp && !selected_itemp->getFiltered()) && !mAutoSelectOverride)
+		if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->potentiallyFiltered()))
 		{
-			// select first filtered item
-			LLSelectFirstFilteredItem filter;
-			applyFunctorRecursively(filter);
+			// these are named variables to get around gcc not binding non-const references to rvalues
+			// and functor application is inherently non-const to allow for stateful functors
+			LLSelectFirstFilteredItem functor;
+			applyFunctorRecursively(functor);
 		}
 
 		// Open filtered folders for folder views with mAutoSelectOverride=TRUE.
 		// Used by LLPlacesFolderView.
 		if (mAutoSelectOverride && !mFilter->getFilterSubString().empty())
 		{
-			LLOpenFilteredFolders filter;
-			applyFunctorRecursively(filter);
+			// these are named variables to get around gcc not binding non-const references to rvalues
+			// and functor application is inherently non-const to allow for stateful functors
+			LLOpenFilteredFolders functor;
+			applyFunctorRecursively(functor);
 		}
 
 		scrollToShowSelection();
 	}
 
+	BOOL filter_finished = mCompletedFilterGeneration >= mFilter->getCurrentGeneration() 
+						&& !LLInventoryModelBackgroundFetch::instance().folderFetchActive();
+	if (filter_finished 
+		|| gFocusMgr.childHasKeyboardFocus(inventory_panel) 
+		|| gFocusMgr.childHasMouseCapture(inventory_panel))
+	{
+		// finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process
+		mNeedsAutoSelect = FALSE;
+	}
+
+
 	// during filtering process, try to pin selected item's location on screen
 	// this will happen when searching your inventory and when new items arrive
-	if (filter_modified_and_active)
+	if (!filter_finished)
 	{
 		// calculate rectangle to pin item to at start of animated rearrange
 		if (!mPinningSelectedItem && !mSelectedItems.empty())
@@ -2297,7 +2351,7 @@ void LLFolderView::doIdle()
 	{
 		scrollToShowItem(mSelectedItems.back(), constraint_rect);
 		// continue scrolling until animated layout change is done
-		if (!filter_modified_and_active 
+		if (filter_finished
 			&& (!needsArrange() || !is_visible))
 		{
 			mNeedsScroll = FALSE;
diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h
index 1d018b5e6a75f4d0516a61c56625d89cbc483702..da8bb15f8ef9397c45efad6f36d346caf8491f4e 100644
--- a/indra/newview/llfolderview.h
+++ b/indra/newview/llfolderview.h
@@ -44,6 +44,7 @@
 #include "lldepthstack.h"
 #include "lleditmenuhandler.h"
 #include "llfontgl.h"
+#include "llscrollcontainer.h"
 #include "lltooldraganddrop.h"
 #include "llviewertexture.h"
 
@@ -54,15 +55,33 @@ class LLInventoryModel;
 class LLPanel;
 class LLLineEditor;
 class LLMenuGL;
-class LLScrollContainer;
 class LLUICtrl;
 class LLTextBox;
 
+/**
+ * Class LLFolderViewScrollContainer
+ *
+ * A scroll container which provides the information about the height
+ * of currently displayed folder view contents.
+ * Used for updating vertical scroll bar visibility in inventory panel.
+ * See LLScrollContainer::calcVisibleSize().
+ */
+class LLFolderViewScrollContainer : public LLScrollContainer
+{
+public:
+	/*virtual*/ ~LLFolderViewScrollContainer() {};
+	/*virtual*/ const LLRect getScrolledViewRect() const;
+
+protected:
+	LLFolderViewScrollContainer(const LLScrollContainer::Params& p);
+	friend class LLUICtrlFactory;
+};
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Class LLFolderView
 //
-// Th LLFolderView represents the root level folder view object. It
-// manages the screen region of the folder view.
+// The LLFolderView represents the root level folder view object. 
+// It manages the screen region of the folder view.
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 class LLFolderView : public LLFolderViewFolder, public LLEditMenuHandler
@@ -81,6 +100,9 @@ public:
 
 		Params();
 	};
+
+	friend class LLFolderViewScrollContainer;
+
 	LLFolderView(const Params&);
 	virtual ~LLFolderView( void );
 
@@ -88,7 +110,7 @@ public:
 
 	virtual LLFolderView*	getRoot() { return this; }
 
-	// FolderViews default to sort by name.  This will change that,
+	// FolderViews default to sort by name. This will change that,
 	// and resort the items if necessary.
 	void setSortOrder(U32 order);
 	void setFilterPermMask(PermissionMask filter_perm_mask);
@@ -117,20 +139,20 @@ public:
 	virtual void setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse);
 	virtual BOOL addFolder( LLFolderViewFolder* folder);
 
-	// Finds width and height of this object and it's children.  Also
-	// makes sure that this view and it's children are the right size.
+	// Find width and height of this object and its children. Also
+	// makes sure that this view and its children are the right size.
 	virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
 
 	void arrangeAll() { mArrangeGeneration++; }
 	S32 getArrangeGeneration() { return mArrangeGeneration; }
 
-	// applies filters to control visibility of inventory items
+	// Apply filters to control visibility of inventory items
 	virtual void filter( LLInventoryFilter& filter);
 
-	// get the last selected item
+	// Get the last selected item
 	virtual LLFolderViewItem* getCurSelectedItem( void );
 
-	// Record the selected item and pass it down the hierachy.
+	// Record the selected item and pass it down the hierarchy.
 	virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem,
 		BOOL take_keyboard_focus);
 
@@ -140,13 +162,13 @@ public:
 	// Called once a frame to update the selection if mSelectThisID has been set
 	void updateSelection();
 
-	// This method is used to toggle the selection of an item. Walks
-	// children, and keeps track of selected objects.
+	// This method is used to toggle the selection of an item. 
+	// Walks children and keeps track of selected objects.
 	virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
 
 	virtual std::set<LLUUID> getSelectionList() const;
 
-	// make sure if ancestor is selected, descendents are not
+	// Make sure if ancestor is selected, descendents are not
 	void sanitizeSelection();
 	void clearSelection();
 	void addToSelectionList(LLFolderViewItem* item);
@@ -157,21 +179,22 @@ public:
 	void setDraggingOverItem(LLFolderViewItem* item) { mDraggingOverItem = item; }
 	LLFolderViewItem* getDraggingOverItem() { return mDraggingOverItem; }
 
-	// deletion functionality
+	// Deletion functionality
  	void removeSelectedItems();
+ 	static void removeCutItems();
 
-	// open the selected item.
+	// Open the selected item
 	void openSelectedItems( void );
 	void propertiesSelectedItems( void );
 
-	// change the folder type
+	// Change the folder type
 	void changeType(LLInventoryModel *model, LLFolderType::EType new_folder_type);
 
 	void autoOpenItem(LLFolderViewFolder* item);
 	void closeAutoOpenedFolders();
 	BOOL autoOpenTest(LLFolderViewFolder* item);
 
-	// copy & paste
+	// Copy & paste
 	virtual void	copy();
 	virtual BOOL	canCopy() const;
 
@@ -184,7 +207,7 @@ public:
 	virtual void	doDelete();
 	virtual BOOL	canDoDelete() const;
 
-	// public rename functionality - can only start the process
+	// Public rename functionality - can only start the process
 	void startRenamingSelectedItem( void );
 
 	// These functions were used when there was only one folderview,
@@ -325,7 +348,7 @@ protected:
 
 	/**
 	 * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll.
-	 * NOTE: For now it uses only to cut LLFolderViewItem::mLabel text to be used for Landmarks in Places Panel.
+	 * NOTE: For now it's used only to cut LLFolderViewItem::mLabel text for Landmarks in Places Panel.
 	 */
 	bool							mUseEllipses; // See EXT-719
 
diff --git a/indra/newview/llfoldervieweventlistener.h b/indra/newview/llfoldervieweventlistener.h
index aee31ca0339e2caa1f39ed1b4dc57bf10e44da23..06682dcbf1f9d8058e9449e146027f4ae80f38a0 100644
--- a/indra/newview/llfoldervieweventlistener.h
+++ b/indra/newview/llfoldervieweventlistener.h
@@ -75,7 +75,7 @@ public:
 	virtual void move( LLFolderViewEventListener* parent_listener ) = 0;
 	virtual BOOL isItemCopyable() const = 0;
 	virtual BOOL copyToClipboard() const = 0;
-	virtual void cutToClipboard() = 0;
+	virtual BOOL cutToClipboard() const = 0;
 	virtual BOOL isClipboardPasteable() const = 0;
 	virtual void pasteFromClipboard() = 0;
 	virtual void pasteLinkFromClipboard() = 0;
diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp
index c8ce5f74322a1836642b7b1dce425cb2f333a5eb..515e544452b0bd57df69fd5c1ba40acc33acb287 100644
--- a/indra/newview/llfolderviewitem.cpp
+++ b/indra/newview/llfolderviewitem.cpp
@@ -40,6 +40,7 @@
 #include "llviewerwindow.h"		// Argh, only for setCursor()
 
 // linden library includes
+#include "llclipboard.h"
 #include "llfocusmgr.h"		// gFocusMgr
 #include "lltrans.h"
 
@@ -220,6 +221,11 @@ BOOL LLFolderViewItem::potentiallyVisible()
 {
 	// we haven't been checked against min required filter
 	// or we have and we passed
+	return potentiallyFiltered();
+}
+
+BOOL LLFolderViewItem::potentiallyFiltered()
+{
 	return getLastFilterGeneration() < getRoot()->getFilter()->getMinRequiredGeneration() || getFiltered();
 }
 
@@ -1002,7 +1008,7 @@ void LLFolderViewItem::draw()
 	LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor;
 	if (highlight_link) color = sLinkColor;
 	if (in_library) color = sLibraryColor;
-
+	
 	F32 right_x  = 0;
 	F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD;
 	F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
@@ -1158,7 +1164,37 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation)
 		mNeedsSort = false;
 	}
 
-	mHasVisibleChildren = hasFilteredDescendants(filter_generation);
+	// evaluate mHasVisibleChildren
+	mHasVisibleChildren = false;
+	if (hasFilteredDescendants(filter_generation))
+	{
+		// We have to verify that there's at least one child that's not filtered out
+		bool found = false;
+		// Try the items first
+		for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit)
+		{
+			LLFolderViewItem* itemp = (*iit);
+			found = (itemp->getFiltered(filter_generation));
+			if (found)
+				break;
+		}
+		if (!found)
+		{
+			// If no item found, try the folders
+			for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit)
+			{
+				LLFolderViewFolder* folderp = (*fit);
+				found = ( folderp->getListener()
+								&&	(folderp->getFiltered(filter_generation)
+									 ||	(folderp->getFilteredFolder(filter_generation) 
+										 && folderp->hasFilteredDescendants(filter_generation))));
+				if (found)
+					break;
+			}
+		}
+
+		mHasVisibleChildren = found;
+	}
 
 	// calculate height as a single item (without any children), and reshapes rectangle to match
 	LLFolderViewItem::arrange( width, height, filter_generation );
@@ -1311,7 +1347,7 @@ void LLFolderViewFolder::requestSort()
 
 void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up)
 {
-	mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation);
+	//mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation);
 	mCompletedFilterGeneration = generation;
 	// only aggregate up if we are a lower (older) value
 	if (recurse_up
@@ -1345,7 +1381,8 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter)
 			&& !mPassedFilter)									// and did not pass the filter
 		{
 			// go ahead and flag this folder as done
-			mLastFilterGeneration = filter_generation;			
+			mLastFilterGeneration = filter_generation;
+			mStringMatchOffset = std::string::npos;
 		}
 		else // filter self only on first pass through
 		{
diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h
index 4e8dc2da167caa822e21c6f14129f85b12043f3d..3c7592046a15dc0d589f586af2e573363503e38f 100644
--- a/indra/newview/llfolderviewitem.h
+++ b/indra/newview/llfolderviewitem.h
@@ -304,7 +304,8 @@ public:
 	BOOL			isDescendantOf( const LLFolderViewFolder* potential_ancestor );
 	S32				getIndentation() { return mIndentation; }
 
-	virtual BOOL	potentiallyVisible(); // do we know for a fact that this item has been filtered out?
+	virtual BOOL	potentiallyVisible(); // do we know for a fact that this item won't be displayed?
+	virtual BOOL	potentiallyFiltered(); // do we know for a fact that this item has been filtered out?
 
 	virtual BOOL	getFiltered();
 	virtual BOOL	getFiltered(S32 filter_generation);
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 2de2b17373af9596b0dd37ce9e09d22d11a1586b..b86c453d617693bced1035bd28af8d9a3c0654ce 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -47,7 +47,7 @@
 #include "llgiveinventory.h" 
 #include "llimfloater.h"
 #include "llimview.h"
-#include "llinventoryclipboard.h"
+#include "llclipboard.h"
 #include "llinventorydefines.h"
 #include "llinventoryfunctions.h"
 #include "llinventorymodel.h"
@@ -143,6 +143,42 @@ bool isMarketplaceSendAction(const std::string& action)
 	return ("send_to_marketplace" == action);
 }
 
+// Used by LLFolderBridge as callback for directory fetching recursion
+class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+	LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids) : LLInventoryFetchDescendentsObserver(ids) {}
+	~LLRightClickInventoryFetchDescendentsObserver() {}
+	virtual void execute(bool clear_observer = false);
+	virtual void done()
+	{
+		execute(true);
+	}
+};
+
+// Used by LLFolderBridge as callback for directory content items fetching
+class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver
+{
+public:
+	LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) : LLInventoryFetchItemsObserver(ids) { };
+	~LLRightClickInventoryFetchObserver() {}
+	void execute(bool clear_observer = false)
+	{
+		if (clear_observer)
+		{
+			dec_busy_count();
+			gInventory.removeObserver(this);
+			delete this;
+		}
+		// we've downloaded all the items, so repaint the dialog
+		LLFolderBridge::staticFolderOptionsMenu();
+	}
+	virtual void done()
+	{
+		execute(true);
+	}
+};
+
 // +=================================================+
 // |        LLInvFVBridge                            |
 // +=================================================+
@@ -215,13 +251,27 @@ BOOL LLInvFVBridge::isLink() const
 /**
  * @brief Adds this item into clipboard storage
  */
-void LLInvFVBridge::cutToClipboard()
+BOOL LLInvFVBridge::cutToClipboard() const
+{
+	const LLInventoryObject* obj = gInventory.getObject(mUUID);
+	if (obj && isItemMovable() && isItemRemovable())
+	{
+		LLClipboard::instance().setCutMode(true);
+		return LLClipboard::instance().addToClipboard(mUUID);
+	}
+	return FALSE;
+}
+
+BOOL LLInvFVBridge::copyToClipboard() const
 {
-	if(isItemMovable())
+	const LLInventoryObject* obj = gInventory.getObject(mUUID);
+	if (obj && isItemCopyable())
 	{
-		LLInventoryClipboard::instance().cut(mUUID);
+		return LLClipboard::instance().addToClipboard(mUUID);
 	}
+	return FALSE;
 }
+
 // *TODO: make sure this does the right thing
 void LLInvFVBridge::showProperties()
 {
@@ -396,6 +446,11 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*
 	for(; it != end; ++it)
 	{
 		gInventory.moveObject((*it), trash_id);
+		LLViewerInventoryItem* item = gInventory.getItem(*it);
+		if (item)
+		{
+			model->updateItem(item);
+		}
 	}
 
 	// notify inventory observers.
@@ -404,7 +459,8 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*
 
 BOOL LLInvFVBridge::isClipboardPasteable() const
 {
-	if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+	// Return FALSE on degenerated cases: empty clipboard, no inventory, no agent
+	if (!LLClipboard::instance().hasContents() || !isAgentInventory())
 	{
 		return FALSE;
 	}
@@ -414,37 +470,42 @@ BOOL LLInvFVBridge::isClipboardPasteable() const
 		return FALSE;
 	}
 
-	const LLUUID &agent_id = gAgent.getID();
+	// In cut mode, whatever is on the clipboard is always pastable
+	if (LLClipboard::instance().isCutMode())
+	{
+		return TRUE;
+	}
 
+	// In normal mode, we need to check each element of the clipboard to know if we can paste or not
 	LLDynamicArray<LLUUID> objects;
-	LLInventoryClipboard::instance().retrieve(objects);
+	LLClipboard::instance().pasteFromClipboard(objects);
 	S32 count = objects.count();
 	for(S32 i = 0; i < count; i++)
 	{
 		const LLUUID &item_id = objects.get(i);
 
-		// Can't paste folders
+		// Folders are pastable if all items in there are copyable
 		const LLInventoryCategory *cat = model->getCategory(item_id);
 		if (cat)
 		{
+			LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, item_id);
+			if (!cat_br.isItemCopyable())
 			return FALSE;
+			// Skip to the next item in the clipboard
+			continue;
 		}
 
-		const LLInventoryItem *item = model->getItem(item_id);
-		if (item)
-		{
-			if (!item->getPermissions().allowCopyBy(agent_id))
-			{
+		// Each item must be copyable to be pastable
+		LLItemBridge item_br(mInventoryPanel.get(), mRoot, item_id);
+		if (!item_br.isItemCopyable())
 				return FALSE;
 			}
-		}
-	}
 	return TRUE;
 }
 
 BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
 {
-	if (!LLInventoryClipboard::instance().hasContents() || !isAgentInventory())
+	if (!LLClipboard::instance().hasContents() || !isAgentInventory())
 	{
 		return FALSE;
 	}
@@ -455,7 +516,7 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
 	}
 
 	LLDynamicArray<LLUUID> objects;
-	LLInventoryClipboard::instance().retrieve(objects);
+	LLClipboard::instance().pasteFromClipboard(objects);
 	S32 count = objects.count();
 	for(S32 i = 0; i < count; i++)
 	{
@@ -606,6 +667,12 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id,
 				disabled_items.push_back(std::string("Copy"));
 			}
 
+			items.push_back(std::string("Cut"));
+			if (!isItemMovable() || !isItemRemovable())
+			{
+				disabled_items.push_back(std::string("Cut"));
+			}
+
 			if (canListOnMarketplace())
 			{
 				items.push_back(std::string("Marketplace Separator"));
@@ -917,7 +984,7 @@ void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
 									 const LLUUID& new_parent_id,
 									 BOOL restamp)
 {
-	change_item_parent(model, item, new_parent_id, restamp);
+	model->changeItemParent(item, new_parent_id, restamp);
 }
 
 // static
@@ -926,7 +993,7 @@ void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,
 										 const LLUUID& new_parent_id,
 										 BOOL restamp)
 {
-	change_category_parent(model, cat, new_parent_id, restamp);
+	model->changeCategoryParent(cat, new_parent_id, restamp);
 }
 
 LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
@@ -1285,6 +1352,12 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
 		gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer));
 		return;
 	}
+	else if ("cut" == action)
+	{
+		cutToClipboard();
+		LLFolderView::removeCutItems();
+		return;
+	}
 	else if ("copy" == action)
 	{
 		copyToClipboard();
@@ -1292,7 +1365,6 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)
 	}
 	else if ("paste" == action)
 	{
-		// Single item only
 		LLInventoryItem* itemp = model->getItem(mUUID);
 		if (!itemp) return;
 
@@ -1667,16 +1739,6 @@ BOOL LLItemBridge::isItemCopyable() const
 	return FALSE;
 }
 
-BOOL LLItemBridge::copyToClipboard() const
-{
-	if(isItemCopyable())
-	{
-		LLInventoryClipboard::instance().add(mUUID);
-		return TRUE;
-	}
-	return FALSE;
-}
-
 LLViewerInventoryItem* LLItemBridge::getItem() const
 {
 	LLViewerInventoryItem* item = NULL;
@@ -1710,16 +1772,20 @@ BOOL LLFolderBridge::isItemMovable() const
 	LLInventoryObject* obj = getInventoryObject();
 	if(obj)
 	{
-		return (!LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType()));
+		// If it's a protected type folder, we can't move it
+		if (LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)obj)->getPreferredType()))
+			return FALSE;
+		return TRUE;
 	}
 	return FALSE;
 }
 
 void LLFolderBridge::selectItem()
 {
+	// Have no fear: the first thing start() does is to test if everything for that folder has been fetched...
+	LLInventoryModelBackgroundFetch::instance().start(getUUID(), true);
 }
 
-
 // Iterate through a folder's children to determine if
 // all the children are removable.
 class LLIsItemRemovable : public LLFolderViewFunctor
@@ -1775,19 +1841,35 @@ BOOL LLFolderBridge::isUpToDate() const
 
 BOOL LLFolderBridge::isItemCopyable() const
 {
-	// Can copy folders to paste-as-link, but not for straight paste.
-	return gSavedSettings.getBOOL("InventoryLinking");
+	// Folders are copyable if items in them are, recursively, copyable.
+	
+	// Get the content of the folder
+	LLInventoryModel::cat_array_t* cat_array;
+	LLInventoryModel::item_array_t* item_array;
+	gInventory.getDirectDescendentsOf(mUUID,cat_array,item_array);
+
+	// Check the items
+	LLInventoryModel::item_array_t item_array_copy = *item_array;
+	for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
+	{
+		LLInventoryItem* item = *iter;
+		LLItemBridge item_br(mInventoryPanel.get(), mRoot, item->getUUID());
+		if (!item_br.isItemCopyable())
+			return FALSE;
 }
 
-BOOL LLFolderBridge::copyToClipboard() const
+	// Check the folders
+	LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
+	for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
 {
-	if(isItemCopyable())
-	{
-		LLInventoryClipboard::instance().add(mUUID);
+		LLViewerInventoryCategory* category = *iter;
+		LLFolderBridge cat_br(mInventoryPanel.get(), mRoot, category->getUUID());
+		if (!cat_br.isItemCopyable())
+			return FALSE;
+	}
+	
 		return TRUE;
 	}
-	return FALSE;
-}
 
 BOOL LLFolderBridge::isClipboardPasteable() const
 {
@@ -1804,7 +1886,7 @@ BOOL LLFolderBridge::isClipboardPasteable() const
 		}
 
 		LLDynamicArray<LLUUID> objects;
-		LLInventoryClipboard::instance().retrieve(objects);
+		LLClipboard::instance().pasteFromClipboard(objects);
 		const LLViewerInventoryCategory *current_cat = getCategory();
 
 		// Search for the direct descendent of current Friends subfolder among all pasted items,
@@ -1842,7 +1924,7 @@ BOOL LLFolderBridge::isClipboardPasteableAsLink() const
 		const BOOL is_in_friend_folder = LLFriendCardsManager::instance().isCategoryInFriendFolder( current_cat );
 		const LLUUID &current_cat_id = current_cat->getUUID();
 		LLDynamicArray<LLUUID> objects;
-		LLInventoryClipboard::instance().retrieve(objects);
+		LLClipboard::instance().pasteFromClipboard(objects);
 		S32 count = objects.count();
 		for(S32 i = 0; i < count; i++)
 		{
@@ -2432,121 +2514,114 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
 	return accept;
 }
 
-//Used by LLFolderBridge as callback for directory recursion.
-class LLRightClickInventoryFetchObserver : public LLInventoryFetchItemsObserver
-{
-public:
-	LLRightClickInventoryFetchObserver(const uuid_vec_t& ids) :
-		LLInventoryFetchItemsObserver(ids),
-		mCopyItems(false)
-	{ };
-	LLRightClickInventoryFetchObserver(const uuid_vec_t& ids,
-									   const LLUUID& cat_id, 
-									   bool copy_items) :
-		LLInventoryFetchItemsObserver(ids),
-		mCatID(cat_id),
-		mCopyItems(copy_items)
-	{ };
-	virtual void done()
-	{
-		// we've downloaded all the items, so repaint the dialog
-		LLFolderBridge::staticFolderOptionsMenu();
-
-		gInventory.removeObserver(this);
-		delete this;
-	}
-
-protected:
-	LLUUID mCatID;
-	bool mCopyItems;
-
-};
-
-//Used by LLFolderBridge as callback for directory recursion.
-class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
-{
-public:
-	LLRightClickInventoryFetchDescendentsObserver(const uuid_vec_t& ids,
-												  bool copy_items) : 
-		LLInventoryFetchDescendentsObserver(ids),
-		mCopyItems(copy_items) 
-	{}
-	~LLRightClickInventoryFetchDescendentsObserver() {}
-	virtual void done();
-protected:
-	bool mCopyItems;
-};
-
-void LLRightClickInventoryFetchDescendentsObserver::done()
+void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer)
 {
-	// Avoid passing a NULL-ref as mCompleteFolders.front() down to
-	// gInventory.collectDescendents()
+	// Bail out immediately if no descendents
 	if( mComplete.empty() )
 	{
 		llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl;
+		if (clear_observer)
+		{
 		dec_busy_count();
 		gInventory.removeObserver(this);
 		delete this;
+		}
 		return;
 	}
 
-	// What we do here is get the complete information on the items in
-	// the library, and set up an observer that will wait for that to
-	// happen.
-	LLInventoryModel::cat_array_t cat_array;
-	LLInventoryModel::item_array_t item_array;
-	gInventory.collectDescendents(mComplete.front(),
-								  cat_array,
-								  item_array,
-								  LLInventoryModel::EXCLUDE_TRASH);
-	S32 count = item_array.count();
-#if 0 // HACK/TODO: Why?
-	// This early causes a giant menu to get produced, and doesn't seem to be needed.
-	if(!count)
-	{
-		llwarns << "Nothing fetched in category " << mCompleteFolders.front()
-				<< llendl;
+	// Copy the list of complete fetched folders while "this" is still valid
+	uuid_vec_t completed_folder = mComplete;
+	
+	// Clean up, and remove this as an observer now since recursive calls
+	// could notify observers and throw us into an infinite loop.
+	if (clear_observer)
+	{
 		dec_busy_count();
 		gInventory.removeObserver(this);
 		delete this;
-		return;
 	}
-#endif
 
-	uuid_vec_t ids;
-	for(S32 i = 0; i < count; ++i)
+	for (uuid_vec_t::iterator current_folder = completed_folder.begin(); current_folder != completed_folder.end(); ++current_folder)
 	{
-		ids.push_back(item_array.get(i)->getUUID());
-	}
+		// Get the information on the fetched folder items and subfolders and fetch those 
+		LLInventoryModel::cat_array_t* cat_array;
+		LLInventoryModel::item_array_t* item_array;
+		gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array);
 
-	LLRightClickInventoryFetchObserver* outfit = new LLRightClickInventoryFetchObserver(ids, mComplete.front(), mCopyItems);
+		S32 item_count = item_array->count();
+		S32 cat_count = cat_array->count();
+	
+		// Move to next if current folder empty
+		if ((item_count == 0) && (cat_count == 0))
+	{
+			continue;
+	}
 
-	// clean up, and remove this as an observer since the call to the
-	// outfit could notify observers and throw us into an infinite
-	// loop.
-	dec_busy_count();
-	gInventory.removeObserver(this);
-	delete this;
+		uuid_vec_t ids;
+		LLRightClickInventoryFetchObserver* outfit = NULL;
+		LLRightClickInventoryFetchDescendentsObserver* categories = NULL;
 
-	// increment busy count and either tell the inventory to check &
-	// call done, or add this object to the inventory for observation.
-	inc_busy_count();
+		// Fetch the items
+		if (item_count)
+		{
+			for (S32 i = 0; i < item_count; ++i)
+			{
+				ids.push_back(item_array->get(i)->getUUID());
+			}
+			outfit = new LLRightClickInventoryFetchObserver(ids);
+		}
+		// Fetch the subfolders
+		if (cat_count)
+		{
+			for (S32 i = 0; i < cat_count; ++i)
+			{
+				ids.push_back(cat_array->get(i)->getUUID());
+			}
+			categories = new LLRightClickInventoryFetchDescendentsObserver(ids);
+		}
 
-	// do the fetch
+		// Perform the item fetch
+		if (outfit)
+		{
 	outfit->startFetch();
-	outfit->done();				//Not interested in waiting and this will be right 99% of the time.
+			outfit->execute();				// Not interested in waiting and this will be right 99% of the time.
+			delete outfit;
 //Uncomment the following code for laggy Inventory UI.
-/*	if(outfit->isFinished())
+			/*
+			 if (outfit->isFinished())
 	{
 	// everything is already here - call done.
-	outfit->done();
+				outfit->execute();
+				delete outfit;
 	}
 	else
 	{
-	// it's all on it's way - add an observer, and the inventory
+				// it's all on its way - add an observer, and the inventory
 	// will call done for us when everything is here.
+				inc_busy_count();
 	gInventory.addObserver(outfit);
-	}*/
+			}
+			*/
+		}
+		// Perform the subfolders fetch : this is where we truly recurse down the folder hierarchy
+		if (categories)
+		{
+			categories->startFetch();
+			if (categories->isFinished())
+			{
+				// everything is already here - call done.
+				categories->execute();
+				delete categories;
+			}
+			else
+			{
+				// it's all on its way - add an observer, and the inventory
+				// will call done for us when everything is here.
+				inc_busy_count();
+				gInventory.addObserver(categories);
+			}
+		}
+	}
 }
 
 
@@ -2665,6 +2740,12 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
 		modifyOutfit(TRUE);
 		return;
 	}
+	else if ("cut" == action)
+	{
+		cutToClipboard();
+		LLFolderView::removeCutItems();
+		return;
+	}
 	else if ("copy" == action)
 	{
 		copyToClipboard();
@@ -2867,7 +2948,7 @@ bool LLFolderBridge::removeItemResponse(const LLSD& notification, const LLSD& re
 	{
 		// move it to the trash
 		LLPreview::hide(mUUID);
-		remove_category(getInventoryModel(), mUUID);
+		getInventoryModel()->removeCategory(mUUID);
 		return TRUE;
 	}
 	return FALSE;
@@ -2886,7 +2967,7 @@ void LLFolderBridge::pasteFromClipboard()
 		const BOOL move_is_into_outbox = model->isObjectDescendentOf(mUUID, outbox_id);
 
 		LLDynamicArray<LLUUID> objects;
-		LLInventoryClipboard::instance().retrieve(objects);
+		LLClipboard::instance().pasteFromClipboard(objects);
 
 		if (move_is_into_outbox)
 		{
@@ -2936,7 +3017,8 @@ void LLFolderBridge::pasteFromClipboard()
 			const LLUUID& item_id = (*iter);
 
 			LLInventoryItem *item = model->getItem(item_id);
-			if (item)
+			LLInventoryObject *obj = model->getObject(item_id);
+			if (obj)
 			{
 				if (move_is_into_current_outfit || move_is_into_outfit)
 				{
@@ -2945,10 +3027,21 @@ void LLFolderBridge::pasteFromClipboard()
 						dropToOutfit(item, move_is_into_current_outfit);
 					}
 				}
-				else if(LLInventoryClipboard::instance().isCutMode())
+				else if (LLClipboard::instance().isCutMode())
+				{
+					// Do a move to "paste" a "cut"
+					// move_inventory_item() is not enough, as we have to update inventory locally too
+					if (LLAssetType::AT_CATEGORY == obj->getType())
+					{
+						LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id);
+						llassert(vicat);
+						if (vicat)
+						{
+							changeCategoryParent(model, vicat, parent_id, FALSE);
+						}
+					}
+					else
 				{
-					// move_inventory_item() is not enough,
-					//we have to update inventory locally too
 					LLViewerInventoryItem* viitem = dynamic_cast<LLViewerInventoryItem*>(item);
 					llassert(viitem);
 					if (viitem)
@@ -2956,6 +3049,19 @@ void LLFolderBridge::pasteFromClipboard()
 						changeItemParent(model, viitem, parent_id, FALSE);
 					}
 				}
+				}
+				else
+				{
+					// Do a "copy" to "paste" a regular copy clipboard
+					if (LLAssetType::AT_CATEGORY == obj->getType())
+					{
+						LLViewerInventoryCategory* vicat = (LLViewerInventoryCategory *) model->getCategory(item_id);
+						llassert(vicat);
+						if (vicat)
+						{
+							copy_inventory_category(model, vicat, parent_id);
+						}
+					}
 				else
 				{
 					copy_inventory_item(
@@ -2969,6 +3075,9 @@ void LLFolderBridge::pasteFromClipboard()
 			}
 		}
 	}
+		// Change mode to paste for next paste
+		LLClipboard::instance().setCutMode(false);
+	}
 }
 
 void LLFolderBridge::pasteLinkFromClipboard()
@@ -2992,7 +3101,7 @@ void LLFolderBridge::pasteLinkFromClipboard()
 		const LLUUID parent_id(mUUID);
 
 		LLDynamicArray<LLUUID> objects;
-		LLInventoryClipboard::instance().retrieve(objects);
+		LLClipboard::instance().pasteFromClipboard(objects);
 		for (LLDynamicArray<LLUUID>::const_iterator iter = objects.begin();
 			 iter != objects.end();
 			 ++iter)
@@ -3030,6 +3139,8 @@ void LLFolderBridge::pasteLinkFromClipboard()
 					LLPointer<LLInventoryCallback>(NULL));
 			}
 		}
+		// Change mode to paste for next paste
+		LLClipboard::instance().setCutMode(false);
 	}
 }
 
@@ -3287,16 +3398,19 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 		folders.push_back(category->getUUID());
 
 		sSelf = getHandle();
-		LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders, FALSE);
+		LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(folders);
 		fetch->startFetch();
-		inc_busy_count();
 		if (fetch->isFinished())
 		{
+			// Do not call execute() or done() here as if the folder is here, there's likely no point drilling down 
+			// This saves lots of time as buildContextMenu() is called a lot
+			delete fetch;
 			buildContextMenuFolderOptions(flags);
 		}
 		else
 		{
 			// it's all on its way - add an observer, and the inventory will call done for us when everything is here.
+			inc_busy_count();
 			gInventory.addObserver(fetch);
 		}
 	}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 3b4f845f54b771941f1fe683bfca346e67be386d..dc9e88d54d84fb2c048609a5478eb0ad6b335098 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -106,8 +106,8 @@ public:
 	virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch);
 	virtual void move(LLFolderViewEventListener* new_parent_bridge) {}
 	virtual BOOL isItemCopyable() const { return FALSE; }
-	virtual BOOL copyToClipboard() const { return FALSE; }
-	virtual void cutToClipboard();
+	virtual BOOL copyToClipboard() const;
+	virtual BOOL cutToClipboard() const;
 	virtual BOOL isClipboardPasteable() const;
 	virtual BOOL isClipboardPasteableAsLink() const;
 	virtual void pasteFromClipboard() {}
@@ -212,7 +212,6 @@ public:
 	virtual BOOL renameItem(const std::string& new_name);
 	virtual BOOL removeItem();
 	virtual BOOL isItemCopyable() const;
-	virtual BOOL copyToClipboard() const;
 	virtual BOOL hasChildren() const { return FALSE; }
 	virtual BOOL isUpToDate() const { return TRUE; }
 
@@ -275,7 +274,6 @@ public:
 	virtual BOOL isItemCopyable() const;
 	virtual BOOL isClipboardPasteable() const;
 	virtual BOOL isClipboardPasteableAsLink() const;
-	virtual BOOL copyToClipboard() const;
 	
 	static void createWearable(LLFolderBridge* bridge, LLWearableType::EType type);
 
diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp
index d176aa9da64fc0b3d7e5f5398ed5c8747066abc9..4573074c7322683f533427032cb39f3adb8a5754 100644
--- a/indra/newview/llinventoryfilter.cpp
+++ b/indra/newview/llinventoryfilter.cpp
@@ -39,8 +39,11 @@
 #include "llviewerfoldertype.h"
 
 // linden library includes
+#include "llclipboard.h"
 #include "lltrans.h"
 
+LLFastTimer::DeclareTimer FT_FILTER_CLIPBOARD("Filter Clipboard");
+
 LLInventoryFilter::FilterOps::FilterOps() :
 	mFilterObjectTypes(0xffffffffffffffffULL),
 	mFilterCategoryTypes(0xffffffffffffffffULL),
@@ -88,11 +91,15 @@ LLInventoryFilter::~LLInventoryFilter()
 
 BOOL LLInventoryFilter::check(const LLFolderViewItem* item) 
 {
-	// If it's a folder and we're showing all folders, return TRUE automatically.
+	// Clipboard cut items are *always* filtered so we need this value upfront
+	const LLFolderViewEventListener* listener = item->getListener();
+	const BOOL passed_clipboard = (listener ? checkAgainstClipboard(listener->getUUID()) : TRUE);
+
+	// If it's a folder and we're showing all folders, return automatically.
 	const BOOL is_folder = (dynamic_cast<const LLFolderViewFolder*>(item) != NULL);
 	if (is_folder && (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS))
 	{
-		return TRUE;
+		return passed_clipboard;
 	}
 
 	mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : std::string::npos;
@@ -103,6 +110,7 @@ BOOL LLInventoryFilter::check(const LLFolderViewItem* item)
 	const BOOL passed = (passed_filtertype &&
 						 passed_permissions &&
 						 passed_filterlink &&
+						 passed_clipboard &&
 						 (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos));
 
 	return passed;
@@ -114,8 +122,10 @@ bool LLInventoryFilter::check(const LLInventoryItem* item)
 
 	const bool passed_filtertype = checkAgainstFilterType(item);
 	const bool passed_permissions = checkAgainstPermissions(item);
+	const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID());
 	const bool passed = (passed_filtertype &&
 						 passed_permissions &&
+						 passed_clipboard &&
 						 (mFilterSubString.size() == 0 || mSubStringMatchOffset != std::string::npos));
 
 	return passed;
@@ -145,12 +155,15 @@ bool LLInventoryFilter::checkFolder(const LLFolderViewFolder* folder) const
 
 bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
 {
+	// Always check against the clipboard
+	const BOOL passed_clipboard = checkAgainstClipboard(folder_id);
+	
 	// we're showing all folders, overriding filter
 	if (mFilterOps.mShowFolderState == LLInventoryFilter::SHOW_ALL_FOLDERS)
 	{
-		return true;
+		return passed_clipboard;
 	}
-	
+
 	if (mFilterOps.mFilterTypes & FILTERTYPE_CATEGORY)
 	{
 		// Can only filter categories for items in your inventory
@@ -163,7 +176,7 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const
 			return false;
 	}
 
-	return true;
+	return passed_clipboard;
 }
 
 BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) const
@@ -255,7 +268,7 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con
 			}
 		}
 	}
-	
+
 	return TRUE;
 }
 
@@ -309,6 +322,31 @@ bool LLInventoryFilter::checkAgainstFilterType(const LLInventoryItem* item) cons
 	return true;
 }
 
+// Items and folders that are on the clipboard or, recursively, in a folder which  
+// is on the clipboard must be filtered out if the clipboard is in the "cut" mode.
+bool LLInventoryFilter::checkAgainstClipboard(const LLUUID& object_id) const
+{
+	if (LLClipboard::instance().isCutMode())
+	{
+		LLFastTimer ft(FT_FILTER_CLIPBOARD);
+		LLUUID current_id = object_id;
+		LLInventoryObject *current_object = gInventory.getObject(object_id);
+		while (current_id.notNull() && current_object)
+		{
+			if (LLClipboard::instance().isOnClipboard(current_id))
+			{
+				return false;
+			}
+			current_id = current_object->getParentUUID();
+			if (current_id.notNull())
+			{
+				current_object = gInventory.getObject(current_id);
+			}
+		}
+	}
+	return true;
+}
+
 BOOL LLInventoryFilter::checkAgainstPermissions(const LLFolderViewItem* item) const
 {
 	const LLFolderViewEventListener* listener = item->getListener();
@@ -364,6 +402,11 @@ std::string::size_type LLInventoryFilter::getStringMatchOffset() const
 	return mSubStringMatchOffset;
 }
 
+BOOL LLInventoryFilter::isDefault() const
+{
+	return !isNotDefault();
+}
+
 // has user modified default filter params?
 BOOL LLInventoryFilter::isNotDefault() const
 {
@@ -379,7 +422,7 @@ BOOL LLInventoryFilter::isNotDefault() const
 	not_default |= (mFilterOps.mMinDate != mDefaultFilterOps.mMinDate);
 	not_default |= (mFilterOps.mMaxDate != mDefaultFilterOps.mMaxDate);
 	not_default |= (mFilterOps.mHoursAgo != mDefaultFilterOps.mHoursAgo);
-
+	
 	return not_default;
 }
 
@@ -558,8 +601,14 @@ void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date)
 		setModified();
 	}
 
-	areDateLimitsSet() ? mFilterOps.mFilterTypes |= FILTERTYPE_DATE
-			: mFilterOps.mFilterTypes &= ~FILTERTYPE_DATE;
+	if (areDateLimitsSet())
+	{
+		mFilterOps.mFilterTypes |= FILTERTYPE_DATE;
+	}
+	else
+	{
+		mFilterOps.mFilterTypes &= ~FILTERTYPE_DATE;
+	}
 }
 
 void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl)
@@ -575,8 +624,14 @@ void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl)
 		setModified();
 	}
 
-	areDateLimitsSet() ? mFilterOps.mFilterTypes |= FILTERTYPE_DATE
-			: mFilterOps.mFilterTypes &= ~FILTERTYPE_DATE;
+	if (areDateLimitsSet())
+	{
+		mFilterOps.mFilterTypes |= FILTERTYPE_DATE;
+	}
+	else
+	{
+		mFilterOps.mFilterTypes &= ~FILTERTYPE_DATE;
+	}
 }
 
 BOOL LLInventoryFilter::isSinceLogoff() const
@@ -622,8 +677,14 @@ void LLInventoryFilter::setHoursAgo(U32 hours)
 		}
 	}
 
-	areDateLimitsSet() ? mFilterOps.mFilterTypes |= FILTERTYPE_DATE
-			: mFilterOps.mFilterTypes &= ~FILTERTYPE_DATE;
+	if (areDateLimitsSet())
+	{
+		mFilterOps.mFilterTypes |= FILTERTYPE_DATE;
+	}
+	else
+	{
+		mFilterOps.mFilterTypes &= ~FILTERTYPE_DATE;
+	}
 }
 
 void LLInventoryFilter::setFilterLinks(U64 filter_links)
@@ -947,7 +1008,7 @@ void LLInventoryFilter::fromLLSD(LLSD& data)
 {
 	if(data.has("filter_types"))
 	{
-		setFilterObjectTypes((U32)data["filter_types"].asInteger());
+		setFilterObjectTypes((U64)data["filter_types"].asInteger());
 	}
 
 	if(data.has("min_date") && data.has("max_date"))
diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h
index 6be2acfaa34d07a563d720e195849e5127cdf8ab..9e600c036f0e8679846b564bacbc478fb7db1d1c 100644
--- a/indra/newview/llinventoryfilter.h
+++ b/indra/newview/llinventoryfilter.h
@@ -124,6 +124,7 @@ public:
 	BOOL 				checkAgainstPermissions(const LLFolderViewItem* item) const;
 	bool 				checkAgainstPermissions(const LLInventoryItem* item) const;
 	BOOL 				checkAgainstFilterLinks(const LLFolderViewItem* item) const;
+	bool				checkAgainstClipboard(const LLUUID& object_id) const;
 
 	std::string::size_type getStringMatchOffset() const;
 
@@ -162,6 +163,7 @@ public:
 	// +-------------------------------------------------------------------+
 	// + Default
 	// +-------------------------------------------------------------------+
+	BOOL 				isDefault() const;
 	BOOL 				isNotDefault() const;
 	void 				markDefault();
 	void 				resetDefault();
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index dd92188e9d2d0367d0eb8acfda16375249bac2bd..ab5b08291568165e81d6cb38c35349c9d6f25630 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -54,7 +54,6 @@
 #include "lliconctrl.h"
 #include "llimview.h"
 #include "llinventorybridge.h"
-#include "llinventoryclipboard.h"
 #include "llinventorymodel.h"
 #include "llinventorypanel.h"
 #include "lllineeditor.h"
@@ -109,91 +108,6 @@ void append_path(const LLUUID& id, std::string& path)
 	path.append(temp);
 }
 
-void change_item_parent(LLInventoryModel* model,
-						LLViewerInventoryItem* item,
-						const LLUUID& new_parent_id,
-						BOOL restamp)
-{
-	if (item->getParentUUID() != new_parent_id)
-	{
-		LLInventoryModel::update_list_t update;
-		LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
-		update.push_back(old_folder);
-		LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
-		update.push_back(new_folder);
-		gInventory.accountForUpdate(update);
-
-		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
-		new_item->setParent(new_parent_id);
-		new_item->updateParentOnServer(restamp);
-		model->updateItem(new_item);
-		model->notifyObservers();
-	}
-}
-
-void change_category_parent(LLInventoryModel* model,
-	LLViewerInventoryCategory* cat,
-	const LLUUID& new_parent_id,
-	BOOL restamp)
-{
-	if (!model || !cat)
-	{
-		return;
-	}
-
-	// Can't move a folder into a child of itself.
-	if (model->isObjectDescendentOf(new_parent_id, cat->getUUID()))
-	{
-		return;
-	}
-
-	LLInventoryModel::update_list_t update;
-	LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
-	update.push_back(old_folder);
-	LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
-	update.push_back(new_folder);
-	model->accountForUpdate(update);
-
-	LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
-	new_cat->setParent(new_parent_id);
-	new_cat->updateParentOnServer(restamp);
-	model->updateCategory(new_cat);
-	model->notifyObservers();
-}
-
-void remove_category(LLInventoryModel* model, const LLUUID& cat_id)
-{
-	if (!model || !get_is_category_removable(model, cat_id))
-	{
-		return;
-	}
-
-	// Look for any gestures and deactivate them
-	LLInventoryModel::cat_array_t	descendent_categories;
-	LLInventoryModel::item_array_t	descendent_items;
-	gInventory.collectDescendents(cat_id, descendent_categories, descendent_items, FALSE);
-
-	for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin();
-		 iter != descendent_items.end();
-		 ++iter)
-	{
-		const LLViewerInventoryItem* item = (*iter);
-		const LLUUID& item_id = item->getUUID();
-		if (item->getType() == LLAssetType::AT_GESTURE
-			&& LLGestureMgr::instance().isGestureActive(item_id))
-		{
-			LLGestureMgr::instance().deactivateGesture(item_id);
-		}
-	}
-
-	LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
-	if (cat)
-	{
-		const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH);
-		change_category_parent(model, cat, trash_id, TRUE);
-	}
-}
-
 void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name)
 {
 	LLViewerInventoryCategory* cat;
@@ -214,6 +128,49 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s
 	model->notifyObservers();
 }
 
+void copy_inventory_category(LLInventoryModel* model,
+							 LLViewerInventoryCategory* cat,
+							 const LLUUID& parent_id,
+							 const LLUUID& root_copy_id)
+{
+	// Create the initial folder
+	LLUUID new_cat_uuid = gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName());
+	model->notifyObservers();
+	
+	// We need to exclude the initial root of the copy to avoid recursively copying the copy, etc...
+	LLUUID root_id = (root_copy_id.isNull() ? new_cat_uuid : root_copy_id);
+
+	// Get the content of the folder
+	LLInventoryModel::cat_array_t* cat_array;
+	LLInventoryModel::item_array_t* item_array;
+	gInventory.getDirectDescendentsOf(cat->getUUID(),cat_array,item_array);
+
+	// Copy all the items
+	LLInventoryModel::item_array_t item_array_copy = *item_array;
+	for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++)
+	{
+		LLInventoryItem* item = *iter;
+		copy_inventory_item(
+							gAgent.getID(),
+							item->getPermissions().getOwner(),
+							item->getUUID(),
+							new_cat_uuid,
+							std::string(),
+							LLPointer<LLInventoryCallback>(NULL));
+	}
+	
+	// Copy all the folders
+	LLInventoryModel::cat_array_t cat_array_copy = *cat_array;
+	for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++)
+	{
+		LLViewerInventoryCategory* category = *iter;
+		if (category->getUUID() != root_id)
+		{
+			copy_inventory_category(model, category, new_cat_uuid, root_id);
+		}
+	}
+}
+
 class LLInventoryCollectAllItems : public LLInventoryCollectFunctor
 {
 public:
@@ -568,8 +525,7 @@ void move_to_outbox_cb_action(const LLSD& payload)
 
 		LLUUID parent = viitem->getParentUUID();
 
-		change_item_parent(
-			&gInventory,
+		gInventory.changeItemParent(
 			viitem,
 			dest_folder_id,
 			false);
@@ -596,7 +552,7 @@ void move_to_outbox_cb_action(const LLSD& payload)
 
 				if (cat_array->empty() && item_array->empty())
 				{
-					remove_category(&gInventory, parent);
+					gInventory.removeCategory(parent);
 				}
 
 				if (parent == top_level_folder)
@@ -670,7 +626,7 @@ void move_item_within_outbox(LLInventoryItem* inv_item, LLUUID dest_folder, S32
 	
 	LLViewerInventoryItem * viewer_inv_item = (LLViewerInventoryItem *) inv_item;
 
-	change_item_parent(&gInventory,
+	gInventory.changeItemParent(
 					   viewer_inv_item,
 					   dest_folder,
 					   false);
@@ -992,20 +948,24 @@ void LLSaveFolderState::setApply(BOOL apply)
 void LLSaveFolderState::doFolder(LLFolderViewFolder* folder)
 {
 	LLMemType mt(LLMemType::MTYPE_INVENTORY_DO_FOLDER);
+	LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener();
+	if(!bridge) return;
+	
 	if(mApply)
 	{
 		// we're applying the open state
-		LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener();
-		if(!bridge) return;
 		LLUUID id(bridge->getUUID());
 		if(mOpenFolders.find(id) != mOpenFolders.end())
 		{
-			folder->setOpen(TRUE);
+			if (!folder->isOpen())
+			{
+				folder->setOpen(TRUE);
+			}
 		}
 		else
 		{
 			// keep selected filter in its current state, this is less jarring to user
-			if (!folder->isSelected())
+			if (!folder->isSelected() && folder->isOpen())
 			{
 				folder->setOpen(FALSE);
 			}
@@ -1016,8 +976,6 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder)
 		// we're recording state at this point
 		if(folder->isOpen())
 		{
-			LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener();
-			if(!bridge) return;
 			mOpenFolders.insert(bridge->getUUID());
 		}
 	}
@@ -1053,7 +1011,6 @@ void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item)
 		{
 			item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
 		}
-		item->getRoot()->scrollToShowSelection();
 		mItemSelected = TRUE;
 	}
 }
@@ -1067,7 +1024,6 @@ void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder)
 		{
 			folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
 		}
-		folder->getRoot()->scrollToShowSelection();
 		mItemSelected = TRUE;
 	}
 }
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index ce2b89b22e67baaeb18d0444590a8cc3665a4e6e..5cf9c528b01057ad0095a48f032ee286a7beb0bf 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -57,20 +57,10 @@ void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id);
 
 void show_item_original(const LLUUID& item_uuid);
 
-void change_item_parent(LLInventoryModel* model,
-									 LLViewerInventoryItem* item,
-									 const LLUUID& new_parent_id,
-									 BOOL restamp);
-
-void change_category_parent(LLInventoryModel* model,
-	LLViewerInventoryCategory* cat,
-	const LLUUID& new_parent_id,
-	BOOL restamp);
-
-void remove_category(LLInventoryModel* model, const LLUUID& cat_id);
-
 void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name);
 
+void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null);
+
 // Generates a string containing the path to the item specified by item_id.
 void append_path(const LLUUID& id, std::string& path);
 
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index a71b699fdd7cbef9c6cfa32552192bc453fd8bf8..8092f3bf36bbcfe7cf5898847b4532ca9dbe503a 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -30,6 +30,7 @@
 #include "llagent.h"
 #include "llagentwearables.h"
 #include "llappearancemgr.h"
+#include "llclipboard.h"
 #include "llinventorypanel.h"
 #include "llinventorybridge.h"
 #include "llinventoryfunctions.h"
@@ -46,6 +47,8 @@
 #include "llviewerregion.h"
 #include "llcallbacklist.h"
 #include "llvoavatarself.h"
+#include "llgesturemgr.h"
+#include <typeinfo>
 
 //#define DIFF_INVENTORY_FILES
 #ifdef DIFF_INVENTORY_FILES
@@ -1017,6 +1020,66 @@ void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
 	}
 }
 
+// Migrated from llinventoryfunctions
+void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item,
+										const LLUUID& new_parent_id,
+										BOOL restamp)
+{
+	if (item->getParentUUID() == new_parent_id)
+	{
+		LL_DEBUGS("Inventory") << "'" << item->getName() << "' (" << item->getUUID()
+							   << ") is already in folder " << new_parent_id << LL_ENDL;
+	}
+	else
+	{
+		LL_INFOS("Inventory") << "Moving '" << item->getName() << "' (" << item->getUUID()
+							  << ") from " << item->getParentUUID() << " to folder "
+							  << new_parent_id << LL_ENDL;
+		LLInventoryModel::update_list_t update;
+		LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+		update.push_back(old_folder);
+		LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
+		update.push_back(new_folder);
+		accountForUpdate(update);
+
+		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+		new_item->setParent(new_parent_id);
+		new_item->updateParentOnServer(restamp);
+		updateItem(new_item);
+		notifyObservers();
+	}
+}
+
+// Migrated from llinventoryfunctions
+void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat,
+											const LLUUID& new_parent_id,
+											BOOL restamp)
+{
+	if (!cat)
+	{
+		return;
+	}
+
+	// Can't move a folder into a child of itself.
+	if (isObjectDescendentOf(new_parent_id, cat->getUUID()))
+	{
+		return;
+	}
+
+	LLInventoryModel::update_list_t update;
+	LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
+	update.push_back(old_folder);
+	LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
+	update.push_back(new_folder);
+	accountForUpdate(update);
+
+	LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+	new_cat->setParent(new_parent_id);
+	new_cat->updateParentOnServer(restamp);
+	updateCategory(new_cat);
+	notifyObservers();
+}
+
 // Delete a particular inventory object by ID.
 void LLInventoryModel::deleteObject(const LLUUID& id)
 {
@@ -1110,50 +1173,82 @@ void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
 		return;
 	}
 	LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
-	if(cat.notNull())
-	{
-		// do the cache accounting
-		llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
-				<< llendl;
-		S32 descendents = cat->getDescendentCount();
-		if(descendents > 0)
-		{
-			LLCategoryUpdate up(id, -descendents);
-			accountForUpdate(up);
+	if (cat.notNull())
+	{
+		if (LLClipboard::instance().hasContents() && LLClipboard::instance().isCutMode())
+		{
+			// Something on the clipboard is in "cut mode" and needs to be preserved
+			llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
+			<< " iterate and purge non hidden items" << llendl;
+			cat_array_t* categories;
+			item_array_t* items;
+			// Get the list of direct descendants in tha categoy passed as argument
+			getDirectDescendentsOf(id, categories, items);
+			std::vector<LLUUID> list_uuids;
+			// Make a unique list with all the UUIDs of the direct descendants (items and categories are not treated differently)
+			// Note: we need to do that shallow copy as purging things will invalidate the categories or items lists
+			for (cat_array_t::const_iterator it = categories->begin(); it != categories->end(); ++it)
+			{
+				list_uuids.push_back((*it)->getUUID());
+			}
+			for (item_array_t::const_iterator it = items->begin(); it != items->end(); ++it)
+			{
+				list_uuids.push_back((*it)->getUUID());
+			}
+			// Iterate through the list and only purge the UUIDs that are not on the clipboard
+			for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it)
+			{
+				if (!LLClipboard::instance().isOnClipboard(*it))
+				{
+					purgeObject(*it);
+				}
+			}
 		}
+		else
+		{
+			// Fast purge
+			// do the cache accounting
+			llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
+				<< llendl;
+			S32 descendents = cat->getDescendentCount();
+			if(descendents > 0)
+			{
+				LLCategoryUpdate up(id, -descendents);
+				accountForUpdate(up);
+			}
 
-		// we know that descendent count is 0, aide since the
-		// accounting may actually not do an update, we should force
-		// it here.
-		cat->setDescendentCount(0);
+			// we know that descendent count is 0, however since the
+			// accounting may actually not do an update, we should force
+			// it here.
+			cat->setDescendentCount(0);
+
+			// send it upstream
+			LLMessageSystem* msg = gMessageSystem;
+			msg->newMessage("PurgeInventoryDescendents");
+			msg->nextBlock("AgentData");
+			msg->addUUID("AgentID", gAgent.getID());
+			msg->addUUID("SessionID", gAgent.getSessionID());
+			msg->nextBlock("InventoryData");
+			msg->addUUID("FolderID", id);
+			gAgent.sendReliableMessage();
 
-		// send it upstream
-		LLMessageSystem* msg = gMessageSystem;
-		msg->newMessage("PurgeInventoryDescendents");
-		msg->nextBlock("AgentData");
-		msg->addUUID("AgentID", gAgent.getID());
-		msg->addUUID("SessionID", gAgent.getSessionID());
-		msg->nextBlock("InventoryData");
-		msg->addUUID("FolderID", id);
-		gAgent.sendReliableMessage();
-
-		// unceremoniously remove anything we have locally stored.
-		cat_array_t categories;
-		item_array_t items;
-		collectDescendents(id,
-						   categories,
-						   items,
-						   INCLUDE_TRASH);
-		S32 count = items.count();
-		S32 i;
-		for(i = 0; i < count; ++i)
-		{
-			deleteObject(items.get(i)->getUUID());
-		}
-		count = categories.count();
-		for(i = 0; i < count; ++i)
-		{
-			deleteObject(categories.get(i)->getUUID());
+			// unceremoniously remove anything we have locally stored.
+			cat_array_t categories;
+			item_array_t items;
+			collectDescendents(id,
+							   categories,
+							   items,
+							   INCLUDE_TRASH);
+			S32 count = items.count();
+			for(S32 i = 0; i < count; ++i)
+			{
+				deleteObject(items.get(i)->getUUID());
+			}
+			count = categories.count();
+			for(S32 i = 0; i < count; ++i)
+			{
+				deleteObject(categories.get(i)->getUUID());
+			}
 		}
 	}
 }
@@ -1664,6 +1759,7 @@ bool LLInventoryModel::loadSkeleton(
 		update_map_t child_counts;
 		cat_array_t categories;
 		item_array_t items;
+		item_array_t possible_broken_links;
 		cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded.
 		std::string owner_id_str;
 		owner_id.toString(owner_id_str);
@@ -1712,7 +1808,7 @@ bool LLInventoryModel::loadSkeleton(
 				LLViewerInventoryCategory* tcat = *cit;
 				
 				// we can safely ignore anything loaded from file, but
-				// not sent down in the skeleton.
+				// not sent down in the skeleton. Must have been removed from inventory.
 				if(cit == not_cached)
 				{
 					continue;
@@ -1750,6 +1846,8 @@ bool LLInventoryModel::loadSkeleton(
 			// Add all the items loaded which are parented to a
 			// category with a correctly cached parent
 			S32 bad_link_count = 0;
+			S32 good_link_count = 0;
+			S32 recovered_link_count = 0;
 			cat_map_t::iterator unparented = mCategoryMap.end();
 			for(item_array_t::const_iterator item_iter = items.begin();
 				item_iter != items.end();
@@ -1766,26 +1864,56 @@ bool LLInventoryModel::loadSkeleton(
 						// This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache.
 						if (item->getIsBrokenLink())
 						{
-							bad_link_count++;
+							//bad_link_count++;
 							lldebugs << "Attempted to add cached link item without baseobj present ( name: "
 									 << item->getName() << " itemID: " << item->getUUID()
 									 << " assetID: " << item->getAssetUUID()
 									 << " ).  Ignoring and invalidating " << cat->getName() << " . " << llendl;
-							invalid_categories.insert(cit->second);
+							possible_broken_links.push_back(item);
 							continue;
 						}
+						else if (item->getIsLinkType())
+						{
+							good_link_count++;
+						}
 						addItem(item);
 						cached_item_count += 1;
 						++child_counts[cat->getUUID()];
 					}
 				}
 			}
-			if (bad_link_count > 0)
+			if (possible_broken_links.size() > 0)
 			{
-				llinfos << "Attempted to add " << bad_link_count
-						<< " cached link items without baseobj present. "
-						<< "The corresponding categories were invalidated." << llendl;
+				for(item_array_t::const_iterator item_iter = possible_broken_links.begin();
+				    item_iter != possible_broken_links.end();
+				    ++item_iter)
+				{
+					LLViewerInventoryItem *item = (*item_iter).get();
+					const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID());
+					const LLViewerInventoryCategory* cat = cit->second.get();
+					if (item->getIsBrokenLink())
+					{
+						bad_link_count++;
+						invalid_categories.insert(cit->second);
+						//llinfos << "link still broken: " << item->getName() << " in folder " << cat->getName() << llendl;
+					}
+					else
+					{
+						// was marked as broken because of loading order, its actually fine to load
+						addItem(item);
+						cached_item_count += 1;
+						++child_counts[cat->getUUID()];
+						recovered_link_count++;
+					}
+				}
+
+ 				llinfos << "Attempted to add " << bad_link_count
+ 						<< " cached link items without baseobj present. "
+					    << good_link_count << " link items were successfully added. "
+					    << recovered_link_count << " links added in recovery. "
+ 						<< "The corresponding categories were invalidated." << llendl;
 			}
+
 		}
 		else
 		{
@@ -2683,7 +2811,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
 	{
 		LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
 		titem->unpackMessage(msg, _PREHASH_ItemData, i);
-		llinfos << "unpaked item '" << titem->getName() << "' in "
+		llinfos << "unpacked item '" << titem->getName() << "' in "
 				<< titem->getParentUUID() << llendl;
 		U32 callback_id;
 		msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id);
@@ -2931,21 +3059,77 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLFolderT
 void LLInventoryModel::removeItem(const LLUUID& item_id)
 {
 	LLViewerInventoryItem* item = getItem(item_id);
-	const LLUUID new_parent = findCategoryUUIDForType(LLFolderType::FT_TRASH);
-	if (item && item->getParentUUID() != new_parent)
+	if (! item)
 	{
-		LLInventoryModel::update_list_t update;
-		LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
-		update.push_back(old_folder);
-		LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
-		update.push_back(new_folder);
-		accountForUpdate(update);
+		LL_WARNS("Inventory") << "couldn't find inventory item " << item_id << LL_ENDL;
+	}
+	else
+	{
+		const LLUUID new_parent = findCategoryUUIDForType(LLFolderType::FT_TRASH);
+		if (new_parent.notNull())
+		{
+			LL_INFOS("Inventory") << "Moving to Trash (" << new_parent << "):" << LL_ENDL;
+			changeItemParent(item, new_parent, TRUE);
+		}
+	}
+}
 
-		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
-		new_item->setParent(new_parent);
-		new_item->updateParentOnServer(TRUE);
-		updateItem(new_item);
-		notifyObservers();
+void LLInventoryModel::removeCategory(const LLUUID& category_id)
+{
+	if (! get_is_category_removable(this, category_id))
+	{
+		return;
+	}
+
+	// Look for any gestures and deactivate them
+	LLInventoryModel::cat_array_t	descendent_categories;
+	LLInventoryModel::item_array_t	descendent_items;
+	collectDescendents(category_id, descendent_categories, descendent_items, FALSE);
+
+	for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin();
+		 iter != descendent_items.end();
+		 ++iter)
+	{
+		const LLViewerInventoryItem* item = (*iter);
+		const LLUUID& item_id = item->getUUID();
+		if (item->getType() == LLAssetType::AT_GESTURE
+			&& LLGestureMgr::instance().isGestureActive(item_id))
+		{
+			LLGestureMgr::instance().deactivateGesture(item_id);
+		}
+	}
+
+	LLViewerInventoryCategory* cat = getCategory(category_id);
+	if (cat)
+	{
+		const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
+		if (trash_id.notNull())
+		{
+			changeCategoryParent(cat, trash_id, TRUE);
+		}
+	}
+}
+
+void LLInventoryModel::removeObject(const LLUUID& object_id)
+{
+	LLInventoryObject* obj = getObject(object_id);
+	if (dynamic_cast<LLViewerInventoryItem*>(obj))
+	{
+		removeItem(object_id);
+	}
+	else if (dynamic_cast<LLViewerInventoryCategory*>(obj))
+	{
+		removeCategory(object_id);
+	}
+	else if (obj)
+	{
+		LL_WARNS("Inventory") << "object ID " << object_id
+							  << " is an object of unrecognized class "
+							  << typeid(*obj).name() << LL_ENDL;
+	}
+	else
+	{
+		LL_WARNS("Inventory") << "object ID " << object_id << " not found" << LL_ENDL;
 	}
 }
 
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 7cd85c4ab7bfd59aa1a0616136c9006f81a6f963..8382e875b48690a67c87feefa92ea03747aac248 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -306,6 +306,16 @@ public:
 	// observer notification, or server update is performed.
 	void moveObject(const LLUUID& object_id, const LLUUID& cat_id);
 
+	// Migrated from llinventoryfunctions
+	void changeItemParent(LLViewerInventoryItem* item,
+						  const LLUUID& new_parent_id,
+						  BOOL restamp);
+
+	// Migrated from llinventoryfunctions
+	void changeCategoryParent(LLViewerInventoryCategory* cat,
+							  const LLUUID& new_parent_id,
+							  BOOL restamp);
+
 	//--------------------------------------------------------------------
 	// Delete
 	//--------------------------------------------------------------------
@@ -315,8 +325,13 @@ public:
 	// consistent internal state. No cache accounting, observer
 	// notification, or server update is performed.
 	void deleteObject(const LLUUID& id);
+	/// move Item item_id to Trash
 	void removeItem(const LLUUID& item_id);
-	
+	/// move Category category_id to Trash
+	void removeCategory(const LLUUID& category_id);
+	/// removeItem() or removeCategory(), whichever is appropriate
+	void removeObject(const LLUUID& object_id);
+
 	// Delete a particular inventory object by ID, and delete it from
 	// the server. Also updates linked items.
 	void purgeObject(const LLUUID& id);
diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp
index 01a8ecfb5d8129289aa3b8cf8563aa0be3dac42e..71dd963f28c0dcc8ebd3a2a537cb7cb865293083 100644
--- a/indra/newview/llinventorypanel.cpp
+++ b/indra/newview/llinventorypanel.cpp
@@ -33,6 +33,7 @@
 #include "llagentwearables.h"
 #include "llappearancemgr.h"
 #include "llavataractions.h"
+#include "llclipboard.h"
 #include "llfloaterinventory.h"
 #include "llfloaterreg.h"
 #include "llfloatersidepanelcontainer.h"
@@ -206,10 +207,11 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
 		scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
 		LLScrollContainer::Params scroller_params(params.scroll());
 		scroller_params.rect(scroller_view_rect);
-		mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroller_params);
+		mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
 		addChild(mScroller);
 		mScroller->addChild(mFolderRoot);
 		mFolderRoot->setScrollContainer(mScroller);
+		mFolderRoot->setFollowsAll();
 		mFolderRoot->addChild(mFolderRoot->mStatusTextBox);
 	}
 
@@ -247,6 +249,9 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
 		getFilter()->setFilterEmptySystemFolders();
 	}
 	
+	// keep track of the clipboard state so that we avoid filtering too much
+	mClipboardState = LLClipboard::instance().getGeneration();
+	
 	// Initialize base class params.
 	LLPanel::initFromParams(params);
 }
@@ -277,6 +282,14 @@ void LLInventoryPanel::draw()
 {
 	// Select the desired item (in case it wasn't loaded when the selection was requested)
 	mFolderRoot->updateSelection();
+	
+	// Nudge the filter if the clipboard state changed
+	if (mClipboardState != LLClipboard::instance().getGeneration())
+	{
+		mClipboardState = LLClipboard::instance().getGeneration();
+		getFilter()->setModified(LLClipboard::instance().isCutMode() ? LLInventoryFilter::FILTER_MORE_RESTRICTIVE : LLInventoryFilter::FILTER_LESS_RESTRICTIVE);
+	}
+	
 	LLPanel::draw();
 }
 
diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h
index 7d805f6862a43ab9b136cb1c0ae2307ad4974345..6db59afb9b92a7a967a3bf5d81e4aeb30ac50b86 100644
--- a/indra/newview/llinventorypanel.h
+++ b/indra/newview/llinventorypanel.h
@@ -222,6 +222,7 @@ public:
 
 private:
 	std::string					mSortOrderSetting;
+	int							mClipboardState;
 
 	//--------------------------------------------------------------------
 	// Hidden folders
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..459e52c4f4e6f7844b17e664d1e9aa20a738d888
--- /dev/null
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -0,0 +1,942 @@
+/** 
+ * @file lllocalbitmaps.cpp
+ * @author Vaalith Jinn
+ * @brief Local Bitmaps source
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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$
+ */
+
+/* precompiled headers */
+#include "llviewerprecompiledheaders.h"
+
+/* own header */
+#include "lllocalbitmaps.h"
+
+/* boost: will not compile unless equivalent is undef'd, beware. */
+#ifdef equivalent
+#undef equivalent
+#endif 
+#include <boost/filesystem.hpp>
+
+/* image compression headers. */
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagejpeg.h"
+#include "llimagepng.h"
+
+/* time headers */
+#include <time.h>
+#include <ctime>
+
+/* misc headers */
+#include "llscrolllistctrl.h"
+#include "llfilepicker.h"
+#include "llviewertexturelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerobject.h"
+#include "llface.h"
+#include "llvoavatarself.h"
+#include "llwearable.h"
+#include "llagentwearables.h"
+#include "lltexlayerparams.h"
+#include "llvovolume.h"
+#include "llnotificationsutil.h"
+
+/*=======================================*/
+/*  Formal declarations, constants, etc. */
+/*=======================================*/ 
+std::list<LLLocalBitmap*>   LLLocalBitmapMgr::sBitmapList;
+LLLocalBitmapTimer          LLLocalBitmapMgr::sTimer;
+bool                        LLLocalBitmapMgr::sNeedsRebake;
+
+static const F32 LL_LOCAL_TIMER_HEARTBEAT   = 3.0;
+static const BOOL LL_LOCAL_USE_MIPMAPS      = true;
+static const S32 LL_LOCAL_DISCARD_LEVEL     = 0;
+static const U32 LL_LOCAL_TEXLAYER_FOR_IDX  = 0;
+static const bool LL_LOCAL_SLAM_FOR_DEBUG   = true;
+static const bool LL_LOCAL_REPLACE_ON_DEL   = true;
+static const S32 LL_LOCAL_UPDATE_RETRIES    = 5;
+
+/*=======================================*/
+/*  LLLocalBitmap: unit class            */
+/*=======================================*/ 
+LLLocalBitmap::LLLocalBitmap(std::string filename)
+	: mFilename(filename)
+	, mShortName(gDirUtilp->getBaseFileName(filename, true))
+	, mValid(false)
+	, mLastModified()
+	, mLinkStatus(LS_ON)
+	, mUpdateRetries(LL_LOCAL_UPDATE_RETRIES)
+{
+	mTrackingID.generate();
+
+	/* extension */
+	std::string temp_exten = gDirUtilp->getExtension(mFilename);
+
+	if (temp_exten == "bmp")
+	{ 
+		mExtension = ET_IMG_BMP;
+	}
+	else if (temp_exten == "tga")
+	{
+		mExtension = ET_IMG_TGA;
+	}
+	else if (temp_exten == "jpg" || temp_exten == "jpeg")
+	{
+		mExtension = ET_IMG_JPG;
+	}
+	else if (temp_exten == "png")
+	{
+		mExtension = ET_IMG_PNG;
+	}
+	else
+	{
+		llwarns << "File of no valid extension given, local bitmap creation aborted." << "\n"
+			    << "Filename: " << mFilename << llendl;
+		return; // no valid extension.
+	}
+
+	/* next phase of unit creation is nearly the same as an update cycle.
+	   we're running updateSelf as a special case with the optional UT_FIRSTUSE
+	   which omits the parts associated with removing the outdated texture */
+	mValid = updateSelf(UT_FIRSTUSE);
+}
+
+LLLocalBitmap::~LLLocalBitmap()
+{
+	// replace IDs with defaults, if set to do so.
+	if(LL_LOCAL_REPLACE_ON_DEL && mValid) // fix for STORM-1837
+	{
+		replaceIDs(mWorldID, IMG_DEFAULT);
+		LLLocalBitmapMgr::doRebake();
+	}
+
+	// delete self from gimagelist
+	LLViewerFetchedTexture* image = gTextureList.findImage(mWorldID);
+	gTextureList.deleteImage(image);
+
+	if (image)
+	{
+		image->unref();
+	}
+}
+
+/* accessors */
+std::string LLLocalBitmap::getFilename()
+{
+	return mFilename;
+}
+
+std::string LLLocalBitmap::getShortName()
+{
+	return mShortName;
+}
+
+LLUUID LLLocalBitmap::getTrackingID()
+{
+	return mTrackingID;
+}
+
+LLUUID LLLocalBitmap::getWorldID()
+{
+	return mWorldID;
+}
+
+bool LLLocalBitmap::getValid()
+{
+	return mValid;
+}
+
+/* update functions */
+bool LLLocalBitmap::updateSelf(EUpdateType optional_firstupdate)
+{
+	bool updated = false;
+	
+	if (mLinkStatus == LS_ON)
+	{
+		// verifying that the file exists
+		if (gDirUtilp->fileExists(mFilename))
+		{
+			// verifying that the file has indeed been modified
+			const std::time_t temp_time = boost::filesystem::last_write_time(boost::filesystem::path(mFilename));
+			LLSD new_last_modified = asctime(localtime(&temp_time));
+
+			if (mLastModified.asString() != new_last_modified.asString())
+			{
+				/* loading the image file and decoding it, here is a critical point which,
+				   if fails, invalidates the whole update (or unit creation) process. */
+				LLPointer<LLImageRaw> raw_image = new LLImageRaw();
+				if (decodeBitmap(raw_image))
+				{
+					// decode is successful, we can safely proceed.
+					LLUUID old_id = LLUUID::null;
+					if (!(optional_firstupdate == UT_FIRSTUSE) && !mWorldID.isNull())
+					{
+						old_id = mWorldID;
+					}
+					mWorldID.generate();
+					mLastModified = new_last_modified;
+
+					LLPointer<LLViewerFetchedTexture> texture = new LLViewerFetchedTexture
+						("file://"+mFilename, mWorldID, LL_LOCAL_USE_MIPMAPS);
+
+					texture->createGLTexture(LL_LOCAL_DISCARD_LEVEL, raw_image);
+					texture->setCachedRawImage(LL_LOCAL_DISCARD_LEVEL, raw_image);
+					texture->ref(); 
+
+					gTextureList.addImage(texture);
+			
+					if (!optional_firstupdate == UT_FIRSTUSE)
+					{
+						// seek out everything old_id uses and replace it with mWorldID
+						replaceIDs(old_id, mWorldID);
+
+						// remove old_id from gimagelist
+						LLViewerFetchedTexture* image = gTextureList.findImage(old_id);
+						gTextureList.deleteImage(image);
+						image->unref();
+					}
+
+					mUpdateRetries = LL_LOCAL_UPDATE_RETRIES;
+					updated = true;
+				}
+
+				// if decoding failed, we get here and it will attempt to decode it in the next cycles
+				// until mUpdateRetries runs out. this is done because some software lock the bitmap while writing to it
+				else
+				{
+					if (mUpdateRetries)
+					{
+						mUpdateRetries--;
+					}
+					else
+					{
+						llwarns << "During the update process the following file was found" << "\n"
+							    << "but could not be opened or decoded for " << LL_LOCAL_UPDATE_RETRIES << " attempts." << "\n"
+								<< "Filename: " << mFilename << "\n"
+								<< "Disabling further update attempts for this file." << llendl;
+
+						LLSD notif_args;
+						notif_args["FNAME"] = mFilename;
+						notif_args["NRETRIES"] = LL_LOCAL_UPDATE_RETRIES;
+						LLNotificationsUtil::add("LocalBitmapsUpdateFailedFinal", notif_args);
+
+						mLinkStatus = LS_BROKEN;
+					}
+				}		
+			}
+			
+		} // end if file exists
+
+		else
+		{
+			llwarns << "During the update process, the following file was not found." << "\n" 
+			        << "Filename: " << mFilename << "\n"
+				    << "Disabling further update attempts for this file." << llendl;
+
+			LLSD notif_args;
+			notif_args["FNAME"] = mFilename;
+			LLNotificationsUtil::add("LocalBitmapsUpdateFileNotFound", notif_args);
+
+			mLinkStatus = LS_BROKEN;
+		}
+	}
+
+	return updated;
+}
+
+bool LLLocalBitmap::decodeBitmap(LLPointer<LLImageRaw> rawimg)
+{
+	bool decode_successful = false;
+
+	switch (mExtension)
+	{
+		case ET_IMG_BMP:
+		{
+			LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+			if (bmp_image->load(mFilename) && bmp_image->decode(rawimg, 0.0f))
+			{
+				rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+				decode_successful = true;
+			}
+			break;
+		}
+
+		case ET_IMG_TGA:
+		{
+			LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+			if ((tga_image->load(mFilename) && tga_image->decode(rawimg))
+			&& ((tga_image->getComponents() == 3) || (tga_image->getComponents() == 4)))
+			{
+				rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+				decode_successful = true;
+			}
+			break;
+		}
+
+		case ET_IMG_JPG:
+		{
+			LLPointer<LLImageJPEG> jpeg_image = new LLImageJPEG;
+			if (jpeg_image->load(mFilename) && jpeg_image->decode(rawimg, 0.0f))
+			{
+				rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+				decode_successful = true;
+			}
+			break;
+		}
+
+		case ET_IMG_PNG:
+		{
+			LLPointer<LLImagePNG> png_image = new LLImagePNG;
+			if (png_image->load(mFilename) && png_image->decode(rawimg, 0.0f))
+			{
+				rawimg->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
+				decode_successful = true;
+			}
+			break;
+		}
+
+		default:
+		{
+			// separating this into -several- llwarns calls because in the extremely unlikely case that this happens
+			// accessing mFilename and any other object properties might very well crash the viewer.
+			// getting here should be impossible, or there's been a pretty serious bug.
+
+			llwarns << "During a decode attempt, the following local bitmap had no properly assigned extension." << llendl;
+			llwarns << "Filename: " << mFilename << llendl;
+		    llwarns << "Disabling further update attempts for this file." << llendl;
+			mLinkStatus = LS_BROKEN;
+		}
+	}
+
+	return decode_successful;
+}
+
+void LLLocalBitmap::replaceIDs(LLUUID old_id, LLUUID new_id)
+{
+	// checking for misuse.
+	if (old_id == new_id)
+	{
+		llinfos << "An attempt was made to replace a texture with itself. (matching UUIDs)" << "\n"
+			    << "Texture UUID: " << old_id.asString() << llendl;
+		return;
+	}
+
+	updateUserPrims(old_id, new_id);
+	updateUserSculpts(old_id, new_id); // isn't there supposed to be an IMG_DEFAULT_SCULPT or something?
+	
+	// default safeguard image for layers
+	if( new_id == IMG_DEFAULT )
+	{
+		new_id = IMG_DEFAULT_AVATAR;
+	}
+
+	/* It doesn't actually update all of those, it merely checks if any of them
+		contain the referenced ID and if so, updates. */
+	updateUserLayers(old_id, new_id, LLWearableType::WT_ALPHA);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_EYES);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_GLOVES);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_JACKET);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_PANTS);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_SHIRT);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_SHOES);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_SKIN);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_SKIRT);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_SOCKS);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_TATTOO);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERPANTS);
+	updateUserLayers(old_id, new_id, LLWearableType::WT_UNDERSHIRT);
+}
+
+// this function sorts the faces from a getFaceList[getNumFaces] into a list of objects
+// in order to prevent multiple sendTEUpdate calls per object during updateUserPrims
+std::vector<LLViewerObject*> LLLocalBitmap::prepUpdateObjects(LLUUID old_id)
+{
+	std::vector<LLViewerObject*> obj_list;
+	LLViewerFetchedTexture* old_texture = gTextureList.findImage(old_id);
+
+	for(U32 face_iterator = 0; face_iterator < old_texture->getNumFaces(); face_iterator++)
+	{
+		// getting an object from a face
+		LLFace* face_to_object = (*old_texture->getFaceList())[face_iterator];
+
+		if(face_to_object)
+		{
+			LLViewerObject* affected_object = face_to_object->getViewerObject();
+
+			if(affected_object)
+			{
+
+				// we have an object, we'll take it's UUID and compare it to
+				// whatever we already have in the returnable object list.
+				// if there is a match - we do not add (to prevent duplicates)
+				LLUUID mainlist_obj_id = affected_object->getID();
+				bool add_object = true;
+
+				// begin looking for duplicates
+				std::vector<LLViewerObject*>::iterator objlist_iter = obj_list.begin();
+				for(; (objlist_iter != obj_list.end()) && add_object; objlist_iter++)
+				{
+					LLViewerObject* obj = *objlist_iter;
+					if (obj->getID() == mainlist_obj_id)
+					{
+						add_object = false; // duplicate found.
+					}
+				}
+				// end looking for duplicates
+
+				if(add_object)
+				{
+					obj_list.push_back(affected_object);
+				}
+
+			}
+
+		}
+		
+	} // end of face-iterating for()
+
+	return obj_list;
+}
+
+void LLLocalBitmap::updateUserPrims(LLUUID old_id, LLUUID new_id)
+{
+	std::vector<LLViewerObject*> objectlist = prepUpdateObjects(old_id);
+
+	for(std::vector<LLViewerObject*>::iterator object_iterator = objectlist.begin();
+		object_iterator != objectlist.end(); object_iterator++)
+	{
+		LLViewerObject* object = *object_iterator;
+
+		if(object)
+		{
+			bool update_obj = false;
+			S32 num_faces = object->getNumFaces();
+
+			for (U8 face_iter = 0; face_iter < num_faces; face_iter++)
+			{
+				if (object->mDrawable)
+				{
+					LLFace* face = object->mDrawable->getFace(face_iter);
+					if (face && face->getTexture() && face->getTexture()->getID() == old_id)
+					{
+						object->setTEImage(face_iter, LLViewerTextureManager::getFetchedTexture
+							(new_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE));
+
+						update_obj = true;
+					}
+				}
+			}
+			
+			if (update_obj)
+			{
+				object->sendTEUpdate();
+			}
+		}
+	}
+	
+}
+
+void LLLocalBitmap::updateUserSculpts(LLUUID old_id, LLUUID new_id)
+{
+	LLViewerFetchedTexture* old_texture = gTextureList.findImage(old_id);
+	for(U32 volume_iter = 0; volume_iter < old_texture->getNumVolumes(); volume_iter++)
+	{
+		LLVOVolume* volume_to_object = (*old_texture->getVolumeList())[volume_iter];
+		LLViewerObject* object = (LLViewerObject*)volume_to_object;
+	
+		if(object)
+		{
+			if (object->isSculpted() && object->getVolume() &&
+				object->getVolume()->getParams().getSculptID() == old_id)
+			{
+				LLSculptParams* old_params = (LLSculptParams*)object->getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+				LLSculptParams new_params(*old_params);
+				new_params.setSculptTexture(new_id);
+				object->setParameterEntry(LLNetworkData::PARAMS_SCULPT, new_params, TRUE);
+			}
+		}
+	}
+}
+
+void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableType::EType type)
+{
+	U32 count = gAgentWearables.getWearableCount(type);
+	for(U32 wearable_iter = 0; wearable_iter < count; wearable_iter++)
+	{
+		LLWearable* wearable = gAgentWearables.getWearable(type, wearable_iter);
+		if (wearable)
+		{
+			std::vector<LLLocalTextureObject*> texture_list = wearable->getLocalTextureListSeq();
+			for(std::vector<LLLocalTextureObject*>::iterator texture_iter = texture_list.begin();
+				texture_iter != texture_list.end(); texture_iter++)
+			{
+				LLLocalTextureObject* lto = *texture_iter;
+
+				if (lto && lto->getID() == old_id)
+				{
+					U32 local_texlayer_index = 0; /* can't keep that as static const, gives errors, so i'm leaving this var here */
+					LLVOAvatarDefines::EBakedTextureIndex baked_texind =
+						lto->getTexLayer(local_texlayer_index)->getTexLayerSet()->getBakedTexIndex();
+				
+					LLVOAvatarDefines::ETextureIndex reg_texind = getTexIndex(type, baked_texind);
+					if (reg_texind != LLVOAvatarDefines::TEX_NUM_INDICES)
+					{
+						U32 index = gAgentWearables.getWearableIndex(wearable);
+						gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index);
+						gAgentAvatarp->wearableUpdated(type, FALSE);
+
+						/* telling the manager to rebake once update cycle is fully done */
+						LLLocalBitmapMgr::setNeedsRebake();
+					}
+
+				}
+			}
+		}
+	}
+}
+
+LLVOAvatarDefines::ETextureIndex LLLocalBitmap::getTexIndex(
+	LLWearableType::EType type, LLVOAvatarDefines::EBakedTextureIndex baked_texind)
+{
+	LLVOAvatarDefines::ETextureIndex result = LLVOAvatarDefines::TEX_NUM_INDICES; // using as a default/fail return.
+
+	switch(type)
+	{
+		case LLWearableType::WT_ALPHA:
+		{
+			switch(baked_texind)
+			{
+				case LLVOAvatarDefines::BAKED_EYES:
+				{
+					result = LLVOAvatarDefines::TEX_EYES_ALPHA;
+					break;
+				}
+
+				case LLVOAvatarDefines::BAKED_HAIR:
+				{
+					result = LLVOAvatarDefines::TEX_HAIR_ALPHA;
+					break;
+				}
+
+				case LLVOAvatarDefines::BAKED_HEAD:
+				{
+					result = LLVOAvatarDefines::TEX_HEAD_ALPHA;
+					break;
+				}
+
+				case LLVOAvatarDefines::BAKED_LOWER:
+				{
+					result = LLVOAvatarDefines::TEX_LOWER_ALPHA;
+					break;
+				}
+				case LLVOAvatarDefines::BAKED_UPPER:
+				{
+					result = LLVOAvatarDefines::TEX_UPPER_ALPHA;
+					break;
+				}
+
+				default:
+				{
+					break;
+				}
+
+			}
+			break;
+
+		}
+
+		case LLWearableType::WT_EYES:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_EYES)
+			{
+				result = LLVOAvatarDefines::TEX_EYES_IRIS;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_GLOVES:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+			{
+				result = LLVOAvatarDefines::TEX_UPPER_GLOVES;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_JACKET:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+			{
+				result = LLVOAvatarDefines::TEX_LOWER_JACKET;
+			}
+			else if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+			{
+				result = LLVOAvatarDefines::TEX_UPPER_JACKET;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_PANTS:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+			{
+				result = LLVOAvatarDefines::TEX_LOWER_PANTS;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_SHIRT:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+			{
+				result = LLVOAvatarDefines::TEX_UPPER_SHIRT;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_SHOES:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+			{
+				result = LLVOAvatarDefines::TEX_LOWER_SHOES;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_SKIN:
+		{
+			switch(baked_texind)
+			{
+				case LLVOAvatarDefines::BAKED_HEAD:
+				{
+					result = LLVOAvatarDefines::TEX_HEAD_BODYPAINT;
+					break;
+				}
+
+				case LLVOAvatarDefines::BAKED_LOWER:
+				{
+					result = LLVOAvatarDefines::TEX_LOWER_BODYPAINT;
+					break;
+				}
+				case LLVOAvatarDefines::BAKED_UPPER:
+				{
+					result = LLVOAvatarDefines::TEX_UPPER_BODYPAINT;
+					break;
+				}
+
+				default:
+				{
+					break;
+				}
+
+			}
+			break;
+		}
+
+		case LLWearableType::WT_SKIRT:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_SKIRT)
+			{
+				result = LLVOAvatarDefines::TEX_SKIRT;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_SOCKS:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+			{
+				result = LLVOAvatarDefines::TEX_LOWER_SOCKS;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_TATTOO:
+		{
+			switch(baked_texind)
+			{
+				case LLVOAvatarDefines::BAKED_HEAD:
+				{
+					result = LLVOAvatarDefines::TEX_HEAD_TATTOO;
+					break;
+				}
+
+				case LLVOAvatarDefines::BAKED_LOWER:
+				{
+					result = LLVOAvatarDefines::TEX_LOWER_TATTOO;
+					break;
+				}
+				case LLVOAvatarDefines::BAKED_UPPER:
+				{
+					result = LLVOAvatarDefines::TEX_UPPER_TATTOO;
+					break;
+				}
+
+				default:
+				{
+					break;
+				}
+
+			}
+			break;
+		}
+
+		case LLWearableType::WT_UNDERPANTS:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_LOWER)
+			{
+				result = LLVOAvatarDefines::TEX_LOWER_UNDERPANTS;
+			}
+
+			break;
+		}
+
+		case LLWearableType::WT_UNDERSHIRT:
+		{
+			if (baked_texind == LLVOAvatarDefines::BAKED_UPPER)
+			{
+				result = LLVOAvatarDefines::TEX_UPPER_UNDERSHIRT;
+			}
+
+			break;
+		}
+
+		default:
+		{
+			llwarns << "Unknown wearable type: " << (int)type << "\n"
+				    << "Baked Texture Index: " << (int)baked_texind << "\n"
+					<< "Filename: " << mFilename << "\n"
+					<< "TrackingID: " << mTrackingID << "\n"
+					<< "InworldID: " << mWorldID << llendl;
+		}
+
+	}
+	return result;
+}
+
+/*=======================================*/
+/*  LLLocalBitmapTimer: timer class      */
+/*=======================================*/ 
+LLLocalBitmapTimer::LLLocalBitmapTimer() : LLEventTimer(LL_LOCAL_TIMER_HEARTBEAT)
+{
+}
+
+LLLocalBitmapTimer::~LLLocalBitmapTimer()
+{
+}
+
+void LLLocalBitmapTimer::startTimer()
+{
+	mEventTimer.start();
+}
+
+void LLLocalBitmapTimer::stopTimer()
+{
+	mEventTimer.stop();
+}
+
+bool LLLocalBitmapTimer::isRunning()
+{
+	return mEventTimer.getStarted();
+}
+
+BOOL LLLocalBitmapTimer::tick()
+{
+	LLLocalBitmapMgr::doUpdates();
+	return FALSE;
+}
+
+/*=======================================*/
+/*  LLLocalBitmapMgr: manager class      */
+/*=======================================*/ 
+LLLocalBitmapMgr::LLLocalBitmapMgr()
+{
+	// The class is all made of static members, should i even bother instantiating?
+}
+
+LLLocalBitmapMgr::~LLLocalBitmapMgr()
+{
+}
+
+bool LLLocalBitmapMgr::addUnit()
+{
+	bool add_successful = false;
+
+	LLFilePicker& picker = LLFilePicker::instance();
+	if (picker.getMultipleOpenFiles(LLFilePicker::FFLOAD_IMAGE))
+	{
+		sTimer.stopTimer();
+
+		std::string filename = picker.getFirstFile();
+		while(!filename.empty())
+		{
+			LLLocalBitmap* unit = new LLLocalBitmap(filename);
+
+			if (unit->getValid())
+			{
+				sBitmapList.push_back(unit);
+				add_successful = true;
+			}
+			else
+			{
+				llwarns << "Attempted to add invalid or unreadable image file, attempt cancelled.\n"
+					    << "Filename: " << filename << llendl;
+
+				LLSD notif_args;
+				notif_args["FNAME"] = filename;
+				LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args);
+
+				delete unit;
+				unit = NULL;
+			}
+
+			filename = picker.getNextFile();
+		}
+		
+		sTimer.startTimer();
+	}
+
+	return add_successful;
+}
+
+void LLLocalBitmapMgr::delUnit(LLUUID tracking_id)
+{
+	if (!sBitmapList.empty())
+	{	
+		std::vector<LLLocalBitmap*> to_delete;
+		for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
+		{   /* finding which ones we want deleted and making a separate list */
+			LLLocalBitmap* unit = *iter;
+			if (unit->getTrackingID() == tracking_id)
+			{
+				to_delete.push_back(unit);
+			}
+		}
+
+		for(std::vector<LLLocalBitmap*>::iterator del_iter = to_delete.begin();
+			del_iter != to_delete.end(); del_iter++)
+		{   /* iterating over a temporary list, hence preserving the iterator validity while deleting. */
+			LLLocalBitmap* unit = *del_iter;
+			sBitmapList.remove(unit);
+			delete unit;
+			unit = NULL;
+		}
+	}
+}
+
+LLUUID LLLocalBitmapMgr::getWorldID(LLUUID tracking_id)
+{
+	LLUUID world_id = LLUUID::null;
+
+	for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
+	{
+		LLLocalBitmap* unit = *iter;
+		if (unit->getTrackingID() == tracking_id)
+		{
+			world_id = unit->getWorldID();
+		}
+	}
+
+	return world_id;
+}
+
+std::string LLLocalBitmapMgr::getFilename(LLUUID tracking_id)
+{
+	std::string filename = "";
+
+	for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
+	{
+		LLLocalBitmap* unit = *iter;
+		if (unit->getTrackingID() == tracking_id)
+		{
+			filename = unit->getFilename();
+		}
+	}
+
+	return filename;
+}
+
+void LLLocalBitmapMgr::feedScrollList(LLScrollListCtrl* ctrl)
+{
+	if (ctrl)
+	{
+		ctrl->clearRows();
+
+		if (!sBitmapList.empty())
+		{
+			for (local_list_iter iter = sBitmapList.begin();
+				 iter != sBitmapList.end(); iter++)
+			{
+				LLSD element;
+				element["columns"][0]["column"] = "unit_name";
+				element["columns"][0]["type"]   = "text";
+				element["columns"][0]["value"]  = (*iter)->getShortName();
+
+				element["columns"][1]["column"] = "unit_id_HIDDEN";
+				element["columns"][1]["type"]   = "text";
+				element["columns"][1]["value"]  = (*iter)->getTrackingID();
+
+				ctrl->addElement(element);
+			}
+		}
+	}
+
+}
+
+void LLLocalBitmapMgr::doUpdates()
+{
+	// preventing theoretical overlap in cases with huge number of loaded images.
+	sTimer.stopTimer();
+	sNeedsRebake = false;
+
+	for (local_list_iter iter = sBitmapList.begin(); iter != sBitmapList.end(); iter++)
+	{
+		(*iter)->updateSelf();
+	}
+
+	doRebake();
+	sTimer.startTimer();
+}
+
+void LLLocalBitmapMgr::setNeedsRebake()
+{
+	sNeedsRebake = true;
+}
+
+void LLLocalBitmapMgr::doRebake()
+{ /* separated that from doUpdates to insure a rebake can be called separately during deletion */
+	if (sNeedsRebake)
+	{
+		gAgentAvatarp->forceBakeAllTextures(LL_LOCAL_SLAM_FOR_DEBUG);
+		sNeedsRebake = false;
+	}
+}
+
diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h
new file mode 100644
index 0000000000000000000000000000000000000000..7a23c7ef6e3d2a203b07b4137960f58e00fafd40
--- /dev/null
+++ b/indra/newview/lllocalbitmaps.h
@@ -0,0 +1,136 @@
+/** 
+ * @file lllocalbitmaps.h
+ * @author Vaalith Jinn
+ * @brief Local Bitmaps header
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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$
+ */
+
+#ifndef LL_LOCALBITMAPS_H
+#define LL_LOCALBITMAPS_H
+
+#include "lleventtimer.h"
+#include "llwearabletype.h"
+#include "llvoavatardefines.h"
+
+class LLScrollListCtrl;
+
+class LLLocalBitmap
+{
+	public: /* main */
+		LLLocalBitmap(std::string filename);
+		~LLLocalBitmap();
+
+	public: /* accessors */
+		std::string	getFilename();
+		std::string	getShortName();
+		LLUUID		getTrackingID();
+		LLUUID		getWorldID();
+		bool		getValid();
+
+	public: /* self update public section */
+		enum EUpdateType
+		{
+			UT_FIRSTUSE,
+			UT_REGUPDATE
+		};
+
+		bool updateSelf(EUpdateType = UT_REGUPDATE);
+
+	private: /* self update private section */
+		bool decodeBitmap(LLPointer<LLImageRaw> raw);
+		void replaceIDs(LLUUID old_id, LLUUID new_id);
+		std::vector<LLViewerObject*> prepUpdateObjects(LLUUID old_id);
+		void updateUserPrims(LLUUID old_id, LLUUID new_id);
+		void updateUserSculpts(LLUUID old_id, LLUUID new_id);
+		void updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableType::EType type);
+		LLVOAvatarDefines::ETextureIndex getTexIndex(LLWearableType::EType type, LLVOAvatarDefines::EBakedTextureIndex baked_texind);
+
+	private: /* private enums */
+		enum ELinkStatus
+		{
+			LS_ON,
+			LS_BROKEN,
+		};
+
+		enum EExtension
+		{
+			ET_IMG_BMP,
+			ET_IMG_TGA,
+			ET_IMG_JPG,
+			ET_IMG_PNG
+		};
+
+	private: /* members */
+		std::string mFilename;
+		std::string mShortName;
+		LLUUID      mTrackingID;
+		LLUUID      mWorldID;
+		bool        mValid;
+		LLSD        mLastModified;
+		EExtension  mExtension;
+		ELinkStatus mLinkStatus;
+		S32         mUpdateRetries;
+
+};
+
+class LLLocalBitmapTimer : public LLEventTimer
+{
+	public:
+		LLLocalBitmapTimer();
+		~LLLocalBitmapTimer();
+
+	public:
+		void startTimer();
+		void stopTimer();
+		bool isRunning();
+		BOOL tick();
+
+};
+
+class LLLocalBitmapMgr
+{
+	public:
+		LLLocalBitmapMgr();
+		~LLLocalBitmapMgr();
+
+	public:
+		static bool         addUnit();
+		static void         delUnit(LLUUID tracking_id);
+
+		static LLUUID       getWorldID(LLUUID tracking_id);
+		static std::string  getFilename(LLUUID tracking_id);
+		
+		static void         feedScrollList(LLScrollListCtrl* ctrl);
+		static void         doUpdates();
+		static void         setNeedsRebake();
+		static void         doRebake();
+		
+	private:
+		static std::list<LLLocalBitmap*>    sBitmapList;
+		static LLLocalBitmapTimer           sTimer;
+		static bool                         sNeedsRebake;
+		typedef std::list<LLLocalBitmap*>::iterator local_list_iter;
+};
+
+#endif
+
diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp
index 705046a5174b7874dbb32882c1bb7ca5ca92adb8..00ff81724cc12efd307a9715a102e1e1787b1326 100644
--- a/indra/newview/llnearbychatbar.cpp
+++ b/indra/newview/llnearbychatbar.cpp
@@ -34,6 +34,7 @@
 
 #include "llfirstuse.h"
 #include "llnearbychatbar.h"
+#include "llnearbychatbarlistener.h"
 #include "llagent.h"
 #include "llgesturemgr.h"
 #include "llmultigesture.h"
@@ -80,6 +81,7 @@ LLNearbyChatBar::LLNearbyChatBar(const LLSD& key)
 	mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT)
 {
 	mSpeakerMgr = LLLocalSpeakerMgr::getInstance();
+	mListener.reset(new LLNearbyChatBarListener(*this));
 }
 
 //virtual
diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h
index 0b1c3a4ca4db5e6145aa0ccd62f5e156ec9a5d04..662496d3383cc37ccfc05548cd954f84efef65a0 100644
--- a/indra/newview/llnearbychatbar.h
+++ b/indra/newview/llnearbychatbar.h
@@ -35,6 +35,8 @@
 #include "lloutputmonitorctrl.h"
 #include "llspeakers.h"
 
+class LLNearbyChatBarListener;
+
 class LLNearbyChatBar :	public LLFloater
 {
 	LOG_CLASS(LLNearbyChatBar);
@@ -95,6 +97,8 @@ protected:
 	LLLocalSpeakerMgr*		mSpeakerMgr;
 
 	S32 mExpandedHeight;
+
+	boost::shared_ptr<LLNearbyChatBarListener> mListener;
 };
 
 #endif
diff --git a/indra/newview/llnotificationscripthandler.cpp b/indra/newview/llnotificationscripthandler.cpp
index 995915206bced30d7eb5a2535c030dfb9f373d35..398f54c6f799d0f41047df69c00f8b7d1cd580e1 100644
--- a/indra/newview/llnotificationscripthandler.cpp
+++ b/indra/newview/llnotificationscripthandler.cpp
@@ -101,7 +101,7 @@ bool LLScriptHandler::processNotification(const LLSD& notify)
 		}
 		else
 		{
-			LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification);
+			LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification);
 
 			LLToast::Params p;
 			p.notif_id = notification->getID();
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 1dc4d796ab20d1af7f6953f6b5ffffdf24e2f41d..ef5ef2ddc82353bfc2e70d7d2ea8cfb60d574775 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -640,7 +640,7 @@ void LLOutfitsList::onOutfitsRemovalConfirmation(const LLSD& notification, const
 
 	if (mSelectedOutfitUUID.notNull())
 	{
-		remove_category(&gInventory, mSelectedOutfitUUID);
+		gInventory.removeCategory(mSelectedOutfitUUID);
 	}
 }
 
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp
index ca48e8561b11aa4b9dbd1fe769717992bb37a227..7a15d931811371ac3dc5bd7fcf22e69c1270416f 100644
--- a/indra/newview/llpanelgroupinvite.cpp
+++ b/indra/newview/llpanelgroupinvite.cpp
@@ -289,12 +289,12 @@ void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata)
 		//Soon the avatar picker will be embedded into this panel
 		//instead of being it's own separate floater.  But that is next week.
 		//This will do for now. -jwolk May 10, 2006
-		LLFloater* parentp;
-
-		parentp = gFloaterView->getParentFloater(panelp);
-		parentp->addDependentFloater(LLFloaterAvatarPicker::show(boost::bind(impl::callbackAddUsers, _1,
-																panelp->mImplementation),
-																 TRUE));
+		LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(
+			boost::bind(impl::callbackAddUsers, _1, panelp->mImplementation), TRUE);
+		if (picker)
+		{
+			gFloaterView->getParentFloater(panelp)->addDependentFloater(picker);
+		}
 	}
 }
 
diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp
index c7454e85a91d82929c11adffd9256ff2c645a32d..68a3b6d1cdd5957b8abb76ed6d0676ad1bb2e4b1 100644
--- a/indra/newview/llpanellandmarks.cpp
+++ b/indra/newview/llpanellandmarks.cpp
@@ -1149,7 +1149,7 @@ Rules:
 	- cut/rename/delete in any other accordions
 	- paste - only in Favorites, Landmarks accordions
  3. For Folders we can: perform any action in Landmarks accordion, except Received folder
- 4. We can not paste folders from Clipboard (processed by LLFolderView::canPaste())
+ 4. We can paste folders from Clipboard (processed by LLFolderView::canPaste())
  5. Check LLFolderView/Inventory Bridges rules
  */
 bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFolderViewItem* item) const
@@ -1206,8 +1206,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold
 
 		if ("cut" == command_name)
 		{
-			// "Cut" disabled for folders. See EXT-8697.
-			can_be_modified = root_folder->canCut() && listenerp->getInventoryType() != LLInventoryType::IT_CATEGORY;
+			can_be_modified = root_folder->canCut();
 		}
 		else if ("rename" == command_name)
 		{
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index c3c62920d338e8a7773a0f4244be5b1debb38337..c11597f532366fd793cafca0d26a6910cfcf5336 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -375,7 +375,7 @@ void LLPanelMainInventory::onClearSearch()
 	if (mActivePanel)
 	{
 		mActivePanel->setFilterSubString(LLStringUtil::null);
-		mActivePanel->setFilterTypes(0xffffffff);
+		mActivePanel->setFilterTypes(0xffffffffffffffffULL);
 		mActivePanel->setFilterLinks(LLInventoryFilter::FILTERLINK_INCLUDE_LINKS);
 	}
 
@@ -726,7 +726,7 @@ void LLFloaterInventoryFinder::updateElementsFromFilter()
 void LLFloaterInventoryFinder::draw()
 {
 	LLMemType mt(LLMemType::MTYPE_INVENTORY_DRAW);
-	U32 filter = 0xffffffff;
+	U64 filter = 0xffffffffffffffffULL;
 	BOOL filtered_by_all_types = TRUE;
 
 	if (!getChild<LLUICtrl>("check_animation")->getValue())
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index 98ea6805043ea1f69fb2f251e1553f0caed4a532..1ca24f30316339da42901fe3e6e0acddb20090cc 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -124,7 +124,7 @@ public:
 	virtual void move(LLFolderViewEventListener* parent_listener);
 	virtual BOOL isItemCopyable() const;
 	virtual BOOL copyToClipboard() const;
-	virtual void cutToClipboard();
+	virtual BOOL cutToClipboard() const;
 	virtual BOOL isClipboardPasteable() const;
 	virtual void pasteFromClipboard();
 	virtual void pasteLinkFromClipboard();
@@ -524,8 +524,9 @@ BOOL LLTaskInvFVBridge::copyToClipboard() const
 	return FALSE;
 }
 
-void LLTaskInvFVBridge::cutToClipboard()
+BOOL LLTaskInvFVBridge::cutToClipboard() const
 {
+	return FALSE;
 }
 
 BOOL LLTaskInvFVBridge::isClipboardPasteable() const
@@ -1568,7 +1569,7 @@ void LLPanelObjectInventory::reset()
 	scroll_p.rect(scroller_rect);
 	scroll_p.tab_stop(true);
 	scroll_p.follows.flags(FOLLOWS_ALL);
-	mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_p);
+	mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroll_p);
 	addChild(mScroller);
 	mScroller->addChild(mFolders);
 	
diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp
index 1f1cccad856bc8890381f08c8af2c771e626b8e7..c63d89fc9812706427b7b41282a38184ca31e8f7 100644
--- a/indra/newview/llpanelteleporthistory.cpp
+++ b/indra/newview/llpanelteleporthistory.cpp
@@ -358,7 +358,7 @@ void LLTeleportHistoryPanel::ContextMenu::onInfo()
 //static
 void LLTeleportHistoryPanel::ContextMenu::gotSLURLCallback(const std::string& slurl)
 {
-	gClipboard.copyFromString(utf8str_to_wstring(slurl));
+	LLClipboard::instance().copyToClipboard(utf8str_to_wstring(slurl),0,slurl.size());
 }
 
 void LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard()
diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp
index eb4c7572d43c0151119a4500926d437839154b42..280cc11179670ea84450245460433d7890a1cf7f 100644
--- a/indra/newview/llpaneltopinfobar.cpp
+++ b/indra/newview/llpaneltopinfobar.cpp
@@ -467,7 +467,7 @@ void LLPanelTopInfoBar::onContextMenuItemClicked(const LLSD::String& item)
 		LLAgentUI::buildSLURL(slurl, false);
 		LLUIString location_str(slurl.getSLURLString());
 
-		gClipboard.copyFromString(location_str);
+		LLClipboard::instance().copyToClipboard(location_str,0,location_str.length());
 	}
 }
 
diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp
index e2801c09bde095d5a28b6f26b8c1b9b87cf6518c..3b9934d4be36b08b941ac55b711a455af598fc3d 100644
--- a/indra/newview/llpanelwearing.cpp
+++ b/indra/newview/llpanelwearing.cpp
@@ -302,6 +302,6 @@ void LLPanelWearing::copyToClipboard()
 		}
 	}
 
-	gClipboard.copyFromString(utf8str_to_wstring(text));
+	LLClipboard::instance().copyToClipboard(utf8str_to_wstring(text),0,text.size());
 }
 // EOF
diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp
index 4974dde282c24044446503e184dbe3fc36ccd76b..3a9360fd23dbab67252ce95890ef83fb87fe83da 100644
--- a/indra/newview/llpreviewnotecard.cpp
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -29,7 +29,6 @@
 #include "llpreviewnotecard.h"
 
 #include "llinventory.h"
-#include "llinventoryfunctions.h" // for change_item_parent()
 
 #include "llagent.h"
 #include "llassetuploadresponders.h"
@@ -494,7 +493,7 @@ void LLPreviewNotecard::deleteNotecard()
 	if (item != NULL)
 	{
 		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
-		change_item_parent(&gInventory, item, trash_id, FALSE);
+		gInventory.changeItemParent(item, trash_id, FALSE);
 	}
 
 	closeFloater();
diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp
index c8b67cc9ecfe978609157b57bc9f306c4a28e300..4f9ab318a56807443cc0fdee6c3bad8f0b25d207 100644
--- a/indra/newview/llsidepanelinventory.cpp
+++ b/indra/newview/llsidepanelinventory.cpp
@@ -78,7 +78,6 @@ static const char * const MARKETPLACE_INBOX_PANEL = "marketplace_inbox";
 //
 // Helpers
 //
-
 class LLInboxAddedObserver : public LLInventoryCategoryAddedObserver
 {
 public:
@@ -130,6 +129,11 @@ LLSidepanelInventory::LLSidepanelInventory()
 
 LLSidepanelInventory::~LLSidepanelInventory()
 {
+	LLLayoutPanel* inbox_layout_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);
+
+	// Save the InventoryMainPanelHeight in settings per account
+	gSavedPerAccountSettings.setS32("InventoryInboxHeight", inbox_layout_panel->getTargetDim());
+
 	if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver))
 	{
 		gInventory.removeObserver(mCategoriesObserver);
@@ -226,7 +230,12 @@ BOOL LLSidepanelInventory::postBuild()
 		bool is_inbox_collapsed = !inbox_button->getToggleState();
 
 		// Restore the collapsed inbox panel state
-		inv_stack->collapsePanel(getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME), is_inbox_collapsed);
+		LLLayoutPanel* inbox_panel = getChild<LLLayoutPanel>(INBOX_LAYOUT_PANEL_NAME);
+		inv_stack->collapsePanel(inbox_panel, is_inbox_collapsed);
+		if (!is_inbox_collapsed)
+		{
+			inbox_panel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight"));
+		}
 
 		// Set the inbox visible based on debug settings (final setting comes from http request below)
 		enableInbox(gSavedSettings.getBOOL("InventoryDisplayInbox"));
@@ -370,10 +379,19 @@ void LLSidepanelInventory::onToggleInboxBtn()
 	// Expand/collapse the indicated panel
 	inv_stack->collapsePanel(inboxPanel, !inbox_expanded);
 
-	if (inbox_expanded && inboxPanel->isInVisibleChain())
+	if (inbox_expanded)
 	{
-		gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected());
+		inboxPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight"));
+		if (inboxPanel->isInVisibleChain())
+		{
+			gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected());
+		}
 	}
+	else
+	{
+		gSavedPerAccountSettings.setS32("InventoryInboxHeight", inboxPanel->getTargetDim());
+	}
+
 }
 
 void LLSidepanelInventory::onOpen(const LLSD& key)
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 38c7dd8283d59284efad6ffe01562d66e8ae0ab0..8e62b79d7f21c8c4c9c11f7c402b1290e4e748b0 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -3648,6 +3648,110 @@ void renderShadowFrusta(LLDrawInfo* params)
 	gGL.setSceneBlendType(LLRender::BT_ALPHA);
 }
 
+void renderTexelDensity(LLDrawable* drawable)
+{
+	if (LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_OFF
+		|| LLViewerTexture::sCheckerBoardImagep.isNull())
+	{
+		return;
+	}
+
+	LLGLEnable _(GL_BLEND);
+	//gObjectFullbrightProgram.bind();
+
+	LLMatrix4 checkerboard_matrix;
+	S32 discard_level = -1;
+
+	for (S32 f = 0; f < drawable->getNumFaces(); f++)
+	{
+		LLFace* facep = drawable->getFace(f);
+		LLVertexBuffer* buffer = facep->getVertexBuffer();
+		LLViewerTexture* texturep = facep->getTexture();
+
+		if (texturep == NULL) continue;
+
+		switch(LLViewerTexture::sDebugTexelsMode)
+		{
+		case LLViewerTexture::DEBUG_TEXELS_CURRENT:
+			discard_level = -1;
+			break;
+		case LLViewerTexture::DEBUG_TEXELS_DESIRED:
+			{
+				LLViewerFetchedTexture* fetched_texturep = dynamic_cast<LLViewerFetchedTexture*>(texturep);
+				discard_level = fetched_texturep ? fetched_texturep->getDesiredDiscardLevel() : -1;
+				break;
+			}
+		default:
+		case LLViewerTexture::DEBUG_TEXELS_FULL:
+			discard_level = 0;
+			break;
+		}
+
+		checkerboard_matrix.initScale(LLVector3(texturep->getWidth(discard_level) / 8, texturep->getHeight(discard_level) / 8, 1.f));
+
+		gGL.getTexUnit(0)->bind(LLViewerTexture::sCheckerBoardImagep, TRUE);
+		gGL.matrixMode(LLRender::MM_TEXTURE);
+		gGL.loadMatrix((GLfloat*)&checkerboard_matrix.mMatrix);
+
+		if (buffer && (facep->getGeomCount() >= 3))
+		{
+			buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0);
+			U16 start = facep->getGeomStart();
+			U16 end = start + facep->getGeomCount()-1;
+			U32 count = facep->getIndicesCount();
+			U16 offset = facep->getIndicesStart();
+			buffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+		}
+
+		gGL.loadIdentity();
+		gGL.matrixMode(LLRender::MM_MODELVIEW);
+	}
+
+	//S32 num_textures = llmax(1, (S32)params->mTextureList.size());
+
+	//for (S32 i = 0; i < num_textures; i++)
+	//{
+	//	LLViewerTexture* texturep = params->mTextureList.empty() ? params->mTexture.get() : params->mTextureList[i].get();
+	//	if (texturep == NULL) continue;
+
+	//	LLMatrix4 checkboard_matrix;
+	//	S32 discard_level = -1;
+	//	switch(LLViewerTexture::sDebugTexelsMode)
+	//	{
+	//	case LLViewerTexture::DEBUG_TEXELS_CURRENT:
+	//		discard_level = -1;
+	//		break;
+	//	case LLViewerTexture::DEBUG_TEXELS_DESIRED:
+	//		{
+	//			LLViewerFetchedTexture* fetched_texturep = dynamic_cast<LLViewerFetchedTexture*>(texturep);
+	//			discard_level = fetched_texturep ? fetched_texturep->getDesiredDiscardLevel() : -1;
+	//			break;
+	//		}
+	//	default:
+	//	case LLViewerTexture::DEBUG_TEXELS_FULL:
+	//		discard_level = 0;
+	//		break;
+	//	}
+
+	//	checkboard_matrix.initScale(LLVector3(texturep->getWidth(discard_level) / 8, texturep->getHeight(discard_level) / 8, 1.f));
+	//	gGL.getTexUnit(i)->activate();
+
+	//	glMatrixMode(GL_TEXTURE);
+	//	glPushMatrix();
+	//	glLoadIdentity();
+	//	//gGL.matrixMode(LLRender::MM_TEXTURE);
+	//	glLoadMatrixf((GLfloat*) checkboard_matrix.mMatrix);
+
+	//	gGL.getTexUnit(i)->bind(LLViewerTexture::sCheckerBoardImagep, TRUE);
+
+	//	pushVerts(params, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR | LLVertexBuffer::MAP_NORMAL );
+
+	//	glPopMatrix();
+	//	glMatrixMode(GL_MODELVIEW);
+	//	//gGL.matrixMode(LLRender::MM_MODELVIEW);
+	//}
+}
+
 
 void renderLights(LLDrawable* drawablep)
 {
@@ -4043,6 +4147,10 @@ public:
 			{
 				renderComplexityDisplay(drawable);
 			}
+			if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY))
+			{
+				renderTexelDensity(drawable);
+			}
 
 			LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
 			
@@ -4292,7 +4400,8 @@ void LLSpatialPartition::renderDebug()
 									  LLPipeline::RENDER_DEBUG_AGENT_TARGET |
 									  //LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
 									  LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA |
-									  LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY)) 
+									  LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY |
+									  LLPipeline::RENDER_DEBUG_TEXEL_DENSITY)) 
 	{
 		return;
 	}
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 0ac8c1fe397d8b2fb0c6fd444a5b9e21cae561af..6b0fc26db7a62d2e9a660591b19105462e60ca4a 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -240,6 +240,7 @@ static bool mLoginStatePastUI = false;
 
 boost::scoped_ptr<LLEventPump> LLStartUp::sStateWatcher(new LLEventStream("StartupState"));
 boost::scoped_ptr<LLStartupListener> LLStartUp::sListener(new LLStartupListener());
+boost::scoped_ptr<LLViewerStats::PhaseMap> LLStartUp::sPhases(new LLViewerStats::PhaseMap);
 
 //
 // local function declaration
@@ -2705,7 +2706,10 @@ void LLStartUp::setStartupState( EStartupState state )
 	LL_INFOS("AppInit") << "Startup state changing from " <<  
 		getStartupStateString() << " to " <<  
 		startupStateToString(state) << LL_ENDL;
+
+	sPhases->stopPhase(getStartupStateString());
 	gStartupState = state;
+	sPhases->startPhase(getStartupStateString());
 	postStartupState();
 }
 
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
index 0a18ef1b2d140389e32f8db302669e9f89ca9782..3754aaf966e5fc8503d9fe6e8667e6ba5fa65bf9 100644
--- a/indra/newview/llstartup.h
+++ b/indra/newview/llstartup.h
@@ -34,6 +34,8 @@ class LLEventPump;
 class LLStartupListener;
 class LLSLURL;
 
+#include "llviewerstats.h"
+
 // functions
 bool idle_startup();
 void release_start_screen();
@@ -113,6 +115,7 @@ public:
 
 	static bool startLLProxy(); // Initialize the SOCKS 5 proxy
 
+	static LLViewerStats::PhaseMap& getPhases() { return *sPhases; }
 private:
 	static LLSLURL sStartSLURL;
 
@@ -120,6 +123,7 @@ private:
 	static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState
 	static boost::scoped_ptr<LLEventPump> sStateWatcher;
 	static boost::scoped_ptr<LLStartupListener> sListener;
+	static boost::scoped_ptr<LLViewerStats::PhaseMap> sPhases;
 };
 
 
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 6f6d5dbf122ea787f6d24f4065209104015ada5d..467115c92849b6ad9bc2c8e7d637c752ba9d6b8e 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -55,6 +55,9 @@ using namespace LLVOAvatarDefines;
 static const S32 BAKE_UPLOAD_ATTEMPTS = 7;
 static const F32 BAKE_UPLOAD_RETRY_DELAY = 2.f; // actual delay grows by power of 2 each attempt
 
+// runway consolidate
+extern std::string self_av_string();
+
 class LLTexLayerInfo
 {
 	friend class LLTexLayer;
@@ -494,7 +497,6 @@ void LLTexLayerSetBuffer::doUpload()
 	}
 	
 	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C;
-	compressedImage->setRate(0.f);
 	const char* comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // writes into baked_color_data. 5 channels (rgb, heightfield/alpha, mask)
 	if (compressedImage->encode(baked_image, comment_text))
 	{
@@ -577,7 +579,7 @@ void LLTexLayerSetBuffer::doUpload()
 					args["BODYREGION"] = mTexLayerSet->getBodyRegionName();
 					args["RESOLUTION"] = lod_str;
 					LLNotificationsUtil::add("AvatarRezSelfBakedTextureUploadNotification",args);
-					llinfos << "Uploading [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << llendl;
+					LL_DEBUGS("Avatar") << self_av_string() << "Uploading [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << LL_ENDL;
 				}
 			}
 			else
@@ -631,7 +633,7 @@ void LLTexLayerSetBuffer::doUpdate()
 		args["BODYREGION"] = mTexLayerSet->getBodyRegionName();
 		args["RESOLUTION"] = lod_str;
 		LLNotificationsUtil::add("AvatarRezSelfBakedTextureUpdateNotification",args);
-		llinfos << "Locally updating [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUpdateTimer.getElapsedTimeF32() << " ]" << llendl;
+		LL_DEBUGS("Avatar") << self_av_string() << "Locally updating [ name: " << mTexLayerSet->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUpdateTimer.getElapsedTimeF32() << " ]" << LL_ENDL;
 	}
 }
 
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index 19a944e88e9a9510e324cc3430e2ae50ff5d846d..ed9faa070601ea72801cee172df5fecdd9bd58fb 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -67,6 +67,9 @@
 #include "lluictrlfactory.h"
 #include "lltrans.h"
 
+#include "llradiogroup.h"
+#include "llfloaterreg.h"
+#include "lllocalbitmaps.h"
 
 static const S32 HPAD = 4;
 static const S32 VPAD = 4;
@@ -78,6 +81,8 @@ static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
 static const F32 CONTEXT_CONE_OUT_ALPHA = 1.f;
 static const F32 CONTEXT_FADE_TIME = 0.08f;
 
+static const S32 LOCAL_TRACKING_ID_COLUMN = 1;
+
 //static const char CURRENT_IMAGE_NAME[] = "Current Texture";
 //static const char WHITE_IMAGE_NAME[] = "Blank Texture";
 //static const char NO_IMAGE_NAME[] = "None";
@@ -142,6 +147,12 @@ public:
 	static void		onApplyImmediateCheck(LLUICtrl* ctrl, void* userdata);
 		   void		onTextureSelect( const LLTextureEntry& te );
 
+	static void		onModeSelect(LLUICtrl* ctrl, void *userdata);
+	static void		onBtnAdd(void* userdata);
+	static void		onBtnRemove(void* userdata);
+	static void		onBtnUpload(void* userdata);
+	static void		onLocalScrollCommit(LLUICtrl* ctrl, void* userdata);
+
 protected:
 	LLPointer<LLViewerTexture> mTexturep;
 	LLTextureCtrl*		mOwner;
@@ -169,8 +180,10 @@ protected:
 	BOOL				mNoCopyTextureSelected;
 	F32					mContextConeOpacity;
 	LLSaveFolderState	mSavedFolderState;
-
 	BOOL				mSelectedItemPinned;
+	
+	LLRadioGroup*		mModeSelector;
+	LLScrollListCtrl*	mLocalScrollCtrl;
 };
 
 LLFloaterTexturePicker::LLFloaterTexturePicker(	
@@ -437,6 +450,17 @@ BOOL LLFloaterTexturePicker::postBuild()
 		mInventoryPanel->setSelection(findItemID(mImageAssetID, FALSE), TAKE_FOCUS_NO);
 	}
 
+	mModeSelector = getChild<LLRadioGroup>("mode_selection");
+	mModeSelector->setCommitCallback(onModeSelect, this);
+	mModeSelector->setSelectedIndex(0, 0);
+
+	childSetAction("l_add_btn", LLFloaterTexturePicker::onBtnAdd, this);
+	childSetAction("l_rem_btn", LLFloaterTexturePicker::onBtnRemove, this);
+	childSetAction("l_upl_btn", LLFloaterTexturePicker::onBtnUpload, this);
+
+	mLocalScrollCtrl = getChild<LLScrollListCtrl>("l_name_list");
+	mLocalScrollCtrl->setCommitCallback(onLocalScrollCommit, this);
+	LLLocalBitmapMgr::feedScrollList(mLocalScrollCtrl);
 
 	mNoCopyTextureSelected = FALSE;
 
@@ -464,7 +488,6 @@ BOOL LLFloaterTexturePicker::postBuild()
 // virtual
 void LLFloaterTexturePicker::draw()
 {
-	S32 floater_header_size = getHeaderHeight();
 	if (mOwner)
 	{
 		// draw cone of context pointing back to texture swatch	
@@ -554,10 +577,7 @@ void LLFloaterTexturePicker::draw()
 		}
 
 		// Border
-		LLRect border( BORDER_PAD, 
-					   getRect().getHeight() - floater_header_size - BORDER_PAD, 
-					   ((getMinWidth() / 2) - TEXTURE_INVENTORY_PADDING - HPAD) - BORDER_PAD,
-					   BORDER_PAD + FOOTER_HEIGHT + (getRect().getHeight() - getMinHeight()));
+		LLRect border = getChildView("preview_widget")->getRect();
 		gl_rect_2d( border, LLColor4::black, FALSE );
 
 
@@ -748,7 +768,15 @@ void LLFloaterTexturePicker::onBtnSelect(void* userdata)
 	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
 	if (self->mOwner)
 	{
-		self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT);
+		LLUUID local_id = LLUUID::null;
+
+		if (self->mLocalScrollCtrl->getVisible() && !self->mLocalScrollCtrl->getAllSelected().empty())
+		{
+			LLUUID temp_id = self->mLocalScrollCtrl->getFirstSelected()->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
+			local_id = LLLocalBitmapMgr::getWorldID(temp_id);
+		}
+
+		self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT, local_id);
 	}
 	self->closeFloater();
 }
@@ -791,6 +819,112 @@ void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem
 	}
 }
 
+// static
+void LLFloaterTexturePicker::onModeSelect(LLUICtrl* ctrl, void *userdata)
+{
+	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+	bool mode = (self->mModeSelector->getSelectedIndex() == 0);
+
+	self->getChild<LLButton>("Default")->setVisible(mode);
+	self->getChild<LLButton>("Blank")->setVisible(mode);
+	self->getChild<LLButton>("None")->setVisible(mode);
+	self->getChild<LLButton>("Pipette")->setVisible(mode);
+	self->getChild<LLFilterEditor>("inventory search editor")->setVisible(mode);
+	self->getChild<LLInventoryPanel>("inventory panel")->setVisible(mode);
+
+	/*self->getChild<LLCheckBox>("show_folders_check")->setVisible(mode);
+	  no idea under which conditions the above is even shown, needs testing. */
+
+	self->getChild<LLButton>("l_add_btn")->setVisible(!mode);
+	self->getChild<LLButton>("l_rem_btn")->setVisible(!mode);
+	self->getChild<LLButton>("l_upl_btn")->setVisible(!mode);
+	self->getChild<LLScrollListCtrl>("l_name_list")->setVisible(!mode);
+}
+
+// static
+void LLFloaterTexturePicker::onBtnAdd(void* userdata)
+{
+	if (LLLocalBitmapMgr::addUnit() == true)
+	{
+		LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+		LLLocalBitmapMgr::feedScrollList(self->mLocalScrollCtrl);
+	}
+}
+
+// static
+void LLFloaterTexturePicker::onBtnRemove(void* userdata)
+{
+	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+	std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
+
+	if (!selected_items.empty())
+	{
+		for(std::vector<LLScrollListItem*>::iterator iter = selected_items.begin();
+			iter != selected_items.end(); iter++)
+		{
+			LLScrollListItem* list_item = *iter;
+			if (list_item)
+			{
+				LLUUID tracking_id = list_item->getColumn(LOCAL_TRACKING_ID_COLUMN)->getValue().asUUID();
+				LLLocalBitmapMgr::delUnit(tracking_id);
+			}
+		}
+
+		self->getChild<LLButton>("l_rem_btn")->setEnabled(false);
+		self->getChild<LLButton>("l_upl_btn")->setEnabled(false);
+		LLLocalBitmapMgr::feedScrollList(self->mLocalScrollCtrl);
+	}
+
+}
+
+// static
+void LLFloaterTexturePicker::onBtnUpload(void* userdata)
+{
+	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+	std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
+
+	if (selected_items.empty())
+	{
+		return;
+	}
+
+	/* currently only allows uploading one by one, picks the first item from the selection list.  (not the vector!)
+	   in the future, it might be a good idea to check the vector size and if more than one units is selected - opt for multi-image upload. */
+	
+	LLUUID tracking_id = (LLUUID)self->mLocalScrollCtrl->getSelectedItemLabel(LOCAL_TRACKING_ID_COLUMN);
+	std::string filename = LLLocalBitmapMgr::getFilename(tracking_id);
+
+	if (!filename.empty())
+	{
+		LLFloaterReg::showInstance("upload_image", LLSD(filename));
+	}
+
+}
+
+//static
+void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata)
+{
+	LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+	std::vector<LLScrollListItem*> selected_items = self->mLocalScrollCtrl->getAllSelected();
+	bool has_selection = !selected_items.empty();
+
+	self->getChild<LLButton>("l_rem_btn")->setEnabled(has_selection);
+	self->getChild<LLButton>("l_upl_btn")->setEnabled(has_selection && (selected_items.size() < 2));
+	/* since multiple-localbitmap upload is not implemented, upl button gets disabled if more than one is selected. */
+
+	if (has_selection)
+	{
+		LLUUID tracking_id = (LLUUID)self->mLocalScrollCtrl->getSelectedItemLabel(LOCAL_TRACKING_ID_COLUMN); 
+		LLUUID inworld_id = LLLocalBitmapMgr::getWorldID(tracking_id);
+		self->mOwner->setImageAssetID(inworld_id);
+
+		if (self->childGetValue("apply_immediate_check").asBoolean())
+		{
+			self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE, inworld_id);
+		}
+	}
+}
+
 // static
 void LLFloaterTexturePicker::onShowFolders(LLUICtrl* ctrl, void *user_data)
 {
@@ -1133,7 +1267,7 @@ void LLTextureCtrl::onFloaterClose()
 	mFloaterHandle.markDead();
 }
 
-void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
+void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id)
 {
 	LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get();
 
@@ -1146,14 +1280,24 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
 		// (i.e. op == TEXTURE_SELECT) or texture changes via DnD.
 		else if (mCommitOnSelection || op == TEXTURE_SELECT)
 			mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here?
-			
-		if( floaterp->isDirty() )
+
+		if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work.
 		{
 			setTentative( FALSE );
-			mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
-			lldebugs << "mImageItemID: " << mImageItemID << llendl;
-			mImageAssetID = floaterp->getAssetID();
-			lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
+
+			if (id.notNull())
+			{
+				mImageItemID = id;
+				mImageAssetID = id;
+			}
+			else
+			{
+				mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
+				lldebugs << "mImageItemID: " << mImageItemID << llendl;
+				mImageAssetID = floaterp->getAssetID();
+				lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
+			}
+
 			if (op == TEXTURE_SELECT && mOnSelectCallback)
 			{
 				mOnSelectCallback( this, LLSD() );
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
index b1312d641f64fe848d7e136be829db43576f43b0..3abe84dcc31a630ad2ea3c1903e37d9eb9d71c9c 100644
--- a/indra/newview/lltexturectrl.h
+++ b/indra/newview/lltexturectrl.h
@@ -157,7 +157,7 @@ public:
 	void			closeDependentFloater();
 
 	void			onFloaterClose();
-	void			onFloaterCommit(ETexturePickOp op);
+	void			onFloaterCommit(ETexturePickOp op, LLUUID id = LLUUID::null);
 
 	// This call is returned when a drag is detected. Your callback
 	// should return TRUE if the drag is acceptable.
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index f18aa8b4e61b05cefaf821af67165f368855ec08..e2af497a7d05470de4b219fdfbb7d494a82f07a8 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -52,12 +52,20 @@
 #include "llviewerstats.h"
 #include "llviewerassetstats.h"
 #include "llworld.h"
+#include "llsdutil.h"
+#include "llstartup.h"
+#include "llviewerstats.h"
+
+bool LLTextureFetchDebugger::sDebuggerEnabled = false ;
+LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128);
+LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128);
 
 //////////////////////////////////////////////////////////////////////////////
 class LLTextureFetchWorker : public LLWorkerClass
 {
 	friend class LLTextureFetch;
 	friend class HTTPGetResponder;
+	friend class LLTextureFetchDebugger;
 	
 private:
 	class CacheReadResponder : public LLTextureCache::ReadResponder
@@ -242,6 +250,8 @@ private:
 	S32 mDecodedDiscard;
 	LLFrameTimer mRequestedTimer;
 	LLFrameTimer mFetchTimer;
+	LLTimer			mCacheReadTimer;
+	F32				mCacheReadTime;
 	LLTextureCache::handle_t mCacheReadHandle;
 	LLTextureCache::handle_t mCacheWriteHandle;
 	U8* mBuffer;
@@ -258,6 +268,7 @@ private:
 	BOOL mNeedsAux;
 	BOOL mHaveAllData;
 	BOOL mInLocalCache;
+	BOOL mInCache;
 	bool mCanUseHTTP ;
 	bool mCanUseNET ; //can get from asset server.
 	S32 mHTTPFailCount;
@@ -653,6 +664,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
 	  mRequestedDiscard(-1),
 	  mLoadedDiscard(-1),
 	  mDecodedDiscard(-1),
+	  mCacheReadTime(0.f),
 	  mCacheReadHandle(LLTextureCache::nullHandle()),
 	  mCacheWriteHandle(LLTextureCache::nullHandle()),
 	  mBuffer(NULL),
@@ -669,6 +681,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
 	  mNeedsAux(FALSE),
 	  mHaveAllData(FALSE),
 	  mInLocalCache(FALSE),
+	  mInCache(FALSE),
 	  mCanUseHTTP(true),
 	  mHTTPFailCount(0),
 	  mRetryAttempt(0),
@@ -838,6 +851,8 @@ void LLTextureFetchWorker::startWork(S32 param)
 // Called from LLWorkerThread::processRequest()
 bool LLTextureFetchWorker::doWork(S32 param)
 {
+	static const F32 FETCHING_TIMEOUT = 120.f;//seconds
+
 	LLMutexLock lock(&mWorkMutex);
 
 	if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
@@ -896,6 +911,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 		mCacheReadHandle = LLTextureCache::nullHandle();
 		mCacheWriteHandle = LLTextureCache::nullHandle();
 		mState = LOAD_FROM_TEXTURE_CACHE;
+		mInCache = FALSE;
 		mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
 		LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
 							 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
@@ -926,6 +942,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
 				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
 																		  offset, size, responder);
+				mCacheReadTimer.reset();
 			}
 			else if (mUrl.empty())
 			{
@@ -934,6 +951,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
 				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
 																		  offset, size, responder);
+				mCacheReadTimer.reset();
 			}
 			else if(mCanUseHTTP)
 			{
@@ -982,11 +1000,12 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			llassert_always(mFormattedImage->getDataSize() > 0);
 			mLoadedDiscard = mDesiredDiscard;
 			mState = DECODE_IMAGE;
+			mInCache = TRUE;
 			mWriteToCacheState = NOT_WRITE ;
 			LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
 								 << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
 								 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
-			// fall through
+			LLTextureFetch::sCacheHitRate.addValue(100.f);
 		}
 		else
 		{
@@ -1002,6 +1021,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				mState = LOAD_FROM_NETWORK;
 			}
 			// fall through
+			LLTextureFetch::sCacheHitRate.addValue(0.f);
 		}
 	}
 
@@ -1177,6 +1197,8 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			bool res = false;
 			if (!mUrl.empty())
 			{
+				mRequestedTimer.reset();
+
 				mLoaded = FALSE;
 				mGetStatus = 0;
 				mGetReason.clear();
@@ -1335,6 +1357,13 @@ bool LLTextureFetchWorker::doWork(S32 param)
 		}
 		else
 		{
+			if(FETCHING_TIMEOUT < mRequestedTimer.getElapsedTimeF32())
+			{
+				//timeout, abort.
+				mState = DONE;
+				return true;
+			}
+
 			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 			return false;
 		}
@@ -1396,6 +1425,11 @@ bool LLTextureFetchWorker::doWork(S32 param)
 	{
 		if (mDecoded)
 		{
+			if(mFetcher->getFetchDebugger() && !mInLocalCache)
+			{
+				mFetcher->getFetchDebugger()->addHistoryEntry(this);
+			}
+
 			if (mDecodedDiscard < 0)
 			{
 				LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
@@ -1780,6 +1814,7 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag
 	mDecoded = TRUE;
 // 	llinfos << mID << " : DECODE COMPLETE " << llendl;
 	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+	mCacheReadTime = mCacheReadTimer.getElapsedTimeF32();
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1824,11 +1859,18 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	  mHTTPTextureBits(0),
 	  mTotalHTTPRequests(0),
 	  mCurlGetRequest(NULL),
-	  mQAMode(qa_mode)
+	  mQAMode(qa_mode),
+	  mFetchDebugger(NULL)
 {
 	mCurlPOSTRequestCount = 0;
 	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
 	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
+
+	LLTextureFetchDebugger::sDebuggerEnabled = gSavedSettings.getBOOL("TextureFetchDebuggerEnabled");
+	if(LLTextureFetchDebugger::isEnabled())
+	{
+		mFetchDebugger = new LLTextureFetchDebugger(this, cache, imagedecodethread) ;
+	}
 }
 
 LLTextureFetch::~LLTextureFetch()
@@ -1843,11 +1885,17 @@ LLTextureFetch::~LLTextureFetch()
 	}
 	
 	// ~LLQueuedThread() called here
+
+	delete mFetchDebugger;
 }
 
 bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
 								   S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http)
 {
+	if(mFetcherLocked)
+	{
+		return false;
+	}
 	if (mDebugPause)
 	{
 		return false;
@@ -2092,6 +2140,11 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
 			discard_level = worker->mDecodedDiscard;
 			raw = worker->mRawImage;
 			aux = worker->mAuxImage;
+			F32 cache_read_time = worker->mCacheReadTime;
+			if (cache_read_time != 0.f)
+			{
+				sCacheReadLatency.addValue(cache_read_time * 1000.f);
+			}
 			res = true;
 			LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
 			worker->unlockWorkMutex();
@@ -2222,7 +2275,13 @@ S32 LLTextureFetch::update(F32 max_time_ms)
 	
 	if (!mDebugPause)
 	{
-		sendRequestListToSimulators();
+		// this is the startup state when send_complete_agent_movement() message is sent.
+		// Before this, the RequestImages message sent by sendRequestListToSimulators 
+		// won't work so don't bother trying
+		if (LLStartUp::getStartupState() > STATE_AGENT_SEND)
+		{
+			sendRequestListToSimulators();
+		}
 	}
 
 	if (!mThreaded)
@@ -2258,6 +2317,11 @@ void LLTextureFetch::startThread()
 {
 	// Construct mCurlGetRequest from Worker Thread
 	mCurlGetRequest = new LLCurlRequest();
+	
+	if(mFetchDebugger)
+	{
+		mFetchDebugger->setCurlGetRequest(mCurlGetRequest);
+	}
 }
 
 // WORKER THREAD
@@ -2266,6 +2330,10 @@ void LLTextureFetch::endThread()
 	// Destroy mCurlGetRequest from Worker Thread
 	delete mCurlGetRequest;
 	mCurlGetRequest = NULL;
+	if(mFetchDebugger)
+	{
+		mFetchDebugger->setCurlGetRequest(NULL);
+	}
 }
 
 // WORKER THREAD
@@ -2803,7 +2871,6 @@ void LLTextureFetch::cmdDoWork()
 	}
 }
 
-
 //////////////////////////////////////////////////////////////////////////////
 
 // Private (anonymous) class methods implementing the command scheme.
@@ -2959,7 +3026,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
 	// In QA mode, Metrics submode, log the result for ease of testing
 	if (fetcher->isQAMode())
 	{
-		LL_INFOS("Textures") << merged_llsd << LL_ENDL;
+		LL_INFOS("Textures") << ll_pretty_print_sd(merged_llsd) << LL_ENDL;
 	}
 
 	gViewerAssetStatsThread1->reset();
@@ -3007,5 +3074,659 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics)
 
 } // end of anonymous namespace
 
+///////////////////////////////////////////////////////////////////////////////////////////
+//Start LLTextureFetchDebugger
+///////////////////////////////////////////////////////////////////////////////////////////
+//---------------------
+class LLDebuggerCacheReadResponder : public LLTextureCache::ReadResponder
+{
+public:
+	LLDebuggerCacheReadResponder(LLTextureFetchDebugger* debugger, S32 id, LLImageFormatted* image)
+		: mDebugger(debugger), mID(id)
+	{
+		setImage(image);
+	}
+	virtual void completed(bool success)
+	{
+		mDebugger->callbackCacheRead(mID, success, mFormattedImage, mImageSize, mImageLocal);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerCacheWriteResponder : public LLTextureCache::WriteResponder
+{
+public:
+	LLDebuggerCacheWriteResponder(LLTextureFetchDebugger* debugger, S32 id)
+		: mDebugger(debugger), mID(id)
+	{
+	}
+	virtual void completed(bool success)
+	{
+		mDebugger->callbackCacheWrite(mID, success);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerDecodeResponder : public LLImageDecodeThread::Responder
+{
+public:
+	LLDebuggerDecodeResponder(LLTextureFetchDebugger* debugger, S32 id)
+		: mDebugger(debugger), mID(id)
+	{
+	}
+	virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
+	{
+		mDebugger->callbackDecoded(mID, success, raw, aux);
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mID;
+};
+
+class LLDebuggerHTTPResponder : public LLCurl::Responder
+{
+public:
+	LLDebuggerHTTPResponder(LLTextureFetchDebugger* debugger, S32 index)
+	: mDebugger(debugger), mIndex(index)
+	{
+	}
+	virtual void completedRaw(U32 status, const std::string& reason,
+							  const LLChannelDescriptors& channels,
+							  const LLIOPipe::buffer_ptr_t& buffer)
+	{
+		bool success = false;
+		bool partial = false;
+		if (HTTP_OK <= status &&  status < HTTP_MULTIPLE_CHOICES)
+		{
+			success = true;
+			if (HTTP_PARTIAL_CONTENT == status) // partial information
+			{
+				partial = true;
+			}
+		}
+		if (!success)
+		{
+			llinfos << "Fetch Debugger : CURL GET FAILED, index = " << mIndex << ", status:" << status << " reason:" << reason << llendl;
+		}
+		mDebugger->callbackHTTP(mIndex, channels, buffer, partial, success);
+	}
+	virtual bool followRedir()
+	{
+		return true;
+	}
+private:
+	LLTextureFetchDebugger* mDebugger;
+	S32 mIndex;
+};
+
+LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) :
+	mFetcher(fetcher),
+	mTextureCache(cache),
+	mImageDecodeThread(imagedecodethread),
+	mCurlGetRequest(NULL)
+{
+	init();
+}
+	
+LLTextureFetchDebugger::~LLTextureFetchDebugger()
+{
+	mFetchingHistory.clear();
+	stopDebug();
+}
+
+void LLTextureFetchDebugger::init()
+{
+	mState = IDLE;
+	
+	mCacheReadTime = -1.f;
+	mCacheWriteTime = -1.f;
+	mDecodingTime = -1.f;
+	mHTTPTime = -1.f;
+	mGLCreationTime = -1.f;
+	mTotalFetchingTime = 0.f;
+	mRefetchVisCacheTime = -1.f;
+	mRefetchVisHTTPTime = -1.f;
+
+	mNumFetchedTextures = 0;
+	mNumCacheHits = 0;
+	mNumVisibleFetchedTextures = 0;
+	mNumVisibleFetchingRequests = 0;
+	mFetchedData = 0;
+	mDecodedData = 0;
+	mVisibleFetchedData = 0;
+	mVisibleDecodedData = 0;
+	mRenderedData = 0;
+	mRenderedDecodedData = 0;
+	mFetchedPixels = 0;
+	mRenderedPixels = 0;
+	mRefetchedData = 0;
+	mRefetchedPixels = 0;
+
+	mFreezeHistory = FALSE;
+}
+
+void LLTextureFetchDebugger::startDebug()
+{
+	//lock the fetcher
+	mFetcher->lockFetcher(true);
+	mFreezeHistory = TRUE;
+
+	//clear the current fetching queue
+	gTextureList.clearFetchingRequests();
+
+	//wait for all works to be done
+	while(1)
+	{
+		S32 pending = 0;
+		pending += LLAppViewer::getTextureCache()->update(1); 
+		pending += LLAppViewer::getImageDecodeThread()->update(1); 
+		pending += LLAppViewer::getTextureFetch()->update(1); 
+		if(!pending)
+		{
+			break;
+		}
+	}
+
+	//collect statistics
+	mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime;
+	
+	std::set<LLUUID> fetched_textures;
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size; i++)
+	{
+		bool in_list = true;
+		if(fetched_textures.find(mFetchingHistory[i].mID) == fetched_textures.end())
+		{
+			fetched_textures.insert(mFetchingHistory[i].mID);
+			in_list = false;
+		}
+		
+		LLViewerFetchedTexture* tex = LLViewerTextureManager::findFetchedTexture(mFetchingHistory[i].mID);
+		if(tex && tex->isJustBound()) //visible
+		{
+			if(!in_list)
+			{
+				mNumVisibleFetchedTextures++;
+			}
+			mNumVisibleFetchingRequests++;
+	
+			mVisibleFetchedData += mFetchingHistory[i].mFetchedSize;
+			mVisibleDecodedData += mFetchingHistory[i].mDecodedSize;
+	
+			if(tex->getDiscardLevel() >= mFetchingHistory[i].mDecodedLevel)
+			{
+				mRenderedData += mFetchingHistory[i].mFetchedSize;
+				mRenderedDecodedData += mFetchingHistory[i].mDecodedSize;
+				mRenderedPixels += tex->getWidth() * tex->getHeight();
+			}
+		}
+	}
+
+	mNumFetchedTextures = fetched_textures.size();
+}
+
+void LLTextureFetchDebugger::stopDebug()
+{
+	//clear the current debug work
+	S32 size = mFetchingHistory.size();
+	switch(mState)
+	{
+	case READ_CACHE:		
+		for(S32 i = 0 ; i < size; i++)
+		{
+			if (mFetchingHistory[i]. mCacheHandle != LLTextureCache::nullHandle())
+			{
+				mTextureCache->readComplete(mFetchingHistory[i].mCacheHandle, true);
+			}
+		}	
+		break;
+	case WRITE_CACHE:
+		for(S32 i = 0 ; i < size; i++)
+		{
+			if (mFetchingHistory[i].mCacheHandle != LLTextureCache::nullHandle())
+			{
+				mTextureCache->writeComplete(mFetchingHistory[i].mCacheHandle, true);
+			}
+		}
+		break;
+	case DECODING:
+		break;
+	case HTTP_FETCHING:
+		break;
+	case GL_TEX:
+		break;
+	default:
+		break;
+	}
+
+	while(1)
+	{
+		if(update())
+		{
+			break;
+		}
+	}
+
+	//unlock the fetcher
+	mFetcher->lockFetcher(false);
+	mFreezeHistory = FALSE;
+	mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset
+}
+
+//called in the main thread and when the fetching queue is empty
+void LLTextureFetchDebugger::clearHistory()
+{
+	mFetchingHistory.clear();
+	init();
+}
+
+void LLTextureFetchDebugger::addHistoryEntry(LLTextureFetchWorker* worker)
+{
+	if(mFreezeHistory)
+	{
+		mRefetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight();
+		mRefetchedData += worker->mFormattedImage->getDataSize();
+		return;
+	}
+
+	if(worker->mInCache)
+	{
+		mNumCacheHits++;
+	}
+	mFetchedData += worker->mFormattedImage->getDataSize();
+	mDecodedData += worker->mRawImage->getDataSize();
+	mFetchedPixels += worker->mRawImage->getWidth() * worker->mRawImage->getHeight();
+
+	mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize()));
+	//mFetchingHistory.push_back(FetchEntry(worker->mID, worker->mDesiredSize, worker->mHaveAllData ? 0 : worker->mLoadedDiscard, worker->mFormattedImage->getComponents(),
+		//worker->mDecodedDiscard, worker->mFormattedImage->getDataSize(), worker->mRawImage->getDataSize()));
+}
+
+void LLTextureFetchDebugger::lockCache()
+{
+}
+	
+void LLTextureFetchDebugger::unlockCache()
+{
+}
+	
+void LLTextureFetchDebugger::debugCacheRead()
+{
+	lockCache();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = READ_CACHE;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		mFetchingHistory[i].mFormattedImage = NULL;
+		mFetchingHistory[i].mCacheHandle = mTextureCache->readFromCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, 0, mFetchingHistory[i].mFetchedSize, 
+			new LLDebuggerCacheReadResponder(this, i, mFetchingHistory[i].mFormattedImage));
+	}
+}
+	
+void LLTextureFetchDebugger::clearCache()
+{
+	S32 size = mFetchingHistory.size();
+	{
+		std::set<LLUUID> deleted_list;
+		for(S32 i = 0 ; i < size ; i++)
+		{
+			if(deleted_list.find(mFetchingHistory[i].mID) == deleted_list.end())
+			{
+				deleted_list.insert(mFetchingHistory[i].mID);
+				mTextureCache->removeFromCache(mFetchingHistory[i].mID);
+			}
+		}
+	}
+}
+
+void LLTextureFetchDebugger::debugCacheWrite()
+{
+	//remove from cache
+	clearCache();
+
+	lockCache();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = WRITE_CACHE;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		if(mFetchingHistory[i].mFormattedImage.notNull())
+		{
+			mFetchingHistory[i].mCacheHandle = mTextureCache->writeToCache(mFetchingHistory[i].mID, LLWorkerThread::PRIORITY_NORMAL, 
+				mFetchingHistory[i].mFormattedImage->getData(), mFetchingHistory[i].mFetchedSize,
+				mFetchingHistory[i].mDecodedLevel == 0 ? mFetchingHistory[i].mFetchedSize : mFetchingHistory[i].mFetchedSize + 1, 
+				new LLDebuggerCacheWriteResponder(this, i));					
+		}
+	}
+}
+
+void LLTextureFetchDebugger::lockDecoder()
+{
+}
+	
+void LLTextureFetchDebugger::unlockDecoder()
+{
+}
+
+void LLTextureFetchDebugger::debugDecoder()
+{
+	lockDecoder();
+	llassert_always(mState == IDLE);
+	mTimer.reset();
+	mState = DECODING;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{		
+		if(mFetchingHistory[i].mFormattedImage.isNull())
+		{
+			continue;
+		}
+
+		mImageDecodeThread->decodeImage(mFetchingHistory[i].mFormattedImage, LLWorkerThread::PRIORITY_NORMAL, 
+			mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mNeedsAux,
+			new LLDebuggerDecodeResponder(this, i));
+	}
+}
+
+void LLTextureFetchDebugger::debugHTTP()
+{
+	llassert_always(mState == IDLE);
+
+	LLViewerRegion* region = gAgent.getRegion();
+	if (!region)
+	{
+		llinfos << "Fetch Debugger : Current region undefined. Cannot fetch textures through HTTP." << llendl;
+		return;
+	}
+	
+	mHTTPUrl = region->getHttpUrl();
+	if (mHTTPUrl.empty())
+	{
+		llinfos << "Fetch Debugger : Current region URL undefined. Cannot fetch textures through HTTP." << llendl;
+		return;
+	}
+	
+	mTimer.reset();
+	mState = HTTP_FETCHING;
+	
+	S32 size = mFetchingHistory.size();
+	for (S32 i = 0 ; i < size ; i++)
+	{
+		mFetchingHistory[i].mCurlState = FetchEntry::CURL_NOT_DONE;
+		mFetchingHistory[i].mCurlReceivedSize = 0;
+		mFetchingHistory[i].mHTTPFailCount = 0;
+	}
+	mNbCurlRequests = 0;
+	mNbCurlCompleted = 0;
+	
+	fillCurlQueue();
+}
+
+S32 LLTextureFetchDebugger::fillCurlQueue()
+{
+	if (mNbCurlRequests == 24)
+		return mNbCurlRequests;
+	
+	S32 size = mFetchingHistory.size();
+	for (S32 i = 0 ; i < size ; i++)
+	{		
+		if (mFetchingHistory[i].mCurlState != FetchEntry::CURL_NOT_DONE)
+			continue;
+		std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[i].mID.asString().c_str();
+		S32 requestedSize = mFetchingHistory[i].mRequestedSize;
+		// We request the whole file if the size was not set.
+		requestedSize = llmax(0,requestedSize);
+		// We request the whole file if the size was set to an absurdly high value (meaning all file)
+		requestedSize = (requestedSize == 33554432 ? 0 : requestedSize);
+		std::vector<std::string> headers;
+		headers.push_back("Accept: image/x-j2c");
+		bool res = mCurlGetRequest->getByteRange(texture_url, headers, 0, requestedSize, new LLDebuggerHTTPResponder(this, i));
+		if (res)
+		{
+			mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS;
+			mNbCurlRequests++;
+			// Hack
+			if (mNbCurlRequests == 24)
+				break;
+		}
+		else 
+		{
+			break;
+		}
+	}
+	//llinfos << "Fetch Debugger : Having " << mNbCurlRequests << " requests through the curl thread." << llendl;
+	return mNbCurlRequests;
+}
+
+void LLTextureFetchDebugger::debugGLTextureCreation()
+{
+	llassert_always(mState == IDLE);
+	mState = GL_TEX;
+	std::vector<LLViewerFetchedTexture*> tex_list;
+
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{
+		if(mFetchingHistory[i].mRawImage.notNull())
+		{
+			LLViewerFetchedTexture* tex = gTextureList.findImage(mFetchingHistory[i].mID) ;
+			if(tex && !tex->isForSculptOnly())
+			{
+				tex->destroyGLTexture() ;
+				tex_list.push_back(tex);
+			}
+		}
+	}
+
+	mTimer.reset();
+	S32 j = 0 ;
+	S32 size1 = tex_list.size();
+	for(S32 i = 0 ; i < size && j < size1; i++)
+	{
+		if(mFetchingHistory[i].mRawImage.notNull())
+		{
+			if(mFetchingHistory[i].mID == tex_list[j]->getID())
+			{
+				tex_list[j]->createGLTexture(mFetchingHistory[i].mDecodedLevel, mFetchingHistory[i].mRawImage, 0, TRUE, tex_list[j]->getBoostLevel());
+				j++;
+			}
+		}
+	}
+
+	mGLCreationTime = mTimer.getElapsedTimeF32() ;
+	return;
+}
+
+//clear fetching results of all textures.
+void LLTextureFetchDebugger::clearTextures()
+{
+	S32 size = mFetchingHistory.size();
+	for(S32 i = 0 ; i < size ; i++)
+	{
+		LLViewerFetchedTexture* tex = gTextureList.findImage(mFetchingHistory[i].mID) ;
+		if(tex)
+		{
+			tex->clearFetchedResults() ;
+		}
+	}
+}
+
+void LLTextureFetchDebugger::debugRefetchVisibleFromCache()
+{
+	llassert_always(mState == IDLE);
+	mState = REFETCH_VIS_CACHE;
+
+	clearTextures();
+
+	mTimer.reset();
+	mFetcher->lockFetcher(false);
+}
+
+void LLTextureFetchDebugger::debugRefetchVisibleFromHTTP()
+{
+	llassert_always(mState == IDLE);
+	mState = REFETCH_VIS_HTTP;
+
+	clearCache();
+	clearTextures();
+
+	mTimer.reset();
+	mFetcher->lockFetcher(false);
+}
+
+bool LLTextureFetchDebugger::update()
+{
+	switch(mState)
+	{
+	case READ_CACHE:
+		if(!mTextureCache->update(1))
+		{
+			mCacheReadTime = mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockCache();
+		}
+		break;
+	case WRITE_CACHE:
+		if(!mTextureCache->update(1))
+		{
+			mCacheWriteTime = mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockCache();
+		}
+		break;
+	case DECODING:
+		if(!mImageDecodeThread->update(1))
+		{
+			mDecodingTime =  mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+			unlockDecoder();
+		}
+		break;
+	case HTTP_FETCHING:
+		mCurlGetRequest->process();
+		LLCurl::getCurlThread()->update(1);
+		if (!fillCurlQueue() && mNbCurlCompleted == mFetchingHistory.size())
+		{
+			mHTTPTime =  mTimer.getElapsedTimeF32() ;
+			mState = IDLE;
+		}
+		break;
+	case GL_TEX:
+		mState = IDLE;
+		break;
+	case REFETCH_VIS_CACHE:
+		if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
+		{
+			mRefetchVisCacheTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime;
+			mState = IDLE;
+			mFetcher->lockFetcher(true);
+		}
+		break;
+	case REFETCH_VIS_HTTP:
+		if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
+		{
+			mRefetchVisHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime;
+			mState = IDLE;
+			mFetcher->lockFetcher(true);
+		}
+		break;
+	default:
+		mState = IDLE;
+		break;
+	}
+
+	return mState == IDLE;
+}
+
+void LLTextureFetchDebugger::callbackCacheRead(S32 id, bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal)
+{
+	if (success)
+	{
+		mFetchingHistory[id].mFormattedImage = image;
+	}
+	mTextureCache->readComplete(mFetchingHistory[id].mCacheHandle, false);
+	mFetchingHistory[id].mCacheHandle = LLTextureCache::nullHandle();
+}
+
+void LLTextureFetchDebugger::callbackCacheWrite(S32 id, bool success)
+{
+	mTextureCache->writeComplete(mFetchingHistory[id].mCacheHandle);
+	mFetchingHistory[id].mCacheHandle = LLTextureCache::nullHandle();
+}
+
+void LLTextureFetchDebugger::callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux)
+{
+	if (success)
+	{
+		llassert_always(raw);
+		mFetchingHistory[id].mRawImage = raw;
+	}
+}
+
+void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& channels,
+										  const LLIOPipe::buffer_ptr_t& buffer, 
+										  bool partial, bool success)
+{
+	mNbCurlRequests--;
+	if (success)
+	{
+		mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE;
+		mNbCurlCompleted++;
+
+		S32 data_size = buffer->countAfter(channels.in(), NULL);
+		mFetchingHistory[id].mCurlReceivedSize += data_size;
+		//llinfos << "Fetch Debugger : got results for " << id << ", data_size = " << data_size << ", received = " << mFetchingHistory[id].mCurlReceivedSize << ", requested = " << mFetchingHistory[id].mRequestedSize << ", partial = " << partial << llendl;
+		if ((mFetchingHistory[id].mCurlReceivedSize >= mFetchingHistory[id].mRequestedSize) || !partial || (mFetchingHistory[id].mRequestedSize == 600))
+		{
+			U8* d_buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size);
+			buffer->readAfter(channels.in(), NULL, d_buffer, data_size);
+			
+			llassert_always(mFetchingHistory[id].mFormattedImage.isNull());
+			{
+				// For now, create formatted image based on extension
+				std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[id].mID.asString().c_str();
+				std::string extension = gDirUtilp->getExtension(texture_url);
+				mFetchingHistory[id].mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
+				if (mFetchingHistory[id].mFormattedImage.isNull())
+				{
+					mFetchingHistory[id].mFormattedImage = new LLImageJ2C; // default
+				}
+			}
+						
+			mFetchingHistory[id].mFormattedImage->setData(d_buffer, data_size);	
+		}
+	}
+	else //failed
+	{
+		mFetchingHistory[id].mHTTPFailCount++;
+		if(mFetchingHistory[id].mHTTPFailCount < 5)
+		{
+			// Fetch will have to be redone
+			mFetchingHistory[id].mCurlState = FetchEntry::CURL_NOT_DONE;
+		}
+		else //skip
+		{
+			mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE;
+			mNbCurlCompleted++;
+		}
+	}
+}
+
+
+//---------------------
+///////////////////////////////////////////////////////////////////////////////////////////
+//End LLTextureFetchDebugger
+///////////////////////////////////////////////////////////////////////////////////////////
 
 
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 35df7d816f20b91e28b319c40c9767e1a428c7b6..107e1623b0167dafe7eff655a5fb95b8cfba66f8 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -34,14 +34,17 @@
 #include "llcurl.h"
 #include "lltextureinfo.h"
 #include "llapr.h"
+#include "llimageworker.h"
+//#include "lltexturecache.h"
 
 class LLViewerTexture;
 class LLTextureFetchWorker;
 class HTTPGetResponder;
-class LLTextureCache;
 class LLImageDecodeThread;
 class LLHost;
 class LLViewerAssetStats;
+class LLTextureFetchDebugger;
+class LLTextureCache;
 
 // Interface class
 class LLTextureFetch : public LLWorkerThread
@@ -164,6 +167,9 @@ private:
 	LLMutex mQueueMutex;        //to protect mRequestMap and mCommands only
 	LLMutex mNetworkQueueMutex; //to protect mNetworkQueue, mHTTPTextureQueue and mCancelQueue.
 
+	static LLStat sCacheHitRate;
+	static LLStat sCacheReadLatency;
+
 	LLTextureCache* mTextureCache;
 	LLImageDecodeThread* mImageDecodeThread;
 	LLCurlRequest* mCurlGetRequest;
@@ -209,7 +215,195 @@ public:
 	// attempt to log metrics follows a break in the metrics stream
 	// reporting due to either startup or a problem POSTing data.
 	static volatile bool svMetricsDataBreak;
+
+private:
+	//debug use
+	LLTextureFetchDebugger* mFetchDebugger;
+	bool mFetcherLocked;
+
+public:
+	//debug use
+	LLTextureFetchDebugger* getFetchDebugger() { return mFetchDebugger;}
+	void lockFetcher(bool lock) { mFetcherLocked = lock;}
 };
 
+//debug use
+class LLTextureFetchDebugger
+{
+	friend class LLTextureFetch;
+public:
+	LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) ;
+	~LLTextureFetchDebugger();
+
+public:
+	enum e_debug_state
+	{
+		IDLE = 0,
+		READ_CACHE,
+		WRITE_CACHE,
+		DECODING,
+		HTTP_FETCHING,
+		GL_TEX,
+		REFETCH_VIS_CACHE,
+		REFETCH_VIS_HTTP,
+		REFETCH_ALL_CACHE,
+		REFETCH_ALL_HTTP,
+		INVALID
+	};
+
+private:	
+	struct FetchEntry
+	{
+		enum e_curl_state
+		{
+			CURL_NOT_DONE = 0,
+			CURL_IN_PROGRESS,
+			CURL_DONE
+		};
+		LLUUID mID;
+		S32 mRequestedSize;
+		S32 mDecodedLevel;
+		S32 mFetchedSize;
+		S32 mDecodedSize;
+		BOOL mNeedsAux;
+		U32 mCacheHandle;
+		LLPointer<LLImageFormatted> mFormattedImage;
+		LLPointer<LLImageRaw> mRawImage;
+		e_curl_state mCurlState;
+		S32 mCurlReceivedSize;
+		S32 mHTTPFailCount;
+
+		FetchEntry() :
+			mDecodedLevel(-1),
+			mFetchedSize(0),
+			mDecodedSize(0)
+			{}
+		FetchEntry(LLUUID& id, S32 r_size, /*S32 f_discard, S32 c,*/ S32 level, S32 f_size, S32 d_size) :
+			mID(id),
+			mRequestedSize(r_size),
+			mDecodedLevel(level),
+			mFetchedSize(f_size),
+			mDecodedSize(d_size),
+			mNeedsAux(false),
+			mHTTPFailCount(0)
+			{}
+	};
+	std::vector<FetchEntry> mFetchingHistory;
+	
+	e_debug_state mState;
+	
+	F32 mCacheReadTime;
+	F32 mCacheWriteTime;
+	F32 mDecodingTime;
+	F32 mHTTPTime;
+	F32 mGLCreationTime;
+
+	F32 mTotalFetchingTime;
+	F32 mRefetchVisCacheTime;
+	F32 mRefetchVisHTTPTime;
+
+	LLTimer mTimer;
+	
+	LLTextureFetch* mFetcher;
+	LLTextureCache* mTextureCache;
+	LLImageDecodeThread* mImageDecodeThread;
+	LLCurlRequest* mCurlGetRequest;
+	
+	S32 mNumFetchedTextures;
+	S32 mNumCacheHits;
+	S32 mNumVisibleFetchedTextures;
+	S32 mNumVisibleFetchingRequests;
+	U32 mFetchedData;
+	U32 mDecodedData;
+	U32 mVisibleFetchedData;
+	U32 mVisibleDecodedData;
+	U32 mRenderedData;
+	U32 mRenderedDecodedData;
+	U32 mFetchedPixels;
+	U32 mRenderedPixels;
+	U32 mRefetchedData;
+	U32 mRefetchedPixels;
+
+	BOOL mFreezeHistory;
+
+	std::string mHTTPUrl;
+	S32 mNbCurlRequests;
+	S32 mNbCurlCompleted;
+
+public:
+	bool update(); //called in the main thread once per frame
+
+	//fetching history
+	void clearHistory();
+	void addHistoryEntry(LLTextureFetchWorker* worker);
+	
+	void setCurlGetRequest(LLCurlRequest* request) { mCurlGetRequest = request;}
+	
+	void startDebug();
+	void stopDebug(); //stop everything
+	void debugCacheRead();
+	void debugCacheWrite();	
+	void debugHTTP();
+	void debugDecoder();
+	void debugGLTextureCreation();
+	void debugRefetchVisibleFromCache();
+	void debugRefetchVisibleFromHTTP();
+
+	void callbackCacheRead(S32 id, bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal);
+	void callbackCacheWrite(S32 id, bool success);
+	void callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux);
+	void callbackHTTP(S32 id, const LLChannelDescriptors& channels,
+					  const LLIOPipe::buffer_ptr_t& buffer, 
+					  bool partial, bool success);
+	
+
+	e_debug_state getState()             {return mState;}
+	S32  getNumFetchedTextures()         {return mNumFetchedTextures;}
+	S32  getNumFetchingRequests()        {return mFetchingHistory.size();}
+	S32  getNumCacheHits()               {return mNumCacheHits;}
+	S32  getNumVisibleFetchedTextures()  {return mNumVisibleFetchedTextures;}
+	S32  getNumVisibleFetchingRequests() {return mNumVisibleFetchingRequests;}
+	U32  getFetchedData()                {return mFetchedData;}
+	U32  getDecodedData()                {return mDecodedData;}
+	U32  getVisibleFetchedData()         {return mVisibleFetchedData;}
+	U32  getVisibleDecodedData()         {return mVisibleDecodedData;}
+	U32  getRenderedData()               {return mRenderedData;}
+	U32  getRenderedDecodedData()        {return mRenderedDecodedData;}
+	U32  getFetchedPixels()              {return mFetchedPixels;}
+	U32  getRenderedPixels()             {return mRenderedPixels;}
+	U32  getRefetchedData()              {return mRefetchedData;}
+	U32  getRefetchedPixels()            {return mRefetchedPixels;}
+
+	F32  getCacheReadTime()     {return mCacheReadTime;}
+	F32  getCacheWriteTime()    {return mCacheWriteTime;}
+	F32  getDecodeTime()        {return mDecodingTime;}
+	F32  getGLCreationTime()    {return mGLCreationTime;}
+	F32  getHTTPTime()          {return mHTTPTime;}
+	F32  getTotalFetchingTime() {return mTotalFetchingTime;}
+	F32  getRefetchVisCacheTime() {return mRefetchVisCacheTime;}
+	F32  getRefetchVisHTTPTime()  {return mRefetchVisHTTPTime;}
+
+private:
+	void init();
+	void clearTextures();//clear fetching results of all textures.
+	void clearCache();
+
+	void lockFetcher();
+	void unlockFetcher();
+
+	void lockCache();
+	void unlockCache();
+
+	void lockDecoder();
+	void unlockDecoder();
+	
+	S32 fillCurlQueue();
+
+private:
+	static bool sDebuggerEnabled;
+public:
+	static bool isEnabled() {return sDebuggerEnabled;}
+};
 #endif // LL_LLTEXTUREFETCH_H
 
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 5b41a05f2aac01d5f66ad1fcf9b4c849c731e9c6..425bf7ee871f41b5ae8ba863e1fd41c7e9ea2c08 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -57,8 +57,6 @@
 extern F32 texmem_lower_bound_scale;
 
 LLTextureView *gTextureView = NULL;
-LLTextureSizeView *gTextureSizeView = NULL;
-LLTextureSizeView *gTextureCategoryView = NULL;
 
 #define HIGH_PRIORITY 100000000.f
 
@@ -512,8 +510,8 @@ void LLGLTexMemBar::draw()
 	F32 discard_bias = LLViewerTexture::sDesiredDiscardBias;
 	F32 cache_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getUsage()) ;
 	F32 cache_max_usage = (F32)BYTES_TO_MEGA_BYTES(LLAppViewer::getTextureCache()->getMaxUsage()) ;
-	S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-	S32 v_offset = (S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f);
+	S32 line_height = (S32)(LLFontGL::getFontMonospace()->getLineHeight() + .5f);
+	S32 v_offset = 0;//(S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f);
 	F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024);
 	F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024);
 	U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests() ;
@@ -527,80 +525,24 @@ void LLGLTexMemBar::draw()
 	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
 
-	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d",
+	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB FBO: %d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB",
 					total_mem,
 					max_total_mem,
 					bound_mem,
 					max_bound_mem,
 					LLRenderTarget::sBytesAllocated/(1024*1024),
 					LLImageRaw::sGlobalRawMemory >> 20,	discard_bias,
-					cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded, total_http_requests);
-	//, cache_entries, cache_max_entries
-
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,
+					cache_usage, cache_max_usage);
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
 
-	//----------------------------------------------------------------------------
-#if 0
-	S32 bar_left = 400;
-	S32 bar_width = 200;
-	S32 top = line_height*3 - 2 + v_offset;
-	S32 bottom = top - 6;
-	S32 left = bar_left;
-	S32 right = left + bar_width;
-	F32 bar_scale;
-	
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-
-	// GL Mem Bar
-		
-	left = bar_left;
-	text = "GL";
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3,
+	text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d",
+					total_texture_downloaded, total_object_downloaded, total_http_requests);
+	//, cache_entries, cache_max_entries
+	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3,
 											 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	
-	left = bar_left+20;
-	right = left + bar_width;
-	
-	gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f); // grey
-	gl_rect_2d(left, top, right, bottom);
-
-	bar_scale = (F32)bar_width / (max_total_mem * 1.5f);
-	right = left + llfloor(total_mem * bar_scale);
-	right = llclamp(right, bar_left, bar_left + bar_width);
-	
-	color = (total_mem < llfloor(max_total_mem * texmem_lower_bound_scale)) ? LLColor4::green :
-		  	(total_mem < max_total_mem) ? LLColor4::yellow : LLColor4::red;
-	color[VALPHA] = .75f;
-	gGL.diffuseColor4fv(color.mV);
-	
-	gl_rect_2d(left, top, right, bottom); // red/yellow/green
-
-	//
-	bar_left += bar_width + bar_space;
-	//top = bottom - 2; bottom = top - 6;
-	
-	// Bound Mem Bar
-
-	left = bar_left;
-	text = "GL";
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, line_height*3,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	left = bar_left + 20;
-	right = left + bar_width;
-	
-	gGL.color4f(0.5f, 0.5f, 0.5f, 0.75f);
-	gl_rect_2d(left, top, right, bottom);
-
-	color = (bound_mem < llfloor(max_bound_mem * texmem_lower_bound_scale)) ? LLColor4::green :
-		  	(bound_mem < max_bound_mem) ? LLColor4::yellow : LLColor4::red;
-	color[VALPHA] = .75f;
-	gGL.diffuseColor4fv(color.mV);
 
-	gl_rect_2d(left, top, right, bottom);
-#else
 	S32 left = 0 ;
-#endif
 	//----------------------------------------------------------------------------
 
 	text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d",
@@ -669,8 +611,7 @@ BOOL LLGLTexMemBar::handleMouseDown(S32 x, S32 y, MASK mask)
 LLRect LLGLTexMemBar::getRequiredRect()
 {
 	LLRect rect;
-	//rect.mTop = 50;
-	rect.mTop = 0;
+	rect.mTop = 50; //LLFontGL::getFontMonospace()->getLineHeight() * 6;
 	return rect;
 }
 
@@ -954,9 +895,11 @@ void LLTextureView::draw()
 		LLRect tmbr;
 		tmbp.name("gl texmem bar");
 		tmbp.rect(tmbr);
+		tmbp.follows.flags = FOLLOWS_LEFT|FOLLOWS_TOP;
 		tmbp.texture_view(this);
 		mGLTexMemBar = LLUICtrlFactory::create<LLGLTexMemBar>(tmbp);
-		addChildInBack(mGLTexMemBar);
+		addChild(mGLTexMemBar);
+		sendChildToFront(mGLTexMemBar);
 
 		LLAvatarTexBar::Params atbp;
 		LLRect atbr;
@@ -965,16 +908,13 @@ void LLTextureView::draw()
 		atbp.rect(atbr);
 		mAvatarTexBar = LLUICtrlFactory::create<LLAvatarTexBar>(atbp);
 		addChild(mAvatarTexBar);
+		sendChildToFront(mAvatarTexBar);
 
 		reshape(getRect().getWidth(), getRect().getHeight(), TRUE);
 
-		/*
-		  count = gTextureList.getNumImages();
-		  std::string info_string;
-		  info_string = llformat("Global Info:\nTexture Count: %d", count);
-		  mInfoTextp->setText(info_string);
-		*/
-
+		LLUI::popMatrix();
+		LLUI::pushMatrix();
+		LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
 
 		for (child_list_const_iter_t child_iter = getChildList()->begin();
 			 child_iter != getChildList()->end(); ++child_iter)
@@ -1049,302 +989,4 @@ BOOL LLTextureView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
 	return FALSE;
 }
 
-//-----------------------------------------------------------------
-LLTextureSizeView::LLTextureSizeView(const LLTextureSizeView::Params& p) : LLContainerView(p)
-{
-	setVisible(FALSE) ;
-
-	mTextureSizeBarWidth = 30 ;
-}
-
-LLTextureSizeView::~LLTextureSizeView()
-{
-	if(mTextureSizeBar.size())
-	{
-		for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-		{
-			delete mTextureSizeBar[i] ;
-		}
-		mTextureSizeBar.clear() ;
-	}
-}
-void LLTextureSizeView::draw()
-{
-	if(mType == TEXTURE_MEM_OVER_SIZE)
-	{
-		drawTextureSizeGraph();
-	}
-	else
-	{
-		drawTextureCategoryGraph() ;
-	}
-	
-	LLView::draw();
-}
-
-BOOL LLTextureSizeView::handleHover(S32 x, S32 y, MASK mask) 
-{
-	if(x > mTextureSizeBarRect.mLeft && x < mTextureSizeBarRect.mRight)
-	{
-		mTextureSizeBar[(x - mTextureSizeBarRect.mLeft) / mTextureSizeBarWidth]->handleHover(x, y, mask, (mType == TEXTURE_MEM_OVER_SIZE)) ;
-	}
-
-	return TRUE ;
-}
-
-//draw real-time texture mem bar over size
-void LLTextureSizeView::drawTextureSizeGraph()
-{
-	if(mTextureSizeBar.size() == 0)
-	{
-		S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-		mTextureSizeBar.resize(LLImageGL::sTextureLoadedCounter.size()) ;
-		mTextureSizeBarRect.set(700, line_height * 2 + 400, 700 + mTextureSizeBar.size() * mTextureSizeBarWidth, line_height * 2) ;
-		
-		for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-		{				
-			mTextureSizeBar[i] = new LLGLTexSizeBar(i, mTextureSizeBarRect.mLeft + i * mTextureSizeBarWidth , 
-				line_height * 2, mTextureSizeBarRect.mLeft + (i + 1) * mTextureSizeBarWidth, line_height) ;				
-		}			
-	}
-
-	F32 size_bar_scale = drawTextureSizeDistributionGraph() ;		
-	for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-	{
-		mTextureSizeBar[i]->setTop(LLImageGL::sTextureLoadedCounter[i], LLImageGL::sTextureBoundCounter[i], size_bar_scale) ;
-		mTextureSizeBar[i]->draw() ;
-	}		
-	LLImageGL::resetCurTexSizebar();
-}
-
-//draw background of texture size bar graph
-F32 LLTextureSizeView::drawTextureSizeDistributionGraph()
-{	
-	//scale
-	F32 scale = 1.0f ;
-	
-	LLGLSUIDefault gls_ui;
-
-	{
-		S32 count = 0 ;
-		for(U32 i = 0 ; i < LLImageGL::sTextureLoadedCounter.size() ; i++)
-		{
-			if(LLImageGL::sTextureLoadedCounter[i] > count)
-			{
-				count = LLImageGL::sTextureLoadedCounter[i] ;
-			}
-		}
-		if(count > mTextureSizeBarRect.getHeight())
-		{
-			scale = (F32)mTextureSizeBarRect.getHeight() / count ;
-		}
-	}
-
-	S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-	S32 left = mTextureSizeBarRect.mLeft ;
-	S32 bottom = mTextureSizeBarRect.mBottom ;
-	S32 right = mTextureSizeBarRect.mRight ;
-	S32 top = mTextureSizeBarRect.mTop ;
-
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-	
-	//background rect
-	gl_rect_2d(left - 25, top + 30, right + 100, bottom - 25, LLColor4(0.0f, 0.0f, 0.0f, 0.25f)) ;
-
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.75f);
-	gl_line_2d(left, bottom, right, bottom) ; //x axis
-	gl_line_2d(left, bottom, left, top) ; //y axis
-
-	//ruler
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.5f);
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		gl_line_2d(left, i, right, i) ;
-	}
-
-	//texts
-	//--------------------------------------------------
-	F32 text_color[] = {1.f, 1.f, 1.f, 0.75f};	
-	std::string text;
-	
-	//-------
-	//x axis: size label
-	text = llformat("%d", 0) ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	for(U32 i = 1 ; i < mTextureSizeBar.size() ; i++)
-	{
-		text = llformat("%d", (1 << (i / 2)) + ((i & 1) ? ((1 << (i / 2)) >> 1) : 0)) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + i * mTextureSizeBarWidth + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-	text = llformat("(w + h)/2") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 10, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	//-------
-
-	//y axis: number label
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		text = llformat("%d", (S32)((i - bottom) / scale)) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 5, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-
-	//--------------------------------------------------
-	F32 loaded_color[] = {1.0f, 0.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 70, top + line_height * 2, left + 90, top + line_height, loaded_color) ;
-	text = llformat("Loaded") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 100, top + line_height * 2,
-									 loaded_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 170, top + line_height * 2, left + 190, top + line_height, bound_color) ;
-	text = llformat("Bound") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 200, top + line_height * 2,
-									 bound_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	//--------------------------------------------------
-
-	//title
-	text = llformat("Texture Size Distribution") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	return scale ;
-}
-
-//draw real-time texture mem bar over category
-void LLTextureSizeView::drawTextureCategoryGraph()
-{
-	if(mTextureSizeBar.size() == 0)
-	{
-		S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-		mTextureSizeBar.resize(LLViewerTexture::getTotalNumOfCategories()) ;
-		mTextureSizeBarRect.set(700, line_height * 2 + 400, 700 + mTextureSizeBar.size() * mTextureSizeBarWidth, line_height * 2) ;
-		
-		for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-		{				
-			mTextureSizeBar[i] = new LLGLTexSizeBar(i, mTextureSizeBarRect.mLeft + i * mTextureSizeBarWidth , 
-				line_height * 2, mTextureSizeBarRect.mLeft + (i + 1) * mTextureSizeBarWidth, line_height) ;				
-		}			
-	}
-
-	F32 size_bar_scale = drawTextureCategoryDistributionGraph() ;		
-	for(U32 i = 0 ; i < mTextureSizeBar.size() ; i++)
-	{
-		U32 k = LLViewerTexture::getIndexFromCategory(i) ;
-		mTextureSizeBar[i]->setTop(LLImageGL::sTextureMemByCategory[k] >> 20, LLImageGL::sTextureMemByCategoryBound[k] >> 20, size_bar_scale) ;
-		mTextureSizeBar[i]->draw() ;
-	}		
-	LLImageGL::resetCurTexSizebar();
-}
-
-//draw background for TEXTURE_MEM_OVER_CATEGORY
-F32 LLTextureSizeView::drawTextureCategoryDistributionGraph() 
-{
-	//scale
-	F32 scale = 4.0f ;
-	
-	LLGLSUIDefault gls_ui;
 
-	{
-		S32 count = 0 ;
-		for(U32 i = 0 ; i < LLImageGL::sTextureMemByCategory.size() ; i++)
-		{
-			S32 tmp = LLImageGL::sTextureMemByCategory[i] >> 20 ;
-			if(tmp > count)
-			{
-				count = tmp ;
-			}
-		}
-		if(count > mTextureSizeBarRect.getHeight() * 0.25f)
-		{
-			scale = (F32)mTextureSizeBarRect.getHeight() * 0.25f / count ;
-		}
-	}
-
-	S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
-	S32 left = mTextureSizeBarRect.mLeft ;
-	S32 bottom = mTextureSizeBarRect.mBottom ;
-	S32 right = mTextureSizeBarRect.mRight ;
-	S32 top = mTextureSizeBarRect.mTop ;
-
-	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-	
-	//background rect
-	gl_rect_2d(left - 25, top + 30, right + 100, bottom - 25, LLColor4(0.0f, 0.0f, 0.0f, 0.25f)) ;
-
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.75f);
-	gl_line_2d(left, bottom, right, bottom) ; //x axis
-	gl_line_2d(left, bottom, left, top) ; //y axis
-
-	//ruler
-	//--------------------------------------------------
-	gGL.color4f(1.0f, 0.5f, 0.5f, 0.5f);
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		gl_line_2d(left, i, right, i) ;
-	}
-
-	//texts
-	//--------------------------------------------------
-	F32 text_color[] = {1.f, 1.f, 1.f, 0.75f};	
-	std::string text;
-	
-	//-------
-	//x axis: size label			
-	static char category[LLViewerTexture::MAX_GL_IMAGE_CATEGORY][4] = 
-	{"Non", "Bak", "Av", "Cld", "Scp", "Hi", "Trn", "Slt", "Hud", "Bsf", "UI", "Pvw", "Map", "Mvs", "Slf", "Loc", "Scr", "Dyn", "Mdi", "ALT", "Oth" } ;
-
-	text = llformat("%s", category[0]) ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	for(U32 i = 1 ; i < mTextureSizeBar.size() ; i++)
-	{
-		text = llformat("%s", category[i]) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + i * mTextureSizeBarWidth + 12, bottom - line_height / 2,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-	//-------
-
-	//y axis: number label
-	for(S32 i = bottom + 50 ; i <= top ; i += 50)
-	{
-		text = llformat("%d", (S32)((i - bottom) / scale)) ;
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-		LLFontGL::getFontMonospace()->renderUTF8(text, 0, right + 5, i + line_height / 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	}
-
-	text = llformat("MB") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left - 20, top + line_height * 2 ,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-	//--------------------------------------------------
-	F32 loaded_color[] = {1.0f, 0.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 70, top + line_height * 2, left + 90, top + line_height, loaded_color) ;
-	text = llformat("Loaded") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 100, top + line_height * 2,
-									 loaded_color, 
-									 LLFontGL::LEFT, LLFontGL::TOP);
-
-	F32 bound_color[] = {1.0f, 1.0f, 0.0f, 0.75f};
-	gl_rect_2d(left + 170, top + line_height * 2, left + 190, top + line_height, bound_color) ;
-	text = llformat("Bound") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 200, top + line_height * 2,
-									 bound_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	//--------------------------------------------------
-
-	//title
-	text = llformat("Texture Category Distribution") ;
-	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left + 250, top + line_height * 3,
-									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
-
-	return scale ;
-}
diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h
index 3723eb737b46751fc3247364831d34b691524396..900b4e17d82ada90e6abdae8f6a8708106a29dcb 100644
--- a/indra/newview/lltextureview.h
+++ b/indra/newview/lltextureview.h
@@ -75,41 +75,6 @@ public:
 };
 
 class LLGLTexSizeBar;
-class LLTextureSizeView : public LLContainerView
-{
-protected:
-	LLTextureSizeView(const Params&);
-	friend class LLUICtrlFactory;
-public:	
-	~LLTextureSizeView();
-
-	/*virtual*/ void draw();
-	/*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) ;
-	
-	void setType(S32 type) {mType = type ;}
-	enum
-	{
-		TEXTURE_MEM_OVER_SIZE,
-		TEXTURE_MEM_OVER_CATEGORY
-	};
-private:
-	//draw background for TEXTURE_MEM_OVER_SIZE
-	F32 drawTextureSizeDistributionGraph() ;
-	//draw real-time texture mem bar over size
-	void drawTextureSizeGraph();
-
-	//draw background for TEXTURE_MEM_OVER_CATEGORY
-	F32 drawTextureCategoryDistributionGraph() ;
-	//draw real-time texture mem bar over category
-	void drawTextureCategoryGraph();
 
-private:
-	std::vector<LLGLTexSizeBar*> mTextureSizeBar ;
-	LLRect mTextureSizeBarRect ;
-	S32    mTextureSizeBarWidth ;	
-	S32    mType ;
-};
 extern LLTextureView *gTextureView;
-extern LLTextureSizeView *gTextureSizeView;
-extern LLTextureSizeView *gTextureCategoryView;
 #endif // LL_TEXTURE_VIEW_H
diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp
index de305bf3d9d3de96586d68e4c8fb7845509577c4..a473ee7ce03f42a80f65c9136fccffc1531302d2 100644
--- a/indra/newview/lltoastnotifypanel.cpp
+++ b/indra/newview/lltoastnotifypanel.cpp
@@ -52,7 +52,7 @@ const LLFontGL* LLToastNotifyPanel::sFontSmall = NULL;
 
 LLToastNotifyPanel::button_click_signal_t LLToastNotifyPanel::sButtonClickSignal;
 
-LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification, const LLRect& rect, bool show_images) : 
+LLToastNotifyPanel::LLToastNotifyPanel(const LLNotificationPtr& notification, const LLRect& rect, bool show_images) :
 LLToastPanel(notification),
 mTextBox(NULL),
 mInfoPanel(NULL),
@@ -243,8 +243,8 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt
 	mBtnCallbackData.push_back(userdata);
 
 	LLButton::Params p;
-	bool is_ignore_btn = form_element["index"].asInteger() == -1;
-	const LLFontGL* font = is_ignore_btn ? sFontSmall: sFont; // for ignore button in script dialog
+	bool make_small_btn = form_element["index"].asInteger() == -1 || form_element["index"].asInteger() == -2;
+	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.font(font);
@@ -264,7 +264,7 @@ LLButton* LLToastNotifyPanel::createButton(const LLSD& form_element, BOOL is_opt
 		p.rect.width = 1;
 		p.auto_resize = true;
 	}
-	else if (mIsScriptDialog && is_ignore_btn)
+	else if (mIsScriptDialog && make_small_btn)
 	{
 		// this is ignore button, make it smaller
 		p.rect.height = BTN_HEIGHT_SMALL;
@@ -536,7 +536,7 @@ void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id
 	}
 }
 
-void LLToastNotifyPanel::disableRespondedOptions(LLNotificationPtr& notification)
+void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification)
 {
 	LLSD response = notification->getResponse();
 	for (LLSD::map_const_iterator response_it = response.beginMap(); 
diff --git a/indra/newview/lltoastnotifypanel.h b/indra/newview/lltoastnotifypanel.h
index 57711b3d80f1a8f9850465481cbca5a42b9be4cb..db517ec8581d8dfd189c55e09216860312b564bb 100644
--- a/indra/newview/lltoastnotifypanel.h
+++ b/indra/newview/lltoastnotifypanel.h
@@ -60,7 +60,7 @@ public:
 	 * @deprecated if you intend to instantiate LLToastNotifyPanel - it's point to
 	 * implement right class for desired toast panel. @see LLGenericTipPanel as example.
 	 */
-	LLToastNotifyPanel(LLNotificationPtr& pNotification, const LLRect& rect = LLRect::null, bool show_images = true);
+	LLToastNotifyPanel(const LLNotificationPtr& pNotification, const LLRect& rect = LLRect::null, bool show_images = true);
 	virtual ~LLToastNotifyPanel();
 	LLPanel * getControlPanel() { return mControlPanel; }
 
@@ -118,7 +118,7 @@ protected:
 	/**
 	 * Process response data. Will disable selected options
 	 */
-	void disableRespondedOptions(LLNotificationPtr& notification);
+	void disableRespondedOptions(const LLNotificationPtr& notification);
 
 	bool mIsTip;
 	bool mAddedDefaultBtn;
diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp
index d2a4ce8745a27f655a2a11200d313aebd180a60d..c33fde99c59f4d3ffd9a4abfacde1b9ea7331acf 100644
--- a/indra/newview/lltoastpanel.cpp
+++ b/indra/newview/lltoastpanel.cpp
@@ -29,7 +29,9 @@
 #include "llpanelgenerictip.h"
 #include "llpanelonlinestatus.h"
 #include "llnotifications.h"
+#include "lltoastnotifypanel.h"
 #include "lltoastpanel.h"
+#include "lltoastscriptquestion.h"
 
 //static
 const S32 LLToastPanel::MIN_PANEL_HEIGHT = 40; // VPAD(4)*2 + ICON_HEIGHT(32)
@@ -106,6 +108,17 @@ LLToastPanel* LLToastPanel::buidPanelFromNotification(
 			res = new LLPanelGenericTip(notification);
 		}
 	}
+	else if("notify" == notification->getType())
+	{
+		if (notification->getPriority() == NOTIFICATION_PRIORITY_CRITICAL)
+		{
+			res = new LLToastScriptQuestion(notification);
+		}
+		else
+		{
+			res = new LLToastNotifyPanel(notification);
+		}
+	}
 	/*
 	 else if(...)
 	 create all other specific non-public toast panel
diff --git a/indra/newview/lltoastscriptquestion.cpp b/indra/newview/lltoastscriptquestion.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..feeb8ca77b7e72fac7b85f7305c6518ae9d0ccf6
--- /dev/null
+++ b/indra/newview/lltoastscriptquestion.cpp
@@ -0,0 +1,130 @@
+/**
+ * @file lltoastscriptquestion.cpp
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, 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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llbutton.h"
+#include "llnotifications.h"
+#include "lltoastscriptquestion.h"
+
+const int LEFT_PAD = 10;
+const int BUTTON_HEIGHT = 27;
+const int MAX_LINES_COUNT = 50;
+
+LLToastScriptQuestion::LLToastScriptQuestion(const LLNotificationPtr& notification)
+:
+LLToastPanel(notification)
+{
+	buildFromFile("panel_script_question_toast.xml");
+}
+
+BOOL LLToastScriptQuestion::postBuild()
+{
+	createButtons();
+
+	LLTextBox* mMessage = getChild<LLTextBox>("top_info_message");
+	LLTextBox* mFooter = getChild<LLTextBox>("bottom_info_message");
+
+	mMessage->setValue(mNotification->getMessage());
+	mFooter->setValue(mNotification->getFooter());
+
+	snapToMessageHeight();
+
+	return TRUE;
+}
+void LLToastScriptQuestion::snapToMessageHeight()
+{
+	LLTextBox* mMessage = getChild<LLTextBox>("top_info_message");
+	LLTextBox* mFooter = getChild<LLTextBox>("bottom_info_message");
+	if (!mMessage || !mFooter)
+	{
+		return;
+	}
+
+	if (mMessage->getVisible() && mFooter->getVisible())
+	{
+		S32 heightDelta = 0;
+		S32 maxTextHeight = (mMessage->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT)
+						  + (mFooter->getDefaultFont()->getLineHeight() * MAX_LINES_COUNT);
+
+		LLRect messageRect = mMessage->getRect();
+		LLRect footerRect  = mFooter->getRect();
+
+		S32 oldTextHeight = messageRect.getHeight() + footerRect.getHeight();
+
+		S32 requiredTextHeight = mMessage->getTextBoundingRect().getHeight() + mFooter->getTextBoundingRect().getHeight();
+		S32 newTextHeight = llmin(requiredTextHeight, maxTextHeight);
+
+		heightDelta = newTextHeight - oldTextHeight - heightDelta;
+
+		reshape( getRect().getWidth(), llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT));
+	}
+}
+
+void LLToastScriptQuestion::createButtons()
+{
+	LLNotificationFormPtr form = mNotification->getForm();
+	int num_elements = form->getNumElements();
+	int buttons_width = 0;
+
+	for (int i = 0; i < num_elements; ++i)
+	{
+		LLSD form_element = form->getElement(i);
+		if ("button" == form_element["type"].asString())
+		{
+			LLButton::Params p;
+			const LLFontGL* font = LLFontGL::getFontSansSerif();
+			p.name(form_element["name"].asString());
+			p.label(form_element["text"].asString());
+			p.layout("topleft");
+			p.font(font);
+			p.rect.height(BUTTON_HEIGHT);
+			p.click_callback.function(boost::bind(&LLToastScriptQuestion::onButtonClicked, this, form_element["name"].asString()));
+			p.rect.left = LEFT_PAD;
+			p.rect.width = font->getWidth(form_element["text"].asString());
+			p.auto_resize = true;
+			p.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
+			p.image_color(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
+			p.image_color_disabled(LLUIColorTable::instance().getColor("ButtonCautionImageColor"));
+
+			LLButton* button = LLUICtrlFactory::create<LLButton>(p);
+			button->autoResize();
+			getChild<LLPanel>("buttons_panel")->addChild(button);
+
+			LLRect rect = button->getRect();
+			rect.setLeftTopAndSize(buttons_width, rect.mTop, rect.getWidth(), rect.getHeight());
+			button->setRect(rect);
+
+			buttons_width += rect.getWidth() + LEFT_PAD;
+		}
+	}
+}
+
+void LLToastScriptQuestion::onButtonClicked(std::string btn_name)
+{
+	LLSD response = mNotification->getResponseTemplate();
+	response[btn_name] = true;
+	mNotification->respond(response);
+}
diff --git a/indra/newview/lltoastscriptquestion.h b/indra/newview/lltoastscriptquestion.h
new file mode 100644
index 0000000000000000000000000000000000000000..3a557f60f66dc70fde27f855117416ca4691c828
--- /dev/null
+++ b/indra/newview/lltoastscriptquestion.h
@@ -0,0 +1,49 @@
+/**
+ * @file lltoastscriptquestion.h
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, 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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoastpanel.h"
+
+#ifndef LLTOASTSCRIPTQUESTION_H_
+#define LLTOASTSCRIPTQUESTION_H_
+
+class LLToastScriptQuestion : public LLToastPanel
+{
+	LOG_CLASS(LLToastScriptQuestion);
+
+public:
+	LLToastScriptQuestion(const LLNotificationPtr& notification);
+	virtual BOOL postBuild();
+	virtual ~LLToastScriptQuestion(){};
+
+private:
+	void snapToMessageHeight();
+
+	void createButtons();
+	void onButtonClicked(std::string btn_name);
+};
+
+#endif /* LLTOASTSCRIPTQUESTION_H_ */
diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp
index eccb2cf2f15900301f90b63e3e101fcbe66e9fee..81ad96f39e4f34b321ee0bb3a47b982e0a01e36c 100644
--- a/indra/newview/lltoolbarview.cpp
+++ b/indra/newview/lltoolbarview.cpp
@@ -75,6 +75,7 @@ LLToolBarView::LLToolBarView(const LLToolBarView::Params& p)
 	mDragStarted(false),
 	mShowToolbars(true),
 	mDragToolbarButton(NULL),
+	mDragItem(NULL),
 	mToolbarsLoaded(false)
 {
 	for (S32 i = 0; i < TOOLBAR_COUNT; i++)
@@ -579,7 +580,6 @@ BOOL LLToolBarView::handleDragTool( S32 x, S32 y, const LLUUID& uuid, LLAssetTyp
 			uuid_vec_t cargo_ids;
 			types.push_back(DAD_WIDGET);
 			cargo_ids.push_back(uuid);
-			gClipboard.setSourceObject(uuid,LLAssetType::AT_WIDGET);
 			LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_VIEWER;
 			LLUUID srcID;
 			LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src, srcID);
@@ -662,6 +662,18 @@ void LLToolBarView::resetDragTool(LLToolBarButton* toolbarButton)
 	gToolBarView->mDragToolbarButton = toolbarButton;
 }
 
+// Provide a handle on a free standing inventory item containing references to the tool.
+// This might be used by Drag and Drop to move around references to tool items.
+LLInventoryObject* LLToolBarView::getDragItem()
+{
+	if (mDragToolbarButton)
+	{
+		LLUUID item_uuid = mDragToolbarButton->getCommandId().uuid();
+		mDragItem = new LLInventoryObject (item_uuid, LLUUID::null, LLAssetType::AT_WIDGET, "");
+	}
+	return mDragItem;
+}
+
 void LLToolBarView::setToolBarsVisible(bool visible)
 {
 	mShowToolbars = visible;
diff --git a/indra/newview/lltoolbarview.h b/indra/newview/lltoolbarview.h
index be66bcae3614feb779a67a2f62e3597c44af3932..9c4194ebed1ce1c78a40f24bcd579d40f752e63f 100644
--- a/indra/newview/lltoolbarview.h
+++ b/indra/newview/lltoolbarview.h
@@ -31,6 +31,7 @@
 #include "lluictrl.h"
 #include "lltoolbar.h"
 #include "llcommandmanager.h"
+#include "llinventory.h"
 
 class LLUICtrlFactory;
 
@@ -106,6 +107,7 @@ public:
 	static BOOL handleDragTool(S32 x, S32 y, const LLUUID& uuid, LLAssetType::EType type);
 	static BOOL handleDropTool(void* cargo_data, S32 x, S32 y, LLToolBar* toolbar);
 	static void resetDragTool(LLToolBarButton* toolbarButton);
+	LLInventoryObject* getDragItem();
 
 	bool isModified() const;
 	
@@ -129,6 +131,7 @@ private:
 	
 	bool				mDragStarted;
 	LLToolBarButton*	mDragToolbarButton;
+	LLInventoryObject*	mDragItem;
 	bool				mShowToolbars;
 };
 
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index c7ab934f9eba9563f937fe73581d7fb6b3a15ae0..4f4eef0f3d5421dc8cec30ffbccebc1e7a816e63 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -48,6 +48,7 @@
 #include "llpreviewnotecard.h"
 #include "llrootview.h"
 #include "llselectmgr.h"
+#include "lltoolbarview.h"
 #include "lltoolmgr.h"
 #include "lltooltip.h"
 #include "lltrans.h"
@@ -2528,7 +2529,7 @@ LLInventoryObject* LLToolDragAndDrop::locateInventory(
 	}
 	else if(mSource == SOURCE_VIEWER)
 	{
-		item = (LLViewerInventoryItem*)gClipboard.getSourceObject();
+		item = (LLViewerInventoryItem*)gToolBarView->getDragItem();
 	}
 	if(item) return item;
 	if(cat) return cat;
diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp
index 56b5bbf9420685965a9562183f83314dda4b2951..cad57690421a4c0b51fb1403fb4f6305cb884525 100644
--- a/indra/newview/llurllineeditorctrl.cpp
+++ b/indra/newview/llurllineeditorctrl.cpp
@@ -89,5 +89,5 @@ void LLURLLineEditor::copyEscapedURLToClipboard()
 	else // human-readable location
 		text_to_copy = utf8str_to_wstring(unescaped_text);
 		
-	gClipboard.copyFromString( text_to_copy );
+	LLClipboard::instance().copyToClipboard(text_to_copy, 0, text_to_copy.size());
 }
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp
index e621cf647ef777f1354e43fb0dceb9854c7a5304..4c59fd037195925fe656ac3d99a5085061381173 100644
--- a/indra/newview/llviewerassetstats.cpp
+++ b/indra/newview/llviewerassetstats.cpp
@@ -30,6 +30,7 @@
 #include "llregionhandle.h"
 
 #include "stdtypes.h"
+#include "llvoavatar.h"
 
 /*
  * Classes and utility functions for per-thread and per-region
@@ -126,6 +127,8 @@ LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionSta
 		mFPS.merge(src.mFPS);
 	}
 
+	// Avatar stats - data all comes from main thread, so leave alone.
+
 	// Requests
 	for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i)
 	{
@@ -133,6 +136,7 @@ LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionSta
 		mRequests[i].mDequeued.merge(src.mRequests[i].mDequeued);
 		mRequests[i].mResponse.merge(src.mRequests[i].mResponse);
 	}
+
 }
 
 
@@ -156,7 +160,9 @@ LLViewerAssetStats::LLViewerAssetStats()
 
 LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src)
 	: mRegionHandle(src.mRegionHandle),
-	  mResetTimestamp(src.mResetTimestamp)
+	  mResetTimestamp(src.mResetTimestamp),
+	  mPhaseStats(src.mPhaseStats),
+	  mAvatarRezStates(src.mAvatarRezStates)
 {
 	const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
 	for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
@@ -252,6 +258,17 @@ LLViewerAssetStats::recordFPS(F32 fps)
 	mCurRegionStats->mFPS.record(fps);
 }
 
+void
+LLViewerAssetStats::recordAvatarStats()
+{
+	std::vector<S32> rez_counts;
+	LLVOAvatar::getNearbyRezzedStats(rez_counts);
+	mAvatarRezStates = rez_counts;
+	mPhaseStats.clear();
+	mPhaseStats["cloud"] = LLViewerStats::PhaseMap::getPhaseStats("cloud");
+	mPhaseStats["cloud-or-gray"] = LLViewerStats::PhaseMap::getPhaseStats("cloud-or-gray");
+}
+
 LLSD
 LLViewerAssetStats::asLLSD(bool compact_output)
 {
@@ -282,6 +299,11 @@ LLViewerAssetStats::asLLSD(bool compact_output)
 	static const LLSD::String max_tag("max");
 	static const LLSD::String mean_tag("mean");
 
+	// Avatar sub-tags
+	static const LLSD::String avatar_tag("avatar");
+	static const LLSD::String avatar_nearby_tag("nearby");
+	static const LLSD::String avatar_phase_stats_tag("phase_stats");
+	
 	const duration_t now = LLViewerAssetStatsFF::get_timestamp();
 	mCurRegionStats->accumulateTime(now);
 
@@ -329,7 +351,6 @@ LLViewerAssetStats::asLLSD(bool compact_output)
 			slot[max_tag] = LLSD(F64(stats.mFPS.getMax()));
 			slot[mean_tag] = LLSD(F64(stats.mFPS.getMean()));
 		}
-
 		U32 grid_x(0), grid_y(0);
 		grid_from_region_handle(it->first, &grid_x, &grid_y);
 		reg_stat["grid_x"] = LLSD::Integer(grid_x);
@@ -341,6 +362,16 @@ LLViewerAssetStats::asLLSD(bool compact_output)
 	LLSD ret = LLSD::emptyMap();
 	ret["regions"] = regions;
 	ret["duration"] = LLSD::Real((now - mResetTimestamp) * 1.0e-6);
+	LLSD avatar_info;
+	avatar_info[avatar_nearby_tag] = LLSD::emptyArray();
+	for (S32 rez_stat=0; rez_stat < mAvatarRezStates.size(); ++rez_stat)
+	{
+		std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat);
+		avatar_info[avatar_nearby_tag][rez_status_name] = mAvatarRezStates[rez_stat];
+	}
+	avatar_info[avatar_phase_stats_tag]["cloud"] = mPhaseStats["cloud"].getData();
+	avatar_info[avatar_phase_stats_tag]["cloud-or-gray"] = mPhaseStats["cloud-or-gray"].getData();
+	ret[avatar_tag] = avatar_info;
 	
 	return ret;
 }
@@ -439,6 +470,14 @@ record_fps_main(F32 fps)
 	gViewerAssetStatsMain->recordFPS(fps);
 }
 
+void
+record_avatar_stats()
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	gViewerAssetStatsMain->recordAvatarStats();
+}
 
 // 'thread1' - should be for TextureFetch thread
 
diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h
index 73ec5974b296928b227a890c3ff2b69bb8a59c46..83197522306f2572ce64bec9e908354d1ab1f255 100644
--- a/indra/newview/llviewerassetstats.h
+++ b/indra/newview/llviewerassetstats.h
@@ -36,6 +36,7 @@
 #include "llviewerassetstorage.h"
 #include "llsimplestat.h"
 #include "llsd.h"
+#include "llvoavatar.h"
 
 /**
  * @class LLViewerAssetStats
@@ -181,6 +182,9 @@ public:
 	// Frames-Per-Second Samples
 	void recordFPS(F32 fps);
 
+	// Avatar-related statistics
+	void recordAvatarStats();
+
 	// Merge a source instance into a destination instance.  This is
 	// conceptually an 'operator+=()' method:
 	// - counts are added
@@ -252,6 +256,10 @@ protected:
 
 	// Time of last reset
 	duration_t mResetTimestamp;
+
+	// Nearby avatar stats
+	std::vector<S32> mAvatarRezStates;
+	LLViewerStats::phase_stats_t mPhaseStats;
 };
 
 
@@ -310,6 +318,7 @@ void record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_t
 
 void record_fps_main(F32 fps);
 
+void record_avatar_stats();
 
 /**
  * Region context, event and duration loggers for Thread 1.
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 093b84413a03cd4555d90178374df2a74e7d6e6d..f2712e7590880a38175ddcf9974dcdb13c1d86db 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -87,7 +87,6 @@ std::string gLastRunVersion;
 
 extern BOOL gResizeScreenTexture;
 extern BOOL gDebugGL;
-extern BOOL gAuditTexture;
 ////////////////////////////////////////////////////////////////////////////
 // Listeners
 
@@ -181,6 +180,16 @@ static bool handleReleaseGLBufferChanged(const LLSD& newvalue)
 	return true;
 }
 
+static bool handleLUTBufferChanged(const LLSD& newvalue)
+{
+	if (gPipeline.isInit())
+	{
+		gPipeline.releaseLUTBuffers();
+		gPipeline.createLUTBuffers();
+	}
+	return true;
+}
+
 static bool handleAnisotropicChanged(const LLSD& newvalue)
 {
 	LLImageGL::sGlobalUseAnisotropic = newvalue.asBoolean();
@@ -401,12 +410,6 @@ static bool handleRenderUseImpostorsChanged(const LLSD& newvalue)
 	return true;
 }
 
-static bool handleAuditTextureChanged(const LLSD& newvalue)
-{
-	gAuditTexture = newvalue.asBoolean();
-	return true;
-}
-
 static bool handleRenderDebugGLChanged(const LLSD& newvalue)
 {
 	gDebugGL = newvalue.asBoolean() || gDebugSession;
@@ -569,9 +572,9 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("RenderUIBuffer")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
 	gSavedSettings.getControl("RenderDepthOfField")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
 	gSavedSettings.getControl("RenderFSAASamples")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderSpecularResX")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderSpecularResY")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
-	gSavedSettings.getControl("RenderSpecularExponent")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
+	gSavedSettings.getControl("RenderSpecularResX")->getSignal()->connect(boost::bind(&handleLUTBufferChanged, _2));
+	gSavedSettings.getControl("RenderSpecularResY")->getSignal()->connect(boost::bind(&handleLUTBufferChanged, _2));
+	gSavedSettings.getControl("RenderSpecularExponent")->getSignal()->connect(boost::bind(&handleLUTBufferChanged, _2));
 	gSavedSettings.getControl("RenderAnisotropic")->getSignal()->connect(boost::bind(&handleAnisotropicChanged, _2));
 	gSavedSettings.getControl("RenderShadowResolutionScale")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
 	gSavedSettings.getControl("RenderGlow")->getSignal()->connect(boost::bind(&handleReleaseGLBufferChanged, _2));
@@ -608,7 +611,6 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("RenderDeferredSSAO")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2));
 	gSavedSettings.getControl("RenderPerformanceTest")->getSignal()->connect(boost::bind(&handleRenderPerfTestChanged, _2));
 	gSavedSettings.getControl("TextureMemory")->getSignal()->connect(boost::bind(&handleVideoMemoryChanged, _2));
-	gSavedSettings.getControl("AuditTexture")->getSignal()->connect(boost::bind(&handleAuditTextureChanged, _2));
 	gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&handleChatFontSizeChanged, _2));
 	gSavedSettings.getControl("ChatPersistTime")->getSignal()->connect(boost::bind(&handleChatPersistTimeChanged, _2));
 	gSavedSettings.getControl("ConsoleMaxLines")->getSignal()->connect(boost::bind(&handleConsoleMaxLinesChanged, _2));
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 7fdaac68c8753b048e3f36e3a163cfb67e4f3e64..d0e0d0d8264363a772ca97d1cbe6fc01e23ee5dd 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -103,6 +103,7 @@
 #include "llfloatertelehub.h"
 #include "llfloatertestinspectors.h"
 #include "llfloatertestlistview.h"
+#include "llfloatertexturefetchdebugger.h"
 #include "llfloatertools.h"
 #include "llfloatertos.h"
 #include "llfloatertopobjects.h"
@@ -227,6 +228,11 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterLandHoldings>);
 	
 	LLFloaterReg::add("mem_leaking", "floater_mem_leaking.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMemLeak>);
+
+	if(gSavedSettings.getBOOL("TextureFetchDebuggerEnabled"))
+	{
+		LLFloaterReg::add("tex_fetch_debugger", "floater_texture_fetch_debugger.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTextureFetchDebugger>);
+	}
 	LLFloaterReg::add("media_settings", "floater_media_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMediaSettings>);	
 	LLFloaterReg::add("message_critical", "floater_critical.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
 	LLFloaterReg::add("message_tos", "floater_tos.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index 45ca23cdfe2d3b19b49dd1f1410d99902731a57b..b47a41c44c9d891acafafd097d90388a8499b550 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -342,8 +342,8 @@ void LLViewerInventoryItem::cloneViewerItem(LLPointer<LLViewerInventoryItem>& ne
 
 void LLViewerInventoryItem::removeFromServer()
 {
-	llinfos << "Removing inventory item " << mUUID << " from server."
-			<< llendl;
+	lldebugs << "Removing inventory item " << mUUID << " from server."
+			 << llendl;
 
 	LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
 	gInventory.accountForUpdate(up);
diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp
index e052e373931e81044340f01d32d53bee68a09696..f029ae5302f040785f4390eb5a8c723b6e4f90f9 100644
--- a/indra/newview/llviewerjointmesh.cpp
+++ b/indra/newview/llviewerjointmesh.cpp
@@ -576,7 +576,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass, BOOL is_dummy)
 		{
 			old_mode = mTexture->getAddressMode();
 		}
-		gGL.getTexUnit(diffuse_channel)->bind(mTexture.get());
 		gGL.getTexUnit(diffuse_channel)->bind(mTexture);
 		gGL.getTexUnit(diffuse_channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
 	}
diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h
index 0191f0cae8f8a6b06719f6ad6dab44746cd39157..dd5dae1dc1e997cd330ffcfbf9c6aa03e0eedbdd 100644
--- a/indra/newview/llviewerjointmesh.h
+++ b/indra/newview/llviewerjointmesh.h
@@ -61,6 +61,7 @@ public:
 //-----------------------------------------------------------------------------
 class LLViewerJointMesh : public LLViewerJoint
 {
+	friend class LLVOAvatar;
 protected:
 	LLColor4					mColor;			// color value
 // 	LLColor4					mSpecular;		// specular color (always white for now)
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index b944daa8fe1db4aded43e25a9220d65c359f6f6a..34e916fec004a160db599876bbfcc7f488b47b5a 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -25,6 +25,11 @@
  */
 
 #include "llviewerprecompiledheaders.h"
+
+#ifdef INCLUDE_VLD
+#include "vld.h"
+#endif
+
 #include "llviewermenu.h" 
 
 // linden library includes
@@ -215,7 +220,7 @@ void near_sit_down_point(BOOL success, void *);
 
 
 void velocity_interpolate( void* );
-
+void handle_visual_leak_detector_toggle(void*);
 void handle_rebake_textures(void*);
 BOOL check_admin_override(void*);
 void handle_admin_override_toggle(void*);
@@ -512,14 +517,6 @@ class LLAdvancedToggleConsole : public view_listener_t
 		{
 			toggle_visibility( (void*)static_cast<LLUICtrl*>(gDebugView->mDebugConsolep));
 		}
-		else if (gTextureSizeView && "texture size" == console_type)
-		{
-			toggle_visibility( (void*)gTextureSizeView );
-		}
-		else if (gTextureCategoryView && "texture category" == console_type)
-		{
-			toggle_visibility( (void*)gTextureCategoryView );
-		}
 		else if ("fast timers" == console_type)
 		{
 			LLFloaterReg::toggleInstance("fast_timers");
@@ -552,14 +549,6 @@ class LLAdvancedCheckConsole : public view_listener_t
 		{
 			new_value = get_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) );
 		}
-		else if (gTextureSizeView && "texture size" == console_type)
-		{
-			new_value = get_visibility( (void*)gTextureSizeView );
-		}
-		else if (gTextureCategoryView && "texture category" == console_type)
-		{
-			new_value = get_visibility( (void*)gTextureCategoryView );
-		}
 		else if ("fast timers" == console_type)
 		{
 			new_value = LLFloaterReg::instanceVisible("fast_timers");
@@ -862,6 +851,73 @@ class LLAdvancedCheckFeature : public view_listener_t
 }
 };
 
+class LLAdvancedCheckDisplayTextureDensity : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		std::string mode = userdata.asString();
+		if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY))
+		{
+			return mode == "none";
+		}
+		if (mode == "current")
+		{
+			return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_CURRENT;
+		}
+		else if (mode == "desired")
+		{
+			return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_DESIRED;
+		}
+		else if (mode == "full")
+		{
+			return LLViewerTexture::sDebugTexelsMode == LLViewerTexture::DEBUG_TEXELS_FULL;
+		}
+		return false;
+	}
+};
+
+class LLAdvancedSetDisplayTextureDensity : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		std::string mode = userdata.asString();
+		if (mode == "none")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == TRUE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
+		}
+		else if (mode == "current")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_CURRENT;
+		}
+		else if (mode == "desired")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			gPipeline.setRenderDebugFeatureControl(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY, true);
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_DESIRED;
+		}
+		else if (mode == "full")
+		{
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXEL_DENSITY) == FALSE) 
+			{
+				gPipeline.toggleRenderDebug((void*)LLPipeline::RENDER_DEBUG_TEXEL_DENSITY);
+			}
+			LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_FULL;
+		}
+		return true;
+	}
+};
+
 
 //////////////////
 // INFO DISPLAY //
@@ -976,6 +1032,10 @@ U32 info_display_from_string(std::string info_display)
 	{
 		return LLPipeline::RENDER_DEBUG_WIND_VECTORS;
 	}
+	else if ("texel density" == info_display)
+	{
+		return LLPipeline::RENDER_DEBUG_TEXEL_DENSITY;
+	}
 	else
 	{
 		return 0;
@@ -2020,6 +2080,15 @@ class LLAdvancedToggleViewAdminOptions : public view_listener_t
 	}
 };
 
+class LLAdvancedToggleVisualLeakDetector : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		handle_visual_leak_detector_toggle(NULL);
+		return true;
+	}
+};
+
 class LLAdvancedCheckViewAdminOptions : public view_listener_t
 {
 	bool handleEvent(const LLSD& userdata)
@@ -2233,6 +2302,14 @@ class LLDevelopSetLoggingLevel : public view_listener_t
 	}
 };
 
+class LLDevelopTextureFetchDebugger : public view_listener_t
+{
+	bool handleEvent(const LLSD& userdata)
+	{
+		return gSavedSettings.getBOOL("TextureFetchDebuggerEnabled");
+	}
+};
+
 //////////////////
 // ADMIN MENU   //
 //////////////////
@@ -3446,6 +3523,35 @@ void handle_admin_override_toggle(void*)
 	show_debug_menus();
 }
 
+void handle_visual_leak_detector_toggle(void*)
+{
+	static bool vld_enabled = false;
+
+	if ( vld_enabled )
+	{
+#ifdef INCLUDE_VLD
+		// only works for debug builds (hard coded into vld.h)
+#ifdef _DEBUG
+		// start with Visual Leak Detector turned off
+		VLDDisable();
+#endif // _DEBUG
+#endif // INCLUDE_VLD
+		vld_enabled = false;
+	}
+	else
+	{
+#ifdef INCLUDE_VLD
+		// only works for debug builds (hard coded into vld.h)
+	#ifdef _DEBUG
+		// start with Visual Leak Detector turned off
+		VLDEnable();
+	#endif // _DEBUG
+#endif // INCLUDE_VLD
+
+		vld_enabled = true;
+	};
+}
+
 void handle_god_mode(void*)
 {
 	gAgent.requestEnterGodMode();
@@ -6518,31 +6624,37 @@ class LLToolsSelectedScriptAction : public view_listener_t
 		std::string action = userdata.asString();
 		bool mono = false;
 		std::string msg, name;
+		std::string title;
 		if (action == "compile mono")
 		{
 			name = "compile_queue";
 			mono = true;
 			msg = "Recompile";
+			title = LLTrans::getString("CompileQueueTitle");
 		}
 		if (action == "compile lsl")
 		{
 			name = "compile_queue";
 			msg = "Recompile";
+			title = LLTrans::getString("CompileQueueTitle");
 		}
 		else if (action == "reset")
 		{
 			name = "reset_queue";
 			msg = "Reset";
+			title = LLTrans::getString("ResetQueueTitle");
 		}
 		else if (action == "start")
 		{
 			name = "start_queue";
 			msg = "SetRunning";
+			title = LLTrans::getString("RunQueueTitle");
 		}
 		else if (action == "stop")
 		{
 			name = "stop_queue";
 			msg = "SetRunningNot";
+			title = LLTrans::getString("NotRunQueueTitle");
 		}
 		LLUUID id; id.generate();
 		
@@ -6551,6 +6663,7 @@ class LLToolsSelectedScriptAction : public view_listener_t
 		{
 			queue->setMono(mono);
 			queue_actions(queue, msg);
+			queue->setTitle(title);
 		}
 		else
 		{
@@ -8119,6 +8232,10 @@ void initialize_menus()
 	//// Advanced > Render > Features
 	view_listener_t::addMenu(new LLAdvancedToggleFeature(), "Advanced.ToggleFeature");
 	view_listener_t::addMenu(new LLAdvancedCheckFeature(), "Advanced.CheckFeature");
+
+	view_listener_t::addMenu(new LLAdvancedCheckDisplayTextureDensity(), "Advanced.CheckDisplayTextureDensity");
+	view_listener_t::addMenu(new LLAdvancedSetDisplayTextureDensity(), "Advanced.SetDisplayTextureDensity");
+
 	// Advanced > Render > Info Displays
 	view_listener_t::addMenu(new LLAdvancedToggleInfoDisplay(), "Advanced.ToggleInfoDisplay");
 	view_listener_t::addMenu(new LLAdvancedCheckInfoDisplay(), "Advanced.CheckInfoDisplay");
@@ -8242,12 +8359,17 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions");
 	view_listener_t::addMenu(new LLAdvancedToggleViewAdminOptions(), "Advanced.ToggleViewAdminOptions");
 	view_listener_t::addMenu(new LLAdvancedCheckViewAdminOptions(), "Advanced.CheckViewAdminOptions");
+	view_listener_t::addMenu(new LLAdvancedToggleVisualLeakDetector(), "Advanced.ToggleVisualLeakDetector");
+
 	view_listener_t::addMenu(new LLAdvancedRequestAdminStatus(), "Advanced.RequestAdminStatus");
 	view_listener_t::addMenu(new LLAdvancedLeaveAdminStatus(), "Advanced.LeaveAdminStatus");
 
 	// Develop >Set logging level
 	view_listener_t::addMenu(new LLDevelopCheckLoggingLevel(), "Develop.CheckLoggingLevel");
 	view_listener_t::addMenu(new LLDevelopSetLoggingLevel(), "Develop.SetLoggingLevel");
+	
+	//Develop (Texture Fetch Debug Console)
+	view_listener_t::addMenu(new LLDevelopTextureFetchDebugger(), "Develop.SetTexFetchDebugger");
 
 	// Admin >Object
 	view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy");
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 5afb52db1122e7e284e2782540c3c8f800648647..a9bff67f40fed8589d8440abaec20e189d90e99f 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -156,7 +156,8 @@ const std::string SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] =
 		"AddAndRemoveJoints",
 		"ChangePermissions",
 		"TrackYourCamera",
-		"ControlYourCamera"
+		"ControlYourCamera",
+		"TeleportYourAgent"
 	};
 
 const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] = 
@@ -171,7 +172,8 @@ const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] =
 	FALSE,	// AddAndRemoveJoints
 	FALSE,	// ChangePermissions
 	FALSE,	// TrackYourCamera,
-	FALSE	// ControlYourCamera
+	FALSE,	// ControlYourCamera
+	FALSE	// TeleportYourAgent
 };
 
 bool friendship_offer_callback(const LLSD& notification, const LLSD& response)
@@ -1065,7 +1067,9 @@ public:
 		// If we now try to remove the inventory item, it will cause a nested
 		// notifyObservers() call, which won't work.
 		// So defer moving the item to trash until viewer gets idle (in a moment).
-		LLAppViewer::instance()->addOnIdleCallback(boost::bind(&LLInventoryModel::removeItem, &gInventory, mObjectID));
+		// Use removeObject() rather than removeItem() because at this level,
+		// the object could be either an item or a folder.
+		LLAppViewer::instance()->addOnIdleCallback(boost::bind(&LLInventoryModel::removeObject, &gInventory, mObjectID));
 		gInventory.removeObserver(this);
 		delete this;
 	}
@@ -5972,16 +5976,21 @@ void process_script_question(LLMessageSystem *msg, void **user_data)
 		args["OBJECTNAME"] = object_name;
 		args["NAME"] = LLCacheName::cleanFullName(owner_name);
 
+		BOOL has_not_only_debit = questions ^ LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_DEBIT];
 		// check the received permission flags against each permission
 		for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++)
 		{
 			if (questions & LSCRIPTRunTimePermissionBits[i])
 			{
 				count++;
-				script_question += "    " + LLTrans::getString(SCRIPT_QUESTIONS[i]) + "\n";
 
 				// check whether permission question should cause special caution dialog
 				caution |= (SCRIPT_QUESTION_IS_CAUTION[i]);
+
+				if (("ScriptTakeMoney" ==  SCRIPT_QUESTIONS[i]) && has_not_only_debit)
+					continue;
+
+				script_question += "    " + LLTrans::getString(SCRIPT_QUESTIONS[i]) + "\n";
 			}
 		}
 		args["QUESTIONS"] = script_question;
@@ -5997,6 +6006,10 @@ void process_script_question(LLMessageSystem *msg, void **user_data)
 		// check whether cautions are even enabled or not
 		if (gSavedSettings.getBOOL("PermissionsCautionEnabled"))
 		{
+			if (caution)
+			{
+				args["FOOTERTEXT"] = (count > 1) ? LLTrans::getString("AdditionalPermissionsRequestHeader") + "\n\n" + script_question : "";
+			}
 			// display the caution permissions prompt
 			LLNotificationsUtil::add(caution ? "ScriptQuestionCaution" : "ScriptQuestion", args, payload);
 		}
@@ -6845,12 +6858,14 @@ void process_covenant_reply(LLMessageSystem* msg, void**)
 
 	LLPanelEstateCovenant::updateEstateName(estate_name);
 	LLPanelLandCovenant::updateEstateName(estate_name);
+	LLPanelEstateInfo::updateEstateName(estate_name);
 	LLFloaterBuyLand::updateEstateName(estate_name);
 
 	std::string owner_name =
 		LLSLURL("agent", estate_owner_id, "inspect").getSLURLString();
 	LLPanelEstateCovenant::updateEstateOwnerName(owner_name);
 	LLPanelLandCovenant::updateEstateOwnerName(owner_name);
+	LLPanelEstateInfo::updateEstateOwnerName(owner_name);
 	LLFloaterBuyLand::updateEstateOwnerName(owner_name);
 
 	LLPanelPlaceProfile* panel = LLFloaterSidePanelContainer::getPanel<LLPanelPlaceProfile>("places", "panel_place_profile");
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index e590f29a9a3f8b798cf277e26725fbf73b6a6977..cd300accb7f74070ea0f940d7d7d459912393d25 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -877,6 +877,13 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 	LLMemType mt(LLMemType::MTYPE_OBJECT);
 	U32 retval = 0x0;
 	
+	// If region is removed from the list it is also deleted.
+	if (!LLWorld::instance().isRegionListed(mRegionp))
+	{
+		llwarns << "Updating object in an invalid region" << llendl;
+		return retval;
+	}
+
 	// Coordinates of objects on simulators are region-local.
 	U64 region_handle;
 	mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
@@ -3478,7 +3485,8 @@ LLNameValue *LLViewerObject::getNVPair(const std::string& name) const
 
 void LLViewerObject::updatePositionCaches() const
 {
-	if(mRegionp)
+	// If region is removed from the list it is also deleted.
+	if(mRegionp && LLWorld::instance().isRegionListed(mRegionp))
 	{
 		if (!isRoot())
 		{
@@ -3495,7 +3503,8 @@ void LLViewerObject::updatePositionCaches() const
 
 const LLVector3d LLViewerObject::getPositionGlobal() const
 {	
-	if(mRegionp)
+	// If region is removed from the list it is also deleted.
+	if(mRegionp && LLWorld::instance().isRegionListed(mRegionp))
 	{
 		LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion());
 
@@ -3514,7 +3523,8 @@ const LLVector3d LLViewerObject::getPositionGlobal() const
 
 const LLVector3 &LLViewerObject::getPositionAgent() const
 {
-	if (mRegionp)
+	// If region is removed from the list it is also deleted.
+	if(mRegionp && LLWorld::instance().isRegionListed(mRegionp))
 	{
 		if (mDrawable.notNull() && (!mDrawable->isRoot() && getParent()))
 		{
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 6912faa9ec93bc5420356d13f8aa9aaae56e57b6..54ccfb9aae4d44069429441866238425b30e0094 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -91,8 +91,9 @@ extern LLPipeline	gPipeline;
 
 // Statics for object lookup tables.
 U32						LLViewerObjectList::sSimulatorMachineIndex = 1; // Not zero deliberately, to speed up index check.
-std::map<U64, U32>			LLViewerObjectList::sIPAndPortToIndex;
+std::map<U64, U32>		LLViewerObjectList::sIPAndPortToIndex;
 std::map<U64, LLUUID>	LLViewerObjectList::sIndexAndLocalIDToUUID;
+LLStat					LLViewerObjectList::sCacheHitRate("object_cache_hits", 128);
 
 LLViewerObjectList::LLViewerObjectList()
 {
@@ -542,6 +543,8 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
 			}
 			justCreated = TRUE;
 			mNumNewObjects++;
+			sCacheHitRate.addValue(cached ? 100.f : 0.f);
+
 		}
 
 
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index c5f2a2c1eee1a229cfcd23ea291629ee022b9d5d..64925f46aeacea27376e964bd3587eb326d5f1c6 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -192,6 +192,8 @@ protected:
 	std::vector<OrphanInfo> mOrphanChildren;	// UUID's of orphaned objects
 	S32 mNumOrphans;
 
+	static LLStat sCacheHitRate;
+
 	typedef std::vector<LLPointer<LLViewerObject> > vobj_list_t;
 
 	vobj_list_t mObjects;
diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h
index f738b84bb9a140c058b313891be86b9a17fe55a5..6c8a827ba3f9e107e6db93555773f4ea341fa580 100644
--- a/indra/newview/llviewerprecompiledheaders.h
+++ b/indra/newview/llviewerprecompiledheaders.h
@@ -57,6 +57,8 @@
 #include "lldeleteutils.h"
 #include "imageids.h"
 #include "indra_constants.h"
+#include "llinitparam.h"
+
 //#include "linden_common.h"
 //#include "llpreprocessor.h"
 #include "llallocator.h"
@@ -124,7 +126,5 @@
 // Library includes from llmessage project
 #include "llcachename.h"
 
-// Library includes from llxuixml
-#include "llinitparam.h"
 
 #endif
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index c88122f22c9078efd0a46fc00a388cb5444e59cf..497e95c5e3712286b376b608cc666fac4a6df5b4 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -860,3 +860,110 @@ void send_stats()
 	LLHTTPClient::post(url, body, new ViewerStatsResponder());
 }
 
+LLFrameTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name)
+{
+	phase_map_t::iterator iter = mPhaseMap.find(phase_name);
+	if (iter == mPhaseMap.end())
+	{
+		LLFrameTimer timer;
+		mPhaseMap[phase_name] = timer;
+	}
+	LLFrameTimer& timer = mPhaseMap[phase_name];
+	return timer;
+}
+
+void LLViewerStats::PhaseMap::startPhase(const std::string& phase_name)
+{
+	LLFrameTimer& timer = getPhaseTimer(phase_name);
+	lldebugs << "startPhase " << phase_name << llendl;
+	timer.unpause();
+}
+
+void LLViewerStats::PhaseMap::stopPhase(const std::string& phase_name)
+{
+	phase_map_t::iterator iter = mPhaseMap.find(phase_name);
+	if (iter != mPhaseMap.end())
+	{
+		if (iter->second.getStarted())
+		{
+			// Going from started to paused state - record stats.
+			recordPhaseStat(phase_name,iter->second.getElapsedTimeF32());
+		}
+		lldebugs << "stopPhase " << phase_name << llendl;
+		iter->second.pause();
+	}
+	else
+	{
+		lldebugs << "stopPhase " << phase_name << " is not started, no-op" << llendl;
+	}
+}
+
+void LLViewerStats::PhaseMap::stopAllPhases()
+{
+	for (phase_map_t::iterator iter = mPhaseMap.begin();
+		 iter != mPhaseMap.end(); ++iter)
+	{
+		const std::string& phase_name = iter->first;
+		if (iter->second.getStarted())
+		{
+			// Going from started to paused state - record stats.
+			recordPhaseStat(phase_name,iter->second.getElapsedTimeF32());
+		}
+		lldebugs << "stopPhase (all) " << phase_name << llendl;
+		iter->second.pause();
+	}
+}
+
+void LLViewerStats::PhaseMap::clearPhases()
+{
+	lldebugs << "clearPhases" << llendl;
+
+	mPhaseMap.clear();
+}
+
+LLSD LLViewerStats::PhaseMap::dumpPhases()
+{
+	LLSD result;
+	for (phase_map_t::iterator iter = mPhaseMap.begin(); iter != mPhaseMap.end(); ++iter)
+	{
+		const std::string& phase_name = iter->first;
+		result[phase_name]["completed"] = !(iter->second.getStarted());
+		result[phase_name]["elapsed"] = iter->second.getElapsedTimeF32();
+#if 0 // global stats for each phase seem like overkill here
+		phase_stats_t::iterator stats_iter = sPhaseStats.find(phase_name);
+		if (stats_iter != sPhaseStats.end())
+		{
+			result[phase_name]["stats"] = stats_iter->second.getData();
+		}
+#endif
+	}
+	return result;
+}
+
+// static initializer
+//static
+LLViewerStats::phase_stats_t LLViewerStats::PhaseMap::sStats;
+
+LLViewerStats::PhaseMap::PhaseMap()
+{
+}
+
+// static
+LLViewerStats::StatsAccumulator& LLViewerStats::PhaseMap::getPhaseStats(const std::string& phase_name)
+{
+	phase_stats_t::iterator it = sStats.find(phase_name);
+	if (it == sStats.end())
+	{
+		LLViewerStats::StatsAccumulator new_stats;
+		sStats[phase_name] = new_stats;
+	}
+	return sStats[phase_name];
+}
+
+// static
+void LLViewerStats::PhaseMap::recordPhaseStat(const std::string& phase_name, F32 value)
+{
+	LLViewerStats::StatsAccumulator& stats = getPhaseStats(phase_name);
+	stats.push(value);
+}
+
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index f91a1241fe584cd5770bf009d19aa7ee67ff16f8..750d963f69e6792310034b5c5fe85e5f4084a616 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -244,7 +244,7 @@ public:
 		inline F32 getStdDev() const
 		{
 			const F32 mean = getMean();
-			return (mCount == 0) ? 0.f : sqrt( mSumOfSquares/mCount - (mean * mean) );
+			return (mCount < 2) ? 0.f : sqrt(llmax(0.f,mSumOfSquares/mCount - (mean * mean)));
 		}
 		
 		inline U32 getCount() const
@@ -274,7 +274,28 @@ public:
 	};
 
 	StatsAccumulator mAgentPositionSnaps;
-	
+
+	// Phase tracking (originally put in for avatar rezzing), tracking
+	// progress of active/completed phases for activities like outfit changing.
+	typedef std::map<std::string,LLFrameTimer>	phase_map_t;
+	typedef std::map<std::string,StatsAccumulator>	phase_stats_t;
+	class PhaseMap
+	{
+	private:
+		phase_map_t mPhaseMap;
+		static phase_stats_t sStats;
+	public:
+		PhaseMap();
+		LLFrameTimer& 	getPhaseTimer(const std::string& phase_name);
+		void			startPhase(const std::string& phase_name);
+		void			stopPhase(const std::string& phase_name);
+		void			stopAllPhases();
+		void			clearPhases();
+		LLSD			dumpPhases();
+		static StatsAccumulator& getPhaseStats(const std::string& phase_name);
+		static void recordPhaseStat(const std::string& phase_name, F32 value);
+	};
+
 private:
 	F64	mStats[ST_COUNT];
 
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 51737149ef913878be5a08c4beedf511f1fce0c4..d844aeb12a407958b42abdff73780dcfd4d4c5be 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -67,6 +67,7 @@
 // statics
 LLPointer<LLViewerTexture>        LLViewerTexture::sNullImagep = NULL;
 LLPointer<LLViewerTexture>        LLViewerTexture::sBlackImagep = NULL;
+LLPointer<LLViewerTexture>        LLViewerTexture::sCheckerBoardImagep = NULL;
 LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sMissingAssetImagep = NULL;
 LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sWhiteImagep = NULL;
 LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultImagep = NULL;
@@ -87,6 +88,7 @@ S32 LLViewerTexture::sMaxBoundTextureMemInMegaBytes = 0;
 S32 LLViewerTexture::sMaxTotalTextureMemInMegaBytes = 0;
 S32 LLViewerTexture::sMaxDesiredTextureMemInBytes = 0 ;
 S8  LLViewerTexture::sCameraMovingDiscardBias = 0 ;
+F32 LLViewerTexture::sCameraMovingBias = 0.0f ;
 S32 LLViewerTexture::sMaxSculptRez = 128 ; //max sculpt image size
 const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64 ;
 const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez ;
@@ -96,6 +98,9 @@ S32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ;
 BOOL LLViewerTexture::sFreezeImageScalingDown = FALSE ;
 F32 LLViewerTexture::sCurrentTime = 0.0f ;
 BOOL LLViewerTexture::sUseTextureAtlas        = FALSE ;
+F32  LLViewerTexture::sTexelPixelRatio = 1.0f;
+
+LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
 
 const F32 desired_discard_bias_min = -2.0f; // -max number of levels to improve image quality by
 const F32 desired_discard_bias_max = (F32)MAX_DISCARD_LEVEL; // max number of levels to reduce image quality by
@@ -175,7 +180,12 @@ LLViewerTexture*  LLViewerTextureManager::findTexture(const LLUUID& id)
 	}
 	return tex ;
 }
-		
+
+LLViewerFetchedTexture*  LLViewerTextureManager::findFetchedTexture(const LLUUID& id) 
+{
+	return gTextureList.findImage(id);
+}
+
 LLViewerMediaTexture* LLViewerTextureManager::findMediaTexture(const LLUUID &media_id)
 {
 	return LLViewerMediaTexture::findMediaTexture(media_id) ;	
@@ -347,6 +357,21 @@ void LLViewerTextureManager::init()
  	LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, TRUE, LLViewerTexture::BOOST_UI);
 	LLViewerFetchedTexture::sSmokeImagep->setNoDelete() ;
 
+	image_raw = new LLImageRaw(32,32,3);
+	data = image_raw->getData();
+
+	for (S32 i = 0; i < (32*32*3); i+=3)
+	{
+		S32 x = (i % (32*3)) / (3*16);
+		S32 y = i / (32*3*16);
+		U8 color = ((x + y) % 2) * 255;
+		data[i] = color;
+		data[i+1] = color;
+		data[i+2] = color;
+	}
+
+	LLViewerTexture::sCheckerBoardImagep = LLViewerTextureManager::getLocalTexture(image_raw.get(), TRUE);
+
 	LLViewerTexture::initClass() ;
 
 	if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName))
@@ -367,6 +392,7 @@ void LLViewerTextureManager::cleanup()
 	LLImageGL::sDefaultGLTexture = NULL ;
 	LLViewerTexture::sNullImagep = NULL;
 	LLViewerTexture::sBlackImagep = NULL;
+	LLViewerTexture::sCheckerBoardImagep = NULL;
 	LLViewerFetchedTexture::sDefaultImagep = NULL;	
 	LLViewerFetchedTexture::sSmokeImagep = NULL;
 	LLViewerFetchedTexture::sMissingAssetImagep = NULL;
@@ -383,11 +409,7 @@ void LLViewerTextureManager::cleanup()
 void LLViewerTexture::initClass()
 {
 	LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ;
-
-	if(gAuditTexture)
-	{
-		LLImageGL::setHighlightTexture(LLViewerTexture::OTHER) ;	
-	}
+	sTexelPixelRatio = gSavedSettings.getF32("TexelPixelRatio");
 }
 
 // static
@@ -534,7 +556,8 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity
 	
 	F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ;
 	F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed();
-	sCameraMovingDiscardBias = (S8)llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1) ;
+	sCameraMovingBias = llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1);
+	sCameraMovingDiscardBias = (S8)(sCameraMovingBias);
 
 	LLViewerTexture::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < 0.75f * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) &&
 				(BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < 0.75f * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ;
@@ -655,10 +678,6 @@ void LLViewerTexture::setBoostLevel(S32 level)
 		{
 			setNoDelete() ;		
 		}
-		if(gAuditTexture)
-		{
-			setCategory(mBoostLevel);
-		}
 	}
 }
 
@@ -712,6 +731,7 @@ void LLViewerTexture::addTextureStats(F32 virtual_size, BOOL needs_gltexture) co
 		mNeedsGLTexture = TRUE ;
 	}
 
+	virtual_size *= sTexelPixelRatio;
 	if(!mMaxVirtualSizeResetCounter)
 	{
 		//flag to reset the values because the old values are used.
@@ -1287,6 +1307,7 @@ void LLViewerFetchedTexture::cleanup()
 	mCachedRawDiscardLevel = -1 ;
 	mCachedRawImageReady = FALSE ;
 	mSavedRawImage = NULL ;
+	mSavedRawDiscardLevel = -1;
 }
 
 void LLViewerFetchedTexture::setForSculpt()
@@ -1880,6 +1901,8 @@ S32 LLViewerFetchedTexture::getCurrentDiscardLevelForFetching()
 bool LLViewerFetchedTexture::updateFetch()
 {
 	static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
+	static LLCachedControl<F32>  sCameraMotionThreshold(gSavedSettings,"TextureCameraMotionThreshold");
+	static LLCachedControl<S32>  sCameraMotionBoost(gSavedSettings,"TextureCameraMotionBoost");
 	if(textures_decode_disabled)
 	{
 		return false ;
@@ -2042,18 +2065,24 @@ bool LLViewerFetchedTexture::updateFetch()
 	//	make_request = false;
 	//}
 	
-	if(make_request)
+	if (make_request)
 	{
-		//load the texture progressively.
+		// Load the texture progressively: we try not to rush to the desired discard too fast.
+		// If the camera is not moving, we do not tweak the discard level notch by notch but go to the desired discard with larger boosted steps
+		// This mitigates the "textures stay blurry" problem when loading while not killing the texture memory while moving around
 		S32 delta_level = (mBoostLevel > LLViewerTexture::BOOST_NONE) ? 2 : 1 ; 
-		if(current_discard < 0)
+		if (current_discard < 0)
 		{
 			desired_discard = llmax(desired_discard, getMaxDiscardLevel() - delta_level);
 		}
-		else
+		else if (LLViewerTexture::sCameraMovingBias < sCameraMotionThreshold)
 		{
-			desired_discard = llmax(desired_discard, current_discard - delta_level);
+			desired_discard = llmax(desired_discard, current_discard - sCameraMotionBoost);
 		}
+        else
+        {
+			desired_discard = llmax(desired_discard, current_discard - delta_level);
+        }
 
 		if (mIsFetching)
 		{
@@ -2121,6 +2150,30 @@ bool LLViewerFetchedTexture::updateFetch()
 	return mIsFetching ? true : false;
 }
 
+void LLViewerFetchedTexture::clearFetchedResults()
+{
+	llassert_always(!mNeedsCreateTexture && !mIsFetching);
+	
+	cleanup();
+	destroyGLTexture();
+
+	if(getDiscardLevel() >= 0) //sculpty texture, force to invalidate
+	{
+		mGLTexturep->forceToInvalidateGLTexture();
+	}
+}
+
+void LLViewerFetchedTexture::forceToDeleteRequest()
+{
+	if (mHasFetcher)
+	{
+		LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
+		mHasFetcher = FALSE;
+		mIsFetching = FALSE ;
+		resetTextureStats();
+	}
+}
+
 void LLViewerFetchedTexture::setIsMissingAsset()
 {
 	if (mUrl.empty())
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index af8afef2031716a7212d5cb709561901b39d5294..f1105c3705e652294581a7391e3dae0d82a29aa9 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -139,6 +139,7 @@ public:
 		OTHER,
 		MAX_GL_IMAGE_CATEGORY
 	};
+
 	static S32 getTotalNumOfCategories() ;
 	static S32 getIndexFromCategory(S32 category) ;
 	static S32 getCategoryFromIndex(S32 index) ;
@@ -312,6 +313,7 @@ protected:
 	} LLGLTextureState;
 	LLGLTextureState  mTextureState ;
 
+	static F32 sTexelPixelRatio;
 public:
 	static const U32 sCurrentFileVersion;	
 	static S32 sImageCount;
@@ -326,6 +328,7 @@ public:
 	static S32 sMaxTotalTextureMemInMegaBytes;
 	static S32 sMaxDesiredTextureMemInBytes ;
 	static S8  sCameraMovingDiscardBias;
+	static F32 sCameraMovingBias;
 	static S32 sMaxSculptRez ;
 	static S32 sMinLargeImageSize ;
 	static S32 sMaxSmallImageSize ;
@@ -333,8 +336,19 @@ public:
 	static F32  sCurrentTime ;
 	static BOOL sUseTextureAtlas ;
 
+	enum EDebugTexels
+	{
+		DEBUG_TEXELS_OFF,
+		DEBUG_TEXELS_CURRENT,
+		DEBUG_TEXELS_DESIRED,
+		DEBUG_TEXELS_FULL
+	};
+
+	static EDebugTexels sDebugTexelsMode;
+
 	static LLPointer<LLViewerTexture> sNullImagep; // Null texture for non-textured objects.
 	static LLPointer<LLViewerTexture> sBlackImagep;	// Texture to show NOTHING (pure black)
+	static LLPointer<LLViewerTexture> sCheckerBoardImagep;	// Texture to show NOTHING (pure black)
 };
 
 
@@ -423,6 +437,8 @@ public:
 
 	bool updateFetch();
 	
+	void clearFetchedResults(); //clear all fetched results, for debug use.
+
 	// Override the computation of discard levels if we know the exact output
 	// size of the image.  Used for UI textures to not decode, even if we have
 	// more data.
@@ -481,6 +497,7 @@ public:
 	BOOL        hasFetcher() const { return mHasFetcher;}
 	void        setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;}
 
+	void        forceToDeleteRequest();
 protected:
 	/*virtual*/ void switchToCachedImage();
 	S32 getCurrentDiscardLevelForFetching() ;
@@ -690,6 +707,7 @@ public:
 	//"find-texture" just check if the texture exists, if yes, return it, otherwise return null.
 	//
 	static LLViewerTexture*           findTexture(const LLUUID& id) ;
+	static LLViewerFetchedTexture*    findFetchedTexture(const LLUUID& id) ;
 	static LLViewerMediaTexture*      findMediaTexture(const LLUUID& id) ;
 	
 	static LLViewerMediaTexture*      createMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index f1555388a29f1225564e52f2dea2fb73966e547e..528e0080b78a61987fff9243925959432d885566 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -58,6 +58,7 @@
 #include "pipeline.h"
 #include "llappviewer.h"
 #include "llxuiparser.h"
+#include "llagent.h"
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -597,6 +598,12 @@ static LLFastTimer::DeclareTimer FTM_IMAGE_STATS("Stats");
 
 void LLViewerTextureList::updateImages(F32 max_time)
 {
+	if(gAgent.getTeleportState() != LLAgent::TELEPORT_NONE)
+	{
+		clearFetchingRequests();
+		return;
+	}
+
 	LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec());
 
 	LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages);
@@ -659,6 +666,24 @@ void LLViewerTextureList::updateImages(F32 max_time)
 	}
 }
 
+void LLViewerTextureList::clearFetchingRequests()
+{
+	if (LLAppViewer::getTextureFetch()->getNumRequests() == 0)
+	{
+		return;
+	}
+
+	for (image_priority_list_t::iterator iter = mImageList.begin();
+		 iter != mImageList.end(); ++iter)
+	{
+		LLViewerFetchedTexture* image = *iter;
+		if(image->hasFetcher())
+		{
+			image->forceToDeleteRequest() ;
+		}
+	}
+}
+
 void LLViewerTextureList::updateImagesDecodePriorities()
 {
 	// Update the decode priority for N images each frame
@@ -1030,7 +1055,6 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage
 {
 	raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
 	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
-	compressedImage->setRate(0.f);
 	
 	if (gSavedSettings.getBOOL("LosslessJ2CUpload") &&
 		(raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF))
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index e0a362596d196a8f33c60f09a9259df7daaabbf2..e89997fe283950870a3da2b4fe2481966f3325eb 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -65,6 +65,7 @@ class LLViewerTextureList
 
 	friend class LLTextureView;
 	friend class LLViewerTextureManager;
+	friend class LLLocalBitmap;
 	
 public:
 	static BOOL createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec);
@@ -109,6 +110,8 @@ public:
 	void doPreloadImages();
 	void doPrefetchImages();
 
+	void clearFetchingRequests();
+
 	static S32 getMinVideoRamSetting();
 	static S32 getMaxVideoRamSetting(bool get_recommended = false);
 	
@@ -163,7 +166,7 @@ private:
 	// Request image from a specific host, used for baked avatar textures.
 	// Implemented in header in case someone changes default params above. JC
 	LLViewerFetchedTexture* getImageFromHost(const LLUUID& image_id, LLHost host)
-	{ return getImage(image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host); }
+	{ return getImage(image_id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host); }	
 
 public:
 	typedef std::set<LLPointer<LLViewerFetchedTexture> > image_list_t;	
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 35527d49772ebdcab9131964db7d345d34f23af0..39e330ad666c727d874576d4457e88666b0b0b9a 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -613,7 +613,7 @@ public:
 				addText(xpos, ypos, llformat("%d/%d Mesh HTTP Requests/Retries", LLMeshRepository::sHTTPRequestCount,
 					LLMeshRepository::sHTTPRetryCount));
 				ypos += y_inc;
-				
+
 				addText(xpos, ypos, llformat("%d/%d Mesh LOD Pending/Processing", LLMeshRepository::sLODPending, LLMeshRepository::sLODProcessing));
 				ypos += y_inc;
 
@@ -1973,12 +1973,12 @@ void LLViewerWindow::shutdownViews()
 		gMorphView->setVisible(FALSE);
 	}
 	llinfos << "Global views cleaned." << llendl ;
-
+	
 	// DEV-40930: Clear sModalStack. Otherwise, any LLModalDialog left open
 	// will crump with LL_ERRS.
 	LLModalDialog::shutdownModals();
 	llinfos << "LLModalDialog shut down." << llendl; 
-	
+
 	// destroy the nav bar, not currently part of gViewerWindow
 	// *TODO: Make LLNavigationBar part of gViewerWindow
 	if (LLNavigationBar::instanceExists())
@@ -1986,17 +1986,17 @@ void LLViewerWindow::shutdownViews()
 		delete LLNavigationBar::getInstance();
 	}
 	llinfos << "LLNavigationBar destroyed." << llendl ;
-
+	
 	// destroy menus after instantiating navbar above, as it needs
 	// access to gMenuHolder
 	cleanup_menus();
 	llinfos << "menus destroyed." << llendl ;
-
+	
 	// Delete all child views.
 	delete mRootView;
 	mRootView = NULL;
 	llinfos << "RootView deleted." << llendl ;
-
+	
 	// Automatically deleted as children of mRootView.  Fix the globals.
 	gStatusBar = NULL;
 	gIMMgr = NULL;
@@ -2165,13 +2165,19 @@ void LLViewerWindow::reshape(S32 width, S32 height)
 			// tell the OS specific window code about min window size
 			mWindow->setMinSize(min_window_width, min_window_height);
 
+			LLCoordScreen window_rect;
+			if (mWindow->getSize(&window_rect))
+			{
 			// Only save size if not maximized
-			gSavedSettings.setU32("WindowWidth", mWindowRectRaw.getWidth());
-			gSavedSettings.setU32("WindowHeight", mWindowRectRaw.getHeight());
+				gSavedSettings.setU32("WindowWidth", window_rect.mX);
+				gSavedSettings.setU32("WindowHeight", window_rect.mY);
+			}
 		}
 
 		LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width);
 		LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height);
+
+		LLLayoutStack::updateClass();
 	}
 }
 
@@ -4104,7 +4110,7 @@ void LLViewerWindow::movieSize(S32 new_width, S32 new_height)
 	gViewerWindow->getWindow()->getSize(&size);
 	if ( size != new_size )
 	{
-		gViewerWindow->getWindow()->setSize(new_size.convert());
+		gViewerWindow->getWindow()->setSize(new_size);
 	}
 }
 
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 7cbb47100d5aadeff4be0a21a8b6a5adfe0f211e..eada77156e477aa9c4cc1640995537c8acaeeaa4 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -101,6 +101,8 @@
 #include "llvoicevisualizer.h" // Ventrella
 
 #include "lldebugmessagebox.h"
+#include "llsdutil.h"
+
 extern F32 SPEED_ADJUST_MAX;
 extern F32 SPEED_ADJUST_MAX_SEC;
 extern F32 ANIM_SPEED_MAX;
@@ -149,10 +151,6 @@ const F32 PELVIS_LAG_WALKING	= 0.4f;	// ...while walking
 const F32 PELVIS_LAG_MOUSELOOK = 0.15f;
 const F32 MOUSELOOK_PELVIS_FOLLOW_FACTOR = 0.5f;
 const F32 PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON = 0.0001f; // not zero! - something gets divided by this!
-
-const F32 PELVIS_ROT_THRESHOLD_SLOW = 60.0f;	// amount of deviation allowed between
-const F32 PELVIS_ROT_THRESHOLD_FAST = 2.0f;	// the pelvis and the view direction
-											// when moving fast & slow
 const F32 TORSO_NOISE_AMOUNT = 1.0f;	// Amount of deviation from up-axis, in degrees
 const F32 TORSO_NOISE_SPEED = 0.2f;	// Time scale factor on torso noise.
 
@@ -631,7 +629,6 @@ F32 LLVOAvatar::sLODFactor = 1.f;
 F32 LLVOAvatar::sPhysicsLODFactor = 1.f;
 BOOL LLVOAvatar::sUseImpostors = FALSE;
 BOOL LLVOAvatar::sJointDebug = FALSE;
-
 F32 LLVOAvatar::sUnbakedTime = 0.f;
 F32 LLVOAvatar::sUnbakedUpdateTime = 0.f;
 F32 LLVOAvatar::sGreyTime = 0.f;
@@ -687,13 +684,16 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mNeedsSkin(FALSE),
 	mLastSkinTime(0.f),
 	mUpdatePeriod(1),
+	mFirstFullyVisible(TRUE),
 	mFullyLoaded(FALSE),
 	mPreviousFullyLoaded(FALSE),
 	mFullyLoadedInitialized(FALSE),
 	mSupportsAlphaLayers(FALSE),
 	mLoadedCallbacksPaused(FALSE),
 	mHasPelvisOffset( FALSE ),
-	mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar"))
+	mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar")),
+	mLastRezzedStatus(-1)
+
 {
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 	//VTResume();  // VTune
@@ -774,32 +774,46 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mLastPelvisFixup = 0.0f;
 }
 
+std::string LLVOAvatar::avString() const
+{
+	std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
+	return " Avatar '" + getFullname() + "' " + viz_string + " ";
+}
+
+void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string comment)
+{
+	LL_INFOS("Avatar") << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32()
+					   << "sec ]"
+					   << avString() 
+					   << "RuthTimer " << (U32)mRuthDebugTimer.getElapsedTimeF32()
+					   << " Notification " << notification_name
+					   << " : " << comment
+					   << llendl;
+
+	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	{
+		LLSD args;
+		args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
+		args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
+		args["NAME"] = getFullname();
+		LLNotificationsUtil::add(notification_name,args);
+	}
+}
+
 //------------------------------------------------------------------------
 // LLVOAvatar::~LLVOAvatar()
 //------------------------------------------------------------------------
 LLVOAvatar::~LLVOAvatar()
 {
-	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	if (!mFullyLoaded)
 	{
-		if (!mFullyLoaded)
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left after " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds as cloud." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezLeftCloudNotification",args);
-		}
-		else
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezLeftNotification",args);
-		}
-
+		debugAvatarRezTime("AvatarRezLeftCloudNotification","left after ruth seconds as cloud");
+	}
+	else
+	{
+		debugAvatarRezTime("AvatarRezLeftNotification","left sometime after declouding");
 	}
+
 	lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl;
 
 	mRoot.removeAllChildren();
@@ -848,6 +862,8 @@ LLVOAvatar::~LLVOAvatar()
 	mAnimationSources.clear();
 	LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ;
 
+	getPhases().clearPhases();
+	
 	lldebugs << "LLVOAvatar Destructor end" << llendl;
 }
 
@@ -881,6 +897,55 @@ BOOL LLVOAvatar::isFullyBaked()
 	return TRUE;
 }
 
+BOOL LLVOAvatar::isFullyTextured() const
+{
+	for (S32 i = 0; i < mMeshLOD.size(); i++)
+	{
+		LLViewerJoint* joint = (LLViewerJoint*) mMeshLOD[i];
+		if (i==MESH_ID_SKIRT && !isWearingWearableType(LLWearableType::WT_SKIRT))
+		{
+			continue; // don't care about skirt textures if we're not wearing one.
+		}
+		if (!joint)
+		{
+			continue; // nonexistent LOD OK.
+		}
+		std::vector<LLViewerJointMesh*>::iterator meshIter = joint->mMeshParts.begin();
+		if (meshIter != joint->mMeshParts.end())
+		{
+			LLViewerJointMesh *mesh = (LLViewerJointMesh *) *meshIter;
+			if (!mesh)
+			{
+				continue; // nonexistent mesh OK
+			}
+			if (mesh->mTexture.notNull() && mesh->mTexture->hasGLTexture())
+			{
+				continue; // Mesh exists and has a baked texture.
+			}
+			if (mesh->mLayerSet && mesh->mLayerSet->hasComposite())
+			{
+				continue; // Mesh exists and has a composite texture.
+			}
+			// Fail
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+BOOL LLVOAvatar::hasGray() const
+{
+	return !getIsCloud() && !isFullyTextured();
+}
+
+S32 LLVOAvatar::getRezzedStatus() const
+{
+	if (getIsCloud()) return 0;
+	if (isFullyTextured()) return 2;
+	llassert(hasGray());
+	return 1; // gray
+}
+
 void LLVOAvatar::deleteLayerSetCaches(bool clearAll)
 {
 	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
@@ -927,6 +992,31 @@ BOOL LLVOAvatar::areAllNearbyInstancesBaked(S32& grey_avatars)
 	return res;
 }
 
+// static
+void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts)
+{
+	counts.clear();
+	counts.resize(3);
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		 iter != LLCharacter::sInstances.end(); ++iter)
+	{
+		LLVOAvatar* inst = (LLVOAvatar*) *iter;
+		if (!inst)
+			continue;
+		S32 rez_status = inst->getRezzedStatus();
+		counts[rez_status]++;
+	}
+}
+
+// static
+std::string LLVOAvatar::rezStatusToString(S32 rez_status)
+{
+	if (rez_status==0) return "cloud";
+	if (rez_status==1) return "gray";
+	if (rez_status==2) return "textured";
+	return "unknown";
+}
+
 // static
 void LLVOAvatar::dumpBakedStatus()
 {
@@ -2252,18 +2342,12 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys,
 	U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
 
 	// Print out arrival information once we have name of avatar.
-	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	if (has_name && getNVPair("FirstName"))
 	{
-		if (has_name && getNVPair("FirstName"))
-		{
-			mDebugExistenceTimer.reset();
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezArrivedNotification",args);
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' arrived." << llendl;
-		}
+		mDebugExistenceTimer.reset();
+		debugAvatarRezTime("AvatarRezArrivedNotification","avatar arrived");
 	}
+
 	if(retval & LLViewerObject::INVALID_UPDATE)
 	{
 		if (isSelf())
@@ -2769,16 +2853,16 @@ void LLVOAvatar::idleUpdateLoadingEffect()
 	// update visibility when avatar is partially loaded
 	if (updateIsFullyLoaded()) // changed?
 	{
-		if (isFullyLoaded() && isSelf())
+		if (isFullyLoaded() && mFirstFullyVisible && isSelf())
 		{
-			static bool first_fully_visible = true;
-			if (first_fully_visible)
-			{
-				llinfos << "self isFullyLoaded, first_fully_visible" << llendl;
-
-				first_fully_visible = false;
-				LLAppearanceMgr::instance().onFirstFullyVisible();
-			}
+			LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL;
+			mFirstFullyVisible = FALSE;
+			LLAppearanceMgr::instance().onFirstFullyVisible();
+		}
+		if (isFullyLoaded() && mFirstFullyVisible && !isSelf())
+		{
+			LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL;
+			mFirstFullyVisible = FALSE;
 		}
 		if (isFullyLoaded())
 		{
@@ -2922,43 +3006,43 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 		return;
 	}
 
-		BOOL new_name = FALSE;
-		if (visible_chat != mVisibleChat)
+	BOOL new_name = FALSE;
+	if (visible_chat != mVisibleChat)
+	{
+		mVisibleChat = visible_chat;
+		new_name = TRUE;
+	}
+		
+	if (sRenderGroupTitles != mRenderGroupTitles)
+	{
+		mRenderGroupTitles = sRenderGroupTitles;
+		new_name = TRUE;
+	}
+
+	// First Calculate Alpha
+	// If alpha > 0, create mNameText if necessary, otherwise delete it
+	F32 alpha = 0.f;
+	if (mAppAngle > 5.f)
+	{
+		const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION;
+		if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME)
 		{
-			mVisibleChat = visible_chat;
-			new_name = TRUE;
+			alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION;
 		}
-		
-		if (sRenderGroupTitles != mRenderGroupTitles)
+		else
 		{
-			mRenderGroupTitles = sRenderGroupTitles;
-			new_name = TRUE;
+			// ...not fading, full alpha
+			alpha = 1.f;
 		}
-
-		// First Calculate Alpha
-		// If alpha > 0, create mNameText if necessary, otherwise delete it
-			F32 alpha = 0.f;
-			if (mAppAngle > 5.f)
-			{
-				const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION;
-				if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME)
-				{
-					alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION;
-				}
-				else
-				{
-					// ...not fading, full alpha
-					alpha = 1.f;
-				}
-			}
-			else if (mAppAngle > 2.f)
-			{
-				// far away is faded out also
-				alpha = (mAppAngle-2.f)/3.f;
-			}
+	}
+	else if (mAppAngle > 2.f)
+	{
+		// far away is faded out also
+		alpha = (mAppAngle-2.f)/3.f;
+	}
 
 	if (alpha <= 0.f)
-			{
+	{
 		if (mNameText)
 		{
 			mNameText->markDead();
@@ -2968,19 +3052,19 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 		return;
 	}
 
-				if (!mNameText)
-				{
+	if (!mNameText)
+	{
 		mNameText = static_cast<LLHUDNameTag*>( LLHUDObject::addHUDObject(
-			LLHUDObject::LL_HUD_NAME_TAG) );
+													LLHUDObject::LL_HUD_NAME_TAG) );
 		//mNameText->setMass(10.f);
-					mNameText->setSourceObject(this);
+		mNameText->setSourceObject(this);
 		mNameText->setVertAlignment(LLHUDNameTag::ALIGN_VERT_TOP);
-					mNameText->setVisibleOffScreen(TRUE);
-					mNameText->setMaxLines(11);
-					mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
-					sNumVisibleChatBubbles++;
-					new_name = TRUE;
-				}
+		mNameText->setVisibleOffScreen(TRUE);
+		mNameText->setMaxLines(11);
+		mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+		sNumVisibleChatBubbles++;
+		new_name = TRUE;
+	}
 				
 	LLVector3 name_position = idleUpdateNameTagPosition(root_pos_last);
 	mNameText->setPositionAgent(name_position);				
@@ -2989,10 +3073,10 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
 }
 
 void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
-			{
-		LLNameValue *title = getNVPair("Title");
-		LLNameValue* firstname = getNVPair("FirstName");
-		LLNameValue* lastname = getNVPair("LastName");
+{
+	LLNameValue *title = getNVPair("Title");
+	LLNameValue* firstname = getNVPair("FirstName");
+	LLNameValue* lastname = getNVPair("LastName");
 
 	// Avatars must have a first and last name
 	if (!firstname || !lastname) return;
@@ -3006,34 +3090,23 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
 		is_muted = false;
 	}
 	else
-		{
+	{
 		is_muted = LLMuteList::getInstance()->isMuted(getID());
 	}
 	bool is_friend = LLAvatarTracker::instance().isBuddy(getID());
 	bool is_cloud = getIsCloud();
 
-			if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
-			{
-				if (is_appearance != mNameAppearance)
-				{
-					if (is_appearance)
-					{
-						LLSD args;
-						args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-						args["NAME"] = getFullname();
-						LLNotificationsUtil::add("AvatarRezEnteredAppearanceNotification",args);
-						llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' entered appearance mode." << llendl;
-					}
-					else
-					{
-						LLSD args;
-						args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-						args["NAME"] = getFullname();
-						LLNotificationsUtil::add("AvatarRezLeftAppearanceNotification",args);
-						llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' left appearance mode." << llendl;
-					}
-				}
-			}
+	if (is_appearance != mNameAppearance)
+	{
+		if (is_appearance)
+		{
+			debugAvatarRezTime("AvatarRezEnteredAppearanceNotification","entered appearance mode");
+		}
+		else
+		{
+			debugAvatarRezTime("AvatarRezLeftAppearanceNotification","left appearance mode");
+		}
+	}
 
 	// Rebuild name tag if state change detected
 	if (mNameString.empty()
@@ -3043,56 +3116,56 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
 		|| is_away != mNameAway 
 		|| is_busy != mNameBusy 
 		|| is_muted != mNameMute
-				|| is_appearance != mNameAppearance 
+		|| is_appearance != mNameAppearance 
 		|| is_friend != mNameFriend
 		|| is_cloud != mNameCloud)
-				{
+	{
 		LLColor4 name_tag_color = getNameTagColor(is_friend);
 
 		clearNameTag();
 
 		if (is_away || is_muted || is_busy || is_appearance)
-				{
+		{
 			std::string line;
-					if (is_away)
-					{
-						line += LLTrans::getString("AvatarAway");
+			if (is_away)
+			{
+				line += LLTrans::getString("AvatarAway");
 				line += ", ";
-					}
-					if (is_busy)
-					{
+			}
+			if (is_busy)
+			{
 				line += LLTrans::getString("AvatarBusy");
 				line += ", ";
 			}
 			if (is_muted)
-						{
+			{
 				line += LLTrans::getString("AvatarMuted");
-							line += ", ";
-						}
+				line += ", ";
+			}
 			if (is_appearance)
 			{
 				line += LLTrans::getString("AvatarEditingAppearance");
 				line += ", ";
-					}
+			}
 			if (is_cloud)
-					{
+			{
 				line += LLTrans::getString("LoadingData");
 				line += ", ";
 			}
 			// trim last ", "
 			line.resize( line.length() - 2 );
 			addNameTagLine(line, name_tag_color, LLFontGL::NORMAL,
-				LLFontGL::getFontSansSerifSmall());
+						   LLFontGL::getFontSansSerifSmall());
 		}
 
 		if (sRenderGroupTitles
 			&& title && title->getString() && title->getString()[0] != '\0')
-						{
+		{
 			std::string title_str = title->getString();
 			LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR);
 			addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL,
-				LLFontGL::getFontSansSerifSmall());
-						}
+						   LLFontGL::getFontSansSerifSmall());
+		}
 
 		static LLUICachedControl<bool> show_display_names("NameTagShowDisplayNames");
 		static LLUICachedControl<bool> show_usernames("NameTagShowUsernames");
@@ -3105,120 +3178,120 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
 				// ...call this function back when the name arrives
 				// and force a rebuild
 				LLAvatarNameCache::get(getID(),
-					boost::bind(&LLVOAvatar::clearNameTag, this));
-					}
+									   boost::bind(&LLVOAvatar::clearNameTag, this));
+			}
 
 			// Might be blank if name not available yet, that's OK
 			if (show_display_names)
 			{
 				addNameTagLine(av_name.mDisplayName, name_tag_color, LLFontGL::NORMAL,
-					LLFontGL::getFontSansSerif());
-				}
+							   LLFontGL::getFontSansSerif());
+			}
 			// Suppress SLID display if display name matches exactly (ugh)
 			if (show_usernames && !av_name.mIsDisplayNameDefault)
-				{
+			{
 				// *HACK: Desaturate the color
 				LLColor4 username_color = name_tag_color * 0.83f;
 				addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL,
-					LLFontGL::getFontSansSerifSmall());
+							   LLFontGL::getFontSansSerifSmall());
 			}
-				}
+		}
 		else
-				{
+		{
 			const LLFontGL* font = LLFontGL::getFontSansSerif();
 			std::string full_name =
 				LLCacheName::buildFullName( firstname->getString(), lastname->getString() );
 			addNameTagLine(full_name, name_tag_color, LLFontGL::NORMAL, font);
-				}
+		}
 
-				mNameAway = is_away;
-				mNameBusy = is_busy;
-				mNameMute = is_muted;
-				mNameAppearance = is_appearance;
+		mNameAway = is_away;
+		mNameBusy = is_busy;
+		mNameMute = is_muted;
+		mNameAppearance = is_appearance;
 		mNameFriend = is_friend;
-				mNameCloud = is_cloud;
-				mTitle = title ? title->getString() : "";
-				LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR);
-				new_name = TRUE;
-			}
+		mNameCloud = is_cloud;
+		mTitle = title ? title->getString() : "";
+		LLStringFn::replace_ascii_controlchars(mTitle,LL_UNKNOWN_CHAR);
+		new_name = TRUE;
+	}
 
 	if (mVisibleChat)
-			{
-				mNameText->setFont(LLFontGL::getFontSansSerif());
+	{
+		mNameText->setFont(LLFontGL::getFontSansSerif());
 		mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_LEFT);
-				mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f);
+		mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f);
 			
-				char line[MAX_STRING];		/* Flawfinder: ignore */
-				line[0] = '\0';
-				std::deque<LLChat>::iterator chat_iter = mChats.begin();
-				mNameText->clearString();
-
-				LLColor4 new_chat = LLUIColorTable::instance().getColor( isSelf() ? "UserChatColor" : "AgentChatColor" );
-				LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f);
-				LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f);
-				if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) 
-				{
-					++chat_iter;
-				}
-
-				for(; chat_iter != mChats.end(); ++chat_iter)
-				{
-					F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f);
-					LLFontGL::StyleFlags style;
-					switch(chat_iter->mChatType)
-					{
-						case CHAT_TYPE_WHISPER:
-							style = LLFontGL::ITALIC;
-							break;
-						case CHAT_TYPE_SHOUT:
-							style = LLFontGL::BOLD;
-							break;
-						default:
-							style = LLFontGL::NORMAL;
-							break;
-					}
-					if (chat_fade_amt < 1.f)
-					{
-						F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f);
-						mNameText->addLine(chat_iter->mText, lerp(new_chat, normal_chat, u), style);
-					}
-					else if (chat_fade_amt < 2.f)
-					{
-						F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f);
-						mNameText->addLine(chat_iter->mText, lerp(normal_chat, old_chat, u), style);
-					}
-					else if (chat_fade_amt < 3.f)
-					{
-						// *NOTE: only remove lines down to minimum number
-						mNameText->addLine(chat_iter->mText, old_chat, style);
-					}
-				}
-				mNameText->setVisibleOffScreen(TRUE);
+		char line[MAX_STRING];		/* Flawfinder: ignore */
+		line[0] = '\0';
+		std::deque<LLChat>::iterator chat_iter = mChats.begin();
+		mNameText->clearString();
 
-				if (mTyping)
-				{
-					S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1;
-					switch(dot_count)
-					{
-						case 1:
-							mNameText->addLine(".", new_chat);
-							break;
-						case 2:
-							mNameText->addLine("..", new_chat);
-							break;
-						case 3:
-							mNameText->addLine("...", new_chat);
-							break;
-					}
+		LLColor4 new_chat = LLUIColorTable::instance().getColor( isSelf() ? "UserChatColor" : "AgentChatColor" );
+		LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f);
+		LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f);
+		if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES) 
+		{
+			++chat_iter;
+		}
 
-				}
+		for(; chat_iter != mChats.end(); ++chat_iter)
+		{
+			F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f);
+			LLFontGL::StyleFlags style;
+			switch(chat_iter->mChatType)
+			{
+				case CHAT_TYPE_WHISPER:
+				style = LLFontGL::ITALIC;
+				break;
+				case CHAT_TYPE_SHOUT:
+				style = LLFontGL::BOLD;
+				break;
+				default:
+				style = LLFontGL::NORMAL;
+				break;
 			}
-			else
+			if (chat_fade_amt < 1.f)
+			{
+				F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f);
+				mNameText->addLine(chat_iter->mText, lerp(new_chat, normal_chat, u), style);
+			}
+			else if (chat_fade_amt < 2.f)
 			{
+				F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f);
+				mNameText->addLine(chat_iter->mText, lerp(normal_chat, old_chat, u), style);
+			}
+			else if (chat_fade_amt < 3.f)
+			{
+				// *NOTE: only remove lines down to minimum number
+				mNameText->addLine(chat_iter->mText, old_chat, style);
+			}
+		}
+		mNameText->setVisibleOffScreen(TRUE);
+
+		if (mTyping)
+		{
+			S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1;
+			switch(dot_count)
+			{
+				case 1:
+				mNameText->addLine(".", new_chat);
+				break;
+				case 2:
+				mNameText->addLine("..", new_chat);
+				break;
+				case 3:
+				mNameText->addLine("...", new_chat);
+				break;
+			}
+
+		}
+	}
+	else
+	{
 		// ...not using chat bubbles, just names
 		mNameText->setTextAlignment(LLHUDNameTag::ALIGN_TEXT_CENTER);
-				mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
-				mNameText->setVisibleOffScreen(FALSE);
+		mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+		mNameText->setVisibleOffScreen(FALSE);
 	}
 }
 
@@ -3241,8 +3314,8 @@ void LLVOAvatar::clearNameTag()
 {
 	mNameString.clear();
 	if (mNameText)
-				{
-					mNameText->setLabel("");
+	{
+		mNameText->setLabel("");
 		mNameText->setString( "" );
 	}
 }
@@ -3657,7 +3730,11 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
 			BOOL self_in_mouselook = isSelf() && gAgentCamera.cameraMouselook();
 
 			LLVector3 pelvisDir( mRoot.getWorldMatrix().getFwdRow4().mV );
-			F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, PELVIS_ROT_THRESHOLD_SLOW, PELVIS_ROT_THRESHOLD_FAST);
+
+			static LLCachedControl<F32> s_pelvis_rot_threshold_slow(gSavedSettings, "AvatarRotateThresholdSlow");
+			static LLCachedControl<F32> s_pelvis_rot_threshold_fast(gSavedSettings, "AvatarRotateThresholdFast");
+
+			F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, s_pelvis_rot_threshold_slow, s_pelvis_rot_threshold_fast);
 						
 			if (self_in_mouselook)
 			{
@@ -3964,7 +4041,7 @@ void LLVOAvatar::updateVisibility()
 			LLNameValue* firstname = getNVPair("FirstName");
 			if (firstname)
 			{
-				llinfos << "Avatar " << firstname->getString() << " updating visiblity" << llendl;
+				LL_DEBUGS("Avatar") << avString() << " updating visibility" << LL_ENDL;
 			}
 			else
 			{
@@ -4130,11 +4207,11 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
 		if (needs_rebuild || mDirtyMesh >= 2 || mVisibilityRank <= 4)
 		{
-		updateMeshData();
+			updateMeshData();
 			mDirtyMesh = 0;
-		mNeedsSkin = TRUE;
-		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
-	}
+			mNeedsSkin = TRUE;
+			mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+		}
 	}
 
 	if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) <= 0)
@@ -4176,7 +4253,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 		LLNameValue* firstname = getNVPair("FirstName");
 		if (firstname)
 		{
-			llinfos << "Avatar " << firstname->getString() << " in render" << llendl;
+			LL_DEBUGS("Avatar") << avString() << " in render" << LL_ENDL;
 		}
 		else
 		{
@@ -6393,10 +6470,10 @@ BOOL LLVOAvatar::isVisible() const
 }
 
 // Determine if we have enough avatar data to render
-BOOL LLVOAvatar::getIsCloud()
+BOOL LLVOAvatar::getIsCloud() const
 {
 	// Do we have a shape?
-	if (visualParamWeightsAreDefault())
+	if ((const_cast<LLVOAvatar*>(this))->visualParamWeightsAreDefault())
 	{
 		return TRUE;
 	}
@@ -6415,11 +6492,53 @@ BOOL LLVOAvatar::getIsCloud()
 	return FALSE;
 }
 
+void LLVOAvatar::updateRezzedStatusTimers()
+{
+	// State machine for rezzed status. Statuses are 0 = cloud, 1 = gray, 2 = textured.
+	// Purpose is to collect time data for each period of cloud or cloud+gray.
+	S32 rez_status = getRezzedStatus();
+	if (rez_status != mLastRezzedStatus)
+	{
+		LL_DEBUGS("Avatar") << avString() << "rez state change: " << mLastRezzedStatus << " -> " << rez_status << LL_ENDL;
+		bool is_cloud_or_gray = (rez_status==0 || rez_status==1);
+		bool was_cloud_or_gray = (mLastRezzedStatus==0 || mLastRezzedStatus==1);
+		bool is_cloud = (rez_status==0);
+		bool was_cloud = (mLastRezzedStatus==0);
+
+		// Non-cloud to cloud
+		if (is_cloud && !was_cloud)
+		{
+			// start cloud timer.
+			getPhases().startPhase("cloud");
+		}
+		else if (was_cloud && !is_cloud)
+		{
+			// stop cloud timer, which will capture stats.
+			getPhases().stopPhase("cloud");
+		}
+
+		// Non-cloud-or-gray to cloud-or-gray
+		if (is_cloud_or_gray && !was_cloud_or_gray)
+		{
+			// start cloud-or-gray timer.
+			getPhases().startPhase("cloud-or-gray");
+		}
+		else if (was_cloud_or_gray && !is_cloud_or_gray)
+		{
+			// stop cloud-or-gray timer, which will capture stats.
+			getPhases().stopPhase("cloud-or-gray");
+		}
+		
+		mLastRezzedStatus = rez_status;
+	}
+}
+
 // call periodically to keep isFullyLoaded up to date.
 // returns true if the value has changed.
 BOOL LLVOAvatar::updateIsFullyLoaded()
 {
 	const BOOL loading = getIsCloud();
+	updateRezzedStatusTimers();
 	updateRuthTimer(loading);
 	return processFullyLoadedChange(loading);
 }
@@ -6434,27 +6553,19 @@ void LLVOAvatar::updateRuthTimer(bool loading)
 	if (mPreviousFullyLoaded)
 	{
 		mRuthTimer.reset();
-		if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' became cloud." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezCloudNotification",args);
-		}
-		mRuthDebugTimer.reset();
+		debugAvatarRezTime("AvatarRezCloudNotification","became cloud");
 	}
 	
 	const F32 LOADING_TIMEOUT__SECONDS = 120.f;
 	if (mRuthTimer.getElapsedTimeF32() > LOADING_TIMEOUT__SECONDS)
 	{
-		llinfos << "Ruth Timer timeout: Missing texture data for '" << getFullname() << "' "
+		LL_DEBUGS("Avatar") << avString()
+				<< "Ruth Timer timeout: Missing texture data for '" << getFullname() << "' "
 				<< "( Params loaded : " << !visualParamWeightsAreDefault() << " ) "
 				<< "( Lower : " << isTextureDefined(TEX_LOWER_BAKED) << " ) "
 				<< "( Upper : " << isTextureDefined(TEX_UPPER_BAKED) << " ) "
 				<< "( Head : " << isTextureDefined(TEX_HEAD_BAKED) << " )."
-				<< llendl;
+				<< LL_ENDL;
 		
 		LLAvatarPropertiesProcessor::getInstance()->sendAvatarTexturesRequest(getID());
 		mRuthTimer.reset();
@@ -6471,20 +6582,13 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading)
 	
 	mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > PAUSE);
 
-	if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+	if (!mPreviousFullyLoaded && !loading && mFullyLoaded)
 	{
-		if (!mPreviousFullyLoaded && !loading && mFullyLoaded)
-		{
-			llinfos << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ] Avatar '" << getFullname() << "' resolved in " << (U32)mRuthDebugTimer.getElapsedTimeF32() << " seconds." << llendl;
-			LLSD args;
-			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
-			args["TIME"] = llformat("%d",(U32)mRuthDebugTimer.getElapsedTimeF32());
-			args["NAME"] = getFullname();
-			LLNotificationsUtil::add("AvatarRezNotification",args);
-		}
+		debugAvatarRezTime("AvatarRezNotification","fully loaded");
 	}
 
 	// did our loading state "change" from last call?
+	// runway - why are we updating every 30 calls even if nothing has changed?
 	const S32 UPDATE_RATE = 30;
 	BOOL changed =
 		((mFullyLoaded != mPreviousFullyLoaded) ||         // if the value is different from the previous call
@@ -6924,7 +7028,7 @@ LLColor4 LLVOAvatar::getDummyColor()
 
 void LLVOAvatar::dumpAvatarTEs( const std::string& context ) const
 {	
-	llinfos << (isSelf() ? "Self: " : "Other: ") << context << llendl;
+	LL_DEBUGS("Avatar") << avString() << (isSelf() ? "Self: " : "Other: ") << context << LL_ENDL;
 	for (LLVOAvatarDictionary::Textures::const_iterator iter = LLVOAvatarDictionary::getInstance()->getTextures().begin();
 		 iter != LLVOAvatarDictionary::getInstance()->getTextures().end();
 		 ++iter)
@@ -6934,23 +7038,23 @@ void LLVOAvatar::dumpAvatarTEs( const std::string& context ) const
 		const LLViewerTexture* te_image = getImage(iter->first,0);
 		if( !te_image )
 		{
-			llinfos << "       " << texture_dict->mName << ": null ptr" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": null ptr" << LL_ENDL;
 		}
 		else if( te_image->getID().isNull() )
 		{
-			llinfos << "       " << texture_dict->mName << ": null UUID" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": null UUID" << LL_ENDL;
 		}
 		else if( te_image->getID() == IMG_DEFAULT )
 		{
-			llinfos << "       " << texture_dict->mName << ": IMG_DEFAULT" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": IMG_DEFAULT" << LL_ENDL;
 		}
 		else if( te_image->getID() == IMG_DEFAULT_AVATAR )
 		{
-			llinfos << "       " << texture_dict->mName << ": IMG_DEFAULT_AVATAR" << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": IMG_DEFAULT_AVATAR" << LL_ENDL;
 		}
 		else
 		{
-			llinfos << "       " << texture_dict->mName << ": " << te_image->getID() << llendl;
+			LL_DEBUGS("Avatar") << avString() << "       " << texture_dict->mName << ": " << te_image->getID() << LL_ENDL;
 		}
 	}
 }
@@ -7081,6 +7185,7 @@ void LLVOAvatar::rebuildHUD()
 //-----------------------------------------------------------------------------
 void LLVOAvatar::onFirstTEMessageReceived()
 {
+	LL_INFOS("Avatar") << avString() << LL_ENDL;
 	if( !mFirstTEMessageReceived )
 	{
 		mFirstTEMessageReceived = TRUE;
@@ -7109,6 +7214,7 @@ void LLVOAvatar::onFirstTEMessageReceived()
 					image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, TRUE, new LLTextureMaskData( mID ), 
 						src_callback_list, paused);
 				}
+				LL_DEBUGS("Avatar") << avString() << "layer_baked, setting onInitialBakedTextureLoaded as callback" << LL_ENDL;
 				image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, FALSE, new LLUUID( mID ), 
 					src_callback_list, paused );
 			}
@@ -7167,14 +7273,16 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 
-//	llinfos << "processAvatarAppearance start " << mID << llendl;
 	BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived;
-
 	mFirstAppearanceMessageReceived = TRUE;
 
+	LL_INFOS("Avatar") << avString() << "processAvatarAppearance start " << mID
+			<< " first? " << is_first_appearance_message << " self? " << isSelf() << LL_ENDL;
+
+
 	if( isSelf() )
 	{
-		llwarns << "Received AvatarAppearance for self" << llendl;
+		llwarns << avString() << "Received AvatarAppearance for self" << llendl;
 		if( mFirstTEMessageReceived )
 		{
 //			llinfos << "processAvatarAppearance end  " << mID << llendl;
@@ -7202,7 +7310,10 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	}
 
 
-	if( !is_first_appearance_message )
+	// runway - was
+	// if (!is_first_appearance_message )
+	// which means it would be called on second appearance message - probably wrong.
+	if (is_first_appearance_message )
 	{
 		onFirstTEMessageReceived();
 	}
@@ -7223,6 +7334,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	bool drop_visual_params_debug = gSavedSettings.getBOOL("BlockSomeAvatarAppearanceVisualParams") && (ll_rand(2) == 0); // pretend that ~12% of AvatarAppearance messages arrived without a VisualParam block, for testing
 	if( num_blocks > 1 && !drop_visual_params_debug)
 	{
+		LL_DEBUGS("Avatar") << avString() << " handle visual params, num_blocks " << num_blocks << LL_ENDL;
 		BOOL params_changed = FALSE;
 		BOOL interp_params = FALSE;
 		
@@ -7295,6 +7407,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 	else
 	{
 		// AvatarAppearance message arrived without visual params
+		LL_DEBUGS("Avatar") << avString() << "no visual params" << LL_ENDL;
 		if (drop_visual_params_debug)
 		{
 			llinfos << "Debug-faked lack of parameters on AvatarAppearance for object: "  << getID() << llendl;
@@ -7447,8 +7560,15 @@ void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerFetchedTexture
 // static
 void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
 {
+
+	
 	LLUUID *avatar_idp = (LLUUID *)userdata;
 	LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+	
+	if (selfp)
+	{
+		LL_DEBUGS("Avatar") << selfp->avString() << "discard_level " << discard_level << " success " << success << " final " << final << LL_ENDL;
+	}
 
 	if (!success && selfp)
 	{
@@ -7460,13 +7580,20 @@ void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerFetchedTextu
 	}
 }
 
-void LLVOAvatar::onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata)
+// Static
+void LLVOAvatar::onBakedTextureLoaded(BOOL success,
+									  LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src,
+									  S32 discard_level, BOOL final, void* userdata)
 {
 	//llinfos << "onBakedTextureLoaded: " << src_vi->getID() << llendl;
 
 	LLUUID id = src_vi->getID();
 	LLUUID *avatar_idp = (LLUUID *)userdata;
 	LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+	if (selfp)
+	{	
+		LL_DEBUGS("Avatar") << selfp->avString() << "discard_level " << discard_level << " success " << success << " final " << final << " id " << src_vi->getID() << LL_ENDL;
+	}
 
 	if (selfp && !success)
 	{
@@ -7488,6 +7615,8 @@ void LLVOAvatar::onBakedTextureLoaded(BOOL success, LLViewerFetchedTexture *src_
 // Called when baked texture is loaded and also when we start up with a baked texture
 void LLVOAvatar::useBakedTexture( const LLUUID& id )
 {
+
+	
 	/* if(id == head_baked->getID())
 		 mHeadBakedLoaded = TRUE;
 		 mLastHeadBakedID = id;
@@ -7498,6 +7627,7 @@ void LLVOAvatar::useBakedTexture( const LLUUID& id )
 		LLViewerTexture* image_baked = getImage( mBakedTextureDatas[i].mTextureIndex, 0 );
 		if (id == image_baked->getID())
 		{
+			LL_DEBUGS("Avatar") << avString() << " i " << i << " id " << id << LL_ENDL;
 			mBakedTextureDatas[i].mIsLoaded = true;
 			mBakedTextureDatas[i].mLastTextureIndex = id;
 			mBakedTextureDatas[i].mIsUsed = true;
@@ -7537,12 +7667,16 @@ void LLVOAvatar::useBakedTexture( const LLUUID& id )
 void LLVOAvatar::dumpArchetypeXML( void* )
 {
 	LLAPRFile outfile;
-	outfile.open(gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"new archetype.xml"), LL_APR_WB );
-	apr_file_t* file = outfile.getFileHandle() ;
+	outfile.open(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"new archetype.xml"), LL_APR_WB );
+	apr_file_t* file = outfile.getFileHandle();
 	if (!file)
 	{
 		return;
 	}
+	else
+	{
+		llinfos << "xmlfile write handle obtained : " << gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"new archetype.xml") << llendl;
+	}
 
 	apr_file_printf( file, "<?xml version=\"1.0\" encoding=\"US-ASCII\" standalone=\"yes\"?>\n" );
 	apr_file_printf( file, "<linden_genepool version=\"1.0\">\n" );
@@ -7582,6 +7716,11 @@ void LLVOAvatar::dumpArchetypeXML( void* )
 	}
 	apr_file_printf( file, "\t</archetype>\n" );
 	apr_file_printf( file, "\n</linden_genepool>\n" );
+	//explictly close the file if it is still open which it should be
+	if (file)
+	{
+		outfile.close();
+	}
 }
 
 
@@ -7663,6 +7802,9 @@ void LLVOAvatar::cullAvatarsByPixelArea()
 		}
 	}
 
+	// runway - this doesn't detect gray/grey state.
+	// think we just need to be checking self av since it's the only
+	// one with lltexlayer stuff.
 	S32 grey_avatars = 0;
 	if (LLVOAvatar::areAllNearbyInstancesBaked(grey_avatars))
 	{
@@ -8466,7 +8608,9 @@ void LLVOAvatar::idleUpdateRenderCost()
 		}
 	}
 
-	setDebugText(llformat("%d", cost));
+	
+	std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
+	setDebugText(llformat("%s %d", viz_string.c_str(), cost));
 	mVisualComplexity = cost;
 	F32 green = 1.f-llclamp(((F32) cost-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
 	F32 red = llmin((F32) cost/(F32)ARC_LIMIT, 1.f);
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 6a4e09593c1dc22335efb0f65feb263089c63a2a..6fb56a4c0bb2bf70a0134746170612c59e665079 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -48,6 +48,7 @@
 #include "lltexglobalcolor.h"
 #include "lldriverparam.h"
 #include "material_codes.h"		// LL_MCODE_END
+#include "llviewerstats.h"
 
 extern const LLUUID ANIM_AGENT_BODY_NOISE;
 extern const LLUUID ANIM_AGENT_BREATHE_ROT;
@@ -277,14 +278,27 @@ public:
 public:
 	BOOL			isFullyLoaded() const;
 	bool			isTooComplex() const;
-	bool visualParamWeightsAreDefault();
+	bool 			visualParamWeightsAreDefault();
+	virtual BOOL	getIsCloud() const;
+	BOOL			isFullyTextured() const;
+	BOOL			hasGray() const; 
+	S32				getRezzedStatus() const; // 0 = cloud, 1 = gray, 2 = fully textured.
+	void			updateRezzedStatusTimers();
+
+	S32				mLastRezzedStatus;
+
+	LLViewerStats::PhaseMap& getPhases()
+	{
+		return mPhases;
+	}
+
 protected:
-	virtual BOOL	getIsCloud();
 	BOOL			updateIsFullyLoaded();
 	BOOL			processFullyLoadedChange(bool loading);
 	void			updateRuthTimer(bool loading);
 	F32 			calcMorphAmount();
 private:
+	BOOL			mFirstFullyVisible;
 	BOOL			mFullyLoaded;
 	BOOL			mPreviousFullyLoaded;
 	BOOL			mFullyLoadedInitialized;
@@ -292,6 +306,28 @@ private:
 	S32				mVisualComplexity;
 	LLFrameTimer	mFullyLoadedTimer;
 	LLFrameTimer	mRuthTimer;
+
+public:
+	class ScopedPhaseSetter
+	{
+	public:
+		ScopedPhaseSetter(LLVOAvatar *avatarp, std::string phase_name):
+			mAvatar(avatarp), mPhaseName(phase_name)
+		{
+			if (mAvatar) { mAvatar->getPhases().startPhase(mPhaseName); }
+		}
+		~ScopedPhaseSetter()
+		{
+			if (mAvatar) { mAvatar->getPhases().stopPhase(mPhaseName); }
+		}
+	private:
+		std::string mPhaseName;
+		LLVOAvatar* mAvatar;
+	};
+
+private:
+	LLViewerStats::PhaseMap mPhases;
+
 protected:
 	LLFrameTimer    mInvisibleTimer;
 	
@@ -518,9 +554,10 @@ public:
 	virtual BOOL	isTextureVisible(LLVOAvatarDefines::ETextureIndex type, U32 index = 0) const;
 	virtual BOOL	isTextureVisible(LLVOAvatarDefines::ETextureIndex type, LLWearable *wearable) const;
 
-protected:
 	BOOL			isFullyBaked();
 	static BOOL		areAllNearbyInstancesBaked(S32& grey_avatars);
+	static void		getNearbyRezzedStats(std::vector<S32>& counts);
+	static std::string rezStatusToString(S32 status);
 
 	//--------------------------------------------------------------------
 	// Baked textures
@@ -882,6 +919,7 @@ private:
 
 public:
 	std::string		getFullname() const; // Returns "FirstName LastName"
+	std::string		avString() const; // Frequently used string in log messages "Avatar '<full name'"
 protected:
 	static void		getAnimLabels(LLDynamicArray<std::string>* labels);
 	static void		getAnimNames(LLDynamicArray<std::string>* names);	
@@ -983,7 +1021,9 @@ private:
 	// Avatar Rez Metrics
 	//--------------------------------------------------------------------
 public:
+	void 			debugAvatarRezTime(std::string notification_name, std::string comment = "");
 	F32				debugGetExistenceTimeElapsedF32() const { return mDebugExistenceTimer.getElapsedTimeF32(); }
+
 protected:
 	LLFrameTimer	mRuthDebugTimer; // For tracking how long it takes for av to rez
 	LLFrameTimer	mDebugExistenceTimer; // Debugging for how long the avatar has been in memory.
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index f063653cc5555ab84f3ee05f2317e5a1d00b67b0..d2609e5587226a5103b08d3deefc4491e3951506 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -58,6 +58,8 @@
 #include "llappearancemgr.h"
 #include "llmeshrepository.h"
 #include "llvovolume.h"
+#include "llsdutil.h"
+#include "llstartup.h"
 
 #if LL_MSVC
 // disable boost::lexical_cast warning
@@ -75,6 +77,39 @@ BOOL isAgentAvatarValid()
 			(!gAgentAvatarp->isDead()));
 }
 
+void selfStartPhase(const std::string& phase_name)
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().startPhase(phase_name);
+	}
+}
+
+void selfStopPhase(const std::string& phase_name)
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().stopPhase(phase_name);
+	}
+}
+
+void selfClearPhases()
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().clearPhases();
+		gAgentAvatarp->mLastRezzedStatus = -1;
+	}
+}
+
+void selfStopAllPhases()
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->getPhases().stopAllPhases();
+	}
+}
+
 using namespace LLVOAvatarDefines;
 
 /*********************************************************************************
@@ -131,7 +166,8 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id,
 	LLVOAvatar(id, pcode, regionp),
 	mScreenp(NULL),
 	mLastRegionHandle(0),
-	mRegionCrossingCount(0)
+	mRegionCrossingCount(0),
+	mInitialBakesLoaded(false)
 {
 	gAgentWearables.setAvatarObject(this);
 
@@ -164,6 +200,7 @@ void LLVOAvatarSelf::initInstance()
 	{
 		mDebugBakedTextureTimes[i][0] = -1.0f;
 		mDebugBakedTextureTimes[i][1] = -1.0f;
+		mInitialBakeIDs[i] = LLUUID::null;
 	}
 
 	status &= buildMenus();
@@ -762,6 +799,41 @@ void LLVOAvatarSelf::stopMotionFromSource(const LLUUID& source_id)
 	}
 }
 
+//virtual
+U32  LLVOAvatarSelf::processUpdateMessage(LLMessageSystem *mesgsys,
+													 void **user_data,
+													 U32 block_num,
+													 const EObjectUpdateType update_type,
+													 LLDataPacker *dp)
+{
+	U32 retval = LLVOAvatar::processUpdateMessage(mesgsys,user_data,block_num,update_type,dp);
+
+	if (mInitialBakesLoaded == false && retval == 0x0)
+	{
+		// call update textures to force the images to be created
+		updateMeshTextures();
+
+		// unpack the texture UUIDs to the texture slots
+		retval = unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
+
+		// need to trigger a few operations to get the avatar to use the new bakes
+		for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
+		{
+			const LLVOAvatarDefines::ETextureIndex te = mBakedTextureDatas[i].mTextureIndex;
+			LLUUID texture_id = getTEImage(te)->getID();
+			setNewBakedTexture(te, texture_id);
+			mInitialBakeIDs[i] = texture_id;
+		}
+
+		onFirstTEMessageReceived();
+
+		mInitialBakesLoaded = true;
+	}
+
+	return retval;
+}
+
+
 void LLVOAvatarSelf::setLocalTextureTE(U8 te, LLViewerTexture* image, U32 index)
 {
 	if (te >= TEX_NUM_INDICES)
@@ -1889,7 +1961,7 @@ void LLVOAvatarSelf::dumpTotalLocalTextureByteCount()
 	llinfos << "Total Avatar LocTex GL:" << (gl_bytes/1024) << "KB" << llendl;
 }
 
-BOOL LLVOAvatarSelf::getIsCloud()
+BOOL LLVOAvatarSelf::getIsCloud() const
 {
 	// do we have our body parts?
 	if (gAgentWearables.getWearableCount(LLWearableType::WT_SHAPE) == 0 ||
@@ -2055,6 +2127,80 @@ const std::string LLVOAvatarSelf::debugDumpAllLocalTextureDataInfo() const
 	return text;
 }
 
+// Dump avatar metrics data.
+LLSD LLVOAvatarSelf::metricsData()
+{
+	// runway - add region info
+	LLSD result;
+	result["id"] = getID();
+	result["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus());
+	result["is_self"] = isSelf();
+	std::vector<S32> rez_counts;
+	LLVOAvatar::getNearbyRezzedStats(rez_counts);
+	result["nearby"] = LLSD::emptyMap();
+	for (S32 i=0; i<rez_counts.size(); ++i)
+	{
+		std::string rez_status_name = LLVOAvatar::rezStatusToString(i);
+		result["nearby"][rez_status_name] = rez_counts[i];
+	}
+	result["timers"]["debug_existence"] = mDebugExistenceTimer.getElapsedTimeF32();
+	result["timers"]["ruth_debug"] = mRuthDebugTimer.getElapsedTimeF32();
+	result["timers"]["ruth"] = mRuthTimer.getElapsedTimeF32();
+	result["timers"]["invisible"] = mInvisibleTimer.getElapsedTimeF32();
+	result["timers"]["fully_loaded"] = mFullyLoadedTimer.getElapsedTimeF32();
+	result["phases"] = getPhases().dumpPhases();
+	result["startup"] = LLStartUp::getPhases().dumpPhases();
+	
+	return result;
+}
+
+class ViewerAppearanceChangeMetricsResponder: public LLCurl::Responder
+{
+public:
+	ViewerAppearanceChangeMetricsResponder()
+	{
+	}
+
+	virtual void completed(U32 status,
+						   const std::string& reason,
+						   const LLSD& content)
+	{
+		if (isGoodStatus(status))
+		{
+			LL_DEBUGS("Avatar") << "OK" << LL_ENDL;
+			result(content);
+		}
+		else
+		{
+			LL_WARNS("Avatar") << "Failed " << status << " reason " << reason << LL_ENDL;
+			error(status,reason);
+		}
+	}
+};
+
+void LLVOAvatarSelf::sendAppearanceChangeMetrics()
+{
+	// gAgentAvatarp->stopAllPhases();
+
+	LLSD msg = metricsData();
+	msg["message"] = "ViewerAppearanceChangeMetrics";
+
+	LL_DEBUGS("Avatar") << avString() << "message: " << ll_pretty_print_sd(msg) << LL_ENDL;
+	std::string	caps_url;
+	if (getRegion())
+	{
+		// runway - change here to activate.
+		caps_url = getRegion()->getCapability("ViewerMetrics");
+	}
+	if (!caps_url.empty())
+	{
+		LLCurlRequest::headers_t headers;
+		LLHTTPClient::post(caps_url,
+							msg,
+							new ViewerAppearanceChangeMetricsResponder);
+	}
+}
+
 const LLUUID& LLVOAvatarSelf::grabBakedTexture(EBakedTextureIndex baked_index) const
 {
 	if (canGrabBakedTexture(baked_index))
@@ -2253,11 +2399,25 @@ void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
 			if (isAllLocalTextureDataFinal())
 			{
 				LLNotificationsUtil::add("AvatarRezSelfBakedDoneNotification",args);
+				LL_DEBUGS("Avatar") << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32()
+						<< "sec ]"
+						<< avString() 
+						<< "RuthTimer " << (U32)mRuthDebugTimer.getElapsedTimeF32()
+						<< " SelfLoadTimer " << (U32)mDebugSelfLoadTimer.getElapsedTimeF32()
+						<< " Notification " << "AvatarRezSelfBakedDoneNotification"
+						<< llendl;
 			}
 			else
 			{
 				args["STATUS"] = debugDumpAllLocalTextureDataInfo();
 				LLNotificationsUtil::add("AvatarRezSelfBakedUpdateNotification",args);
+				LL_DEBUGS("Avatar") << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32()
+						<< "sec ]"
+						<< avString() 
+						<< "RuthTimer " << (U32)mRuthDebugTimer.getElapsedTimeF32()
+						<< " SelfLoadTimer " << (U32)mDebugSelfLoadTimer.getElapsedTimeF32()
+						<< " Notification " << "AvatarRezSelfBakedUpdateNotification"
+						<< llendl;
 			}
 		}
 
@@ -2265,7 +2425,7 @@ void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
 	}
 }
 
-// FIXME: This is never called. Something may be broken.
+// FIXME: This is not called consistently. Something may be broken.
 void LLVOAvatarSelf::outputRezDiagnostics() const
 {
 	if(!gSavedSettings.getBOOL("DebugAvatarLocalTexLoadedTime"))
@@ -2274,11 +2434,11 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
 	}
 
 	const F32 final_time = mDebugSelfLoadTimer.getElapsedTimeF32();
-	llinfos << "REZTIME: Myself rez stats:" << llendl;
-	llinfos << "\t Time from avatar creation to load wearables: " << (S32)mDebugTimeWearablesLoaded << llendl;
-	llinfos << "\t Time from avatar creation to de-cloud: " << (S32)mDebugTimeAvatarVisible << llendl;
-	llinfos << "\t Time from avatar creation to de-cloud for others: " << (S32)final_time << llendl;
-	llinfos << "\t Load time for each texture: " << llendl;
+	LL_DEBUGS("Avatar") << "REZTIME: Myself rez stats:" << llendl;
+	LL_DEBUGS("Avatar") << "\t Time from avatar creation to load wearables: " << (S32)mDebugTimeWearablesLoaded << llendl;
+	LL_DEBUGS("Avatar") << "\t Time from avatar creation to de-cloud: " << (S32)mDebugTimeAvatarVisible << llendl;
+	LL_DEBUGS("Avatar") << "\t Time from avatar creation to de-cloud for others: " << (S32)final_time << llendl;
+	LL_DEBUGS("Avatar") << "\t Load time for each texture: " << llendl;
 	for (U32 i = 0; i < LLVOAvatarDefines::TEX_NUM_INDICES; ++i)
 	{
 		std::stringstream out;
@@ -2302,12 +2462,14 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
 
 		// Don't print out non-existent textures.
 		if (j != 0)
-			llinfos << out.str() << llendl;
+		{
+			LL_DEBUGS("Avatar") << out.str() << LL_ENDL;
+		}
 	}
-	llinfos << "\t Time points for each upload (start / finish)" << llendl;
+	LL_DEBUGS("Avatar") << "\t Time points for each upload (start / finish)" << llendl;
 	for (U32 i = 0; i < LLVOAvatarDefines::BAKED_NUM_INDICES; ++i)
 	{
-		llinfos << "\t\t (" << i << ") \t" << (S32)mDebugBakedTextureTimes[i][0] << " / " << (S32)mDebugBakedTextureTimes[i][1] << llendl;
+		LL_DEBUGS("Avatar") << "\t\t (" << i << ") \t" << (S32)mDebugBakedTextureTimes[i][0] << " / " << (S32)mDebugBakedTextureTimes[i][1] << llendl;
 	}
 
 	for (LLVOAvatarDefines::LLVOAvatarDictionary::BakedTextures::const_iterator baked_iter = LLVOAvatarDefines::LLVOAvatarDictionary::getInstance()->getBakedTextures().begin();
@@ -2319,15 +2481,16 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
 		if (!layerset) continue;
 		const LLTexLayerSetBuffer *layerset_buffer = layerset->getComposite();
 		if (!layerset_buffer) continue;
-		llinfos << layerset_buffer->dumpTextureInfo() << llendl;
+		LL_DEBUGS("Avatar") << layerset_buffer->dumpTextureInfo() << llendl;
 	}
 }
 
 void LLVOAvatarSelf::outputRezTiming(const std::string& msg) const
 {
-	LL_DEBUGS("Avatar Rez")
+	LL_INFOS("Avatar")
+		<< avString()
 		<< llformat("%s. Time from avatar creation: %.2f", msg.c_str(), mDebugSelfLoadTimer.getElapsedTimeF32())
-		<< llendl;
+		<< LL_ENDL;
 }
 
 void LLVOAvatarSelf::reportAvatarRezTime() const
@@ -2351,6 +2514,18 @@ void LLVOAvatarSelf::setCachedBakedTexture( ETextureIndex te, const LLUUID& uuid
 	{
 		if ( mBakedTextureDatas[i].mTextureIndex == te && mBakedTextureDatas[i].mTexLayerSet)
 		{
+			if (mInitialBakeIDs[i] != LLUUID::null)
+			{
+				if (mInitialBakeIDs[i] == uuid)
+				{
+					llinfos << "baked texture correctly loaded at login! " << i << llendl;
+				}
+				else
+				{
+					llwarns << "baked texture does not match id loaded at login!" << i << llendl;
+				}
+				mInitialBakeIDs[i] = LLUUID::null;
+			}
 			mBakedTextureDatas[i].mTexLayerSet->cancelUpload();
 		}
 	}
@@ -2478,6 +2653,20 @@ LLTexLayerSet* LLVOAvatarSelf::getLayerSet(ETextureIndex index) const
 	return NULL;
 }
 
+LLTexLayerSet* LLVOAvatarSelf::getLayerSet(EBakedTextureIndex baked_index) const
+{
+       /* switch(index)
+               case TEX_HEAD_BAKED:
+               case TEX_HEAD_BODYPAINT:
+                       return mHeadLayerSet; */
+       if (baked_index >= 0 && baked_index < BAKED_NUM_INDICES)
+       {
+                       return mBakedTextureDatas[baked_index].mTexLayerSet;
+       }
+       return NULL;
+}
+
+
 // static
 void LLVOAvatarSelf::onCustomizeStart()
 {
@@ -2558,49 +2747,6 @@ BOOL LLVOAvatarSelf::needsRenderBeam()
 // static
 void LLVOAvatarSelf::deleteScratchTextures()
 {
-	if(gAuditTexture)
-	{
-		S32 total_tex_size = sScratchTexBytes ;
-		S32 tex_size = SCRATCH_TEX_WIDTH * SCRATCH_TEX_HEIGHT ;
-
-		if( sScratchTexNames.checkData( GL_LUMINANCE ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_ALPHA ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_COLOR_INDEX ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 1, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= tex_size ;
-		}
-		if( sScratchTexNames.checkData( LLRender::sGLCoreProfile ? GL_RG : GL_LUMINANCE_ALPHA ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 2, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 2 * tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_RGB ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 3, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 3 * tex_size ;
-		}
-		if( sScratchTexNames.checkData( GL_RGBA ) )
-		{
-			LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 4 * tex_size ;
-		}
-		//others
-		while(total_tex_size > 0)
-		{
-			LLImageGL::decTextureCounter(tex_size, 4, LLViewerTexture::AVATAR_SCRATCH_TEX) ;
-			total_tex_size -= 4 * tex_size ;
-		}
-	}
-
 	for( LLGLuint* namep = sScratchTexNames.getFirstData(); 
 		 namep; 
 		 namep = sScratchTexNames.getNextData() )
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index 655fb3a012a03832aee569f8456738c17ee495e6..543891ca632366a759fdcaa83b12e60449c330b7 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -93,15 +93,27 @@ public:
 	/*virtual*/ void updateVisualParams();
 	/*virtual*/ void idleUpdateAppearanceAnimation();
 
+	/*virtual*/ U32  processUpdateMessage(LLMessageSystem *mesgsys,
+													 void **user_data,
+													 U32 block_num,
+													 const EObjectUpdateType update_type,
+													 LLDataPacker *dp);
+
 private:
 	// helper function. Passed in param is assumed to be in avatar's parameter list.
 	BOOL setParamWeight(LLViewerVisualParam *param, F32 weight, BOOL upload_bake = FALSE );
 
 
+
 /**                    Initialization
  **                                                                            **
  *******************************************************************************/
 
+private:
+	LLUUID mInitialBakeIDs[6];
+	bool mInitialBakesLoaded;
+
+
 /********************************************************************************
  **                                                                            **
  **                    STATE
@@ -121,7 +133,7 @@ public:
 	// Loading state
 	//--------------------------------------------------------------------
 public:
-	/*virtual*/ BOOL    getIsCloud();
+	/*virtual*/ BOOL    getIsCloud() const;
 
 	//--------------------------------------------------------------------
 	// Region state
@@ -229,6 +241,7 @@ public:
 	void				requestLayerSetUpload(LLVOAvatarDefines::EBakedTextureIndex i);
 	void				requestLayerSetUpdate(LLVOAvatarDefines::ETextureIndex i);
 	LLTexLayerSet*		getLayerSet(LLVOAvatarDefines::ETextureIndex index) const;
+	LLTexLayerSet* 		getLayerSet(LLVOAvatarDefines::EBakedTextureIndex baked_index) const;
 	
 	//--------------------------------------------------------------------
 	// Composites
@@ -369,6 +382,8 @@ public:
 	const LLTexLayerSet*  	debugGetLayerSet(LLVOAvatarDefines::EBakedTextureIndex index) const { return mBakedTextureDatas[index].mTexLayerSet; }
 	const std::string		debugDumpLocalTextureDataInfo(const LLTexLayerSet* layerset) const; // Lists out state of this particular baked texture layer
 	const std::string		debugDumpAllLocalTextureDataInfo() const; // Lists out which baked textures are at highest LOD
+	LLSD					metricsData();
+	void					sendAppearanceChangeMetrics(); // send data associated with completing a change.
 private:
 	LLFrameTimer    		mDebugSelfLoadTimer;
 	F32						mDebugTimeWearablesLoaded;
@@ -387,4 +402,9 @@ extern LLPointer<LLVOAvatarSelf> gAgentAvatarp;
 
 BOOL isAgentAvatarValid();
 
+void selfStartPhase(const std::string& phase_name);
+void selfStopPhase(const std::string& phase_name);
+void selfStopAllPhases();
+void selfClearPhases();
+
 #endif // LL_VO_AVATARSELF_H
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index df1d3f2955730da098c2eb413ce31ac898aa2822..820d1d73e149434cddde9f7f8bd9bc72b6baf63c 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -27,8 +27,6 @@
 #include "llviewerprecompiledheaders.h"
 #include "llvoicevivox.h"
 
-#include <boost/tokenizer.hpp>
-
 #include "llsdutil.h"
 
 // Linden library includes
@@ -47,6 +45,7 @@
 #include "llbase64.h"
 #include "llviewercontrol.h"
 #include "llappviewer.h"	// for gDisconnected, gDisableVoice
+#include "llprocess.h"
 
 // Viewer includes
 #include "llmutelist.h"  // to check for muted avatars
@@ -242,59 +241,21 @@ void LLVivoxVoiceClientCapResponder::result(const LLSD& content)
 	}
 }
 
-
-
-#if LL_WINDOWS
-static HANDLE sGatewayHandle = 0;
+static LLProcessPtr sGatewayPtr;
 
 static bool isGatewayRunning()
 {
-	bool result = false;
-	if(sGatewayHandle != 0)		
-	{
-		DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
-		if(waitresult != WAIT_OBJECT_0)
-		{
-			result = true;
-		}			
-	}
-	return result;
-}
-static void killGateway()
-{
-	if(sGatewayHandle != 0)
-	{
-		TerminateProcess(sGatewayHandle,0);
-	}
-}
-
-#else // Mac and linux
-
-static pid_t sGatewayPID = 0;
-static bool isGatewayRunning()
-{
-	bool result = false;
-	if(sGatewayPID != 0)
-	{
-		// A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists.
-		if(kill(sGatewayPID, 0) == 0)
-		{
-			result = true;
-		}
-	}
-	return result;
+	return sGatewayPtr && sGatewayPtr->isRunning();
 }
 
 static void killGateway()
 {
-	if(sGatewayPID != 0)
+	if (sGatewayPtr)
 	{
-		kill(sGatewayPID, SIGTERM);
+		sGatewayPtr->kill();
 	}
 }
 
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 LLVivoxVoiceClient::LLVivoxVoiceClient() :
@@ -790,7 +751,7 @@ void LLVivoxVoiceClient::stateMachine()
 			}
 			else if(!isGatewayRunning())
 			{
-				if(true)
+				if (true)           // production build, not test
 				{
 					// Launch the voice daemon
 					
@@ -809,102 +770,33 @@ void LLVivoxVoiceClient::stateMachine()
 #endif
 					// See if the vivox executable exists
 					llstat s;
-					if(!LLFile::stat(exe_path, &s))
+					if (!LLFile::stat(exe_path, &s))
 					{
 						// vivox executable exists.  Build the command line and launch the daemon.
+						LLProcess::Params params;
+						params.executable = exe_path;
 						// SLIM SDK: these arguments are no longer necessary.
 //						std::string args = " -p tcp -h -c";
-						std::string args;
-						std::string cmd;
 						std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
-						
 						if(loglevel.empty())
 						{
 							loglevel = "-1";	// turn logging off completely
 						}
-						
-						args += " -ll ";
-						args += loglevel;
-						
-						LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL;
-
-#if LL_WINDOWS
-						PROCESS_INFORMATION pinfo;
-						STARTUPINFOA sinfo;
-						
-						memset(&sinfo, 0, sizeof(sinfo));
-						
-						std::string exe_dir = gDirUtilp->getAppRODataDir();
-						cmd = "SLVoice.exe";
-						cmd += args;
-
-						// So retarded.  Windows requires that the second parameter to CreateProcessA be writable (non-const) string...
-						char *args2 = new char[args.size() + 1];
-						strcpy(args2, args.c_str());
-						if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo))
-						{
-//							DWORD dwErr = GetLastError();
-						}
-						else
-						{
-							// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
-							// CloseHandle(pinfo.hProcess); // stops leaks - nothing else
-							sGatewayHandle = pinfo.hProcess;
-							CloseHandle(pinfo.hThread); // stops leaks - nothing else
-						}		
-						
-						delete[] args2;
-#else	// LL_WINDOWS
-						// This should be the same for mac and linux
-						{
-							std::vector<std::string> arglist;
-							arglist.push_back(exe_path);
-							
-							// Split the argument string into separate strings for each argument
-							typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
-							boost::char_separator<char> sep(" ");
-							tokenizer tokens(args, sep);
-							tokenizer::iterator token_iter;
 
-							for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
-							{
-								arglist.push_back(*token_iter);
-							}
-							
-							// create an argv vector for the child process
-							char **fakeargv = new char*[arglist.size() + 1];
-							int i;
-							for(i=0; i < arglist.size(); i++)
-								fakeargv[i] = const_cast<char*>(arglist[i].c_str());
+						params.args.add("-ll");
+						params.args.add(loglevel);
+						params.cwd = gDirUtilp->getAppRODataDir();
+						sGatewayPtr = LLProcess::create(params);
 
-							fakeargv[i] = NULL;
-							
-							fflush(NULL); // flush all buffers before the child inherits them
-							pid_t id = vfork();
-							if(id == 0)
-							{
-								// child
-								execv(exe_path.c_str(), fakeargv);
-								
-								// If we reach this point, the exec failed.
-								// Use _exit() instead of exit() per the vfork man page.
-								_exit(0);
-							}
-
-							// parent
-							delete[] fakeargv;
-							sGatewayPID = id;
-						}
-#endif	// LL_WINDOWS
 						mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
-					}	
+					}
 					else
 					{
 						LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
-					}	
+					}
 				}
 				else
-				{		
+				{
 					// SLIM SDK: port changed from 44124 to 44125.
 					// We can connect to a client gateway running on another host.  This is useful for testing.
 					// To do this, launch the gateway on a nearby host like this:
diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp
index 0f7f63061be64291f9f58b2aef42ba6b6a30b66c..402504933cd9e3ea8e4e5fdb5ccb5da7f94cceeb 100644
--- a/indra/newview/llwearable.cpp
+++ b/indra/newview/llwearable.cpp
@@ -810,6 +810,20 @@ const LLLocalTextureObject* LLWearable::getLocalTextureObject(S32 index) const
 	return NULL;
 }
 
+std::vector<LLLocalTextureObject*> LLWearable::getLocalTextureListSeq()
+{
+	std::vector<LLLocalTextureObject*> result;
+
+	for(te_map_t::const_iterator iter = mTEMap.begin();
+		iter != mTEMap.end(); iter++)
+	{
+		LLLocalTextureObject* lto = iter->second;
+		result.push_back(lto);
+	}
+
+	return result;
+}
+
 void LLWearable::setLocalTextureObject(S32 index, LLLocalTextureObject &lto)
 {
 	if( mTEMap.find(index) != mTEMap.end() )
diff --git a/indra/newview/llwearable.h b/indra/newview/llwearable.h
index fd614ade64b5b3c46d712df76d3c8b381aac3d25..3d8c53a755c3a17efe810d10d272b224f0d750f8 100644
--- a/indra/newview/llwearable.h
+++ b/indra/newview/llwearable.h
@@ -106,6 +106,7 @@ public:
 
 	LLLocalTextureObject* getLocalTextureObject(S32 index);
 	const LLLocalTextureObject* getLocalTextureObject(S32 index) const;
+	std::vector<LLLocalTextureObject*> getLocalTextureListSeq();
 
 	void				setLocalTextureObject(S32 index, LLLocalTextureObject &lto);
 	void				addVisualParam(LLVisualParam *param);
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index e1d3c802da411a331d00e55bb0b85dbb58cdff8f..b061c90d98c6d8b126e63dd04f3676956ed48c6b 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -1187,20 +1187,23 @@ void LLWorld::getAvatars(uuid_vec_t* avatar_ids, std::vector<LLVector3d>* positi
 		iter != LLCharacter::sInstances.end(); ++iter)
 	{
 		LLVOAvatar* pVOAvatar = (LLVOAvatar*) *iter;
-		LLVector3d pos_global = pVOAvatar->getPositionGlobal();
-		LLUUID uuid = pVOAvatar->getID();
-		if( !pVOAvatar->isDead()
-			&& !pVOAvatar->isSelf()
-			&& !uuid.isNull() &&
-			dist_vec_squared(pos_global, relative_to) <= radius_squared)
+
+		if (!pVOAvatar->isDead() && !pVOAvatar->isSelf())
 		{
-			if(positions != NULL)
-			{
-				positions->push_back(pos_global);
-			}
-			if(avatar_ids !=NULL)
+			LLVector3d pos_global = pVOAvatar->getPositionGlobal();
+			LLUUID uuid = pVOAvatar->getID();
+
+			if (!uuid.isNull()
+				&& dist_vec_squared(pos_global, relative_to) <= radius_squared)
 			{
-				avatar_ids->push_back(uuid);
+				if(positions != NULL)
+				{
+					positions->push_back(pos_global);
+				}
+				if(avatar_ids !=NULL)
+				{
+					avatar_ids->push_back(uuid);
+				}
 			}
 		}
 	}
@@ -1231,6 +1234,11 @@ void LLWorld::getAvatars(uuid_vec_t* avatar_ids, std::vector<LLVector3d>* positi
 	}
 }
 
+bool LLWorld::isRegionListed(const LLViewerRegion* region) const
+{
+	region_list_t::const_iterator it = find(mRegionList.begin(), mRegionList.end(), region);
+	return it != mRegionList.end();
+}
 
 LLHTTPRegistration<LLEstablishAgentCommunication>
 	gHTTPRegistrationEstablishAgentCommunication(
diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h
index d8ab4bc5080e4b6833ba1b593307ccf34525eb05..f350009d100c09f6d10f4b0cc4f1014716f88995 100644
--- a/indra/newview/llworld.h
+++ b/indra/newview/llworld.h
@@ -157,6 +157,11 @@ public:
 		std::vector<LLVector3d>* positions = NULL, 
 		const LLVector3d& relative_to = LLVector3d(), F32 radius = FLT_MAX) const;
 
+	// Returns 'true' if the region is in mRegionList,
+	// 'false' if the region has been removed due to region change
+	// or if the circuit to this simulator had been lost.
+	bool isRegionListed(const LLViewerRegion* region) const;
+
 private:
 	region_list_t	mActiveRegionList;
 	region_list_t	mRegionList;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index de7296b2dcf98fdc76b7141d11cab2b1f6cbf457..ed636a40b290ae73045d8fb8b981a05d8e6e5cf7 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -848,9 +848,10 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
 
 		if (shadow_detail > 0)
 		{ //allocate 4 sun shadow maps
+			U32 sun_shadow_map_width = ((U32(resX*scale)+1)&~1); // must be even to avoid a stripe in the horizontal shadow blur
 			for (U32 i = 0; i < 4; i++)
 			{
-				if (!mShadow[i].allocate(U32(resX*scale),U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE)) return false;
+				if (!mShadow[i].allocate(sun_shadow_map_width,U32(resY*scale), 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE)) return false;
 			}
 		}
 		else
@@ -866,9 +867,10 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples)
 
 		if (shadow_detail > 1)
 		{ //allocate two spot shadow maps
+			U32 spot_shadow_map_width = width;
 			for (U32 i = 4; i < 6; i++)
 			{
-				if (!mShadow[i].allocate(width, height, 0, TRUE, FALSE)) return false;
+				if (!mShadow[i].allocate(spot_shadow_map_width, height, 0, TRUE, FALSE)) return false;
 			}
 		}
 		else
@@ -1038,11 +1040,7 @@ void LLPipeline::releaseGLBuffers()
 		mTrueNoiseMap = 0;
 	}
 
-	if (mLightFunc)
-	{
-		LLImageGL::deleteTextures(1, &mLightFunc);
-		mLightFunc = 0;
-	}
+	releaseLUTBuffers();
 
 	mWaterRef.release();
 	mWaterDis.release();
@@ -1058,6 +1056,15 @@ void LLPipeline::releaseGLBuffers()
 	LLVOAvatar::resetImpostors();
 }
 
+void LLPipeline::releaseLUTBuffers()
+{
+	if (mLightFunc)
+	{
+		LLImageGL::deleteTextures(1, &mLightFunc);
+		mLightFunc = 0;
+	}
+}
+
 void LLPipeline::releaseScreenBuffers()
 {
 	mUIScreen.release();
@@ -1153,50 +1160,69 @@ void LLPipeline::createGLBuffers()
 			gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
 		}
 
+		createLUTBuffers();
+	}
+
+	gBumpImageList.restoreGL();
+}
+
+void LLPipeline::createLUTBuffers()
+{
+	if (sRenderDeferred)
+	{
 		if (!mLightFunc)
 		{
 			U32 lightResX = gSavedSettings.getU32("RenderSpecularResX");
 			U32 lightResY = gSavedSettings.getU32("RenderSpecularResY");
-			U8* lg = new U8[lightResX*lightResY];
-
+			U8* ls = new U8[lightResX*lightResY];
+			F32 specExp = gSavedSettings.getF32("RenderSpecularExponent");
+            // Calculate the (normalized) Blinn-Phong specular lookup texture.
 			for (U32 y = 0; y < lightResY; ++y)
 			{
 				for (U32 x = 0; x < lightResX; ++x)
 				{
-					//spec func
+					ls[y*lightResX+x] = 0;
 					F32 sa = (F32) x/(lightResX-1);
 					F32 spec = (F32) y/(lightResY-1);
-					//lg[y*lightResX+x] = (U8) (powf(sa, 128.f*spec*spec)*255);
-
-					//F32 sp = acosf(sa)/(1.f-spec);
-
-					sa = powf(sa, gSavedSettings.getF32("RenderSpecularExponent"));
-					F32 a = acosf(sa*0.25f+0.75f);
-					F32 m = llmax(0.5f-spec*0.5f, 0.001f);
-					F32 t2 = tanf(a)/m;
-					t2 *= t2;
-
-					F32 c4a = (3.f+4.f*cosf(2.f*a)+cosf(4.f*a))/8.f;
-					F32 bd = 1.f/(4.f*m*m*c4a)*powf(F_E, -t2);
-
-					lg[y*lightResX+x] = (U8) (llclamp(bd, 0.f, 1.f)*255);
+					F32 n = spec * spec * specExp;
+					
+					// Nothing special here.  Just your typical blinn-phong term.
+					spec = powf(sa, n);
+					
+					// Apply our normalization function.
+					// Note: This is the full equation that applies the full normalization curve, not an approximation.
+					// This is fine, given we only need to create our LUT once per buffer initialization.
+					// The only trade off is we have a really low dynamic range.
+					// This means we have to account for things not being able to exceed 0 to 1 in our shaders.
+					spec *= (((n + 2) * (n + 4)) / (8 * F_PI * (powf(2, -n/2) + n)));
+					
+					// Always sample at a 1.0/2.2 curve.
+					// This "Gamma corrects" our specular term, boosting our lower exponent reflections.
+					spec = powf(spec, 1.f/2.2f);
+					
+					// Easy fix for our dynamic range problem: divide by 6 here, multiply by 6 in our shaders.
+					// This allows for our specular term to exceed a value of 1 in our shaders.
+					// This is something that can be important for energy conserving specular models where higher exponents can result in highlights that exceed a range of 0 to 1.
+					// Technically, we could just use an R16F texture, but driver support for R16F textures can be somewhat spotty at times.
+					// This works remarkably well for higher specular exponents, though banding can sometimes be seen on lower exponents.
+					// Combined with a bit of noise and trilinear filtering, the banding is hardly noticable.
+					ls[y*lightResX+x] = (U8)(llclamp(spec * (1.f / 6), 0.f, 1.f) * 255);
 				}
 			}
-
+			
 			LLImageGL::generateTextures(1, &mLightFunc);
 			gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mLightFunc);
-			LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_R8, lightResX, lightResY, GL_RED, GL_UNSIGNED_BYTE, lg, false);
+			LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_R8, lightResX, lightResY, GL_RED, GL_UNSIGNED_BYTE, ls, false);
 			gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP);
 			gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR);
-
-			delete [] lg;
+			
+			delete [] ls;
 		}
 	}
-
-	gBumpImageList.restoreGL();
 }
 
-void LLPipeline::restoreGL() 
+
+void LLPipeline::restoreGL()
 {
 	LLMemType mt_cb(LLMemType::MTYPE_PIPELINE_RESTORE_GL);
 	assertInitialized();
@@ -8274,7 +8300,7 @@ static LLFastTimer::DeclareTimer FTM_SHADOW_RENDER("Render Shadows");
 static LLFastTimer::DeclareTimer FTM_SHADOW_ALPHA("Alpha Shadow");
 static LLFastTimer::DeclareTimer FTM_SHADOW_SIMPLE("Simple Shadow");
 
-void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& shadow_cam, LLCullResult &result, BOOL use_shader, BOOL use_occlusion)
+void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& shadow_cam, LLCullResult &result, BOOL use_shader, BOOL use_occlusion, U32 target_width)
 {
 	LLFastTimer t(FTM_SHADOW_RENDER);
 
@@ -8365,7 +8391,8 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera
 		LLFastTimer ftm(FTM_SHADOW_ALPHA);
 		gDeferredShadowAlphaMaskProgram.bind();
 		gDeferredShadowAlphaMaskProgram.setMinimumAlpha(0.598f);
-		
+		gDeferredShadowAlphaMaskProgram.uniform1f(LLShaderMgr::DEFERRED_SHADOW_TARGET_WIDTH, (float)target_width);
+
 		U32 mask =	LLVertexBuffer::MAP_VERTEX | 
 					LLVertexBuffer::MAP_TEXCOORD0 | 
 					LLVertexBuffer::MAP_COLOR | 
@@ -9218,11 +9245,13 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 			mShadow[j].getViewport(gGLViewport);
 			mShadow[j].clear();
 		
+			U32 target_width = mShadow[j].getWidth();
+
 			{
 				static LLCullResult result[4];
 
 				//LLGLEnable enable(GL_DEPTH_CLAMP_NV);
-				renderShadow(view[j], proj[j], shadow_cam, result[j], TRUE);
+				renderShadow(view[j], proj[j], shadow_cam, result[j], TRUE, TRUE, target_width);
 			}
 
 			mShadow[j].flush();
@@ -9361,11 +9390,13 @@ void LLPipeline::generateSunShadow(LLCamera& camera)
 			mShadow[i+4].getViewport(gGLViewport);
 			mShadow[i+4].clear();
 
+			U32 target_width = mShadow[i+4].getWidth();
+
 			static LLCullResult result[2];
 
 			LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW0+i+4;
 
-			renderShadow(view[i+4], proj[i+4], shadow_cam, result[i], FALSE, FALSE);
+			renderShadow(view[i+4], proj[i+4], shadow_cam, result[i], FALSE, FALSE, target_width);
 
 			mShadow[i+4].flush();
  		}
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index 252fe1346ce026bc0fb205080497049b98b96d38..5c623fc9f2dce8a634333893851be7ae6bf18db6 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -114,8 +114,10 @@ public:
 	void doResetVertexBuffers();
 	void resizeScreenTexture();
 	void releaseGLBuffers();
+	void releaseLUTBuffers();
 	void releaseScreenBuffers();
 	void createGLBuffers();
+	void createLUTBuffers();
 
 	void allocateScreenBuffer(U32 resX, U32 resY);
 	bool allocateScreenBuffer(U32 resX, U32 resY, U32 samples);
@@ -262,7 +264,7 @@ public:
 	void setHighlightObject(LLDrawable* obj) { mHighlightObject = obj; }
 
 
-	void renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& camera, LLCullResult& result, BOOL use_shader = TRUE, BOOL use_occlusion = TRUE);
+	void renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& camera, LLCullResult& result, BOOL use_shader, BOOL use_occlusion, U32 target_width);
 	void renderHighlights();
 	void renderDebug();
 	void renderPhysicsDisplay();
@@ -462,6 +464,7 @@ public:
 		RENDER_DEBUG_LOD_INFO	        = 0x04000000,
 		RENDER_DEBUG_RENDER_COMPLEXITY  = 0x08000000,
 		RENDER_DEBUG_ATTACHMENT_BYTES	= 0x10000000,
+		RENDER_DEBUG_TEXEL_DENSITY		= 0x20000000
 	};
 
 public:
diff --git a/indra/newview/skins/default/xui/de/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/de/floater_animation_anim_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3dc554b1202513a35110a0cbe34cf6ab2c0f1172
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/floater_animation_anim_preview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Anim Preview" title="ANIMATION.ANIM">
+	<text name="name_label">
+		Name:
+	</text>
+	<text name="description_label">
+		Beschreibung:
+	</text>
+	<button label="Hochladen (L$ [AMOUNT])" name="ok_btn"/>
+	<button label="Abbrechen" label_selected="Abbrechen" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/de/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/de/floater_animation_bvh_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9a6f5e016694ad96673d26ee038f56393e38423a
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/floater_animation_bvh_preview.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Animation Preview">
+	<floater.string name="failed_to_initialize">
+		Bewegung konnte nicht initialisiert werden
+	</floater.string>
+	<floater.string name="anim_too_long">
+		Animationsdatei ist [LENGTH] Sekunden lang.
+
+Maximal erlaubt sind [MAX_LENGTH] Sekunden.
+	</floater.string>
+	<floater.string name="failed_file_read">
+		Animationsdatei kann nicht gelesen werden.
+
+[STATUS]
+	</floater.string>
+	<floater.string name="E_ST_OK">
+		OK
+	</floater.string>
+	<floater.string name="E_ST_EOF">
+		Vorzeitiges Dateiende.
+	</floater.string>
+	<floater.string name="E_ST_NO_CONSTRAINT">
+		Constraint-Definition kann nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_FILE">
+		BVH-Datei kann nicht geöffnet werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_HIER">
+		Ungültige HIERARCHY-Kopfzeile.
+	</floater.string>
+	<floater.string name="E_ST_NO_JOINT">
+		ROOT oder JOINT nicht gefunden.
+	</floater.string>
+	<floater.string name="E_ST_NO_NAME">
+		JOINT-Name nicht erfasst.
+	</floater.string>
+	<floater.string name="E_ST_NO_OFFSET">
+		OFFSET nicht gefunden.
+	</floater.string>
+	<floater.string name="E_ST_NO_CHANNELS">
+		CHANNELS nicht gefunden.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROTATION">
+		Rotationsreihenfolge kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_AXIS">
+		Rotationsachse kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_MOTION">
+		MOTION nicht gefunden.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAMES">
+		Anzahl der Bilder kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAME_TIME">
+		Bildzeit kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_POS">
+		Positionswerte können nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROT">
+		Rotationswerte können nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_FILE">
+		Transformationsdatei kann nicht geöffnet werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HEADER">
+		Transformationskopfzeile kann nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_NAME">
+		Transformationsnamen können nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_IGNORE">
+		Ignorieren-Transformationswert kann nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_RELATIVE">
+		Relativer Transformationswert kann nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_OUTNAME">
+		Outname-Transformationswert kann nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MATRIX">
+		Transformationsmatrix kann nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGECHILD">
+		Mergechild-Name kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGEPARENT">
+		Mergeparent-Name kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_PRIORITY">
+		Priority-Wert kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_LOOP">
+		Loop-Wert kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEIN">
+		EaseIn-Werte können nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEOUT">
+		EaseOut-Werte können nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HAND">
+		Hand-Morph-Wert kann nicht erfasst werden.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EMOTE">
+		Emote-Name kann nicht gelesen werden.
+	</floater.string>
+	<floater.string name="E_ST_BAD_ROOT">
+		Falscher Root-Joint-Name, „hip“ verwenden.
+	</floater.string>
+	<text name="name_label">
+		Name:
+	</text>
+	<text name="description_label">
+		Beschreibung:
+	</text>
+	<spinner label="Priorität" name="priority" tool_tip="Steuert, welche Animationen von dieser Animation überschrieben werden können"/>
+	<check_box label="Schleife" name="loop_check" tool_tip="Erzeugt eine Animationsschleife"/>
+	<spinner label="Ein (%)" name="loop_in_point" tool_tip="Anfang einer Animationsschleife festlegen"/>
+	<spinner label="Aus (%)" name="loop_out_point" tool_tip="Ende einer Animationsschleife festlegen"/>
+	<text name="hand_label">
+		Handhaltung
+	</text>
+	<combo_box name="hand_pose_combo" tool_tip="Steuert während der Animation die Bewegung der Hände">
+		<combo_box.item label="Dehnen" name="Spread"/>
+		<combo_box.item label="Entspannt" name="Relaxed"/>
+		<combo_box.item label="Beide zeigen" name="PointBoth"/>
+		<combo_box.item label="Faust" name="Fist"/>
+		<combo_box.item label="Links entspannt" name="RelaxedLeft"/>
+		<combo_box.item label="Nach links zeigen" name="PointLeft"/>
+		<combo_box.item label="Linke Faust" name="FistLeft"/>
+		<combo_box.item label="Rechts entspannt" name="RelaxedRight"/>
+		<combo_box.item label="Nach rechts zeigen" name="PointRight"/>
+		<combo_box.item label="Rechte Faust" name="FistRight"/>
+		<combo_box.item label="Rechts salutieren" name="SaluteRight"/>
+		<combo_box.item label="Tippen" name="Typing"/>
+		<combo_box.item label="Friedensgeste rechts" name="PeaceRight"/>
+	</combo_box>
+	<text name="emote_label">
+		Ausdruck
+	</text>
+	<combo_box name="emote_combo" tool_tip="Steuert Gesichtsregungen während der Animation">
+		<item label="(Keine)" name="[None]" value=""/>
+		<item label="Aaaaah" name="Aaaaah" value="Aaaaah"/>
+		<item label="Ängstlich" name="Afraid" value="Ängstlich"/>
+		<item label="Verärgert" name="Angry" value="Verärgert"/>
+		<item label="Grinst" name="BigSmile" value="Grinst"/>
+		<item label="Gelangweilt" name="Bored" value="Gelangweilt"/>
+		<item label="Weinen" name="Cry" value="Weinen"/>
+		<item label="Verachtung" name="Disdain" value="Verachtung"/>
+		<item label="Verlegen" name="Embarrassed" value="Verlegen"/>
+		<item label="Stirnrunzeln" name="Frown" value="Stirnrunzeln"/>
+		<item label="Küssen" name="Kiss" value="Küssen"/>
+		<item label="Lachen" name="Laugh" value="Lachen"/>
+		<item label="Bäääh" name="Plllppt" value="Bäääh"/>
+		<item label="Angewidert" name="Repulsed" value="Angewidert"/>
+		<item label="Traurig" name="Sad" value="Traurig"/>
+		<item label="Achselzucken" name="Shrug" value="Achselzucken"/>
+		<item label="Lächeln" name="Smile" value="Lächeln"/>
+		<item label="Ãœberraschung" name="Surprise" value="Ãœberraschung"/>
+		<item label="Zwinkern" name="Wink" value="Zwinkern"/>
+		<item label="Sorgenvoll" name="Worry" value="Sorgenvoll"/>
+	</combo_box>
+	<text name="preview_label">
+		Vorschau während:
+	</text>
+	<combo_box name="preview_base_anim" tool_tip="Hiermit können Sie das Verhalten Ihres Avatars testen, während Ihr Avatar normale Bewegungen ausführt.">
+		<item label="Stehen" name="Standing" value="Stehen"/>
+		<item label="Gehen" name="Walking" value="Gehen"/>
+		<item label="Sitzen" name="Sitting" value="Sitzen"/>
+		<item label="Fliegen" name="Flying" value="Fliegen"/>
+	</combo_box>
+	<spinner label="Einblenden (s)" name="ease_in_time" tool_tip="Einblendungszeit für Animationen (in Sekunden)"/>
+	<spinner label="Ausblenden (s)" name="ease_out_time" tool_tip="Ausblendungszeit für Animationen (in Sekunden)"/>
+	<button name="play_btn" tool_tip="Animation abspielen"/>
+	<button name="pause_btn" tool_tip="Animation unterbrechen"/>
+	<button name="stop_btn" tool_tip="Animation anhalten"/>
+	<text name="bad_animation_text">
+		Animationsdatei kann nicht gelesen werden.
+
+Wir empfehlen exportierte BVH-Dateien aus Poser 4.
+	</text>
+	<button label="Hochladen (L$ [AMOUNT])" name="ok_btn"/>
+	<button label="Abbrechen" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/de/floater_preview_animation.xml b/indra/newview/skins/default/xui/de/floater_preview_animation.xml
index 3dcdb52555742654bda28f662f397831da939a89..2dd47a27ad810e897c14d2f74e8701c92647cb73 100644
--- a/indra/newview/skins/default/xui/de/floater_preview_animation.xml
+++ b/indra/newview/skins/default/xui/de/floater_preview_animation.xml
@@ -7,6 +7,6 @@
 		Beschreibung:
 	</text>
 	<line_editor left="108" name="desc" width="160"/>
-	<button label="Inworld abspielen" label_selected="Stopp" name="Anim play btn" tool_tip="Diese Animation so wiedergeben, dass andere sie sehen können." width="116"/>
-	<button label="Lokal abspielen" label_selected="Stopp" left="171" name="Anim audition btn" tool_tip="Diese Animation so wiedergeben, dass nur Sie sie sehen." width="116"/>
+	<button label="Inworld abspielen" label_selected="Stopp" name="Inworld" tool_tip="Diese Animation so wiedergeben, dass andere sie sehen können."/>
+	<button label="Lokal wiedergeben" label_selected="Stopp" name="Locally" tool_tip="Diese Animation so wiedergeben, dass nur Sie sie sehen."/>
 </floater>
diff --git a/indra/newview/skins/default/xui/de/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/de/floater_test_text_vertical_aligment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..506d2b013a27b48b2cbcd821b6b00a5e8234ab82
--- /dev/null
+++ b/indra/newview/skins/default/xui/de/floater_test_text_vertical_aligment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Test Floater" title="TEST-FENSTER"/>
diff --git a/indra/newview/skins/default/xui/de/floater_tools.xml b/indra/newview/skins/default/xui/de/floater_tools.xml
index cf1d03f32d25182d6b1457f3590267e88f0c910b..dee89b28e532a30fd7ca5e91e038d5c85d737f70 100644
--- a/indra/newview/skins/default/xui/de/floater_tools.xml
+++ b/indra/newview/skins/default/xui/de/floater_tools.xml
@@ -1,5 +1,20 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="toolbox floater" short_title="BAU-WERKZEUGE" title="">
+	<floater.string name="grid_screen_text">
+		Bildschirm
+	</floater.string>
+	<floater.string name="grid_local_text">
+		Lokal
+	</floater.string>
+	<floater.string name="grid_world_text">
+		Welt
+	</floater.string>
+	<floater.string name="grid_reference_text">
+		Referenz
+	</floater.string>
+	<floater.string name="grid_attachment_text">
+		Anhang
+	</floater.string>
 	<floater.string name="status_rotate">
 		An den farbigen Bändern ziehen, um das Objekt zu drehen
 	</floater.string>
@@ -63,7 +78,12 @@
 	</text>
 	<check_box initial_value="true" label="Texturen dehnen" name="checkbox stretch textures" top_pad="-5"/>
 	<check_box initial_value="true" label="Einrasten" name="checkbox snap to grid" top_pad="15"/>
-	<button label="Optionen..." label_selected="Optionen..." name="Options..." tool_tip="Mehr Raster-Optionen anzeigen"/>
+	<combo_box name="combobox grid mode" tool_tip="Wählen Sie das gewünschte Rasterlineal zum Positionieren des Objekts aus.">
+		<combo_box.item label="Welt" name="World"/>
+		<combo_box.item label="Lokal" name="Local"/>
+		<combo_box.item label="Referenz" name="Reference"/>
+	</combo_box>
+	<button label="" label_selected="Optionen..." name="Options..." tool_tip="Mehr Raster-Optionen anzeigen"/>
 	<button label="" label_selected="" name="ToolCube" tool_tip="Würfel"/>
 	<button label="" label_selected="" name="ToolPrism" tool_tip="Prisma"/>
 	<button label="" label_selected="" name="ToolPyramid" tool_tip="Pyramide"/>
diff --git a/indra/newview/skins/default/xui/de/floater_voice_effect.xml b/indra/newview/skins/default/xui/de/floater_voice_effect.xml
index 3dc0fc2322f2846f1344c513e3403fc52dc827f2..413a46525c56146876bcb8fe4ee226097d0b577f 100644
--- a/indra/newview/skins/default/xui/de/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/de/floater_voice_effect.xml
@@ -42,13 +42,16 @@
 	<string name="effect_Demon">
 		Dämon
 	</string>
+	<string name="effect_Female Elf">
+		Weibliche Elfe
+	</string>
 	<string name="effect_Flirty">
 		Kokett
 	</string>
 	<string name="effect_Foxy">
 		Attraktiv
 	</string>
-	<string name="effect_Halloween_2010_Bonus">
+	<string name="effect_Halloween 2010 Bonus">
 		Bonus_Halloween_2010
 	</string>
 	<string name="effect_Helium">
@@ -57,9 +60,18 @@
 	<string name="effect_Husky">
 		Rauchig
 	</string>
+	<string name="effect_Husky Whisper">
+		Rauchiges Flüstern
+	</string>
 	<string name="effect_Intercom">
 		Intercom
 	</string>
+	<string name="effect_Julia">
+		Julia
+	</string>
+	<string name="effect_Lo Lilt">
+		Leises Trällern
+	</string>
 	<string name="effect_Macho">
 		Macho
 	</string>
@@ -69,6 +81,9 @@
 	<string name="effect_Mini">
 		Mini
 	</string>
+	<string name="effect_Model">
+		Modell
+	</string>
 	<string name="effect_Nano">
 		Nano
 	</string>
@@ -90,6 +105,9 @@
 	<string name="effect_Roxanne">
 		Roxanne
 	</string>
+	<string name="effect_Rumble">
+		Rumpeln
+	</string>
 	<string name="effect_Sabrina">
 		Sabrina
 	</string>
@@ -102,6 +120,9 @@
 	<string name="effect_Shorty">
 		Shorty
 	</string>
+	<string name="effect_Smaller">
+		Kleiner
+	</string>
 	<string name="effect_Sneaky">
 		Hinterhältig
 	</string>
diff --git a/indra/newview/skins/default/xui/de/menu_inventory.xml b/indra/newview/skins/default/xui/de/menu_inventory.xml
index a82982f98636f292a8450a1e030332c36354f810..39b3099336ab2279fc1e0d9de0317f90283e7aec 100644
--- a/indra/newview/skins/default/xui/de/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/de/menu_inventory.xml
@@ -59,6 +59,7 @@
 	<menu_item_call label="Eigenschaften" name="Properties"/>
 	<menu_item_call label="Umbenennen" name="Rename"/>
 	<menu_item_call label="Asset-UUID kopieren" name="Copy Asset UUID"/>
+	<menu_item_call label="Ausschneiden" name="Cut"/>
 	<menu_item_call label="Kopieren" name="Copy"/>
 	<menu_item_call label="Einfügen" name="Paste"/>
 	<menu_item_call label="Als Link einfügen" name="Paste As Link"/>
diff --git a/indra/newview/skins/default/xui/de/menu_viewer.xml b/indra/newview/skins/default/xui/de/menu_viewer.xml
index a870a4c84d274cbdd1e10c4f2307d4e82494adb8..d011c7295cb7169d8b42bc6985b9e1f4238e2d69 100644
--- a/indra/newview/skins/default/xui/de/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/de/menu_viewer.xml
@@ -21,6 +21,7 @@
 			<menu_item_call label="Beschäftigt" name="Set Busy"/>
 		</menu>
 		<menu_item_call label="L$ kaufen..." name="Buy and Sell L$"/>
+		<menu_item_call label="Händler-Outbox..." name="MerchantOutbox"/>
 		<menu_item_call label="Kontoübersicht..." name="Manage My Account">
 			<menu_item_call.on_click name="ManageMyAccount_url" parameter="WebLaunchJoinNow,http://secondlife.com/account/index.php?lang=de"/>
 		</menu_item_call>
@@ -393,9 +394,16 @@
 		<menu_item_check label="HTTP-Texturen" name="HTTP Textures"/>
 		<menu_item_check label="HTTP-Inventar" name="HTTP Inventory"/>
 		<menu_item_call label="Bilder komprimieren" name="Compress Images"/>
+		<menu_item_call label="Visual Leak Detector aktivieren" name="Enable Visual Leak Detector"/>
 		<menu_item_check label="Ausgabe Fehlerbeseitigung ausgeben" name="Output Debug Minidump"/>
 		<menu_item_check label="Bei nächster Ausführung Fenster öffnen" name="Console Window"/>
-		<menu label="Protokollierungsstufe festlegen" name="Set Logging Level"/>
+		<menu label="Protokollierungsstufe festlegen" name="Set Logging Level">
+			<menu_item_check label="Debug" name="Debug"/>
+			<menu_item_check label="Info" name="Info"/>
+			<menu_item_check label="Warnung" name="Warning"/>
+			<menu_item_check label="Fehler" name="Error"/>
+			<menu_item_check label="Keine" name="None"/>
+		</menu>
 		<menu_item_call label="Admin-Status anfordern" name="Request Admin Options"/>
 		<menu_item_call label="Admin-Status verlassen" name="Leave Admin Options"/>
 		<menu_item_check label="Admin-Menü anzeigen" name="View Admin Options"/>
diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml
index b69bb197c8d2cf434b1e6341a479b3bc20fde957..ac068fcd4ea1be1ed135094464bac53d5e69ffa5 100644
--- a/indra/newview/skins/default/xui/de/notifications.xml
+++ b/indra/newview/skins/default/xui/de/notifications.xml
@@ -677,7 +677,7 @@ Erwartet wurde [VALIDS]
 		Ausgabedatei konnte nicht erstellt werden: [FILE]
 	</notification>
 	<notification name="DoNotSupportBulkAnimationUpload">
-		Der Mehrfach-Upload von Animationsdateien wird zurzeit von [APP_NAME] nicht unterstützt.
+		Der Bulk-Upload von BVH-Animationsdateien wird zurzeit von [APP_NAME] nicht unterstützt.
 	</notification>
 	<notification name="CannotUploadReason">
 		Datei [FILE] kann aus folgendem Grund nicht hochgeladen werden: [REASON]
@@ -2644,16 +2644,16 @@ Anfrage gestatten?
 		„&lt;nolink&gt;[TITLE]&lt;/nolink&gt;“ von [NAME]
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Blockieren"/>
-			<button name="Ignore" text="Ignorieren"/>
+			<button name="Client_Side_Mute" text="Blockieren"/>
+			<button name="Client_Side_Ignore" text="Ignorieren"/>
 		</form>
 	</notification>
 	<notification name="ScriptDialogGroup">
 		„&lt;nolink&gt;[TITLE]&lt;/nolink&gt;“ von [GROUPNAME]
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Blockieren"/>
-			<button name="Ignore" text="Ignorieren"/>
+			<button name="Client_Side_Mute" text="Blockieren"/>
+			<button name="Client_Side_Ignore" text="Ignorieren"/>
 		</form>
 	</notification>
 	<notification name="BuyLindenDollarSuccess">
diff --git a/indra/newview/skins/default/xui/de/panel_nearby_chat.xml b/indra/newview/skins/default/xui/de/panel_nearby_chat.xml
index 2068c39024366cdc6beeb9982bf44def6df939b3..07ad761791cd7bd3f7be2091ddcc65ad5928a0e1 100644
--- a/indra/newview/skins/default/xui/de/panel_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/de/panel_nearby_chat.xml
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="nearby_chat">
-	<check_box label="Chat übersetzen" name="translate_chat_checkbox"/>
+	<layout_stack name="stack">
+		<layout_panel name="translate_chat_checkbox_lp">
+			<check_box label="Chat übersetzen" name="translate_chat_checkbox"/>
+		</layout_panel>
+	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/de/panel_status_bar.xml b/indra/newview/skins/default/xui/de/panel_status_bar.xml
index 2493d60df6697edbbabbe0b81fdbef6d468decba..14ace0ac3af0d071f3365356a2017bffeacf7360 100644
--- a/indra/newview/skins/default/xui/de/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/de/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		[AMT] L$
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-415" name="balance_bg" width="205">
 		<text name="balance" tool_tip="Klicken, um L$-Guthaben zu aktualisieren" value="20 L$"/>
 		<button label="L$ kaufen" name="buyL" tool_tip="Hier klicken, um mehr L$ zu kaufen"/>
 		<button label="Einkaufen" name="goShop" tool_tip="Second Life-Marktplatz öffnen" width="85"/>
diff --git a/indra/newview/skins/default/xui/de/sidepanel_inventory.xml b/indra/newview/skins/default/xui/de/sidepanel_inventory.xml
index 4fc397e7d27dc86e597811e536560e8cd04f896d..18aad64f964dbf8058b93b98d676e510301b4a66 100644
--- a/indra/newview/skins/default/xui/de/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/de/sidepanel_inventory.xml
@@ -14,9 +14,9 @@
 					<text name="inbox_fresh_new_count">
 						[NUM] neu
 					</text>
-					<panel tool_tip="Drag and drop items to your inventory to manage and use them">
+					<panel name="inbox_inventory_placeholder_panel" tool_tip="Artikel zur Verwendung in Ihr Inventar ziehen">
 						<text name="inbox_inventory_placeholder">
-							Einkäufe auf dem Marktplatz werden hierher geliefert.
+							Einkäufe aus dem Marktplatz werden hierher geliefert.
 						</text>
 					</panel>
 				</panel>
diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml
index 44296f2619f64f44fedc4d169d154f3d47106d23..d917d33d12ee9426f2dc627fd62a0345137dba60 100644
--- a/indra/newview/skins/default/xui/de/strings.xml
+++ b/indra/newview/skins/default/xui/de/strings.xml
@@ -835,6 +835,9 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden.
 	<string name="anim_yes_head">
 		Ja
 	</string>
+	<string name="multiple_textures">
+		Mehrfach
+	</string>
 	<string name="texture_loading">
 		Wird geladen...
 	</string>
@@ -1235,7 +1238,7 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden.
 		Sie haben keine Kopie dieser Textur in Ihrem Inventar.
 	</string>
 	<string name="InventoryInboxNoItems">
-		Hier erscheinen bestimmte Artikel, die Sie erhalten, wie z. B. Premium-Geschenke. Sie können diese dann in Ihr Inventar ziehen.
+		Einkäufe aus dem Marktplatz erscheinen hier. Sie können diese dann zur Verwendung in Ihr Inventar ziehen.
 	</string>
 	<string name="MarketplaceURL">
 		https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
@@ -3921,6 +3924,9 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_
 	<string name="Saved_message">
 		(Gespeichert am [LONG_TIMESTAMP])
 	</string>
+	<string name="IM_unblock_only_groups_friends">
+		Wenn Sie diese Meldung sehen, müssen Sie unter „Einstellungen“ &gt; „Privatsphäre“ die Option „Nur IMs und Anrufe von Freunden oder Gruppen durchstellen“ deaktivieren.
+	</string>
 	<string name="answered_call">
 		Ihr Anruf wurde entgegengenommen
 	</string>
diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml
index c7e9ec781daa343bdbf3ff75220412b4df54548e..060d88900327b5adf81538625697af44b2c9d353 100644
--- a/indra/newview/skins/default/xui/en/floater_about.xml
+++ b/indra/newview/skins/default/xui/en/floater_about.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="centered"
+ positioning="centered"
  legacy_header_height="18"
  height="440"
  layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml
index 3a6c2678c1adfbb0ea37474d6b49ba3f5668fef2..fb123ec4d1df418d7077b74183834f804d6107b4 100644
--- a/indra/newview/skins/default/xui/en/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/en/floater_about_land.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  can_tear_off="false"
  height="420"
  layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/floater_avatar.xml b/indra/newview/skins/default/xui/en/floater_avatar.xml
index 82c340300823b5f509e9b421eadbc37b63c5a55b..cd5cca02bdb19023929b882956c8308713bb7f6b 100644
--- a/indra/newview/skins/default/xui/en/floater_avatar.xml
+++ b/indra/newview/skins/default/xui/en/floater_avatar.xml
@@ -1,13 +1,13 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  ignore_ui_scale="false"
  legacy_header_height="225"
  can_minimize="true"
  can_close="true"
  can_resize="true"
  min_height="230"
- min_width="450"
+ min_width="515"
  height="230"
  layout="topleft"
  name="Avatar"
diff --git a/indra/newview/skins/default/xui/en/floater_avatar_picker.xml b/indra/newview/skins/default/xui/en/floater_avatar_picker.xml
index cbbbeb609481a49ce10f0c3b6111d078e447f4b6..1a55dc2e2ce46108413c32671dd27c480783defe 100644
--- a/indra/newview/skins/default/xui/en/floater_avatar_picker.xml
+++ b/indra/newview/skins/default/xui/en/floater_avatar_picker.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  legacy_header_height="18"
  can_resize="true"
  height="350"
diff --git a/indra/newview/skins/default/xui/en/floater_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml
index 4673c6def59a729e2a241588dbff3ae60e175d09..22bc488a925377a06c974a10b9bd11393c3f726f 100644
--- a/indra/newview/skins/default/xui/en/floater_camera.xml
+++ b/indra/newview/skins/default/xui/en/floater_camera.xml
@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="specified"
- specified_left="458"
- specified_bottom="80"
+ positioning="specified"
+ left="458"
+ bottom="-80"
+ follows="left|bottom"
  legacy_header_height="18"
  can_minimize="true"
  can_close="true"
diff --git a/indra/newview/skins/default/xui/en/floater_chat_bar.xml b/indra/newview/skins/default/xui/en/floater_chat_bar.xml
index 63992462b34a33973cdd5675a6ad6eadfb203399..688a01ce7bc2d05a8b43a0ef906a73509c312bf3 100644
--- a/indra/newview/skins/default/xui/en/floater_chat_bar.xml
+++ b/indra/newview/skins/default/xui/en/floater_chat_bar.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="specified"
- specified_left="10"
- specified_bottom="10"
+ positioning="specified"
+ left="10"
+ bottom="-10"
  height="60"
  layout="topleft"
  legacy_header_height="25"
diff --git a/indra/newview/skins/default/xui/en/floater_critical.xml b/indra/newview/skins/default/xui/en/floater_critical.xml
index 13b15bf724060976b62fdae0f1a1437628d08e66..143bcb44300ad988dbc8590fa0d8bbed27255062 100644
--- a/indra/newview/skins/default/xui/en/floater_critical.xml
+++ b/indra/newview/skins/default/xui/en/floater_critical.xml
@@ -6,7 +6,7 @@
  height="500"
  layout="topleft"
  name="modal container"
- open_positioning="centered"
+ positioning="centered"
  width="600">
     <button
      height="20"
diff --git a/indra/newview/skins/default/xui/en/floater_destinations.xml b/indra/newview/skins/default/xui/en/floater_destinations.xml
index 373114a1eb8a748170552b2dcf1450fd86defe16..39aa8e07bb647b5131ad52614b8429aa6d4b2d33 100644
--- a/indra/newview/skins/default/xui/en/floater_destinations.xml
+++ b/indra/newview/skins/default/xui/en/floater_destinations.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  ignore_ui_scale="false"
  legacy_header_height="225"
  can_minimize="true"
diff --git a/indra/newview/skins/default/xui/en/floater_gesture.xml b/indra/newview/skins/default/xui/en/floater_gesture.xml
index b96a94a849b2e299e169ee31fd072d26960eaca8..200e9b9537c2228ad07e799201ce8c3b84b999d4 100644
--- a/indra/newview/skins/default/xui/en/floater_gesture.xml
+++ b/indra/newview/skins/default/xui/en/floater_gesture.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  save_rect="true"
  legacy_header_height="18"
  can_resize="true"
diff --git a/indra/newview/skins/default/xui/en/floater_help_browser.xml b/indra/newview/skins/default/xui/en/floater_help_browser.xml
index cd075abc415f5762666fb493d595443c8fd5edd6..c06cb63f8acd1b414d814a5e708740f7306dcc38 100644
--- a/indra/newview/skins/default/xui/en/floater_help_browser.xml
+++ b/indra/newview/skins/default/xui/en/floater_help_browser.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  legacy_header_height="18"
  can_resize="true"
  height="600"
diff --git a/indra/newview/skins/default/xui/en/floater_image_preview.xml b/indra/newview/skins/default/xui/en/floater_image_preview.xml
index 86232de1a4711d88783cf07120802a1139e97ee2..44d2c14cc8d1cbd2090e8bd3407900a222c73540 100644
--- a/indra/newview/skins/default/xui/en/floater_image_preview.xml
+++ b/indra/newview/skins/default/xui/en/floater_image_preview.xml
@@ -2,7 +2,7 @@
 <floater
  legacy_header_height="18"
  can_minimize="false"
- height="440"
+ height="460"
  layout="topleft"
  name="Image Preview"
  help_topic="image_preview"
@@ -108,7 +108,7 @@
     <text
      type="string"
      length="1"
-     bottom="225"
+     bottom="250"
      height="45"
      word_wrap="true"
      follows="top|left"
@@ -137,7 +137,7 @@ Try saving image as 24 bit Targa (.tga).
      layout="topleft"
      left="165"
      name="cancel_btn"
-     top="410"
+     top="430"
      width="125" />
     <button
      follows="bottom|left"
diff --git a/indra/newview/skins/default/xui/en/floater_joystick.xml b/indra/newview/skins/default/xui/en/floater_joystick.xml
index 6e1bb8fcd02fa9b8469766331d618122832b4911..59f6a9434c4ac5661411b878b34c9874b0b2c765 100644
--- a/indra/newview/skins/default/xui/en/floater_joystick.xml
+++ b/indra/newview/skins/default/xui/en/floater_joystick.xml
@@ -13,6 +13,7 @@
     </floater.string>
     <check_box
      bottom="38"
+     height="10"
      control_name="JoystickEnabled"
      halign="left"
      label="Enable Joystick:"
@@ -28,6 +29,7 @@
      width="380" />
     <spinner
      bottom="48"
+     height="10"
      control_name="JoystickAxis1"
      decimal_digits="0"
      increment="1"
@@ -41,6 +43,7 @@
      width="140" />
     <spinner
      bottom="48"
+     height="10"     
      control_name="JoystickAxis2"
      decimal_digits="0"
      increment="1"
@@ -54,6 +57,7 @@
      width="140" />
     <spinner
      bottom="48"
+     height="10"     
      control_name="JoystickAxis0"
      decimal_digits="0"
      increment="1"
@@ -67,6 +71,7 @@
      width="140" />
     <spinner
      bottom="68"
+     height="10"     
      control_name="JoystickAxis4"
      decimal_digits="0"
      increment="1"
@@ -80,6 +85,7 @@
      width="140" />
     <spinner
      bottom="68"
+     height="10"     
      control_name="JoystickAxis5"
      decimal_digits="0"
      increment="1"
@@ -93,6 +99,7 @@
      width="140" />
     <spinner
      bottom="68"
+     height="10"     
      control_name="JoystickAxis3"
      decimal_digits="0"
      increment="1"
@@ -106,6 +113,7 @@
      width="140" />
     <spinner
      bottom="88"
+     height="10"     
      control_name="JoystickAxis6"
      decimal_digits="0"
      increment="1"
@@ -119,6 +127,7 @@
      width="140" />
     <check_box
      bottom_delta="18"
+     height="10"
      control_name="ZoomDirect"
      label="Direct Zoom"
      layout="topleft"
@@ -127,6 +136,7 @@
      width="60" />
     <check_box
      bottom_delta="0"
+     height="10"     
      control_name="Cursor3D"
      label="3D Cursor"
      layout="topleft"
@@ -135,6 +145,7 @@
      width="60" />
     <check_box
      bottom_delta="0"
+     height="10"     
      control_name="AutoLeveling"
      label="Auto Level"
      layout="topleft"
@@ -157,6 +168,7 @@
     </text>
     <check_box
      bottom="127"
+     height="10"     
      control_name="JoystickAvatarEnabled"
      halign="center"
      label="Avatar"
@@ -166,6 +178,7 @@
      width="60" />
     <check_box
      bottom="127"
+     height="10"     
      control_name="JoystickBuildEnabled"
      halign="center"
      label="Build"
@@ -175,6 +188,7 @@
      width="60" />
     <check_box
      bottom="127"
+     height="10"     
      control_name="JoystickFlycamEnabled"
      halign="center"
      label="Flycam"
@@ -257,6 +271,7 @@
     </text>
     <spinner
      bottom="144"
+     height="10"     
      control_name="AvatarAxisScale1"
      decimal_digits="2"
      label_width="0"
@@ -268,6 +283,7 @@
      width="56" />
     <spinner
      bottom="144"
+     height="10"     
      control_name="BuildAxisScale1"
      decimal_digits="2"
      label_width="0"
@@ -279,6 +295,7 @@
      width="56" />
     <spinner
      bottom="144"
+     height="10"     
      control_name="FlycamAxisScale1"
      decimal_digits="2"
      label_width="0"
@@ -301,6 +318,7 @@
     </text>
     <spinner
      bottom="164"
+     height="10"     
      control_name="AvatarAxisScale2"
      decimal_digits="2"
      label_width="0"
@@ -312,6 +330,7 @@
      width="56" />
     <spinner
      bottom="164"
+     height="10"     
      control_name="BuildAxisScale2"
      decimal_digits="2"
      label_width="0"
@@ -323,6 +342,7 @@
      width="56" />
     <spinner
      bottom="164"
+     height="10"     
      control_name="FlycamAxisScale2"
      decimal_digits="2"
      label_width="0"
@@ -345,6 +365,7 @@
     </text>
     <spinner
      bottom="184"
+     height="10"     
      control_name="AvatarAxisScale0"
      decimal_digits="2"
      label_width="0"
@@ -356,6 +377,7 @@
      width="56" />
     <spinner
      bottom="184"
+     height="10"     
      control_name="BuildAxisScale0"
      decimal_digits="2"
      label_width="0"
@@ -367,6 +389,7 @@
      width="56" />
     <spinner
      bottom="184"
+     height="10"     
      control_name="FlycamAxisScale0"
      decimal_digits="2"
      label_width="0"
@@ -389,6 +412,7 @@
     </text>
     <spinner
      bottom="204"
+     height="10"     
      control_name="AvatarAxisScale4"
      decimal_digits="2"
      label_width="0"
@@ -400,6 +424,7 @@
      width="56" />
     <spinner
      bottom="204"
+     height="10"     
      control_name="BuildAxisScale4"
      decimal_digits="2"
      label_width="0"
@@ -411,6 +436,7 @@
      width="56" />
     <spinner
      bottom="204"
+     height="10"     
      control_name="FlycamAxisScale4"
      decimal_digits="2"
      label_width="0"
@@ -433,6 +459,7 @@
     </text>
     <spinner
      bottom="224"
+     height="10"
      control_name="AvatarAxisScale5"
      decimal_digits="2"
      label_width="0"
@@ -444,6 +471,7 @@
      width="56" />
     <spinner
      bottom="224"
+     height="10"
      control_name="BuildAxisScale5"
      decimal_digits="2"
      label_width="0"
@@ -455,6 +483,7 @@
      width="56" />
     <spinner
      bottom="224"
+     height="10"
      control_name="FlycamAxisScale5"
      decimal_digits="2"
      label_width="0"
@@ -477,6 +506,7 @@
     </text>
     <spinner
      bottom="244"
+     height="10"
      control_name="BuildAxisScale3"
      decimal_digits="2"
      label_width="0"
@@ -488,6 +518,7 @@
      width="56" />
     <spinner
      bottom="244"
+     height="10"
      control_name="FlycamAxisScale3"
      decimal_digits="2"
      label_width="0"
@@ -510,6 +541,7 @@
     </text>
     <spinner
      bottom="274"
+     height="10"
      control_name="AvatarAxisDeadZone1"
      decimal_digits="2"
      increment="0.01"
@@ -520,6 +552,7 @@
      width="56" />
     <spinner
      bottom="274"
+     height="10"
      control_name="BuildAxisDeadZone1"
      decimal_digits="2"
      increment="0.01"
@@ -530,6 +563,7 @@
      width="56" />
     <spinner
      bottom="274"
+     height="10"
      control_name="FlycamAxisDeadZone1"
      decimal_digits="2"
      increment="0.01"
@@ -551,6 +585,7 @@
     </text>
     <spinner
      bottom="294"
+     height="10"     
      control_name="AvatarAxisDeadZone2"
      decimal_digits="2"
      increment="0.01"
@@ -561,6 +596,7 @@
      width="56" />
     <spinner
      bottom="294"
+     height="10"
      control_name="BuildAxisDeadZone2"
      decimal_digits="2"
      increment="0.01"
@@ -571,6 +607,7 @@
      width="56" />
     <spinner
      bottom="294"
+     height="10"
      control_name="FlycamAxisDeadZone2"
      decimal_digits="2"
      increment="0.01"
@@ -592,6 +629,7 @@
     </text>
     <spinner
      bottom="314"
+     height="10"
      control_name="AvatarAxisDeadZone0"
      decimal_digits="2"
      increment="0.01"
@@ -602,6 +640,7 @@
      width="56" />
     <spinner
      bottom="314"
+     height="10"
      control_name="BuildAxisDeadZone0"
      decimal_digits="2"
      increment="0.01"
@@ -612,6 +651,7 @@
      width="56" />
     <spinner
      bottom="314"
+     height="10"
      control_name="FlycamAxisDeadZone0"
      decimal_digits="2"
      increment="0.01"
@@ -633,6 +673,7 @@
     </text>
     <spinner
      bottom="334"
+     height="10"
      control_name="AvatarAxisDeadZone4"
      decimal_digits="2"
      increment="0.01"
@@ -643,6 +684,7 @@
      width="56" />
     <spinner
      bottom="334"
+     height="10"
      control_name="BuildAxisDeadZone4"
      decimal_digits="2"
      increment="0.01"
@@ -653,6 +695,7 @@
      width="56" />
     <spinner
      bottom="334"
+     height="10"
      control_name="FlycamAxisDeadZone4"
      decimal_digits="2"
      increment="0.01"
@@ -674,6 +717,7 @@
     </text>
     <spinner
      bottom="354"
+     height="10"
      control_name="AvatarAxisDeadZone5"
      decimal_digits="2"
      increment="0.01"
@@ -684,6 +728,7 @@
      width="56" />
     <spinner
      bottom="354"
+     height="10"
      control_name="BuildAxisDeadZone5"
      decimal_digits="2"
      increment="0.01"
@@ -694,6 +739,7 @@
      width="56" />
     <spinner
      bottom="354"
+     height="10"
      control_name="FlycamAxisDeadZone5"
      decimal_digits="2"
      increment="0.01"
@@ -715,6 +761,7 @@
     </text>
     <spinner
      bottom="374"
+     height="10"
      control_name="BuildAxisDeadZone3"
      decimal_digits="2"
      increment="0.01"
@@ -725,6 +772,7 @@
      width="56" />
     <spinner
      bottom="374"
+     height="10"
      control_name="FlycamAxisDeadZone3"
      decimal_digits="2"
      increment="0.01"
@@ -802,6 +850,7 @@
     </text>
     <spinner
      bottom="430"
+     height="10"
      control_name="FlycamAxisScale6"
      decimal_digits="2"
      label_width="0"
@@ -824,6 +873,7 @@
     </text>
     <spinner
      bottom="450"
+     height="10"
      control_name="FlycamAxisDeadZone6"
      decimal_digits="2"
      increment="0.01"
diff --git a/indra/newview/skins/default/xui/en/floater_land_holdings.xml b/indra/newview/skins/default/xui/en/floater_land_holdings.xml
index 3737294ebe633bb0b9e0e65c7e6182f305181435..390ec9ab7d4015364fbc0bcd1660fb2acc994377 100644
--- a/indra/newview/skins/default/xui/en/floater_land_holdings.xml
+++ b/indra/newview/skins/default/xui/en/floater_land_holdings.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="centered"
+ positioning="centered"
  legacy_header_height="18"
  height="430"
  layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/floater_map.xml b/indra/newview/skins/default/xui/en/floater_map.xml
index 3eeebcf120628eb393daa6b73d77056f9e41d3aa..b8893e11d90207b5430afd4630aadccc0f57fc44 100644
--- a/indra/newview/skins/default/xui/en/floater_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_map.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  can_minimize="true" 
  can_resize="true"
  chrome="true"
diff --git a/indra/newview/skins/default/xui/en/floater_merchant_outbox.xml b/indra/newview/skins/default/xui/en/floater_merchant_outbox.xml
index 3d33d19de5885afb692a4ef47a09da6bdd58cc62..b98f280b562735228408e092aa809394db6b4f01 100644
--- a/indra/newview/skins/default/xui/en/floater_merchant_outbox.xml
+++ b/indra/newview/skins/default/xui/en/floater_merchant_outbox.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <floater
-    open_positioning="cascading"
+    positioning="cascading"
     can_close="true"
     can_resize="true"
     height="440"
diff --git a/indra/newview/skins/default/xui/en/floater_moveview.xml b/indra/newview/skins/default/xui/en/floater_moveview.xml
index 065dab0413110dce69077744dbf9b9d788026c1d..4e7ee7913fba8e7766646b3406b49bc1c81df15e 100644
--- a/indra/newview/skins/default/xui/en/floater_moveview.xml
+++ b/indra/newview/skins/default/xui/en/floater_moveview.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="specified"
- specified_left="320"
- specified_bottom="80"
+ positioning="specified"
+ left="320"
+ bottom="-80"
  legacy_header_height="18"
  can_dock="false"
  can_minimize="true"
diff --git a/indra/newview/skins/default/xui/en/floater_my_appearance.xml b/indra/newview/skins/default/xui/en/floater_my_appearance.xml
index 1c4b25a7b02a4bb8e6fbfd158660252e4e0a383c..fdea7a821ab28e26259eb9d2a54d417f77a5af57 100644
--- a/indra/newview/skins/default/xui/en/floater_my_appearance.xml
+++ b/indra/newview/skins/default/xui/en/floater_my_appearance.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 
 <floater
-  open_positioning="cascading"
+  positioning="cascading"
   legacy_header_height="18"
   can_resize="true"
   height="588"
diff --git a/indra/newview/skins/default/xui/en/floater_my_inventory.xml b/indra/newview/skins/default/xui/en/floater_my_inventory.xml
index cd0b59dc5136e76f1e1b9a1391d33b1df62808be..184f29625595a485568d60304ff98e526df51ba0 100644
--- a/indra/newview/skins/default/xui/en/floater_my_inventory.xml
+++ b/indra/newview/skins/default/xui/en/floater_my_inventory.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  can_close="true"
  can_resize="true"
  height="570"
diff --git a/indra/newview/skins/default/xui/en/floater_people.xml b/indra/newview/skins/default/xui/en/floater_people.xml
index d6d8431150af131810b6811d53549a2f3fd22283..08d0b00a835ebd5140021d85e94d9b23ddb12d74 100644
--- a/indra/newview/skins/default/xui/en/floater_people.xml
+++ b/indra/newview/skins/default/xui/en/floater_people.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
 
 <floater
-  open_positioning="cascading"
+  positioning="cascading"
   can_close="true"
   can_resize="true"
   height="570"
diff --git a/indra/newview/skins/default/xui/en/floater_picks.xml b/indra/newview/skins/default/xui/en/floater_picks.xml
index 78821166624401bdea23a42c6c97d09a1aa11f25..984894b0165a4012b6056806ac3a813cc96d5b5a 100644
--- a/indra/newview/skins/default/xui/en/floater_picks.xml
+++ b/indra/newview/skins/default/xui/en/floater_picks.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  can_close="true"
  can_resize="true"
  height="572"
diff --git a/indra/newview/skins/default/xui/en/floater_places.xml b/indra/newview/skins/default/xui/en/floater_places.xml
index ccceac0a7b28af05d95e0b55d00238308a1c1e61..b241e265a9a7ba6bcc502e18d9443131ca6e6a2c 100644
--- a/indra/newview/skins/default/xui/en/floater_places.xml
+++ b/indra/newview/skins/default/xui/en/floater_places.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 
 <floater
-  open_positioning="cascading"
+  positioning="cascading"
   legacy_header_height="18"
   can_resize="true"
   height="588"
diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml
index b2662331b0531e1f398c9d2b2099766761d6e239..bd6faf4ed8fb01650f528413e2c2fb9d303eca51 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
  legacy_header_height="18"
- open_positioning="centered"
+ positioning="centered"
  default_tab_group="1"
  height="460"
  layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/floater_search.xml b/indra/newview/skins/default/xui/en/floater_search.xml
index dd818e2e0659646b6f3d1dda5f582631dfda8640..c3e7028dc5b79e2686a0230143d91e691e84a960 100644
--- a/indra/newview/skins/default/xui/en/floater_search.xml
+++ b/indra/newview/skins/default/xui/en/floater_search.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
-  open_positioning="cascading"
+  positioning="cascading"
   legacy_header_height="18"
   can_resize="true"
   height="775"
diff --git a/indra/newview/skins/default/xui/en/floater_snapshot.xml b/indra/newview/skins/default/xui/en/floater_snapshot.xml
index 0c38283d5975298ddead8832f1297029d26ab539..49d64767cc783f28164c1ad28da13ef9515bab51 100644
--- a/indra/newview/skins/default/xui/en/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/en/floater_snapshot.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  legacy_header_height="18"
  can_minimize="true"
  can_close="true"
diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml
index 2fd932786bdf9e897bbcf0f06bfd740bd5aa751d..9400f7b94ffa3774c29f6a609f47f6a2b5687097 100644
--- a/indra/newview/skins/default/xui/en/floater_stats.xml
+++ b/indra/newview/skins/default/xui/en/floater_stats.xml
@@ -149,13 +149,52 @@
 				 show_per_sec="true"
 				 show_bar="false">
 			  </stat_bar>
+       <stat_bar
+				 name="object_cache_hits"
+				 label="Object Cache Hit Rate"
+				 stat="object_cache_hits"
+				 bar_min="0"
+				 bar_max="100"
+         unit_label="%"
+				 tick_spacing="20"
+				 label_spacing="20"
+				 show_history="true"
+         show_per_sec="false"
+				 show_bar="false">
+        </stat_bar>
 			</stat_view>
         <!--Texture Stats-->
 			<stat_view
 			   name="texture"
 			   label="Texture"
 			   show_label="true">
-			  <stat_bar
+        <stat_bar
+				 name="texture_cache_hits"
+				 label="Cache Hit Rate"
+				 stat="texture_cache_hits"
+				 bar_min="0.f"
+				 bar_max="100.f"
+         unit_label="%"
+				 tick_spacing="20"
+				 label_spacing="20"
+				 show_history="true"
+         show_per_sec="false"
+				 show_bar="false">
+        </stat_bar>
+        <stat_bar
+				 name="texture_cache_read_latency"
+				 label="Cache Read Latency"
+         unit_label="msec"
+				 stat="texture_cache_read_latency"
+				 bar_min="0.f"
+				 bar_max="1000.f"
+				 tick_spacing="100"
+				 label_spacing="200"
+				 show_history="true"
+         show_per_sec="false"
+				 show_bar="false">
+        </stat_bar>
+        <stat_bar
 				 name="numimagesstat"
 				 label="Count"
 				 stat="numimagesstat" 
diff --git a/indra/newview/skins/default/xui/en/floater_test_text_editor.xml b/indra/newview/skins/default/xui/en/floater_test_text_editor.xml
index 548e24efba3d9cdd817377a4e6ca8cceafb949f6..e1fefc363133250c94dfaa3c8819b24ce71204ff 100644
--- a/indra/newview/skins/default/xui/en/floater_test_text_editor.xml
+++ b/indra/newview/skins/default/xui/en/floater_test_text_editor.xml
@@ -3,6 +3,7 @@
  legacy_header_height="18"
  can_resize="true"
  height="600"
+ single_instance="false"
  layout="topleft"
  name="floater_test_text_editor"
  translate="false"
diff --git a/indra/newview/skins/default/xui/en/floater_test_widgets.xml b/indra/newview/skins/default/xui/en/floater_test_widgets.xml
index 13c850c86c0300c1000a46c372011795bdf3b13b..10854f5a499c358bba03f8b2b4a84f69ce2f7e6a 100644
--- a/indra/newview/skins/default/xui/en/floater_test_widgets.xml
+++ b/indra/newview/skins/default/xui/en/floater_test_widgets.xml
@@ -115,6 +115,7 @@
   </flyout_button>
   <check_box
    bottom_delta="35"
+   height="10"
    label="Checkbox"
    layout="topleft"
    tool_tip="checkbox"
@@ -275,6 +276,7 @@
   <!-- "spinner" is a numerical input widget with an up and down arrow to
        change the value. -->
   <spinner
+    height="10"
    bottom_delta="35"
    follows="top|left"
    label="Spinner"
diff --git a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
index cad7d72ed7f913e6f342f137c53b4159ff64fb51..ffb8b842f0cbb3920eb1507d22de4fa25d5fc1cf 100644
--- a/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
+++ b/indra/newview/skins/default/xui/en/floater_texture_ctrl.xml
@@ -3,15 +3,17 @@
  legacy_header_height="18"
  can_minimize="false"
  can_resize="true"
- height="290"
+ height="330"
  layout="topleft"
- min_height="290"
+ min_height="330"
  min_width="410"
  name="texture picker"
  help_topic="texture_picker"
  title="PICK: TEXTURE"
  width="410">
-    <floater.string
+ 
+<!--  top static -->
+ <floater.string
      name="choose_picture">
         Click to choose a picture
     </floater.string>
@@ -19,6 +21,16 @@
      name="pick title">
         Pick:
     </floater.string>
+
+    <view
+     left="4"
+     top="20"
+     name="preview_widget"
+     height="165"
+     width="165"
+     follows="left|top"
+    />
+  
     <text
      type="string"
      length="1"
@@ -34,70 +46,94 @@
      width="163">
         Multiple textures
     </text>
+	
+	<!-- mode selector -->
+	   <radio_group
+     control_name="mode_selection"
+     height="20"
+     layout="topleft"
+     left="18"
+     top_pad="80"
+     name="mode_selection"
+     follows="left|top">
+        <radio_item
+         label="Inventory"
+         name="inventory"
+         top_delta="20" 
+         layout="topleft"
+         height="16" 
+         left="0" 
+         value="0"
+         width="80" />
+        <radio_item
+         label="Local"
+         left_pad="0"
+         layout="topleft"
+         top_delta="0" 
+         height="16" 
+         name="local"
+         value="1"
+         width="75" />
+    </radio_group>
+	<!-- -->
+	
     <text
      type="string"
      length="1"
      follows="left|top"
      height="14"
      layout="topleft"
-     left_delta="0"
+     left_delta="-12"
      name="unknown"
-     top_pad="80"
-     width="163">
+     top_pad="4"
+     width="">
         Size: [DIMENSIONS]
     </text>
+    
+<!--  middle: inventory mode -->
+
     <button
      enabled="false"
-     follows="left|bottom"
-     height="20"
+     follows="left|top"
+     height="18"
      label="Default"
      label_selected="Default"
      layout="topleft"
-     left_delta="0"
      name="Default"
-     top_pad="4"
-     width="80" />
+     width="73"
+	 left="94"
+     top="215"/>
     <button
+     follows="left|top"
+     height="20"
+     label="Blank"
+     label_selected="Blank"
+     layout="topleft"
+     left_delta="0"
+     name="Blank"
+     top_pad="5"
+     width="73" />
+     <button
      enabled="false"
-     follows="left|bottom"
+     follows="left|top"
      height="20"
      label="None"
      label_selected="None"
      layout="topleft"
-     left_pad="4"
+     left_delta="0"
      name="None"
-     top_delta="0"
-     width="80" />
-    <button
-     follows="left|bottom"
-     height="20"
-     label="Blank"
-     label_selected="Blank"
-     layout="topleft"
-     left="4"
-     name="Blank"
      top_pad="5"
-     width="80" />
-         <button
-     follows="left|bottom"
+     width="73" />
+    <button
+     follows="left|top"
      height="28"
      image_selected="eye_button_active.tga"
      image_unselected="eye_button_inactive.tga"
      layout="topleft"
-     left_pad="50"
-     top_delta="3"
+     left_delta="-80"
+     top_delta="-25"
      name="Pipette"
      width="28" />
-   <check_box
-     follows="left|bottom"
-     height="20"
-     initial_value="true"
-     label="Apply now"
-     layout="topleft"
-     left="4"
-     name="apply_immediate_check"
-     top="262"
-     width="120" />
     <filter_editor
      follows="left|top|right"
      height="23"
@@ -113,7 +149,7 @@
      bg_alpha_color="DkGray2"
      border="false"
      follows="all"
-     height="200"
+     height="233"
      layout="topleft"
      left_delta="0"
      name="inventory panel"
@@ -128,23 +164,89 @@
      top_pad="0"
      left_delta="-3"
      width="200" />
-         <button
-     follows="right|bottom"
+
+<!--  middle: local mode -->
+    <button
+     follows="left|top"
+     height="18"
+     label="Add"
+     label_selected="Add"
+     layout="topleft"
+     left="94"
+     top="215"
+     name="l_add_btn"
+     width="73"
+     visible="false"/>
+    <button
+     enabled="false"
+     follows="left|top"
+     height="20"
+     label="Remove"
+     label_selected="Remove"
+     layout="topleft"
+     left_delta="0"
+     name="l_rem_btn"
+     top_pad="5"
+     width="73"
+     visible="false"/>
+    <button
+     enabled="false"
+     follows="left|top"
+     height="20"
+     label="Upload"
+     label_selected="Upload"
+     layout="topleft"
+     left_delta="0"
+     name="l_upl_btn"
+     top_pad="5"
+     width="73"
+     visible="false"/>
+    <scroll_list
+     name="l_name_list"
+     left="170"
+     top="22"
+     width="235"
+     height="260"
+     follows="left|top|right|bottom"
+     column_padding="0"
+     can_resize="false"
+     draw_heading="true"
+     multi_select="true"
+     search_column="1"
+     visible="false">
+        <column name="unit_name" label="Name" dynamicwidth="true" />
+        <column name="unit_id_HIDDEN" label="ID" width="0" />
+    </scroll_list>
+     
+<!-- bottom static -->
+    <button
+     follows="bottom"
      height="20"
      label="OK"
      label_selected="OK"
      layout="topleft"
-     right="-120"
+     left="95"
+     top="-30"
      name="Select"
      width="100" />
     <button
-     follows="right|bottom"
+     follows="bottom"
      height="20"
      label="Cancel"
      label_selected="Cancel"
      layout="topleft"
-     right="-10"
-     left_pad="5"
+     left_delta="120"
+     top_delta="0"
      name="Cancel"
      width="100" />
+    <check_box
+     follows="left|bottom"
+     height="20"
+     initial_value="true"
+     label="Apply now"
+     layout="topleft"
+     left="6"
+     name="apply_immediate_check"
+     top_delta="0"
+     width="120" />
 </floater>
diff --git a/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
new file mode 100644
index 0000000000000000000000000000000000000000..44b6a63bcae5a98b07899f2cbdd4c27ac1b3b655
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_texture_fetch_debugger.xml
@@ -0,0 +1,341 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_minimize="false"
+ height="550"
+ layout="topleft"
+ name="TexFetchDebugger"
+ help_topic="texfetchdebugger"
+ title="Texture Fetching Debugger"
+ width="540">
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left="10"
+   name="total_num_fetched_label"
+   top="30"
+   width="400">
+    1, Total number of fetched textures: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_fetching_requests_label"
+   top_delta="25"
+   width="400">
+    2, Total number of fetching requests: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_cache_hits_label"
+   top_delta="25"
+   width="400">
+    3, Total number of cache hits: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_visible_tex_label"
+   top_delta="25"
+   width="400">
+    4, Total number of visible textures: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_num_visible_tex_fetch_req_label"
+   top_delta="25"
+   width="450">
+    5, Total number of visible texture fetching requests: [NUM]
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_data_label"
+   top_delta="25"
+   width="530">
+    6, Total number of fetched data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_vis_data_label"
+   top_delta="25"
+   width="480">
+    7, Total number of visible data: [SIZE1]KB, Decoded Data: [SIZE2]KB
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_fetched_rendered_data_label"
+   top_delta="25"
+   width="530">
+    8, Total number of rendered data: [SIZE1]KB, Decoded Data: [SIZE2]KB, [PIXEL]MPixels
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_cache_read_label"
+   top_delta="25"
+   width="400">
+    9, Total time on cache readings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_cache_write_label"
+   top_delta="25"
+   width="400">
+    10, Total time on cache writings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_decode_label"
+   top_delta="25"
+   width="400">
+    11, Total time on decodings: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_gl_label"
+   top_delta="25"
+   width="400">
+    12, Total time on gl texture creation: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_http_label"
+   top_delta="25"
+   width="400">
+    13, Total time on HTTP fetching: [TIME] seconds
+  </text>
+  <text
+   type="string"
+   length="1"
+   follows="left|top"
+   height="25"
+   layout="topleft"
+   left_delta="0"
+   name="total_time_fetch_label"
+   top_delta="25"
+   width="400">
+    14, Total time on entire fetching: [TIME] seconds
+  </text>
+  <text
+  type="string"
+  length="1"
+  follows="left|top"
+  height="25"
+  layout="topleft"
+  left_delta="0"
+  name="total_time_refetch_vis_cache_label"
+  top_delta="25"
+  width="540">
+    15, Refetching visibles from cache, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels
+  </text>
+  <text
+  type="string"
+  length="1"
+  follows="left|top"
+  height="25"
+  layout="topleft"
+  left_delta="0"
+  name="total_time_refetch_vis_http_label"
+  top_delta="25"
+  width="540">
+    16, Refetching visibles from HTTP, Time: [TIME] seconds, Fetched: [SIZE]KB, [PIXEL]MPixels
+  </text>
+  <spinner
+     decimal_digits="2"
+     follows="left|top"
+     height="20"
+     increment="0.01"
+     initial_value="1.0"
+     label="17, Ratio of Texel/Pixel:"
+     label_width="130"
+     layout="topleft"
+     left_delta="0"
+     max_val="10.0"
+     min_val="0.01"
+     name="texel_pixel_ratio"
+     top_delta="30"
+     width="200">
+    <spinner.commit_callback
+		function="TexFetchDebugger.ChangeTexelPixelRatio" />
+  </spinner>
+  <button
+   follows="left|top"
+   height="20"
+   label="Start"
+   layout="topleft"
+   left_delta="0"
+   name="start_btn"
+   top_delta="30"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Start" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Reset"
+   layout="topleft"
+   left_pad="7"
+   name="clear_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Clear" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Close"
+   layout="topleft"
+   left_pad="7"
+   name="close_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Close" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Cache Read"
+   layout="topleft"
+   left="10"
+   name="cacheread_btn"
+   top_delta="30"
+   width="80">
+    <button.commit_callback
+		function="TexFetchDebugger.CacheRead" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Cache Write"
+   layout="topleft"
+   left_pad="7"
+   name="cachewrite_btn"
+   top_delta="0"
+   width="80">
+    <button.commit_callback
+		function="TexFetchDebugger.CacheWrite" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="HTTP"
+   layout="topleft"
+   left_pad="7"
+   name="http_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.HTTPLoad" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Decode"
+   layout="topleft"
+   left_pad="7"
+   name="decode_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.Decode" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="GL Texture"
+   layout="topleft"
+   left_pad="7"
+   name="gl_btn"
+   top_delta="0"
+   width="70">
+    <button.commit_callback
+		function="TexFetchDebugger.GLTexture" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Refetch Vis Cache"
+   layout="topleft"
+   left="10"
+   name="refetchviscache_btn"
+   top_delta="30"
+   width="120">
+    <button.commit_callback
+		function="TexFetchDebugger.RefetchVisCache" />
+  </button>
+  <button
+   follows="left|top"
+   height="20"
+   label="Refetch Vis HTTP"
+   layout="topleft"
+   left_pad="7"
+   name="refetchvishttp_btn"
+   top_delta="0"
+   width="120">
+    <button.commit_callback
+		function="TexFetchDebugger.RefetchVisHTTP" />
+  </button>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml
index 2d63c94fe11f896e91d53c5613edc2be904cbe9d..e37740d20cd71aa64c8ca4746165bb36d7253829 100644
--- a/indra/newview/skins/default/xui/en/floater_tools.xml
+++ b/indra/newview/skins/default/xui/en/floater_tools.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  legacy_header_height="18"
  height="580"
  layout="topleft"
@@ -1206,6 +1206,7 @@ even though the user gets a free copy.
              label="Modify"
              layout="topleft"
              left="10"
+             height="10"
              name="checkbox next owner can modify"
              width="85" />
             <check_box
diff --git a/indra/newview/skins/default/xui/en/floater_toybox.xml b/indra/newview/skins/default/xui/en/floater_toybox.xml
index fcaae9d172aa08bb8b11d9b1260ec8d6b3b71e63..d8211c24a72744f149cdc82c4126fcc79c251654 100644
--- a/indra/newview/skins/default/xui/en/floater_toybox.xml
+++ b/indra/newview/skins/default/xui/en/floater_toybox.xml
@@ -10,7 +10,7 @@
   layout="topleft"
   legacy_header_height="18"
   name="Toybox"
-  open_positioning="centered"
+  positioning="centered"
   save_rect="true"
   single_instance="true"
   title="TOOLBAR BUTTONS"
diff --git a/indra/newview/skins/default/xui/en/floater_voice_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml
index d99b29f32476bac8614c28e3352ece41a0534200..dce2720cf8cf46c63eddfc054524f05ddafd38b3 100644
--- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml
+++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <floater
- open_positioning="cascading"
+ positioning="cascading"
  can_resize="true"
  can_minimize="true"
  can_close="true"
diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml
index 77fb21e27c5eadddaa008933318d557af24602cf..35cb2670d0904187dfcd55e9cd06c0df45a4f27d 100644
--- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml
@@ -36,15 +36,20 @@
   <string name="effect_Cyber">Cyber</string>
   <string name="effect_DeepBot">DeepBot</string>
   <string name="effect_Demon">Demon</string>
+  <string name="effect_Female Elf">Female Elf</string>
   <string name="effect_Flirty">Flirty</string>
   <string name="effect_Foxy">Foxy</string>
-  <string name="effect_Halloween_2010_Bonus">Halloween_2010_Bonus</string>
+  <string name="effect_Halloween 2010 Bonus">Halloween_2010_Bonus</string>
   <string name="effect_Helium">Helium</string>
   <string name="effect_Husky">Husky</string>
+  <string name="effect_Husky Whisper">Husky Whisper</string>
   <string name="effect_Intercom">Intercom</string>
+  <string name="effect_Julia">Julia</string>
+  <string name="effect_Lo Lilt">Lo Lilt</string>
   <string name="effect_Macho">Macho</string>
   <string name="effect_Micro">Micro</string>
   <string name="effect_Mini">Mini</string>
+  <string name="effect_Model">Model</string>
   <string name="effect_Nano">Nano</string>
   <string name="effect_Nightmare">Nightmare</string>
   <string name="effect_PopBot">PopBot</string>
@@ -52,10 +57,12 @@
   <string name="effect_Radio">Radio</string>
   <string name="effect_Robot">Robot</string>
   <string name="effect_Roxanne">Roxanne</string>
+  <string name="effect_Rumble">Rumble</string>
   <string name="effect_Sabrina">Sabrina</string>
   <string name="effect_Samantha">Samantha</string>
   <string name="effect_Sexy">Sexy</string>
   <string name="effect_Shorty">Shorty</string>
+  <string name="effect_Smaller">Smaller</string>
   <string name="effect_Sneaky">Sneaky</string>
   <string name="effect_Stallion">Stallion</string>
   <string name="effect_Sultry">Sultry</string>
diff --git a/indra/newview/skins/default/xui/en/floater_window_size.xml b/indra/newview/skins/default/xui/en/floater_window_size.xml
index 355d25778542db0881796c7aa2f9412bc2ffd4a9..115fe413f3ca4e3dc96c1375ea660487cfed895f 100644
--- a/indra/newview/skins/default/xui/en/floater_window_size.xml
+++ b/indra/newview/skins/default/xui/en/floater_window_size.xml
@@ -28,22 +28,50 @@
      tool_tip="width x height"
      top_pad="5"
      width="179">
-        <combo_box.item
-         label="1000 x 700 (default)"
-         name="item0"
-         value="1000 x 700" />
-        <combo_box.item
-         label="1024 x 768"
-         name="item1"
-         value="1024 x 768" />
-        <combo_box.item
-         label="1280 x 720 (720p)"
-         name="item2"
-         value="1280 x 720" />
-        <combo_box.item
-         label="1920 x 1080 (1080p)"
-         name="item3"
-         value="1920 x 1080" />
+      <combo_box.item
+       label="1000 x 700 (default)"
+       name="item1"
+       value="1000 x 700" />
+      <combo_box.item
+       label="1024 x 768 (4:3 XGA)"
+       name="item2"
+       value="1024 x 768" />
+      <combo_box.item
+       label="1280 x 720 (16:9 HDTV)"
+       name="item3"
+       value="1280 x 720" />
+      <combo_box.item
+       label="1280 x 800 (5:8 WXGA)"
+       name="item4"
+       value="1280 x 800" />
+      <combo_box.item
+       label="1280 x 1024 (5:4 SXGA)"
+       name="item5"
+       value="1280 x 1024" />
+      <combo_box.item
+       label="1440 x 900 (8:5 WSXGA)"
+       name="item7"
+       value="1440 x 900" />
+      <combo_box.item
+       label="1600 x 900 (16:9 HD+)"
+       name="item8"
+       value="1600 x 900" />
+      <combo_box.item
+       label="1600 x 1200 (4:3 UXGA)"
+       name="item9"
+       value="1600 x 1200" />
+      <combo_box.item
+       label="1680 x 1050 (8:5 WSXGA+)"
+       name="item10"
+       value="1680 x 1050" />
+      <combo_box.item
+       label="1920 x 1080 (16:9 HDTV)"
+       name="item11"
+       value="1920 x 1080" />
+      <combo_box.item
+       label="1920 x 1200 (8:5 WUXGA)"
+       name="item12"
+       value="1920 x 1200" />
     </combo_box>
     <button
      follows="right|bottom"
diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml
index 56d79f62c7bf0586b15c66991bc76951f3c1b564..83407069d2eee1ab962a8d3381a9c897bd9c746a 100644
--- a/indra/newview/skins/default/xui/en/floater_world_map.xml
+++ b/indra/newview/skins/default/xui/en/floater_world_map.xml
@@ -2,7 +2,7 @@
 <floater
  legacy_header_height="18"
  can_resize="true"
- open_positioning="centered"
+ positioning="centered"
  height="600"
  layout="topleft"
  min_height="520"
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index ef4a1bc0618b275f25c7b219e85443acecb2b1c9..b13bf5b5086d7594984a3e446139fdf410f2f8ae 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -452,6 +452,14 @@
     <menu_item_separator
      layout="topleft" 
      name="Copy Separator" />
+    <menu_item_call
+     label="Cut"
+     layout="topleft"
+     name="Cut">
+        <menu_item_call.on_click
+         function="Inventory.DoToSelected"
+         parameter="cut" />
+    </menu_item_call>
     <menu_item_call
      label="Copy"
      layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 1d11abcf73eb06d08ad5443113d44dc193debb63..a6898c554fcb6a88654f18a8e13759ca1b51d807 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1876,7 +1876,7 @@
                 <menu_item_check.on_click
                  function="Advanced.ToggleConsole"
                  parameter="texture" />
-            </menu_item_check>
+            </menu_item_check>            
             <menu_item_check
              label="Debug Console"
              name="Debug Console"
@@ -1897,28 +1897,6 @@
                function="Floater.Toggle"
                parameter="notifications_console" />
             </menu_item_call>
-            <menu_item_check
-               label="Texture Size Console"
-               name="Texture Size"
-               shortcut="control|shift|6">
-              <menu_item_check.on_check
-               function="Advanced.CheckConsole"
-               parameter="texture size" />
-              <menu_item_check.on_click
-               function="Advanced.ToggleConsole"
-               parameter="texture size" />
-            </menu_item_check>
-            <menu_item_check
-               label="Texture Category Console"
-               name="Texture Category"
-               shortcut="control|shift|7">
-              <menu_item_check.on_check
-               function="Advanced.CheckConsole"
-               parameter="texture category" />
-              <menu_item_check.on_click
-               function="Advanced.ToggleConsole"
-               parameter="texture category" />
-            </menu_item_check>
             <menu_item_check
              label="Fast Timers"
              name="Fast Timers"
@@ -1953,7 +1931,20 @@
                function="Advanced.ToggleConsole"
                parameter="scene view" />
             </menu_item_check>
-
+            <menu_item_call
+              enabled="false"
+              visible="false"
+              label="Texture Fetch Debug Console"
+              name="Texture Fetch Debug Console">
+              <menu_item_call.on_click
+                function="Floater.Show"
+                parameter="tex_fetch_debugger" />
+              <on_enable
+                function="Develop.SetTexFetchDebugger" />
+              <on_visible
+                function="Develop.SetTexFetchDebugger" />
+            </menu_item_call>
+          
             <menu_item_separator/>
 
             <menu_item_call
@@ -2439,6 +2430,52 @@
            function="Advanced.ToggleInfoDisplay"
            parameter="sculpt" />
 		</menu_item_check>
+       <menu
+         create_jump_keys="true"
+         label="Texture Density"
+         name="Texture Density"
+         tear_off="true">
+          <menu_item_check
+           label="None"
+           name="None">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="none" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="none" />
+          </menu_item_check>
+          <menu_item_check
+           label="Current"
+           name="Current">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="current" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="current" />
+          </menu_item_check>
+          <menu_item_check
+           label="Desired"
+           name="Desired">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="desired" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="desired" />
+          </menu_item_check>
+          <menu_item_check
+           label="Full"
+           name="Full">
+            <menu_item_check.on_check
+             function="Advanced.CheckDisplayTextureDensity"
+             parameter="full" />
+            <menu_item_check.on_click
+             function="Advanced.SetDisplayTextureDensity"
+             parameter="full" />
+          </menu_item_check>
+        </menu>
       </menu>
         <menu
          create_jump_keys="true"
@@ -2605,16 +2642,6 @@
                  function="ToggleControl"
                  parameter="TextureLoadFullRes" />
             </menu_item_check>
-            <menu_item_check
-               label="Audit Textures"
-               name="Audit Textures">
-              <menu_item_check.on_check
-               function="CheckControl"
-               parameter="AuditTexture" />
-              <menu_item_check.on_click
-               function="ToggleControl"
-               parameter="AuditTexture" />
-            </menu_item_check>
             <menu_item_check
              label="Texture Atlas (experimental)"
              name="Texture Atlas">
@@ -3268,6 +3295,14 @@
             <menu_item_call.on_click
              function="Advanced.CompressImage" />
         </menu_item_call>
+
+      <menu_item_call
+         label="Enable Visual Leak Detector"
+         name="Enable Visual Leak Detector">
+        <menu_item_call.on_click
+           function="Advanced.ToggleVisualLeakDetector" />
+        </menu_item_call>
+      
         <menu_item_check
          label="Output Debug Minidump"
          name="Output Debug Minidump">
@@ -3294,6 +3329,7 @@
          name="Set Logging Level"
          tear_off="true">
           <menu_item_check
+            name="Debug"
             label="Debug">
             <menu_item_check.on_check
               function="Develop.CheckLoggingLevel"
@@ -3303,6 +3339,7 @@
              parameter="0" />
           </menu_item_check>
           <menu_item_check
+            name="Info"
             label="Info">
             <menu_item_check.on_check
               function="Develop.CheckLoggingLevel"
@@ -3312,6 +3349,7 @@
              parameter="1" />
           </menu_item_check>
           <menu_item_check
+            name="Warning"
             label="Warning">
             <menu_item_check.on_check
               function="Develop.CheckLoggingLevel"
@@ -3321,6 +3359,7 @@
              parameter="2" />
           </menu_item_check>
           <menu_item_check
+            name="Error"
             label="Error">
             <menu_item_check.on_check
               function="Develop.CheckLoggingLevel"
@@ -3330,6 +3369,7 @@
              parameter="3" />
           </menu_item_check>
           <menu_item_check
+            name="None"
             label="None">
             <menu_item_check.on_check
               function="Develop.CheckLoggingLevel"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 2e6c42e542b1065253ce981bfc4aa9b8aa467a94..a26c5bb3449969e023b538cf835966dcb7f49593 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -6392,31 +6392,28 @@ Is this OK?
   <notification
    icon="notify.tga"
    name="ScriptQuestionCaution"
-   priority="high"
+   priority="critical"
    persist="true"
    type="notify">
-An object named &apos;&lt;nolink&gt;[OBJECTNAME]&lt;/nolink&gt;&apos;, owned by &apos;[NAME]&apos; would like to:
-
-[QUESTIONS]
-If you do not trust this object and its creator, you should deny the request.
+Warning: The object &apos;&lt;nolink&gt;[OBJECTNAME]&lt;/nolink&gt;&apos; wants total access to your Linden Dollars account. If you allow access, it can remove funds from your account at any time, or empty your account completely, on an ongoing basis with no additional warnings.
+  
+It is rare that such a request is legitimate. Do not allow access if you do not fully understand why it wants access to your account.
 
-Grant this request?
   <tag>confirm</tag>
     <form name="form">
       <button
        index="0"
        name="Grant"
-       text="Grant"/>
+       text="Allow total access"/>
       <button
        default="true"
        index="1"
        name="Deny"
        text="Deny"/>
-      <button
-       index="2"
-       name="Details"
-       text="Details..."/>
     </form>
+    <footer>
+[FOOTERTEXT]
+    </footer>
   </notification>
 
   <notification
@@ -7715,4 +7712,31 @@ Otherwise, you can look at the Map and find places marked &quot;Infohub&quot;.
 You died and have been teleported to your home location.
   </global>
 
+  <notification
+   icon="alertmodal.tga"
+   name="LocalBitmapsUpdateFileNotFound"
+   persist="true"
+   type="notify">
+[FNAME] could not be updated because the file could no longer be found.
+Disabling future updates for this file.
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="LocalBitmapsUpdateFailedFinal"
+   persist="true"
+   type="notify">
+[FNAME] could not be opened or decoded for [NRETRIES] attempts, and is now considered broken.
+Disabling future updates for this file.
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="LocalBitmapsVerifyFail"
+   persist="true"
+   type="notify">
+Attempted to add an invalid or unreadable image file [FNAME] which could not be opened or decoded.
+Attempt cancelled.
+  </notification>
+  
 </notifications>
diff --git a/indra/newview/skins/default/xui/en/panel_chat_item.xml b/indra/newview/skins/default/xui/en/panel_chat_item.xml
index 1b97de2b05e30f2d655e5195a4cf631897d79cf8..1ef99649e67d27cd2a46c75406f6fe9adb1f702f 100644
--- a/indra/newview/skins/default/xui/en/panel_chat_item.xml
+++ b/indra/newview/skins/default/xui/en/panel_chat_item.xml
@@ -18,6 +18,7 @@
 	<text_chat
       top="3"
       left="30"
+      right="-10"
       height="120"
       text_color="white"
       word_wrap="true"
diff --git a/indra/newview/skins/default/xui/en/panel_postcard_settings.xml b/indra/newview/skins/default/xui/en/panel_postcard_settings.xml
index e9427a23882beed63b894f6b99ebb334a94a7893..3f67a48b14ff3f848bedd91fd330cab8f9373949 100644
--- a/indra/newview/skins/default/xui/en/panel_postcard_settings.xml
+++ b/indra/newview/skins/default/xui/en/panel_postcard_settings.xml
@@ -85,6 +85,7 @@
              top_delta="0"
              width="95" />
             <check_box
+              height="10"
              bottom_delta="20"
              follows="left|top"
              label="Constrain proportions"
diff --git a/indra/newview/skins/default/xui/en/panel_script_question_toast.xml b/indra/newview/skins/default/xui/en/panel_script_question_toast.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b0436bb6dcd452f5df2ea05b48485ef4fd650869
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_script_question_toast.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<panel
+  background_opaque="false"
+  border_visible="false"
+  background_visible="true"
+  bg_alpha_color="PanelNotificationBackground"
+  bg_opaque_color="PanelNotificationBackground"
+  chrome="true"
+  height="270"
+  label="script_question_panel"
+  layout="topleft"
+  left="0"
+  name="panel_script_question_toast"
+  top="0"
+  width="305">
+    <text
+      follows="left|right|top"
+      font="SansSerifBold"
+      height="160"
+      layout="topleft"
+      left="10"
+      mouse_opaque="false"
+      name="top_info_message"
+      parse_highlights="true"
+      parse_urls="true"
+      text_color="NotifyCautionBoxColor"
+      top="10"
+      value=""
+      width="285"
+      wrap="true"/>
+    <panel
+      background_visible="false"
+      follows="left|right|top"
+      height="30"
+      label="buttons_panel"
+      layout="topleft"
+      name="buttons_panel"
+      top_pad="10"
+      width="285">
+    </panel>
+    <text
+      follows="all"
+      font="SansSerifBold"
+      height="55"
+      layout="topleft"
+      mouse_opaque="false"
+      name="bottom_info_message"
+      parse_highlights="true"
+      parse_urls="true"
+      text_color="NotifyCautionBoxColor"
+      top_pad="5"
+      value=""
+      width="285"
+      wrap="true"/>
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml b/indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml
index 9057ebb65eb4d96915d6a5bc2b46e2cd75e40381..71d808fa4b22b0ac5145739dc3015488bbd59a6a 100644
--- a/indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_inventory.xml
@@ -114,6 +114,7 @@
      width="95" />
     <check_box
      bottom_delta="20"
+     height="10"
      follows="left|top"
      label="Constrain proportions"
      layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_local.xml b/indra/newview/skins/default/xui/en/panel_snapshot_local.xml
index b966358f18d74114288b608dfd88672bb7f84b58..781ab174031b942158a3d8708012983f2a2f8769 100644
--- a/indra/newview/skins/default/xui/en/panel_snapshot_local.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_local.xml
@@ -132,6 +132,7 @@
              width="95" />
             <check_box
              bottom_delta="20"
+             height="10"
              follows="left|top"
              label="Constrain proportions"
              layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/panel_snapshot_profile.xml b/indra/newview/skins/default/xui/en/panel_snapshot_profile.xml
index 5bd383b81e85b585ffe6c608e6d6c6b917a70d98..0dd357aa1a747c9711c800ea5f45c9117ff32039 100644
--- a/indra/newview/skins/default/xui/en/panel_snapshot_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_snapshot_profile.xml
@@ -119,6 +119,7 @@
              top_delta="0"
              width="95" />
             <check_box
+              height="10"
              bottom_delta="20"
              label="Constrain proportions"
              layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml
index 22c1139cdb5539ae92f7030ea899fb89326cb787..3aa34439f13210f1f1b51cd88d147bc29f3831b6 100644
--- a/indra/newview/skins/default/xui/en/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml
@@ -35,8 +35,8 @@
     </panel.string>
   <panel
     height="18"
-    left="-370"
-    width="160"
+    left="-395"
+    width="185"
     top="1"
     follows="right|top" 
     name="balance_bg">
diff --git a/indra/newview/skins/default/xui/en/panel_toast.xml b/indra/newview/skins/default/xui/en/panel_toast.xml
index 0b5aff54ca8fca29db3ad694a05bf89909aaace0..37a904bca81ac724d06613c70a9eef3f0846d6a1 100644
--- a/indra/newview/skins/default/xui/en/panel_toast.xml
+++ b/indra/newview/skins/default/xui/en/panel_toast.xml
@@ -10,7 +10,6 @@
 -->
 
 <floater
- open_positioning="none"
  legacy_header_height="0"
  header_height="0"
  name="toast"
diff --git a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
index ee790e8dd4c95070d20f77a108d4fc8c4b73698f..6ecb57b41d8da6bc45d16df29bc672080f1618ed 100644
--- a/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/en/sidepanel_inventory.xml
@@ -48,10 +48,10 @@
                       height="300"
                       width="330" />
              </layout_panel>
-			    <layout_panel
+			 <layout_panel
                  width="330"
                  layout="topleft"
-                 auto_resize="true"
+                 auto_resize="false"
                  user_resize="true"
                  follows="left|right|top"
                  name="inbox_layout_panel"
@@ -105,6 +105,7 @@
                         [NUM] new
                      </text>
                      <panel
+                        name="inbox_inventory_placeholder_panel"
                         follows="all"
                         left="10"
                         bottom="235"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 97526526794d8f7044523b93e2c15230729df61e..866ea296c3a33ed675c8b0e329931060ebd7b857 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -393,6 +393,7 @@ Please try logging in again in a minute.</string>
   <string name="reconnect_nearby">You will now be reconnected to Nearby Voice Chat</string>
 	<string name="ScriptQuestionCautionChatGranted">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been granted permission to: [PERMISSIONS].</string>
 	<string name="ScriptQuestionCautionChatDenied">'[OBJECTNAME]', an object owned by '[OWNERNAME]', located in [REGIONNAME] at [REGIONPOS], has been denied permission to: [PERMISSIONS].</string>
+	<string name="AdditionalPermissionsRequestHeader">If you allow access to your account, you will also be allowing the object to:</string>
 	<string name="ScriptTakeMoney">Take Linden dollars (L$) from you</string>
 	<string name="ActOnControlInputs">Act on your control inputs</string>
 	<string name="RemapControlInputs">Remap your control inputs</string>
@@ -403,8 +404,9 @@ Please try logging in again in a minute.</string>
 	<string name="AddAndRemoveJoints">Add and remove joints with other objects</string>
 	<string name="ChangePermissions">Change its permissions</string>
 	<string name="TrackYourCamera">Track your camera</string>
-	<string name="ControlYourCamera">Control your camera</string>
-	<string name="NotConnected">Not Connected</string>
+  <string name="ControlYourCamera">Control your camera</string>
+  <string name="TeleportYourAgent">Teleport you</string>
+  <string name="NotConnected">Not Connected</string>
 
 	<!-- Sim Access labels -->
 	<string name="SIM_ACCESS_PG">General</string>
diff --git a/indra/newview/skins/default/xui/en/widgets/floater.xml b/indra/newview/skins/default/xui/en/widgets/floater.xml
index adbb183317884bb66bb78384ce827354fc317d00..97a5ae7d4e575451496c357d644206b4a582a03c 100644
--- a/indra/newview/skins/default/xui/en/widgets/floater.xml
+++ b/indra/newview/skins/default/xui/en/widgets/floater.xml
@@ -2,7 +2,7 @@
 <!-- See also settings.xml UIFloater* settings for configuration -->
 <floater
  name="floater"
- open_positioning="none"
+ positioning="none"
  layout="topleft"
  bg_opaque_color="FloaterFocusBackgroundColor"
  bg_alpha_color="FloaterDefaultBackgroundColor"
diff --git a/indra/newview/skins/default/xui/es/panel_status_bar.xml b/indra/newview/skins/default/xui/es/panel_status_bar.xml
index 79b2c32b23617c0a3e7d357f8b55784531bcea8a..7eead3bc184ddb340d1d18590c79931fc3598f5c 100644
--- a/indra/newview/skins/default/xui/es/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/es/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		[AMT] L$
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-410" name="balance_bg" width="200">
 		<text name="balance" tool_tip="Haz clic para actualizar tu saldo en L$" value="20 L$"/>
 		<button label="Comprar L$" name="buyL" tool_tip="Pulsa para comprar más L$"/>
 		<button label="Comprar" name="goShop" tool_tip="Abrir el mercado de Second Life" width="80"/>
diff --git a/indra/newview/skins/default/xui/fr/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/fr/floater_animation_anim_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e82518ce8055749ee1100ab2ff1db779a8929abd
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/floater_animation_anim_preview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Anim Preview" title="ANIMATION.ANIM">
+	<text name="name_label">
+		Nom :
+	</text>
+	<text name="description_label">
+		Description :
+	</text>
+	<button label="Charger ([AMOUNT] L$)" name="ok_btn"/>
+	<button label="Annuler" label_selected="Annuler" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/fr/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/fr/floater_animation_bvh_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..84c40b5987f38526034301bd2b80a6589338f66d
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/floater_animation_bvh_preview.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Animation Preview">
+	<floater.string name="failed_to_initialize">
+		Échec de l&apos;initialisation du mouvement
+	</floater.string>
+	<floater.string name="anim_too_long">
+		La durée du fichier d&apos;animation est de [LENGTH] secondes.
+
+La limite maximale est de [MAX_LENGTH] secondes.
+	</floater.string>
+	<floater.string name="failed_file_read">
+		Impossible de lire le fichier d&apos;animation.
+
+[STATUS]
+	</floater.string>
+	<floater.string name="E_ST_OK">
+		Ok
+	</floater.string>
+	<floater.string name="E_ST_EOF">
+		Fin prématurée du fichier.
+	</floater.string>
+	<floater.string name="E_ST_NO_CONSTRAINT">
+		Impossible de lire la définition des contraintes.
+	</floater.string>
+	<floater.string name="E_ST_NO_FILE">
+		Impossible d&apos;ouvrir le fichier BVH.
+	</floater.string>
+	<floater.string name="E_ST_NO_HIER">
+		En-tête HIERARCHY non valide.
+	</floater.string>
+	<floater.string name="E_ST_NO_JOINT">
+		Impossible de trouver ROOT ou JOINT.
+	</floater.string>
+	<floater.string name="E_ST_NO_NAME">
+		Impossible d&apos;obtenir le nom JOINT.
+	</floater.string>
+	<floater.string name="E_ST_NO_OFFSET">
+		Impossible de trouver OFFSET.
+	</floater.string>
+	<floater.string name="E_ST_NO_CHANNELS">
+		CHANNELS introuvables.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROTATION">
+		Impossible d&apos;obtenir l&apos;ordre de rotation.
+	</floater.string>
+	<floater.string name="E_ST_NO_AXIS">
+		Impossible d&apos;obtenir l&apos;axe de rotation.
+	</floater.string>
+	<floater.string name="E_ST_NO_MOTION">
+		Impossible de trouver MOTION.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAMES">
+		Impossible d&apos;obtenir le nombre d&apos;images.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAME_TIME">
+		Impossible d&apos;obtenir la durée des images.
+	</floater.string>
+	<floater.string name="E_ST_NO_POS">
+		Impossible d&apos;obtenir les valeurs de position.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROT">
+		Impossible d&apos;obtenir les valeurs de rotation.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_FILE">
+		Impossible d&apos;ouvrir le fichier de traduction.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HEADER">
+		Impossible de lire l&apos;en-tête de traduction.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_NAME">
+		Impossible de lire les noms de traduction.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_IGNORE">
+		Impossible de lire la valeur ignorant la traduction.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_RELATIVE">
+		Impossible de lire la valeur relative de la traduction.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_OUTNAME">
+		Impossible de lire la valeur outname de la traduction.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MATRIX">
+		Impossible de lire la matrice de traduction.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGECHILD">
+		Impossible d&apos;obtenir le nom mergechild.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGEPARENT">
+		Impossible d&apos;obtenir le nom mergeparent.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_PRIORITY">
+		Impossible d&apos;obtenir la valeur priority.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_LOOP">
+		Impossible d&apos;obtenir la valeur loop.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEIN">
+		Impossible d&apos;obtenir les valeurs easeIn.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEOUT">
+		Impossible d&apos;obtenir les valeurs easeOut.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HAND">
+		Impossible d&apos;obtenir la valeur hand morph.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EMOTE">
+		Impossible de lire le nom emote.
+	</floater.string>
+	<floater.string name="E_ST_BAD_ROOT">
+		Nom root joint incorrect ; utiliser &quot;hip&quot;.
+	</floater.string>
+	<text name="name_label">
+		Nom :
+	</text>
+	<text name="description_label">
+		Description :
+	</text>
+	<spinner label="Priorité" name="priority" tool_tip="Contrôle quelles autres animations peuvent être remplacées par cette animation."/>
+	<check_box label="Boucle" name="loop_check" tool_tip="Entraîne la lecture en boucle de cette animation."/>
+	<spinner label="Début (%)" name="loop_in_point" tool_tip="Définit le point de l&apos;animation auquel retourne la boucle."/>
+	<spinner label="Fin (%)" name="loop_out_point" tool_tip="Définit le point de l&apos;animation auquel se termine la boucle."/>
+	<text name="hand_label">
+		Pose des mains
+	</text>
+	<combo_box name="hand_pose_combo" tool_tip="Contrôle ce que font les mains pendant l&apos;animation.">
+		<combo_box.item label="Écartées" name="Spread"/>
+		<combo_box.item label="Détendues" name="Relaxed"/>
+		<combo_box.item label="Montrer du doigt (les deux)" name="PointBoth"/>
+		<combo_box.item label="Poing" name="Fist"/>
+		<combo_box.item label="Détendue (gauche)" name="RelaxedLeft"/>
+		<combo_box.item label="Montrer du doigt (gauche)" name="PointLeft"/>
+		<combo_box.item label="Poing (gauche)" name="FistLeft"/>
+		<combo_box.item label="Détendue (droite)" name="RelaxedRight"/>
+		<combo_box.item label="Montrer du doigt (droite)" name="PointRight"/>
+		<combo_box.item label="Poing (droite)" name="FistRight"/>
+		<combo_box.item label="Salut (droite)" name="SaluteRight"/>
+		<combo_box.item label="Clavier" name="Typing"/>
+		<combo_box.item label="Paix (droite)" name="PeaceRight"/>
+	</combo_box>
+	<text name="emote_label">
+		Expression
+	</text>
+	<combo_box name="emote_combo" tool_tip="Contrôle ce que fait le visage pendant l&apos;animation.">
+		<item label="(Aucune)" name="[None]" value=""/>
+		<item label="Aaaaah" name="Aaaaah" value="Aaaaah"/>
+		<item label="Effrayé" name="Afraid" value="Effrayé"/>
+		<item label="En colère" name="Angry" value="En colère"/>
+		<item label="Grand sourire" name="BigSmile" value="Grand sourire"/>
+		<item label="Ennui" name="Bored" value="Ennui"/>
+		<item label="Pleurer" name="Cry" value="Pleurer"/>
+		<item label="Mépris" name="Disdain" value="Mépris"/>
+		<item label="Gêne" name="Embarrassed" value="Gêne"/>
+		<item label="Froncer les sourcils" name="Frown" value="Froncer les sourcils"/>
+		<item label="Embrasser" name="Kiss" value="Embrasser"/>
+		<item label="Rire" name="Laugh" value="Rire"/>
+		<item label="Tirer la langue" name="Plllppt" value="Tirer la langue"/>
+		<item label="Dégoût" name="Repulsed" value="Dégoût"/>
+		<item label="Triste" name="Sad" value="Triste"/>
+		<item label="Hausser les épaules" name="Shrug" value="Hausser les épaules"/>
+		<item label="Sourire" name="Smile" value="Sourire"/>
+		<item label="Surprise" name="Surprise" value="Surprise"/>
+		<item label="Clin d&apos;Å“il" name="Wink" value="Clin d&apos;Å“il"/>
+		<item label="Inquiétude" name="Worry" value="Inquiétude"/>
+	</combo_box>
+	<text name="preview_label">
+		Aperçu
+	</text>
+	<combo_box name="preview_base_anim" tool_tip="Permet de tester le comportement de l&apos;animation lorsque votre avatar effectue certaines actions courantes.">
+		<item label="Debout" name="Standing" value="Debout"/>
+		<item label="En marche" name="Walking" value="En marche"/>
+		<item label="Assis" name="Sitting" value="Assis"/>
+		<item label="En vol" name="Flying" value="En vol"/>
+	</combo_box>
+	<spinner label="Transition début (s)" name="ease_in_time" tool_tip="Durée (en secondes) de l&apos;entrée en fondu de l&apos;animation."/>
+	<spinner label="Transition fin (s)" name="ease_out_time" tool_tip="Durée (en secondes) de la sortie en fondu de l&apos;animation."/>
+	<button name="play_btn" tool_tip="Lire l&apos;animation."/>
+	<button name="pause_btn" tool_tip="Suspendre l&apos;animation."/>
+	<button name="stop_btn" tool_tip="Arrêter la lecture de l&apos;animation."/>
+	<text name="bad_animation_text">
+		Impossible de lire le fichier d&apos;animation.
+
+Les fichiers BVH exportés depuis Poser 4 sont recommandés.
+	</text>
+	<button label="Charger ([AMOUNT] L$)" name="ok_btn"/>
+	<button label="Annuler" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/fr/floater_preview_animation.xml b/indra/newview/skins/default/xui/fr/floater_preview_animation.xml
index fdd2ac8beb6d8c5ab70a8a43eeaa91a7b23c31b3..f2cb1d5e70eb0ade6fa6c54e65fbc74d1e4c9e2f 100644
--- a/indra/newview/skins/default/xui/fr/floater_preview_animation.xml
+++ b/indra/newview/skins/default/xui/fr/floater_preview_animation.xml
@@ -6,6 +6,6 @@
 	<text name="desc txt">
 		Description :
 	</text>
-	<button label="Exécuter dans Second Life" label_selected="Stop" name="Anim play btn" tool_tip="Lire cette animation de façon à ce que les autres puissent la voir" width="131"/>
-	<button label="Exécuter localement" label_selected="Stop" left="160" name="Anim audition btn" tool_tip="Lire cette animation de façon à ce que vous soyez la seule personne à pouvoir la voir" width="120"/>
+	<button label="Exécuter dans Second Life" label_selected="Arrêter" name="Inworld" tool_tip="Lire cette animation de façon à ce que les autres la voient."/>
+	<button label="Exécuter localement" label_selected="Arrêter" name="Locally" tool_tip="Lire cette animation de façon à ce que vous soyez la seule personne à la voir."/>
 </floater>
diff --git a/indra/newview/skins/default/xui/fr/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/fr/floater_test_text_vertical_aligment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..702170ef524a9d1accfff8ca2697c5f197b574fa
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/floater_test_text_vertical_aligment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Test Floater" title="FLOATER TEST"/>
diff --git a/indra/newview/skins/default/xui/fr/floater_tools.xml b/indra/newview/skins/default/xui/fr/floater_tools.xml
index af5678ff0e796ec87db514445b4a3241aa002d06..e21c6f4c08e78b30110e09d6461706aa09e8e09a 100644
--- a/indra/newview/skins/default/xui/fr/floater_tools.xml
+++ b/indra/newview/skins/default/xui/fr/floater_tools.xml
@@ -1,5 +1,20 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="toolbox floater" short_title="OUTILS POUR LA CONSTRUCTION" title="">
+	<floater.string name="grid_screen_text">
+		Écran
+	</floater.string>
+	<floater.string name="grid_local_text">
+		Local
+	</floater.string>
+	<floater.string name="grid_world_text">
+		Monde
+	</floater.string>
+	<floater.string name="grid_reference_text">
+		Référence
+	</floater.string>
+	<floater.string name="grid_attachment_text">
+		Élément attaché
+	</floater.string>
 	<floater.string name="status_rotate">
 		Pour faire tourner l&apos;objet, faites glisser les bandes de couleur.
 	</floater.string>
@@ -63,7 +78,12 @@
 	</text>
 	<check_box initial_value="true" label="Étirer les textures" name="checkbox stretch textures"/>
 	<check_box initial_value="true" label="Fixer" name="checkbox snap to grid"/>
-	<button label="Options..." label_selected="Options..." name="Options..." tool_tip="Afficher d&apos;autres options de grille"/>
+	<combo_box name="combobox grid mode" tool_tip="Choisir le type d&apos;axe de grille pour le positionnement de l&apos;objet.">
+		<combo_box.item label="Monde" name="World"/>
+		<combo_box.item label="Local" name="Local"/>
+		<combo_box.item label="Référence" name="Reference"/>
+	</combo_box>
+	<button label="" label_selected="Options..." name="Options..." tool_tip="Afficher d&apos;autres options de grille"/>
 	<button label="" label_selected="" name="ToolCube" tool_tip="Cube"/>
 	<button label="" label_selected="" name="ToolPrism" tool_tip="Prisme droit"/>
 	<button label="" label_selected="" name="ToolPyramid" tool_tip="Pyramide"/>
@@ -106,7 +126,7 @@
 		Aucune sélection effectuée.
 	</text>
 	<text name="remaining_capacity">
-		[CAPACITY_STRING] [secondlife:///app/openfloater/object_weights Plus d'infos]
+		[CAPACITY_STRING] [secondlife:///app/openfloater/object_weights Plus d&apos;infos]
 	</text>
 	<tab_container name="Object Info Tabs">
 		<panel label="Général" name="General">
diff --git a/indra/newview/skins/default/xui/fr/floater_voice_effect.xml b/indra/newview/skins/default/xui/fr/floater_voice_effect.xml
index 92ee6ecf24f875ef91f8dc8091fc103f7aabd773..671fb5c14db16b31120104fe2ba635538e8a4172 100644
--- a/indra/newview/skins/default/xui/fr/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/fr/floater_voice_effect.xml
@@ -42,13 +42,16 @@
 	<string name="effect_Demon">
 		Démon
 	</string>
+	<string name="effect_Female Elf">
+		Femme elfe
+	</string>
 	<string name="effect_Flirty">
 		Flirt
 	</string>
 	<string name="effect_Foxy">
 		Séduction
 	</string>
-	<string name="effect_Halloween_2010_Bonus">
+	<string name="effect_Halloween 2010 Bonus">
 		Halloween_2010_Bonus
 	</string>
 	<string name="effect_Helium">
@@ -57,9 +60,18 @@
 	<string name="effect_Husky">
 		Rauque
 	</string>
+	<string name="effect_Husky Whisper">
+		Murmure rauque
+	</string>
 	<string name="effect_Intercom">
 		Interphone
 	</string>
+	<string name="effect_Julia">
+		Julia
+	</string>
+	<string name="effect_Lo Lilt">
+		Mélodieux
+	</string>
 	<string name="effect_Macho">
 		Macho
 	</string>
@@ -69,6 +81,9 @@
 	<string name="effect_Mini">
 		Mini
 	</string>
+	<string name="effect_Model">
+		Modèle
+	</string>
 	<string name="effect_Nano">
 		Nano
 	</string>
@@ -90,6 +105,9 @@
 	<string name="effect_Roxanne">
 		Roxanne
 	</string>
+	<string name="effect_Rumble">
+		Grondement
+	</string>
 	<string name="effect_Sabrina">
 		Sabrina
 	</string>
@@ -102,6 +120,9 @@
 	<string name="effect_Shorty">
 		Petite voix
 	</string>
+	<string name="effect_Smaller">
+		Plus faible
+	</string>
 	<string name="effect_Sneaky">
 		Sournois
 	</string>
diff --git a/indra/newview/skins/default/xui/fr/menu_inventory.xml b/indra/newview/skins/default/xui/fr/menu_inventory.xml
index 4abc74880f1d15dec487d57d8e6211a9141c9fae..59dcff907556df80dcf24dbde73ffaa3363a9e36 100644
--- a/indra/newview/skins/default/xui/fr/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/fr/menu_inventory.xml
@@ -59,6 +59,7 @@
 	<menu_item_call label="Propriétés" name="Properties"/>
 	<menu_item_call label="Renommer" name="Rename"/>
 	<menu_item_call label="Copier l&apos;UUID (identifiant universel unique)" name="Copy Asset UUID"/>
+	<menu_item_call label="Couper" name="Cut"/>
 	<menu_item_call label="Copier" name="Copy"/>
 	<menu_item_call label="Coller" name="Paste"/>
 	<menu_item_call label="Coller comme lien" name="Paste As Link"/>
diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml
index 3ea863131a7fb7e9a77ad4179799a0340471cf4b..3c3d4f5f6940104d92dcf95b59213dd02c5ebaa8 100644
--- a/indra/newview/skins/default/xui/fr/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml
@@ -21,6 +21,7 @@
 			<menu_item_call label="Occupé" name="Set Busy"/>
 		</menu>
 		<menu_item_call label="Acheter des L$..." name="Buy and Sell L$"/>
+		<menu_item_call label="Boîte d&apos;envoi vendeur..." name="MerchantOutbox"/>
 		<menu_item_call label="Page d&apos;accueil du compte..." name="Manage My Account">
 			<menu_item_call.on_click name="ManageMyAccount_url" parameter="WebLaunchJoinNow,http://secondlife.com/account/index.php?lang=fr"/>
 		</menu_item_call>
@@ -393,9 +394,16 @@
 		<menu_item_check label="Textures HTTP" name="HTTP Textures"/>
 		<menu_item_check label="Inventaire HTTP" name="HTTP Inventory"/>
 		<menu_item_call label="Compresser les images" name="Compress Images"/>
+		<menu_item_call label="Activer Visual Leak Detector" name="Enable Visual Leak Detector"/>
 		<menu_item_check label="Output Debug Minidump" name="Output Debug Minidump"/>
 		<menu_item_check label="Console Window on next Run" name="Console Window"/>
-		<menu label="Définir le niveau de connexion" name="Set Logging Level"/>
+		<menu label="Définir le niveau de connexion" name="Set Logging Level">
+			<menu_item_check label="Débogage" name="Debug"/>
+			<menu_item_check label="Infos" name="Info"/>
+			<menu_item_check label="Avertissement" name="Warning"/>
+			<menu_item_check label="Erreur" name="Error"/>
+			<menu_item_check label="Aucun" name="None"/>
+		</menu>
 		<menu_item_call label="Demander le statut Admin" name="Request Admin Options"/>
 		<menu_item_call label="Quitter le statut Admin" name="Leave Admin Options"/>
 		<menu_item_check label="Afficher le menu Admin" name="View Admin Options"/>
diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml
index f2dd02a495d0794897616dbe7d6f266c15900962..746a4b1d5552800f2ebb6a18a6956a070f0b2494 100644
--- a/indra/newview/skins/default/xui/fr/notifications.xml
+++ b/indra/newview/skins/default/xui/fr/notifications.xml
@@ -669,7 +669,7 @@ Assurez-vous que le fichier a l&apos;extension correcte.
 		Impossible de créer le fichier de sortie : [FILE]
 	</notification>
 	<notification name="DoNotSupportBulkAnimationUpload">
-		Actuellement, [APP_NAME] ne prend pas en charge le chargement de lots de fichiers d&apos;animation.
+		Actuellement, [APP_NAME] ne prend pas en charge le chargement par lot de fichiers d&apos;animation au format BVH.
 	</notification>
 	<notification name="CannotUploadReason">
 		Impossible de charger [FILE] suite au problème suivant : [REASON]
@@ -2630,16 +2630,16 @@ Accepter cette requête ?
 		&lt;nolink&gt;[TITLE]&lt;/nolink&gt; de [NAME]
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Ignorer"/>
-			<button name="Ignore" text="Ignorer"/>
+			<button name="Client_Side_Mute" text="Bloquer"/>
+			<button name="Client_Side_Ignore" text="Ignorer"/>
 		</form>
 	</notification>
 	<notification name="ScriptDialogGroup">
 		&lt;nolink&gt;[TITLE]&lt;/nolink&gt; de [GROUPNAME]
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Ignorer"/>
-			<button name="Ignore" text="Ignorer"/>
+			<button name="Client_Side_Mute" text="Bloquer"/>
+			<button name="Client_Side_Ignore" text="Ignorer"/>
 		</form>
 	</notification>
 	<notification name="BuyLindenDollarSuccess">
diff --git a/indra/newview/skins/default/xui/fr/panel_nearby_chat.xml b/indra/newview/skins/default/xui/fr/panel_nearby_chat.xml
index 00bd6e81aeda3b49c076cb3bb0579e3b0e7025af..b02e53269b07266948ca5b25e5e002f79e842167 100644
--- a/indra/newview/skins/default/xui/fr/panel_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/fr/panel_nearby_chat.xml
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="nearby_chat">
-	<check_box label="Traduire le chat" name="translate_chat_checkbox"/>
+	<layout_stack name="stack">
+		<layout_panel name="translate_chat_checkbox_lp">
+			<check_box label="Traduire le chat" name="translate_chat_checkbox"/>
+		</layout_panel>
+	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/fr/panel_status_bar.xml b/indra/newview/skins/default/xui/fr/panel_status_bar.xml
index c0d59a3182d034c3ae276c666e382ee78bf8831e..ba36a7d2991f5f49f344a32bd45a51848422d851 100644
--- a/indra/newview/skins/default/xui/fr/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/fr/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		[AMT] L$
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-405" name="balance_bg" width="195">
 		<text name="balance" tool_tip="Cliquer sur ce bouton pour actualiser votre solde en L$." value="20 L$"/>
 		<button label="Acheter L$" name="buyL" tool_tip="Cliquer pour acheter plus de L$."/>
 		<button label="Achats" name="goShop" tool_tip="Ouvrir la Place du marché Second Life." width="75"/>
diff --git a/indra/newview/skins/default/xui/fr/sidepanel_inventory.xml b/indra/newview/skins/default/xui/fr/sidepanel_inventory.xml
index cdb15a632df67ec292de181d09d3979bde82ff49..1b9c8326799883a2d5423be00fffb56507475b4b 100644
--- a/indra/newview/skins/default/xui/fr/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/fr/sidepanel_inventory.xml
@@ -14,7 +14,7 @@
 					<text name="inbox_fresh_new_count">
 						[NUM] nouv.
 					</text>
-					<panel tool_tip="Drag and drop items to your inventory to manage and use them">
+					<panel name="inbox_inventory_placeholder_panel" tool_tip="Glisser-déposer des articles dans votre inventaire pour les utiliser.">
 						<text name="inbox_inventory_placeholder">
 							Ici seront livrés les achats effectués sur la Place du marché.
 						</text>
diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml
index a0c542d5249a08002a41c2bd31f860764d637d66..3eebed450f6eaa48843ad77c49cd9a9a593f3320 100644
--- a/indra/newview/skins/default/xui/fr/strings.xml
+++ b/indra/newview/skins/default/xui/fr/strings.xml
@@ -835,6 +835,9 @@ Veuillez réessayer de vous connecter dans une minute.
 	<string name="anim_yes_head">
 		Oui
 	</string>
+	<string name="multiple_textures">
+		Multiples
+	</string>
 	<string name="texture_loading">
 		Chargement...
 	</string>
@@ -1235,7 +1238,7 @@ Veuillez réessayer de vous connecter dans une minute.
 		Vous n&apos;avez pas de copie de cette texture dans votre inventaire
 	</string>
 	<string name="InventoryInboxNoItems">
-		Certains articles reçus, tels que les cadeaux Premium, s&apos;afficheront ici. Vous pourrez alors les faire glisser vers votre inventaire.
+		Les achats que vous avez effectués sur la Place du marché s&apos;affichent ici. Vous pouvez alors les faire glisser vers votre inventaire afin de les utiliser.
 	</string>
 	<string name="MarketplaceURL">
 		https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
@@ -3921,6 +3924,9 @@ Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE].
 	<string name="Saved_message">
 		(Enregistrement : [LONG_TIMESTAMP])
 	</string>
+	<string name="IM_unblock_only_groups_friends">
+		Pour afficher ce message, vous devez désactiver la case Seuls mes amis et groupes peuvent m&apos;appeler ou m&apos;envoyer un IM, sous Préférences/Confidentialité.
+	</string>
 	<string name="answered_call">
 		Votre appel a fait l&apos;objet d&apos;une réponse
 	</string>
diff --git a/indra/newview/skins/default/xui/it/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/it/floater_animation_anim_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a2bce0014175004bb05fa8a4c8d9b61881d99ac4
--- /dev/null
+++ b/indra/newview/skins/default/xui/it/floater_animation_anim_preview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Anim Preview" title="ANIMATION.ANIM">
+	<text name="name_label">
+		Nome:
+	</text>
+	<text name="description_label">
+		Descrizione:
+	</text>
+	<button label="Carica ([AMOUNT] L$)" name="ok_btn"/>
+	<button label="Annulla" label_selected="Annulla" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/it/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/it/floater_animation_bvh_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a4319f2e77ed253209ebd5f0732adbdd46bfcbc6
--- /dev/null
+++ b/indra/newview/skins/default/xui/it/floater_animation_bvh_preview.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Animation Preview">
+	<floater.string name="failed_to_initialize">
+		Movimento non inizializzato
+	</floater.string>
+	<floater.string name="anim_too_long">
+		La lunghezza del file di animazione è di [LENGTH] secondi.
+
+La lunghezza massima dell&apos;animazione è [MAX_LENGTH] secondi.
+	</floater.string>
+	<floater.string name="failed_file_read">
+		Impossibile leggere il file di animazione.
+
+[STATUS]
+	</floater.string>
+	<floater.string name="E_ST_OK">
+		Ok
+	</floater.string>
+	<floater.string name="E_ST_EOF">
+		Fine prematura del file.
+	</floater.string>
+	<floater.string name="E_ST_NO_CONSTRAINT">
+		Impossibile leggere la definizione del vincolo.
+	</floater.string>
+	<floater.string name="E_ST_NO_FILE">
+		Impossibile aprire il file BVH.
+	</floater.string>
+	<floater.string name="E_ST_NO_HIER">
+		Intestazione HIERARCHY non valida.
+	</floater.string>
+	<floater.string name="E_ST_NO_JOINT">
+		Impossibile trovare la ROOT o JOINT.
+	</floater.string>
+	<floater.string name="E_ST_NO_NAME">
+		Impossibile trovare il nome JOINT.
+	</floater.string>
+	<floater.string name="E_ST_NO_OFFSET">
+		Impossibile trovare OFFSET.
+	</floater.string>
+	<floater.string name="E_ST_NO_CHANNELS">
+		Impossibile trovare CHANNELS.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROTATION">
+		Impossibile ottenere un ordine di rotazione.
+	</floater.string>
+	<floater.string name="E_ST_NO_AXIS">
+		Rotazione dell&apos;asse non disponibile.
+	</floater.string>
+	<floater.string name="E_ST_NO_MOTION">
+		Impossibile trovare MOTION.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAMES">
+		Impossibile ottenere il numero dei frame.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAME_TIME">
+		Impossibile ottenere il tempo del frame.
+	</floater.string>
+	<floater.string name="E_ST_NO_POS">
+		Impossibile ottenere i valori della posizione.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROT">
+		Impossibile ottenere i valori di rotazione.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_FILE">
+		Impossibile aprire il file di traduzione.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HEADER">
+		Impossibile leggere l&apos;intestazione della traduzione.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_NAME">
+		Impossibile leggere i nomi della traduzione.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_IGNORE">
+		Impossibile leggere la traduzione, ignora il valore.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_RELATIVE">
+		Impossibile leggere la traduzione del valore relativo.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_OUTNAME">
+		Valore non trovato.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MATRIX">
+		Impossibile leggere la matrice di traduzione.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGECHILD">
+		Impossibile trovare il nome mergechild.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGEPARENT">
+		Impossibile ottenere il nome mergeparent.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_PRIORITY">
+		Impossibile ottenere il valore di priorità.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_LOOP">
+		Impossibile ottenere il valore di ripetizione.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEIN">
+		Impossibile ottenere i valori easeIn.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEOUT">
+		Cannot get ease Out values.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HAND">
+		Impossibile ottenere il valore morph della mano.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EMOTE">
+		Impossibile leggere il nome emote.
+	</floater.string>
+	<floater.string name="E_ST_BAD_ROOT">
+		Nome non corretto del root joint, usa &quot;hip&quot;.
+	</floater.string>
+	<text name="name_label">
+		Nome:
+	</text>
+	<text name="description_label">
+		Descrizione:
+	</text>
+	<spinner label="Priorità" name="priority" tool_tip="Definisce quali altre animazioni possono essere sostituite da questa animazione"/>
+	<check_box label="Ripetizione" name="loop_check" tool_tip="Riproduce questa animazione in ripetizione"/>
+	<spinner label="Dentro(%)" name="loop_in_point" tool_tip="Imposta il punto sul quale ritorna l&apos;animazione"/>
+	<spinner label="Fuori(%)" name="loop_out_point" tool_tip="Imposta il punto sul quale termina l&apos;animazione"/>
+	<text name="hand_label">
+		Posa delle mani
+	</text>
+	<combo_box name="hand_pose_combo" tool_tip="Definisce ciò che fanno le mani durante l&apos;animazione">
+		<combo_box.item label="Estese" name="Spread"/>
+		<combo_box.item label="Rilassate" name="Relaxed"/>
+		<combo_box.item label="Indicano entrambe" name="PointBoth"/>
+		<combo_box.item label="Pugno" name="Fist"/>
+		<combo_box.item label="Sinistra rilassata" name="RelaxedLeft"/>
+		<combo_box.item label="Indica sinistra" name="PointLeft"/>
+		<combo_box.item label="Pugno con la sinistra" name="FistLeft"/>
+		<combo_box.item label="Destra rilassata" name="RelaxedRight"/>
+		<combo_box.item label="Indica destra" name="PointRight"/>
+		<combo_box.item label="Pugno destro" name="FistRight"/>
+		<combo_box.item label="Saluta a destra" name="SaluteRight"/>
+		<combo_box.item label="Battitura" name="Typing"/>
+		<combo_box.item label="Pace a destra" name="PeaceRight"/>
+	</combo_box>
+	<text name="emote_label">
+		Espressione
+	</text>
+	<combo_box name="emote_combo" tool_tip="Definisce ciò che fa il viso durante l&apos;animazione">
+		<item label="(Nulla)" name="[None]" value=""/>
+		<item label="Aaaaah" name="Aaaaah" value="Aaaaah"/>
+		<item label="Spavento" name="Afraid" value="Spavento"/>
+		<item label="Arrabbiato" name="Angry" value="Arrabbiato"/>
+		<item label="Grande sorriso" name="BigSmile" value="Grande sorriso"/>
+		<item label="Annoiato" name="Bored" value="Annoiato"/>
+		<item label="Pianto" name="Cry" value="Pianto"/>
+		<item label="Disdegno" name="Disdain" value="Disdegno"/>
+		<item label="Imbarazzato" name="Embarrassed" value="Imbarazzato"/>
+		<item label="Accigliato" name="Frown" value="Accigliato"/>
+		<item label="Bacio" name="Kiss" value="Bacio"/>
+		<item label="Risata" name="Laugh" value="Risata"/>
+		<item label="Linguaccia" name="Plllppt" value="Linguaccia"/>
+		<item label="Repulsione" name="Repulsed" value="Repulsione"/>
+		<item label="Triste" name="Sad" value="Triste"/>
+		<item label="Scrollata di spalle" name="Shrug" value="Scrollata di spalle"/>
+		<item label="Sorriso" name="Smile" value="Sorriso"/>
+		<item label="Stupore" name="Surprise" value="Stupore"/>
+		<item label="Occhiolino" name="Wink" value="Occhiolino"/>
+		<item label="Preoccupato" name="Worry" value="Preoccupato"/>
+	</combo_box>
+	<text name="preview_label">
+		Anteprima mentre
+	</text>
+	<combo_box name="preview_base_anim" tool_tip="Prova il comportamento dell&apos;animazione mentre l&apos;avatar esegue attività comuni.">
+		<item label="In piedi" name="Standing" value="In piedi"/>
+		<item label="Camminare" name="Walking" value="Camminare"/>
+		<item label="Seduto" name="Sitting" value="Seduto"/>
+		<item label="Volare" name="Flying" value="Volare"/>
+	</combo_box>
+	<spinner label="Transizione in ingresso (sec)" name="ease_in_time" tool_tip="Durata (in secondi) della fusione in entrata delle animazioni"/>
+	<spinner label="Transizione in uscita (sec)" name="ease_out_time" tool_tip="Durata (in secondi) della fusione in uscita delle animazioni"/>
+	<button name="play_btn" tool_tip="Riproduci la tua animazione"/>
+	<button name="pause_btn" tool_tip="Metti in pausa la tua animazione"/>
+	<button name="stop_btn" tool_tip="Interrompi la riproduzione dell&apos;animazione"/>
+	<text name="bad_animation_text">
+		Impossibile leggere il file di animazione.
+
+Consigliamo file BVH esportati da Poser 4.
+	</text>
+	<button label="Carica ([AMOUNT] L$)" name="ok_btn"/>
+	<button label="Annulla" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/it/floater_preview_animation.xml b/indra/newview/skins/default/xui/it/floater_preview_animation.xml
index 73082c952686bcd9ab11c5b86bf60078eae46747..ed609c70fa228381969fb146c9ccc6b4ad17995f 100644
--- a/indra/newview/skins/default/xui/it/floater_preview_animation.xml
+++ b/indra/newview/skins/default/xui/it/floater_preview_animation.xml
@@ -6,6 +6,6 @@
 	<text name="desc txt">
 		Descrizione:
 	</text>
-	<button label="Riproduci in Second Life" label_selected="Ferma" left="20" name="Anim play btn" tool_tip="Riproduci questa animazione così che gli altri possano vederla" width="131"/>
-	<button label="Esegui localmente" label_selected="Ferma" left="162" name="Anim audition btn" tool_tip="Riproduci questa animazione così che solo tu possa vederla" width="125"/>
+	<button label="Riproduci in Second Life" label_selected="Ferma" name="Inworld" tool_tip="Riproduci questa animazione così che gli altri possano vederla"/>
+	<button label="Riproduci localmente" label_selected="Ferma" name="Locally" tool_tip="Riproduci questa animazione così che solo tu possa vederla"/>
 </floater>
diff --git a/indra/newview/skins/default/xui/it/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/it/floater_test_text_vertical_aligment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..23da6f7588fc9f0d9da40a9be826c32c3ebf932e
--- /dev/null
+++ b/indra/newview/skins/default/xui/it/floater_test_text_vertical_aligment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Test Floater" title="FINESTRA DI TEST"/>
diff --git a/indra/newview/skins/default/xui/it/floater_tools.xml b/indra/newview/skins/default/xui/it/floater_tools.xml
index 0d981e2424ea46933aee29dcb9813b95f5484130..c963ac72e6a414df3430a9c49a91786a4555ed14 100644
--- a/indra/newview/skins/default/xui/it/floater_tools.xml
+++ b/indra/newview/skins/default/xui/it/floater_tools.xml
@@ -1,5 +1,20 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="toolbox floater" short_title="STRUMENTI PER COSTRUZIONE">
+	<floater.string name="grid_screen_text">
+		Schermo
+	</floater.string>
+	<floater.string name="grid_local_text">
+		Locale
+	</floater.string>
+	<floater.string name="grid_world_text">
+		Mondo
+	</floater.string>
+	<floater.string name="grid_reference_text">
+		Riferimento
+	</floater.string>
+	<floater.string name="grid_attachment_text">
+		Allegato
+	</floater.string>
 	<floater.string name="status_rotate">
 		Sposta le fasce colorate per ruotare l&apos;oggetto
 	</floater.string>
@@ -63,7 +78,12 @@
 	</text>
 	<check_box initial_value="true" label="Ridimensiona le texture" name="checkbox stretch textures"/>
 	<check_box initial_value="true" label="Scatto" name="checkbox snap to grid"/>
-	<button label="Opzioni..." label_selected="Opzioni..." name="Options..." tool_tip="Vedi più opzioni della griglia"/>
+	<combo_box name="combobox grid mode" tool_tip="Scegli il tipo di righello per posizionare l&apos;oggetto">
+		<combo_box.item label="Mondo" name="World"/>
+		<combo_box.item label="Locale" name="Local"/>
+		<combo_box.item label="Riferimento" name="Reference"/>
+	</combo_box>
+	<button label="" label_selected="Opzioni..." name="Options..." tool_tip="Vedi più opzioni della griglia"/>
 	<button label="" label_selected="" name="ToolCube" tool_tip="Cubo"/>
 	<button label="" label_selected="" name="ToolPrism" tool_tip="Prisma"/>
 	<button label="" label_selected="" name="ToolPyramid" tool_tip="Piramide"/>
diff --git a/indra/newview/skins/default/xui/it/floater_voice_effect.xml b/indra/newview/skins/default/xui/it/floater_voice_effect.xml
index a0e49525ea648dbfaa42d25ab16ba448d7487b60..c83b11f698a2b4c08a54c04b32ff93fe9ea431a9 100644
--- a/indra/newview/skins/default/xui/it/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/it/floater_voice_effect.xml
@@ -42,13 +42,16 @@
 	<string name="effect_Demon">
 		Demonio
 	</string>
+	<string name="effect_Female Elf">
+		Elfo donna
+	</string>
 	<string name="effect_Flirty">
 		Civettuolo
 	</string>
 	<string name="effect_Foxy">
 		Scaltro
 	</string>
-	<string name="effect_Halloween_2010_Bonus">
+	<string name="effect_Halloween 2010 Bonus">
 		Halloween_2010_Bonus
 	</string>
 	<string name="effect_Helium">
@@ -57,9 +60,18 @@
 	<string name="effect_Husky">
 		Fusto
 	</string>
+	<string name="effect_Husky Whisper">
+		Sospiro rauco
+	</string>
 	<string name="effect_Intercom">
 		Interfono
 	</string>
+	<string name="effect_Julia">
+		Julia
+	</string>
+	<string name="effect_Lo Lilt">
+		Inflessione bassa
+	</string>
 	<string name="effect_Macho">
 		Macho
 	</string>
@@ -69,6 +81,9 @@
 	<string name="effect_Mini">
 		Mini
 	</string>
+	<string name="effect_Model">
+		Modella
+	</string>
 	<string name="effect_Nano">
 		Nano
 	</string>
@@ -90,6 +105,9 @@
 	<string name="effect_Roxanne">
 		Rosanna
 	</string>
+	<string name="effect_Rumble">
+		Rombo
+	</string>
 	<string name="effect_Sabrina">
 		Sabrina
 	</string>
@@ -102,6 +120,9 @@
 	<string name="effect_Shorty">
 		Bassotto
 	</string>
+	<string name="effect_Smaller">
+		Più piccolo
+	</string>
 	<string name="effect_Sneaky">
 		Vile
 	</string>
diff --git a/indra/newview/skins/default/xui/it/menu_inventory.xml b/indra/newview/skins/default/xui/it/menu_inventory.xml
index fc3a82a959fa4a1a422f6e5beb46eb72ab8e1146..4bf6be82fd8afd067f0e0d0ed512f548deb2996a 100644
--- a/indra/newview/skins/default/xui/it/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/it/menu_inventory.xml
@@ -59,6 +59,7 @@
 	<menu_item_call label="Proprietà" name="Properties"/>
 	<menu_item_call label="Rinomina" name="Rename"/>
 	<menu_item_call label="Copia UUID dell&apos;oggetto" name="Copy Asset UUID"/>
+	<menu_item_call label="Taglia" name="Cut"/>
 	<menu_item_call label="Copia" name="Copy"/>
 	<menu_item_call label="Incolla" name="Paste"/>
 	<menu_item_call label="Incolla come link" name="Paste As Link"/>
diff --git a/indra/newview/skins/default/xui/it/menu_viewer.xml b/indra/newview/skins/default/xui/it/menu_viewer.xml
index dee1634a1b9746b0a860045c52f70a07c061af00..99b7e3c4e6b5f5e75167e7f19840b5ae0f8fb9e3 100644
--- a/indra/newview/skins/default/xui/it/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/it/menu_viewer.xml
@@ -21,6 +21,7 @@
 			<menu_item_call label="Non disponibile" name="Set Busy"/>
 		</menu>
 		<menu_item_call label="Acquista L$..." name="Buy and Sell L$"/>
+		<menu_item_call label="Casella venditore in uscita..." name="MerchantOutbox"/>
 		<menu_item_call label="Dashboard dell&apos;account..." name="Manage My Account">
 			<menu_item_call.on_click name="ManageMyAccount_url" parameter="WebLaunchJoinNow,http://secondlife.com/account/index.php?lang=it"/>
 		</menu_item_call>
@@ -334,8 +335,15 @@
 		</menu>
 		<menu_item_check label="Texture HTTP" name="HTTP Textures"/>
 		<menu_item_check label="Inventario HTTP" name="HTTP Inventory"/>
+		<menu_item_call label="Attiva Visual Leak Detector" name="Enable Visual Leak Detector"/>
 		<menu_item_check label="Finestra Console al prossimo lancio" name="Console Window"/>
-		<menu label="Imposta livello di registrazione" name="Set Logging Level"/>
+		<menu label="Imposta livello di registrazione" name="Set Logging Level">
+			<menu_item_check label="Debug" name="Debug"/>
+			<menu_item_check label="Informazioni" name="Info"/>
+			<menu_item_check label="Attenzione" name="Warning"/>
+			<menu_item_check label="Errore" name="Error"/>
+			<menu_item_check label="Nessuno" name="None"/>
+		</menu>
 		<menu_item_call label="Richiedi diritti Admin" name="Request Admin Options"/>
 		<menu_item_call label="Lascia stato Admin" name="Leave Admin Options"/>
 		<menu_item_check label="Mostra menu Admin" name="View Admin Options"/>
diff --git a/indra/newview/skins/default/xui/it/notifications.xml b/indra/newview/skins/default/xui/it/notifications.xml
index 24e8fd6274e09a7f216f20272ab916fdab0b6bfa..0e6fee60d16a99587936c5aedb44874e42124ddb 100644
--- a/indra/newview/skins/default/xui/it/notifications.xml
+++ b/indra/newview/skins/default/xui/it/notifications.xml
@@ -668,7 +668,7 @@ Attese [VALIDS]
 		Impossibile creare il file in uscita: [FILE]
 	</notification>
 	<notification name="DoNotSupportBulkAnimationUpload">
-		[APP_NAME] non supporta ancora il caricamento in blocco di file di animazione.
+		[APP_NAME] non supporta ancora il caricamento in blocco di file di animazione in formato BVH.
 	</notification>
 	<notification name="CannotUploadReason">
 		Impossibile importare il file [FILE] a causa del seguente motivo: [REASON]
@@ -2632,16 +2632,16 @@ Concedi questa richiesta?
 		&apos;&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos; di [NAME]
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Blocca"/>
-			<button name="Ignore" text="Ignora"/>
+			<button name="Client_Side_Mute" text="Blocca"/>
+			<button name="Client_Side_Ignore" text="Ignora"/>
 		</form>
 	</notification>
 	<notification name="ScriptDialogGroup">
 		&apos;&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos; di [GROUPNAME]
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Blocca"/>
-			<button name="Ignore" text="Ignora"/>
+			<button name="Client_Side_Mute" text="Blocca"/>
+			<button name="Client_Side_Ignore" text="Ignora"/>
 		</form>
 	</notification>
 	<notification name="BuyLindenDollarSuccess">
diff --git a/indra/newview/skins/default/xui/it/panel_nearby_chat.xml b/indra/newview/skins/default/xui/it/panel_nearby_chat.xml
index 1b529e2737cf403c303ac640efc6d406d1424dbc..d46a15c735637fb6dcb804d7ffcfd21860498af4 100644
--- a/indra/newview/skins/default/xui/it/panel_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/it/panel_nearby_chat.xml
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="nearby_chat">
-	<check_box label="Traduci chat" name="translate_chat_checkbox"/>
+	<layout_stack name="stack">
+		<layout_panel name="translate_chat_checkbox_lp">
+			<check_box label="Traduci chat" name="translate_chat_checkbox"/>
+		</layout_panel>
+	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/it/panel_status_bar.xml b/indra/newview/skins/default/xui/it/panel_status_bar.xml
index 4abc90113f4cbb7322cb956b6cc425b184e0b8c5..0aaf89d8c89d8788645d40ddb59183d925bb41b2 100644
--- a/indra/newview/skins/default/xui/it/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/it/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		L$ [AMT]
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-405" name="balance_bg" width="195">
 		<text name="balance" tool_tip="Clicca per aggiornare il tuo saldo in L$" value="L$ 20"/>
 		<button label="Acquista L$" name="buyL" tool_tip="Clicca per acquistare più L$"/>
 		<button label="Acquisti" name="goShop" tool_tip="Apri Mercato Second Life" width="75"/>
diff --git a/indra/newview/skins/default/xui/it/sidepanel_inventory.xml b/indra/newview/skins/default/xui/it/sidepanel_inventory.xml
index f5c00f432b5458487e4390095e65e7c57fbbcc69..5ac0961bd754b5be842130b6c4edefd08835538d 100644
--- a/indra/newview/skins/default/xui/it/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/it/sidepanel_inventory.xml
@@ -14,7 +14,7 @@
 					<text name="inbox_fresh_new_count">
 						[NUM] nuovi
 					</text>
-					<panel tool_tip="Drag and drop items to your inventory to manage and use them">
+					<panel name="inbox_inventory_placeholder_panel" tool_tip="Trascina gli elementi nell&apos;inventario per usarli">
 						<text name="inbox_inventory_placeholder">
 							Gli acquisti dal mercato verranno consegnati qui.
 						</text>
diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml
index 29bfab5f0d7da0d256ce167e9a2a8df9f675b96d..8529fadd7def85ed002bfef479bb0a54b158a81c 100644
--- a/indra/newview/skins/default/xui/it/strings.xml
+++ b/indra/newview/skins/default/xui/it/strings.xml
@@ -829,6 +829,9 @@ Prova ad accedere nuovamente tra un minuto.
 	<string name="anim_yes_head">
 		Si
 	</string>
+	<string name="multiple_textures">
+		Multiple
+	</string>
 	<string name="texture_loading">
 		Caricamento in corso...
 	</string>
@@ -1226,7 +1229,7 @@ Prova ad accedere nuovamente tra un minuto.
 		Non hai una copia di questa texture nel tuo inventario
 	</string>
 	<string name="InventoryInboxNoItems">
-		Alcuni elementi che riceverai, come ad esempio gli omaggi per l&apos;abbonamento Premium, verranno mostrati qui.  Potrai quindi trascinarli nel tuo inventario.
+		Gli acquissti dal mercato verranno mostrati qui. Potrai quindi trascinarli nel tuo inventario per usarli.
 	</string>
 	<string name="MarketplaceURL">
 		https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
@@ -3843,6 +3846,9 @@ Se il messaggio persiste, contatta [SUPPORT_SITE].
 	<string name="Saved_message">
 		(Salvato [LONG_TIMESTAMP])
 	</string>
+	<string name="IM_unblock_only_groups_friends">
+		Per vedere questo messaggio, devi deselezionare &apos;Solo amici e gruppi possono chiamarmi o mandarmi IM&apos; in Preferenze/Privacy.
+	</string>
 	<string name="answered_call">
 		Risposto alla chiamata
 	</string>
diff --git a/indra/newview/skins/default/xui/ja/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/ja/floater_animation_anim_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2bada303ae0b690dc7d23e7cdc0a3c6380193ec0
--- /dev/null
+++ b/indra/newview/skins/default/xui/ja/floater_animation_anim_preview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Anim Preview" title="ANIMATION.ANIM">
+	<text name="name_label">
+		名前:
+	</text>
+	<text name="description_label">
+		説明:
+	</text>
+	<button label="アップロード(L$[AMOUNT])" name="ok_btn"/>
+	<button label="取り消し" label_selected="取り消し" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/ja/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/ja/floater_animation_bvh_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f74bab3598941114a511e624ff75419eb0908cd5
--- /dev/null
+++ b/indra/newview/skins/default/xui/ja/floater_animation_bvh_preview.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Animation Preview">
+	<floater.string name="failed_to_initialize">
+		動きを初期化できませんでした
+	</floater.string>
+	<floater.string name="anim_too_long">
+		アニメーションファイルの長さは [LENGTH] 秒です。
+
+アニメーションの最大長は [MAX_LENGTH] 秒です。
+	</floater.string>
+	<floater.string name="failed_file_read">
+		アニメーションファイルを読み取れません。
+
+[STATUS]
+	</floater.string>
+	<floater.string name="E_ST_OK">
+		Ok
+	</floater.string>
+	<floater.string name="E_ST_EOF">
+		ファイルの終端が不完全です。
+	</floater.string>
+	<floater.string name="E_ST_NO_CONSTRAINT">
+		制約定義を読み取れません。
+	</floater.string>
+	<floater.string name="E_ST_NO_FILE">
+		BVH ファイルを開けません。
+	</floater.string>
+	<floater.string name="E_ST_NO_HIER">
+		HIERARCHY ヘッダーが無効です。
+	</floater.string>
+	<floater.string name="E_ST_NO_JOINT">
+		ROOT または JOINT が見つかりません。
+	</floater.string>
+	<floater.string name="E_ST_NO_NAME">
+		JOINT 名を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_OFFSET">
+		OFFSET が見つかりません。
+	</floater.string>
+	<floater.string name="E_ST_NO_CHANNELS">
+		CHANNELS が見つかりません。
+	</floater.string>
+	<floater.string name="E_ST_NO_ROTATION">
+		回転順序を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_AXIS">
+		回転軸を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_MOTION">
+		MOTION が見つかりません。
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAMES">
+		フレーム数を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAME_TIME">
+		フレーム時間を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_POS">
+		位置の値を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_ROT">
+		回転値を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_FILE">
+		変換ファイルを開けません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HEADER">
+		変換ヘッダーを読み取れません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_NAME">
+		変換名を読み取れません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_IGNORE">
+		変換無視値を読み取れません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_RELATIVE">
+		変換相対値を読み取れません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_OUTNAME">
+		変換 outname 値を読み取れません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MATRIX">
+		変換行列を読み取れません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGECHILD">
+		Mergechild 名を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGEPARENT">
+		Mergeparent 名を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_PRIORITY">
+		priority 値を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_LOOP">
+		loop 値を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEIN">
+		easeln 値を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEOUT">
+		easeOut 値を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HAND">
+		Hand morph 値を取得できません。
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EMOTE">
+		emote 名を読みとれません。
+	</floater.string>
+	<floater.string name="E_ST_BAD_ROOT">
+		ルートジョイント名が不正です。「hip」を使用してください。
+	</floater.string>
+	<text name="name_label">
+		名前:
+	</text>
+	<text name="description_label">
+		説明:
+	</text>
+	<spinner label="優先度" name="priority" tool_tip="このアニメーションでどのアニメーションを上書きできるかを決めます"/>
+	<check_box label="ループ" name="loop_check" tool_tip="このアニメーションをループ再生にします"/>
+	<spinner label="イン(%)" name="loop_in_point" tool_tip="アニメーションのループ復帰点を設定します"/>
+	<spinner label="アウト(%)" name="loop_out_point" tool_tip="アニメーションのループ終了点を設定します"/>
+	<text name="hand_label">
+		手のポーズ
+	</text>
+	<combo_box name="hand_pose_combo" tool_tip="アニメーション再生中の手の動きを決めます">
+		<combo_box.item label="広げる" name="Spread"/>
+		<combo_box.item label="リラックス" name="Relaxed"/>
+		<combo_box.item label="指を指す・両手" name="PointBoth"/>
+		<combo_box.item label="拳" name="Fist"/>
+		<combo_box.item label="リラックス・左" name="RelaxedLeft"/>
+		<combo_box.item label="指を指す・左" name="PointLeft"/>
+		<combo_box.item label="拳を上げる・左" name="FistLeft"/>
+		<combo_box.item label="リラックス・右" name="RelaxedRight"/>
+		<combo_box.item label="指を指す・右" name="PointRight"/>
+		<combo_box.item label="拳を上げる・右" name="FistRight"/>
+		<combo_box.item label="敬礼・右" name="SaluteRight"/>
+		<combo_box.item label="タイピング" name="Typing"/>
+		<combo_box.item label="ピース・右" name="PeaceRight"/>
+	</combo_box>
+	<text name="emote_label">
+		表情
+	</text>
+	<combo_box name="emote_combo" tool_tip="アニメーション再生中の顔の表情を決めます">
+		<item label="(なし)" name="[None]" value=""/>
+		<item label="アーーーーー" name="Aaaaah" value="アーーーーー"/>
+		<item label="恐れる" name="Afraid" value="恐れる"/>
+		<item label="怒る" name="Angry" value="怒る"/>
+		<item label="満面の笑み" name="BigSmile" value="満面の笑み"/>
+		<item label="退屈" name="Bored" value="退屈"/>
+		<item label="泣く" name="Cry" value="泣く"/>
+		<item label="軽蔑" name="Disdain" value="軽蔑"/>
+		<item label="恥ずかしがる" name="Embarrassed" value="恥ずかしがる"/>
+		<item label="しかめっ面" name="Frown" value="しかめっ面"/>
+		<item label="キス" name="Kiss" value="キス"/>
+		<item label="笑う" name="Laugh" value="笑う"/>
+		<item label="むかつく" name="Plllppt" value="むかつく"/>
+		<item label="嫌悪感" name="Repulsed" value="嫌悪感"/>
+		<item label="悲しい" name="Sad" value="悲しい"/>
+		<item label="肩をすくめる" name="Shrug" value="肩をすくめる"/>
+		<item label="微笑む" name="Smile" value="微笑む"/>
+		<item label="驚く" name="Surprise" value="驚く"/>
+		<item label="ウィンク" name="Wink" value="ウィンク"/>
+		<item label="心配する" name="Worry" value="心配する"/>
+	</combo_box>
+	<text name="preview_label">
+		プレビュー中の動作
+	</text>
+	<combo_box name="preview_base_anim" tool_tip="これを使用して、アバターが一般的なアクションを実行している間にアニメーションの動作をテストします。">
+		<item label="立つ" name="Standing" value="立つ"/>
+		<item label="歩く" name="Walking" value="歩く"/>
+		<item label="座る" name="Sitting" value="座る"/>
+		<item label="飛ぶ" name="Flying" value="飛ぶ"/>
+	</combo_box>
+	<spinner label="イーズイン(秒)" name="ease_in_time" tool_tip="アニメーションのブレンドイン時間(秒)"/>
+	<spinner label="イーズアウト(秒)" name="ease_out_time" tool_tip="アニメーションのブレンドアウト時間(秒)"/>
+	<button name="play_btn" tool_tip="アニメーションを再生する"/>
+	<button name="pause_btn" tool_tip="アニメーションを一時停止する"/>
+	<button name="stop_btn" tool_tip="アニメーションの再生を停止する"/>
+	<text name="bad_animation_text">
+		アニメーションファイルを読み取れません。
+
+Poser 4 からエクスポートした BVH ファイルをお勧めします。
+	</text>
+	<button label="アップロード(L$[AMOUNT])" name="ok_btn"/>
+	<button label="取り消し" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml
index 725214086a57ca1878349298e6fe762b991b6bef..c01c46211ec323c51780c404f5c9e2f1c96e60ed 100644
--- a/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml
+++ b/indra/newview/skins/default/xui/ja/floater_inventory_item_properties.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="item properties" title="持ち物アイテムのプロパティ">
+<floater name="item properties" title="インベントリアイテムのプロパティ">
 	<floater.string name="unknown">
 		(不明)
 	</floater.string>
@@ -24,11 +24,11 @@
 	<text name="LabelCreatorTitle">
 		クリエーター
 	</text>
-	<button label="情報" label_selected="" name="BtnCreator"/>
+	<button label="プロフィール..." label_selected="" name="BtnCreator"/>
 	<text name="LabelOwnerTitle">
 		オーナー:
 	</text>
-	<button label="情報" label_selected="" name="BtnOwner"/>
+	<button label="プロフィール..." label_selected="" name="BtnOwner"/>
 	<text name="LabelAcquiredTitle">
 		入手日時:
 	</text>
diff --git a/indra/newview/skins/default/xui/ja/floater_inventory_view_finder.xml b/indra/newview/skins/default/xui/ja/floater_inventory_view_finder.xml
index 47d57da0310451c1888e0e7a9762d2263f09d522..af96edda79bbf8cc5082f29c00ed9d247cca6c7c 100644
--- a/indra/newview/skins/default/xui/ja/floater_inventory_view_finder.xml
+++ b/indra/newview/skins/default/xui/ja/floater_inventory_view_finder.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="Inventory Finder" title="最近取得した持ち物アイテム">
+<floater name="Inventory Finder" title="インベントリ最近取得したアイテム">
 	<check_box label="アニメーション" name="check_animation"/>
 	<check_box label="コーリング・カード" name="check_calling_card"/>
 	<check_box label="服" name="check_clothing"/>
diff --git a/indra/newview/skins/default/xui/ja/floater_model_wizard.xml b/indra/newview/skins/default/xui/ja/floater_model_wizard.xml
index 270031a33eeecccb2dffce2a7882c1e4de69abfa..746bd8553c8f3b6b8a0b0806a26274d7d963edcc 100644
--- a/indra/newview/skins/default/xui/ja/floater_model_wizard.xml
+++ b/indra/newview/skins/default/xui/ja/floater_model_wizard.xml
@@ -137,7 +137,7 @@
 			モデルがアップロードされました。
 		</text>
 		<text name="inventory_text">
-			それは持ち物の「オブジェクト」フォルダにあります。
+			それはインベントリの「オブジェクト」フォルダにあります。
 		</text>
 		<text name="charged_fee">
 			あなたのアカウントに L$ [FEE] が請求されました。
diff --git a/indra/newview/skins/default/xui/ja/floater_my_inventory.xml b/indra/newview/skins/default/xui/ja/floater_my_inventory.xml
index c6a789b63b19e73f4b2d82e93271666c5a4cac98..d708fc3decb93b1f3cefec84dd2b61893743c1f5 100644
--- a/indra/newview/skins/default/xui/ja/floater_my_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/floater_my_inventory.xml
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<floater name="floater_my_inventory" title="持ち物"/>
+<floater name="floater_my_inventory" title="インベントリ"/>
diff --git a/indra/newview/skins/default/xui/ja/floater_object_weights.xml b/indra/newview/skins/default/xui/ja/floater_object_weights.xml
index 3bd9b6b069e232660631e482e7a3f50a34a0e2b7..d727a268fb676151ff18241a546b2e8bd5ca5985 100644
--- a/indra/newview/skins/default/xui/ja/floater_object_weights.xml
+++ b/indra/newview/skins/default/xui/ja/floater_object_weights.xml
@@ -6,7 +6,7 @@
 	<text name="objects_label" value="オブジェクト"/>
 	<text name="prims" value="--"/>
 	<text name="prims_label" value="プリム"/>
-	<text name="weights_of_selected_text" value="選択済み項目のウエイト"/>
+	<text name="weights_of_selected_text" value="選択済みアイテムのウエイト"/>
 	<text name="download" value="--"/>
 	<text name="download_label" value="ダウンロード"/>
 	<text name="physics" value="--"/>
diff --git a/indra/newview/skins/default/xui/ja/floater_openobject.xml b/indra/newview/skins/default/xui/ja/floater_openobject.xml
index bd1b650f9855fd24c8c661a412f18064e46c6b42..af02ffedda1091d2874fba0bfa47a6913a418458 100644
--- a/indra/newview/skins/default/xui/ja/floater_openobject.xml
+++ b/indra/newview/skins/default/xui/ja/floater_openobject.xml
@@ -3,6 +3,6 @@
 	<text name="object_name">
 		[DESC]:
 	</text>
-	<button label="持ち物にコピー" label_selected="持ち物にコピー" name="copy_to_inventory_button"/>
+	<button label="インベントリにコピー" label_selected="インベントリにコピー" name="copy_to_inventory_button"/>
 	<button label="コピーして装着" label_selected="コピーして装着" name="copy_and_wear_button"/>
 </floater>
diff --git a/indra/newview/skins/default/xui/ja/floater_people.xml b/indra/newview/skins/default/xui/ja/floater_people.xml
index 08bee881034d19b182f8b99a1e51bee518ffae53..b180658ab721e4bac0b9989c828f2dd630d47809 100644
--- a/indra/newview/skins/default/xui/ja/floater_people.xml
+++ b/indra/newview/skins/default/xui/ja/floater_people.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="floater_people" title="人">
 	<panel_container name="main_panel">
-		<panel label="グループ情報" name="panel_group_info_sidetray"/>
+		<panel label="グループプロフィール" name="panel_group_info_sidetray"/>
 		<panel label="ブロックされた住人とオブジェクト" name="panel_block_list_sidetray"/>
 	</panel_container>
 </floater>
diff --git a/indra/newview/skins/default/xui/ja/floater_preview_animation.xml b/indra/newview/skins/default/xui/ja/floater_preview_animation.xml
index 4fc59e16d336026dd219cf379b687595f581f0af..a3042f66ea3d51329837ba8faf3075ec1c7ad2ee 100644
--- a/indra/newview/skins/default/xui/ja/floater_preview_animation.xml
+++ b/indra/newview/skins/default/xui/ja/floater_preview_animation.xml
@@ -6,6 +6,6 @@
 	<text name="desc txt">
 		説明:
 	</text>
-	<button label="インワールドで再生" label_selected="停止" name="Anim play btn" tool_tip="他人にも見えるように再生します"/>
-	<button label="ローカル再生" label_selected="停止" name="Anim audition btn" tool_tip="自分だけが見えるように再生します"/>
+	<button label="インワールドで再生" label_selected="停止" name="Inworld" tool_tip="他人に見えるように再生"/>
+	<button label="ローカル再生" label_selected="停止" name="Locally" tool_tip="自分だけが見えるように再生"/>
 </floater>
diff --git a/indra/newview/skins/default/xui/ja/floater_preview_texture.xml b/indra/newview/skins/default/xui/ja/floater_preview_texture.xml
index 6ea1d79cfcfcc6b3d10e038b3c065b2cdbca8974..4617fd1d92c76bbc95f90985cfcfb0d3fc7aab0c 100644
--- a/indra/newview/skins/default/xui/ja/floater_preview_texture.xml
+++ b/indra/newview/skins/default/xui/ja/floater_preview_texture.xml
@@ -4,7 +4,7 @@
 		テクスチャ: [NAME]
 	</floater.string>
 	<floater.string name="Copy">
-		持ち物にコピー
+		インベントリにコピー
 	</floater.string>
 	<text name="desc txt">
 		説明:
diff --git a/indra/newview/skins/default/xui/ja/floater_snapshot.xml b/indra/newview/skins/default/xui/ja/floater_snapshot.xml
index cf4732a68ef0c8316545ebe4792eedbbf57f0802..f145a2e8b887ea45df8009b814c4d3576b391b7b 100644
--- a/indra/newview/skins/default/xui/ja/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/ja/floater_snapshot.xml
@@ -10,7 +10,7 @@
 		投稿
 	</string>
 	<string name="inventory_progress_str">
-		持ち物に保存
+		インベントリに保存
 	</string>
 	<string name="local_progress_str">
 		コンピュータに保存
@@ -22,7 +22,7 @@
 		メールが送信されました
 	</string>
 	<string name="inventory_succeeded_str">
-		持ち物に保存されました
+		インベントリに保存されました
 	</string>
 	<string name="local_succeeded_str">
 		コンピュータに保存されました
@@ -34,7 +34,7 @@
 		メールを送信できませんでした。
 	</string>
 	<string name="inventory_failed_str">
-		持ち物に保存できませんでした。
+		インベントリに保存できませんでした。
 	</string>
 	<string name="local_failed_str">
 		コンピュータに保存できませんでした。
diff --git a/indra/newview/skins/default/xui/ja/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/ja/floater_test_text_vertical_aligment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..40fd8e9f93f0c5e74637b8674e7809803bb8345c
--- /dev/null
+++ b/indra/newview/skins/default/xui/ja/floater_test_text_vertical_aligment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Test Floater" title="テスト用ウィンドウ"/>
diff --git a/indra/newview/skins/default/xui/ja/floater_tools.xml b/indra/newview/skins/default/xui/ja/floater_tools.xml
index 8eddf55a44b680da114fa52cf81a07d6d64a178b..2d12a5e56a4592781546b1c31b9cafa718e288eb 100644
--- a/indra/newview/skins/default/xui/ja/floater_tools.xml
+++ b/indra/newview/skins/default/xui/ja/floater_tools.xml
@@ -1,5 +1,20 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="toolbox floater" short_title="制作ツール" title="">
+	<floater.string name="grid_screen_text">
+		画面
+	</floater.string>
+	<floater.string name="grid_local_text">
+		ローカル
+	</floater.string>
+	<floater.string name="grid_world_text">
+		世界
+	</floater.string>
+	<floater.string name="grid_reference_text">
+		リファレンス
+	</floater.string>
+	<floater.string name="grid_attachment_text">
+		アタッチメント
+	</floater.string>
 	<floater.string name="status_rotate">
 		色の付いたバンドをドラッグしてオブジェクトを回転
 	</floater.string>
@@ -63,7 +78,12 @@
 	</text>
 	<check_box initial_value="true" label="テクスチャを引き延ばす" name="checkbox stretch textures"/>
 	<check_box initial_value="true" label="スナップ" name="checkbox snap to grid"/>
-	<button label="オプション..." label_selected="オプション" name="Options..." tool_tip="グリッドオプションを表示します"/>
+	<combo_box name="combobox grid mode" tool_tip="オブジェクトの配置に使うグリッドルーラの種類を選択">
+		<combo_box.item label="世界" name="World"/>
+		<combo_box.item label="ローカル" name="Local"/>
+		<combo_box.item label="リファレンス" name="Reference"/>
+	</combo_box>
+	<button label="" label_selected="オプション" name="Options..." tool_tip="グリッドオプションを表示します"/>
 	<button label="" label_selected="" name="ToolCube" tool_tip="キューブ"/>
 	<button label="" label_selected="" name="ToolPrism" tool_tip="プリズム"/>
 	<button label="" label_selected="" name="ToolPyramid" tool_tip="ピラミッド"/>
diff --git a/indra/newview/skins/default/xui/ja/floater_voice_effect.xml b/indra/newview/skins/default/xui/ja/floater_voice_effect.xml
index 801b7a9db09d3c2ed0a3c182125d1235a3a058c7..ee675e143b00e1c45fea1ad0d50ef51bdd6e30f7 100644
--- a/indra/newview/skins/default/xui/ja/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/ja/floater_voice_effect.xml
@@ -42,13 +42,16 @@
 	<string name="effect_Demon">
 		デーモン
 	</string>
+	<string name="effect_Female Elf">
+		女性のエルフ
+	</string>
 	<string name="effect_Flirty">
 		気のありそうな
 	</string>
 	<string name="effect_Foxy">
 		魅惑的
 	</string>
-	<string name="effect_Halloween_2010_Bonus">
+	<string name="effect_Halloween 2010 Bonus">
 		ハロウィン_2010_ボーナス
 	</string>
 	<string name="effect_Helium">
@@ -57,9 +60,18 @@
 	<string name="effect_Husky">
 		ハスキー
 	</string>
+	<string name="effect_Husky Whisper">
+		スモーキーウィスパー
+	</string>
 	<string name="effect_Intercom">
 		インターホン
 	</string>
+	<string name="effect_Julia">
+		ジュリア
+	</string>
+	<string name="effect_Lo Lilt">
+		軽快
+	</string>
 	<string name="effect_Macho">
 		マッチョ
 	</string>
@@ -69,6 +81,9 @@
 	<string name="effect_Mini">
 		ミニ
 	</string>
+	<string name="effect_Model">
+		モデル
+	</string>
 	<string name="effect_Nano">
 		ナノ
 	</string>
@@ -90,6 +105,9 @@
 	<string name="effect_Roxanne">
 		ロクサン
 	</string>
+	<string name="effect_Rumble">
+		ランブル
+	</string>
 	<string name="effect_Sabrina">
 		サブリナ
 	</string>
@@ -102,6 +120,9 @@
 	<string name="effect_Shorty">
 		チビ
 	</string>
+	<string name="effect_Smaller">
+		小さめ
+	</string>
 	<string name="effect_Sneaky">
 		コソコソ
 	</string>
diff --git a/indra/newview/skins/default/xui/ja/menu_inventory.xml b/indra/newview/skins/default/xui/ja/menu_inventory.xml
index a59f5659c45986acf033a51cdf653f09161078e3..d1893a0fc86184b27b30d5f7b3c275ad3acbb34f 100644
--- a/indra/newview/skins/default/xui/ja/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/menu_inventory.xml
@@ -59,6 +59,7 @@
 	<menu_item_call label="プロパティ" name="Properties"/>
 	<menu_item_call label="名前を変更する" name="Rename"/>
 	<menu_item_call label="UUID をコピーする" name="Copy Asset UUID"/>
+	<menu_item_call label="カット" name="Cut"/>
 	<menu_item_call label="コピー" name="Copy"/>
 	<menu_item_call label="貼り付け" name="Paste"/>
 	<menu_item_call label="リンクを貼り付ける" name="Paste As Link"/>
diff --git a/indra/newview/skins/default/xui/ja/menu_inventory_gear_default.xml b/indra/newview/skins/default/xui/ja/menu_inventory_gear_default.xml
index 1f425df83c0fa87d7430c7f9b7aa2c15bad19556..f38dbc71a8a33ac580afdb126dd1ea1df1339e96 100644
--- a/indra/newview/skins/default/xui/ja/menu_inventory_gear_default.xml
+++ b/indra/newview/skins/default/xui/ja/menu_inventory_gear_default.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <toggleable_menu name="menu_gear_default">
-	<menu_item_call label="新しい持ち物ウィンドウ" name="new_window"/>
+	<menu_item_call label="新しいインベントリウィンドウ" name="new_window"/>
 	<menu_item_check label="名前で並べ替え" name="sort_by_name"/>
 	<menu_item_check label="新しい順に並べ替え" name="sort_by_recent"/>
 	<menu_item_check label="フォルダを常に名前順に並べる" name="sort_folders_by_name"/>
diff --git a/indra/newview/skins/default/xui/ja/menu_places_gear_folder.xml b/indra/newview/skins/default/xui/ja/menu_places_gear_folder.xml
index e64f97fda54be3bdd3566b9ba942ad026705c52c..c45520472273f84849795d18087ef3b7e47a46fd 100644
--- a/indra/newview/skins/default/xui/ja/menu_places_gear_folder.xml
+++ b/indra/newview/skins/default/xui/ja/menu_places_gear_folder.xml
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <toggleable_menu name="menu_folder_gear">
 	<menu_item_call label="ランドマークを追加" name="add_landmark"/>
 	<menu_item_call label="フォルダを追加" name="add_folder"/>
-	<menu_item_call label="商品を復元" name="restore_item"/>
+	<menu_item_call label="アイテムを復元" name="restore_item"/>
 	<menu_item_call label="切り取り" name="cut"/>
 	<menu_item_call label="コピー" name="copy_folder"/>
 	<menu_item_call label="貼り付け" name="paste"/>
diff --git a/indra/newview/skins/default/xui/ja/menu_places_gear_landmark.xml b/indra/newview/skins/default/xui/ja/menu_places_gear_landmark.xml
index f416b5b1f66a653572a7cddd124eb357c378aff9..579f2c2cbd10005438ab513b068d174a5951b003 100644
--- a/indra/newview/skins/default/xui/ja/menu_places_gear_landmark.xml
+++ b/indra/newview/skins/default/xui/ja/menu_places_gear_landmark.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <toggleable_menu name="menu_ladmark_gear">
 	<menu_item_call label="テレポート" name="teleport"/>
 	<menu_item_call label="もっと詳しく" name="more_info"/>
 	<menu_item_call label="地図に表示" name="show_on_map"/>
 	<menu_item_call label="ランドマークを追加" name="add_landmark"/>
 	<menu_item_call label="フォルダを追加" name="add_folder"/>
-	<menu_item_call label="商品を復元" name="restore_item"/>
+	<menu_item_call label="アイテムを復元" name="restore_item"/>
 	<menu_item_call label="切り取り" name="cut"/>
 	<menu_item_call label="ランドマークをコピー" name="copy_landmark"/>
 	<menu_item_call label="SLurl をコピー" name="copy_slurl"/>
diff --git a/indra/newview/skins/default/xui/ja/menu_url_inventory.xml b/indra/newview/skins/default/xui/ja/menu_url_inventory.xml
index 7af2f9e2cd866262c8f7290218f6ffc74e12c08b..147ab44a1b1f1f2e0258bae03c8d72635cfbf487 100644
--- a/indra/newview/skins/default/xui/ja/menu_url_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/menu_url_inventory.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <context_menu name="Url Popup">
-	<menu_item_call label="持ち物アイテムを表示" name="show_item"/>
+	<menu_item_call label="インベントリアイテムを表示" name="show_item"/>
 	<menu_item_call label="名前をクリップボードにコピー" name="url_copy_label"/>
 	<menu_item_call label="SLurl をクリップボードにコピー" name="url_copy"/>
 </context_menu>
diff --git a/indra/newview/skins/default/xui/ja/menu_viewer.xml b/indra/newview/skins/default/xui/ja/menu_viewer.xml
index 4430ec054c39d71b6bec4f301711d1378c854f75..8496dfb1dbff3e9a9ad50a24370555ad8c4c0248 100644
--- a/indra/newview/skins/default/xui/ja/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/ja/menu_viewer.xml
@@ -4,8 +4,8 @@
 		<menu_item_call label="プロフィール..." name="Profile"/>
 		<menu_item_call label="容姿..." name="ChangeOutfit"/>
 		<menu_item_call label="アバターを選択..." name="Avatar Picker"/>
-		<menu_item_check label="持ち物..." name="Inventory"/>
-		<menu_item_call label="新しい持ち物ウィンドウ" name="NewInventoryWindow"/>
+		<menu_item_check label="インベントリ..." name="Inventory"/>
+		<menu_item_call label="新しいインベントリウィンドウ" name="NewInventoryWindow"/>
 		<menu_item_call label="場所..." name="Places"/>
 		<menu_item_call label="ピック..." name="Picks"/>
 		<menu_item_call label="カメラコントロール..." name="Camera Controls"/>
@@ -21,6 +21,7 @@
 			<menu_item_call label="取り込み中" name="Set Busy"/>
 		</menu>
 		<menu_item_call label="L$ の購入..." name="Buy and Sell L$"/>
+		<menu_item_call label="マーチャントアウトボックス..." name="MerchantOutbox"/>
 		<menu_item_call label="マイアカウント..." name="Manage My Account">
 			<menu_item_call.on_click name="ManageMyAccount_url" parameter="WebLaunchJoinNow,http://secondlife.com/account/index.php?lang=ja"/>
 		</menu_item_call>
@@ -112,7 +113,7 @@
 			<menu_item_call label="買う" name="Menu Object Buy"/>
 			<menu_item_call label="取る" name="Menu Object Take"/>
 			<menu_item_call label="コピーを取る" name="Take Copy"/>
-			<menu_item_call label="「持ち物」に保存" name="Save Object Back to My Inventory"/>
+			<menu_item_call label="マイインベントリに保存" name="Save Object Back to My Inventory"/>
 			<menu_item_call label="オブジェクトの中身に保存" name="Save Object Back to Object Contents"/>
 			<menu_item_call label="オブジェクトを返却する" name="Return Object back to Owner"/>
 		</menu>
@@ -337,7 +338,7 @@
 			<menu_item_call label="メディアブラウザのテスト" name="Web Browser Test"/>
 			<menu_item_call label="Web コンテンツブラウザ" name="Web Content Browser"/>
 			<menu_item_call label="SelectMgr をダンプ" name="Dump SelectMgr"/>
-			<menu_item_call label="持ち物の出力" name="Dump Inventory"/>
+			<menu_item_call label="インベントリの出力" name="Dump Inventory"/>
 			<menu_item_call label="タイマーをダンプ" name="Dump Timers"/>
 			<menu_item_call label="フォーカスホールダーをダンプ" name="Dump Focus Holder"/>
 			<menu_item_call label="選択したオブジェクト情報をプリント" name="Print Selected Object Info"/>
@@ -391,11 +392,18 @@
 			<menu_item_call label="ローカルテクスチャをダンプ" name="Dump Local Textures"/>
 		</menu>
 		<menu_item_check label="HTTP Texture" name="HTTP Textures"/>
-		<menu_item_check label="HTTP 持ち物" name="HTTP Inventory"/>
+		<menu_item_check label="HTTP インベントリ" name="HTTP Inventory"/>
 		<menu_item_call label="圧縮画像" name="Compress Images"/>
+		<menu_item_call label="Visual Leak Detector を有効にする" name="Enable Visual Leak Detector"/>
 		<menu_item_check label="デバッグ用のミニダンプを出力する" name="Output Debug Minidump"/>
 		<menu_item_check label="次回の起動時にコンソールウィンドウを表示する" name="Console Window"/>
-		<menu label="ログレベルを設定" name="Set Logging Level"/>
+		<menu label="ログレベルを設定" name="Set Logging Level">
+			<menu_item_check label="デバッグ" name="Debug"/>
+			<menu_item_check label="情報" name="Info"/>
+			<menu_item_check label="警告" name="Warning"/>
+			<menu_item_check label="エラー" name="Error"/>
+			<menu_item_check label="なし" name="None"/>
+		</menu>
 		<menu_item_call label="管理者ステータスの呼び出し" name="Request Admin Options"/>
 		<menu_item_call label="管理者ステータス解除" name="Leave Admin Options"/>
 		<menu_item_check label="管理者メニューを表示する" name="View Admin Options"/>
diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml
index c8e8dbb0f1361d39ab9d1efb3fe121167e5cf93b..7bf8a7b8beea04d10beda4392f7d0f367adb2646 100644
--- a/indra/newview/skins/default/xui/ja/notifications.xml
+++ b/indra/newview/skins/default/xui/ja/notifications.xml
@@ -264,7 +264,7 @@ L$ が不足しているのでこのグループに参加することができ
 		<usetemplate name="okcancelbuttons" notext="キャンセル" yestext="OK"/>
 	</notification>
 	<notification name="ReturnObjectsDeededToGroup">
-		この区画のグループ [NAME] 共有のすべてのオブジェクトを、以前の所有者の「持ち物」に戻そうとしています。
+		この区画のグループ [NAME] 共有のすべてのオブジェクトを、以前の所有者のインベントリに戻そうとしています。
 操作を続行しますか?
 
 *警告* これにより、
@@ -275,21 +275,21 @@ L$ が不足しているのでこのグループに参加することができ
 	<notification name="ReturnObjectsOwnedByUser">
 		この区画で、
 住人 [NAME] が所有する全てのオブジェクトを
-本人の「持ち物」に本当に返却してもよいですか?
+本人のインベントリに本当に返却してもよいですか?
 
 オブジェクト: [N]
 		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="OK"/>
 	</notification>
 	<notification name="ReturnObjectsOwnedBySelf">
 		この土地区画内にある、あなたが所有するすべてのオブジェクトを、
-あなたの「持ち物」に戻そうとしています。続けますか?
+あなたのインベントリに戻そうとしています。続けますか?
 
 オブジェクト: [N]
 		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="OK"/>
 	</notification>
 	<notification name="ReturnObjectsNotOwnedBySelf">
 		この土地区画内にある、あなた以外が所有するすべてのオブジェクトを、
-それぞれの所有者の「持ち物」に戻そうとしています。
+それぞれの所有者のインベントリに戻そうとしています。
 操作を続行しますか?
 グループに譲渡された「再販・プレゼント可」のオブジェクトは、以前の所有者に返却されます。
 
@@ -300,7 +300,7 @@ L$ が不足しているのでこのグループに参加することができ
 	</notification>
 	<notification name="ReturnObjectsNotOwnedByUser">
 		この土地区画内にある、
-[NAME]以外による所有のオブジェクトをすべてそれぞれの所有者の「持ち物」に返却しようとしています。
+[NAME]以外による所有のオブジェクトをすべてそれぞれの所有者のインベントリに返却しようとしています。
 操作を続行しますか?グループに譲渡された「再販・プレゼント可」のオブジェクトは、以前の所有者に返却されます。
 
 *警告* これにより、
@@ -464,14 +464,14 @@ L$ が不足しているのでこのグループに参加することができ
 		ジェスチャーの保存に失敗しました。少し待ってからもう一度試してください。
 	</notification>
 	<notification name="GestureSaveFailedObjectNotFound">
-		ジェスチャーの保存に失敗しました。オブジェクト、または関連するオブジェクト持ち物が見つかりません。
+		ジェスチャーの保存に失敗しました。オブジェクト、または関連するオブジェクトインベントリが見つかりません。
 オブジェクトが範囲内に存在しないか、または削除された可能性があります。
 	</notification>
 	<notification name="GestureSaveFailedReason">
 		次の理由で、ジェスチャーの保存時に問題が起こりました。 [REASON]。  後でもう一度試してください。
 	</notification>
 	<notification name="SaveNotecardFailObjectNotFound">
-		ノートカードの保存に失敗しました。オブジェクト、または関連するオブジェクト持ち物が見つかりません。
+		ノートカードの保存に失敗しました。オブジェクト、または関連するオブジェクトインベントリが見つかりません。
 オブジェクトが範囲内に存在しないか、または削除された可能性があります。
 	</notification>
 	<notification name="SaveNotecardFailReason">
@@ -535,11 +535,11 @@ L$ が不足しているのでこのグループに参加することができ
 		[REGION] では、地形の変更ができません。
 	</notification>
 	<notification name="CannotCopyWarning">
-		あなたには[ITEMS]というアイテムをコピーする許可がありません。他の住人に提供すると、そのアイテムはあなたの「持ち物」から削除されます。本当にこれらのアイテムを譲りますか?
+		あなたには[ITEMS]というアイテムをコピーする許可がありません。他の住人に提供すると、そのアイテムはあなたのインベントリから削除されます。本当にこれらのアイテムを譲りますか?
 		<usetemplate name="okcancelbuttons" notext="いいえ" yestext="はい"/>
 	</notification>
 	<notification name="CannotGiveItem">
-		持ち物のアイテムを渡せません。
+		インベントリのアイテムを渡せません。
 	</notification>
 	<notification name="TransactionCancelled">
 		取引がキャンセルされました。
@@ -552,7 +552,7 @@ L$ が不足しているのでこのグループに参加することができ
 	</notification>
 	<notification name="CannotCopyCountItems">
 		あなたは選択した [COUNT] 個のアイテムののコピーを許されていません。
-これらのアイテムはあなたの「持ち物」から失われます。
+これらのアイテムはあなたのインベントリから失われます。
 本当にアイテムを渡したいですか?
 		<usetemplate name="okcancelbuttons" notext="いいえ" yestext="はい"/>
 	</notification>
@@ -693,7 +693,7 @@ L$ が不足しているのでこのグループに参加することができ
 		出力ファイルを作成できません: [FILE]
 	</notification>
 	<notification name="DoNotSupportBulkAnimationUpload">
-		現在 [APP_NAME] では、アニメーションの一括アップロードはサポートされていません。
+		現在 [APP_NAME] では、BVH 形式のアニメーションファイルの一括アップロードはサポートされていません。
 	</notification>
 	<notification name="CannotUploadReason">
 		次の理由で、「 [FILE] 」をアップロードできません: [REASON]
@@ -796,7 +796,7 @@ L$ が不足しているのでこのグループに参加することができ
 		テレポート目的地を見つけられません。目的地が一時的に利用できない状態か、すでに消滅している可能性があります。数分後にやり直してください。
 	</notification>
 	<notification name="no_inventory_host">
-		持ち物システムは現在利用できません。
+		インベントリシステムは現在利用できません。
 	</notification>
 	<notification name="CannotSetLandOwnerNothingSelected">
 		土地所有者設定ができません:
@@ -959,7 +959,7 @@ L$ は返金されません。
 		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="OK"/>
 	</notification>
 	<notification name="ConfirmItemCopy">
-		このアイテムをあなたの持ち物にコピーしますか?
+		このアイテムをあなたのインベントリにコピーしますか?
 		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="コピー"/>
 	</notification>
 	<notification name="ResolutionSwitchFail">
@@ -1011,7 +1011,7 @@ L$ は返金されません。
 		</form>
 	</notification>
 	<notification label="着用物を保存" name="SaveWearableAs">
-		アイテムを別名で持ち物に保存:
+		アイテムを別名でインベントリに保存:
 		<form name="form">
 			<input name="message">
 				[DESC](新規)
@@ -1896,7 +1896,7 @@ Adult 専用リージョンに入るには、住人のアカウントが年齢
 	</notification>
 	<notification name="BuyCopy">
 		コピーを [OWNER] から L$ [PRICE] で購入しますか?
-購入したオブジェクトは、あなたの「持ち物」にコピーされます。
+購入したオブジェクトは、あなたのインベントリにコピーされます。
 可能な操作は、
 修正:[MODIFYPERM]、コピー:[COPYPERM]、
 再販・プレゼント:[RESELLPERM] です。
@@ -1904,7 +1904,7 @@ Adult 専用リージョンに入るには、住人のアカウントが年齢
 	</notification>
 	<notification name="BuyCopyNoOwner">
 		L$ [PRICE] でコピーを購入しますか?
-購入したオブジェクトは、あなたの「持ち物」にコピーされます。
+購入したオブジェクトは、あなたのインベントリにコピーされます。
 可能な操作は、
 修正:[MODIFYPERM]、コピー:[COPYPERM]、
 再販・プレゼント:[RESELLPERM] です。
@@ -1912,12 +1912,12 @@ Adult 専用リージョンに入るには、住人のアカウントが年齢
 	</notification>
 	<notification name="BuyContents">
 		中身を [OWNER] から L$ [PRICE] で購入しますか?
-購入した中身は、あなたの「持ち物」にコピーされます。
+購入した中身は、あなたのインベントリにコピーされます。
 		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="OK"/>
 	</notification>
 	<notification name="BuyContentsNoOwner">
 		L$ [PRICE] で中身を購入しますか?
-購入した中身は、あなたの「持ち物」にコピーされます。
+購入した中身は、あなたのインベントリにコピーされます。
 		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="OK"/>
 	</notification>
 	<notification name="ConfirmPurchase">
@@ -1946,20 +1946,20 @@ Adult 専用リージョンに入るには、住人のアカウントが年齢
 		<usetemplate name="okbutton" yestext="OK"/>
 	</notification>
 	<notification name="MoveInventoryFromObject">
-		「コピー不可」の持ち物アイテムを選択しました。
-これらのアイテムはコピーされないまま、あなたの「持ち物」に移動されます。
+		「コピー不可」のインベントリアイテムを選択しました。
+これらのアイテムはコピーされないまま、あなたのインベントリに移動されます。
 
 
 アイテムを動かしますか?
 		<usetemplate ignoretext="「コピー不可」のアイテムをオブジェクトから動かす前の警告" name="okcancelignore" notext="キャンセル" yestext="OK"/>
 	</notification>
 	<notification name="MoveInventoryFromScriptedObject">
-		「コピー不可」の持ち物アイテムを選択しました。
-これらのアイテムはコピーされずに、あなたの「持ち物」に移動されます。
-このオブジェクトはスクリプト付きなので、「持ち物」に移動させると
+		「コピー不可」のインベントリアイテムを選択しました。
+これらのアイテムはコピーされずに、あなたのインベントリに移動されます。
+このオブジェクトはスクリプト付きなので、インベントリに移動させると
 スクリプトに誤動作が起きる可能性があります。
 
-持ち物アイテムを移動しますか?
+インベントリアイテムを移動しますか?
 		<usetemplate ignoretext="スクリプト入りのオブジェクトを壊す恐れのある「コピー不可」のアイテムを動かす前の警告" name="okcancelignore" notext="キャンセル" yestext="OK"/>
 	</notification>
 	<notification name="ClickActionNotPayable">
@@ -2068,7 +2068,7 @@ Linden Lab
 	</notification>
 	<notification name="ConfirmEmptyTrash">
 		ごみ箱の中身をすべて削除しますか?
-		<usetemplate ignoretext="持ち物のごみ箱フォルダを空にする前の確認" name="okcancelignore" notext="キャンセル" yestext="OK"/>
+		<usetemplate ignoretext="インベントリのごみ箱フォルダを空にする前の確認" name="okcancelignore" notext="キャンセル" yestext="OK"/>
 	</notification>
 	<notification name="ConfirmClearBrowserCache">
 		トラベル、Web、検索の履歴をすべて削除しますか?
@@ -2252,7 +2252,7 @@ Web ページにリンクすると、他人がこの場所に簡単にアクセ
 		親エステート間では IM を送信できません。
 	</notification>
 	<notification name="TransferInventoryAcrossParentEstates">
-		親エステート間で持ち物を移動することはできません。
+		親エステート間でインベントリを移動することはできません。
 	</notification>
 	<notification name="UnableToLoadNotecard">
 		ノートカードを読み込めません。あとで再度お試しください。
@@ -2304,7 +2304,7 @@ Web ページにリンクすると、他人がこの場所に簡単にアクセ
 		[NAME] は、あなたが渡したアイテムを受け取りました。
 	</notification>
 	<notification name="InventoryDeclined">
-		[NAME] は、持ち物の提供を断りました。
+		[NAME] は、インベントリの提供を断りました。
 	</notification>
 	<notification name="ObjectMessage">
 		[NAME]: [MESSAGE]
@@ -2389,16 +2389,16 @@ Web ページにリンクすると、他人がこの場所に簡単にアクセ
 		</form>
 	</notification>
 	<notification name="OwnedObjectsReturned">
-		選択した土地の区画上にあったあなたのオブジェクトは、すべてあなたの「持ち物」に返却されました。
+		選択した土地の区画上にあったあなたのオブジェクトは、すべてあなたのインベントリに返却されました。
 	</notification>
 	<notification name="OtherObjectsReturned">
-		[NAME] が所有する、選択した区画にあるオブジェクトは、所有者の持ち物に返却されました。
+		[NAME] が所有する、選択した区画にあるオブジェクトは、所有者のインベントリに返却されました。
 	</notification>
 	<notification name="OtherObjectsReturned2">
 		「 [NAME] 」という名前の住人が所有する、選択した区画上のオブジェクトは、本人に返却されました。
 	</notification>
 	<notification name="GroupObjectsReturned">
-		選択した区画上の、[GROUPNAME] というグループと共有していたオブジェクトは、それぞれの所有者の「持ち物」に返却されました。
+		選択した区画上の、[GROUPNAME] というグループと共有していたオブジェクトは、それぞれの所有者のインベントリに返却されました。
 譲渡されていた「再販・プレゼント可」のオブジェクトは、以前の所有者に返却されました。
 グループに譲渡されていた「再販・プレゼント不可」のオブジェクトは、削除されました。
 	</notification>
@@ -2617,7 +2617,7 @@ Web ページにリンクすると、他人がこの場所に簡単にアクセ
 	</notification>
 	<notification name="OfferCallingCard">
 		[NAME] がコーリングカードを渡そうとしています。
-あなたの持ち物にブックマークが追加され、この住人に素早く IM を送ることができます。
+あなたのインベントリにブックマークが追加され、この住人に素早く IM を送ることができます。
 		<form name="form">
 			<button name="Accept" text="受け入れる"/>
 			<button name="Decline" text="辞退"/>
@@ -2679,16 +2679,16 @@ Web ページにリンクすると、他人がこの場所に簡単にアクセ
 		[NAME] の「&lt;nolink&gt;[TITLE]&lt;/nolink&gt;」
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="ブロック"/>
-			<button name="Ignore" text="無視する"/>
+			<button name="Client_Side_Mute" text="ブロック"/>
+			<button name="Client_Side_Ignore" text="無視"/>
 		</form>
 	</notification>
 	<notification name="ScriptDialogGroup">
 		[GROUPNAME] の「&lt;nolink&gt;[TITLE]&lt;/nolink&gt;」
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="ブロック"/>
-			<button name="Ignore" text="無視する"/>
+			<button name="Client_Side_Mute" text="ブロック"/>
+			<button name="Client_Side_Ignore" text="無視"/>
 		</form>
 	</notification>
 	<notification name="BuyLindenDollarSuccess">
@@ -2707,7 +2707,7 @@ M キーを押して変更します。
 	<notification name="FirstSandbox">
 		ここはサンドボックスエリアです。住人が制作を学ぶことができます。
 
-ここで制作されたものは時間が経つと削除されます。制作したアイテムを右クリックして「取る」を選び、持ち物に入れてお持ち帰りするのをお忘れなく。
+ここで制作されたものは時間が経つと削除されます。制作したアイテムを右クリックして「取る」を選び、インベントリに入れてお持ち帰りするのをお忘れなく。
 	</notification>
 	<notification name="MaxListSelectMessage">
 		このリストから [MAX_SELECT] 個までのアイテムを選択できます。
@@ -2728,7 +2728,7 @@ M キーを押して変更します。
 		[NAME] はお金を受け取り、自動的にブロックが解除されました。
 	</notification>
 	<notification name="AutoUnmuteByInventory">
-		[NAME] は持ち物を受け取り、自動的にブロックが解除されました。
+		[NAME] はインベントリを受け取り、自動的にブロックが解除されました。
 	</notification>
 	<notification name="VoiceInviteGroup">
 		[NAME] は [GROUP] のボイスチャットコールに参加しました。
@@ -2892,6 +2892,18 @@ M キーを押して変更します。
 
 次の住人と共有:
 
+[RESIDENTS]
+		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="Ok"/>
+	</notification>
+	<notification name="ShareFolderConfirmation">
+		フォルダは一度に 1 つしか共有できません。
+
+次のアイテムを共有しますか?
+
+&lt;nolink&gt;[ITEMS]&lt;/nolink&gt;
+
+次の住人と共有:
+
 [RESIDENTS]
 		<usetemplate name="okcancelbuttons" notext="取り消し" yestext="Ok"/>
 	</notification>
@@ -3021,7 +3033,7 @@ M キーを押して変更します。
 		カメラの視点を変更するには、水平・垂直コントロールを使います。Escape を押すか、または歩行すると、視点がリセットされます。
 	</notification>
 	<notification label="インベントリ" name="HintInventory">
-		持ち物にはあなたのアイテムがすべて含まれます。新しく追加されたアイテムは「最新」タブに一覧表示されています。
+		インベントリにはあなたのアイテムがすべて含まれます。新しく追加されたアイテムは「最新」タブに一覧表示されています。
 	</notification>
 	<notification label="あなたのリンデンドル" name="HintLindenDollar">
 		これがあなたの L$ 残高です。リンデンドルを購入するには「L$ の購入」をクリックします。
@@ -3129,7 +3141,7 @@ M キーを押して変更します。
 		<usetemplate name="okcancelbuttons" notext="終了しない" yestext="終了"/>
 	</notification>
 	<notification label="" name="NoInventory">
-		持ち物の表示はアドバンスモードでのみ利用できます。ログアウトしてモードを変更しますか?
+		インベントリの表示はアドバンスモードでのみ利用できます。ログアウトしてモードを変更しますか?
 		<usetemplate name="okcancelbuttons" notext="終了しない" yestext="終了"/>
 	</notification>
 	<notification label="" name="NoAppearance">
diff --git a/indra/newview/skins/default/xui/ja/panel_group_control_panel.xml b/indra/newview/skins/default/xui/ja/panel_group_control_panel.xml
index f7f575206abffaaf18d05558e0c81fb2a2d42e13..49749732c96eec279c8c310eb5ba1978359ba36a 100644
--- a/indra/newview/skins/default/xui/ja/panel_group_control_panel.xml
+++ b/indra/newview/skins/default/xui/ja/panel_group_control_panel.xml
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="panel_im_control_panel">
 	<layout_stack name="vertical_stack">
 		<layout_panel name="group_info_btn_panel">
-			<button label="グループ情報" name="group_info_btn"/>
+			<button label="グループプロフィール" name="group_info_btn"/>
 		</layout_panel>
 		<layout_panel name="call_btn_panel">
 			<button label="グループコール" name="call_btn"/>
diff --git a/indra/newview/skins/default/xui/ja/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/ja/panel_group_info_sidetray.xml
index 85406702bcc6337ceb87f4433b248e38da617e54..7aa1aec6d0e551bda507fb60cab710774d51fa3d 100644
--- a/indra/newview/skins/default/xui/ja/panel_group_info_sidetray.xml
+++ b/indra/newview/skins/default/xui/ja/panel_group_info_sidetray.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<panel label="グループ情報" name="GroupInfo">
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<panel label="グループインベントリ" name="GroupInfo">
 	<panel.string name="default_needs_apply_text">
 		保存していない変更があります
 	</panel.string>
diff --git a/indra/newview/skins/default/xui/ja/panel_group_notices.xml b/indra/newview/skins/default/xui/ja/panel_group_notices.xml
index 96e038297554dc76f92afcdead0de681a228e983..ddad6c1f9b024d13772b9d6199a7295c41a3b3c9 100644
--- a/indra/newview/skins/default/xui/ja/panel_group_notices.xml
+++ b/indra/newview/skins/default/xui/ja/panel_group_notices.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="通知" name="notices_tab">
 	<panel.string name="help_text">
 		通知でメッセージを送ることができ、通知にアイテムを添付することができます。
@@ -39,10 +39,10 @@
 		<text name="string">
 			ここにアイテムをドラッグ&ドロップして添付してください:
 		</text>
-		<button label="持ち物" name="open_inventory" tool_tip="持ち物を開きます"/>
+		<button label="インベントリ" name="open_inventory" tool_tip="インベントリを開きます"/>
 		<button label="取り外す" label_selected="添付物を削除" name="remove_attachment" tool_tip="あなたの通知から添付されたアイテムを削除します"/>
 		<button label="送信" label_selected="送信" name="send_notice"/>
-		<group_drop_target name="drop_target" tool_tip="持ち物のアイテムをこのボックスにドラッグして、通知と一緒に送ります。 添付するには、そのアイテムのコピーと再販・プレゼントの権限があなたにある必要があります。"/>
+		<group_drop_target name="drop_target" tool_tip="インベントリのアイテムをこのボックスにドラッグして、通知と一緒に送ります。 添付するには、そのアイテムのコピーと再販・プレゼントの権限があなたにある必要があります。"/>
 	</panel>
 	<panel label="過去の通知を表示" name="panel_view_past_notice">
 		<text name="lbl">
diff --git a/indra/newview/skins/default/xui/ja/panel_landmarks.xml b/indra/newview/skins/default/xui/ja/panel_landmarks.xml
index e3b716c35bf7c95d95b938e532620c15b0877371..24d6ff23aedeca8dc7e2c307321fad8827a7ae31 100644
--- a/indra/newview/skins/default/xui/ja/panel_landmarks.xml
+++ b/indra/newview/skins/default/xui/ja/panel_landmarks.xml
@@ -1,9 +1,9 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="Landmarks">
 	<accordion name="landmarks_accordion">
 		<accordion_tab name="tab_favorites" title="お気に入りバー"/>
 		<accordion_tab name="tab_landmarks" title="マイ ランドマーク"/>
-		<accordion_tab name="tab_inventory" title="持ち物"/>
+		<accordion_tab name="tab_inventory" title="インベントリ"/>
 		<accordion_tab name="tab_library" title="ライブラリ"/>
 	</accordion>
 	<panel name="bottom_panel">
diff --git a/indra/newview/skins/default/xui/ja/panel_main_inventory.xml b/indra/newview/skins/default/xui/ja/panel_main_inventory.xml
index ff968696b7bb61007a35cf612da9a03281afc66d..f908262f4f0ecfb670034b8fbfd300819d080edf 100644
--- a/indra/newview/skins/default/xui/ja/panel_main_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/panel_main_inventory.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="もの" name="main inventory panel">
 	<panel.string name="ItemcountFetching">
 		[ITEM_COUNT] 個のアイテムを取得中です... [FILTER]
@@ -9,9 +9,9 @@
 	<text name="ItemcountText">
 		アイテム:
 	</text>
-	<filter_editor label="持ち物をフィルター" name="inventory search editor"/>
+	<filter_editor label="インベントリをフィルター" name="inventory search editor"/>
 	<tab_container name="inventory filter tabs">
-		<inventory_panel label="持ち物" name="All Items"/>
+		<inventory_panel label="インベントリ" name="All Items"/>
 		<recent_inventory_panel label="最新" name="Recent Items"/>
 	</tab_container>
 	<layout_stack name="bottom_panel">
diff --git a/indra/newview/skins/default/xui/ja/panel_me.xml b/indra/newview/skins/default/xui/ja/panel_me.xml
index 896bbff0ee3abea6c5e007d7df179ff6bdc669c0..3df1ae804870c69914a595f59e64e364394e524e 100644
--- a/indra/newview/skins/default/xui/ja/panel_me.xml
+++ b/indra/newview/skins/default/xui/ja/panel_me.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="マイ プロフィール" name="panel_me">
-	<panel label="マイ ピック" name="panel_picks"/>
+	<panel label="マイ-ピック" name="panel_picks"/>
 </panel>
diff --git a/indra/newview/skins/default/xui/ja/panel_nearby_chat.xml b/indra/newview/skins/default/xui/ja/panel_nearby_chat.xml
index aca055bb438fc1614ee41a02c6f7bd55a823b4d1..4048b48d3a59922a01be80605874bb2754b4130d 100644
--- a/indra/newview/skins/default/xui/ja/panel_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/ja/panel_nearby_chat.xml
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="nearby_chat">
-	<check_box label="チャットを翻訳" name="translate_chat_checkbox"/>
+	<layout_stack name="stack">
+		<layout_panel name="translate_chat_checkbox_lp">
+			<check_box label="チャットを翻訳" name="translate_chat_checkbox"/>
+		</layout_panel>
+	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/ja/panel_outbox_inventory.xml b/indra/newview/skins/default/xui/ja/panel_outbox_inventory.xml
index 47f570ab86afe2a9a80f8cdd4ab2d3214749662f..1a142831134257c67698fb7ed4bb2b2a398389ca 100644
--- a/indra/newview/skins/default/xui/ja/panel_outbox_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/panel_outbox_inventory.xml
@@ -1,2 +1,2 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<outbox_inventory_panel name="inventory_outbox" tool_tip="商品をここにドラッグアンドドロップすると、あなたの店頭に並びます"/>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<outbox_inventory_panel name="inventory_outbox" tool_tip="アイテムをここにドラッグアンドドロップすると、あなたの店頭に並びます"/>
diff --git a/indra/newview/skins/default/xui/ja/panel_outfit_edit.xml b/indra/newview/skins/default/xui/ja/panel_outfit_edit.xml
index 6897660214750f703e0f8efd8feb7db2b59e1e4a..e89ce0c47931297eed6ab8ae512eb5b009cb9d1b 100644
--- a/indra/newview/skins/default/xui/ja/panel_outfit_edit.xml
+++ b/indra/newview/skins/default/xui/ja/panel_outfit_edit.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <!-- Side tray Outfit Edit panel -->
 <panel label="アウトフットの編集" name="outfit_edit">
 	<string name="No Outfit" value="アウトフィットなし"/>
@@ -30,7 +30,7 @@
 					<button label="さらに追加..." name="show_add_wearables_btn" tool_tip="開く/閉じる"/>
 				</layout_panel>
 				<layout_panel name="filter_panel">
-					<filter_editor label="持ち物の着用物をフィルター" name="look_item_filter"/>
+					<filter_editor label="インベントリの着用物をフィルター" name="look_item_filter"/>
 				</layout_panel>
 			</layout_stack>
 		</layout_panel>
diff --git a/indra/newview/skins/default/xui/ja/panel_outfits_inventory.xml b/indra/newview/skins/default/xui/ja/panel_outfits_inventory.xml
index 2a0647653de25b946161213ebe6838e627800f08..93df0ba2bd41de1c9a10d640ceca1d27fe691106 100644
--- a/indra/newview/skins/default/xui/ja/panel_outfits_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/panel_outfits_inventory.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="もの" name="Outfits">
 	<panel.string name="wear_outfit_tooltip">
 		選択したアウトフィットを着用する
 	</panel.string>
 	<panel.string name="wear_items_tooltip">
-		選択した商品を着用
+		選択したアイテムを着用
 	</panel.string>
 	<tab_container name="appearance_tabs">
 		<panel label="マイ アウトフィット" name="outfitslist_tab"/>
diff --git a/indra/newview/skins/default/xui/ja/panel_people.xml b/indra/newview/skins/default/xui/ja/panel_people.xml
index 1c90f7327eed51c75228c57a7d9594ac377363e1..88c31451b51cc1d6caf98795e094a9488f604285 100644
--- a/indra/newview/skins/default/xui/ja/panel_people.xml
+++ b/indra/newview/skins/default/xui/ja/panel_people.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <!-- Side tray panel -->
 <panel label="人" name="people_panel">
 	<string name="no_recent_people" value="最近交流した人はいません。 一緒に何かする仲間をお探しですか? [secondlife:///app/search/people 検索] か [secondlife:///app/worldmap 世界地図] をお試しください。"/>
@@ -73,7 +73,7 @@
 				<button label="コール" name="call_btn" tool_tip="この住人にコールする"/>
 			</layout_panel>
 			<layout_panel name="share_btn_lp">
-				<button label="共有" name="share_btn" tool_tip="「持ち物」のアイテムを共有する"/>
+				<button label="共有" name="share_btn" tool_tip="インベントリのアイテムを共有する"/>
 			</layout_panel>
 			<layout_panel name="teleport_btn_lp">
 				<button label="テレポート" name="teleport_btn" tool_tip="テレポートを送ります"/>
@@ -81,7 +81,7 @@
 		</layout_stack>
 		<layout_stack name="bottom_bar_ls1">
 			<layout_panel name="group_info_btn_lp">
-				<button label="グループ情報" name="group_info_btn" tool_tip="グループ情報を表示します"/>
+				<button label="グループプロフィール" name="group_info_btn" tool_tip="グループプロフィールを表示します"/>
 			</layout_panel>
 			<layout_panel name="chat_btn_lp">
 				<button label="グループチャット" name="chat_btn" tool_tip="チャットを開始します"/>
diff --git a/indra/newview/skins/default/xui/ja/panel_snapshot_inventory.xml b/indra/newview/skins/default/xui/ja/panel_snapshot_inventory.xml
index 46f2b0a3f99ea49212b29ba5d54fb3305fc83f49..e496e6602b1d2cf2a02e4e19f383c34caab8d9d4 100644
--- a/indra/newview/skins/default/xui/ja/panel_snapshot_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/panel_snapshot_inventory.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="panel_snapshot_inventory">
 	<text name="title">
-		「持ち物」に保存
+		インベントリに保存
 	</text>
 	<text name="hint_lbl">
-		画像を持ち物に保存するには L$[UPLOAD_COST] の費用がかかります。画像をテクスチャとして保存するには平方形式の 1 つを選択してください。
+		画像をインベントリに保存するには L$[UPLOAD_COST] の費用がかかります。画像をテクスチャとして保存するには平方形式の 1 つを選択してください。
 	</text>
 	<combo_box label="解像度" name="texture_size_combo">
 		<combo_box.item label="現在のウィンドウ" name="CurrentWindow"/>
diff --git a/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml b/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml
index cd5b7590ad8e680aed43e742091fb5baffd8521e..c3b1cd91e7349dca8fdb254755f30ba00bdacb9e 100644
--- a/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml
+++ b/indra/newview/skins/default/xui/ja/panel_snapshot_options.xml
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="panel_snapshot_options">
 	<button label="プロフィールフィードに投稿する" name="save_to_profile_btn"/>
 	<button label="メール" name="save_to_email_btn"/>
-	<button label="持ち物に保存(L$[Amount])" name="save_to_inventory_btn"/>
+	<button label="インベントリに保存(L$[Amount])" name="save_to_inventory_btn"/>
 	<button label="コンピューターに保存" name="save_to_computer_btn"/>
 </panel>
diff --git a/indra/newview/skins/default/xui/ja/panel_status_bar.xml b/indra/newview/skins/default/xui/ja/panel_status_bar.xml
index 4fb876f690bd95fa00360b6b516d32fcbdc3e1a1..f09643d56223817b68da9a208b260e7d6dd2b830 100644
--- a/indra/newview/skins/default/xui/ja/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/ja/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		L$ [AMT]
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-370" name="balance_bg" width="160">
 		<text name="balance" tool_tip="クリックして L$ 残高を更新" value="L$20"/>
 		<button label="L$ の購入" name="buyL" tool_tip="クリックして L$ を購入します"/>
 		<button label="店" name="goShop" tool_tip="Second Life マーケットプレイスを開く"  width="40"/>
diff --git a/indra/newview/skins/default/xui/ja/role_actions.xml b/indra/newview/skins/default/xui/ja/role_actions.xml
index c53ad838f74390fdce9c77c8bdca4fc57d12c1d7..896ed556ae4de38d4515b216837dec531081fdd8 100644
--- a/indra/newview/skins/default/xui/ja/role_actions.xml
+++ b/indra/newview/skins/default/xui/ja/role_actions.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <role_actions>
 	<action_set description="これらの能力には、グループメンバーを追加、排除し、招待状なしに新メンバーの参加を認める権限が含まれます。" name="Membership">
 		<action description="このグループに人を招待" longdescription="「役割」セクションの「メンバー」タブ内にある「招待」ボタンを押して、このグループにメンバーを招待します。" name="member invite" value="1"/>
@@ -51,7 +51,7 @@
 		<action description="グループ所有オブジェクトの返却" longdescription="グループ所有の区画上のオブジェクトのうち、グループ所有のオブジェクトを返却するには、「土地情報」>「オブジェクト」タブを使います。" name="land return group owned" value="48"/>
 		<action description="グループに設定されているオブジェクトを返却" longdescription="グループ所有の区画上のオブジェクトのうち、グループに設定されているオブジェクトを返却するには、「土地情報」>「オブジェクト」タブを使います。" name="land return group set" value="33"/>
 		<action description="非グループオブジェクトの返却" longdescription="グループ所有の区画上のオブジェクトのうち、グループ以外のオブジェクトを返却するには、「土地情報」>「オブジェクト」タブを使います。" name="land return non group" value="34"/>
-		<action description="リンデン製の植物を使用して景観作成" longdescription="リンデン製の樹木、植物、草を植える、景観づくりの能力です。 これらの植物はあなたの持ち物内の「ライブラリ」 &gt; 「オブジェクト」フォルダにあります。「制作」メニューで作成することもできます。" name="land gardening" value="35"/>
+		<action description="リンデン製の植物を使用して景観作成" longdescription="リンデン製の樹木、植物、草を植える、景観づくりの能力です。 これらの植物はあなたのインベントリ内の「ライブラリ」 &gt; 「オブジェクト」フォルダにあります。「制作」メニューで作成することもできます。" name="land gardening" value="35"/>
 	</action_set>
 	<action_set description="これらの「能力」には、グループ所有のオブジェクトを譲渡、修正、販売する権限が含まれます。 変更は「制作ツール」 &gt; 「一般」タブで行います。 オブジェクトを右クリックして「編集」を開くと設定内容を確認できます。" name="Object Management">
 		<action description="グループにオブジェクトを譲渡" longdescription="「制作ツール」 &gt; 「一般」タブで、オブジェクトをグループに譲渡します。" name="object deed" value="36"/>
diff --git a/indra/newview/skins/default/xui/ja/sidepanel_inventory.xml b/indra/newview/skins/default/xui/ja/sidepanel_inventory.xml
index a450d9b3c35d380eebb7020bff24a20993bc97bd..51d6d48f90aad1a93597759a8ab6a3f2174d2481 100644
--- a/indra/newview/skins/default/xui/ja/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/ja/sidepanel_inventory.xml
@@ -14,7 +14,7 @@
 					<text name="inbox_fresh_new_count">
 						[NUM] 個の新アイテム
 					</text>
-					<panel tool_tip="Drag and drop items to your inventory to manage and use them">
+					<panel name="inbox_inventory_placeholder_panel" tool_tip="アイテムを使用するには、そのアイテムをインベントリにドラッグアンドドロップ">
 						<text name="inbox_inventory_placeholder">
 							マーケットプレイスから購入した商品はここに配達されます。
 						</text>
@@ -28,7 +28,7 @@
 					<button label="プロフィール" name="info_btn" tool_tip="オブジェクトのプロフィールを表示する"/>
 				</layout_panel>
 				<layout_panel name="share_btn_lp">
-					<button label="共有" name="share_btn" tool_tip="「持ち物」のアイテムを共有する"/>
+					<button label="共有" name="share_btn" tool_tip="インベントリのアイテムを共有する"/>
 				</layout_panel>
 				<layout_panel name="shop_btn_lp">
 					<button label="店" name="shop_btn" tool_tip="マーケットプレイスのサイトを開く"/>
diff --git a/indra/newview/skins/default/xui/ja/sidepanel_item_info.xml b/indra/newview/skins/default/xui/ja/sidepanel_item_info.xml
index d820994b59029cfa67e344219c757fe344e775b8..6931e448b312c26218ea8ebfb24059c8822be5cf 100644
--- a/indra/newview/skins/default/xui/ja/sidepanel_item_info.xml
+++ b/indra/newview/skins/default/xui/ja/sidepanel_item_info.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="item properties" title="アイテムのプロフィール">
 	<panel.string name="unknown">
 		(不明)
@@ -16,13 +16,13 @@
 		[year,datetime,local] [mth,datetime,local] [day,datetime,local] [wkday,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]
 	</panel.string>
 	<panel.string name="origin_inventory">
-		(持ち物)
+		(インベントリ)
 	</panel.string>
 	<panel.string name="origin_inworld">
 		(インワールド)
 	</panel.string>
 	<text name="title" value="アイテムのプロフィール"/>
-	<text name="origin" value="(持ち物)"/>
+	<text name="origin" value="(インベントリ)"/>
 	<scroll_container name="item_profile_scroll">
 		<panel label="" name="item_profile">
 			<text name="LabelItemNameTitle">
diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml
index a1279510c7791ad33f333ad50fba8c88878a9030..680ef608909e84b8687a3396d92aad455f44c72f 100644
--- a/indra/newview/skins/default/xui/ja/strings.xml
+++ b/indra/newview/skins/default/xui/ja/strings.xml
@@ -346,7 +346,7 @@ support@secondlife.com にお問い合わせください。
 		これらオブジェクトの 1 つまたは複数は売り渡したり譲渡したりできないものです。
 	</string>
 	<string name="TooltipOutboxNotInInventory">
-		マーチャントアウトボックスでは、ご自分の持ち物からのアイテムしか受け入れることができません
+		マーチャントアウトボックスでは、ご自分のインベントリからのアイテムしか受け入れることができません
 	</string>
 	<string name="TooltipOutboxWorn">
 		着用しているアイテムをマーチャントアウトボックスに入れることはできません
@@ -835,6 +835,9 @@ support@secondlife.com にお問い合わせください。
 	<string name="anim_yes_head">
 		頷く
 	</string>
+	<string name="multiple_textures">
+		複数
+	</string>
 	<string name="texture_loading">
 		ローディング...
 	</string>
@@ -1232,10 +1235,10 @@ support@secondlife.com にお問い合わせください。
 		ここにランドマークをドラッグしてお気に入りに追加します。
 	</string>
 	<string name="InventoryNoTexture">
-		「持ち物」内にこのテクスチャのコピーがありません
+		インベントリ内にこのテクスチャのコピーがありません
 	</string>
 	<string name="InventoryInboxNoItems">
-		プレミアムギフトなど、受け取る特定のアイテムはここに表示されます。その後、それらのアイテムを自分の持ち物の中にドラッグできます。
+		マーケットプレイスで購入した商品はここに表示されます。その後、アイテムをインベントリにドラッグすれば、それらのアイテムを使用できます。
 	</string>
 	<string name="MarketplaceURL">
 		https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
@@ -1270,25 +1273,25 @@ support@secondlife.com にお問い合わせください。
 		エラーなし
 	</string>
 	<string name="Marketplace Error Not Merchant">
-		エラー:マーケットプレイスに商品を送る前に、あなた自身をマーチャント登録する必要があります(登録は無料です)。
+		エラー:マーケットプレイスにアイテムを送る前に、あなた自身をマーチャント登録する必要があります(登録は無料です)。
 	</string>
 	<string name="Marketplace Error Empty Folder">
 		エラー:このフォルダは空です。
 	</string>
 	<string name="Marketplace Error Unassociated Products">
-		エラー:あなたのマーチャントアカウントには、商品と無関係のアイテムが多すぎるため、このアイテムをアップロードできませんでした。このエラーを解消するには、マーケットプレイスのウェブサイトにログインし、商品に関係のないアイテムの数を減らしてください。
+		エラー:あなたのマーチャントアカウントには、商品に関連付けられていないアイテムが多すぎるため、このアイテムをアップロードできませんでした。このエラーを解消するには、マーケットプレイスの Web サイトにログインし、関連付けられていないアイテムの数を減らしてください。
 	</string>
 	<string name="Marketplace Error Object Limit">
-		エラー:この商品に含まれるオブジェクトが多すぎます。オブジェクトをいくつかボックスにまとめ、オブジェクト数を200以下に減らしてください。
+		エラー:このアイテムに含まれるオブジェクトが多すぎます。オブジェクトをいくつかボックスにまとめ、オブジェクト数を200以下に減らしてください。
 	</string>
 	<string name="Marketplace Error Folder Depth">
-		エラー:この商品はネスト入りフォルダの階層が多すぎます。ネスト入りフォルダを 3 階層以内にまとめ直してください。
+		エラー:このアイテムはネスト入りフォルダの階層が多すぎます。ネスト入りフォルダを 3 階層以内にまとめ直してください。
 	</string>
 	<string name="Marketplace Error Unsellable Item">
-		エラー:この項目をマーケットプレイスで販売することはできません。
+		エラー:このアイテムをマーケットプレイスで販売することはできません。
 	</string>
 	<string name="Marketplace Error Internal Import">
-		エラー:この商品に関して問題が発生しました。しばらくしてからお試しください。
+		エラー:このアイテムに関して問題が発生しました。しばらくしてからお試しください。
 	</string>
 	<string name="Open landmarks">
 		ランドマークを開く
@@ -1337,7 +1340,7 @@ support@secondlife.com にお問い合わせください。
 	<string name="No Filters" value="いいえ "/>
 	<string name="Since Logoff" value=" - ログオフ以来"/>
 	<string name="InvFolder My Inventory">
-		持ち物
+		インベントリ
 	</string>
 	<string name="InvFolder Library">
 		ライブラリ
@@ -1370,7 +1373,7 @@ support@secondlife.com にお問い合わせください。
 		新規フォルダ
 	</string>
 	<string name="InvFolder Inventory">
-		持ち物
+		インベントリ
 	</string>
 	<string name="InvFolder Uncompressed Images">
 		圧縮されていない画像
@@ -3921,6 +3924,9 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ
 	<string name="Saved_message">
 		(保存日時:[LONG_TIMESTAMP])
 	</string>
+	<string name="IM_unblock_only_groups_friends">
+		このメッセージを表示するには、「環境設定」の「プライバシー」で「フレンドとグループ以外からはコールと IM を受信しない」チェックボックスをオフにします。
+	</string>
 	<string name="answered_call">
 		相手がコールを受けました
 	</string>
@@ -3952,10 +3958,10 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ
 		[AGENT_NAME] とコンファレンスする
 	</string>
 	<string name="inventory_item_offered-im">
-		持ち物アイテムを送りました
+		インベントリアイテムを送りました
 	</string>
 	<string name="share_alert">
-		持ち物からここにアイテムをドラッグします
+		インベントリからここにアイテムをドラッグします
 	</string>
 	<string name="no_session_message">
 		(IM セッションが存在しません)
@@ -4839,7 +4845,7 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ
 		ハウツー
 	</string>
 	<string name="Command_Inventory_Label">
-		持ち物
+		インベントリ
 	</string>
 	<string name="Command_Map_Label">
 		地図
@@ -4914,7 +4920,7 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ
 		一般的タスクの実行方法
 	</string>
 	<string name="Command_Inventory_Tooltip">
-		持ち物を表示・使用
+		インベントリを表示・使用
 	</string>
 	<string name="Command_Map_Tooltip">
 		世界地図
diff --git a/indra/newview/skins/default/xui/ja/teleport_strings.xml b/indra/newview/skins/default/xui/ja/teleport_strings.xml
index 6c69c7a2313b62b9b49ea06d3ae2a4b02358770a..64f01f4030cde9c26968a5bc6a7eab56708cca03 100644
--- a/indra/newview/skins/default/xui/ja/teleport_strings.xml
+++ b/indra/newview/skins/default/xui/ja/teleport_strings.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <teleport_messages>
 	<message_set name="errors">
 		<message name="invalid_tport">
@@ -45,7 +45,7 @@
 			テレポートの目的地を見つけられません。目的地が一時的に利用できない状態か、またはすでに消滅している可能性があります。数分後にやり直してください。
 		</message>
 		<message name="no_inventory_host">
-			持ち物システムは現在利用できません。
+			インベントリシステムは現在利用できません。
 		</message>
 	</message_set>
 	<message_set name="progress">
diff --git a/indra/newview/skins/default/xui/pt/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/pt/floater_animation_anim_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..05326d85948bf96fba298ce69d7bc50f6a59b5ee
--- /dev/null
+++ b/indra/newview/skins/default/xui/pt/floater_animation_anim_preview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Anim Preview" title="ANIMATION.ANIM">
+	<text name="name_label">
+		Nome:
+	</text>
+	<text name="description_label">
+		Descrição:
+	</text>
+	<button label="Envio (L$[AMOUNT])" name="ok_btn"/>
+	<button label="Cancelar" label_selected="Cancelar" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/pt/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/pt/floater_animation_bvh_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5cae581045eb6dd0be3446da7a41ecdc231c1eac
--- /dev/null
+++ b/indra/newview/skins/default/xui/pt/floater_animation_bvh_preview.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Animation Preview">
+	<floater.string name="failed_to_initialize">
+		Falha ao iniciar animação
+	</floater.string>
+	<floater.string name="anim_too_long">
+		O arquivo de animação possui [LENGTH] segundos de duração.
+
+A duração máxima da animação é de [MAX_LENGTH] segundos.
+	</floater.string>
+	<floater.string name="failed_file_read">
+		Não foi possível ler o arquivo de animação.
+
+[STATUS]
+	</floater.string>
+	<floater.string name="E_ST_OK">
+		Ok
+	</floater.string>
+	<floater.string name="E_ST_EOF">
+		Fim de arquivo prematuro.
+	</floater.string>
+	<floater.string name="E_ST_NO_CONSTRAINT">
+		Impossível ler definição constraint.
+	</floater.string>
+	<floater.string name="E_ST_NO_FILE">
+		Impossível abrir arquivo BVH.
+	</floater.string>
+	<floater.string name="E_ST_NO_HIER">
+		Invalid HIERARCHY header.
+	</floater.string>
+	<floater.string name="E_ST_NO_JOINT">
+		RAIZ ou JUNTA não encontrados.
+	</floater.string>
+	<floater.string name="E_ST_NO_NAME">
+		Impossível obter nome JOINT.
+	</floater.string>
+	<floater.string name="E_ST_NO_OFFSET">
+		Impossível localizar OFFSET.
+	</floater.string>
+	<floater.string name="E_ST_NO_CHANNELS">
+		Impossível localizar CHANNELS.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROTATION">
+		Impossível obter ordem de rotação.
+	</floater.string>
+	<floater.string name="E_ST_NO_AXIS">
+		Impossível obter eixo de rotação.
+	</floater.string>
+	<floater.string name="E_ST_NO_MOTION">
+		Impossível localizar MOTION.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAMES">
+		Impossível determinar número de quadros.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAME_TIME">
+		Impossível determinar tempo dos quadros.
+	</floater.string>
+	<floater.string name="E_ST_NO_POS">
+		Impossível definir posicionamento.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROT">
+		Impossível definir valores da rotação.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_FILE">
+		Impossível abrir arquivo de tradução.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HEADER">
+		Impossível ler cabeçalho de tradução.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_NAME">
+		Impossível ler nomes traduzidos.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_IGNORE">
+		Impossível obter valor traduzido a ignorar.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_RELATIVE">
+		Impossível obter valor traduzido relativo.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_OUTNAME">
+		Impossível obter valor traduzido.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MATRIX">
+		Impossível ler matriz de tradução.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGECHILD">
+		Impossível obter nome mergechild.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGEPARENT">
+		Impossível obter nome mergeparent.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_PRIORITY">
+		Impossível obter valor prioritário.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_LOOP">
+		Impossível obter valor do loop.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEIN">
+		Impossível obter valor easeIn.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEOUT">
+		Impossível obter valor easeOut.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HAND">
+		Impossível obter valor de morph da mão.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EMOTE">
+		Impossível ler nome do emote.
+	</floater.string>
+	<floater.string name="E_ST_BAD_ROOT">
+		Nome da junta incorreto, use &quot;quadril&quot;.
+	</floater.string>
+	<text name="name_label">
+		Nome:
+	</text>
+	<text name="description_label">
+		Descrição:
+	</text>
+	<spinner label="Prioridade" name="priority" tool_tip="Controla quais animações podem ser interrompidas por esta animação"/>
+	<check_box label="Loop" name="loop_check" tool_tip="Executa esta animação sem parar"/>
+	<spinner label="Dentro(%)" name="loop_in_point" tool_tip="Define o ponto em que a animação em loop reinicia"/>
+	<spinner label="Fora(%)" name="loop_out_point" tool_tip="Define o ponto em que a animação em loop acaba"/>
+	<text name="hand_label">
+		Pose das mãos
+	</text>
+	<combo_box name="hand_pose_combo" tool_tip="Controla os gestos das mãos durante a animação">
+		<combo_box.item label="Abrir" name="Spread"/>
+		<combo_box.item label="Relaxado" name="Relaxed"/>
+		<combo_box.item label="Apontar ambas" name="PointBoth"/>
+		<combo_box.item label="Punho" name="Fist"/>
+		<combo_box.item label="E relaxada" name="RelaxedLeft"/>
+		<combo_box.item label="Apontar E" name="PointLeft"/>
+		<combo_box.item label="Punho E" name="FistLeft"/>
+		<combo_box.item label="D relaxada" name="RelaxedRight"/>
+		<combo_box.item label="Apontar D" name="PointRight"/>
+		<combo_box.item label="Punho D" name="FistRight"/>
+		<combo_box.item label="Saudação D" name="SaluteRight"/>
+		<combo_box.item label="Escrevendo" name="Typing"/>
+		<combo_box.item label="Paz D" name="PeaceRight"/>
+	</combo_box>
+	<text name="emote_label">
+		Expressão
+	</text>
+	<combo_box name="emote_combo" tool_tip="Controla as expressões faciais durante a animação">
+		<item label="(nenhum)" name="[None]" value=""/>
+		<item label="Aaaaah" name="Aaaaah" value="Aaaaah"/>
+		<item label="Com medo" name="Afraid" value="Com medo"/>
+		<item label="Bravo" name="Angry" value="Bravo"/>
+		<item label="Sorriso contagiante" name="BigSmile" value="Sorriso contagiante"/>
+		<item label="À toa" name="Bored" value="À toa"/>
+		<item label="Chorar" name="Cry" value="Chorar"/>
+		<item label="Desdenho" name="Disdain" value="Desdenho"/>
+		<item label="Com vergonha" name="Embarrassed" value="Com vergonha"/>
+		<item label="Franzir testa" name="Frown" value="Franzir testa"/>
+		<item label="Beijo" name="Kiss" value="Beijo"/>
+		<item label="Rir" name="Laugh" value="Rir"/>
+		<item label="Mostrar a língua" name="Plllppt" value="Mostrar a língua"/>
+		<item label="Asco" name="Repulsed" value="Asco"/>
+		<item label="Triste" name="Sad" value="Triste"/>
+		<item label="Encolher os ombros" name="Shrug" value="Encolher os ombros"/>
+		<item label="Sorriso" name="Smile" value="Sorriso"/>
+		<item label="Surpresa" name="Surprise" value="Surpresa"/>
+		<item label="Piscar" name="Wink" value="Piscar"/>
+		<item label="Preocupado" name="Worry" value="Preocupado"/>
+	</combo_box>
+	<text name="preview_label">
+		Visualizar enquanto
+	</text>
+	<combo_box name="preview_base_anim" tool_tip="Use para testar o comportamento de sua animação enquanto seu avatar executa ações comuns.">
+		<item label="Em pé" name="Standing" value="Em pé"/>
+		<item label="Andar" name="Walking" value="Andar"/>
+		<item label="Sentado" name="Sitting" value="Sentado"/>
+		<item label="Voar" name="Flying" value="Voar"/>
+	</combo_box>
+	<spinner label="Aproximação (seg)" name="ease_in_time" tool_tip="Tempo (em segundos) da transição inicial da animação"/>
+	<spinner label="Afastamento (seg)" name="ease_out_time" tool_tip="Tempo (em segundos) da transição de saída da animação"/>
+	<button name="play_btn" tool_tip="Executar animação"/>
+	<button name="pause_btn" tool_tip="Pausar a animação"/>
+	<button name="stop_btn" tool_tip="Interromper a execução da animação"/>
+	<text name="bad_animation_text">
+		Não foi possível ler o arquivo de animação.
+
+Recomendamos que os arquivos BVH sejam exportados do Poser 4.
+	</text>
+	<button label="Envio (L$[AMOUNT])" name="ok_btn"/>
+	<button label="Cancelar" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/pt/floater_preview_animation.xml b/indra/newview/skins/default/xui/pt/floater_preview_animation.xml
index b650b7945c3764e30ea55a670583f7d74ab4cfba..19474d80996288a2dd7bc323a89428be56344b0a 100644
--- a/indra/newview/skins/default/xui/pt/floater_preview_animation.xml
+++ b/indra/newview/skins/default/xui/pt/floater_preview_animation.xml
@@ -6,6 +6,6 @@
 	<text name="desc txt">
 		Descrição:
 	</text>
-	<button label="Tocar inworld" label_selected="Parar" name="Anim play btn" tool_tip="Tocar essa animação de forma que outros possam ver" width="131"/>
-	<button label="Executar localmente" label_selected="Parar" left="162" name="Anim audition btn" tool_tip="Tocar essa animação de forma que apenas você possa ver" width="120"/>
+	<button label="Tocar inworld" label_selected="Parar" name="Inworld" tool_tip="Executar essa animação de forma que outros possam ver"/>
+	<button label="Tocar localmente" label_selected="Parar" name="Locally" tool_tip="Executar animação de forma que apenas você possa ver"/>
 </floater>
diff --git a/indra/newview/skins/default/xui/pt/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/pt/floater_test_text_vertical_aligment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dd282bf1fce726874ada239d75b48eda59c1b07a
--- /dev/null
+++ b/indra/newview/skins/default/xui/pt/floater_test_text_vertical_aligment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Test Floater" title="JANELA DE TESTE"/>
diff --git a/indra/newview/skins/default/xui/pt/floater_tools.xml b/indra/newview/skins/default/xui/pt/floater_tools.xml
index f35f31f5f7e067996887a72ec1865372a4287a02..4b318336021dba31ac55a5f5eb05f935131dc19c 100644
--- a/indra/newview/skins/default/xui/pt/floater_tools.xml
+++ b/indra/newview/skins/default/xui/pt/floater_tools.xml
@@ -1,5 +1,20 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="toolbox floater" short_title="BUILD TOOLS" title="">
+	<floater.string name="grid_screen_text">
+		Tela
+	</floater.string>
+	<floater.string name="grid_local_text">
+		Local
+	</floater.string>
+	<floater.string name="grid_world_text">
+		Mundo
+	</floater.string>
+	<floater.string name="grid_reference_text">
+		Referência
+	</floater.string>
+	<floater.string name="grid_attachment_text">
+		Anexo
+	</floater.string>
 	<floater.string name="status_rotate">
 		Arrastar as faixas coloridas para girar o objeto
 	</floater.string>
@@ -63,7 +78,12 @@
 	</text>
 	<check_box initial_value="true" label="Esticar texturas" name="checkbox stretch textures"/>
 	<check_box initial_value="true" label="Ajustar" name="checkbox snap to grid"/>
-	<button label="Opções..." label_selected="Opções..." name="Options..." tool_tip="Mais opções de grade"/>
+	<combo_box name="combobox grid mode" tool_tip="Selecione o tipo de régua da grade onde o objeto será colocado">
+		<combo_box.item label="Mundo" name="World"/>
+		<combo_box.item label="Local" name="Local"/>
+		<combo_box.item label="Referência" name="Reference"/>
+	</combo_box>
+	<button label="" label_selected="Opções..." name="Options..." tool_tip="Mais opções de grade"/>
 	<button label="" label_selected="" name="ToolCube" tool_tip="Cubo"/>
 	<button label="" label_selected="" name="ToolPrism" tool_tip="Prisma"/>
 	<button label="" label_selected="" name="ToolPyramid" tool_tip="Pirâmide"/>
diff --git a/indra/newview/skins/default/xui/pt/floater_voice_effect.xml b/indra/newview/skins/default/xui/pt/floater_voice_effect.xml
index 4f01600d9fce4f8d65c8afb6f469407776baa9cc..b29ca3d699fff88bb9221975865bcf138fce9a10 100644
--- a/indra/newview/skins/default/xui/pt/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/pt/floater_voice_effect.xml
@@ -42,13 +42,16 @@
 	<string name="effect_Demon">
 		Demônio
 	</string>
+	<string name="effect_Female Elf">
+		Elfa
+	</string>
 	<string name="effect_Flirty">
 		Paquerador
 	</string>
 	<string name="effect_Foxy">
 		Sensual
 	</string>
-	<string name="effect_Halloween_2010_Bonus">
+	<string name="effect_Halloween 2010 Bonus">
 		Bônus_Halloween_2010
 	</string>
 	<string name="effect_Helium">
@@ -57,9 +60,18 @@
 	<string name="effect_Husky">
 		Rouco
 	</string>
+	<string name="effect_Husky Whisper">
+		Sussurro rouco
+	</string>
 	<string name="effect_Intercom">
 		Interfone
 	</string>
+	<string name="effect_Julia">
+		Julia
+	</string>
+	<string name="effect_Lo Lilt">
+		Cantarolado baixo
+	</string>
 	<string name="effect_Macho">
 		Macho
 	</string>
@@ -69,6 +81,9 @@
 	<string name="effect_Mini">
 		Mini
 	</string>
+	<string name="effect_Model">
+		Modelo
+	</string>
 	<string name="effect_Nano">
 		Nano
 	</string>
@@ -90,6 +105,9 @@
 	<string name="effect_Roxanne">
 		Roxanne
 	</string>
+	<string name="effect_Rumble">
+		Ronco
+	</string>
 	<string name="effect_Sabrina">
 		Sabrina
 	</string>
@@ -102,6 +120,9 @@
 	<string name="effect_Shorty">
 		Baixinho
 	</string>
+	<string name="effect_Smaller">
+		Menor
+	</string>
 	<string name="effect_Sneaky">
 		Sorrateiro
 	</string>
diff --git a/indra/newview/skins/default/xui/pt/menu_inventory.xml b/indra/newview/skins/default/xui/pt/menu_inventory.xml
index 24a2f713fdf1762c0fe50e0f841f22657191e99c..09e1fbf72ea8d5dc97c8dae88fff93f40f0446d5 100644
--- a/indra/newview/skins/default/xui/pt/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/pt/menu_inventory.xml
@@ -59,6 +59,7 @@
 	<menu_item_call label="Propriedades" name="Properties"/>
 	<menu_item_call label="Renomear" name="Rename"/>
 	<menu_item_call label="Copiar item UUID" name="Copy Asset UUID"/>
+	<menu_item_call label="Cortar" name="Cut"/>
 	<menu_item_call label="Copiar" name="Copy"/>
 	<menu_item_call label="Colar" name="Paste"/>
 	<menu_item_call label="Colar como link" name="Paste As Link"/>
diff --git a/indra/newview/skins/default/xui/pt/menu_viewer.xml b/indra/newview/skins/default/xui/pt/menu_viewer.xml
index 8960ffec8135f52ed4d9e7ffacd1f77cedd36c53..d7d5d59a3326141ac91934ccbbac9fe1cc45c14c 100644
--- a/indra/newview/skins/default/xui/pt/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/pt/menu_viewer.xml
@@ -21,6 +21,7 @@
 			<menu_item_call label="Ocupado" name="Set Busy"/>
 		</menu>
 		<menu_item_call label="Comprar L$..." name="Buy and Sell L$"/>
+		<menu_item_call label="Caixa de saída do lojista..." name="MerchantOutbox"/>
 		<menu_item_call label="Painel da conta..." name="Manage My Account">
 			<menu_item_call.on_click name="ManageMyAccount_url" parameter="WebLaunchJoinNow,http://secondlife.com/account/index.php?lang=pt"/>
 		</menu_item_call>
@@ -334,8 +335,15 @@
 		</menu>
 		<menu_item_check label="Texturas HTTP" name="HTTP Textures"/>
 		<menu_item_check label="Inventário HTTP" name="HTTP Inventory"/>
+		<menu_item_call label="Habilitar Visual Leak Detector" name="Enable Visual Leak Detector"/>
 		<menu_item_check label="Console Window on next Run" name="Console Window"/>
-		<menu label="Configurar nível de registro em log" name="Set Logging Level"/>
+		<menu label="Configurar nível de registro em log" name="Set Logging Level">
+			<menu_item_check label="Depurar" name="Debug"/>
+			<menu_item_check label="Info" name="Info"/>
+			<menu_item_check label="Aviso" name="Warning"/>
+			<menu_item_check label="Error" name="Error"/>
+			<menu_item_check label="Nenhum" name="None"/>
+		</menu>
 		<menu_item_call label="Request Admin Status" name="Request Admin Options"/>
 		<menu_item_call label="Sair do modo admin" name="Leave Admin Options"/>
 		<menu_item_check label="Mostrar menu admin" name="View Admin Options"/>
diff --git a/indra/newview/skins/default/xui/pt/notifications.xml b/indra/newview/skins/default/xui/pt/notifications.xml
index 20d59aa0f8569c5736ad6c2b9ba0dfd4f1901eae..3b39c0f92cb1948b836a7a29e48e1ff686958d66 100644
--- a/indra/newview/skins/default/xui/pt/notifications.xml
+++ b/indra/newview/skins/default/xui/pt/notifications.xml
@@ -664,7 +664,7 @@ Esperada [VALIDS]
 		Incapaz de criar arquivo de saída: [FILE]
 	</notification>
 	<notification name="DoNotSupportBulkAnimationUpload">
-		O [APP_NAME] ainda não faz o upload de vários arquivos de animação de uma vez.
+		O [APP_NAME] ainda não faz o upload de vários arquivos de animação no formato BVH de uma vez.
 	</notification>
 	<notification name="CannotUploadReason">
 		Incapaz de carregar [FILE] devido ao seguinte motivo: [REASON]
@@ -2612,16 +2612,16 @@ Deseja aceitar?
 		&apos;&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos; de [NAME]
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Bloquear"/>
-			<button name="Ignore" text="Ignorar"/>
+			<button name="Client_Side_Mute" text="Bloquear"/>
+			<button name="Client_Side_Ignore" text="Ignorar"/>
 		</form>
 	</notification>
 	<notification name="ScriptDialogGroup">
 		&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos; de [GROUPNAME]&apos;
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Bloquear"/>
-			<button name="Ignore" text="Ignorar"/>
+			<button name="Client_Side_Mute" text="Bloquear"/>
+			<button name="Client_Side_Ignore" text="Ignorar"/>
 		</form>
 	</notification>
 	<notification name="BuyLindenDollarSuccess">
diff --git a/indra/newview/skins/default/xui/pt/panel_nearby_chat.xml b/indra/newview/skins/default/xui/pt/panel_nearby_chat.xml
index 15470dc94a182224a2f71ee6d7a7f192ec54fbc3..6828d41deeb583a182512e65a92638a1644f3760 100644
--- a/indra/newview/skins/default/xui/pt/panel_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/pt/panel_nearby_chat.xml
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="nearby_chat">
-	<check_box label="Traduzir bate-papo" name="translate_chat_checkbox"/>
+	<layout_stack name="stack">
+		<layout_panel name="translate_chat_checkbox_lp">
+			<check_box label="Traduzir bate-papo" name="translate_chat_checkbox"/>
+		</layout_panel>
+	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/pt/panel_status_bar.xml b/indra/newview/skins/default/xui/pt/panel_status_bar.xml
index 22853f0643cd32d1deaa7e9ccaaf52530f54e07f..cb9a6eb757629b22f5b0e4fc9a3ed9dcaaa88638 100644
--- a/indra/newview/skins/default/xui/pt/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/pt/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		L$ [AMT]
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-410" name="balance_bg" width="200">
 		<text name="balance" tool_tip="Atualizar saldo de L$" value="L$20"/>
 		<button label="Comprar L$" name="buyL" tool_tip="Comprar mais L$"/>
 		<button label="Comprar" name="goShop" tool_tip="Abrir Mercado do Second Life" width="80"/>
diff --git a/indra/newview/skins/default/xui/pt/sidepanel_inventory.xml b/indra/newview/skins/default/xui/pt/sidepanel_inventory.xml
index 72baf3a5c3497f454eb8fa4fa508348222d57372..7908ea5f3a44430d3064d71d52c781766c61ed53 100644
--- a/indra/newview/skins/default/xui/pt/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/pt/sidepanel_inventory.xml
@@ -14,9 +14,9 @@
 					<text name="inbox_fresh_new_count">
 						[NUM] novo(s)
 					</text>
-					<panel tool_tip="Drag and drop items to your inventory to manage and use them">
+					<panel name="inbox_inventory_placeholder_panel" tool_tip="Arraste e solte itens para o seu inventário para usá-los">
 						<text name="inbox_inventory_placeholder">
-							Compras do marketplace serão entregues aqui.
+							As compras do marketplace serão entregues aqui.
 						</text>
 					</panel>
 				</panel>
diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml
index 7fb3b3e6ee5b3b9cbfee9e0c841de62a78802e26..342a52356b95e8360578272e0f1a039da7a481e5 100644
--- a/indra/newview/skins/default/xui/pt/strings.xml
+++ b/indra/newview/skins/default/xui/pt/strings.xml
@@ -784,6 +784,9 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para
 	<string name="anim_yes_head">
 		Sim
 	</string>
+	<string name="multiple_textures">
+		Múltiplo
+	</string>
 	<string name="texture_loading">
 		Carregando...
 	</string>
@@ -1181,7 +1184,7 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para
 		Você não possui uma cópia desta textura no seu inventário
 	</string>
 	<string name="InventoryInboxNoItems">
-		Alguns itens recebidos, como os brindes premium, aparecerão aqui.  Você pode arrastá-los para o seu inventário.
+		Suas compras do Marketplace aparecerão aqui. Depois, você poderá arrastá-las para seu inventário para usá-las.
 	</string>
 	<string name="MarketplaceURL">
 		https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
@@ -3797,6 +3800,9 @@ If you continue to receive this message, contact the [SUPPORT_SITE].
 	<string name="Saved_message">
 		(Salvo em [LONG_TIMESTAMP])
 	</string>
+	<string name="IM_unblock_only_groups_friends">
+		Para visualizar esta mensagem, você deve desmarcar &quot;Apenas amigos e grupos podem me ligar ou enviar MIs&quot; em Preferências/Privacidade.
+	</string>
 	<string name="answered_call">
 		Ligação atendida
 	</string>
diff --git a/indra/newview/skins/default/xui/ru/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/ru/floater_animation_anim_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..711afc271723fadcf366921ba18afd1ff934632f
--- /dev/null
+++ b/indra/newview/skins/default/xui/ru/floater_animation_anim_preview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Anim Preview" title="ANIMATION.ANIM">
+	<text name="name_label">
+		Название:
+	</text>
+	<text name="description_label">
+		Описание:
+	</text>
+	<button label="Передать (L$[AMOUNT])" name="ok_btn"/>
+	<button label="Отмена" label_selected="Отмена" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/ru/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/ru/floater_animation_bvh_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8ad9d8657a154096af470401e2e3f2f5ba0bc261
--- /dev/null
+++ b/indra/newview/skins/default/xui/ru/floater_animation_bvh_preview.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Animation Preview">
+	<floater.string name="failed_to_initialize">
+		Невозможно инициализировать движение
+	</floater.string>
+	<floater.string name="anim_too_long">
+		Длина файла анимации: [LENGTH] с.
+
+Максимальная длина анимации: [MAX_LENGTH] с.
+	</floater.string>
+	<floater.string name="failed_file_read">
+		Невозможно прочитать файл анимации.
+
+[STATUS]
+	</floater.string>
+	<floater.string name="E_ST_OK">
+		ОК
+	</floater.string>
+	<floater.string name="E_ST_EOF">
+		Преждевременный конец файла.
+	</floater.string>
+	<floater.string name="E_ST_NO_CONSTRAINT">
+		Не могу прочитать определение ограничений.
+	</floater.string>
+	<floater.string name="E_ST_NO_FILE">
+		Не удалось открыть BVH-файл.
+	</floater.string>
+	<floater.string name="E_ST_NO_HIER">
+		Неправильный заголовок HIERARCHY.
+	</floater.string>
+	<floater.string name="E_ST_NO_JOINT">
+		Не удалось найти ROOT или JOINT.
+	</floater.string>
+	<floater.string name="E_ST_NO_NAME">
+		Не удалось получить имя JOINT.
+	</floater.string>
+	<floater.string name="E_ST_NO_OFFSET">
+		Не удалось найти OFFSET.
+	</floater.string>
+	<floater.string name="E_ST_NO_CHANNELS">
+		Не удалось найти CHANNELS.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROTATION">
+		Не удалось получить порядок вращения.
+	</floater.string>
+	<floater.string name="E_ST_NO_AXIS">
+		Не удалось получить оси вращения.
+	</floater.string>
+	<floater.string name="E_ST_NO_MOTION">
+		Не удалось найти MOTION.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAMES">
+		Не удалось получить количество кадров.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAME_TIME">
+		Не удалось получить время кадра.
+	</floater.string>
+	<floater.string name="E_ST_NO_POS">
+		Не удалось получить значения position.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROT">
+		Не удалось получить значения rotation.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_FILE">
+		Не удалось открыть файл перевода.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HEADER">
+		Не удалось прочитать заголовок перевода.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_NAME">
+		Не удалось прочитать имена перевода.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_IGNORE">
+		Не удалось прочитать значение перевода ignore.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_RELATIVE">
+		Не удалось прочитать значение перевода relative.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_OUTNAME">
+		Не удалось прочитать значение перевода outname.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MATRIX">
+		Не удалось прочитать матрицу перевода.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGECHILD">
+		Не удалось получить имя mergechild.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGEPARENT">
+		Не удалось получить имя mergeparent.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_PRIORITY">
+		Не удалось получить значение priority.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_LOOP">
+		Не удалось получить значение loop.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEIN">
+		Не удалось получить значения easeIn.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEOUT">
+		Не удалось получить значения easeOut.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HAND">
+		Не удалось получить значение hand morph.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EMOTE">
+		Не удалось прочитать имя emote.
+	</floater.string>
+	<floater.string name="E_ST_BAD_ROOT">
+		Неверное имя корневого соединения, должно быть «hip».
+	</floater.string>
+	<text name="name_label">
+		Название:
+	</text>
+	<text name="description_label">
+		Описание:
+	</text>
+	<spinner label="Приоритет" name="priority" tool_tip="Управляет тем, как другие анимации могут перекрываться этой"/>
+	<check_box label="Цикл" name="loop_check" tool_tip="Делает анимацию зацикленной"/>
+	<spinner label="Начало(%)" name="loop_in_point" tool_tip="Устанавливает точку возврата цикла"/>
+	<spinner label="Конец(%)" name="loop_out_point" tool_tip="Устанавливает точку конца цикла"/>
+	<text name="hand_label">
+		Положение пальцев
+	</text>
+	<combo_box name="hand_pose_combo" tool_tip="Контролирует положение пальцев во время анимации">
+		<combo_box.item label="Разведены" name="Spread"/>
+		<combo_box.item label="Расслаблены" name="Relaxed"/>
+		<combo_box.item label="Указывают" name="PointBoth"/>
+		<combo_box.item label="Сжаты в кулак" name="Fist"/>
+		<combo_box.item label="Левые расслаблены" name="RelaxedLeft"/>
+		<combo_box.item label="Левые указывают" name="PointLeft"/>
+		<combo_box.item label="Левые в кулак" name="FistLeft"/>
+		<combo_box.item label="Правые расслаблены" name="RelaxedRight"/>
+		<combo_box.item label="Правые указывают" name="PointRight"/>
+		<combo_box.item label="Правые в кулак" name="FistRight"/>
+		<combo_box.item label="Правые в приветствии" name="SaluteRight"/>
+		<combo_box.item label="Печатают" name="Typing"/>
+		<combo_box.item label="Правые «V»" name="PeaceRight"/>
+	</combo_box>
+	<text name="emote_label">
+		Выражение лица
+	</text>
+	<combo_box name="emote_combo" tool_tip="Контролирует выражение лица во время анимации">
+		<item label="(нет)" name="[None]" value=""/>
+		<item label="Ааааах" name="Aaaaah" value="Ааааах"/>
+		<item label="Боится" name="Afraid" value="Боится"/>
+		<item label="Злится" name="Angry" value="Злится"/>
+		<item label="Широко улыбается" name="BigSmile" value="Широко улыбается"/>
+		<item label="Скучает" name="Bored" value="Скучает"/>
+		<item label="Плачет" name="Cry" value="Плачет"/>
+		<item label="Презирает" name="Disdain" value="Презирает"/>
+		<item label="Смущается" name="Embarrassed" value="Смущается"/>
+		<item label="Хмурится" name="Frown" value="Хмурится"/>
+		<item label="Целует" name="Kiss" value="Целует"/>
+		<item label="Смеется" name="Laugh" value="Смеется"/>
+		<item label="Дразнится" name="Plllppt" value="Дразнится"/>
+		<item label="Не соглашается" name="Repulsed" value="Не соглашается"/>
+		<item label="Грустит" name="Sad" value="Грустит"/>
+		<item label="Не понимает" name="Shrug" value="Не понимает"/>
+		<item label="Улыбается" name="Smile" value="Улыбается"/>
+		<item label="Удивляется" name="Surprise" value="Удивляется"/>
+		<item label="Подмигивает" name="Wink" value="Подмигивает"/>
+		<item label="Беспокоится" name="Worry" value="Беспокоится"/>
+	</combo_box>
+	<text name="preview_label">
+		Просмотр во время
+	</text>
+	<combo_box name="preview_base_anim" tool_tip="Просмотр вашей анимации во время выполнения аватаром действий.">
+		<item label="Стояние" name="Standing" value="Стояние"/>
+		<item label="Ходьба" name="Walking" value="Ходьба"/>
+		<item label="Сидение" name="Sitting" value="Сидение"/>
+		<item label="Полет" name="Flying" value="Полет"/>
+	</combo_box>
+	<spinner label="Вход (сек.)" name="ease_in_time" tool_tip="Количество времени (в секундах) для входа в стартовое положение"/>
+	<spinner label="Выход (сек.)" name="ease_out_time" tool_tip="Количество времени (в секундах) для выхода из анимации"/>
+	<button name="play_btn" tool_tip="Проиграть анимацию"/>
+	<button name="pause_btn" tool_tip="Приостановить анимацию"/>
+	<button name="stop_btn" tool_tip="Остановить проигрывание анимации"/>
+	<text name="bad_animation_text">
+		Невозможно прочитать файл анимации.
+
+Рекомендуется использовать BVH-файлы, экспортированные из Poser 4.
+	</text>
+	<button label="Передать (L$[AMOUNT])" name="ok_btn"/>
+	<button label="Отмена" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/ru/floater_preview_animation.xml b/indra/newview/skins/default/xui/ru/floater_preview_animation.xml
index a1fabedb85f49a09cf9c6268593f8e81fff6d2ae..22c6bc59015697b7a5f48d3f5034cf6a7654d0ff 100644
--- a/indra/newview/skins/default/xui/ru/floater_preview_animation.xml
+++ b/indra/newview/skins/default/xui/ru/floater_preview_animation.xml
@@ -6,6 +6,6 @@
 	<text name="desc txt">
 		Описание:
 	</text>
-	<button label="Проиграть для всех" label_selected="Стоп" name="Anim play btn" tool_tip="Проигрывание этой анимации могут видеть другие участники"/>
-	<button label="Проиграть для себя" label_selected="Стоп" name="Anim audition btn" tool_tip="Проигрывание этой анимации можете видеть только вы"/>
+	<button label="Проиграть для всех" label_selected="Стоп" name="Inworld" tool_tip="Проигрывание этой анимации могут видеть другие участники"/>
+	<button label="Проиграть для себя" label_selected="Стоп" name="Locally" tool_tip="Проигрывание этой анимации можете видеть только вы"/>
 </floater>
diff --git a/indra/newview/skins/default/xui/ru/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/ru/floater_test_text_vertical_aligment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d0bd86160e6898efabf651859aa6eb8f647ae81d
--- /dev/null
+++ b/indra/newview/skins/default/xui/ru/floater_test_text_vertical_aligment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Test Floater" title="ТЕСТИРОВАТЬ ОКНО"/>
diff --git a/indra/newview/skins/default/xui/ru/floater_tools.xml b/indra/newview/skins/default/xui/ru/floater_tools.xml
index eb9083f7fc81f003399182f6cd0406c7970fbaf3..3d7d1198f083343e97fbabcb9456177ead3c3ab1 100644
--- a/indra/newview/skins/default/xui/ru/floater_tools.xml
+++ b/indra/newview/skins/default/xui/ru/floater_tools.xml
@@ -1,5 +1,20 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="toolbox floater" short_title="ИНСТРУМЕНТЫ ДЛЯ СТРОИТЕЛЬСТВА">
+	<floater.string name="grid_screen_text">
+		Экран
+	</floater.string>
+	<floater.string name="grid_local_text">
+		Локальная
+	</floater.string>
+	<floater.string name="grid_world_text">
+		Мировая
+	</floater.string>
+	<floater.string name="grid_reference_text">
+		Точка отсчета
+	</floater.string>
+	<floater.string name="grid_attachment_text">
+		Присоединение
+	</floater.string>
 	<floater.string name="status_rotate">
 		Перетягивайте цветные полосы для вращения объекта
 	</floater.string>
@@ -63,7 +78,12 @@
 	</text>
 	<check_box initial_value="истина" label="Растягивать текстуры" name="checkbox stretch textures"/>
 	<check_box initial_value="истина" label="Привязка" name="checkbox snap to grid"/>
-	<button label="Параметры..." name="Options..." tool_tip="Дополнительные параметры сетки"/>
+	<combo_box name="combobox grid mode" tool_tip="Выберите тип линейки сетки для размещения объекта">
+		<combo_box.item label="Мировая" name="World"/>
+		<combo_box.item label="Локальная" name="Local"/>
+		<combo_box.item label="Точка отсчета" name="Reference"/>
+	</combo_box>
+	<button label="" name="Options..." tool_tip="Дополнительные параметры сетки"/>
 	<button name="ToolCube" tool_tip="Куб"/>
 	<button name="ToolPrism" tool_tip="Призма"/>
 	<button name="ToolPyramid" tool_tip="Пирамида"/>
diff --git a/indra/newview/skins/default/xui/ru/floater_voice_effect.xml b/indra/newview/skins/default/xui/ru/floater_voice_effect.xml
index d4bf615fe45b9f4b13cdd8a7a9115e31dacec812..1eb8a94d7a7bba4373e729057f601a01b83113d5 100644
--- a/indra/newview/skins/default/xui/ru/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/ru/floater_voice_effect.xml
@@ -42,13 +42,16 @@
 	<string name="effect_Demon">
 		Demon
 	</string>
+	<string name="effect_Female Elf">
+		Фея
+	</string>
 	<string name="effect_Flirty">
 		Flirty
 	</string>
 	<string name="effect_Foxy">
 		Foxy
 	</string>
-	<string name="effect_Halloween_2010_Bonus">
+	<string name="effect_Halloween 2010 Bonus">
 		Бонус_за_Хэллоуин_2010
 	</string>
 	<string name="effect_Helium">
@@ -57,9 +60,18 @@
 	<string name="effect_Husky">
 		Husky
 	</string>
+	<string name="effect_Husky Whisper">
+		Хриплый шепот
+	</string>
 	<string name="effect_Intercom">
 		Внутренняя связь
 	</string>
+	<string name="effect_Julia">
+		Julia
+	</string>
+	<string name="effect_Lo Lilt">
+		Напев
+	</string>
 	<string name="effect_Macho">
 		Macho
 	</string>
@@ -69,6 +81,9 @@
 	<string name="effect_Mini">
 		Mini
 	</string>
+	<string name="effect_Model">
+		Модель
+	</string>
 	<string name="effect_Nano">
 		Nano
 	</string>
@@ -90,6 +105,9 @@
 	<string name="effect_Roxanne">
 		Roxanne
 	</string>
+	<string name="effect_Rumble">
+		Урчание
+	</string>
 	<string name="effect_Sabrina">
 		Sabrina
 	</string>
@@ -102,6 +120,9 @@
 	<string name="effect_Shorty">
 		Shorty
 	</string>
+	<string name="effect_Smaller">
+		Меньше
+	</string>
 	<string name="effect_Sneaky">
 		Sneaky
 	</string>
diff --git a/indra/newview/skins/default/xui/ru/menu_inventory.xml b/indra/newview/skins/default/xui/ru/menu_inventory.xml
index df5e5329a3e095c51376abfdfb21703a3bf67f5a..49f7281b4ebae7a58dd9e2d442c168565c9e39a4 100644
--- a/indra/newview/skins/default/xui/ru/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/ru/menu_inventory.xml
@@ -59,6 +59,7 @@
 	<menu_item_call label="Свойства" name="Properties"/>
 	<menu_item_call label="Переименовать" name="Rename"/>
 	<menu_item_call label="Копировать UUID актива" name="Copy Asset UUID"/>
+	<menu_item_call label="Вырезать" name="Cut"/>
 	<menu_item_call label="Копировать" name="Copy"/>
 	<menu_item_call label="Вставить" name="Paste"/>
 	<menu_item_call label="Вставить как ссылку" name="Paste As Link"/>
diff --git a/indra/newview/skins/default/xui/ru/menu_viewer.xml b/indra/newview/skins/default/xui/ru/menu_viewer.xml
index 7698614751eeef4fd3ee04c0f95ec9cce42cc7de..0699314d971f643dffd13e1c8ffc10ab3b50bddf 100644
--- a/indra/newview/skins/default/xui/ru/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/ru/menu_viewer.xml
@@ -21,6 +21,7 @@
 			<menu_item_call label="Не беспокоить" name="Set Busy"/>
 		</menu>
 		<menu_item_call label="Купить L$..." name="Buy and Sell L$"/>
+		<menu_item_call label="Торговые исходящие..." name="MerchantOutbox"/>
 		<menu_item_call label="Информационная панель аккаунта..." name="Manage My Account"/>
 		<menu_item_call label="Настройки..." name="Preferences"/>
 		<menu_item_call label="Кнопки панели инструментов..." name="Toolbars"/>
@@ -391,9 +392,16 @@
 		<menu_item_check label="Текстуры HTTP" name="HTTP Textures"/>
 		<menu_item_check label="Инвентарь HTTP" name="HTTP Inventory"/>
 		<menu_item_call label="Сжатие изображений" name="Compress Images"/>
+		<menu_item_call label="Включить Visual Leak Detector" name="Enable Visual Leak Detector"/>
 		<menu_item_check label="Вывод минидампа при отладке" name="Output Debug Minidump"/>
 		<menu_item_check label="Окно консоли при следующем запуске" name="Console Window"/>
-		<menu label="Уровень журнала" name="Set Logging Level"/>
+		<menu label="Уровень журнала" name="Set Logging Level">
+			<menu_item_check label="Отладка" name="Debug"/>
+			<menu_item_check label="Информация" name="Info"/>
+			<menu_item_check label="Предупреждение" name="Warning"/>
+			<menu_item_check label="Ошибка" name="Error"/>
+			<menu_item_check label="Нет" name="None"/>
+		</menu>
 		<menu_item_call label="Запрос статуса администратора" name="Request Admin Options"/>
 		<menu_item_call label="Выход из статуса администратора" name="Leave Admin Options"/>
 		<menu_item_check label="Показать меню администратора" name="View Admin Options"/>
diff --git a/indra/newview/skins/default/xui/ru/notifications.xml b/indra/newview/skins/default/xui/ru/notifications.xml
index 1854d43e0a7ce78d2c66e16d81fe5aadf101ba1c..b4692385d185f4bef55731d65d59b4834aebd300 100644
--- a/indra/newview/skins/default/xui/ru/notifications.xml
+++ b/indra/newview/skins/default/xui/ru/notifications.xml
@@ -670,7 +670,7 @@
 		Невозможно создать выходной файл: [FILE]
 	</notification>
 	<notification name="DoNotSupportBulkAnimationUpload">
-		[APP_NAME] пока не поддерживает массовую передачу файлов анимации.
+		[APP_NAME] пока не поддерживает массовую передачу файлов анимации формата BVH.
 	</notification>
 	<notification name="CannotUploadReason">
 		Невозможно передать [FILE] по следующей причине: [REASON]
@@ -2630,16 +2630,16 @@ http://secondlife.com/download.
 		[NAME] – «&lt;nolink&gt;[TITLE]&lt;/nolink&gt;»
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Блокировать"/>
-			<button name="Ignore" text="Игнорировать"/>
+			<button name="Client_Side_Mute" text="Заблокировать"/>
+			<button name="Client_Side_Ignore" text="Игнорировать"/>
 		</form>
 	</notification>
 	<notification name="ScriptDialogGroup">
 		[GROUPNAME] – «&lt;nolink&gt;[TITLE]&lt;/nolink&gt;»
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Блокировать"/>
-			<button name="Ignore" text="Игнорировать"/>
+			<button name="Client_Side_Mute" text="Заблокировать"/>
+			<button name="Client_Side_Ignore" text="Игнорировать"/>
 		</form>
 	</notification>
 	<notification name="BuyLindenDollarSuccess">
diff --git a/indra/newview/skins/default/xui/ru/panel_nearby_chat.xml b/indra/newview/skins/default/xui/ru/panel_nearby_chat.xml
index 8e3aac38d2dd356ab3164eba79fc9fcf7df1df00..a8fdfde1c4bc1d6c1a4084eaa598847150d260e2 100644
--- a/indra/newview/skins/default/xui/ru/panel_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/ru/panel_nearby_chat.xml
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="nearby_chat">
-	<check_box label="Переводить чат" name="translate_chat_checkbox"/>
+	<layout_stack name="stack">
+		<layout_panel name="translate_chat_checkbox_lp">
+			<check_box label="Переводить чат" name="translate_chat_checkbox"/>
+		</layout_panel>
+	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/ru/panel_status_bar.xml b/indra/newview/skins/default/xui/ru/panel_status_bar.xml
index babe5811acefa94369de93156ac3a53ae4fc778f..9c84ff1fd894e142e4a65e95c0e64b7128fd371d 100644
--- a/indra/newview/skins/default/xui/ru/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/ru/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		L$ [AMT]
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-450" name="balance_bg" width="240">
 		<text name="balance" tool_tip="Щелкните для обновления вашего баланса L$" value="L$20"/>
 		<button label="Купить L$" name="buyL" tool_tip="Щелкните для покупки L$"/>
 		<button label="Торговый центр" name="goShop" tool_tip="Открыть торговый центр Second Life" width="121"/>
diff --git a/indra/newview/skins/default/xui/ru/sidepanel_inventory.xml b/indra/newview/skins/default/xui/ru/sidepanel_inventory.xml
index b3d3ed9aad5a4c2fb0211f30d059c5a88e81a572..c106c2de7943defc766a3ab1f64be175f7273cf6 100644
--- a/indra/newview/skins/default/xui/ru/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/ru/sidepanel_inventory.xml
@@ -14,7 +14,7 @@
 					<text name="inbox_fresh_new_count">
 						Новых: [NUM]
 					</text>
-					<panel tool_tip="Drag and drop items to your inventory to manage and use them">
+					<panel name="inbox_inventory_placeholder_panel" tool_tip="Перетащите вещи в ваш инвентарь для их использования">
 						<text name="inbox_inventory_placeholder">
 							Покупки из торгового центра будут доставлены сюда.
 						</text>
diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml
index 4240514621d40b1b1547e185cd7d4b5da8abfb24..8dbc4f092d05f7d3c4f7080857d8ff0a0825a6dd 100644
--- a/indra/newview/skins/default/xui/ru/strings.xml
+++ b/indra/newview/skins/default/xui/ru/strings.xml
@@ -832,6 +832,9 @@ support@secondlife.com.
 	<string name="anim_yes_head">
 		Согласие
 	</string>
+	<string name="multiple_textures">
+		Несколько
+	</string>
 	<string name="texture_loading">
 		Загрузка...
 	</string>
@@ -1232,7 +1235,7 @@ support@secondlife.com.
 		В вашем инвентаре нет копии этой текстуры
 	</string>
 	<string name="InventoryInboxNoItems">
-		Здесь будут появляться полученные вами предметы, например подарки.  Их можно будет перетащить в ваш инвентарь.
+		Здесь будут показаны ваши покупки из торгового центра. Их можно будет перетащить в ваш инвентарь для использования.
 	</string>
 	<string name="MarketplaceURL">
 		https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
@@ -3915,6 +3918,9 @@ support@secondlife.com.
 	<string name="Saved_message">
 		(Сохранено [LONG_TIMESTAMP])
 	</string>
+	<string name="IM_unblock_only_groups_friends">
+		Для просмотра этого сообщения снимите флажок «Только друзья и группы могут звонить мне и отправлять IM» в окне «Настройки/Приватность».
+	</string>
 	<string name="answered_call">
 		На ваш звонок ответили
 	</string>
diff --git a/indra/newview/skins/default/xui/tr/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/tr/floater_animation_anim_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a63e1e107e4afb5bc7bf377c41bc73a76c36f9d4
--- /dev/null
+++ b/indra/newview/skins/default/xui/tr/floater_animation_anim_preview.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Anim Preview" title="ANIMATION.ANIM">
+	<text name="name_label">
+		Ad:
+	</text>
+	<text name="description_label">
+		Açıklama:
+	</text>
+	<button label="Karşıya Yükle (L$[AMOUNT])" name="ok_btn"/>
+	<button label="Ä°ptal" label_selected="Ä°ptal" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/tr/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/tr/floater_animation_bvh_preview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f8800c674d0b54dceae778ef1daea45410e581f0
--- /dev/null
+++ b/indra/newview/skins/default/xui/tr/floater_animation_bvh_preview.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Animation Preview">
+	<floater.string name="failed_to_initialize">
+		Hareket başlatılamadı
+	</floater.string>
+	<floater.string name="anim_too_long">
+		Animasyon dosyası [LENGTH] saniye uzunluğunda.
+
+Maksimum animasyon uzunluÄŸu [LENGTH] saniye.
+	</floater.string>
+	<floater.string name="failed_file_read">
+		Animasyon dosyası okunamadı.
+
+[STATUS]
+	</floater.string>
+	<floater.string name="E_ST_OK">
+		Tamam
+	</floater.string>
+	<floater.string name="E_ST_EOF">
+		Dosyanın zamanından önce sonu.
+	</floater.string>
+	<floater.string name="E_ST_NO_CONSTRAINT">
+		Kısıtlama tanımı okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_FILE">
+		BVH dosyası açılamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_HIER">
+		Geçersiz HİYERARŞİ üst bilgisi.
+	</floater.string>
+	<floater.string name="E_ST_NO_JOINT">
+		KÖK veya EKLEM bulunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_NAME">
+		EKLEM adı alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_OFFSET">
+		OFSET bulunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_CHANNELS">
+		KANALLAR bulunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROTATION">
+		Döndürme sırası alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_AXIS">
+		Döndürme ekseni alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_MOTION">
+		HAREKET bulunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAMES">
+		kARE SAYISI alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_FRAME_TIME">
+		Kare zamanı alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_POS">
+		Konum değerleri alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_ROT">
+		Döndürme değerleri alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_FILE">
+		Çeviri dosyası açılamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HEADER">
+		Çeviri üst bilgisi okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_NAME">
+		Çeviri adları okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_IGNORE">
+		Çeviri yoksay değeri okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_RELATIVE">
+		Çeviri nisbi değeri okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_OUTNAME">
+		Çeviri çıkış adı değeri okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MATRIX">
+		Çeviri matrisi okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGECHILD">
+		Birleştirme alt birim adı alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_MERGEPARENT">
+		Birleştirme üst birim adı alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_PRIORITY">
+		Öncelik değerleri alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_LOOP">
+		Döngü (tekrar) değerleri alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEIN">
+		Easln (Yavaş Başlangıç) değerleri alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EASEOUT">
+		EaseOut (Yavaş Bitiş) değerleri alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_HAND">
+		El şekillendirme değeri alınamadı.
+	</floater.string>
+	<floater.string name="E_ST_NO_XLT_EMOTE">
+		Duygu ifadesi adı okunamadı.
+	</floater.string>
+	<floater.string name="E_ST_BAD_ROOT">
+		Yanlış kök eklem adı, &quot;kalça&quot; kullanın
+	</floater.string>
+	<text name="name_label">
+		Ad:
+	</text>
+	<text name="description_label">
+		Açıklama:
+	</text>
+	<spinner label="Öncelik" name="priority" tool_tip="Bu animasyonun diğer animasyonları geçersiz kılabileceği kontrolleri"/>
+	<check_box label="Döngü" name="loop_check" tool_tip="Bu animasyonun döngülenmesini (tekrarlanmasını) sağlar"/>
+	<spinner label="İç (%)" name="loop_in_point" tool_tip="Döngünün döndüğü animasyon noktasını belirler"/>
+	<spinner label="Dış (%)" name="loop_out_point" tool_tip="Animasyonda döngünün bittiği noktayı belirler"/>
+	<text name="hand_label">
+		El DuruÅŸu
+	</text>
+	<combo_box name="hand_pose_combo" tool_tip="Ellerin animasyon sırasında neler yaptığını kontrol eder">
+		<combo_box.item label="Yayılım" name="Spread"/>
+		<combo_box.item label="Rahat" name="Relaxed"/>
+		<combo_box.item label="Her Ä°kisi de Ä°ÅŸaret Ediyor" name="PointBoth"/>
+		<combo_box.item label="Yumruk" name="Fist"/>
+		<combo_box.item label="Sol Rahat" name="RelaxedLeft"/>
+		<combo_box.item label="Sol Ä°ÅŸaret Ediyor" name="PointLeft"/>
+		<combo_box.item label="Sol Yumruk" name="FistLeft"/>
+		<combo_box.item label="SaÄŸ Rahat" name="RelaxedRight"/>
+		<combo_box.item label="SaÄŸ Ä°ÅŸaret Ediyor" name="PointRight"/>
+		<combo_box.item label="SaÄŸ Yumruk" name="FistRight"/>
+		<combo_box.item label="Sağı Selamlıyor" name="SaluteRight"/>
+		<combo_box.item label="Yazı Yazıyor" name="Typing"/>
+		<combo_box.item label="Sağ Barış" name="PeaceRight"/>
+	</combo_box>
+	<text name="emote_label">
+		Ä°fade
+	</text>
+	<combo_box name="emote_combo" tool_tip="Yüzün animasyon sırasındaki ifadesini kontrol eder">
+		<item label="(Hiçbiri)" name="[None]" value=""/>
+		<item label="Aaaaah" name="Aaaaah" value="Aaaaah"/>
+		<item label="KorkmuÅŸ" name="Afraid" value="KorkmuÅŸ"/>
+		<item label="Kızgın" name="Angry" value="Kızgın"/>
+		<item label="Yaygın Gülümseyiş" name="BigSmile" value="Yaygın Gülümseyiş"/>
+		<item label="Canı Sıkılmış" name="Bored" value="Canı Sıkılmış"/>
+		<item label="AÄŸlama" name="Cry" value="AÄŸlama"/>
+		<item label="Dudak Bükme" name="Disdain" value="Dudak Bükme"/>
+		<item label="Utanmış" name="Embarrassed" value="Utanmış"/>
+		<item label="Kaş Çatma" name="Frown" value="Kaş Çatma"/>
+		<item label="Öpücük" name="Kiss" value="Öpücük"/>
+		<item label="Gülme" name="Laugh" value="Gülme"/>
+		<item label="Kahkaha" name="Plllppt" value="Kahkaha"/>
+		<item label="TiksinmiÅŸ" name="Repulsed" value="TiksinmiÅŸ"/>
+		<item label="Üzgün" name="Sad" value="Üzgün"/>
+		<item label="Omuz Silkme" name="Shrug" value="Omuz Silkme"/>
+		<item label="Gülümseme" name="Smile" value="Gülümseme"/>
+		<item label="Sürpriz" name="Surprise" value="Sürpriz"/>
+		<item label="Göz Kırpma" name="Wink" value="Göz Kırpma"/>
+		<item label="EndiÅŸelenme" name="Worry" value="EndiÅŸelenme"/>
+	</combo_box>
+	<text name="preview_label">
+		Şu sırada önizle
+	</text>
+	<combo_box name="preview_base_anim" tool_tip="Animasyon davranışınızı avatarınız genel hareketleri yaparken test etmek için bunu kullanın.">
+		<item label="Ayakta Duruyor" name="Standing" value="Ayakta Duruyor"/>
+		<item label="Yürüyor" name="Walking" value="Yürüyor"/>
+		<item label="Oturuyor" name="Sitting" value="Oturuyor"/>
+		<item label="Uçuyor" name="Flying" value="Uçuyor"/>
+	</combo_box>
+	<spinner label="Yavaş Başlangıç (saniye)" name="ease_in_time" tool_tip="Animasyonun kaynaştığı süre (saniye olarak)"/>
+	<spinner label="Yavaş Bitiş (saniye)" name="ease_out_time" tool_tip="Animasyonun ayrıştığı süre (saniye olarak)"/>
+	<button name="play_btn" tool_tip="Animasyonunu oynat"/>
+	<button name="pause_btn" tool_tip="Animasyonunu duraklat"/>
+	<button name="stop_btn" tool_tip="Animasyo oynatmayı durdur"/>
+	<text name="bad_animation_text">
+		Animasyon dosyası okunamadı.
+
+Poser 4&apos;ten aktarılan BHV dosyalarını tavsiye ederiz.
+	</text>
+	<button label="Karşıya Yükle (L$[AMOUNT])" name="ok_btn"/>
+	<button label="Ä°ptal" name="cancel_btn"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/tr/floater_preview_animation.xml b/indra/newview/skins/default/xui/tr/floater_preview_animation.xml
index 1c526c75f9bd73a95f7006ff635183ea0f4d08e0..23b484833378add7b6832740b23b464045c321db 100644
--- a/indra/newview/skins/default/xui/tr/floater_preview_animation.xml
+++ b/indra/newview/skins/default/xui/tr/floater_preview_animation.xml
@@ -6,6 +6,6 @@
 	<text name="desc txt">
 		Açıklama:
 	</text>
-	<button label="SL Dünyasında Oynat" label_selected="Durdur" name="Anim play btn" tool_tip="Bu animasyonu başkaları görebilecek şekilde oynatın"/>
-	<button label="Yerel Olarak Oynat" label_selected="Durdur" name="Anim audition btn" tool_tip="Bu animasyonu sadece kendinizin görebileceği şekilde oynatın"/>
+	<button label="SL Dünyasında Oynat" label_selected="Durdur" name="Inworld" tool_tip="Bu animasyonu başkaları görebilecek şekilde oynatın"/>
+	<button label="Yerel Olarak Oynat" label_selected="Durdur" name="Locally" tool_tip="Bu animasyonu sadece kendinizin görebileceği şekilde oynatın"/>
 </floater>
diff --git a/indra/newview/skins/default/xui/tr/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/tr/floater_test_text_vertical_aligment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fcb7d87287f2196460d32533789b674e4998404e
--- /dev/null
+++ b/indra/newview/skins/default/xui/tr/floater_test_text_vertical_aligment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater name="Test Floater" title="TEST PENCERESÄ°"/>
diff --git a/indra/newview/skins/default/xui/tr/floater_tools.xml b/indra/newview/skins/default/xui/tr/floater_tools.xml
index d4ee9995ddab6d92436e38bb33b63b8d71769030..b0c59ced42444b0af0a60f499909e84c49695f4a 100644
--- a/indra/newview/skins/default/xui/tr/floater_tools.xml
+++ b/indra/newview/skins/default/xui/tr/floater_tools.xml
@@ -1,5 +1,20 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="toolbox floater" short_title="İNŞA ET ARAÇLARI">
+	<floater.string name="grid_screen_text">
+		Ekran
+	</floater.string>
+	<floater.string name="grid_local_text">
+		Yerel
+	</floater.string>
+	<floater.string name="grid_world_text">
+		Dünya
+	</floater.string>
+	<floater.string name="grid_reference_text">
+		Referans
+	</floater.string>
+	<floater.string name="grid_attachment_text">
+		Aksesuar
+	</floater.string>
 	<floater.string name="status_rotate">
 		Nesneyi döndürmek için renkli bantları sürükleyin
 	</floater.string>
@@ -63,7 +78,12 @@
 	</text>
 	<check_box initial_value="true" label="Dokuları Uzat" name="checkbox stretch textures"/>
 	<check_box initial_value="true" label="Yasla" name="checkbox snap to grid"/>
-	<button label="Seçenekler..." name="Options..." tool_tip="İlave ağ seçeneklerine bak"/>
+	<combo_box name="combobox grid mode" tool_tip="Nesneyi konumlandırmak için ağ cetvelini seçin">
+		<combo_box.item label="Dünya" name="World"/>
+		<combo_box.item label="Yerel" name="Local"/>
+		<combo_box.item label="Referans" name="Reference"/>
+	</combo_box>
+	<button label="" name="Options..." tool_tip="İlave ağ seçeneklerine bak"/>
 	<button name="ToolCube" tool_tip="Küp"/>
 	<button name="ToolPrism" tool_tip="Prizma"/>
 	<button name="ToolPyramid" tool_tip="Piramit"/>
diff --git a/indra/newview/skins/default/xui/tr/floater_voice_effect.xml b/indra/newview/skins/default/xui/tr/floater_voice_effect.xml
index a10da39a69ec04599e3dffbc3dc657e4524ddeba..3534a3fe906a58ac7f33f5b6ed8e0b96e44704fb 100644
--- a/indra/newview/skins/default/xui/tr/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/tr/floater_voice_effect.xml
@@ -42,13 +42,16 @@
 	<string name="effect_Demon">
 		Ä°blis
 	</string>
+	<string name="effect_Female Elf">
+		DiÅŸi Cin
+	</string>
 	<string name="effect_Flirty">
 		Cilveli
 	</string>
 	<string name="effect_Foxy">
 		Alımlı
 	</string>
-	<string name="effect_Halloween_2010_Bonus">
+	<string name="effect_Halloween 2010 Bonus">
 		Halloween_2010_Bonus
 	</string>
 	<string name="effect_Helium">
@@ -57,9 +60,18 @@
 	<string name="effect_Husky">
 		Güçlü
 	</string>
+	<string name="effect_Husky Whisper">
+		Boğuk Fısıltı
+	</string>
 	<string name="effect_Intercom">
 		Ä°nterkom
 	</string>
+	<string name="effect_Julia">
+		Julia
+	</string>
+	<string name="effect_Lo Lilt">
+		Yavaş Mırıltı
+	</string>
 	<string name="effect_Macho">
 		Maço
 	</string>
@@ -69,6 +81,9 @@
 	<string name="effect_Mini">
 		Mini
 	</string>
+	<string name="effect_Model">
+		Model
+	</string>
 	<string name="effect_Nano">
 		Nano
 	</string>
@@ -90,6 +105,9 @@
 	<string name="effect_Roxanne">
 		Roxanne
 	</string>
+	<string name="effect_Rumble">
+		Gurultu
+	</string>
 	<string name="effect_Sabrina">
 		Sabrina
 	</string>
@@ -102,6 +120,9 @@
 	<string name="effect_Shorty">
 		Bücür
 	</string>
+	<string name="effect_Smaller">
+		Daha Küçük
+	</string>
 	<string name="effect_Sneaky">
 		Sinsi
 	</string>
diff --git a/indra/newview/skins/default/xui/tr/menu_inventory.xml b/indra/newview/skins/default/xui/tr/menu_inventory.xml
index 6aaaab9c79fbfdfb0d97d7da4812c25934b8038b..170cdebd24ca5e23a34bb8508253a950ac3f07f6 100644
--- a/indra/newview/skins/default/xui/tr/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/tr/menu_inventory.xml
@@ -59,6 +59,7 @@
 	<menu_item_call label="Özellikler" name="Properties"/>
 	<menu_item_call label="Yeniden Adlandır" name="Rename"/>
 	<menu_item_call label="Varlık UUID&apos;sini Kopyala" name="Copy Asset UUID"/>
+	<menu_item_call label="Kes" name="Cut"/>
 	<menu_item_call label="Kopyala" name="Copy"/>
 	<menu_item_call label="Yapıştır" name="Paste"/>
 	<menu_item_call label="Bağlantı Olarak Yapıştır" name="Paste As Link"/>
diff --git a/indra/newview/skins/default/xui/tr/menu_viewer.xml b/indra/newview/skins/default/xui/tr/menu_viewer.xml
index d24757bb79998c0ce8e13d338349f363691c0cf3..d7b20bac4b4ef248562ad776de00ad683fdcbca8 100644
--- a/indra/newview/skins/default/xui/tr/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/tr/menu_viewer.xml
@@ -21,6 +21,7 @@
 			<menu_item_call label="MeÅŸgul" name="Set Busy"/>
 		</menu>
 		<menu_item_call label="L$ Satın Al..." name="Buy and Sell L$"/>
+		<menu_item_call label="Satıcı Giden Kutusu..." name="MerchantOutbox"/>
 		<menu_item_call label="Hesap kontrol paneli..." name="Manage My Account"/>
 		<menu_item_call label="Tercihler..." name="Preferences"/>
 		<menu_item_call label="Araç çubuğu düğmeleri..." name="Toolbars"/>
@@ -391,9 +392,16 @@
 		<menu_item_check label="HTTP Dokuları" name="HTTP Textures"/>
 		<menu_item_check label="HTTP Envanteri" name="HTTP Inventory"/>
 		<menu_item_call label="Görüntüleri Sıkıştır" name="Compress Images"/>
+		<menu_item_call label="Visual Leak Detector&apos;ı Etkinleştir" name="Enable Visual Leak Detector"/>
 		<menu_item_check label="Mini Döküm Dosyası Hata Ayıklama Çıktısı" name="Output Debug Minidump"/>
 		<menu_item_check label="Sonraki Çalışmada Konsol Penceresi" name="Console Window"/>
-		<menu label="Günlük Tutma Seviyesini Seç" name="Set Logging Level"/>
+		<menu label="Günlük Tutma Seviyesini Seç" name="Set Logging Level">
+			<menu_item_check label="Hata ayıkla" name="Debug"/>
+			<menu_item_check label="Bilgi" name="Info"/>
+			<menu_item_check label="Uyarı" name="Warning"/>
+			<menu_item_check label="Hata" name="Error"/>
+			<menu_item_check label="Hiçbiri" name="None"/>
+		</menu>
 		<menu_item_call label="Yönetici Durumu Talep Et" name="Request Admin Options"/>
 		<menu_item_call label="Yönetici Durumundan Ayrıl" name="Leave Admin Options"/>
 		<menu_item_check label="Yönetici Menüsünü Göster" name="View Admin Options"/>
diff --git a/indra/newview/skins/default/xui/tr/notifications.xml b/indra/newview/skins/default/xui/tr/notifications.xml
index 719dbb8781a78964df7fad32ecb0b1add03cdad2..6681cdac7affacb6b216590ff983ae55b8b78b5f 100644
--- a/indra/newview/skins/default/xui/tr/notifications.xml
+++ b/indra/newview/skins/default/xui/tr/notifications.xml
@@ -670,7 +670,7 @@ Beklenen [VALIDS]
 		Çıkış dosyası oluşturulamıyor: [FILE]
 	</notification>
 	<notification name="DoNotSupportBulkAnimationUpload">
-		[APP_NAME] şu an için animasyon dosyalarının toplu olarak karşıya yüklenmesini desteklemiyor.
+		[APP_NAME] şu an için BVH formatında animasyon dosyalarının toplu olarak yüklenmesini desteklemiyor.
 	</notification>
 	<notification name="CannotUploadReason">
 		Aşağıdaki nedenden dolayı [FILE] dosyası karşıya yüklenemedi: [REASON]
@@ -2630,16 +2630,16 @@ Talep kabul edilsin mi?
 		[NAME] adlı kişiye ait &apos;&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos;
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Engelle"/>
-			<button name="Ignore" text="Yok say"/>
+			<button name="Client_Side_Mute" text="Engelle"/>
+			<button name="Client_Side_Ignore" text="Yok say"/>
 		</form>
 	</notification>
 	<notification name="ScriptDialogGroup">
 		[GROUPNAME] grubuna ait &apos;&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos;
 [MESSAGE]
 		<form name="form">
-			<button name="Mute" text="Engelle"/>
-			<button name="Ignore" text="Yok say"/>
+			<button name="Client_Side_Mute" text="Engelle"/>
+			<button name="Client_Side_Ignore" text="Yok say"/>
 		</form>
 	</notification>
 	<notification name="BuyLindenDollarSuccess">
diff --git a/indra/newview/skins/default/xui/tr/panel_nearby_chat.xml b/indra/newview/skins/default/xui/tr/panel_nearby_chat.xml
index c405105e007570dacc5541ba2508f88db2bbb676..d238388b0eec421839e4df7d3353a182f7500f73 100644
--- a/indra/newview/skins/default/xui/tr/panel_nearby_chat.xml
+++ b/indra/newview/skins/default/xui/tr/panel_nearby_chat.xml
@@ -1,4 +1,8 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="nearby_chat">
-	<check_box label="Sohbeti çevir" name="translate_chat_checkbox"/>
+	<layout_stack name="stack">
+		<layout_panel name="translate_chat_checkbox_lp">
+			<check_box label="Sohbeti çevir" name="translate_chat_checkbox"/>
+		</layout_panel>
+	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/tr/panel_status_bar.xml b/indra/newview/skins/default/xui/tr/panel_status_bar.xml
index 81c304a5d80203d90663c9de7cbcc5573ae6d18c..178cbda4a2e000d64c0c39e08b3065417b09e91a 100644
--- a/indra/newview/skins/default/xui/tr/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/tr/panel_status_bar.xml
@@ -15,7 +15,7 @@
 	<panel.string name="buycurrencylabel">
 		L$ [AMT]
 	</panel.string>
-	<panel name="balance_bg">
+	<panel left="-425" name="balance_bg" width="215">
 		<text name="balance" tool_tip="L$ bakiyenizi yenilemek için buraya tıklayın" value="L$20"/>
 		<button label="L$ Satın Al" name="buyL" tool_tip="Daha fazla L$ satın almak için tıklayın"/>
 		<button label="Alışveriş yap" name="goShop" tool_tip="Second Life Pazaryeri Aç" width="95"/>
diff --git a/indra/newview/skins/default/xui/tr/sidepanel_inventory.xml b/indra/newview/skins/default/xui/tr/sidepanel_inventory.xml
index f801cc5968b3799ed0b5d8d70c69c00552c0fa39..938b5a76d83ca146acae7cc8d065d2a60d40928c 100644
--- a/indra/newview/skins/default/xui/tr/sidepanel_inventory.xml
+++ b/indra/newview/skins/default/xui/tr/sidepanel_inventory.xml
@@ -14,7 +14,7 @@
 					<text name="inbox_fresh_new_count">
 						[NUM] yeni
 					</text>
-					<panel tool_tip="Drag and drop items to your inventory to manage and use them">
+					<panel name="inbox_inventory_placeholder_panel" tool_tip="Öğeleri kullanmak için bunları sürükleyin ve envanterinize bırakın">
 						<text name="inbox_inventory_placeholder">
 							Pazaryerinden satın alınan öğeler buraya teslim edilir.
 						</text>
diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml
index 199fa06d4fc0efaef08a6a9bfaee81f0c51b2ceb..2a4e2c20a7e6d83eeb1c0fcddac7c1a0ecffa97f 100644
--- a/indra/newview/skins/default/xui/tr/strings.xml
+++ b/indra/newview/skins/default/xui/tr/strings.xml
@@ -832,6 +832,9 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin.
 	<string name="anim_yes_head">
 		Evet
 	</string>
+	<string name="multiple_textures">
+		Birden Çok
+	</string>
 	<string name="texture_loading">
 		Yükleniyor...
 	</string>
@@ -1232,7 +1235,7 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin.
 		Envanterinizde bu dokunun kopyası yok
 	</string>
 	<string name="InventoryInboxNoItems">
-		Özel hediyeler gibi aldığınız belirli öğeler burada görünecektir.  Daha sonra bunları envanterinize sürükleyebilirsiniz.
+		Pazaryerinda satın aldıklarınız burada görünecektir. Bunları kullanmak için envanterinize sürükleyebilirsiniz.
 	</string>
 	<string name="MarketplaceURL">
 		https://marketplace.[MARKETPLACE_DOMAIN_NAME]/
@@ -3918,6 +3921,9 @@ Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun.
 	<string name="Saved_message">
 		(Kaydedildi [LONG_TIMESTAMP])
 	</string>
+	<string name="IM_unblock_only_groups_friends">
+		Bu mesajı görmek için Tercihler/Gizlilik&apos;de &apos;Sadece arkadaşlar ve gruplar beni arasın veya Aİ göndersin&apos; seçeneğinin işaretini kaldırmalısınız.
+	</string>
 	<string name="answered_call">
 		Aramanız yanıtlandı
 	</string>
diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp
index 3faddc13c1bcf47cf20d86e2060cd8d5a8f3260b..f8923b986814dcd4c80692859e06b6a08d6d7f34 100644
--- a/indra/newview/tests/llviewerassetstats_test.cpp
+++ b/indra/newview/tests/llviewerassetstats_test.cpp
@@ -35,6 +35,31 @@
 #include "lluuid.h"
 #include "llsdutil.h"
 #include "llregionhandle.h"
+#include "../llvoavatar.h"
+
+void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts)
+{
+	counts.resize(3);
+	counts[0] = 0;
+	counts[1] = 0;
+	counts[2] = 1;
+}
+
+// static
+std::string LLVOAvatar::rezStatusToString(S32 rez_status)
+{
+	if (rez_status==0) return "cloud";
+	if (rez_status==1) return "gray";
+	if (rez_status==2) return "textured";
+	return "unknown";
+}
+
+// static
+LLViewerStats::StatsAccumulator& LLViewerStats::PhaseMap::getPhaseStats(const std::string& phase_name)
+{
+	static LLViewerStats::StatsAccumulator junk;
+	return junk;
+}
 
 static const char * all_keys[] = 
 {
@@ -104,18 +129,25 @@ is_single_key_map(const LLSD & sd, const std::string & key)
 {
 	return sd.isMap() && 1 == sd.size() && sd.has(key);
 }
-#endif
 
 static bool
 is_double_key_map(const LLSD & sd, const std::string & key1, const std::string & key2)
 {
 	return sd.isMap() && 2 == sd.size() && sd.has(key1) && sd.has(key2);
 }
+#endif
+
+static bool
+is_triple_key_map(const LLSD & sd, const std::string & key1, const std::string & key2, const std::string& key3)
+{
+	return sd.isMap() && 3 == sd.size() && sd.has(key1) && sd.has(key2) && sd.has(key3);
+}
+
 
 static bool
 is_no_stats_map(const LLSD & sd)
 {
-	return is_double_key_map(sd, "duration", "regions");
+	return is_triple_key_map(sd, "duration", "regions", "avatar");
 }
 
 static bool
@@ -226,7 +258,7 @@ namespace tut
 		// Once the region is set, we will get a response even with no data collection
 		it->setRegion(region1_handle);
 		sd_full = it->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd_full, "duration", "regions"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd_full, "duration", "regions", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd_full["regions"], region1_handle));
 		
 		LLSD sd = sd_full["regions"][0];
@@ -267,7 +299,7 @@ namespace tut
 		it->setRegion(region1_handle);
 		
 		LLSD sd = it->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd[0];
 		
@@ -292,7 +324,7 @@ namespace tut
 		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
 
 		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd["regions"][0];
 		
@@ -332,7 +364,7 @@ namespace tut
 		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
 		ensure("Other collector is empty", is_no_stats_map(sd));
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = sd["regions"][0];
 		
@@ -382,7 +414,7 @@ namespace tut
 
 		// std::cout << sd << std::endl;
 		
-		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		ensure("Correct double-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle));
 		LLSD sd1 = get_region(sd, region1_handle);
 		LLSD sd2 = get_region(sd, region2_handle);
@@ -405,7 +437,7 @@ namespace tut
 		// Reset leaves current region in place
 		gViewerAssetStatsMain->reset();
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
 		sd2 = sd["regions"][0];
 		
@@ -454,7 +486,7 @@ namespace tut
 
 		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
 
-		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		ensure("Correct double-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct double-slot LLSD array regions", is_double_slot_array(sd["regions"], region1_handle, region2_handle));
 		LLSD sd1 = get_region(sd, region1_handle);
 		LLSD sd2 = get_region(sd, region2_handle);
@@ -477,7 +509,7 @@ namespace tut
 		// Reset leaves current region in place
 		gViewerAssetStatsMain->reset();
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "duration", "regions", "avatar"));
 		ensure("Correct single-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
 		sd2 = get_region(sd, region2_handle);
 		ensure("Region2 is present in results", sd2.isMap());
@@ -523,7 +555,7 @@ namespace tut
 		LLSD sd = gViewerAssetStatsThread1->asLLSD(false);
 		ensure("Other collector is empty", is_no_stats_map(sd));
 		sd = gViewerAssetStatsMain->asLLSD(false);
-		ensure("Correct single-key LLSD map root", is_double_key_map(sd, "regions", "duration"));
+		ensure("Correct single-key LLSD map root", is_triple_key_map(sd, "regions", "duration", "avatar"));
 		ensure("Correct single-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
 		sd = get_region(sd, region1_handle);
 		ensure("Region1 is present in results", sd.isMap());
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 9bf755c8f8eba58148ed53617becf13039353ef4..36267803e3e99360e8222e8a2187927018a44441 100644
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -1025,45 +1025,47 @@ class Linux_i686Manifest(LinuxManifest):
         super(Linux_i686Manifest, self).construct()
 
         if self.prefix("../packages/lib/release", dst="lib"):
-            self.path("libapr-1.so")
-            self.path("libapr-1.so.0")
-            self.path("libapr-1.so.0.4.2")
-            self.path("libaprutil-1.so")
-            self.path("libaprutil-1.so.0")
-            self.path("libaprutil-1.so.0.3.10")
-            self.path("libbreakpad_client.so.0.0.0")
-            self.path("libbreakpad_client.so.0")
-            self.path("libbreakpad_client.so")
+            self.path("libapr-1.so*")
+            self.path("libaprutil-1.so*")
+            self.path("libbreakpad_client.so*")
             self.path("libcollada14dom.so")
-            self.path("libdb-5.1.so")
-            self.path("libdb-5.so")
-            self.path("libdb.so")
-            self.path("libcrypto.so.1.0.0")
-            self.path("libexpat.so.1.5.2")
+            self.path("libdb*.so")
+            self.path("libcrypto.so.*")
+            self.path("libexpat.so.*")
             self.path("libssl.so.1.0.0")
             self.path("libglod.so")
             self.path("libminizip.so")
-            self.path("libuuid.so")
-            self.path("libuuid.so.16")
-            self.path("libuuid.so.16.0.22")
-            self.path("libSDL-1.2.so.0.11.3")
-            self.path("libSDL-1.2.so.0")
-            self.path("libdirectfb-1.4.so.5.0.4")
+            self.path("libuuid.so*")
+            self.path("libSDL-1.2.so.*")
+            self.path("libdirectfb-1.*.so.*")
+            self.path("libfusion-1.*.so.*")
+            self.path("libdirect-1.*.so.*")
+            self.path("libopenjpeg.so*")
             self.path("libdirectfb-1.4.so.5")
-            self.path("libfusion-1.4.so.5.0.4")
             self.path("libfusion-1.4.so.5")
-            self.path("libdirect-1.4.so.5.0.4")
             self.path("libdirect-1.4.so.5")
-            self.path("libopenjpeg.so.1.4.0")
-            self.path("libopenjpeg.so.1")
-            self.path("libopenjpeg.so")
             self.path("libalut.so")
             self.path("libopenal.so", "libopenal.so.1")
             self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname
-            self.path("libfontconfig.so.1.4.4")
-            self.path("libtcmalloc.so", "libtcmalloc.so") #formerly called google perf tools
-            self.path("libtcmalloc.so.0", "libtcmalloc.so.0") #formerly called google perf tools
-            self.path("libtcmalloc.so.0.1.0", "libtcmalloc.so.0.1.0") #formerly called google perf tools
+            # KLUDGE: As of 2012-04-11, the 'fontconfig' package installs
+            # libfontconfig.so.1.4.4, along with symlinks libfontconfig.so.1
+            # and libfontconfig.so. Before we added support for library-file
+            # wildcards, though, this self.path() call specifically named
+            # libfontconfig.so.1.4.4 WITHOUT also copying the symlinks. When I
+            # (nat) changed the call to self.path("libfontconfig.so.*"), we
+            # ended up with the libfontconfig.so.1 symlink in the target
+            # directory as well. But guess what! At least on Ubuntu 10.04,
+            # certain viewer fonts look terrible with libfontconfig.so.1
+            # present in the target directory. Removing that symlink suffices
+            # to improve them. I suspect that means we actually do better when
+            # the viewer fails to find our packaged libfontconfig.so*, falling
+            # back on the system one instead -- but diagnosing and fixing that
+            # is a bit out of scope for the present project. Meanwhile, this
+            # particular wildcard specification gets us exactly what the
+            # previous call did, without having to explicitly state the
+            # version number.
+            self.path("libfontconfig.so.*.*")
+            self.path("libtcmalloc.so*") #formerly called google perf tools
             try:
                     self.path("libfmod-3.75.so")
                     pass
diff --git a/indra/test/catch_and_store_what_in.h b/indra/test/catch_and_store_what_in.h
new file mode 100644
index 0000000000000000000000000000000000000000..59f8cc008585769dfe250e4c4ddde6059129cd43
--- /dev/null
+++ b/indra/test/catch_and_store_what_in.h
@@ -0,0 +1,86 @@
+/**
+ * @file   catch_and_store_what_in.h
+ * @author Nat Goodspeed
+ * @date   2012-02-15
+ * @brief  CATCH_AND_STORE_WHAT_IN() macro
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_CATCH_AND_STORE_WHAT_IN_H)
+#define LL_CATCH_AND_STORE_WHAT_IN_H
+
+/**
+ * Idiom useful for test programs: catch an expected exception, store its
+ * what() string in a specified std::string variable. From there the caller
+ * can do things like:
+ * @code
+ * ensure("expected exception not thrown", ! string.empty());
+ * @endcode
+ * or
+ * @code
+ * ensure_contains("exception doesn't mention blah", string, "blah");
+ * @endcode
+ * etc.
+ *
+ * The trouble is that when linking to a dynamic libllcommon.so on Linux, we
+ * generally fail to catch the specific exception. Oddly, we can catch it as
+ * std::runtime_error and validate its typeid().name(), so we do -- but that's
+ * a lot of boilerplate per test. Encapsulate with this macro. Usage:
+ *
+ * @code
+ * std::string threw;
+ * try
+ * {
+ *     some_call_that_should_throw_Foo();
+ * }
+ * CATCH_AND_STORE_WHAT_IN(threw, Foo)
+ * ensure("some_call_that_should_throw_Foo() didn't throw", ! threw.empty());
+ * @endcode
+ */
+#define CATCH_AND_STORE_WHAT_IN(THREW, EXCEPTION)	\
+catch (const EXCEPTION& ex)							\
+{													\
+	(THREW) = ex.what();							\
+}													\
+CATCH_MISSED_LINUX_EXCEPTION(THREW, EXCEPTION)
+
+#ifndef LL_LINUX
+#define CATCH_MISSED_LINUX_EXCEPTION(THREW, EXCEPTION)					\
+	/* only needed on Linux */
+#else // LL_LINUX
+
+#define CATCH_MISSED_LINUX_EXCEPTION(THREW, EXCEPTION)					\
+catch (const std::runtime_error& ex)									\
+{																		\
+	/* This clause is needed on Linux, on the viewer side, because	*/	\
+	/* the exception isn't caught by catch (const EXCEPTION&).		*/	\
+	/* But if the expected exception was thrown, allow the test to	*/	\
+	/* succeed anyway. Not sure how else to handle this odd case.	*/	\
+	if (std::string(typeid(ex).name()) == typeid(EXCEPTION).name())		\
+	{																	\
+		/* std::cerr << "Caught " << typeid(ex).name() */				\
+		/*			 << " with Linux workaround" << std::endl; */		\
+		(THREW) = ex.what();											\
+		/*std::cout << ex.what() << std::endl;*/						\
+	}																	\
+	else																\
+	{																	\
+		/* We don't even recognize this exception. Let it propagate	*/	\
+		/* out to TUT to fail the test.								*/	\
+		throw;															\
+	}																	\
+}																		\
+catch (...)																\
+{																		\
+	std::cerr << "Failed to catch expected exception "					\
+			  << #EXCEPTION << "!" << std::endl;						\
+	/* This indicates a problem in the test that should be addressed. */ \
+	throw;																\
+}
+
+#endif // LL_LINUX
+
+#endif /* ! defined(LL_CATCH_AND_STORE_WHAT_IN_H) */
diff --git a/indra/test/llevents_tut.cpp b/indra/test/llevents_tut.cpp
index 4699bb1827199325dea86e9dfe2d1b1c296ad600..a9114075fc944467e18e6afec3cfbf2eddc046b9 100644
--- a/indra/test/llevents_tut.cpp
+++ b/indra/test/llevents_tut.cpp
@@ -49,46 +49,12 @@
 #include <boost/assign/list_of.hpp>
 // other Linden headers
 #include "lltut.h"
+#include "catch_and_store_what_in.h"
 #include "stringize.h"
 #include "tests/listener.h"
 
 using boost::assign::list_of;
 
-#ifdef LL_LINUX
-#define CATCH_MISSED_LINUX_EXCEPTION(exception, threw)										\
-catch (const std::runtime_error& ex)														\
-{																							\
-	/* This clause is needed on Linux, on the viewer side, because the	*/					\
-	/* exception isn't caught by the clause above. Warn the user...		*/					\
-	std::cerr << "Failed to catch " << typeid(ex).name() << std::endl;						\
-	/* But if the expected exception was thrown, allow the test to		*/					\
-	/* succeed anyway. Not sure how else to handle this odd case.		*/					\
-	/* This approach is also used in llsdmessage_test.cpp. 				*/					\
-	if (std::string(typeid(ex).name()) == typeid(exception).name())							\
-	{																						\
-		threw = ex.what();																	\
-		/*std::cout << ex.what() << std::endl;*/											\
-	}																						\
-	else																					\
-	{																						\
-		/* We don't even recognize this exception. Let it propagate		*/					\
-		/* out to TUT to fail the test.									*/					\
-		throw;																				\
-	}																						\
-}																							\
-catch (...)																					\
-{																							\
-	std::cerr << "Utterly failed to catch expected exception " << #exception << "!" <<		\
-	std::endl;																				\
-	/* This indicates a problem in the test that should be addressed.   */					\
-	throw;																					\
-}
-
-#else // LL_LINUX
-#define CATCH_MISSED_LINUX_EXCEPTION(exception, threw)										\
-	/* Not needed on other platforms */
-#endif // LL_LINUX
-
 template<typename T>
 T make(const T& value)
 {
@@ -178,11 +144,7 @@ void events_object::test<1>()
 		per_frame.listen(listener0.getName(), // note bug, dup name
 						 boost::bind(&Listener::call, boost::ref(listener1), _1));
 	}
-	catch (const LLEventPump::DupListenerName& e)
-	{
-		threw = e.what();
-	}
-	CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::DupListenerName, threw)
+	CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::DupListenerName)
 	ensure_equals(threw,
 				  std::string("DupListenerName: "
 							  "Attempt to register duplicate listener name '") +
@@ -354,7 +316,6 @@ void events_object::test<7>()
 {
 	set_test_name("listener dependency order");
 	typedef LLEventPump::NameList NameList;
-	typedef Collect::StringList StringList;
 	LLEventPump& button(pumps.obtain("button"));
 	Collect collector;
 	button.listen("Mary",
@@ -368,7 +329,7 @@ void events_object::test<7>()
 	button.listen("spot",
 				  boost::bind(&Collect::add, boost::ref(collector), "spot", _1));
 	button.post(1);
-	ensure_equals(collector.result, make<StringList>(list_of("spot")("checked")("Mary")));
+	ensure_equals(collector.result, make<StringVec>(list_of("spot")("checked")("Mary")));
 	collector.clear();
 	button.stopListening("Mary");
 	button.listen("Mary",
@@ -377,7 +338,7 @@ void events_object::test<7>()
 			// now "Mary" must come before "spot"
 			make<NameList>(list_of("spot")));
 	button.post(2);
-	ensure_equals(collector.result, make<StringList>(list_of("Mary")("spot")("checked")));
+	ensure_equals(collector.result, make<StringVec>(list_of("Mary")("spot")("checked")));
 	collector.clear();
 	button.stopListening("spot");
 	std::string threw;
@@ -388,12 +349,7 @@ void events_object::test<7>()
 					  // after "Mary" and "checked" -- whoops!
 			 		  make<NameList>(list_of("Mary")("checked")));
 	}
-	catch (const LLEventPump::Cycle& e)
-	{
-		threw = e.what();
-		// std::cout << "Caught: " << e.what() << '\n';
-	}
-	CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::Cycle, threw)
+	CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::Cycle)
 	// Obviously the specific wording of the exception text can
 	// change; go ahead and change the test to match.
 	// Establish that it contains:
@@ -416,7 +372,7 @@ void events_object::test<7>()
 				  boost::bind(&Collect::add, boost::ref(collector), "shoelaces", _1),
 				  make<NameList>(list_of("checked")));
 	button.post(3);
-	ensure_equals(collector.result, make<StringList>(list_of("Mary")("checked")("yellow")("shoelaces")));
+	ensure_equals(collector.result, make<StringVec>(list_of("Mary")("checked")("yellow")("shoelaces")));
 	collector.clear();
 	threw.clear();
 	try
@@ -426,12 +382,7 @@ void events_object::test<7>()
 					  make<NameList>(list_of("shoelaces")),
 					  make<NameList>(list_of("yellow")));
 	}
-	catch (const LLEventPump::OrderChange& e)
-	{
-		threw = e.what();
-		// std::cout << "Caught: " << e.what() << '\n';
-	}
-	CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::OrderChange, threw)
+	CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::OrderChange)
 	// Same remarks about the specific wording of the exception. Just
 	// ensure that it contains enough information to clarify the
 	// problem and what must be done to resolve it.
@@ -443,7 +394,7 @@ void events_object::test<7>()
 	ensure_contains("old order", threw, "was: Mary, checked, yellow, shoelaces");
 	ensure_contains("new order", threw, "now: Mary, checked, shoelaces, of, yellow");
 	button.post(4);
-	ensure_equals(collector.result, make<StringList>(list_of("Mary")("checked")("yellow")("shoelaces")));
+	ensure_equals(collector.result, make<StringVec>(list_of("Mary")("checked")("yellow")("shoelaces")));
 }
 
 template<> template<>
@@ -459,12 +410,7 @@ void events_object::test<8>()
 			// then another with a duplicate name.
 			LLEventStream bob2("bob");
 		}
-		catch (const LLEventPump::DupPumpName& e)
-		{
-			threw = e.what();
-			// std::cout << "Caught: " << e.what() << '\n';
-		}
-		CATCH_MISSED_LINUX_EXCEPTION(LLEventPump::DupPumpName, threw)
+		CATCH_AND_STORE_WHAT_IN(threw, LLEventPump::DupPumpName)
 		ensure("Caught DupPumpName", !threw.empty());
 	} 	// delete first 'bob'
 	LLEventStream bob("bob"); 		// should work, previous one unregistered
@@ -505,11 +451,7 @@ void events_object::test<9>()
 		LLListenerOrPumpName empty;
 		empty(17);
 	}
-	catch (const LLListenerOrPumpName::Empty& e)
-	{
-		threw = e.what();
-	}
-	CATCH_MISSED_LINUX_EXCEPTION(LLListenerOrPumpName::Empty, threw)
+	CATCH_AND_STORE_WHAT_IN(threw, LLListenerOrPumpName::Empty)
 
 	ensure("threw Empty", !threw.empty());
 }
diff --git a/indra/test/manageapr.h b/indra/test/manageapr.h
new file mode 100644
index 0000000000000000000000000000000000000000..2452fb6ae4f78cd762a1b44e77c11567d9fc367a
--- /dev/null
+++ b/indra/test/manageapr.h
@@ -0,0 +1,46 @@
+/**
+ * @file   manageapr.h
+ * @author Nat Goodspeed
+ * @date   2012-01-13
+ * @brief  ManageAPR class for simple test programs
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_MANAGEAPR_H)
+#define LL_MANAGEAPR_H
+
+#include "llapr.h"
+#include <boost/noncopyable.hpp>
+
+/**
+ * Declare a static instance of this class for dead-simple ll_init_apr() at
+ * program startup, ll_cleanup_apr() at termination. This is recommended for
+ * use only with simple test programs. Once you start introducing static
+ * instances of other classes that depend on APR already being initialized,
+ * the indeterminate static-constructor-order problem rears its ugly head.
+ */
+class ManageAPR: public boost::noncopyable
+{
+public:
+    ManageAPR()
+    {
+        ll_init_apr();
+    }
+
+    ~ManageAPR()
+    {
+        ll_cleanup_apr();
+    }
+
+    static std::string strerror(apr_status_t rv)
+    {
+        char errbuf[256];
+        apr_strerror(rv, errbuf, sizeof(errbuf));
+        return errbuf;
+    }
+};
+
+#endif /* ! defined(LL_MANAGEAPR_H) */
diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h
new file mode 100644
index 0000000000000000000000000000000000000000..606906462746c4adc19f54603a60534161db6c46
--- /dev/null
+++ b/indra/test/namedtempfile.h
@@ -0,0 +1,205 @@
+/**
+ * @file   namedtempfile.h
+ * @author Nat Goodspeed
+ * @date   2012-01-13
+ * @brief  NamedTempFile class for tests that need disk files as fixtures.
+ * 
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_NAMEDTEMPFILE_H)
+#define LL_NAMEDTEMPFILE_H
+
+#include "llerror.h"
+#include "llapr.h"
+#include "apr_file_io.h"
+#include <string>
+#include <boost/function.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/lambda/bind.hpp>
+#include <boost/noncopyable.hpp>
+#include <iostream>
+#include <sstream>
+
+/**
+ * Create a text file with specified content "somewhere in the
+ * filesystem," cleaning up when it goes out of scope.
+ */
+class NamedTempFile: public boost::noncopyable
+{
+    LOG_CLASS(NamedTempFile);
+public:
+    NamedTempFile(const std::string& pfx, const std::string& content, apr_pool_t* pool=gAPRPoolp):
+        mPool(pool)
+    {
+        createFile(pfx, boost::lambda::_1 << content);
+    }
+
+    // Disambiguate when passing string literal
+    NamedTempFile(const std::string& pfx, const char* content, apr_pool_t* pool=gAPRPoolp):
+        mPool(pool)
+    {
+        createFile(pfx, boost::lambda::_1 << content);
+    }
+
+    // Function that accepts an ostream ref and (presumably) writes stuff to
+    // it, e.g.:
+    // (boost::lambda::_1 << "the value is " << 17 << '\n')
+    typedef boost::function<void(std::ostream&)> Streamer;
+
+    NamedTempFile(const std::string& pfx, const Streamer& func, apr_pool_t* pool=gAPRPoolp):
+        mPool(pool)
+    {
+        createFile(pfx, func);
+    }
+
+    virtual ~NamedTempFile()
+    {
+        ll_apr_assert_status(apr_file_remove(mPath.c_str(), mPool));
+    }
+
+    virtual std::string getName() const { return mPath; }
+
+    void peep()
+    {
+        std::cout << "File '" << mPath << "' contains:\n";
+        std::ifstream reader(mPath.c_str());
+        std::string line;
+        while (std::getline(reader, line))
+            std::cout << line << '\n';
+        std::cout << "---\n";
+    }
+
+protected:
+    void createFile(const std::string& pfx, const Streamer& func)
+    {
+        // Create file in a temporary place.
+        const char* tempdir = NULL;
+        ll_apr_assert_status(apr_temp_dir_get(&tempdir, mPool));
+
+        // Construct a temp filename template in that directory.
+        char *tempname = NULL;
+        ll_apr_assert_status(apr_filepath_merge(&tempname,
+                                                tempdir,
+                                                (pfx + "XXXXXX").c_str(),
+                                                0,
+                                                mPool));
+
+        // Create a temp file from that template.
+        apr_file_t* fp = NULL;
+        ll_apr_assert_status(apr_file_mktemp(&fp,
+                                             tempname,
+                                             APR_CREATE | APR_WRITE | APR_EXCL,
+                                             mPool));
+        // apr_file_mktemp() alters tempname with the actual name. Not until
+        // now is it valid to capture as our mPath.
+        mPath = tempname;
+
+        // Write desired content.
+        std::ostringstream out;
+        // Stream stuff to it.
+        func(out);
+
+        std::string data(out.str());
+        apr_size_t writelen(data.length());
+        ll_apr_assert_status(apr_file_write(fp, data.c_str(), &writelen));
+        ll_apr_assert_status(apr_file_close(fp));
+        llassert_always(writelen == data.length());
+    }
+
+    std::string mPath;
+    apr_pool_t* mPool;
+};
+
+/**
+ * Create a NamedTempFile with a specified filename extension. This is useful
+ * when, for instance, you must be able to use the file in a Python import
+ * statement.
+ *
+ * A NamedExtTempFile actually has two different names. We retain the original
+ * no-extension name as a placeholder in the temp directory to ensure
+ * uniqueness; to that we link the name plus the desired extension. Naturally,
+ * both must be removed on destruction.
+ */
+class NamedExtTempFile: public NamedTempFile
+{
+    LOG_CLASS(NamedExtTempFile);
+public:
+    NamedExtTempFile(const std::string& ext, const std::string& content, apr_pool_t* pool=gAPRPoolp):
+        NamedTempFile(remove_dot(ext), content, pool),
+        mLink(mPath + ensure_dot(ext))
+    {
+        linkto(mLink);
+    }
+
+    // Disambiguate when passing string literal
+    NamedExtTempFile(const std::string& ext, const char* content, apr_pool_t* pool=gAPRPoolp):
+        NamedTempFile(remove_dot(ext), content, pool),
+        mLink(mPath + ensure_dot(ext))
+    {
+        linkto(mLink);
+    }
+
+    NamedExtTempFile(const std::string& ext, const Streamer& func, apr_pool_t* pool=gAPRPoolp):
+        NamedTempFile(remove_dot(ext), func, pool),
+        mLink(mPath + ensure_dot(ext))
+    {
+        linkto(mLink);
+    }
+
+    virtual ~NamedExtTempFile()
+    {
+        ll_apr_assert_status(apr_file_remove(mLink.c_str(), mPool));
+    }
+
+    // Since the caller has gone to the trouble to create the name with the
+    // extension, that should be the name we return. In this class, mPath is
+    // just a placeholder to ensure that future createFile() calls won't
+    // collide.
+    virtual std::string getName() const { return mLink; }
+
+    static std::string ensure_dot(const std::string& ext)
+    {
+        if (ext.empty())
+        {
+            // What SHOULD we do when the caller makes a point of using
+            // NamedExtTempFile to generate a file with a particular
+            // extension, then passes an empty extension? Use just "."? That
+            // sounds like a Bad Idea, especially on Windows. Treat that as a
+            // coding error.
+            LL_ERRS("NamedExtTempFile") << "passed empty extension" << LL_ENDL;
+        }
+        if (ext[0] == '.')
+        {
+            return ext;
+        }
+        return std::string(".") + ext;
+    }
+
+    static std::string remove_dot(const std::string& ext)
+    {
+        std::string::size_type found = ext.find_first_not_of(".");
+        if (found == std::string::npos)
+        {
+            return ext;
+        }
+        return ext.substr(found);
+    }
+
+private:
+    void linkto(const std::string& path)
+    {
+        // This method assumes that since mPath (without extension) is
+        // guaranteed by apr_file_mktemp() to be unique, then (mPath + any
+        // extension) is also unique. This is likely, though not guaranteed:
+        // files could be created in the same temp directory other than by
+        // this class.
+        ll_apr_assert_status(apr_file_link(mPath.c_str(), path.c_str()));
+    }
+
+    std::string mLink;
+};
+
+#endif /* ! defined(LL_NAMEDTEMPFILE_H) */
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index e58e7293fb754e544c5f2b6f927e2bb118d3e087..9d24383bcc754b4e7f74f5fc6be7e4c76e25671e 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -37,7 +37,9 @@
 #include "linden_common.h"
 #include "llerrorcontrol.h"
 #include "lltut.h"
+#include "tests/wrapllerrs.h"             // RecorderProxy
 #include "stringize.h"
+#include "namedtempfile.h"
 
 #include "apr_pools.h"
 #include "apr_getopt.h"
@@ -70,25 +72,91 @@
 #include <boost/foreach.hpp>
 #include <boost/lambda/lambda.hpp>
 
+#include <fstream>
+
+void wouldHaveCrashed(const std::string& message);
+
 namespace tut
 {
 	std::string sSourceDir;
-	
-    test_runner_singleton runner;
+
+	test_runner_singleton runner;
 }
 
+class LLReplayLog
+{
+public:
+	LLReplayLog() {}
+	virtual ~LLReplayLog() {}
+
+	virtual void reset() {}
+	virtual void replay(std::ostream&) {}
+};
+
+class LLReplayLogReal: public LLReplayLog, public LLError::Recorder, public boost::noncopyable
+{
+public:
+	LLReplayLogReal(LLError::ELevel level, apr_pool_t* pool):
+		mOldSettings(LLError::saveAndResetSettings()),
+		mProxy(new RecorderProxy(this)),
+		mTempFile("log", "", pool),		// create file
+		mFile(mTempFile.getName().c_str()) // open it
+	{
+		LLError::setFatalFunction(wouldHaveCrashed);
+		LLError::setDefaultLevel(level);
+		LLError::addRecorder(mProxy);
+	}
+
+	virtual ~LLReplayLogReal()
+	{
+		LLError::removeRecorder(mProxy);
+		delete mProxy;
+		LLError::restoreSettings(mOldSettings);
+	}
+
+	virtual void recordMessage(LLError::ELevel level, const std::string& message)
+	{
+		mFile << message << std::endl;
+	}
+
+	virtual void reset()
+	{
+		mFile.close();
+		mFile.open(mTempFile.getName().c_str());
+	}
+
+	virtual void replay(std::ostream& out)
+	{
+		mFile.close();
+		std::ifstream inf(mTempFile.getName().c_str());
+		std::string line;
+		while (std::getline(inf, line))
+		{
+			out << line << std::endl;
+		}
+	}
+
+private:
+	LLError::Settings* mOldSettings;
+	LLError::Recorder* mProxy;
+	NamedTempFile mTempFile;
+	std::ofstream mFile;
+};
+
 class LLTestCallback : public tut::callback
 {
 public:
-	LLTestCallback(bool verbose_mode, std::ostream *stream) :
-	mVerboseMode(verbose_mode),
-	mTotalTests(0),
-	mPassedTests(0),
-	mFailedTests(0),
-	mSkippedTests(0),
-	// By default, capture a shared_ptr to std::cout, with a no-op "deleter"
-	// so that destroying the shared_ptr makes no attempt to delete std::cout.
-	mStream(boost::shared_ptr<std::ostream>(&std::cout, boost::lambda::_1))
+	LLTestCallback(bool verbose_mode, std::ostream *stream,
+				   boost::shared_ptr<LLReplayLog> replayer) :
+		mVerboseMode(verbose_mode),
+		mTotalTests(0),
+		mPassedTests(0),
+		mFailedTests(0),
+		mSkippedTests(0),
+		// By default, capture a shared_ptr to std::cout, with a no-op "deleter"
+		// so that destroying the shared_ptr makes no attempt to delete std::cout.
+		mStream(boost::shared_ptr<std::ostream>(&std::cout, boost::lambda::_1)),
+		mReplayer(replayer)
 	{
 		if (stream)
 		{
@@ -126,6 +194,16 @@ public:
 	virtual void test_completed(const tut::test_result& tr)
 	{
 		++mTotalTests;
+
+		// If this test failed, dump requested log messages BEFORE stating the
+		// test result.
+		if (tr.result != tut::test_result::ok && tr.result != tut::test_result::skip)
+		{
+			mReplayer->replay(*mStream);
+		}
+		// Either way, clear stored messages in preparation for next test.
+		mReplayer->reset();
+
 		std::ostringstream out;
 		out << "[" << tr.group << ", " << tr.test;
 		if (! tr.name.empty())
@@ -206,6 +284,7 @@ protected:
 	int mFailedTests;
 	int mSkippedTests;
 	boost::shared_ptr<std::ostream> mStream;
+	boost::shared_ptr<LLReplayLog> mReplayer;
 };
 
 // TeamCity specific class which emits service messages
@@ -214,8 +293,9 @@ protected:
 class LLTCTestCallback : public LLTestCallback
 {
 public:
-	LLTCTestCallback(bool verbose_mode, std::ostream *stream) :
-		LLTestCallback(verbose_mode, stream)
+	LLTCTestCallback(bool verbose_mode, std::ostream *stream,
+					 boost::shared_ptr<LLReplayLog> replayer) :
+		LLTestCallback(verbose_mode, stream, replayer)
 	{
 	}
 
@@ -356,6 +436,14 @@ void stream_usage(std::ostream& s, const char* app)
 		++option;
 	}
 
+	s << app << " is also sensitive to environment variables:\n"
+	  << "LOGTEST=level : for all tests, emit log messages at level 'level'\n"
+	  << "LOGFAIL=level : only for failed tests, emit log messages at level 'level'\n"
+	  << "where 'level' is one of ALL, DEBUG, INFO, WARN, ERROR, NONE.\n"
+	  << "--debug is like LOGTEST=DEBUG, but --debug overrides LOGTEST.\n"
+	  << "Setting LOGFAIL overrides both LOGTEST and --debug: the only log\n"
+	  << "messages you will see will be for failed tests.\n\n";
+
 	s << "Examples:" << std::endl;
 	s << "  " << app << " --verbose" << std::endl;
 	s << "\tRun all the tests and report all results." << std::endl;
@@ -392,8 +480,14 @@ int main(int argc, char **argv)
 	LLError::initForApplication(".");
 	LLError::setFatalFunction(wouldHaveCrashed);
 	LLError::setDefaultLevel(LLError::LEVEL_ERROR);
-	//< *TODO: should come from error config file. Note that we
-	// have a command line option that sets this to debug.
+	// ^ possibly overridden by --debug, LOGTEST or LOGFAIL
+
+	// LOGTEST overrides default, but can be overridden by --debug or LOGFAIL.
+	const char* LOGTEST = getenv("LOGTEST");
+	if (LOGTEST)
+	{
+		LLError::setDefaultLevel(LLError::decodeLevel(LOGTEST));
+	}
 
 #ifdef CTYPE_WORKAROUND
 	ctype_workaround();
@@ -468,8 +562,6 @@ int main(int argc, char **argv)
 				wait_at_exit = true;
 				break;
 			case 'd':
-				// *TODO: should come from error config file. We set it to
-				// ERROR by default, so this allows full debug levels.
 				LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
 				break;
 			case 'x':
@@ -484,14 +576,28 @@ int main(int argc, char **argv)
 
 	// run the tests
 
+	const char* LOGFAIL = getenv("LOGFAIL");
+	boost::shared_ptr<LLReplayLog> replayer;
+	// As described in stream_usage(), LOGFAIL overrides both --debug and
+	// LOGTEST.
+	if (LOGFAIL)
+	{
+		LLError::ELevel level = LLError::decodeLevel(LOGFAIL);
+		replayer.reset(new LLReplayLogReal(level, pool));
+	}
+	else
+	{
+		replayer.reset(new LLReplayLog());
+	}
+
 	LLTestCallback* mycallback;
 	if (getenv("TEAMCITY_PROJECT_NAME"))
 	{
-		mycallback = new LLTCTestCallback(verbose_mode, output.get());		
+		mycallback = new LLTCTestCallback(verbose_mode, output.get(), replayer);
 	}
 	else
 	{
-		mycallback = new LLTestCallback(verbose_mode, output.get());
+		mycallback = new LLTestCallback(verbose_mode, output.get(), replayer);
 	}
 
 	tut::runner.get().set_callback(mycallback);
diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp
index 19ac418e9efb197982fc03adbc61fba8bee3a6f6..75e455e3f6450c62070006badf357c6bb2e2ee95 100644
--- a/indra/viewer_components/updater/llupdatedownloader.cpp
+++ b/indra/viewer_components/updater/llupdatedownloader.cpp
@@ -1,24 +1,24 @@
-/** 
+/**
  * @file llupdatedownloader.cpp
  *
  * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  * Second Life Viewer Source Code
  * Copyright (C) 2010, 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$
  */
@@ -58,7 +58,7 @@ public:
 	int onProgress(double downloadSize, double bytesDownloaded);
 	void resume(void);
 	void setBandwidthLimit(U64 bytesPerSecond);
-	
+
 private:
 	curl_off_t mBandwidthLimit;
 	bool mCancelled;
@@ -69,7 +69,7 @@ private:
 	unsigned char mDownloadPercent;
 	std::string mDownloadRecordPath;
 	curl_slist * mHeaderList;
-	
+
 	void initializeCurlGet(std::string const & url, bool processHeader);
 	void resumeDownloading(size_t startByte);
 	void run(void);
@@ -93,7 +93,7 @@ namespace {
 		}
 	};
 
-		
+
 	const char * gSecondLifeUpdateRecord = "SecondLifeUpdateDownload.xml";
 };
 
@@ -192,18 +192,18 @@ LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client &
 	mHeaderList(0)
 {
 	CURLcode code = curl_global_init(CURL_GLOBAL_ALL); // Just in case.
-	llverify(code == CURLE_OK); // TODO: real error handling here. 
+	llverify(code == CURLE_OK); // TODO: real error handling here.
 }
 
 
 LLUpdateDownloader::Implementation::~Implementation()
 {
-	if(isDownloading()) 
+	if(isDownloading())
 	{
 		cancel();
 		shutdown();
-	} 
-	else 
+	}
+	else
 	{
 		; // No op.
 	}
@@ -218,7 +218,7 @@ void LLUpdateDownloader::Implementation::cancel(void)
 {
 	mCancelled = true;
 }
-	
+
 
 void LLUpdateDownloader::Implementation::download(LLURI const & uri,
 												  std::string const & hash,
@@ -259,24 +259,24 @@ void LLUpdateDownloader::Implementation::resume(void)
 		mClient.downloadError("no download marker");
 		return;
 	}
-	
+
 	LLSDSerialize::fromXMLDocument(mDownloadData, dataStream);
-	
+
 	if(!mDownloadData.asBoolean()) {
 		mClient.downloadError("no download information in marker");
 		return;
 	}
-	
+
 	std::string filePath = mDownloadData["path"].asString();
 	try {
-		if(LLFile::isfile(filePath)) {		
+		if(LLFile::isfile(filePath)) {
 			llstat fileStatus;
 			LLFile::stat(filePath, &fileStatus);
 			if(fileStatus.st_size != mDownloadData["size"].asInteger()) {
 				resumeDownloading(fileStatus.st_size);
 			} else if(!validateDownload()) {
 				LLFile::remove(filePath);
-				download(LLURI(mDownloadData["url"].asString()), 
+				download(LLURI(mDownloadData["url"].asString()),
 						 mDownloadData["hash"].asString(),
 						 mDownloadData["update_version"].asString(),
 						 mDownloadData["required"].asBoolean());
@@ -284,7 +284,7 @@ void LLUpdateDownloader::Implementation::resume(void)
 				mClient.downloadComplete(mDownloadData);
 			}
 		} else {
-			download(LLURI(mDownloadData["url"].asString()), 
+			download(LLURI(mDownloadData["url"].asString()),
 					 mDownloadData["hash"].asString(),
 					 mDownloadData["update_version"].asString(),
 					 mDownloadData["required"].asBoolean());
@@ -301,7 +301,7 @@ void LLUpdateDownloader::Implementation::setBandwidthLimit(U64 bytesPerSecond)
 		llassert(mCurl != 0);
 		mBandwidthLimit = bytesPerSecond;
 		CURLcode code = curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, &mBandwidthLimit);
-		if(code != CURLE_OK) LL_WARNS("UpdateDownload") << 
+		if(code != CURLE_OK) LL_WARNS("UpdateDownload") <<
 			"unable to change dowload bandwidth" << LL_ENDL;
 	} else {
 		mBandwidthLimit = bytesPerSecond;
@@ -315,7 +315,7 @@ size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
 	std::string header(headerPtr, headerPtr + size);
 	size_t colonPosition = header.find(':');
 	if(colonPosition == std::string::npos) return size; // HTML response; ignore.
-	
+
 	if(header.substr(0, colonPosition) == "Content-Length") {
 		try {
 			size_t firstDigitPos = header.find_first_of("0123456789", colonPosition);
@@ -323,18 +323,18 @@ size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
 			std::string contentLength = header.substr(firstDigitPos, lastDigitPos - firstDigitPos + 1);
 			size_t size = boost::lexical_cast<size_t>(contentLength);
 			LL_INFOS("UpdateDownload") << "download size is " << size << LL_ENDL;
-			
+
 			mDownloadData["size"] = LLSD(LLSD::Integer(size));
 			llofstream odataStream(mDownloadRecordPath);
 			LLSDSerialize::toPrettyXML(mDownloadData, odataStream);
 		} catch (std::exception const & e) {
-			LL_WARNS("UpdateDownload") << "unable to read content length (" 
+			LL_WARNS("UpdateDownload") << "unable to read content length ("
 				<< e.what() << ")" << LL_ENDL;
 		}
 	} else {
 		; // No op.
 	}
-	
+
 	return size;
 }
 
@@ -342,9 +342,9 @@ size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
 size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size)
 {
 	if(mCancelled) return 0; // Forces a write error which will halt curl thread.
-	if((size == 0) || (buffer == 0)) return 0; 
-	
-	mDownloadStream.write(reinterpret_cast<const char *>(buffer), size);
+	if((size == 0) || (buffer == 0)) return 0;
+
+	mDownloadStream.write(static_cast<const char *>(buffer), size);
 	if(mDownloadStream.bad()) {
 		return 0;
 	} else {
@@ -358,7 +358,7 @@ int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double b
 	int downloadPercent = static_cast<int>(100. * (bytesDownloaded / downloadSize));
 	if(downloadPercent > mDownloadPercent) {
 		mDownloadPercent = downloadPercent;
-		
+
 		LLSD event;
 		event["pump"] = LLUpdaterService::pumpName();
 		LLSD payload;
@@ -367,12 +367,12 @@ int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double b
 		payload["bytes_downloaded"] = bytesDownloaded;
 		event["payload"] = payload;
 		LLEventPumps::instance().obtain("mainlooprepeater").post(event);
-		
+
 		LL_INFOS("UpdateDownload") << "progress event " << payload << LL_ENDL;
 	} else {
 		; // Keep events to a reasonalbe number.
 	}
-	
+
 	return 0;
 }
 
@@ -396,13 +396,13 @@ void LLUpdateDownloader::Implementation::run(void)
 		LL_INFOS("UpdateDownload") << "download canceled by user" << LL_ENDL;
 		// Do not call back client.
 	} else {
-		LL_WARNS("UpdateDownload") << "download failed with error '" << 
+		LL_WARNS("UpdateDownload") << "download failed with error '" <<
 			curl_easy_strerror(code) << "'" << LL_ENDL;
 		LLFile::remove(mDownloadRecordPath);
 		if(mDownloadData.has("path")) LLFile::remove(mDownloadData["path"].asString());
 		mClient.downloadError("curl error");
 	}
-	
+
 	if(mHeaderList) {
 		curl_slist_free_all(mHeaderList);
 		mHeaderList = 0;
@@ -412,17 +412,17 @@ void LLUpdateDownloader::Implementation::run(void)
 
 void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & url, bool processHeader)
 {
-	if(mCurl == 0) 
+	if(mCurl == 0)
 	{
 		mCurl = LLCurl::newEasyHandle();
-	} 
-	else 
+	}
+	else
 	{
 		curl_easy_reset(mCurl);
 	}
-	
+
 	if(mCurl == 0) throw DownloadError("failed to initialize curl");
-	
+
 	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, true));
 	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_FOLLOWLOCATION, true));
 	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &write_function));
@@ -439,7 +439,7 @@ void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & u
 	// if it's a required update set the bandwidth limit to 0 (unlimited)
 	curl_off_t limit = mDownloadData["required"].asBoolean() ? 0 : mBandwidthLimit;
 	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, limit));
-	
+
 	mDownloadPercent = 0;
 }
 
@@ -450,7 +450,7 @@ void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte)
 		<< " at byte " << startByte << LL_ENDL;
 
 	initializeCurlGet(mDownloadData["url"].asString(), false);
-	
+
 	// The header 'Range: bytes n-' will request the bytes remaining in the
 	// source begining with byte n and ending with the last byte.
 	boost::format rangeHeaderFormat("Range: bytes=%u-");
@@ -458,7 +458,7 @@ void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte)
 	mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str());
 	if(mHeaderList == 0) throw DownloadError("cannot add Range header");
 	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaderList));
-	
+
 	mDownloadStream.open(mDownloadData["path"].asString(),
 						 std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	start();
@@ -479,10 +479,10 @@ void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std
 	LL_INFOS("UpdateDownload") << "downloading " << filePath
 		<< " from " << uri.asString() << LL_ENDL;
 	LL_INFOS("UpdateDownload") << "hash of file is " << hash << LL_ENDL;
-		
+
 	llofstream dataStream(mDownloadRecordPath);
 	LLSDSerialize::toPrettyXML(mDownloadData, dataStream);
-	
+
 	mDownloadStream.open(filePath, std::ios_base::out | std::ios_base::binary);
 	initializeCurlGet(uri.asString(), true);
 	start();
diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp
index c7b70c2de8e91fcd8deec2075c3a66f9df8f8675..2f87d59373a6e8927951c6b0fbc213b1ed9db70b 100644
--- a/indra/viewer_components/updater/llupdateinstaller.cpp
+++ b/indra/viewer_components/updater/llupdateinstaller.cpp
@@ -26,10 +26,10 @@
 #include "linden_common.h"
 #include <apr_file_io.h>
 #include "llapr.h"
-#include "llprocesslauncher.h"
+#include "llprocess.h"
 #include "llupdateinstaller.h"
 #include "lldir.h" 
-
+#include "llsd.h"
 
 #if defined(LL_WINDOWS)
 #pragma warning(disable: 4702)      // disable 'unreachable code' so we can use lexical_cast (really!).
@@ -78,15 +78,13 @@ int ll_install_update(std::string const & script,
 	llinfos << "UpdateInstaller: installing " << updatePath << " using " <<
 		actualScriptPath << LL_ENDL;
 	
-	LLProcessLauncher launcher;
-	launcher.setExecutable(actualScriptPath);
-	launcher.addArgument(updatePath);
-	launcher.addArgument(ll_install_failed_marker_path().c_str());
-	launcher.addArgument(boost::lexical_cast<std::string>(required));
-	int result = launcher.launch();
-	launcher.orphan();
-	
-	return result;
+	LLProcess::Params params;
+	params.executable = actualScriptPath;
+	params.args.add(updatePath);
+	params.args.add(ll_install_failed_marker_path());
+	params.args.add(boost::lexical_cast<std::string>(required));
+	params.autokill = false;
+	return LLProcess::create(params)? 0 : -1;
 }