diff --git a/.hgtags b/.hgtags
index 1289dfdf24b23570a90b59203ecda0ed78259aeb..d7465f4c596a67bcec1bf0cdcea7d5086dae40aa 100644
--- a/.hgtags
+++ b/.hgtags
@@ -40,3 +40,5 @@ dbc206fc61d89ff4cfe15aade0bf0c7bc7fee1c9 2.4.0-start
 dc6483491b4af559060bccaef8e9045a303212dd 2.4.0-beta1
 dc6483491b4af559060bccaef8e9045a303212dd 2.4.0-beta1
 3bc1f50a72e117f4d4ad8d555f0c785ea8cc201e 2.4.0-beta1
+25bd6007e3d2fc15db9326ed4b18a24a5969a46a 2.4.0-beta2
+1ed382c6a08ba3850b6ce9061bc551ddece0ea07 2.4.0-release
diff --git a/BuildParams b/BuildParams
index e2d97211ed0a3ede28b8e00f96c094d2defed443..0a4271f7a8bba336d155de09405b4e341d3c08dd 100644
--- a/BuildParams
+++ b/BuildParams
@@ -46,9 +46,6 @@ viewer-beta.viewer_channel = "Second Life Beta Viewer"
 viewer-beta.login_channel = "Second Life Beta Viewer"
 viewer-beta.build_debug_release_separately = true
 viewer-beta.build_viewer_update_version_manager = true
-# Settings to test new code ticket service
-viewer-beta.codeticket_server_url = "http://pdp75.lindenlab.com:8000/codeticket/linden/"
-viewer-beta.codeticket_add_context = true
 
 # ========================================
 # Viewer Release
@@ -219,4 +216,22 @@ viewer-experience.public_build = false
 viewer-experience.viewer_channel = "Second Life SkyLight Viewer"
 viewer-experience.login_channel = "Second Life SkyLight Viewer"
 
+# =================================================================
+# asset delivery 2010 projects
+# =================================================================
+viewer-asset-delivery.viewer_channel = "Second Life Development"
+viewer-asset-delivery.login_channel = "Second Life Development"
+viewer-asset-delivery.build_viewer_update_version_manager = false
+viewer-asset-delivery.email = monty@lindenlab.com
+viewer-asset-delivery.build_server = false
+viewer-asset-delivery.build_server_tests = false
+
+viewer-asset-delivery-metrics.viewer_channel = "Second Life Development"
+viewer-asset-delivery-metrics.login_channel = "Second Life Development"
+viewer-asset-delivery-metrics.build_viewer_update_version_manager = false
+viewer-asset-delivery-metrics.email = monty@lindenlab.com
+viewer-asset-delivery-metrics.build_server = false
+viewer-asset-delivery-metrics.build_server_tests = false
+
+
 # eof
diff --git a/build.sh b/build.sh
index f9c6beefeded66430d40c4d0d7cd9c5c6be66bed..c5f74c23ee07ac283b76289af82d8d5fe2a2644c 100755
--- a/build.sh
+++ b/build.sh
@@ -59,10 +59,11 @@ pre_build()
     -t $variant \
     -G "$cmake_generator" \
    configure \
-	-DGRID:STRING="$viewer_grid" \
+    -DGRID:STRING="$viewer_grid" \
     -DVIEWER_CHANNEL:STRING="$viewer_channel" \
     -DVIEWER_LOGIN_CHANNEL:STRING="$login_channel" \
     -DINSTALL_PROPRIETARY:BOOL=ON \
+    -DRELEASE_CRASH_REPORTING:BOOL=ON \
     -DLOCALIZESETUP:BOOL=ON \
     -DPACKAGE:BOOL=ON \
     -DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 4c33834a467a232a26ada3f0c2668725edb30bf7..0954704c262dcf73e470909cc1b7bc57e537fdf6 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -61,19 +61,27 @@ Aimee Trescothick
 Alejandro Rosenthal
 	VWR-1184
 Aleric Inglewood
+	SNOW-240
 	SNOW-522
 	SNOW-626
 	SNOW-756
 	SNOW-764
 	VWR-10001
+	VWR-10579
 	VWR-10759
 	VWR-10837
 	VWR-12691
 	VWR-12984
 	VWR-13996
 	VWR-14426
+	VWR-24247
+	VWR-24251
+	VWR-24252
+	VWR-24254
+	VWR-24261
 	SNOW-84
 	SNOW-477
+	SNOW-744
 	SNOW-766
 	STORM-163
 Ales Beaumont
@@ -211,6 +219,8 @@ Catherine Pfeffer
 Celierra Darling
 	VWR-1274
 	VWR-6975
+Cron Stardust
+	VWR-10579
 Cypren Christenson
 	STORM-417
 Dale Glass
@@ -354,11 +364,14 @@ JB Kraft
 Joghert LeSabre
 	VWR-64
 Jonathan Yap
-	VWR-17801
+	STORM-596
+	STORM-523
 	STORM-616
 	STORM-679
-	STORM-596
+	STORM-737
 	STORM-726
+	VWR-17801
+	STORM-785
 Kage Pixel
 	VWR-11
 Ken March
@@ -372,6 +385,9 @@ Khyota Wulluf
 	VWR-9966
 Kitty Barnett
 	VWR-19699
+	STORM-288
+	STORM-799
+	STORM-800
 Kunnis Basiat
 	VWR-82
 	VWR-102
@@ -584,6 +600,7 @@ Robin Cornelius
 	STORM-422
 	VWR-2488
 	VWR-9557
+	VWR-10579
 	VWR-11128
 	VWR-12533
 	VWR-12587
@@ -607,6 +624,7 @@ Sammy Frederix
 	VWR-6186
 Satomi Ahn
 	STORM-501
+	STORM-229
 Scrippy Scofield
 	VWR-3748
 Seg Baphomet
@@ -731,6 +749,8 @@ Tue Torok
 	CT-74
 Twisted Laws
 	SNOW-352
+	STORM-466
+	STORM-467
 Vadim Bigbear
 	VWR-2681
 Vector Hastings
@@ -773,10 +793,12 @@ WolfPup Lowenhar
 	STORM-143
 	STORM-255
 	STORM-256
+	STORM-288
 	STORM-535
 	STORM-544
 	STORM-654
 	STORM-674
+	STORM-776
 	VWR-20741
 	VWR-20933
 Zai Lynch
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index d01e1869b6983dfbb1116499330bdbb783504e54..6d17a6f402efbf917fe8a24a51fa25fe70fbefb3 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -43,6 +43,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)
 add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter)
 add_subdirectory(${LIBS_OPEN_PREFIX}llcommon)
 add_subdirectory(${LIBS_OPEN_PREFIX}llimage)
+add_subdirectory(${LIBS_OPEN_PREFIX}llkdu)
 add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj)
 add_subdirectory(${LIBS_OPEN_PREFIX}llinventory)
 add_subdirectory(${LIBS_OPEN_PREFIX}llmath)
@@ -53,10 +54,6 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llvfs)
 add_subdirectory(${LIBS_OPEN_PREFIX}llwindow)
 add_subdirectory(${LIBS_OPEN_PREFIX}llxml)
 
-if (EXISTS ${LIBS_CLOSED_DIR}llkdu)
-  add_subdirectory(${LIBS_CLOSED_PREFIX}llkdu)
-endif (EXISTS ${LIBS_CLOSED_DIR}llkdu)
-
 add_subdirectory(${LIBS_OPEN_PREFIX}lscript)
 
 if (WINDOWS AND EXISTS ${LIBS_CLOSED_DIR}copy_win_scripts)
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index db2cdb5ff8fa3f41e8f41621d3067f0797d80ca0..dbe0cf5cd051eb02b865210c6c39e104fc1caa6e 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -4,27 +4,28 @@
 
 include(Variables)
 
-
 # Portable compilation flags.
-
-if (EXISTS ${CMAKE_SOURCE_DIR}/llphysics)
-  # The release build should only offer to send crash reports if we're
-  # building from a Linden internal source tree.
-  set(release_crash_reports 1)
-else (EXISTS ${CMAKE_SOURCE_DIR}/llphysics)
-  set(release_crash_reports 0) 
-endif (EXISTS ${CMAKE_SOURCE_DIR}/llphysics)
-
 set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG -DLL_DEBUG=1")
 set(CMAKE_CXX_FLAGS_RELEASE
-    "-DLL_RELEASE=1 -DLL_RELEASE_FOR_DOWNLOAD=1 -D_SECURE_SCL=0 -DLL_SEND_CRASH_REPORTS=${release_crash_reports} -DNDEBUG") 
+    "-DLL_RELEASE=1 -DLL_RELEASE_FOR_DOWNLOAD=1 -D_SECURE_SCL=0 -DNDEBUG") 
 
 set(CMAKE_CXX_FLAGS_RELWITHDEBINFO 
-    "-DLL_RELEASE=1 -D_SECURE_SCL=0 -DLL_SEND_CRASH_REPORTS=0 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1")
+    "-DLL_RELEASE=1 -D_SECURE_SCL=0 -DNDEBUG -DLL_RELEASE_WITH_DEBUG_INFO=1")
 
+# Configure crash reporting
+set(RELEASE_CRASH_REPORTING OFF CACHE BOOL "Enable use of crash reporting in release builds")
+set(NON_RELEASE_CRASH_REPORTING OFF CACHE BOOL "Enable use of crash reporting in developer builds")
 
-# Don't bother with a MinSizeRel build.
+if(RELEASE_CRASH_REPORTING)
+  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DLL_SEND_CRASH_REPORTS=1")
+endif()
+
+if(NON_RELEASE_CRASH_REPORTING)
+  set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DLL_SEND_CRASH_REPORTS=1")
+  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLL_SEND_CRASH_REPORTS=1")
+endif()  
 
+# Don't bother with a MinSizeRel build.
 set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;Debug" CACHE STRING
     "Supported build types." FORCE)
 
diff --git a/indra/cmake/BerkeleyDB.cmake b/indra/cmake/BerkeleyDB.cmake
index d98e79179d9f6c635d69473438408926901ef0ca..e3ca0fd77d62c55e08165c53ace8e9564526cf64 100644
--- a/indra/cmake/BerkeleyDB.cmake
+++ b/indra/cmake/BerkeleyDB.cmake
@@ -6,6 +6,11 @@ set(DB_FIND_REQUIRED ON)
 if (STANDALONE)
   include(FindBerkeleyDB)
 else (STANDALONE)
-  set(DB_LIBRARIES db-4.2)
+  if (LINUX)
+    # Need to add dependency pthread explicitely to support ld.gold.
+    set(DB_LIBRARIES db-4.2 pthread)
+  else (LINUX)
+    set(DB_LIBRARIES db-4.2)
+  endif (LINUX)
   set(DB_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
 endif (STANDALONE)
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index 6470836286135d173fe4287d1504bbe0817d5959..3f421b270b92507fc3295cf14a99000de2e9595c 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -33,6 +33,7 @@ set(cmake_SOURCE_FILES
     FindMySQL.cmake
     FindOpenJPEG.cmake
     FindXmlRpcEpi.cmake
+    FindZLIB.cmake
     FMOD.cmake
     FreeType.cmake
     GStreamer010Plugin.cmake
diff --git a/indra/cmake/CURL.cmake b/indra/cmake/CURL.cmake
index 6e5fed4d5285e473d121d367c4da18ee15f76b18..9aba08e573df9bc636cdc6cd59f841f12228c72f 100644
--- a/indra/cmake/CURL.cmake
+++ b/indra/cmake/CURL.cmake
@@ -10,10 +10,10 @@ else (STANDALONE)
   use_prebuilt_binary(curl)
   if (WINDOWS)
     set(CURL_LIBRARIES 
-    debug libcurld
-    optimized libcurl)
+    debug libcurld.lib
+    optimized libcurl.lib)
   else (WINDOWS)
-    set(CURL_LIBRARIES curl)
+    set(CURL_LIBRARIES libcurl.a)
   endif (WINDOWS)
   set(CURL_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include)
 endif (STANDALONE)
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index e852cf463c03ed0cb07e3e0b9ed4b0ef5d386e21..176ae9787e5523abcc07c89b1465b6a9d25b3f4c 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -60,22 +60,6 @@ if(WINDOWS)
       set(release_files ${release_files} fmod.dll)
     endif (FMOD)
 
-    #*******************************
-    # LLKDU
-    set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu")
-    if(NOT EXISTS ${internal_llkdu_path})
-        if (EXISTS "${debug_src_dir}/llkdu.dll")
-            set(debug_llkdu_src "${debug_src_dir}/llkdu.dll")
-            set(debug_llkdu_dst "${SHARED_LIB_STAGING_DIR_DEBUG}/llkdu.dll")
-        endif (EXISTS "${debug_src_dir}/llkdu.dll")
-
-        if (EXISTS "${release_src_dir}/llkdu.dll")
-            set(release_llkdu_src "${release_src_dir}/llkdu.dll")
-            set(release_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELEASE}/llkdu.dll")
-            set(relwithdebinfo_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}/llkdu.dll")
-        endif (EXISTS "${release_src_dir}/llkdu.dll")
-    endif (NOT EXISTS ${internal_llkdu_path})
-
 #*******************************
 # Copy MS C runtime dlls, required for packaging.
 # *TODO - Adapt this to support VC9
@@ -174,21 +158,6 @@ elseif(DARWIN)
     # fmod is statically linked on darwin
     set(fmod_files "")
 
-    #*******************************
-    # LLKDU
-    set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu")
-    if(NOT EXISTS ${internal_llkdu_path})
-        if (EXISTS "${debug_src_dir}/libllkdu.dylib")
-            set(debug_llkdu_src "${debug_src_dir}/libllkdu.dylib")
-            set(debug_llkdu_dst "${SHARED_LIB_STAGING_DIR_DEBUG}/libllkdu.dylib")
-        endif (EXISTS "${debug_src_dir}/libllkdu.dylib")
-
-        if (EXISTS "${release_src_dir}/libllkdu.dylib")
-            set(release_llkdu_src "${release_src_dir}/libllkdu.dylib")
-            set(release_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELEASE}/libllkdu.dylib")
-            set(relwithdebinfo_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}/libllkdu.dylib")
-        endif (EXISTS "${release_src_dir}/libllkdu.dylib")
-    endif (NOT EXISTS ${internal_llkdu_path})
 elseif(LINUX)
     # linux is weird, multiple side by side configurations aren't supported
     # and we don't seem to have any debug shared libs built yet anyways...
@@ -242,21 +211,6 @@ elseif(LINUX)
       set(release_files ${release_files} "libfmod-3.75.so")
     endif (FMOD)
 
-    #*******************************
-    # LLKDU
-    set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu")
-    if(NOT EXISTS ${internal_llkdu_path})
-        if (EXISTS "${debug_src_dir}/libllkdu.so")
-            set(debug_llkdu_src "${debug_src_dir}/libllkdu.so")
-            set(debug_llkdu_dst "${SHARED_LIB_STAGING_DIR_DEBUG}/libllkdu.so")
-        endif (EXISTS "${debug_src_dir}/libllkdu.so")
-
-        if (EXISTS "${release_src_dir}/libllkdu.so")
-            set(release_llkdu_src "${release_src_dir}/libllkdu.so")
-            set(release_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELEASE}/libllkdu.so")
-            set(relwithdebinfo_llkdu_dst "${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}/libllkdu.so")
-        endif (EXISTS "${release_src_dir}/libllkdu.so")
-    endif(NOT EXISTS ${internal_llkdu_path})
 else(WINDOWS)
     message(STATUS "WARNING: unrecognized platform for staging 3rd party libs, skipping...")
     set(vivox_src_dir "${CMAKE_SOURCE_DIR}/newview/vivox-runtime/i686-linux")
@@ -334,40 +288,29 @@ copy_if_different(
     )
 set(third_party_targets ${third_party_targets} ${out_targets})
 
-#*******************************
-# LLKDU
-set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu")
-if(NOT EXISTS ${internal_llkdu_path})
-    if (EXISTS "${debug_llkdu_src}")
-        ADD_CUSTOM_COMMAND(
-            OUTPUT  ${debug_llkdu_dst}
-            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${debug_llkdu_src} ${debug_llkdu_dst}
-            DEPENDS ${debug_llkdu_src}
-            COMMENT "Copying llkdu.dll ${SHARED_LIB_STAGING_DIR_DEBUG}"
-            )
-        set(third_party_targets ${third_party_targets} $} ${debug_llkdu_dst})
-    endif (EXISTS "${debug_llkdu_src}")
-
-    if (EXISTS "${release_llkdu_src}")
-        ADD_CUSTOM_COMMAND(
-            OUTPUT  ${release_llkdu_dst}
-            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${release_llkdu_src} ${release_llkdu_dst}
-            DEPENDS ${release_llkdu_src}
-            COMMENT "Copying llkdu.dll ${SHARED_LIB_STAGING_DIR_RELEASE}"
-            )
-        set(third_party_targets ${third_party_targets} ${release_llkdu_dst})
-
-        ADD_CUSTOM_COMMAND(
-            OUTPUT  ${relwithdebinfo_llkdu_dst}
-            COMMAND ${CMAKE_COMMAND} -E copy_if_different ${release_llkdu_src} ${relwithdebinfo_llkdu_dst}
-            DEPENDS ${release_llkdu_src}
-            COMMENT "Copying llkdu.dll ${SHARED_LIB_STAGING_DIR_RELWITHDEBINFO}"
-            )
-        set(third_party_targets ${third_party_targets} ${relwithdebinfo_llkdu_dst})
-    endif (EXISTS "${release_llkdu_src}")
-
-endif (NOT EXISTS ${internal_llkdu_path})
-
+if (FMOD_SDK_DIR)
+    copy_if_different(
+        ${FMOD_SDK_DIR} 
+        "${CMAKE_CURRENT_BINARY_DIR}/Debug"
+        out_targets 
+        ${fmod_files}
+        )
+    set(all_targets ${all_targets} ${out_targets})
+    copy_if_different(
+        ${FMOD_SDK_DIR} 
+        "${CMAKE_CURRENT_BINARY_DIR}/Release"
+        out_targets 
+        ${fmod_files}
+        )
+    set(all_targets ${all_targets} ${out_targets})
+    copy_if_different(
+        ${FMOD_SDK_DIR} 
+        "${CMAKE_CURRENT_BINARY_DIR}/RelWithDbgInfo"
+        out_targets 
+        ${fmod_files}
+        )
+    set(all_targets ${all_targets} ${out_targets})
+endif (FMOD_SDK_DIR)
 
 if(NOT STANDALONE)
   add_custom_target(
diff --git a/indra/cmake/FindJsonCpp.cmake b/indra/cmake/FindJsonCpp.cmake
index 9d16f2aaabcfbdb88e7763f16d0ef38deb8580c4..cf84b309c1f64617aeb916d46fc89a8dcb26a419 100644
--- a/indra/cmake/FindJsonCpp.cmake
+++ b/indra/cmake/FindJsonCpp.cmake
@@ -21,7 +21,12 @@ EXEC_PROGRAM(${CMAKE_CXX_COMPILER}
             OUTPUT_STRIP_TRAILING_WHITESPACE
             )
 
+# Try to find a library that was compiled with the same compiler version as we currently use.
 SET(JSONCPP_NAMES ${JSONCPP_NAMES} libjson_linux-gcc-${_gcc_COMPILER_VERSION}_libmt.so)
+IF (STANDALONE)
+	# On standalone, assume that the system installed library was compiled with the used compiler.
+	SET(JSONCPP_NAMES ${JSONCPP_NAMES} libjson.so)
+ENDIF (STANDALONE)
 FIND_LIBRARY(JSONCPP_LIBRARY
   NAMES ${JSONCPP_NAMES}
   PATHS /usr/lib /usr/local/lib
diff --git a/indra/cmake/FindLLQtWebkit.cmake b/indra/cmake/FindLLQtWebkit.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..c747ec32a2850957f45ccb5fe239b6f995cb611c
--- /dev/null
+++ b/indra/cmake/FindLLQtWebkit.cmake
@@ -0,0 +1,62 @@
+# -*- cmake -*-
+
+# - Find llqtwebkit
+# Find the llqtwebkit includes and library
+# This module defines
+#  LLQTWEBKIT_INCLUDE_DIR, where to find llqtwebkit.h, etc.
+#  LLQTWEBKIT_LIBRARY, the llqtwebkit library with full path.
+#  LLQTWEBKIT_FOUND, If false, do not try to use llqtwebkit.
+# also defined, but not for general use are
+#  LLQTWEBKIT_LIBRARIES, the libraries needed to use llqtwebkit.
+#  LLQTWEBKIT_LIBRARY_DIRS, where to find the llqtwebkit library.
+#  LLQTWEBKIT_DEFINITIONS - You should add_definitions(${LLQTWEBKIT_DEFINITIONS})
+#      before compiling code that includes llqtwebkit library files.
+
+# Try to use pkg-config first.
+# This allows to have two different libllqtwebkit packages installed:
+# one for viewer 2.x and one for viewer 1.x.
+include(FindPkgConfig)
+if (PKG_CONFIG_FOUND)
+    if (LLQtWebkit_FIND_REQUIRED AND LLQtWebkit_FIND_VERSION)
+        set(_PACKAGE_ARGS libllqtwebkit>=${LLQtWebkit_FIND_VERSION} REQUIRED)
+    else (LLQtWebkit_FIND_REQUIRED AND LLQtWebkit_FIND_VERSION)
+        set(_PACKAGE_ARGS libllqtwebkit)
+    endif (LLQtWebkit_FIND_REQUIRED AND LLQtWebkit_FIND_VERSION)
+    if (NOT "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_LESS "2.8")
+      # As virtually nobody will have a pkg-config file for this, do this check always quiet.
+      # Unfortunately cmake 2.8 or higher is required for pkg_check_modules to have a 'QUIET'.
+      set(_PACKAGE_ARGS ${_PACKAGE_ARGS} QUIET)
+    endif ()
+    pkg_check_modules(LLQTWEBKIT ${_PACKAGE_ARGS})
+endif (PKG_CONFIG_FOUND)
+set(LLQTWEBKIT_DEFINITIONS ${LLQTWEBKIT_CFLAGS_OTHER})
+
+find_path(LLQTWEBKIT_INCLUDE_DIR llqtwebkit.h NO_SYSTEM_ENVIRONMENT_PATH HINTS ${LLQTWEBKIT_INCLUDE_DIRS})
+
+find_library(LLQTWEBKIT_LIBRARY NAMES llqtwebkit NO_SYSTEM_ENVIRONMENT_PATH HINTS ${LLQTWEBKIT_LIBRARY_DIRS})
+
+if (NOT PKG_CONFIG_FOUND OR NOT LLQTWEBKIT_FOUND)	# If pkg-config couldn't find it, pretend we don't have pkg-config.
+   set(LLQTWEBKIT_LIBRARIES llqtwebkit)
+   get_filename_component(LLQTWEBKIT_LIBRARY_DIRS ${LLQTWEBKIT_LIBRARY} PATH)
+endif (NOT PKG_CONFIG_FOUND OR NOT LLQTWEBKIT_FOUND)
+
+# Handle the QUIETLY and REQUIRED arguments and set LLQTWEBKIT_FOUND
+# to TRUE if all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(
+  LLQTWEBKIT
+  DEFAULT_MSG
+  LLQTWEBKIT_LIBRARY
+  LLQTWEBKIT_INCLUDE_DIR
+  LLQTWEBKIT_LIBRARIES
+  LLQTWEBKIT_LIBRARY_DIRS
+  )
+
+mark_as_advanced(
+  LLQTWEBKIT_LIBRARY
+  LLQTWEBKIT_INCLUDE_DIR
+  LLQTWEBKIT_LIBRARIES
+  LLQTWEBKIT_LIBRARY_DIRS
+  LLQTWEBKIT_DEFINITIONS
+  )
+
diff --git a/indra/cmake/FindNDOF.cmake b/indra/cmake/FindNDOF.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..6dcf590a53d5f77cb6614752d8f78f0ef0336f30
--- /dev/null
+++ b/indra/cmake/FindNDOF.cmake
@@ -0,0 +1,39 @@
+# -*- cmake -*-
+
+# - Find NDOF
+# Find the NDOF includes and library
+# This module defines
+#  NDOF_INCLUDE_DIR, where to find ndofdev_external.h, etc.
+#  NDOF_LIBRARY, the library needed to use NDOF.
+#  NDOF_FOUND, If false, do not try to use NDOF.
+
+find_path(NDOF_INCLUDE_DIR ndofdev_external.h
+  PATH_SUFFIXES ndofdev
+  )
+
+set(NDOF_NAMES ${NDOF_NAMES} ndofdev libndofdev)
+find_library(NDOF_LIBRARY
+  NAMES ${NDOF_NAMES}
+  )
+
+if (NDOF_LIBRARY AND NDOF_INCLUDE_DIR)
+  set(NDOF_FOUND "YES")
+else (NDOF_LIBRARY AND NDOF_INCLUDE_DIR)
+  set(NDOF_FOUND "NO")
+endif (NDOF_LIBRARY AND NDOF_INCLUDE_DIR)
+
+
+if (NDOF_FOUND)
+  if (NOT NDOF_FIND_QUIETLY)
+    message(STATUS "Found NDOF: Library in '${NDOF_LIBRARY}' and header in '${NDOF_INCLUDE_DIR}' ")
+  endif (NOT NDOF_FIND_QUIETLY)
+else (NDOF_FOUND)
+  if (NDOF_FIND_REQUIRED)
+    message(FATAL_ERROR " * * *\nCould not find NDOF library!\nIf you don't need Space Navigator Joystick support you can skip this test by configuring with -DNDOF:BOOL=OFF\n * * *")
+  endif (NDOF_FIND_REQUIRED)
+endif (NDOF_FOUND)
+
+mark_as_advanced(
+  NDOF_LIBRARY
+  NDOF_INCLUDE_DIR
+  )
diff --git a/indra/cmake/FindTut.cmake b/indra/cmake/FindTut.cmake
index b5d58f6396e63c265eafda0d9d6df0c0e6c1906a..c2a9f430534aa1ee7e50ab87ed0bbe6b0cb241a2 100644
--- a/indra/cmake/FindTut.cmake
+++ b/indra/cmake/FindTut.cmake
@@ -3,12 +3,11 @@
 # - Find Tut
 # Find the Tut unit test framework includes and library
 # This module defines
-#  TUT_INCLUDE_DIR, where to find tut.h, etc.
+#  TUT_INCLUDE_DIR, where to find tut/tut.hpp.
 #  TUT_FOUND, If false, do not try to use Tut.
 
-find_path(TUT_INCLUDE_DIR tut.h
-    /usr/local/include/
-    /usr/include
+find_path(TUT_INCLUDE_DIR tut/tut.hpp
+    NO_SYSTEM_ENVIRONMENT_PATH
     )
 
 if (TUT_INCLUDE_DIR)
diff --git a/indra/cmake/FindZLIB.cmake b/indra/cmake/FindZLIB.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..6d630f1ba967e4d418965730b3032d4290f5c842
--- /dev/null
+++ b/indra/cmake/FindZLIB.cmake
@@ -0,0 +1,46 @@
+# -*- cmake -*-
+
+# - Find zlib
+# Find the ZLIB includes and library
+# This module defines
+#  ZLIB_INCLUDE_DIRS, where to find zlib.h, etc.
+#  ZLIB_LIBRARIES, the libraries needed to use zlib.
+#  ZLIB_FOUND, If false, do not try to use zlib.
+#
+# This FindZLIB is about 43 times as fast the one provided with cmake (2.8.x),
+# because it doesn't look up the version of zlib, resulting in a dramatic
+# speed up for configure (from 4 minutes 22 seconds to 6 seconds).
+#
+# Note: Since this file is only used for standalone, the windows
+# specific parts were left out.
+
+FIND_PATH(ZLIB_INCLUDE_DIR zlib.h
+  NO_SYSTEM_ENVIRONMENT_PATH
+  )
+
+FIND_LIBRARY(ZLIB_LIBRARY z)
+
+if (ZLIB_LIBRARY AND ZLIB_INCLUDE_DIR)
+  SET(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR})
+  SET(ZLIB_LIBRARIES ${ZLIB_LIBRARY})
+  SET(ZLIB_FOUND "YES")
+else (ZLIB_LIBRARY AND ZLIB_INCLUDE_DIR)
+  SET(ZLIB_FOUND "NO")
+endif (ZLIB_LIBRARY AND ZLIB_INCLUDE_DIR)
+
+if (ZLIB_FOUND)
+  if (NOT ZLIB_FIND_QUIETLY)
+	message(STATUS "Found ZLIB: ${ZLIB_LIBRARIES}")
+	SET(ZLIB_FIND_QUIETLY TRUE)
+  endif (NOT ZLIB_FIND_QUIETLY)
+else (ZLIB_FOUND)
+  if (ZLIB_FIND_REQUIRED)
+	message(FATAL_ERROR "Could not find ZLIB library")
+  endif (ZLIB_FIND_REQUIRED)
+endif (ZLIB_FOUND)
+
+mark_as_advanced(
+  ZLIB_LIBRARY
+  ZLIB_INCLUDE_DIR
+  )
+
diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake
index 29e2492551cfd3f0b81c8708d860da14416ccf0c..62b764bb307debcee404eb7bc355f8b361cabadb 100644
--- a/indra/cmake/LLAddBuildTest.cmake
+++ b/indra/cmake/LLAddBuildTest.cmake
@@ -205,6 +205,9 @@ FUNCTION(LL_ADD_INTEGRATION_TEST
   endif(TEST_DEBUG)
   ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})
   SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
+  if(STANDALONE)
+    SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES COMPILE_FLAGS -I"${TUT_INCLUDE_DIR}")
+  endif(STANDALONE)
 
   # Add link deps to the executable
   if(TEST_DEBUG)
diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake
index d1ab264a413b8a3eaf263ea3bbef32e59ac42e9c..17e211cb9937eb8e5544423e1c3ddb03a675fabc 100644
--- a/indra/cmake/LLCommon.cmake
+++ b/indra/cmake/LLCommon.cmake
@@ -13,7 +13,14 @@ set(LLCOMMON_INCLUDE_DIRS
     ${Boost_INCLUDE_DIRS}
     )
 
-set(LLCOMMON_LIBRARIES llcommon)
+if (LINUX)
+    # In order to support using ld.gold on linux, we need to explicitely
+    # specify all libraries that llcommon uses.
+    # llcommon uses `clock_gettime' which is provided by librt on linux.
+    set(LLCOMMON_LIBRARIES llcommon rt)
+else (LINUX)
+    set(LLCOMMON_LIBRARIES llcommon)
+endif (LINUX)
 
 add_definitions(${TCMALLOC_FLAG})
 
diff --git a/indra/cmake/LLKDU.cmake b/indra/cmake/LLKDU.cmake
index 27c8ada686ea51ee75ec5d94926a879a04588fd7..f5cbad03a6174607ae5e7f0d29e21b5181d3812c 100644
--- a/indra/cmake/LLKDU.cmake
+++ b/indra/cmake/LLKDU.cmake
@@ -1,7 +1,20 @@
 # -*- cmake -*-
 include(Prebuilt)
 
+# USE_KDU can be set when launching cmake or develop.py as an option using the argument -DUSE_KDU:BOOL=ON
+# When building using proprietary binaries though (i.e. having access to LL private servers), we always build with KDU
 if (INSTALL_PROPRIETARY AND NOT STANDALONE)
-  use_prebuilt_binary(kdu)
-  set(LLKDU_LIBRARY llkdu)
+  set(USE_KDU ON)
 endif (INSTALL_PROPRIETARY AND NOT STANDALONE)
+
+if (USE_KDU)
+  use_prebuilt_binary(kdu)
+  if (WINDOWS)
+    set(KDU_LIBRARY kdu.lib)
+  else (WINDOWS)
+    set(KDU_LIBRARY libkdu.a)
+  endif (WINDOWS)
+  set(KDU_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/kdu)
+  set(LLKDU_INCLUDE_DIRS ${LIBS_OPEN_DIR}/llkdu)
+  set(LLKDU_LIBRARIES llkdu)
+endif (USE_KDU)
diff --git a/indra/cmake/LLPlugin.cmake b/indra/cmake/LLPlugin.cmake
index 9722f16c3c31ebc09fac22b4f5bda01c0ae312c1..7ee404b9bdfa68fc6d398c48a2549b4449fc84d9 100644
--- a/indra/cmake/LLPlugin.cmake
+++ b/indra/cmake/LLPlugin.cmake
@@ -5,4 +5,10 @@ set(LLPLUGIN_INCLUDE_DIRS
     ${LIBS_OPEN_DIR}/llplugin
     )
 
-set(LLPLUGIN_LIBRARIES llplugin)
+if (LINUX)
+    # In order to support using ld.gold on linux, we need to explicitely
+    # specify all libraries that llplugin uses.
+	set(LLPLUGIN_LIBRARIES llplugin pthread)
+else (LINUX)
+	set(LLPLUGIN_LIBRARIES llplugin)
+endif (LINUX)
diff --git a/indra/cmake/NDOF.cmake b/indra/cmake/NDOF.cmake
index dad74e99b1bb7c8a4d79022938cb6df2d0ecb2bf..7a463d11907d9596e24ae1d2527a611459b4958d 100644
--- a/indra/cmake/NDOF.cmake
+++ b/indra/cmake/NDOF.cmake
@@ -1,14 +1,32 @@
 # -*- cmake -*-
 include(Prebuilt)
 
-use_prebuilt_binary(ndofdev)
+set(NDOF ON CACHE BOOL "Use NDOF space navigator joystick library.")
 
-if (WINDOWS OR DARWIN OR LINUX)
+if (NDOF)
+  if (STANDALONE)
+	set(NDOF_FIND_REQUIRED ON)
+	include(FindNDOF)
+  else (STANDALONE)
+	use_prebuilt_binary(ndofdev)
+
+	if (WINDOWS)
+	  set(NDOF_LIBRARY libndofdev)
+	elseif (DARWIN OR LINUX)
+	  set(NDOF_LIBRARY ndofdev)
+	endif (WINDOWS)
+
+	set(NDOF_INCLUDE_DIR ${ARCH_PREBUILT_DIRS}/include/ndofdev)
+	set(NDOF_FOUND 1)
+  endif (STANDALONE)
+endif (NDOF)
+
+if (NDOF_FOUND)
   add_definitions(-DLIB_NDOF=1)
-endif (WINDOWS OR DARWIN OR LINUX)
+  include_directories(${NDOF_INCLUDE_DIR})
+else (NDOF_FOUND)
+  message(STATUS "Building without N-DoF joystick support")
+  set(NDOF_INCLUDE_DIR "")
+  set(NDOF_LIBRARY "")
+endif (NDOF_FOUND)
 
-if (WINDOWS)
-  set(NDOF_LIBRARY libndofdev)
-elseif (DARWIN OR LINUX)
-  set(NDOF_LIBRARY ndofdev)
-endif (WINDOWS)
diff --git a/indra/cmake/PulseAudio.cmake b/indra/cmake/PulseAudio.cmake
index e918de0198aca68e0b4de20e6e376b3868f88459..360a971058717b989bee052de2e8a99d72ea5a37 100644
--- a/indra/cmake/PulseAudio.cmake
+++ b/indra/cmake/PulseAudio.cmake
@@ -1,7 +1,7 @@
 # -*- cmake -*-
 include(Prebuilt)
 
-set(PULSEAUDIO ON CACHE BOOL "Build with PulseAudio support, if available.")
+set(PULSEAUDIO OFF CACHE BOOL "Build with PulseAudio support, if available.")
 
 if (PULSEAUDIO)
   if (STANDALONE)
diff --git a/indra/cmake/Tut.cmake b/indra/cmake/Tut.cmake
index 784560471d503492a7daf43bfdaaba166de58bec..738c08c42fe6c10e3c82d0e48994d095e9744271 100644
--- a/indra/cmake/Tut.cmake
+++ b/indra/cmake/Tut.cmake
@@ -6,7 +6,6 @@ set(TUT_FIND_QUIETLY TRUE)
 
 if (STANDALONE)
   include(FindTut)
-  include_directories(${TUT_INCLUDE_DIR})
 else (STANDALONE)
   use_prebuilt_binary(tut)
 endif (STANDALONE)
diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake
index 12ba1b1b35b8f95f43f194c7fb3a35a0d12fb64f..1f5b0f5d84b65d8dd2bd937cc8f7bec1c4ef5a50 100644
--- a/indra/cmake/WebKitLibPlugin.cmake
+++ b/indra/cmake/WebKitLibPlugin.cmake
@@ -3,6 +3,29 @@ include(Linking)
 include(Prebuilt)
 
 if (STANDALONE)
+  # The minimal version, 4.4.3, is rather arbitrary: it's the version in Debian/Lenny.
+  find_package(Qt4 4.4.3 COMPONENTS QtCore QtGui QtNetwork QtOpenGL QtWebKit REQUIRED)
+  include(${QT_USE_FILE})
+  set(QTDIR $ENV{QTDIR})
+  if (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin")
+	message(FATAL_ERROR "\"${QT_BINARY_DIR}\" is unequal \"${QTDIR}/bin\"; "
+	  "Qt is found by looking for qmake in your PATH. "
+	  "Please set your PATH such that 'qmake' is found in \$QTDIR/bin, "
+	  "or unset QTDIR if the found Qt is correct.")
+	endif (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin")
+  find_package(LLQtWebkit REQUIRED QUIET)
+  # Add the plugins.
+  set(QT_PLUGIN_LIBRARIES)
+  foreach(qlibname qgif qjpeg)
+	find_library(QT_PLUGIN_${qlibname} ${qlibname} PATHS ${QT_PLUGINS_DIR}/imageformats NO_DEFAULT_PATH)
+	if (QT_PLUGIN_${qlibname})
+	  list(APPEND QT_PLUGIN_LIBRARIES ${QT_PLUGIN_${qlibname}})
+	else (QT_PLUGIN_${qtlibname})
+	  message(FATAL_ERROR "Could not find the Qt plugin ${qlibname} in \"${QT_PLUGINS_DIR}/imageformats\"!")
+	endif (QT_PLUGIN_${qlibname})
+  endforeach(qlibname)
+  # qjpeg depends on libjpeg
+  list(APPEND QT_PLUGIN_LIBRARIES jpeg)
   set(WEBKITLIBPLUGIN OFF CACHE BOOL
       "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.")
 else (STANDALONE)
@@ -35,7 +58,7 @@ elseif (DARWIN)
       )
 elseif (LINUX)
   if (STANDALONE) 
-    set(WEBKIT_PLUGIN_LIBRARIES llqtwebkit)
+    set(WEBKIT_PLUGIN_LIBRARIES ${LLQTWEBKIT_LIBRARY} ${QT_LIBRARIES} ${QT_PLUGIN_LIBRARIES})
   else (STANDALONE)
     set(WEBKIT_PLUGIN_LIBRARIES
         llqtwebkit
diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt
index 452d37d3be7c6350efc9307be5489131b107328e..e0772e55ca35990697e0d7d103297eb0f665d8ad 100644
--- a/indra/integration_tests/llui_libtest/CMakeLists.txt
+++ b/indra/integration_tests/llui_libtest/CMakeLists.txt
@@ -10,6 +10,7 @@ include(00-Common)
 include(LLCommon)
 include(LLImage)
 include(LLImageJ2COJ)   # ugh, needed for images
+include(LLKDU)
 include(LLMath)
 include(LLMessage)
 include(LLRender)
@@ -71,6 +72,11 @@ endif (DARWIN)
 target_link_libraries(llui_libtest
     llui
     llmessage
+    ${LLRENDER_LIBRARIES}
+    ${LLIMAGE_LIBRARIES}
+    ${LLKDU_LIBRARIES}
+    ${KDU_LIBRARY}
+    ${LLIMAGEJ2COJ_LIBRARIES}
     ${OS_LIBRARIES}
     ${GOOGLE_PERFTOOLS_LIBRARIES}
     )
diff --git a/indra/lib/python/indra/util/test_win32_manifest.py b/indra/lib/python/indra/util/test_win32_manifest.py
index 786521c06805d6533afb378ee76bb57360cc9c85..da8ee6c545741a56a43d849f0f68420b4f3d2df9 100644
--- a/indra/lib/python/indra/util/test_win32_manifest.py
+++ b/indra/lib/python/indra/util/test_win32_manifest.py
@@ -52,20 +52,22 @@ def get_HKLM_registry_value(key_str, value_str):
         
 def find_vc_dir():
     supported_versions = (r'8.0', r'9.0')
+    supported_products = (r'VisualStudio', r'VCExpress')
     value_str = (r'ProductDir')
     
-    for version in supported_versions:
-        key_str = (r'SOFTWARE\Microsoft\VisualStudio\%s\Setup\VC' %
-                   version)
-        try:
-            return get_HKLM_registry_value(key_str, value_str)
-        except WindowsError, err:
-            x64_key_str = (r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\%s\Setup\VS' %
-                       version)
+    for product in supported_products:
+        for version in supported_versions:
+            key_str = (r'SOFTWARE\Microsoft\%s\%s\Setup\VC' %
+                      (product, version))
             try:
-                return get_HKLM_registry_value(x64_key_str, value_str)
-            except:
-                print >> sys.stderr, "Didn't find MS VC version %s " % version
+                return get_HKLM_registry_value(key_str, value_str)
+            except WindowsError, err:
+                x64_key_str = (r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\%s\Setup\VS' %
+                        version)
+                try:
+                    return get_HKLM_registry_value(x64_key_str, value_str)
+                except:
+                    print >> sys.stderr, "Didn't find MS %s version %s " % (product,version)
         
     raise
 
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 66ec5bad2c55291b1d798811da15e454c3120b95..d1c44c94032851a19a4ab42f20006ce04a1eb1f4 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -28,6 +28,7 @@
 
 #include "linden_common.h"
 #include "llapr.h"
+#include "apr_dso.h"
 
 apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
 LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool.
@@ -279,14 +280,31 @@ bool ll_apr_warn_status(apr_status_t status)
 {
 	if(APR_SUCCESS == status) return false;
 	char buf[MAX_STRING];	/* Flawfinder: ignore */
-	apr_strerror(status, buf, MAX_STRING);
+	apr_strerror(status, buf, sizeof(buf));
 	LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
 	return true;
 }
 
+bool ll_apr_warn_status(apr_status_t status, apr_dso_handle_t *handle)
+{
+    bool result = ll_apr_warn_status(status);
+    // Despite observed truncation of actual Mac dylib load errors, increasing
+    // this buffer to more than MAX_STRING doesn't help: it appears that APR
+    // stores the output in a fixed 255-character internal buffer. (*sigh*)
+    char buf[MAX_STRING];           /* Flawfinder: ignore */
+    apr_dso_error(handle, buf, sizeof(buf));
+    LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
+    return result;
+}
+
 void ll_apr_assert_status(apr_status_t status)
 {
-	llassert(ll_apr_warn_status(status) == false);
+	llassert(! ll_apr_warn_status(status));
+}
+
+void ll_apr_assert_status(apr_status_t status, apr_dso_handle_t *handle)
+{
+    llassert(! ll_apr_warn_status(status, handle));
 }
 
 //---------------------------------------------------------------------
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 4930270af81c843ef24420d499e774989e09630b..af33ce666f1558193d3d2818cf0c3a5e98d6ee7a 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -53,6 +53,8 @@
 extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
 extern apr_thread_mutex_t* gCallStacksLogMutexp;
 
+struct apr_dso_handle_t;
+
 /** 
  * @brief initialize the common apr constructs -- apr itself, the
  * global pool, and a mutex.
@@ -259,8 +261,11 @@ class LL_COMMON_API LLAPRFile : boost::noncopyable
  * @return Returns <code>true</code> if status is an error condition.
  */
 bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
+/// There's a whole other APR error-message function if you pass a DSO handle.
+bool LL_COMMON_API ll_apr_warn_status(apr_status_t status, apr_dso_handle_t* handle);
 
 void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
+void LL_COMMON_API ll_apr_assert_status(apr_status_t status, apr_dso_handle_t* handle);
 
 extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
 
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 84a6620a77782b82cb764b0d1691ee9a52919bd9..97e2bdeb5754288521d26fe379b987f19312a2d0 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -475,7 +475,7 @@ void LLEventPump::stopListening(const std::string& name)
 *****************************************************************************/
 bool LLEventStream::post(const LLSD& event)
 {
-    if (! mEnabled)
+    if (! mEnabled || !mSignal)
     {
         return false;
     }
@@ -515,6 +515,8 @@ bool LLEventQueue::post(const LLSD& event)
 
 void LLEventQueue::flush()
 {
+	if(!mSignal) return;
+		
     // Consider the case when a given listener on this LLEventQueue posts yet
     // another event on the same queue. If we loop over mEventQueue directly,
     // we'll end up processing all those events during the same flush() call
diff --git a/indra/llcommon/llprocesslauncher.cpp b/indra/llcommon/llprocesslauncher.cpp
index 81e5f8820d73c9b21723b2a3ed5f29ff3c769d87..4b0f6b02511ecd46f46a889262c4c2017b4f8b2c 100644
--- a/indra/llcommon/llprocesslauncher.cpp
+++ b/indra/llcommon/llprocesslauncher.cpp
@@ -265,14 +265,7 @@ int LLProcessLauncher::launch(void)
 	delete[] fake_argv;
 	
 	mProcessID = id;
-	
-	// At this point, the child process will have been created (since that's how vfork works -- the child borrowed our execution context until it forked)
-	// If the process doesn't exist at this point, the exec failed.
-	if(!isRunning())
-	{
-		result = -1;
-	}
-	
+
 	return result;
 }
 
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index c75e0e2bbf9e613579d24283662d27443b7f0512..a53b22f6fc2889622682d9cb446a3a5eabff7d89 100644
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -179,7 +179,7 @@ class LL_COMMON_API LLQueuedThread : public LLThread
 	void waitOnPending();
 	void printQueueStats();
 
-	S32 getPending();
+	virtual S32 getPending();
 	bool getThreaded() { return mThreaded ? true : false; }
 
 	// Request accessors
diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt
index a69621a57b3c53e1dc576427287c540b8fbd2e09..ea8c1a1107eef6c7f3b2be9cdbad5dce5d45c54c 100644
--- a/indra/llimage/CMakeLists.txt
+++ b/indra/llimage/CMakeLists.txt
@@ -3,12 +3,13 @@
 project(llimage)
 
 include(00-Common)
-include(LLAddBuildTest)
 include(LLCommon)
 include(LLImage)
 include(LLMath)
 include(LLVFS)
 include(ZLIB)
+include(LLAddBuildTest)
+include(Tut)
 
 include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
@@ -57,11 +58,18 @@ add_library (llimage ${llimage_SOURCE_FILES})
 # Sort by high-level to low-level
 target_link_libraries(llimage
     llcommon
-    llimagej2coj        # *HACK: In theory a noop for KDU builds?
     ${JPEG_LIBRARIES}
     ${PNG_LIBRARIES}
     ${ZLIB_LIBRARIES}
     )
 
 # Add tests
-#ADD_BUILD_TEST(llimageworker llimage)
+if (LL_TESTS)
+  SET(llimage_TEST_SOURCE_FILES
+    llimageworker.cpp
+    )
+  LL_ADD_PROJECT_UNIT_TESTS(llimage "${llimage_TEST_SOURCE_FILES}")
+endif (LL_TESTS)
+
+
+
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 5c33b675ca3a64221ee46f48cb5869f14615e5e7..b46a99e03034160b3598e24427f3072f1f8fc692 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -52,13 +52,11 @@ LLMutex* LLImage::sMutex = NULL;
 void LLImage::initClass()
 {
 	sMutex = new LLMutex(NULL);
-	LLImageJ2C::openDSO();
 }
 
 //static
 void LLImage::cleanupClass()
 {
-	LLImageJ2C::closeDSO();
 	delete sMutex;
 	sMutex = NULL;
 }
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index d005aaf29fd7ca3359dd9d7efd6884a952b799aa..cb2a85fa917afa683954f342d45c488a3e284ec4 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -24,9 +24,6 @@
  */
 #include "linden_common.h"
 
-#include "apr_pools.h"
-#include "apr_dso.h"
-
 #include "lldir.h"
 #include "llimagej2c.h"
 #include "llmemtype.h"
@@ -37,18 +34,10 @@ typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)();
 typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*);
 typedef const char* (*EngineInfoLLImageJ2CFunction)();
 
-//some "private static" variables so we only attempt to load
-//dynamic libaries once
-CreateLLImageJ2CFunction j2cimpl_create_func;
-DestroyLLImageJ2CFunction j2cimpl_destroy_func;
-EngineInfoLLImageJ2CFunction j2cimpl_engineinfo_func;
-apr_pool_t *j2cimpl_dso_memory_pool;
-apr_dso_handle_t *j2cimpl_dso_handle;
-
-//Declare the prototype for theses functions here, their functionality
-//will be implemented in other files which define a derived LLImageJ2CImpl
-//but only ONE static library which has the implementation for this
-//function should ever be included
+// Declare the prototype for theses functions here. Their functionality
+// will be implemented in other files which define a derived LLImageJ2CImpl
+// but only ONE static library which has the implementation for these
+// functions should ever be included.
 LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl();
 void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl);
 const char* fallbackEngineInfoLLImageJ2CImpl();
@@ -57,121 +46,10 @@ const char* fallbackEngineInfoLLImageJ2CImpl();
 LLImageCompressionTester* LLImageJ2C::sTesterp = NULL ;
 const std::string sTesterName("ImageCompressionTester");
 
-//static
-//Loads the required "create", "destroy" and "engineinfo" functions needed
-void LLImageJ2C::openDSO()
-{
-	//attempt to load a DSO and get some functions from it
-	std::string dso_name;
-	std::string dso_path;
-
-	bool all_functions_loaded = false;
-	apr_status_t rv;
-
-#if LL_WINDOWS
-	dso_name = "llkdu.dll";
-#elif LL_DARWIN
-	dso_name = "libllkdu.dylib";
-#else
-	dso_name = "libllkdu.so";
-#endif
-
-	dso_path = gDirUtilp->findFile(dso_name,
-								   gDirUtilp->getAppRODataDir(),
-								   gDirUtilp->getExecutableDir());
-
-	j2cimpl_dso_handle      = NULL;
-	j2cimpl_dso_memory_pool = NULL;
-
-	//attempt to load the shared library
-	apr_pool_create(&j2cimpl_dso_memory_pool, NULL);
-	rv = apr_dso_load(&j2cimpl_dso_handle,
-					  dso_path.c_str(),
-					  j2cimpl_dso_memory_pool);
-
-	//now, check for success
-	if ( rv == APR_SUCCESS )
-	{
-		//found the dynamic library
-		//now we want to load the functions we're interested in
-		CreateLLImageJ2CFunction  create_func = NULL;
-		DestroyLLImageJ2CFunction dest_func = NULL;
-		EngineInfoLLImageJ2CFunction engineinfo_func = NULL;
-
-		rv = apr_dso_sym((apr_dso_handle_sym_t*)&create_func,
-						 j2cimpl_dso_handle,
-						 "createLLImageJ2CKDU");
-		if ( rv == APR_SUCCESS )
-		{
-			//we've loaded the create function ok
-			//we need to delete via the DSO too
-			//so lets check for a destruction function
-			rv = apr_dso_sym((apr_dso_handle_sym_t*)&dest_func,
-							 j2cimpl_dso_handle,
-							 "destroyLLImageJ2CKDU");
-			if ( rv == APR_SUCCESS )
-			{
-				//we've loaded the destroy function ok
-				rv = apr_dso_sym((apr_dso_handle_sym_t*)&engineinfo_func,
-						 j2cimpl_dso_handle,
-						 "engineInfoLLImageJ2CKDU");
-				if ( rv == APR_SUCCESS )
-				{
-					//ok, everything is loaded alright
-					j2cimpl_create_func  = create_func;
-					j2cimpl_destroy_func = dest_func;
-					j2cimpl_engineinfo_func = engineinfo_func;
-					all_functions_loaded = true;
-				}
-			}
-		}
-	}
-
-	if ( !all_functions_loaded )
-	{
-		//something went wrong with the DSO or function loading..
-		//fall back onto our satefy impl creation function
-
-#if 0
-		// precious verbose debugging, sadly we can't use our
-		// 'llinfos' stream etc. this early in the initialisation seq.
-		char errbuf[256];
-		fprintf(stderr, "failed to load syms from DSO %s (%s)\n",
-			dso_name.c_str(), dso_path.c_str());
-		apr_strerror(rv, errbuf, sizeof(errbuf));
-		fprintf(stderr, "error: %d, %s\n", rv, errbuf);
-		apr_dso_error(j2cimpl_dso_handle, errbuf, sizeof(errbuf));
-		fprintf(stderr, "dso-error: %d, %s\n", rv, errbuf);
-#endif
-
-		if ( j2cimpl_dso_handle )
-		{
-			apr_dso_unload(j2cimpl_dso_handle);
-			j2cimpl_dso_handle = NULL;
-		}
-
-		if ( j2cimpl_dso_memory_pool )
-		{
-			apr_pool_destroy(j2cimpl_dso_memory_pool);
-			j2cimpl_dso_memory_pool = NULL;
-		}
-	}
-}
-
-//static
-void LLImageJ2C::closeDSO()
-{
-	if ( j2cimpl_dso_handle ) apr_dso_unload(j2cimpl_dso_handle);
-	if (j2cimpl_dso_memory_pool) apr_pool_destroy(j2cimpl_dso_memory_pool);
-}
-
 //static
 std::string LLImageJ2C::getEngineInfo()
 {
-	if (!j2cimpl_engineinfo_func)
-		j2cimpl_engineinfo_func = fallbackEngineInfoLLImageJ2CImpl;
-
-	return j2cimpl_engineinfo_func();
+    return fallbackEngineInfoLLImageJ2CImpl();
 }
 
 LLImageJ2C::LLImageJ2C() : 	LLImageFormatted(IMG_CODEC_J2C),
@@ -181,20 +59,7 @@ LLImageJ2C::LLImageJ2C() : 	LLImageFormatted(IMG_CODEC_J2C),
 							mReversible(FALSE),
 							mAreaUsedForDataSizeCalcs(0)
 {
-	//We assume here that if we wanted to create via
-	//a dynamic library that the approriate open calls were made
-	//before any calls to this constructor.
-
-	//Therefore, a NULL creation function pointer here means
-	//we either did not want to create using functions from the dynamic
-	//library or there were issues loading it, either way
-	//use our fall back
-	if ( !j2cimpl_create_func )
-	{
-		j2cimpl_create_func = fallbackCreateLLImageJ2CImpl;
-	}
-
-	mImpl = j2cimpl_create_func();
+	mImpl = fallbackCreateLLImageJ2CImpl();
 
 	// Clear data size table
 	for( S32 i = 0; i <= MAX_DISCARD_LEVEL; i++)
@@ -217,22 +82,9 @@ LLImageJ2C::LLImageJ2C() : 	LLImageFormatted(IMG_CODEC_J2C),
 // virtual
 LLImageJ2C::~LLImageJ2C()
 {
-	//We assume here that if we wanted to destroy via
-	//a dynamic library that the approriate open calls were made
-	//before any calls to this destructor.
-
-	//Therefore, a NULL creation function pointer here means
-	//we either did not want to destroy using functions from the dynamic
-	//library or there were issues loading it, either way
-	//use our fall back
-	if ( !j2cimpl_destroy_func )
-	{
-		j2cimpl_destroy_func = fallbackDestroyLLImageJ2CImpl;
-	}
-
 	if ( mImpl )
 	{
-		j2cimpl_destroy_func(mImpl);
+        fallbackDestroyLLImageJ2CImpl(mImpl);
 	}
 }
 
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index cc3dabd7d8b14a86ed8bc1cd5fc1310d3950413c..dd5bec8b2e2cc0bfb8086d99c587ed7bb84e0fa0 100644
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -72,8 +72,6 @@ class LLImageJ2C : public LLImageFormatted
 	static S32 calcHeaderSizeJ2C();
 	static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = 0.f);
 
-	static void openDSO();
-	static void closeDSO();
 	static std::string getEngineInfo();
 
 protected:
diff --git a/indra/llimage/tests/llimageworker_test.cpp b/indra/llimage/tests/llimageworker_test.cpp
index a1092767092e86d1f46b81969dc59c80474af6f0..08476fb72cce4c4143d2805b7ed2c10d1f05007c 100644
--- a/indra/llimage/tests/llimageworker_test.cpp
+++ b/indra/llimage/tests/llimageworker_test.cpp
@@ -26,10 +26,8 @@
  */
 
 // Precompiled header: almost always required for newview cpp files
-#include <list>
-#include <map>
-#include <algorithm>
-// Class to test
+#include "linden_common.h"
+// Class to test 
 #include "../llimageworker.h"
 // For timer class
 #include "../llcommon/lltimer.h"
@@ -44,7 +42,17 @@
 // * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
 // * A simulator for a class can be implemented here. Please comment and document thoroughly.
 
-LLImageBase::LLImageBase() {}
+LLImageBase::LLImageBase() 
+: mData(NULL),
+mDataSize(0),
+mWidth(0),
+mHeight(0),
+mComponents(0),
+mBadBufferAllocation(false),
+mAllowOverSize(false),
+mMemType(LLMemType::MTYPE_IMAGEBASE)
+{
+}
 LLImageBase::~LLImageBase() {}
 void LLImageBase::dump() { }
 void LLImageBase::sanityCheck() { }
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 95e0997d5b40c343b188c59e36279731d9b5e96c..13b12c0928c27d4a542e40c90bd15edaabc5eaa9 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -90,6 +90,12 @@ void info_callback(const char* msg, void*)
 	lldebugs << "LLImageJ2COJ: " << chomp(msg) << llendl;
 }
 
+// Divide a by 2 to the power of b and round upwards
+int ceildivpow2(int a, int b)
+{
+	return (a + (1 << b) - 1) >> b;
+}
+
 
 LLImageJ2COJ::LLImageJ2COJ()
 	: LLImageJ2CImpl()
diff --git a/indra/llimagej2coj/llimagej2coj.h b/indra/llimagej2coj/llimagej2coj.h
index 7edacbe97c94563713219e12aa152ddbfbe90024..9476665ccbb57ed034ee4d4e594775f4539dc8f2 100644
--- a/indra/llimagej2coj/llimagej2coj.h
+++ b/indra/llimagej2coj/llimagej2coj.h
@@ -34,17 +34,11 @@ class LLImageJ2COJ : public LLImageJ2CImpl
 public:
 	LLImageJ2COJ();
 	virtual ~LLImageJ2COJ();
-
 protected:
 	/*virtual*/ BOOL getMetadata(LLImageJ2C &base);
 	/*virtual*/ BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count);
 	/*virtual*/ BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
 								BOOL reversible = FALSE);
-	int ceildivpow2(int a, int b)
-	{
-		// Divide a by b to the power of 2 and round upwards.
-		return (a + (1 << b) - 1) >> b;
-	}
 };
 
 #endif
diff --git a/indra/llinventory/llnotecard.cpp b/indra/llinventory/llnotecard.cpp
index 62829c284f4dd764f12e1ae4e984546a0ea38995..69152cefe078a75c034e329f5194387a6d0d731b 100644
--- a/indra/llinventory/llnotecard.cpp
+++ b/indra/llinventory/llnotecard.cpp
@@ -199,7 +199,7 @@ bool LLNotecard::importStream(std::istream& str)
 		return FALSE;
 	}
 
-	if(text_len > mMaxText)
+	if(text_len > mMaxText || text_len < 0)
 	{
 		llwarns << "Invalid Linden text length: " << text_len << llendl;
 		return FALSE;
diff --git a/indra/llkdu/CMakeLists.txt b/indra/llkdu/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7ed1c6c694a23bb13d0fb643ef41445fe043b5af
--- /dev/null
+++ b/indra/llkdu/CMakeLists.txt
@@ -0,0 +1,55 @@
+# -*- cmake -*-
+
+project(llkdu)
+
+# Visual Studio 2005 has a dumb bug that causes it to fail compilation
+# of KDU if building with both optimisation and /WS (treat warnings as
+# errors), even when the specific warnings that make it croak are
+# disabled.
+
+#set(VS_DISABLE_FATAL_WARNINGS ON)
+
+include(00-Common)
+include(LLCommon)
+include(LLImage)
+include(LLKDU)
+include(LLMath)
+
+include_directories(
+    ${LLCOMMON_INCLUDE_DIRS}
+    ${LLIMAGE_INCLUDE_DIRS}
+    ${KDU_INCLUDE_DIR}
+    ${LLMATH_INCLUDE_DIRS}
+    )
+
+set(llkdu_SOURCE_FILES
+    llimagej2ckdu.cpp
+    llkdumem.cpp
+    )
+
+set(llkdu_HEADER_FILES
+    CMakeLists.txt
+	
+    llimagej2ckdu.h
+    llkdumem.h
+    )
+
+set_source_files_properties(${llkdu_HEADER_FILES}
+                            PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND llkdu_SOURCE_FILES ${llkdu_HEADER_FILES})
+
+if (USE_KDU)
+  add_library (${LLKDU_LIBRARIES} ${llkdu_SOURCE_FILES})
+  
+  # Add tests
+  if (LL_TESTS)
+    include(LLAddBuildTest)
+    include(Tut)
+    SET(llkdu_TEST_SOURCE_FILES
+      llimagej2ckdu.cpp
+      )
+    LL_ADD_PROJECT_UNIT_TESTS(llkdu "${llkdu_TEST_SOURCE_FILES}")
+  endif (LL_TESTS)
+
+endif (USE_KDU)
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10ea5685e86ca797d26da2a5a8f4767c7f933576
--- /dev/null
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -0,0 +1,1085 @@
+ /** 
+ * @file llimagej2ckdu.cpp
+ * @brief This is an implementation of JPEG2000 encode/decode using Kakadu
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "llimagej2ckdu.h"
+
+#include "lltimer.h"
+#include "llpointer.h"
+#include "llkdumem.h"
+
+
+class kdc_flow_control {
+	
+public: // Member functions
+    kdc_flow_control(kdu_image_in_base *img_in, kdu_codestream codestream);
+    ~kdc_flow_control();
+    bool advance_components();
+    void process_components();
+	
+private: // Data
+    
+    struct kdc_component_flow_control {
+    public: // Data
+        kdu_image_in_base *reader;
+        int vert_subsampling;
+        int ratio_counter;  /*  Initialized to 0, decremented by `count_delta';
+                                when < 0, a new line must be processed, after
+                                which it is incremented by `vert_subsampling'.  */
+        int initial_lines;
+        int remaining_lines;
+        kdu_line_buf *line;
+    };
+    
+    kdu_codestream codestream;
+    kdu_dims valid_tile_indices;
+    kdu_coords tile_idx;
+    kdu_tile tile;
+    int num_components;
+    kdc_component_flow_control *components;
+    int count_delta; // Holds the minimum of the `vert_subsampling' fields
+    kdu_multi_analysis engine;
+    kdu_long max_buffer_memory;
+};
+
+//
+// Kakadu specific implementation
+//
+void set_default_colour_weights(kdu_params *siz);
+
+const char* engineInfoLLImageJ2CKDU()
+{
+	return "KDU v6.4.1";
+}
+
+LLImageJ2CKDU* createLLImageJ2CKDU()
+{
+	return new LLImageJ2CKDU();
+}
+
+void destroyLLImageJ2CKDU(LLImageJ2CKDU* kdu)
+{
+	delete kdu;
+	kdu = NULL;
+}
+
+LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
+{
+	return new LLImageJ2CKDU();
+}
+
+void fallbackDestroyLLImageJ2CImpl(LLImageJ2CImpl* impl)
+{
+	delete impl;
+	impl = NULL;
+}
+
+const char* fallbackEngineInfoLLImageJ2CImpl()
+{
+	return engineInfoLLImageJ2CKDU();
+}
+
+class LLKDUDecodeState
+{
+public:
+
+	S32 mNumComponents;
+	BOOL mUseYCC;
+	kdu_dims mDims;
+	kdu_sample_allocator mAllocator;
+	kdu_tile_comp mComps[4];
+	kdu_line_buf mLines[4];
+	kdu_pull_ifc mEngines[4];
+	bool mReversible[4]; // Some components may be reversible and others not.
+	int mBitDepths[4]; // Original bit-depth may be quite different from 8.
+
+	kdu_tile mTile;
+	kdu_byte *mBuf;
+	S32 mRowGap;
+
+	LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap);
+	~LLKDUDecodeState();
+	BOOL processTileDecode(F32 decode_time, BOOL limit_time = TRUE);
+
+public:
+	int *AssignLayerBytes(siz_params *siz, int &num_specs);
+
+	void setupCodeStream(BOOL keep_codestream, LLImageJ2CKDU::ECodeStreamMode mode);
+	BOOL initDecode(LLImageRaw &raw_image, F32 decode_time, LLImageJ2CKDU::ECodeStreamMode mode, S32 first_channel, S32 max_channel_count );
+};
+
+void ll_kdu_error( void )
+{
+	// *FIX: This exception is bad, bad, bad. It gets thrown from a
+	// destructor which can lead to immediate program termination!
+	throw "ll_kdu_error() throwing an exception";
+}
+
+// Stuff for new kdu error handling
+class LLKDUMessageWarning : public kdu_message
+{
+public:
+	/*virtual*/ void put_text(const char *s);
+	/*virtual*/ void put_text(const kdu_uint16 *s);
+
+	static LLKDUMessageWarning sDefaultMessage;
+};
+
+class LLKDUMessageError : public kdu_message
+{
+public:
+	/*virtual*/ void put_text(const char *s);
+	/*virtual*/ void put_text(const kdu_uint16 *s);
+	/*virtual*/ void flush(bool end_of_message=false);
+	static LLKDUMessageError sDefaultMessage;
+};
+
+void LLKDUMessageWarning::put_text(const char *s)
+{
+	llinfos << "KDU Warning: " << s << llendl;
+}
+
+void LLKDUMessageWarning::put_text(const kdu_uint16 *s)
+{
+	llinfos << "KDU Warning: " << s << llendl;
+}
+
+void LLKDUMessageError::put_text(const char *s)
+{
+	llinfos << "KDU Error: " << s << llendl;
+}
+
+void LLKDUMessageError::put_text(const kdu_uint16 *s)
+{
+	llinfos << "KDU Error: " << s << llendl;
+}
+
+void LLKDUMessageError::flush(bool end_of_message)
+{
+	if( end_of_message ) 
+	{
+		throw "KDU throwing an exception";
+	}
+}
+
+LLKDUMessageWarning LLKDUMessageWarning::sDefaultMessage;
+LLKDUMessageError	LLKDUMessageError::sDefaultMessage;
+static bool kdu_message_initialized = false;
+
+LLImageJ2CKDU::LLImageJ2CKDU() : LLImageJ2CImpl(),
+mInputp(NULL),
+mCodeStreamp(NULL),
+mTPosp(NULL),
+mTileIndicesp(NULL),
+mRawImagep(NULL),
+mDecodeState(NULL)
+{
+}
+
+LLImageJ2CKDU::~LLImageJ2CKDU()
+{
+	cleanupCodeStream(); // in case destroyed before decode completed
+}
+
+// Stuff for new simple decode
+void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision);
+
+void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECodeStreamMode mode)
+{
+	S32 data_size = base.getDataSize();
+	S32 max_bytes = base.getMaxBytes() ? base.getMaxBytes() : data_size;
+
+	//
+	//  Initialization
+	//
+	if (!kdu_message_initialized)
+	{
+		kdu_message_initialized = true;
+		kdu_customize_errors(&LLKDUMessageError::sDefaultMessage);
+		kdu_customize_warnings(&LLKDUMessageWarning::sDefaultMessage);
+	}
+
+	if (mCodeStreamp)
+	{
+		mCodeStreamp->destroy();
+		delete mCodeStreamp;
+		mCodeStreamp = NULL;
+	}
+
+	if (!mInputp && base.getData())
+	{
+		// The compressed data has been loaded
+		// Setup the source for the codestream
+		mInputp = new LLKDUMemSource(base.getData(), data_size);
+	}
+
+	if (mInputp)
+	{
+		mInputp->reset();
+	}
+	mCodeStreamp = new kdu_codestream;
+
+	mCodeStreamp->create(mInputp);
+
+	// Set the maximum number of bytes to use from the codestream
+	mCodeStreamp->set_max_bytes(max_bytes);
+
+	//    If you want to flip or rotate the image for some reason, change
+	// the resolution, or identify a restricted region of interest, this is
+	// the place to do it.  You may use "kdu_codestream::change_appearance"
+	// and "kdu_codestream::apply_input_restrictions" for this purpose.
+	//    If you wish to truncate the code-stream prior to decompression, you
+	// may use "kdu_codestream::set_max_bytes".
+	//    If you wish to retain all compressed data so that the material
+	// can be decompressed multiple times, possibly with different appearance
+	// parameters, you should call "kdu_codestream::set_persistent" here.
+	//    There are a variety of other features which must be enabled at
+	// this point if you want to take advantage of them.  See the
+	// descriptions appearing with the "kdu_codestream" interface functions
+	// in "kdu_compressed.h" for an itemized account of these capabilities.
+
+	switch( mode )
+	{
+	case MODE_FAST:
+		mCodeStreamp->set_fast();
+		break;
+	case MODE_RESILIENT:
+		mCodeStreamp->set_resilient();
+		break;
+	case MODE_FUSSY:
+		mCodeStreamp->set_fussy();
+		break;
+	default:
+		llassert(0);
+		mCodeStreamp->set_fast();
+	}
+
+	kdu_dims dims;
+	mCodeStreamp->get_dims(0,dims);
+
+	S32 components = mCodeStreamp->get_num_components();
+
+	if (components >= 3)
+	{ // Check that components have consistent dimensions (for PPM file)
+		kdu_dims dims1; mCodeStreamp->get_dims(1,dims1);
+		kdu_dims dims2; mCodeStreamp->get_dims(2,dims2);
+		if ((dims1 != dims) || (dims2 != dims))
+		{
+			llerrs << "Components don't have matching dimensions!" << llendl;
+		}
+	}
+
+	base.setSize(dims.size.x, dims.size.y, components);
+
+	if (!keep_codestream)
+	{
+		mCodeStreamp->destroy();
+		delete mCodeStreamp;
+		mCodeStreamp = NULL;
+		delete mInputp;
+		mInputp = NULL;
+	}
+}
+
+void LLImageJ2CKDU::cleanupCodeStream()
+{
+	delete mInputp;
+	mInputp = NULL;
+
+	delete mDecodeState;
+	mDecodeState = NULL;
+
+	if (mCodeStreamp)
+	{
+		mCodeStreamp->destroy();
+		delete mCodeStreamp;
+		mCodeStreamp = NULL;
+	}
+
+	delete mTPosp;
+	mTPosp = NULL;
+
+	delete mTileIndicesp;
+	mTileIndicesp = NULL;
+}
+
+BOOL LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count )
+{
+	base.resetLastError();
+
+	// *FIX: kdu calls our callback function if there's an error, and then bombs.
+	// To regain control, we throw an exception, and catch it here.
+	try
+	{
+		base.updateRawDiscardLevel();
+		setupCodeStream(base, TRUE, mode);
+
+		mRawImagep = &raw_image;
+		mCodeStreamp->change_appearance(false, true, false);
+		mCodeStreamp->apply_input_restrictions(first_channel,max_channel_count,base.getRawDiscardLevel(),0,NULL);
+
+		kdu_dims dims; mCodeStreamp->get_dims(0,dims);
+		S32 channels = base.getComponents() - first_channel;
+		if( channels > max_channel_count )
+		{
+			channels = max_channel_count;
+		}
+		raw_image.resize(dims.size.x, dims.size.y, channels);
+
+		//	llinfos << "Resizing to " << dims.size.x << ":" << dims.size.y << llendl;
+		if (!mTileIndicesp)
+		{
+			mTileIndicesp = new kdu_dims;
+		}
+		mCodeStreamp->get_valid_tiles(*mTileIndicesp);
+		if (!mTPosp)
+		{
+			mTPosp = new kdu_coords;
+			mTPosp->y = 0;
+			mTPosp->x = 0;
+		}
+	}
+	catch (const char* msg)
+	{
+		base.setLastError(ll_safe_string(msg));
+		return FALSE;
+	}
+	catch (...)
+	{
+		base.setLastError("Unknown J2C error");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+// Returns TRUE to mean done, whether successful or not.
+BOOL LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
+{
+	ECodeStreamMode mode = MODE_FAST;
+
+	LLTimer decode_timer;
+
+	if (!mCodeStreamp)
+	{
+		if (!initDecode(base, raw_image, decode_time, mode, first_channel, max_channel_count))
+		{
+			// Initializing the J2C decode failed, bail out.
+			cleanupCodeStream();
+			return TRUE; // done
+		}
+	}
+
+	// These can probably be grabbed from what's saved in the class.
+	kdu_dims dims;
+	mCodeStreamp->get_dims(0,dims);
+
+	// Now we are ready to walk through the tiles processing them one-by-one.
+	kdu_byte *buffer = raw_image.getData();
+
+	while (mTPosp->y < mTileIndicesp->size.y)
+	{
+		while (mTPosp->x < mTileIndicesp->size.x)
+		{
+			try
+			{
+				if (!mDecodeState)
+				{
+					kdu_tile tile = mCodeStreamp->open_tile(*(mTPosp)+mTileIndicesp->pos);
+
+					// Find the region of the buffer occupied by this
+					// tile.  Note that we have no control over
+					// sub-sampling factors which might have been used
+					// during compression and so it can happen that tiles
+					// (at the image component level) actually have
+					// different dimensions.  For this reason, we cannot
+					// figure out the buffer region occupied by a tile
+					// directly from the tile indices.  Instead, we query
+					// the highest resolution of the first tile-component
+					// concerning its location and size on the canvas --
+					// the `dims' object already holds the location and
+					// size of the entire image component on the same
+					// canvas coordinate system.  Comparing the two tells
+					// us where the current tile is in the buffer.
+					S32 channels = base.getComponents() - first_channel;
+					if( channels > max_channel_count )
+					{
+						channels = max_channel_count;
+					}
+					kdu_resolution res = tile.access_component(0).access_resolution();
+					kdu_dims tile_dims; res.get_dims(tile_dims);
+					kdu_coords offset = tile_dims.pos - dims.pos;
+					int row_gap = channels*dims.size.x; // inter-row separation
+					kdu_byte *buf = buffer + offset.y*row_gap + offset.x*channels;
+					mDecodeState = new LLKDUDecodeState(tile, buf, row_gap);
+				}
+				// Do the actual processing
+				F32 remaining_time = decode_time - decode_timer.getElapsedTimeF32();
+				// This is where we do the actual decode.  If we run out of time, return false.
+				if (mDecodeState->processTileDecode(remaining_time, (decode_time > 0.0f)))
+				{
+					delete mDecodeState;
+					mDecodeState = NULL;
+				}
+				else
+				{
+					// Not finished decoding yet.
+					//					setLastError("Ran out of time while decoding");
+					return FALSE;
+				}
+			}
+			catch( const char* msg )
+			{
+				base.setLastError(ll_safe_string(msg));
+				base.decodeFailed();
+				cleanupCodeStream();
+				return TRUE; // done
+			}
+			catch( ... )
+			{
+				base.setLastError( "Unknown J2C error" );
+				base.decodeFailed();
+				cleanupCodeStream();
+				return TRUE; // done
+			}
+
+
+			mTPosp->x++;
+		}
+		mTPosp->y++;
+		mTPosp->x = 0;
+	}
+
+	cleanupCodeStream();
+
+	return TRUE;
+}
+
+
+BOOL LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, BOOL reversible)
+{
+	// Collect simple arguments.
+	bool transpose, vflip, hflip;
+	bool allow_rate_prediction, mem, quiet, no_weights;
+	int cpu_iterations;
+	std::ostream *record_stream;
+
+	transpose = false;
+	record_stream = NULL;
+	allow_rate_prediction = true;
+	no_weights = false;
+	cpu_iterations = -1;
+	mem = false;
+	quiet = false;
+	vflip = true;
+	hflip = false;
+
+	try
+	{
+		// Set up input image files.
+		siz_params siz;
+		
+		// Should set rate someplace here.
+		LLKDUMemIn mem_in(raw_image.getData(),
+			raw_image.getDataSize(),
+			raw_image.getWidth(),
+			raw_image.getHeight(),
+			raw_image.getComponents(),
+			&siz);
+
+		base.setSize(raw_image.getWidth(), raw_image.getHeight(), raw_image.getComponents());
+
+		int num_components = raw_image.getComponents();
+
+		siz.set(Scomponents,0,0,num_components);
+		siz.set(Sdims,0,0,base.getHeight());  // Height of first image component
+		siz.set(Sdims,0,1,base.getWidth());   // Width of first image component
+		siz.set(Sprecision,0,0,8);  // Image samples have original bit-depth of 8
+		siz.set(Ssigned,0,0,false); // Image samples are originally unsigned
+
+		kdu_params *siz_ref = &siz; siz_ref->finalize();
+		siz_params transformed_siz; // Use this one to construct code-strea
+		transformed_siz.copy_from(&siz,-1,-1,-1,0,transpose,false,false);
+
+		// Construct the `kdu_codestream' object and parse all remaining arguments.
+
+		U32 max_output_size = base.getWidth()*base.getHeight()*base.getComponents();
+		if (max_output_size < 1000)
+		{
+			max_output_size = 1000;
+		}
+		U8 *output_buffer = new U8[max_output_size];
+
+		U32 output_size = max_output_size; // gets modified
+		LLKDUMemTarget output(output_buffer, output_size, base.getWidth()*base.getHeight()*base.getComponents());
+		if (output_size > max_output_size)
+		{
+			llerrs << llformat("LLImageJ2C::encode output_size(%d) > max_output_size(%d)",
+				output_size,max_output_size) << llendl;
+		}
+
+		kdu_codestream codestream;
+		codestream.create(&transformed_siz,&output);
+
+		if (comment_text)
+		{
+			// Set the comments for the codestream
+			kdu_codestream_comment comment = codestream.add_comment();
+			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) && !no_weights)
+		{
+			set_default_colour_weights(codestream.access_siz());
+		}
+
+		if (reversible)
+		{
+			// If we're doing reversible, assume we're not using quality layers.
+			// Yes, I know this is incorrect!
+			codestream.access_siz()->parse_string("Creversible=yes");
+			codestream.access_siz()->parse_string("Clayers=1");
+			num_layer_specs = 1;
+			layer_bytes[0] = 0;
+		}
+		else
+		{
+			// 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?
+			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++;
+
+				std::string layer_string = llformat("Clayers=%d",num_layer_specs);
+				codestream.access_siz()->parse_string(layer_string.c_str());
+			}
+			else
+			{
+				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());
+			}
+		}
+		codestream.access_siz()->finalize_all();
+		if (cpu_iterations >= 0)
+		{
+			codestream.collect_timing_stats(cpu_iterations);
+		}
+		codestream.change_appearance(transpose,vflip,hflip);
+
+		// Now we are ready for sample data processing.
+        kdc_flow_control *tile = new kdc_flow_control(&mem_in,codestream);
+        bool done = false;
+        while (!done)
+        { 
+            // Process line by line
+            done = true;
+            if (tile->advance_components())
+            {
+                done = false;
+                tile->process_components();
+            }
+        }
+
+		// Produce the compressed output
+        codestream.flush(layer_bytes,num_layer_specs);
+
+		// Cleanup
+        delete tile;
+
+		codestream.destroy();
+		if (record_stream != NULL)
+		{
+			delete record_stream;
+		}
+
+		// Now that we're done encoding, create the new data buffer for the compressed
+		// image and stick it there.
+
+		base.copyData(output_buffer, output_size);
+		base.updateData(); // set width, height
+		delete[] output_buffer;
+	}
+	catch(const char* msg)
+	{
+		base.setLastError(ll_safe_string(msg));
+		return FALSE;
+	}
+	catch( ... )
+	{
+		base.setLastError( "Unknown J2C error" );
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+BOOL LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
+{
+	// *FIX: kdu calls our callback function if there's an error, and
+	// then bombs.  To regain control, we throw an exception, and
+	// catch it here.
+	try
+	{
+		setupCodeStream(base, FALSE, MODE_FAST);
+		return TRUE;
+	}
+	catch( const char* msg )
+	{
+		base.setLastError(ll_safe_string(msg));
+		return FALSE;
+	}
+	catch( ... )
+	{
+		base.setLastError( "Unknown J2C error" );
+		return FALSE;
+	}
+}
+
+void set_default_colour_weights(kdu_params *siz)
+{
+	kdu_params *cod = siz->access_cluster(COD_params);
+	assert(cod != NULL);
+
+	bool can_use_ycc = true;
+	bool rev0=false;
+	int depth0=0, sub_x0=1, sub_y0=1;
+	for (int c=0; c < 3; c++)
+	{
+		int depth=0; siz->get(Sprecision,c,0,depth);
+		int sub_y=1; siz->get(Ssampling,c,0,sub_y);
+		int sub_x=1; siz->get(Ssampling,c,1,sub_x);
+		kdu_params *coc = cod->access_relation(-1,c);
+		bool rev=false; coc->get(Creversible,0,0,rev);
+		if (c == 0)
+		{ rev0=rev; depth0=depth; sub_x0=sub_x; sub_y0=sub_y; }
+		else if ((rev != rev0) || (depth != depth0) ||
+			(sub_x != sub_x0) || (sub_y != sub_y0))
+			can_use_ycc = false;
+	}
+	if (!can_use_ycc)
+		return;
+
+	bool use_ycc;
+	if (!cod->get(Cycc,0,0,use_ycc))
+		cod->set(Cycc,0,0,use_ycc=true);
+	if (!use_ycc)
+		return;
+	float weight;
+	if (cod->get(Clev_weights,0,0,weight) ||
+		cod->get(Cband_weights,0,0,weight))
+		return; // Weights already specified explicitly.
+
+	/* These example weights are adapted from numbers generated by Marcus Nadenau
+	at EPFL, for a viewing distance of 15 cm and a display resolution of
+	300 DPI. */
+
+	cod->parse_string("Cband_weights:C0="
+		"{0.0901},{0.2758},{0.2758},"
+		"{0.7018},{0.8378},{0.8378},{1}");
+	cod->parse_string("Cband_weights:C1="
+		"{0.0263},{0.0863},{0.0863},"
+		"{0.1362},{0.2564},{0.2564},"
+		"{0.3346},{0.4691},{0.4691},"
+		"{0.5444},{0.6523},{0.6523},"
+		"{0.7078},{0.7797},{0.7797},{1}");
+	cod->parse_string("Cband_weights:C2="
+		"{0.0773},{0.1835},{0.1835},"
+		"{0.2598},{0.4130},{0.4130},"
+		"{0.5040},{0.6464},{0.6464},"
+		"{0.7220},{0.8254},{0.8254},"
+		"{0.8769},{0.9424},{0.9424},{1}");
+}
+
+/******************************************************************************/
+/*                              transfer_bytes                                */
+/******************************************************************************/
+
+void transfer_bytes(kdu_byte *dest, kdu_line_buf &src, int gap, int precision)
+/* Transfers source samples from the supplied line buffer into the output
+byte buffer, spacing successive output samples apart by `gap' bytes
+(to allow for interleaving of colour components).  The function performs
+all necessary level shifting, type conversion, rounding and truncation. */
+{
+	int width = src.get_width();
+	if (src.get_buf32() != NULL)
+	{ // Decompressed samples have a 32-bit representation (integer or float)
+		assert(precision >= 8); // Else would have used 16 bit representation
+		kdu_sample32 *sp = src.get_buf32();
+		if (!src.is_absolute())
+		{ // Transferring normalized floating point data.
+			float scale16 = (float)(1<<16);
+			kdu_int32 val;
+
+			for (; width > 0; width--, sp++, dest+=gap)
+			{
+				val = (kdu_int32)(sp->fval*scale16);
+				val = (val+128)>>8; // May be faster than true rounding
+				val += 128;
+				if (val & ((-1)<<8))
+				{
+					val = (val<0)?0:255;
+				}
+				*dest = (kdu_byte) val;
+			}
+		}
+		else
+		{ // Transferring 32-bit absolute integers.
+			kdu_int32 val;
+			kdu_int32 downshift = precision-8;
+			kdu_int32 offset = (1<<downshift)>>1;
+
+			for (; width > 0; width--, sp++, dest+=gap)
+			{
+				val = sp->ival;
+				val = (val+offset)>>downshift;
+				val += 128;
+				if (val & ((-1)<<8))
+				{
+					val = (val<0)?0:255;
+				}
+				*dest = (kdu_byte) val;
+			}
+		}
+	}
+	else
+	{ // Source data is 16 bits.
+		kdu_sample16 *sp = src.get_buf16();
+		if (!src.is_absolute())
+		{ // Transferring 16-bit fixed point quantities
+			kdu_int16 val;
+
+			if (precision >= 8)
+			{ // Can essentially ignore the bit-depth.
+				for (; width > 0; width--, sp++, dest+=gap)
+				{
+					val = sp->ival;
+					val += (1<<(KDU_FIX_POINT-8))>>1;
+					val >>= (KDU_FIX_POINT-8);
+					val += 128;
+					if (val & ((-1)<<8))
+					{
+						val = (val<0)?0:255;
+					}
+					*dest = (kdu_byte) val;
+				}
+			}
+			else
+			{ // Need to force zeros into one or more least significant bits.
+				kdu_int16 downshift = KDU_FIX_POINT-precision;
+				kdu_int16 upshift = 8-precision;
+				kdu_int16 offset = 1<<(downshift-1);
+
+				for (; width > 0; width--, sp++, dest+=gap)
+				{
+					val = sp->ival;
+					val = (val+offset)>>downshift;
+					val <<= upshift;
+					val += 128;
+					if (val & ((-1)<<8))
+					{
+						val = (val<0)?0:(256-(1<<upshift));
+					}
+					*dest = (kdu_byte) val;
+				}
+			}
+		}
+		else
+		{ // Transferring 16-bit absolute integers.
+			kdu_int16 val;
+
+			if (precision >= 8)
+			{
+				kdu_int16 downshift = precision-8;
+				kdu_int16 offset = (1<<downshift)>>1;
+
+				for (; width > 0; width--, sp++, dest+=gap)
+				{
+					val = sp->ival;
+					val = (val+offset)>>downshift;
+					val += 128;
+					if (val & ((-1)<<8))
+					{
+						val = (val<0)?0:255;
+					}
+					*dest = (kdu_byte) val;
+				}
+			}
+			else
+			{
+				kdu_int16 upshift = 8-precision;
+
+				for (; width > 0; width--, sp++, dest+=gap)
+				{
+					val = sp->ival;
+					val <<= upshift;
+					val += 128;
+					if (val & ((-1)<<8))
+					{
+						val = (val<0)?0:(256-(1<<upshift));
+					}
+					*dest = (kdu_byte) val;
+				}
+			}
+		}
+	}
+}
+
+LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap)
+{
+	S32 c;
+
+	mTile = tile;
+	mBuf = buf;
+	mRowGap = row_gap;
+
+	mNumComponents = tile.get_num_components();
+
+	llassert(mNumComponents<=4);
+	mUseYCC = tile.get_ycc();
+
+	for (c=0; c<4; ++c)
+	{
+		mReversible[c] = false;
+		mBitDepths[c] = 0;
+	}
+
+	// Open tile-components and create processing engines and resources
+	for (c=0; c < mNumComponents; c++)
+	{
+		mComps[c] = mTile.access_component(c);
+		mReversible[c] = mComps[c].get_reversible();
+		mBitDepths[c] = mComps[c].get_bit_depth();
+		kdu_resolution res = mComps[c].access_resolution(); // Get top resolution
+		kdu_dims comp_dims; res.get_dims(comp_dims);
+		if (c == 0)
+		{
+			mDims = comp_dims;
+		}
+		else
+		{
+			llassert(mDims == comp_dims); // Safety check; the caller has ensured this
+		}
+		bool use_shorts = (mComps[c].get_bit_depth(true) <= 16);
+		mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts);
+		if (res.which() == 0) // No DWT levels used
+		{
+			mEngines[c] = kdu_decoder(res.access_subband(LL_BAND),&mAllocator,use_shorts);
+		}
+		else
+		{
+			mEngines[c] = kdu_synthesis(res,&mAllocator,use_shorts);
+		}
+	}
+	mAllocator.finalize(); // Actually creates buffering resources
+	for (c=0; c < mNumComponents; c++)
+	{
+		mLines[c].create(); // Grabs resources from the allocator.
+	}
+}
+
+LLKDUDecodeState::~LLKDUDecodeState()
+{
+	S32 c;
+	// Cleanup
+	for (c=0; c < mNumComponents; c++)
+	{
+		mEngines[c].destroy(); // engines are interfaces; no default destructors
+	}
+
+	mTile.close();
+}
+
+BOOL LLKDUDecodeState::processTileDecode(F32 decode_time, BOOL limit_time)
+/* Decompresses a tile, writing the data into the supplied byte buffer.
+The buffer contains interleaved image components, if there are any.
+Although you may think of the buffer as belonging entirely to this tile,
+the `buf' pointer may actually point into a larger buffer representing
+multiple tiles.  For this reason, `row_gap' is needed to identify the
+separation between consecutive rows in the real buffer. */
+{
+	S32 c;
+	// Now walk through the lines of the buffer, recovering them from the
+	// relevant tile-component processing engines.
+
+	LLTimer decode_timer;
+	while (mDims.size.y--)
+	{
+		for (c=0; c < mNumComponents; c++)
+		{
+			mEngines[c].pull(mLines[c],true);
+		}
+		if ((mNumComponents >= 3) && mUseYCC)
+		{
+			kdu_convert_ycc_to_rgb(mLines[0],mLines[1],mLines[2]);
+		}
+		for (c=0; c < mNumComponents; c++)
+		{
+			transfer_bytes(mBuf+c,mLines[c],mNumComponents,mBitDepths[c]);
+		}
+		mBuf += mRowGap;
+		if (mDims.size.y % 10)
+		{
+			if (limit_time && decode_timer.getElapsedTimeF32() > decode_time)
+			{
+				return FALSE;
+			}
+		}
+	}
+	return TRUE;
+}
+
+// kdc_flow_control 
+
+kdc_flow_control::kdc_flow_control (kdu_image_in_base *img_in, kdu_codestream codestream)
+{
+    int n;
+    
+    this->codestream = codestream;
+    codestream.get_valid_tiles(valid_tile_indices);
+    tile_idx = valid_tile_indices.pos;
+    tile = codestream.open_tile(tile_idx,NULL);
+    
+    // Set up the individual components
+    num_components = codestream.get_num_components(true);
+    components = new kdc_component_flow_control[num_components];
+    count_delta = 0;
+    kdc_component_flow_control *comp = components;
+    for (n = 0; n < num_components; n++, comp++)
+    {
+        comp->line = NULL;
+        comp->reader = img_in;
+        kdu_coords subsampling;  
+        codestream.get_subsampling(n,subsampling,true);
+        kdu_dims dims;  
+        codestream.get_tile_dims(tile_idx,n,dims,true);
+        comp->vert_subsampling = subsampling.y;
+        if ((n == 0) || (comp->vert_subsampling < count_delta))
+        {
+            count_delta = comp->vert_subsampling;
+        }
+        comp->ratio_counter = 0;
+        comp->remaining_lines = comp->initial_lines = dims.size.y;
+    }
+    assert(num_components >= 0);
+    
+    tile.set_components_of_interest(num_components);
+    max_buffer_memory = engine.create(codestream,tile,false,NULL,false,1,NULL,NULL,false);
+}
+
+kdc_flow_control::~kdc_flow_control()
+{
+    if (components != NULL)
+        delete[] components;
+    if (engine.exists())
+        engine.destroy();
+}
+
+bool kdc_flow_control::advance_components()
+{
+    bool found_line = false;
+    while (!found_line)
+    {
+        bool all_done = true;
+        kdc_component_flow_control *comp = components;
+        for (int n = 0; n < num_components; n++, comp++)
+        {
+            assert(comp->ratio_counter >= 0);
+            if (comp->remaining_lines > 0)
+            {
+                all_done = false;
+                comp->ratio_counter -= count_delta;
+                if (comp->ratio_counter < 0)
+                {
+                    found_line = true;
+                    comp->line = engine.exchange_line(n,NULL,NULL);
+                    assert(comp->line != NULL);
+					if (comp->line->get_width())
+					{
+						comp->reader->get(n,*(comp->line),0);
+					}
+                }
+            }
+        }
+        if (all_done)
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+void kdc_flow_control::process_components()
+{
+    kdc_component_flow_control *comp = components;
+    for (int n = 0; n < num_components; n++, comp++)
+    {
+        if (comp->ratio_counter < 0)
+        {
+            comp->ratio_counter += comp->vert_subsampling;
+            assert(comp->ratio_counter >= 0);
+            assert(comp->remaining_lines > 0);
+            comp->remaining_lines--;
+            assert(comp->line != NULL);
+            engine.exchange_line(n,comp->line,NULL);
+            comp->line = NULL;
+        }
+    }
+}
diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h
new file mode 100644
index 0000000000000000000000000000000000000000..5628f69eeb6b44796af332978547f31bce0d2493
--- /dev/null
+++ b/indra/llkdu/llimagej2ckdu.h
@@ -0,0 +1,90 @@
+/** 
+ * @file llimagej2ckdu.h
+ * @brief This is an implementation of JPEG2000 encode/decode using Kakadu
+ *
+ * $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$
+ */
+
+#ifndef LL_LLIMAGEJ2CKDU_H
+#define LL_LLIMAGEJ2CKDU_H
+
+#include "llimagej2c.h"
+
+//
+// KDU core header files
+//
+#include "kdu_elementary.h"
+#include "kdu_messaging.h"
+#include "kdu_params.h"
+#include "kdu_compressed.h"
+#include "kdu_sample_processing.h"
+
+class LLKDUDecodeState;
+class LLKDUMemSource;
+
+class LLImageJ2CKDU : public LLImageJ2CImpl
+{	
+public:
+	enum ECodeStreamMode 
+	{
+		MODE_FAST = 0,
+		MODE_RESILIENT = 1,
+		MODE_FUSSY = 2
+	};
+	LLImageJ2CKDU();
+	virtual ~LLImageJ2CKDU();
+	
+protected:
+	/*virtual*/ BOOL getMetadata(LLImageJ2C &base);
+	/*virtual*/ BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count);
+	/*virtual*/ BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
+								BOOL reversible=FALSE);
+
+private:
+	void setupCodeStream(LLImageJ2C &base, BOOL keep_codestream, ECodeStreamMode mode);
+	void cleanupCodeStream();
+	BOOL initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count );
+
+	// Encode variable
+	LLKDUMemSource *mInputp;
+	kdu_codestream *mCodeStreamp;
+	kdu_coords *mTPosp; // tile position
+	kdu_dims *mTileIndicesp;
+
+	// Temporary variables for in-progress decodes...
+	LLImageRaw *mRawImagep;
+	LLKDUDecodeState *mDecodeState;
+};
+
+#if LL_WINDOWS
+# define LLSYMEXPORT __declspec(dllexport)
+#elif LL_LINUX
+# define LLSYMEXPORT __attribute__ ((visibility("default")))
+#else
+# define LLSYMEXPORT
+#endif
+
+extern "C" LLSYMEXPORT const char* engineInfoLLImageJ2CKDU();
+extern "C" LLSYMEXPORT LLImageJ2CKDU* createLLImageJ2CKDU();
+extern "C" LLSYMEXPORT void destroyLLImageJ2CKDU(LLImageJ2CKDU* kdu);
+
+#endif
diff --git a/indra/llkdu/llkdumem.cpp b/indra/llkdu/llkdumem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1f549cbbe0daa23c0f439f976bc60ef55ce9f818
--- /dev/null
+++ b/indra/llkdu/llkdumem.cpp
@@ -0,0 +1,196 @@
+ /** 
+ * @file llkdumem.cpp
+ * @brief Helper class for kdu memory management
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "llkdumem.h"
+#include "llerror.h"
+
+#if defined(LL_WINDOWS)
+# pragma warning(disable: 4702) // unreachable code
+#endif
+
+LLKDUMemIn::LLKDUMemIn(const U8 *data,
+					   const U32 size,
+					   const U16 width,
+					   const U16 height,
+					   const U8 in_num_components,
+					   siz_params *siz)
+{
+	U8 n;
+
+	first_comp_idx = 0;
+	rows = height;
+	cols = width;
+	num_components = in_num_components;
+	alignment_bytes = 0;
+
+	for (n=0; n<3; ++n)
+	{
+		precision[n] = 0;
+	}
+
+	for (n=0; n < num_components; ++n)
+	{
+		siz->set(Sdims,n,0,rows);
+		siz->set(Sdims,n,1,cols);
+		siz->set(Ssigned,n,0,false);
+		siz->set(Sprecision,n,0,8);
+	}
+	incomplete_lines = NULL;
+	free_lines = NULL;
+	num_unread_rows = rows;
+
+	mData = data;
+	mDataSize = size;
+	mCurPos = 0;
+}
+
+LLKDUMemIn::~LLKDUMemIn()
+{
+	if ((num_unread_rows > 0) || (incomplete_lines != NULL))
+	{
+		kdu_warning w;
+		w << "Not all rows of image components "
+			<< first_comp_idx << " through "
+			<< first_comp_idx+num_components-1
+			<< " were consumed!";
+	}
+	image_line_buf *tmp;
+	while ((tmp=incomplete_lines) != NULL)
+    {
+		incomplete_lines = tmp->next;
+		delete tmp; 
+	}
+	while ((tmp=free_lines) != NULL)
+    {
+		free_lines = tmp->next;
+		delete tmp;
+	}
+}
+
+
+bool LLKDUMemIn::get(int comp_idx, kdu_line_buf &line, int x_tnum)
+{
+	int idx = comp_idx - this->first_comp_idx;
+	assert((idx >= 0) && (idx < num_components));
+	x_tnum = x_tnum*num_components+idx;
+	image_line_buf *scan, *prev=NULL;
+	for (scan=incomplete_lines; scan != NULL; prev=scan, scan=scan->next)
+    {
+		assert(scan->next_x_tnum >= x_tnum);
+		if (scan->next_x_tnum == x_tnum)
+		{
+			break;
+		}
+    }
+	if (scan == NULL)
+    { // Need to read a new image line.
+		assert(x_tnum == 0); // Must consume in very specific order.
+		if (num_unread_rows == 0)
+		{
+	        return false;
+		}
+		if ((scan = free_lines) == NULL)
+		{
+			scan = new image_line_buf(cols+3,num_components);
+		}
+		free_lines = scan->next;
+		if (prev == NULL)
+		{
+	        incomplete_lines = scan;
+		}
+		else
+		{
+			prev->next = scan;
+		}
+
+		// Copy from image buffer into scan.
+		memcpy(scan->buf, mData+mCurPos, cols*num_components);
+		mCurPos += cols*num_components;
+
+		num_unread_rows--;
+		scan->accessed_samples = 0;
+		scan->next_x_tnum = 0;
+    }
+
+	assert((cols-scan->accessed_samples) >= line.get_width());
+
+	int comp_offset = idx;
+	kdu_byte *sp = scan->buf+num_components*scan->accessed_samples + comp_offset;
+	int n=line.get_width();
+
+	if (line.get_buf32() != NULL)
+	{
+		kdu_sample32 *dp = line.get_buf32();
+		if (line.is_absolute())
+		{ // 32-bit absolute integers
+			for (; n > 0; n--, sp+=num_components, dp++)
+			{
+				dp->ival = ((kdu_int32)(*sp)) - 128;
+			}
+		}
+		else
+		{ // true 32-bit floats
+			for (; n > 0; n--, sp+=num_components, dp++)
+			{
+				dp->fval = (((float)(*sp)) / 256.0F) - 0.5F;
+			}
+		}
+	}
+	else
+    {
+		kdu_sample16 *dp = line.get_buf16();
+		if (line.is_absolute())
+		{ // 16-bit absolute integers
+			for (; n > 0; n--, sp+=num_components, dp++)
+			{
+				dp->ival = ((kdu_int16)(*sp)) - 128;
+			}
+		}
+		else
+		{ // 16-bit normalized representation.
+			for (; n > 0; n--, sp+=num_components, dp++)
+			{
+				dp->ival = (((kdu_int16)(*sp)) - 128) << (KDU_FIX_POINT-8);
+			}
+		}
+    }
+
+	scan->next_x_tnum++;
+	if (idx == (num_components-1))
+	{
+		scan->accessed_samples += line.get_width();
+	}
+	if (scan->accessed_samples == cols)
+	{ // Send empty line to free list.
+		assert(scan == incomplete_lines);
+		incomplete_lines = scan->next;
+		scan->next = free_lines;
+		free_lines = scan;
+	}
+
+  return true;
+}
diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h
new file mode 100644
index 0000000000000000000000000000000000000000..7064de4408d682aa87972804c592cfd917646bfd
--- /dev/null
+++ b/indra/llkdu/llkdumem.h
@@ -0,0 +1,145 @@
+/** 
+ * @file llkdumem.h
+ * @brief Helper class for kdu memory management
+ *
+ * $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$
+ */
+
+#ifndef LL_LLKDUMEM_H
+#define LL_LLKDUMEM_H
+
+// Support classes for reading and writing from memory buffers in KDU
+#include "kdu_image.h"
+#include "kdu_elementary.h"
+#include "kdu_messaging.h"
+#include "kdu_params.h"
+#include "kdu_compressed.h"
+#include "kdu_sample_processing.h"
+#include "image_local.h"
+#include "stdtypes.h"
+
+class LLKDUMemSource: public kdu_compressed_source
+{
+public: // Member functions
+	LLKDUMemSource(U8 *input_buffer, U32 size)
+	{
+		mData = input_buffer;
+		mSize = size;
+		mCurPos = 0;
+	}
+
+    ~LLKDUMemSource()
+	{
+	}
+
+    int read(kdu_byte *buf, int num_bytes)
+	{
+		U32 num_out;
+		num_out = num_bytes;
+
+		if ((mSize - mCurPos) < (U32)num_bytes)
+		{
+			num_out = mSize -mCurPos;
+		}
+		memcpy(buf, mData + mCurPos, num_out);
+		mCurPos += num_out;
+		return num_out;
+	}
+
+	void reset()
+	{
+		mCurPos = 0;
+	}
+
+private: // Data
+	U8 *mData;
+	U32 mSize;
+	U32 mCurPos;
+};
+
+class LLKDUMemTarget: public kdu_compressed_target
+{
+public: // Member functions
+	LLKDUMemTarget(U8 *output_buffer, U32 &output_size, const U32 buffer_size)
+	{
+		mData = output_buffer;
+		mSize = buffer_size;
+		mCurPos = 0;
+		mOutputSize = &output_size;
+	}
+
+    ~LLKDUMemTarget()
+    {
+	}
+
+    bool write(const kdu_byte *buf, int num_bytes)
+	{
+		U32 num_out;
+		num_out = num_bytes;
+
+		if ((mSize - mCurPos) < (U32)num_bytes)
+		{
+			num_out = mSize - mCurPos;
+			memcpy(mData + mCurPos, buf, num_out);
+			return false;
+		}
+		memcpy(mData + mCurPos, buf, num_out);
+		mCurPos += num_out;
+		*mOutputSize = mCurPos;
+		return true;
+	}
+	
+private: // Data
+	U8 *mData;
+	U32 mSize;
+	U32 mCurPos;
+	U32 *mOutputSize;
+};
+
+class LLKDUMemIn : public kdu_image_in_base
+{
+public: // Member functions
+    LLKDUMemIn(const U8 *data,
+				const U32 size,
+				const U16 rows,
+				const U16 cols,
+				U8 in_num_components,
+				siz_params *siz);
+    ~LLKDUMemIn();
+
+    bool get(int comp_idx, kdu_line_buf &line, int x_tnum);
+
+private: // Data
+	const U8 *mData;
+    int first_comp_idx;
+    int num_components;
+    int rows, cols;
+    int alignment_bytes; // Number of 0's at end of each line.
+    int precision[3];
+    image_line_buf *incomplete_lines; // Each "sample" represents a full pixel
+    image_line_buf *free_lines;
+    int num_unread_rows;
+
+	U32 mCurPos;
+	U32 mDataSize;
+};
+#endif
diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ccee4bb647445c17babb176106096135abee1a4
--- /dev/null
+++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp
@@ -0,0 +1,248 @@
+/** 
+ * @file llimagej2ckdu_test.cpp
+ * @author Merov Linden
+ * @date 2010-12-17
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+// Class to test 
+#include "../llimagej2ckdu.h"
+#include "../llkdumem.h"
+// Tut header
+#include "../test/lltut.h"
+
+// -------------------------------------------------------------------------------------------
+// Stubbing: Declarations required to link and run the class being tested
+// Notes: 
+// * Add here stubbed implementation of the few classes and methods used in the class to be tested
+// * Add as little as possible (let the link errors guide you)
+// * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
+// * A simulator for a class can be implemented here. Please comment and document thoroughly.
+
+// End Stubbing
+// -------------------------------------------------------------------------------------------
+// Stubb the LL Image Classes
+LLImageRaw::LLImageRaw() { }
+LLImageRaw::~LLImageRaw() { }
+U8* LLImageRaw::allocateData(S32 ) { return NULL; }
+void LLImageRaw::deleteData() { }
+U8* LLImageRaw::reallocateData(S32 ) { return NULL; }
+BOOL LLImageRaw::resize(U16, U16, S8) { return TRUE; } // this method always returns TRUE...
+
+LLImageBase::LLImageBase()
+: mData(NULL),
+mDataSize(0),
+mWidth(0),
+mHeight(0),
+mComponents(0),
+mBadBufferAllocation(false),
+mAllowOverSize(false),
+mMemType(LLMemType::MTYPE_IMAGEBASE)
+{ }
+LLImageBase::~LLImageBase() { }
+U8* LLImageBase::allocateData(S32 ) { return NULL; }
+void LLImageBase::deleteData() { }
+void LLImageBase::dump() { }
+const U8* LLImageBase::getData() const { return NULL; }
+U8* LLImageBase::getData() { return NULL; }
+U8* LLImageBase::reallocateData(S32 ) { return NULL; }
+void LLImageBase::sanityCheck() { }
+void LLImageBase::setSize(S32 , S32 , S32 ) { }
+
+LLImageJ2CImpl::~LLImageJ2CImpl() { }
+
+LLImageFormatted::LLImageFormatted(S8 ) { }
+LLImageFormatted::~LLImageFormatted() { }
+U8* LLImageFormatted::allocateData(S32 ) { return NULL; }
+S32 LLImageFormatted::calcDataSize(S32 ) { return 0; }
+S32 LLImageFormatted::calcDiscardLevelBytes(S32 ) { return 0; }
+BOOL LLImageFormatted::decodeChannels(LLImageRaw*, F32, S32, S32) { return FALSE; }
+BOOL LLImageFormatted::copyData(U8 *, S32) { return TRUE; }  // this method always returns TRUE...
+void LLImageFormatted::deleteData() { }
+void LLImageFormatted::dump() { }
+U8* LLImageFormatted::reallocateData(S32 ) { return NULL; }
+void LLImageFormatted::resetLastError() { }
+void LLImageFormatted::sanityCheck() { }
+void LLImageFormatted::setLastError(const std::string& , const std::string& ) { }
+
+LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C) { }
+LLImageJ2C::~LLImageJ2C() { }
+S32 LLImageJ2C::calcDataSize(S32 ) { return 0; }
+S32 LLImageJ2C::calcDiscardLevelBytes(S32 ) { return 0; }
+S32 LLImageJ2C::calcHeaderSize() { return 0; }
+BOOL LLImageJ2C::decode(LLImageRaw*, F32) { return FALSE; }
+BOOL LLImageJ2C::decodeChannels(LLImageRaw*, F32, S32, S32 ) { return FALSE; }
+void LLImageJ2C::decodeFailed() { }
+BOOL LLImageJ2C::encode(const LLImageRaw*, F32) { return FALSE; }
+S8  LLImageJ2C::getRawDiscardLevel() { return 0; }
+void LLImageJ2C::resetLastError() { }
+void LLImageJ2C::setLastError(const std::string&, const std::string&) { }
+BOOL LLImageJ2C::updateData() { return FALSE; }
+void LLImageJ2C::updateRawDiscardLevel() { }
+
+LLKDUMemIn::LLKDUMemIn(const U8*, const U32, const U16, const U16, const U8, siz_params*) { }
+LLKDUMemIn::~LLKDUMemIn() { }
+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; }
+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* ) { }
+kdu_resolution kdu_tile_comp::access_resolution() { kdu_resolution a; return a; }
+int kdu_tile_comp::get_bit_depth(bool ) { return 8; }
+bool kdu_tile_comp::get_reversible() { return false; }
+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; }
+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) { }
+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::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; }
+bool kdu_params::get(const char*, int, int, float&, bool, bool, bool) { return false; }
+bool kdu_params::get(const char*, int, int, int&, bool, bool, bool) { return false; }
+kdu_params* kdu_params::access_relation(int, int, int, bool) { return NULL; }
+kdu_params* kdu_params::access_cluster(const char*) { return NULL; }
+void kdu_codestream::set_fast() { }
+void kdu_codestream::set_fussy() { }
+void kdu_codestream::get_dims(int, kdu_dims&, bool ) { }
+void kdu_codestream::change_appearance(bool, bool, bool) { }
+void kdu_codestream::get_tile_dims(kdu_coords, int, kdu_dims&, bool ) { }
+void kdu_codestream::destroy() { }
+void kdu_codestream::collect_timing_stats(int ) { }
+void kdu_codestream::set_max_bytes(kdu_long, bool, bool ) { }
+void kdu_codestream::get_valid_tiles(kdu_dims& ) { }
+void kdu_codestream::create(siz_params*, kdu_compressed_target*, kdu_dims*, int, kdu_long ) { }
+void kdu_codestream::create(kdu_compressed_source*, kdu_thread_env*) { }
+void kdu_codestream::apply_input_restrictions( int, int, int, int, kdu_dims*, kdu_component_access_mode ) { }
+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; }
+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; }
+bool kdu_codestream_comment::put_text(const char*) { return false; }
+void kdu_customize_warnings(kdu_message*) { }
+void kdu_customize_errors(kdu_message*) { }
+void kdu_convert_ycc_to_rgb(kdu_line_buf&, kdu_line_buf&, kdu_line_buf&, int) { }
+kdu_long kdu_multi_analysis::create(kdu_codestream, kdu_tile, bool, kdu_roi_image*, bool, int, kdu_thread_env*, kdu_thread_queue*, bool ) { kdu_long a = 0; return a; }
+siz_params::siz_params() : kdu_params(NULL, false, false, false, false, false) { }
+void siz_params::finalize(bool ) { }
+void siz_params::copy_with_xforms(kdu_params*, int, int, bool, bool, bool) { }
+int siz_params::write_marker_segment(kdu_output*, kdu_params*, int) { return 0; }
+bool siz_params::check_marker_segment(kdu_uint16, int, kdu_byte a[], int&) { return false; }
+bool siz_params::read_marker_segment(kdu_uint16, int, kdu_byte a[], int) { return false; }
+
+// -------------------------------------------------------------------------------------------
+// TUT
+// -------------------------------------------------------------------------------------------
+
+namespace tut
+{
+	// Test wrapper declarations
+	struct llimagej2ckdu_test
+	{
+		// Derived test class
+		class LLTestImageJ2CKDU : public LLImageJ2CKDU
+		{
+		public:
+			// Provides public access to some protected methods for testing
+			BOOL callGetMetadata(LLImageJ2C &base) { return getMetadata(base); }
+			BOOL callDecodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
+			{
+				return decodeImpl(base, raw_image, decode_time, first_channel, max_channel_count);
+			}
+			BOOL callEncodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text)
+			{
+				return encodeImpl(base, raw_image, comment_text);
+			}
+		};
+		// Instance to be tested
+		LLTestImageJ2CKDU* mImage;
+		
+		// Constructor and destructor of the test wrapper
+		llimagej2ckdu_test()
+		{
+			mImage = new LLTestImageJ2CKDU;
+		}
+		~llimagej2ckdu_test()
+		{
+			delete mImage;
+		}
+	};
+	
+	// Tut templating thingamagic: test group, object and test instance
+	typedef test_group<llimagej2ckdu_test> llimagej2ckdu_t;
+	typedef llimagej2ckdu_t::object llimagej2ckdu_object_t;
+	tut::llimagej2ckdu_t tut_llimagej2ckdu("LLImageJ2CKDU");
+	
+	// ---------------------------------------------------------------------------------------
+	// Test functions
+	// Notes:
+	// * Test as many as you possibly can without requiring a full blown simulation of everything
+	// * The tests are executed in sequence so the test instance state may change between calls
+	// * Remember that you cannot test private methods with tut
+	// ---------------------------------------------------------------------------------------
+
+	// Test 1 : test getMetadata()
+	template<> template<>
+	void llimagej2ckdu_object_t::test<1>()
+	{
+		LLImageJ2C* image = new LLImageJ2C();
+		BOOL res = mImage->callGetMetadata(*image);
+		// Trying to set up a data stream with all NIL values and stubbed KDU will "work" and return TRUE
+		// Note that is linking with KDU, that call will throw an exception and fail, returning FALSE
+		ensure("getMetadata() test failed", res == TRUE);
+	}
+
+	// Test 2 : test decodeImpl()
+	template<> template<>
+	void llimagej2ckdu_object_t::test<2>()
+	{
+		LLImageJ2C* image = new LLImageJ2C();
+		LLImageRaw* raw = new LLImageRaw();
+		BOOL res = mImage->callDecodeImpl(*image, *raw, 0.0, 0, 0);
+		// Decoding returns TRUE whenever there's nothing else to do, including if decoding failed, so we'll get TRUE here
+		ensure("decodeImpl() test failed", res == TRUE);
+	}
+
+	// Test 3 : test encodeImpl()
+	template<> template<>
+	void llimagej2ckdu_object_t::test<3>()
+	{
+		LLImageJ2C* image = new LLImageJ2C();
+		LLImageRaw* raw = new LLImageRaw();
+		BOOL res = mImage->callEncodeImpl(*image, *raw, NULL);
+		// Encoding returns TRUE unless an exception was raised, so we'll get TRUE here though nothing really was done
+		ensure("encodeImpl() test failed", res == TRUE);
+	}
+}
diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
index b26d412e9fa688b80f4c2eda69549f9fba579989..27a368df3d0e05ed349dcfa86efd491958f8dc03 100644
--- a/indra/llmessage/llassetstorage.cpp
+++ b/indra/llmessage/llassetstorage.cpp
@@ -513,6 +513,10 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LL
 
 }
 
+//
+// *NOTE:  Logic here is replicated in LLViewerAssetStorage::_queueDataRequest.
+// Changes here may need to be replicated in the viewer's derived class.
+//
 void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType atype,
 									   LLGetAssetCallback callback,
 									   void *user_data, BOOL duplicate,
diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt
index d3a73058c4a7a6fbe0b87eabcfe263a65ef53468..1dc05e0b20ba95a29fd4377f610a763f9eefa67f 100644
--- a/indra/llplugin/CMakeLists.txt
+++ b/indra/llplugin/CMakeLists.txt
@@ -20,6 +20,7 @@ include_directories(
     ${LLRENDER_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
+    ${LLQTWEBKIT_INCLUDE_DIR}
     )
 
 set(llplugin_SOURCE_FILES
diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp
index 69ed0fb09c1406ab8fa7c16d27f3d5f7ed992434..595c470a195cfb12ee9425db6910548be5e99882 100644
--- a/indra/llplugin/llpluginclassmedia.cpp
+++ b/indra/llplugin/llpluginclassmedia.cpp
@@ -160,7 +160,7 @@ void LLPluginClassMedia::idle(void)
 		mPlugin->idle();
 	}
 	
-	if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked()))
+	if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked()) || (mOwner == NULL))
 	{
 		// Can't process a size change at this time
 	}
@@ -522,7 +522,15 @@ bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifie
 			}
 		break;
 	}
-	
+
+#if LL_DARWIN	
+	if(modifiers & MASK_ALT)
+	{
+		// Option-key modified characters should be handled by the unicode input path instead of this one.
+		result = false;
+	}
+#endif
+
 	if(result)
 	{
 		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "key_event");
@@ -674,7 +682,21 @@ void LLPluginClassMedia::sendPickFileResponse(const std::string &file)
 {
 	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "pick_file_response");
 	message.setValue("file", file);
-	if(mPlugin->isBlocked())
+	if(mPlugin && mPlugin->isBlocked())
+	{
+		// If the plugin sent a blocking pick-file request, the response should unblock it.
+		message.setValueBoolean("blocking_response", true);
+	}
+	sendMessage(message);
+}
+
+void LLPluginClassMedia::sendAuthResponse(bool ok, const std::string &username, const std::string &password)
+{
+	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "auth_response");
+	message.setValueBoolean("ok", ok);
+	message.setValue("username", username);
+	message.setValue("password", password);
+	if(mPlugin && mPlugin->isBlocked())
 	{
 		// If the plugin sent a blocking pick-file request, the response should unblock it.
 		message.setValueBoolean("blocking_response", true);
@@ -947,6 +969,12 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
 		{
 			mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PICK_FILE_REQUEST);
 		}
+		else if(message_name == "auth_request")
+		{
+			mAuthURL = message.getValue("url");
+			mAuthRealm = message.getValue("realm");
+			mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_AUTH_REQUEST);
+		}
 		else
 		{
 			LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL;
@@ -1019,6 +1047,15 @@ void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message)
 				
 			mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_GEOMETRY_CHANGE);
 		}
+		else if(message_name == "link_hovered")
+		{
+			// text is not currently used -- the tooltip hover text is taken from the "title".
+			mHoverLink = message.getValue("link");
+			mHoverText = message.getValue("title");
+			// message.getValue("text");
+				
+			mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_LINK_HOVERED);
+		}
 		else
 		{
 			LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL;
@@ -1192,6 +1229,20 @@ void LLPluginClassMedia::proxyWindowClosed(const std::string &uuid)
 	sendMessage(message);
 }
 
+void LLPluginClassMedia::ignore_ssl_cert_errors(bool ignore)
+{
+	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "ignore_ssl_cert_errors");
+	message.setValueBoolean("ignore", ignore);
+	sendMessage(message);
+}
+
+void LLPluginClassMedia::addCertificateFilePath(const std::string& path)
+{
+	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "add_certificate_file_path");
+	message.setValue("path", path);
+	sendMessage(message);
+}
+
 void LLPluginClassMedia::crashPlugin()
 {
 	LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "crash");
diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h
index 9cb67fe9091b0f018d0cc19504add6e2caea6a86..c826e13c4078abca91df7eb338a5155415c32c97 100644
--- a/indra/llplugin/llpluginclassmedia.h
+++ b/indra/llplugin/llpluginclassmedia.h
@@ -85,6 +85,8 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
 	
 	void setBackgroundColor(LLColor4 color) { mBackgroundColor = color; };
 	
+	void setOwner(LLPluginClassMediaOwner *owner) { mOwner = owner; };
+	
 	// Returns true if all of the texture parameters (depth, format, size, and texture size) are set up and consistent.
 	// This will initially be false, and will also be false for some time after setSize while the resize is processed.
 	// Note that if this returns true, it is safe to use all the get() functions above without checking for invalid return values
@@ -159,6 +161,8 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
 	
 	void sendPickFileResponse(const std::string &file);
 
+	void sendAuthResponse(bool ok, const std::string &username, const std::string &password);
+
 	// Valid after a MEDIA_EVENT_CURSOR_CHANGED event
 	std::string getCursorName() const { return mCursorName; };
 
@@ -198,6 +202,8 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
 	void setBrowserUserAgent(const std::string& user_agent);
 	void proxyWindowOpened(const std::string &target, const std::string &uuid);
 	void proxyWindowClosed(const std::string &uuid);
+	void ignore_ssl_cert_errors(bool ignore);
+	void addCertificateFilePath(const std::string& path);
 	
 	// This is valid after MEDIA_EVENT_NAVIGATE_BEGIN or MEDIA_EVENT_NAVIGATE_COMPLETE
 	std::string	getNavigateURI() const { return mNavigateURI; };
@@ -231,7 +237,15 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
 	S32 getGeometryY() const { return mGeometryY; };
 	S32 getGeometryWidth() const { return mGeometryWidth; };
 	S32 getGeometryHeight() const { return mGeometryHeight; };
+	
+	// These are valid during MEDIA_EVENT_AUTH_REQUEST
+	std::string	getAuthURL() const { return mAuthURL; };
+	std::string	getAuthRealm() const { return mAuthRealm; };
 
+	// These are valid during MEDIA_EVENT_LINK_HOVERED
+	std::string	getHoverText() const { return mHoverText; };
+	std::string	getHoverLink() const { return mHoverLink; };
+	
 	std::string getMediaName() const { return mMediaName; };
 	std::string getMediaDescription() const { return mMediaDescription; };
 
@@ -369,6 +383,10 @@ class LLPluginClassMedia : public LLPluginProcessParentOwner
 	S32				mGeometryY;
 	S32				mGeometryWidth;
 	S32				mGeometryHeight;
+	std::string		mAuthURL;
+	std::string		mAuthRealm;
+	std::string		mHoverText;
+	std::string		mHoverLink;
 	
 	/////////////////////////////////////////
 	// media_time class
diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h
index c9efff216c076292fc3237c9de81962efc563c11..42e93cc6d7e12da5b69b30cd8d516b33fefa673e 100644
--- a/indra/llplugin/llpluginclassmediaowner.h
+++ b/indra/llplugin/llpluginclassmediaowner.h
@@ -59,7 +59,11 @@ class LLPluginClassMediaOwner
 		MEDIA_EVENT_GEOMETRY_CHANGE,		// The plugin requested its window geometry be changed (per the javascript window interface)
 		
 		MEDIA_EVENT_PLUGIN_FAILED_LAUNCH,	// The plugin failed to launch 
-		MEDIA_EVENT_PLUGIN_FAILED			// The plugin died unexpectedly
+		MEDIA_EVENT_PLUGIN_FAILED,			// The plugin died unexpectedly
+
+		MEDIA_EVENT_AUTH_REQUEST,			// The plugin wants to display an auth dialog
+
+		MEDIA_EVENT_LINK_HOVERED			// Got a "link hovered" event from the plugin
 		
 	} EMediaEvent;
 	
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 864f3f699e26096acd7b6746533f8f50e5ada495..33ab2e93b5cf8c7b0f4c3fc692421395880f736e 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -111,6 +111,7 @@ set(llui_SOURCE_FILES
     llviewmodel.cpp
     llview.cpp
     llviewquery.cpp
+    llwindowshade.cpp
     )
     
 set(llui_HEADER_FILES
@@ -210,6 +211,7 @@ set(llui_HEADER_FILES
     llviewmodel.h
     llview.h
     llviewquery.h
+    llwindowshade.h
     )
 
 set_source_files_properties(${llui_HEADER_FILES}
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 1265733bf5be3213a10d11280e6e5bf00e3db7ca..d30697e1784487c2f9694dae189b3d3ac9e11bf3 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -2910,7 +2910,9 @@ bool LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, const std::str
 	params.from_xui = true;
 	applyXUILayout(params, parent);
  	initFromParams(params);
-	
+	// chrome floaters don't take focus at all
+	setFocusRoot(!getIsChrome());
+
 	initFloater(params);
 	
 	LLMultiFloater* last_host = LLFloater::getFloaterHost();
diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp
index ac30fce39236297601fafae6939a792f030ec055..19ac4c58a851be16bd8f8e734b048a0ebaf26508 100644
--- a/indra/llui/lllayoutstack.cpp
+++ b/indra/llui/lllayoutstack.cpp
@@ -38,6 +38,12 @@
 static LLDefaultChildRegistry::Register<LLLayoutStack> register_layout_stack("layout_stack");
 static LLLayoutStack::LayoutStackRegistry::Register<LLLayoutPanel> register_layout_panel("layout_panel");
 
+void LLLayoutStack::OrientationNames::declareValues()
+{
+	declare("horizontal", HORIZONTAL);
+	declare("vertical", VERTICAL);
+}
+
 //
 // LLLayoutPanel
 //
@@ -47,47 +53,47 @@ LLLayoutPanel::LLLayoutPanel(const Params& p)
  	mMaxDim(p.max_dim), 
  	mAutoResize(p.auto_resize),
  	mUserResize(p.user_resize),
-		mCollapsed(FALSE),
-		mCollapseAmt(0.f),
-		mVisibleAmt(1.f), // default to fully visible
-		mResizeBar(NULL) 
-	{
+	mCollapsed(FALSE),
+	mCollapseAmt(0.f),
+	mVisibleAmt(1.f), // default to fully visible
+	mResizeBar(NULL) 
+{
 	// panels initialized as hidden should not start out partially visible
 	if (!getVisible())
-		{
+	{
 		mVisibleAmt = 0.f;
-		}
-		}
+	}
+}
 
 void LLLayoutPanel::initFromParams(const Params& p)
-		{
+{
 	LLPanel::initFromParams(p);
 	setFollowsNone();
-	}
+}
 
 
 LLLayoutPanel::~LLLayoutPanel()
-	{
-		// probably not necessary, but...
-		delete mResizeBar;
-		mResizeBar = NULL;
-	}
+{
+	// probably not necessary, but...
+	delete mResizeBar;
+	mResizeBar = NULL;
+}
 	
 F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation)
-	{
+{
 	if (orientation == LLLayoutStack::HORIZONTAL)
-		{
-			F32 collapse_amt = 
-			clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth()));
-			return mVisibleAmt * collapse_amt;
-		}
-		else
+	{
+		F32 collapse_amt = 
+		clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth()));
+		return mVisibleAmt * collapse_amt;
+	}
+	else
 	{
 			F32 collapse_amt = 
 			clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight())));
 			return mVisibleAmt * collapse_amt;
-		}
 	}
+}
 
 //
 // LLLayoutStack
@@ -109,7 +115,7 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p)
 	mMinWidth(0),
 	mMinHeight(0),
 	mPanelSpacing(p.border_size),
-	mOrientation((p.orientation() == "vertical") ? VERTICAL : HORIZONTAL),
+	mOrientation(p.orientation),
 	mAnimate(p.animate),
 	mAnimatedThisFrame(false),
 	mClip(p.clip),
diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h
index 2fc6164d7ad02847338f32fadf5e1d76df36da39..4ac8ef0ee9a66ccd299d56fbf85bcabd293bd9e9 100644
--- a/indra/llui/lllayoutstack.h
+++ b/indra/llui/lllayoutstack.h
@@ -37,12 +37,24 @@ class LLLayoutPanel;
 class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
 {
 public:
+	typedef enum e_layout_orientation
+	{
+		HORIZONTAL,
+		VERTICAL
+	} ELayoutOrientation;
+
+	struct OrientationNames
+	:	public LLInitParam::TypeValuesHelper<ELayoutOrientation, OrientationNames>
+	{
+		static void declareValues();
+	};
+
 	struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry>
 	{};
 
 	struct Params : public LLInitParam::Block<Params, LLView::Params>
 	{
-		Mandatory<std::string>	orientation;
+		Mandatory<ELayoutOrientation, OrientationNames>	orientation;
 		Optional<S32>			border_size;
 		Optional<bool>			animate,
 								clip;
@@ -54,12 +66,6 @@ class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
 
 	typedef LayoutStackRegistry child_registry_t;
 
-	typedef enum e_layout_orientation
-	{
-		HORIZONTAL,
-		VERTICAL
-	} ELayoutOrientation;
-
 	virtual ~LLLayoutStack();
 
 	/*virtual*/ void draw();
@@ -171,6 +177,9 @@ friend class LLUICtrlFactory;
 	~LLLayoutPanel();
 
 	void initFromParams(const Params& p);
+	void setMinDim(S32 value) { mMinDim = value; }
+	void setMaxDim(S32 value) { mMaxDim = value; }
+
 protected:
 	LLLayoutPanel(const Params& p)	;
 
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index aed391c780ef79cf2b4c1a3325b1028cf974ea8b..7e348656a92a63fa5d2755af8e9bfdc280f9798f 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -88,6 +88,7 @@ LLLineEditor::Params::Params()
 	revert_on_esc("revert_on_esc", true),
 	commit_on_focus_lost("commit_on_focus_lost", true),
 	ignore_tab("ignore_tab", true),
+	is_password("is_password", false),
 	cursor_color("cursor_color"),
 	text_color("text_color"),
 	text_readonly_color("text_readonly_color"),
@@ -129,7 +130,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 	mBorderThickness( 0 ),
 	mIgnoreArrowKeys( FALSE ),
 	mIgnoreTab( p.ignore_tab ),
-	mDrawAsterixes( FALSE ),
+	mDrawAsterixes( p.is_password ),
 	mSelectAllonFocusReceived( p.select_on_focus ),
 	mPassDelete(FALSE),
 	mReadOnly(FALSE),
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index ce2dfdeeb8e74f6648f7094ca22160f679d41dca..723423a5b9c2e8ffd46850672109db076356bc11 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -85,7 +85,8 @@ class LLLineEditor
 		Optional<bool>					select_on_focus,
 										revert_on_esc,
 										commit_on_focus_lost,
-										ignore_tab;
+										ignore_tab,
+										is_password;
 
 		// colors
 		Optional<LLUIColor>				cursor_color,
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp
index c347e1579257f163cd6f176c44b7e2068eb70987..cd0f0e36b0509d5d40bc311b4d5f8c2be8589ac7 100644
--- a/indra/llui/llnotifications.cpp
+++ b/indra/llui/llnotifications.cpp
@@ -82,6 +82,7 @@ LLNotificationForm::FormButton::FormButton()
 
 LLNotificationForm::FormInput::FormInput()
 :	type("type"),
+	text("text"),
 	max_length_chars("max_length_chars"),
 	width("width", 0),
 	value("value")
@@ -421,7 +422,7 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par
 		it != end_it;
 		++it)
 	{
-		mUniqueContext.push_back(it->key);
+		mUniqueContext.push_back(it->value);
 	}
 	
 	lldebugs << "notification \"" << mName << "\": tag count is " << p.tags.size() << llendl;
@@ -792,13 +793,19 @@ bool LLNotification::isEquivalentTo(LLNotificationPtr that) const
 	{
 		const LLSD& these_substitutions = this->getSubstitutions();
 		const LLSD& those_substitutions = that->getSubstitutions();
+		const LLSD& this_payload = this->getPayload();
+		const LLSD& that_payload = that->getPayload();
 
 		// highlander bit sez there can only be one of these
 		for (std::vector<std::string>::const_iterator it = mTemplatep->mUniqueContext.begin(), end_it = mTemplatep->mUniqueContext.end();
 			it != end_it;
 			++it)
 		{
-			if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString())
+			// if templates differ in either substitution strings or payload with the given field name
+			// then they are considered inequivalent
+			// use of get() avoids converting the LLSD value to a map as the [] operator would
+			if (these_substitutions.get(*it).asString() != those_substitutions.get(*it).asString()
+				|| this_payload.get(*it).asString() != that_payload.get(*it).asString())
 			{
 				return false;
 			}
diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h
index f8f44699587ce70ed42a4b39c8b49dcaddd7e1db..34d3537781ba66df29fa789df743739e507605ed 100644
--- a/indra/llui/llnotifications.h
+++ b/indra/llui/llnotifications.h
@@ -195,6 +195,7 @@ class LLNotificationForm
 		Mandatory<std::string>	type;
 		Optional<S32>			width;
 		Optional<S32>			max_length_chars;
+		Optional<std::string>	text;
 
 		Optional<std::string>	value;
 		FormInput();
diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h
index 5a6ab40a2e4e0160de10e58d978a706092aeb520..eff572b553f0c5413d3ee6f5c846de13f92c6a5e 100644
--- a/indra/llui/llnotificationtemplate.h
+++ b/indra/llui/llnotificationtemplate.h
@@ -74,11 +74,13 @@ struct LLNotificationTemplate
 
 	struct UniquenessContext : public LLInitParam::Block<UniquenessContext>
 	{
-		Mandatory<std::string>	key;
+		Mandatory<std::string>	value;
 
 		UniquenessContext()
-		:	key("key")
-		{}
+		:	value("value")
+		{
+			addSynonym(value, "key");
+		}
 		
 	};
 
@@ -88,7 +90,7 @@ struct LLNotificationTemplate
 		// this idiom allows 
 		// <notification unique="true">
 		// as well as
-		// <notification> <unique> <context key=""/> </unique>...
+		// <notification> <unique> <context></context> </unique>...
 		Optional<bool>			dummy_val;
 	public:
 		Multiple<UniquenessContext>	contexts;
@@ -243,8 +245,8 @@ struct LLNotificationTemplate
     // (used for things like progress indications, or repeating warnings
     // like "the grid is going down in N minutes")
     bool mUnique;
-    // if we want to be unique only if a certain part of the payload is constant
-    // specify the field names for the payload. The notification will only be
+    // if we want to be unique only if a certain part of the payload or substitutions args
+	// are constant specify the field names for the payload. The notification will only be
     // combined if all of the fields named in the context are identical in the
     // new and the old notification; otherwise, the notification will be
     // duplicated. This is to support suppressing duplicate offers from the same
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 94bf716e7d77863a4d32b77918f30042bc9efe49..5a46c7c98e0721d1178f73da7a02241c58de492e 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -277,6 +277,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
 		mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN;
 		updateRects();
 	}
+	
+	mParseOnTheFly = TRUE;
 }
 
 void LLTextEditor::initFromParams( const LLTextEditor::Params& p)
@@ -324,8 +326,10 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Param
 
 	blockUndo();
 	deselect();
-
+	
+	mParseOnTheFly = FALSE;
 	LLTextBase::setText(utf8str, input_params);
+	mParseOnTheFly = TRUE;
 
 	resetDirty();
 }
@@ -1367,6 +1371,7 @@ void LLTextEditor::pastePrimary()
 // paste from primary (itsprimary==true) or clipboard (itsprimary==false)
 void LLTextEditor::pasteHelper(bool is_primary)
 {
+	mParseOnTheFly = FALSE;
 	bool can_paste_it;
 	if (is_primary)
 	{
@@ -1450,6 +1455,7 @@ void LLTextEditor::pasteHelper(bool is_primary)
 	deselect();
 
 	onKeyStroke();
+	mParseOnTheFly = TRUE;
 }
 
 
@@ -2385,7 +2391,7 @@ void LLTextEditor::loadKeywords(const std::string& filename,
 
 void LLTextEditor::updateSegments()
 {
-	if (mReflowIndex < S32_MAX && mKeywords.isLoaded())
+	if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly)
 	{
 		LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING);
 		// HACK:  No non-ascii keywords for now
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 58ecefdccbd4ee9798bc6b24ce873ea26fa9b00e..9e4b95003b7f45aeb1c32094a32332f83b40b8ba 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -315,6 +315,7 @@ class LLTextEditor :
 
 	BOOL			mAllowEmbeddedItems;
 	bool			mShowContextMenu;
+	bool			mParseOnTheFly;
 
 	LLUUID			mSourceID;
 
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
index afd60cbb3ee0491096d43e612b86eef82ea9c42d..0a06b5e74f6b38480b9f1e3df73b9fd69687c2a8 100644
--- a/indra/llui/lluictrl.cpp
+++ b/indra/llui/lluictrl.cpp
@@ -827,7 +827,7 @@ LLUICtrl* LLUICtrl::findRootMostFocusRoot()
 {
 	LLUICtrl* focus_root = NULL;
 	LLUICtrl* next_view = this;
-	while(next_view)
+	while(next_view && next_view->hasTabStop())
 	{
 		if (next_view->isFocusRoot())
 		{
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index e51f28e2e9b19ee8d973b06090c4058ea79f6ccc..4f7b4be52609206899dee5e9934bdf6c9aa49d62 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -805,6 +805,69 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const
 	return ::getStringAfterToken(url, "://");
 }
 
+//
+// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g.
+// secondlife:///app/region/Ahern/128/128/0
+//
+LLUrlEntryRegion::LLUrlEntryRegion()
+{
+	mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?",
+							boost::regex::perl|boost::regex::icase);
+	mMenuName = "menu_url_slurl.xml";
+	mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+	//
+	// we handle SLURLs in the following formats:
+	//   - secondlife:///app/region/Place/X/Y/Z
+	//   - secondlife:///app/region/Place/X/Y
+	//   - secondlife:///app/region/Place/X
+	//   - secondlife:///app/region/Place
+	//
+
+	LLSD path_array = LLURI(url).pathArray();
+	S32 path_parts = path_array.size();
+
+	if (path_parts < 3) // no region name
+	{
+		llwarns << "Failed to parse url [" << url << "]" << llendl;
+		return url;
+	}
+
+	std::string label = unescapeUrl(path_array[2]); // region name
+
+	if (path_parts > 3) // secondlife:///app/region/Place/X
+	{
+		std::string x = path_array[3];
+		label += " (" + x;
+
+		if (path_parts > 4) // secondlife:///app/region/Place/X/Y
+		{
+			std::string y = path_array[4];
+			label += "," + y;
+
+			if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z
+			{
+				std::string z = path_array[5];
+				label = label + "," + z;
+			}
+		}
+
+		label += ")";
+	}
+
+	return label;
+}
+
+std::string LLUrlEntryRegion::getLocation(const std::string &url) const
+{
+	LLSD path_array = LLURI(url).pathArray();
+	std::string region_name = unescapeUrl(path_array[2]);
+	return region_name;
+}
+
 //
 // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
 // secondlife:///app/teleport/Ahern/50/50/50/
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index 43a667c3909599c3a950bd4d3b72d8eea0f59bb8..1791739061cdae4c3cf3db6891984ef99d2eeb08 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -301,6 +301,18 @@ class LLUrlEntryPlace : public LLUrlEntryBase
 	/*virtual*/ std::string getLocation(const std::string &url) const;
 };
 
+///
+/// LLUrlEntryRegion Describes a Second Life location Url, e.g.,
+/// secondlife:///app/region/Ahern/128/128/0
+///
+class LLUrlEntryRegion : public LLUrlEntryBase
+{
+public:
+	LLUrlEntryRegion();
+	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+	/*virtual*/ std::string getLocation(const std::string &url) const;
+};
+
 ///
 /// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
 /// secondlife:///app/teleport/Ahern/50/50/50/
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index 478b412d5ea541a2bbd908d9b4f3055bede59882..523ee5d78c6534c1682d4aee4f166b52c92b4bab 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -54,6 +54,7 @@ LLUrlRegistry::LLUrlRegistry()
 	registerUrl(new LLUrlEntryGroup());
 	registerUrl(new LLUrlEntryParcel());
 	registerUrl(new LLUrlEntryTeleport());
+	registerUrl(new LLUrlEntryRegion());
 	registerUrl(new LLUrlEntryWorldMap());
 	registerUrl(new LLUrlEntryObjectIM());
 	registerUrl(new LLUrlEntryPlace());
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index a5d8e31640ea23ca846294e4cdaec7cb40db9c52..d2bbd663b85b3175cf44739fadfa458a511ea165 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -413,14 +413,9 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 	
 	LLControlVariable *findControl(const std::string& name);
 
-    // Moved setValue(), getValue(), setControlValue(), setControlName(),
-    // controlListener() to LLUICtrl because an LLView is NOT assumed to
-    // contain a value. If that's what you want, use LLUICtrl instead.
-//	virtual bool	handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
-
 	const child_list_t*	getChildList() const { return &mChildList; }
-	const child_list_const_iter_t	beginChild()  { return mChildList.begin(); }
-	const child_list_const_iter_t	endChild()  { return mChildList.end(); }
+	child_list_const_iter_t	beginChild() const { return mChildList.begin(); }
+	child_list_const_iter_t	endChild() const { return mChildList.end(); }
 
 	// LLMouseHandler functions
 	//  Default behavior is to pass events to children
diff --git a/indra/llui/llwindowshade.cpp b/indra/llui/llwindowshade.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..77e94385d424eedb561bf3837e1a10096bfa86eb
--- /dev/null
+++ b/indra/llui/llwindowshade.cpp
@@ -0,0 +1,328 @@
+/**
+ * @file LLWindowShade.cpp
+ * @brief Notification dialog that slides down and optionally disabled a piece of UI
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "llwindowshade.h"
+
+#include "lllayoutstack.h"
+#include "lltextbox.h"
+#include "lliconctrl.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "lllineeditor.h"
+
+const S32 MIN_NOTIFICATION_AREA_HEIGHT = 30;
+const S32 MAX_NOTIFICATION_AREA_HEIGHT = 100;
+
+LLWindowShade::Params::Params()
+:	bg_image("bg_image"),
+	modal("modal", false),
+	text_color("text_color"),
+	can_close("can_close", true)
+{
+	mouse_opaque = false;
+}
+
+LLWindowShade::LLWindowShade(const LLWindowShade::Params& params)
+:	LLUICtrl(params),
+	mNotification(params.notification),
+	mModal(params.modal),
+	mFormHeight(0),
+	mTextColor(params.text_color)
+{
+	setFocusRoot(true);
+}
+
+void LLWindowShade::initFromParams(const LLWindowShade::Params& params)
+{
+	LLUICtrl::initFromParams(params);
+
+	LLLayoutStack::Params layout_p;
+	layout_p.name = "notification_stack";
+	layout_p.rect = params.rect;
+	layout_p.follows.flags = FOLLOWS_ALL;
+	layout_p.mouse_opaque = false;
+	layout_p.orientation = LLLayoutStack::VERTICAL;
+	layout_p.border_size = 0;
+
+	LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
+	addChild(stackp);
+
+	LLLayoutPanel::Params panel_p;
+	panel_p.rect = LLRect(0, 30, 800, 0);
+	panel_p.name = "notification_area";
+	panel_p.visible = false;
+	panel_p.user_resize = false;
+	panel_p.background_visible = true;
+	panel_p.bg_alpha_image = params.bg_image;
+	panel_p.auto_resize = false;
+	LLLayoutPanel* notification_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+	stackp->addChild(notification_panel);
+
+	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+	panel_p.auto_resize = true;
+	panel_p.user_resize = false;
+	panel_p.rect = params.rect;
+	panel_p.name = "background_area";
+	panel_p.mouse_opaque = false;
+	panel_p.background_visible = false;
+	panel_p.bg_alpha_color = LLColor4(0.f, 0.f, 0.f, 0.2f);
+	LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+	stackp->addChild(dummy_panel);
+
+	layout_p = LLUICtrlFactory::getDefaultParams<LLLayoutStack>();
+	layout_p.rect = LLRect(0, 30, 800, 0);
+	layout_p.follows.flags = FOLLOWS_ALL;
+	layout_p.orientation = LLLayoutStack::HORIZONTAL;
+	stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
+	notification_panel->addChild(stackp);
+
+	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+	panel_p.rect.height = 30;
+	LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+	stackp->addChild(panel);
+
+	LLIconCtrl::Params icon_p;
+	icon_p.name = "notification_icon";
+	icon_p.rect = LLRect(5, 23, 21, 8);
+	panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p));
+
+	LLTextBox::Params text_p;
+	text_p.rect = LLRect(31, 20, panel->getRect().getWidth() - 5, 0);
+	text_p.follows.flags = FOLLOWS_ALL;
+	text_p.text_color = mTextColor;
+	text_p.font = LLFontGL::getFontSansSerifSmall();
+	text_p.font.style = "BOLD";
+	text_p.name = "notification_text";
+	text_p.use_ellipses = true;
+	text_p.wrap = true;
+	panel->addChild(LLUICtrlFactory::create<LLTextBox>(text_p));
+
+	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+	panel_p.auto_resize = false;
+	panel_p.user_resize = false;
+	panel_p.name="form_elements";
+	panel_p.rect = LLRect(0, 30, 130, 0);
+	LLLayoutPanel* form_elements_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+	stackp->addChild(form_elements_panel);
+
+	if (params.can_close)
+	{
+		panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
+		panel_p.auto_resize = false;
+		panel_p.user_resize = false;
+		panel_p.rect = LLRect(0, 30, 25, 0);
+		LLLayoutPanel* close_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
+		stackp->addChild(close_panel);
+
+		LLButton::Params button_p;
+		button_p.name = "close_notification";
+		button_p.rect = LLRect(5, 23, 21, 7);
+		button_p.image_color.control="DkGray_66";
+		button_p.image_unselected.name="Icon_Close_Foreground";
+		button_p.image_selected.name="Icon_Close_Press";
+		button_p.click_callback.function = boost::bind(&LLWindowShade::onCloseNotification, this);
+
+		close_panel->addChild(LLUICtrlFactory::create<LLButton>(button_p));
+	}
+
+	LLSD payload = mNotification->getPayload();
+
+	LLNotificationFormPtr formp = mNotification->getForm();
+	LLLayoutPanel& notification_area = getChildRef<LLLayoutPanel>("notification_area");
+	notification_area.getChild<LLUICtrl>("notification_icon")->setValue(mNotification->getIcon());
+	notification_area.getChild<LLUICtrl>("notification_text")->setValue(mNotification->getMessage());
+	notification_area.getChild<LLUICtrl>("notification_text")->setToolTip(mNotification->getMessage());
+
+	LLNotificationForm::EIgnoreType ignore_type = formp->getIgnoreType(); 
+	LLLayoutPanel& form_elements = notification_area.getChildRef<LLLayoutPanel>("form_elements");
+	form_elements.deleteAllChildren();
+
+	const S32 FORM_PADDING_HORIZONTAL = 10;
+	const S32 FORM_PADDING_VERTICAL = 3;
+	const S32 WIDGET_HEIGHT = 24;
+	const S32 LINE_EDITOR_WIDTH = 120;
+	S32 cur_x = FORM_PADDING_HORIZONTAL;
+	S32 cur_y = FORM_PADDING_VERTICAL + WIDGET_HEIGHT;
+	S32 form_width = cur_x;
+
+	if (ignore_type != LLNotificationForm::IGNORE_NO)
+	{
+		LLCheckBoxCtrl::Params checkbox_p;
+		checkbox_p.name = "ignore_check";
+		checkbox_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT);
+		checkbox_p.label = formp->getIgnoreMessage();
+		checkbox_p.label_text.text_color = LLColor4::black;
+		checkbox_p.commit_callback.function = boost::bind(&LLWindowShade::onClickIgnore, this, _1);
+		checkbox_p.initial_value = formp->getIgnored();
+
+		LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p);
+		check->setRect(check->getBoundingRect());
+		form_elements.addChild(check);
+		cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL;
+		form_width = llmax(form_width, cur_x);
+	}
+
+	for (S32 i = 0; i < formp->getNumElements(); i++)
+	{
+		LLSD form_element = formp->getElement(i);
+		std::string type = form_element["type"].asString();
+		if (type == "button")
+		{
+			LLButton::Params button_p;
+			button_p.name = form_element["name"];
+			button_p.label = form_element["text"];
+			button_p.rect = LLRect(cur_x, cur_y, cur_x, cur_y - WIDGET_HEIGHT);
+			button_p.click_callback.function = boost::bind(&LLWindowShade::onClickNotificationButton, this, form_element["name"].asString());
+			button_p.auto_resize = true;
+
+			LLButton* button = LLUICtrlFactory::create<LLButton>(button_p);
+			button->autoResize();
+			form_elements.addChild(button);
+
+			if (form_element["default"].asBoolean())
+			{
+				form_elements.setDefaultBtn(button);
+			}
+
+			cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL;
+			form_width = llmax(form_width, cur_x);
+		}
+		else if (type == "text" || type == "password")
+		{
+			// if not at beginning of line...
+			if (cur_x != FORM_PADDING_HORIZONTAL)
+			{
+				// start new line
+				cur_x = FORM_PADDING_HORIZONTAL;
+				cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL;
+			}
+			LLTextBox::Params label_p;
+			label_p.name = form_element["name"].asString() + "_label";
+			label_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT);
+			label_p.initial_value = form_element["text"];
+			label_p.text_color = mTextColor;
+			label_p.font_valign = LLFontGL::VCENTER;
+			label_p.v_pad = 5;
+			LLTextBox* textbox = LLUICtrlFactory::create<LLTextBox>(label_p);
+			textbox->reshapeToFitText();
+			textbox->reshape(textbox->getRect().getWidth(), form_elements.getRect().getHeight() - 2 * FORM_PADDING_VERTICAL); 
+			form_elements.addChild(textbox);
+			cur_x = textbox->getRect().mRight + FORM_PADDING_HORIZONTAL;
+
+			LLLineEditor::Params line_p;
+			line_p.name = form_element["name"];
+			line_p.keystroke_callback = boost::bind(&LLWindowShade::onEnterNotificationText, this, _1, form_element["name"].asString());
+			line_p.is_password = type == "password";
+			line_p.rect = LLRect(cur_x, cur_y, cur_x + LINE_EDITOR_WIDTH, cur_y - WIDGET_HEIGHT);
+
+			LLLineEditor* line_editor = LLUICtrlFactory::create<LLLineEditor>(line_p);
+			form_elements.addChild(line_editor);
+			form_width = llmax(form_width, cur_x + LINE_EDITOR_WIDTH + FORM_PADDING_HORIZONTAL);
+
+			// reset to start of next line
+			cur_x = FORM_PADDING_HORIZONTAL;
+			cur_y -= WIDGET_HEIGHT + FORM_PADDING_VERTICAL;
+		}
+	}
+
+	mFormHeight = form_elements.getRect().getHeight() - (cur_y - FORM_PADDING_VERTICAL) + WIDGET_HEIGHT;
+	form_elements.reshape(form_width, mFormHeight);
+	form_elements.setMinDim(form_width);
+
+	// move all form elements back onto form surface
+	S32 delta_y = WIDGET_HEIGHT + FORM_PADDING_VERTICAL - cur_y;
+	for (child_list_const_iter_t it = form_elements.getChildList()->begin(), end_it = form_elements.getChildList()->end();
+		it != end_it;
+		++it)
+	{
+		(*it)->translate(0, delta_y);
+	}
+}
+
+void LLWindowShade::show()
+{
+	getChildRef<LLLayoutPanel>("notification_area").setVisible(true);
+	getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(mModal);
+
+	setMouseOpaque(mModal);
+}
+
+void LLWindowShade::draw()
+{
+	LLRect message_rect = getChild<LLTextBox>("notification_text")->getTextBoundingRect();
+
+	LLLayoutPanel* notification_area = getChild<LLLayoutPanel>("notification_area");
+
+	notification_area->reshape(notification_area->getRect().getWidth(), 
+		llclamp(message_rect.getHeight() + 10, 
+				llmin(mFormHeight, MAX_NOTIFICATION_AREA_HEIGHT),
+				MAX_NOTIFICATION_AREA_HEIGHT));
+
+	LLUICtrl::draw();
+	if (mNotification && !mNotification->isActive())
+	{
+		hide();
+	}
+}
+
+void LLWindowShade::hide()
+{
+	getChildRef<LLLayoutPanel>("notification_area").setVisible(false);
+	getChildRef<LLLayoutPanel>("background_area").setBackgroundVisible(false);
+
+	setMouseOpaque(false);
+}
+
+void LLWindowShade::onCloseNotification()
+{
+	LLNotifications::instance().cancel(mNotification);
+}
+
+void LLWindowShade::onClickIgnore(LLUICtrl* ctrl)
+{
+	bool check = ctrl->getValue().asBoolean();
+	if (mNotification && mNotification->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN)
+	{
+		// question was "show again" so invert value to get "ignore"
+		check = !check;
+	}
+	mNotification->setIgnored(check);
+}
+
+void LLWindowShade::onClickNotificationButton(const std::string& name)
+{
+	if (!mNotification) return;
+
+	mNotificationResponse[name] = true;
+
+	mNotification->respond(mNotificationResponse);
+}
+
+void LLWindowShade::onEnterNotificationText(LLUICtrl* ctrl, const std::string& name)
+{
+	mNotificationResponse[name] = ctrl->getValue().asString();
+}
diff --git a/indra/llui/llwindowshade.h b/indra/llui/llwindowshade.h
new file mode 100644
index 0000000000000000000000000000000000000000..00471959296dd0429d0f500f5911e6d9b28756ff
--- /dev/null
+++ b/indra/llui/llwindowshade.h
@@ -0,0 +1,69 @@
+/**
+ * @file llwindowshade.h
+ * @brief Notification dialog that slides down and optionally disabled a piece of UI
+ *
+ * $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$
+ */
+
+#ifndef LL_LLWINDOWSHADE_H
+#define LL_LLWINDOWSHADE_H
+
+#include "lluictrl.h"
+#include "llnotifications.h"
+
+class LLWindowShade : public LLUICtrl
+{
+public:
+	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+	{
+		Mandatory<LLNotificationPtr>	notification;
+		Optional<LLUIImage*>			bg_image;
+		Optional<LLUIColor>				text_color;
+		Optional<bool>					modal,
+										can_close;
+
+		Params();
+	};
+
+	void show();
+	/*virtual*/ void draw();
+	void hide();
+
+private:
+	friend class LLUICtrlFactory;
+
+	LLWindowShade(const Params& p);
+	void initFromParams(const Params& params);
+
+	void onCloseNotification();
+	void onClickNotificationButton(const std::string& name);
+	void onEnterNotificationText(LLUICtrl* ctrl, const std::string& name);
+	void onClickIgnore(LLUICtrl* ctrl);
+
+	LLNotificationPtr	mNotification;
+	LLSD				mNotificationResponse;
+	bool				mModal;
+	S32					mFormHeight;
+	LLUIColor			mTextColor;
+};
+
+#endif // LL_LLWINDOWSHADE_H
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 59c0826ad79de90257270e520cfeebbedabd3883..8f0a48018fb26132f0ca1b5257a1110850dce4d3 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -103,6 +103,45 @@ namespace tut
 		ensure_equals(testname, url, expected);
 	}
 
+	void dummyCallback(const std::string &url, const std::string &label, const std::string& icon)
+	{
+	}
+
+	void testLabel(const std::string &testname, LLUrlEntryBase &entry,
+				   const char *text, const std::string &expected)
+	{
+		boost::regex regex = entry.getPattern();
+		std::string label = "";
+		boost::cmatch result;
+		bool found = boost::regex_search(text, result, regex);
+		if (found)
+		{
+			S32 start = static_cast<U32>(result[0].first - text);
+			S32 end = static_cast<U32>(result[0].second - text);
+			std::string url = std::string(text+start, end-start);
+			label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3));
+		}
+		ensure_equals(testname, label, expected);
+	}
+
+	void testLocation(const std::string &testname, LLUrlEntryBase &entry,
+					  const char *text, const std::string &expected)
+	{
+		boost::regex regex = entry.getPattern();
+		std::string location = "";
+		boost::cmatch result;
+		bool found = boost::regex_search(text, result, regex);
+		if (found)
+		{
+			S32 start = static_cast<U32>(result[0].first - text);
+			S32 end = static_cast<U32>(result[0].second - text);
+			std::string url = std::string(text+start, end-start);
+			location = entry.getLocation(url);
+		}
+		ensure_equals(testname, location, expected);
+	}
+
+
 	template<> template<>
 	void object::test<1>()
 	{
@@ -697,4 +736,114 @@ namespace tut
 				  "<nolink>My Object</nolink>",
 				  "My Object");
 	}
+
+	template<> template<>
+	void object::test<13>()
+	{
+		//
+		// test LLUrlEntryRegion - secondlife:///app/region/<location> URLs
+		//
+		LLUrlEntryRegion url;
+
+		// Regex tests.
+		testRegex("no valid region", url,
+				  "secondlife:///app/region/",
+				  "");
+
+		testRegex("invalid coords", url,
+				  "secondlife:///app/region/Korea2/a/b/c",
+				  "secondlife:///app/region/Korea2/"); // don't count invalid coords
+
+		testRegex("Ahern (50,50,50) [1]", url,
+				  "secondlife:///app/region/Ahern/50/50/50/",
+				  "secondlife:///app/region/Ahern/50/50/50/");
+
+		testRegex("Ahern (50,50,50) [2]", url,
+				  "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
+				  "secondlife:///app/region/Ahern/50/50/50/");
+
+		testRegex("Ahern (50,50,50) [3]", url,
+				  "XXX secondlife:///app/region/Ahern/50/50/50 XXX",
+				  "secondlife:///app/region/Ahern/50/50/50");
+
+		testRegex("Ahern (50,50,50) multicase", url,
+				  "XXX secondlife:///app/region/Ahern/50/50/50/ XXX",
+				  "secondlife:///app/region/Ahern/50/50/50/");
+
+		testRegex("Ahern (50,50) [1]", url,
+				  "XXX secondlife:///app/region/Ahern/50/50/ XXX",
+				  "secondlife:///app/region/Ahern/50/50/");
+
+		testRegex("Ahern (50,50) [2]", url,
+				  "XXX secondlife:///app/region/Ahern/50/50 XXX",
+				  "secondlife:///app/region/Ahern/50/50");
+
+		// DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+		testRegex("Region with brackets", url,
+				  "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX",
+				  "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30");
+
+		// DEV-35459: SLURLs and teleport Links not parsed properly
+		testRegex("Region with quote", url,
+				  "XXX secondlife:///app/region/A'ksha%20Oasis/41/166/701 XXX",
+			          "secondlife:///app/region/A%27ksha%20Oasis/41/166/701");
+
+		// Rendering tests.
+		testLabel("Render /app/region/Ahern/50/50/50/", url,
+			"secondlife:///app/region/Ahern/50/50/50/",
+			"Ahern (50,50,50)");
+
+		testLabel("Render /app/region/Ahern/50/50/50", url,
+			"secondlife:///app/region/Ahern/50/50/50",
+			"Ahern (50,50,50)");
+
+		testLabel("Render /app/region/Ahern/50/50/", url,
+			"secondlife:///app/region/Ahern/50/50/",
+			"Ahern (50,50)");
+
+		testLabel("Render /app/region/Ahern/50/50", url,
+			"secondlife:///app/region/Ahern/50/50",
+			"Ahern (50,50)");
+
+		testLabel("Render /app/region/Ahern/50/", url,
+			"secondlife:///app/region/Ahern/50/",
+			"Ahern (50)");
+
+		testLabel("Render /app/region/Ahern/50", url,
+			"secondlife:///app/region/Ahern/50",
+			"Ahern (50)");
+
+		testLabel("Render /app/region/Ahern/", url,
+			"secondlife:///app/region/Ahern/",
+			"Ahern");
+
+		testLabel("Render /app/region/Ahern/ within context", url,
+			"XXX secondlife:///app/region/Ahern/ XXX",
+			"Ahern");
+
+		testLabel("Render /app/region/Ahern", url,
+			"secondlife:///app/region/Ahern",
+			"Ahern");
+
+		testLabel("Render /app/region/Ahern within context", url,
+			"XXX secondlife:///app/region/Ahern XXX",
+			"Ahern");
+
+		testLabel("Render /app/region/Product%20Engine/", url,
+			"secondlife:///app/region/Product%20Engine/",
+			"Product Engine");
+
+		testLabel("Render /app/region/Product%20Engine", url,
+			"secondlife:///app/region/Product%20Engine",
+			"Product Engine");
+
+		// Location parsing texts.
+		testLocation("Location /app/region/Ahern/50/50/50/", url,
+			"secondlife:///app/region/Ahern/50/50/50/",
+			"Ahern");
+
+		testLocation("Location /app/region/Product%20Engine", url,
+			"secondlife:///app/region/Product%20Engine",
+			"Product Engine");
+	}
 }
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index bf3233f3862635f5872d7b12ba30b6bd45bc2362..4d2677fd91f062a2ea049ceefe7f22bb43c2ba45 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -59,12 +59,13 @@ set(viewer_HEADER_FILES
 
 # Libraries on which this library depends, needed for Linux builds
 # Sort by high-level to low-level
-if (NOT LINUX OR VIEWER)
+if (LINUX AND VIEWER)
   set(llwindow_LINK_LIBRARIES
       ${UI_LIBRARIES}     # for GTK
       ${SDL_LIBRARY}
+      fontconfig          # For FCInit and other FC* functions.
       )
-endif (NOT LINUX OR VIEWER)
+endif (LINUX AND VIEWER)
 
 if (DARWIN)
   list(APPEND llwindow_SOURCE_FILES
diff --git a/indra/mac_updater/CMakeLists.txt b/indra/mac_updater/CMakeLists.txt
index 44f98e5e18c3371a01c65c4ac090467563f6ce98..a4a6b50c6c14fbfc18e733b043a9c5485b143804 100644
--- a/indra/mac_updater/CMakeLists.txt
+++ b/indra/mac_updater/CMakeLists.txt
@@ -3,6 +3,7 @@
 project(mac_updater)
 
 include(00-Common)
+include(OpenSSL)
 include(CURL)
 include(LLCommon)
 include(LLVFS)
@@ -49,6 +50,8 @@ set_target_properties(mac-updater
 
 target_link_libraries(mac-updater
     ${LLVFS_LIBRARIES}
+    ${OPENSSL_LIBRARIES}
+    ${CRYPTO_LIBRARIES}
     ${CURL_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
     )
diff --git a/indra/media_plugins/example/media_plugin_example.cpp b/indra/media_plugins/example/media_plugin_example.cpp
index f8a871930ecdd7fd467f80006606ef33ca606014..da7de0179908f03a9041e6f6106dee1783356d5f 100644
--- a/indra/media_plugins/example/media_plugin_example.cpp
+++ b/indra/media_plugins/example/media_plugin_example.cpp
@@ -6,21 +6,21 @@
  * $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$
  * @endcond
@@ -39,48 +39,48 @@
 ////////////////////////////////////////////////////////////////////////////////
 //
 class MediaPluginExample :
-		public MediaPluginBase
+        public MediaPluginBase
 {
-	public:
-		MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data );
-		~MediaPluginExample();
-
-		/*virtual*/ void receiveMessage( const char* message_string );
-
-	private:
-		bool init();
-		void update( F64 milliseconds );
-		void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b );
-		bool mFirstTime;
-
-		time_t mLastUpdateTime;
-		enum Constants { ENumObjects = 10 };
-		unsigned char* mBackgroundPixels;
-		int mColorR[ ENumObjects ];
-		int mColorG[ ENumObjects ];
-		int mColorB[ ENumObjects ];
-		int mXpos[ ENumObjects ];
-		int mYpos[ ENumObjects ];
-		int mXInc[ ENumObjects ];
-		int mYInc[ ENumObjects ];
-		int mBlockSize[ ENumObjects ];
-		bool mMouseButtonDown;
-		bool mStopAction;
+    public:
+        MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data );
+        ~MediaPluginExample();
+
+        /*virtual*/ void receiveMessage( const char* message_string );
+
+    private:
+        bool init();
+        void update( F64 milliseconds );
+        void write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b );
+        bool mFirstTime;
+
+        time_t mLastUpdateTime;
+        enum Constants { ENumObjects = 10 };
+        unsigned char* mBackgroundPixels;
+        int mColorR[ ENumObjects ];
+        int mColorG[ ENumObjects ];
+        int mColorB[ ENumObjects ];
+        int mXpos[ ENumObjects ];
+        int mYpos[ ENumObjects ];
+        int mXInc[ ENumObjects ];
+        int mYInc[ ENumObjects ];
+        int mBlockSize[ ENumObjects ];
+        bool mMouseButtonDown;
+        bool mStopAction;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //
 MediaPluginExample::MediaPluginExample( LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data ) :
-	MediaPluginBase( host_send_func, host_user_data )
+    MediaPluginBase( host_send_func, host_user_data )
 {
-	mFirstTime = true;
-	mWidth = 0;
-	mHeight = 0;
-	mDepth = 4;
-	mPixels = 0;
-	mMouseButtonDown = false;
-	mStopAction = false;
-	mLastUpdateTime = 0;
+    mFirstTime = true;
+    mWidth = 0;
+    mHeight = 0;
+    mDepth = 4;
+    mPixels = 0;
+    mMouseButtonDown = false;
+    mStopAction = false;
+    mLastUpdateTime = 0;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -93,395 +93,320 @@ MediaPluginExample::~MediaPluginExample()
 //
 void MediaPluginExample::receiveMessage( const char* message_string )
 {
-	LLPluginMessage message_in;
-
-	if ( message_in.parse( message_string ) >= 0 )
-	{
-		std::string message_class = message_in.getClass();
-		std::string message_name = message_in.getName();
-
-		if ( message_class == LLPLUGIN_MESSAGE_CLASS_BASE )
-		{
-			if ( message_name == "init" )
-			{
-				LLPluginMessage message( "base", "init_response" );
-				LLSD versions = LLSD::emptyMap();
-				versions[ LLPLUGIN_MESSAGE_CLASS_BASE ] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
-				versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
-				versions[ LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER ] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
-				message.setValueLLSD( "versions", versions );
-
-				std::string plugin_version = "Example media plugin, Example Version 1.0.0.0";
-				message.setValue( "plugin_version", plugin_version );
-				sendMessage( message );
-			}
-			else
-			if ( message_name == "idle" )
-			{
-				// no response is necessary here.
-				F64 time = message_in.getValueReal( "time" );
-
-				// Convert time to milliseconds for update()
-				update( time );
-			}
-			else
-			if ( message_name == "cleanup" )
-			{
-				// clean up here
-			}
-			else
-			if ( message_name == "shm_added" )
-			{
-				SharedSegmentInfo info;
-				info.mAddress = message_in.getValuePointer( "address" );
-				info.mSize = ( size_t )message_in.getValueS32( "size" );
-				std::string name = message_in.getValue( "name" );
-
-				mSharedSegments.insert( SharedSegmentMap::value_type( name, info ) );
-
-			}
-			else
-			if ( message_name == "shm_remove" )
-			{
-				std::string name = message_in.getValue( "name" );
-
-				SharedSegmentMap::iterator iter = mSharedSegments.find( name );
-				if( iter != mSharedSegments.end() )
-				{
-					if ( mPixels == iter->second.mAddress )
-					{
-						// This is the currently active pixel buffer.
-						// Make sure we stop drawing to it.
-						mPixels = NULL;
-						mTextureSegmentName.clear();
-					};
-					mSharedSegments.erase( iter );
-				}
-				else
-				{
-					//std::cerr << "MediaPluginExample::receiveMessage: unknown shared memory region!" << std::endl;
-				};
-
-				// Send the response so it can be cleaned up.
-				LLPluginMessage message( "base", "shm_remove_response" );
-				message.setValue( "name", name );
-				sendMessage( message );
-			}
-			else
-			{
-				//std::cerr << "MediaPluginExample::receiveMessage: unknown base message: " << message_name << std::endl;
-			};
-		}
-		else
-		if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA )
-		{
-			if ( message_name == "init" )
-			{
-				// Plugin gets to decide the texture parameters to use.
-				LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params" );
-				message.setValueS32( "default_width", mWidth );
-				message.setValueS32( "default_height", mHeight );
-				message.setValueS32( "depth", mDepth );
-				message.setValueU32( "internalformat", GL_RGBA );
-				message.setValueU32( "format", GL_RGBA );
-				message.setValueU32( "type", GL_UNSIGNED_BYTE );
-				message.setValueBoolean( "coords_opengl", false );
-				sendMessage( message );
-			}
-			else if ( message_name == "size_change" )
-			{
-				std::string name = message_in.getValue( "name" );
-				S32 width = message_in.getValueS32( "width" );
-				S32 height = message_in.getValueS32( "height" );
-				S32 texture_width = message_in.getValueS32( "texture_width" );
-				S32 texture_height = message_in.getValueS32( "texture_height" );
-
-				if ( ! name.empty() )
-				{
-					// Find the shared memory region with this name
-					SharedSegmentMap::iterator iter = mSharedSegments.find( name );
-					if ( iter != mSharedSegments.end() )
-					{
-						mPixels = ( unsigned char* )iter->second.mAddress;
-						mWidth = width;
-						mHeight = height;
-
-						mTextureWidth = texture_width;
-						mTextureHeight = texture_height;
-
-						init();
-					};
-				};
-
-				LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response" );
-				message.setValue( "name", name );
-				message.setValueS32( "width", width );
-				message.setValueS32( "height", height );
-				message.setValueS32( "texture_width", texture_width );
-				message.setValueS32( "texture_height", texture_height );
-				sendMessage( message );
-			}
-			else
-			if ( message_name == "load_uri" )
-			{
-				std::string uri = message_in.getValue( "uri" );
-				if ( ! uri.empty() )
-				{
-				};
-			}
-			else
-			if ( message_name == "mouse_event" )
-			{
-				std::string event = message_in.getValue( "event" );
-				S32 button = message_in.getValueS32( "button" );
-
-				// left mouse button
-				if ( button == 0 )
-				{
-					int mouse_x = message_in.getValueS32( "x" );
-					int mouse_y = message_in.getValueS32( "y" );
-					std::string modifiers = message_in.getValue( "modifiers" );
-
-					if ( event == "move" )
-					{
-						if ( mMouseButtonDown )
-							write_pixel( mouse_x, mouse_y, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80, rand() % 0x80 + 0x80 );
-					}
-					else
-					if ( event == "down" )
-					{
-						mMouseButtonDown = true;
-					}
-					else
-					if ( event == "up" )
-					{
-						mMouseButtonDown = false;
-					}
-					else
-					if ( event == "double_click" )
-					{
-					};
-				};
-			}
-			else
-			if ( message_name == "key_event" )
-			{
-				std::string event = message_in.getValue( "event" );
-				S32 key = message_in.getValueS32( "key" );
-				std::string modifiers = message_in.getValue( "modifiers" );
-
-				if ( event == "down" )
-				{
-					if ( key == ' ')
-					{
-						mLastUpdateTime = 0;
-						update( 0.0f );
-					};
-				};
-			}
-			else
-			{
-				//std::cerr << "MediaPluginExample::receiveMessage: unknown media message: " << message_string << std::endl;
-			};
-		}
-		else
-		if ( message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER )
-		{
-			if ( message_name == "browse_reload" )
-			{
-				mLastUpdateTime = 0;
-				mFirstTime = true;
-				mStopAction = false;
-				update( 0.0f );
-			}
-			else
-			if ( message_name == "browse_stop" )
-			{
-				for( int n = 0; n < ENumObjects; ++n )
-					mXInc[ n ] = mYInc[ n ] = 0;
-
-				mStopAction = true;
-				update( 0.0f );
-			}
-			else
-			{
-				//std::cerr << "MediaPluginExample::receiveMessage: unknown media_browser message: " << message_string << std::endl;
-			};
-		}
-		else
-		{
-			//std::cerr << "MediaPluginExample::receiveMessage: unknown message class: " << message_class << std::endl;
-		};
-	};
+//  std::cerr << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl;
+    LLPluginMessage message_in;
+
+    if(message_in.parse(message_string) >= 0)
+    {
+        std::string message_class = message_in.getClass();
+        std::string message_name = message_in.getName();
+        if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE)
+        {
+            if(message_name == "init")
+            {
+                LLPluginMessage message("base", "init_response");
+                LLSD versions = LLSD::emptyMap();
+                versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION;
+                versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION;
+                versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION;
+                message.setValueLLSD("versions", versions);
+
+                std::string plugin_version = "Example plugin 1.0..0";
+                message.setValue("plugin_version", plugin_version);
+                sendMessage(message);
+            }
+            else if(message_name == "idle")
+            {
+                // no response is necessary here.
+                F64 time = message_in.getValueReal("time");
+
+                // Convert time to milliseconds for update()
+                update((int)(time * 1000.0f));
+            }
+            else if(message_name == "cleanup")
+            {
+            }
+            else if(message_name == "shm_added")
+            {
+                SharedSegmentInfo info;
+                info.mAddress = message_in.getValuePointer("address");
+                info.mSize = (size_t)message_in.getValueS32("size");
+                std::string name = message_in.getValue("name");
+
+                mSharedSegments.insert(SharedSegmentMap::value_type(name, info));
+
+            }
+            else if(message_name == "shm_remove")
+            {
+                std::string name = message_in.getValue("name");
+
+                SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+                if(iter != mSharedSegments.end())
+                {
+                    if(mPixels == iter->second.mAddress)
+                    {
+                        // This is the currently active pixel buffer.  Make sure we stop drawing to it.
+                        mPixels = NULL;
+                        mTextureSegmentName.clear();
+                    }
+                    mSharedSegments.erase(iter);
+                }
+                else
+                {
+//                  std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl;
+                }
+
+                // Send the response so it can be cleaned up.
+                LLPluginMessage message("base", "shm_remove_response");
+                message.setValue("name", name);
+                sendMessage(message);
+            }
+            else
+            {
+//              std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl;
+            }
+        }
+        else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA)
+        {
+            if(message_name == "init")
+            {
+                // Plugin gets to decide the texture parameters to use.
+                mDepth = 4;
+                LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params");
+                message.setValueS32("default_width", 1024);
+                message.setValueS32("default_height", 1024);
+                message.setValueS32("depth", mDepth);
+                message.setValueU32("internalformat", GL_RGBA);
+                message.setValueU32("format", GL_RGBA);
+                message.setValueU32("type", GL_UNSIGNED_BYTE);
+                message.setValueBoolean("coords_opengl", true);
+                sendMessage(message);
+            }
+            else if(message_name == "size_change")
+            {
+                std::string name = message_in.getValue("name");
+                S32 width = message_in.getValueS32("width");
+                S32 height = message_in.getValueS32("height");
+                S32 texture_width = message_in.getValueS32("texture_width");
+                S32 texture_height = message_in.getValueS32("texture_height");
+
+                if(!name.empty())
+                {
+                    // Find the shared memory region with this name
+                    SharedSegmentMap::iterator iter = mSharedSegments.find(name);
+                    if(iter != mSharedSegments.end())
+                    {
+                        mPixels = (unsigned char*)iter->second.mAddress;
+                        mWidth = width;
+                        mHeight = height;
+
+                        mTextureWidth = texture_width;
+                        mTextureHeight = texture_height;
+                    };
+                };
+
+                LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response");
+                message.setValue("name", name);
+                message.setValueS32("width", width);
+                message.setValueS32("height", height);
+                message.setValueS32("texture_width", texture_width);
+                message.setValueS32("texture_height", texture_height);
+                sendMessage(message);
+
+            }
+            else if(message_name == "load_uri")
+            {
+            }
+            else if(message_name == "mouse_event")
+            {
+                std::string event = message_in.getValue("event");
+                if(event == "down")
+                {
+
+                }
+                else if(event == "up")
+                {
+                }
+                else if(event == "double_click")
+                {
+                }
+            }
+        }
+        else
+        {
+//          std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl;
+        };
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //
 void MediaPluginExample::write_pixel( int x, int y, unsigned char r, unsigned char g, unsigned char b )
 {
-	// make sure we don't write outside the buffer
-	if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) )
-		return;
-		
-	if ( mBackgroundPixels != NULL )
-	{
-		unsigned char *pixel = mBackgroundPixels;
-		pixel += y * mWidth * mDepth;
-		pixel += ( x * mDepth );
-		pixel[ 0 ] = b;
-		pixel[ 1 ] = g;
-		pixel[ 2 ] = r;
-
-		setDirty( x, y, x + 1, y + 1 );
-	};
+    // make sure we don't write outside the buffer
+    if ( ( x < 0 ) || ( x >= mWidth ) || ( y < 0 ) || ( y >= mHeight ) )
+        return;
+
+    if ( mBackgroundPixels != NULL )
+    {
+        unsigned char *pixel = mBackgroundPixels;
+        pixel += y * mWidth * mDepth;
+        pixel += ( x * mDepth );
+        pixel[ 0 ] = b;
+        pixel[ 1 ] = g;
+        pixel[ 2 ] = r;
+
+        setDirty( x, y, x + 1, y + 1 );
+    };
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //
 void MediaPluginExample::update( F64 milliseconds )
 {
-	if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 )
-		return;
-
-	if ( mPixels == 0 )
-			return;
-
-	if ( mFirstTime )
-	{
-		for( int n = 0; n < ENumObjects; ++n )
-		{
-			mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 );
-			mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 );
-
-			mColorR[ n ] = rand() % 0x60 + 0x60;
-			mColorG[ n ] = rand() % 0x60 + 0x60;
-			mColorB[ n ] = rand() % 0x60 + 0x60;
-
-			mXInc[ n ] = 0;
-			while ( mXInc[ n ] == 0 )
-				mXInc[ n ] = rand() % 7 - 3;
-
-			mYInc[ n ] = 0;
-			while ( mYInc[ n ] == 0 )
-				mYInc[ n ] = rand() % 9 - 4;
-
-			mBlockSize[ n ] = rand() % 0x30 + 0x10;
-		};
-
-		delete [] mBackgroundPixels;
-				
-		mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ];
-
-		mFirstTime = false;
-	};
-
-	if ( mStopAction )
-		return;
-
-	if ( time( NULL ) > mLastUpdateTime + 3 )
-	{
-		const int num_squares = rand() % 20 + 4;
-		int sqr1_r = rand() % 0x80 + 0x20;
-		int sqr1_g = rand() % 0x80 + 0x20;
-		int sqr1_b = rand() % 0x80 + 0x20;
-		int sqr2_r = rand() % 0x80 + 0x20;
-		int sqr2_g = rand() % 0x80 + 0x20;
-		int sqr2_b = rand() % 0x80 + 0x20;
-
-		for ( int y1 = 0; y1 < num_squares; ++y1 )
-		{
-			for ( int x1 = 0; x1 < num_squares; ++x1 )
-			{
-				int px_start = mWidth * x1 / num_squares;
-				int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares;
-				int py_start = mHeight * y1 / num_squares;
-				int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares;
-
-				for( int y2 = py_start; y2 < py_end; ++y2 )
-				{
-					for( int x2 = px_start; x2 < px_end; ++x2 )
-					{
-						int rowspan = mWidth * mDepth;
-
-						if ( ( y1 % 2 ) ^ ( x1 % 2 ) )
-						{
-							mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r;
-							mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g;
-							mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b;
-						}
-						else
-						{
-							mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r;
-							mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g;
-							mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b;
-						};
-					};
-				};
-			};
-		};
-
-		time( &mLastUpdateTime );
-	};
-
-	memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth );
-
-	for( int n = 0; n < ENumObjects; ++n )
-	{
-		if ( rand() % 50 == 0 )
-		{
-				mXInc[ n ] = 0;
-				while ( mXInc[ n ] == 0 )
-					mXInc[ n ] = rand() % 7 - 3;
-
-				mYInc[ n ] = 0;
-				while ( mYInc[ n ] == 0 )
-					mYInc[ n ] = rand() % 9 - 4;
-		};
-
-		if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] )
-			mXInc[ n ] =- mXInc[ n ];
-
-		if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] )
-			mYInc[ n ] =- mYInc[ n ];
-
-		mXpos[ n ] += mXInc[ n ];
-		mYpos[ n ] += mYInc[ n ];
-
-		for( int y = 0; y < mBlockSize[ n ]; ++y )
-		{
-			for( int x = 0; x < mBlockSize[ n ]; ++x )
-			{
-				mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ];
-				mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ];
-				mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ];
-			};
-		};
-	};
-
-	setDirty( 0, 0, mWidth, mHeight );
+    if ( mWidth < 1 || mWidth > 2048 || mHeight < 1 || mHeight > 2048 )
+        return;
+
+    if ( mPixels == 0 )
+            return;
+
+    if ( mFirstTime )
+    {
+        for( int n = 0; n < ENumObjects; ++n )
+        {
+            mXpos[ n ] = ( mWidth / 2 ) + rand() % ( mWidth / 16 ) - ( mWidth / 32 );
+            mYpos[ n ] = ( mHeight / 2 ) + rand() % ( mHeight / 16 ) - ( mHeight / 32 );
+
+            mColorR[ n ] = rand() % 0x60 + 0x60;
+            mColorG[ n ] = rand() % 0x60 + 0x60;
+            mColorB[ n ] = rand() % 0x60 + 0x60;
+
+            mXInc[ n ] = 0;
+            while ( mXInc[ n ] == 0 )
+                mXInc[ n ] = rand() % 7 - 3;
+
+            mYInc[ n ] = 0;
+            while ( mYInc[ n ] == 0 )
+                mYInc[ n ] = rand() % 9 - 4;
+
+            mBlockSize[ n ] = rand() % 0x30 + 0x10;
+        };
+
+        delete [] mBackgroundPixels;
+
+        mBackgroundPixels = new unsigned char[ mWidth * mHeight * mDepth ];
+
+        mFirstTime = false;
+    };
+
+    if ( mStopAction )
+        return;
+
+    if ( time( NULL ) > mLastUpdateTime + 3 )
+    {
+        const int num_squares = rand() % 20 + 4;
+        int sqr1_r = rand() % 0x80 + 0x20;
+        int sqr1_g = rand() % 0x80 + 0x20;
+        int sqr1_b = rand() % 0x80 + 0x20;
+        int sqr2_r = rand() % 0x80 + 0x20;
+        int sqr2_g = rand() % 0x80 + 0x20;
+        int sqr2_b = rand() % 0x80 + 0x20;
+
+        for ( int y1 = 0; y1 < num_squares; ++y1 )
+        {
+            for ( int x1 = 0; x1 < num_squares; ++x1 )
+            {
+                int px_start = mWidth * x1 / num_squares;
+                int px_end = ( mWidth * ( x1 + 1 ) ) / num_squares;
+                int py_start = mHeight * y1 / num_squares;
+                int py_end = ( mHeight * ( y1 + 1 ) ) / num_squares;
+
+                for( int y2 = py_start; y2 < py_end; ++y2 )
+                {
+                    for( int x2 = px_start; x2 < px_end; ++x2 )
+                    {
+                        int rowspan = mWidth * mDepth;
+
+                        if ( ( y1 % 2 ) ^ ( x1 % 2 ) )
+                        {
+                            mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr1_r;
+                            mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr1_g;
+                            mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr1_b;
+                        }
+                        else
+                        {
+                            mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 0 ] = sqr2_r;
+                            mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 1 ] = sqr2_g;
+                            mBackgroundPixels[ y2 * rowspan + x2 * mDepth + 2 ] = sqr2_b;
+                        };
+                    };
+                };
+            };
+        };
+
+        time( &mLastUpdateTime );
+    };
+
+    memcpy( mPixels, mBackgroundPixels, mWidth * mHeight * mDepth );
+
+    for( int n = 0; n < ENumObjects; ++n )
+    {
+        if ( rand() % 50 == 0 )
+        {
+                mXInc[ n ] = 0;
+                while ( mXInc[ n ] == 0 )
+                    mXInc[ n ] = rand() % 7 - 3;
+
+                mYInc[ n ] = 0;
+                while ( mYInc[ n ] == 0 )
+                    mYInc[ n ] = rand() % 9 - 4;
+        };
+
+        if ( mXpos[ n ] + mXInc[ n ] < 0 || mXpos[ n ] + mXInc[ n ] >= mWidth - mBlockSize[ n ] )
+            mXInc[ n ] =- mXInc[ n ];
+
+        if ( mYpos[ n ] + mYInc[ n ] < 0 || mYpos[ n ] + mYInc[ n ] >= mHeight - mBlockSize[ n ] )
+            mYInc[ n ] =- mYInc[ n ];
+
+        mXpos[ n ] += mXInc[ n ];
+        mYpos[ n ] += mYInc[ n ];
+
+        for( int y = 0; y < mBlockSize[ n ]; ++y )
+        {
+            for( int x = 0; x < mBlockSize[ n ]; ++x )
+            {
+                mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 0 ] = mColorR[ n ];
+                mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 1 ] = mColorG[ n ];
+                mPixels[ ( mXpos[ n ] + x ) * mDepth + ( mYpos[ n ] + y ) * mDepth * mWidth + 2 ] = mColorB[ n ];
+            };
+        };
+    };
+
+    setDirty( 0, 0, mWidth, mHeight );
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //
 bool MediaPluginExample::init()
 {
-	LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" );
-	message.setValue( "name", "Example Plugin" );
-	sendMessage( message );
+    LLPluginMessage message( LLPLUGIN_MESSAGE_CLASS_MEDIA, "name_text" );
+    message.setValue( "name", "Example Plugin" );
+    sendMessage( message );
 
-	return true;
+    return true;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //
 int init_media_plugin( LLPluginInstance::sendMessageFunction host_send_func,
-						void* host_user_data,
-						LLPluginInstance::sendMessageFunction *plugin_send_func,
-						void **plugin_user_data )
+                        void* host_user_data,
+                        LLPluginInstance::sendMessageFunction *plugin_send_func,
+                        void **plugin_user_data )
 {
-	MediaPluginExample* self = new MediaPluginExample( host_send_func, host_user_data );
-	*plugin_send_func = MediaPluginExample::staticReceiveMessage;
-	*plugin_user_data = ( void* )self;
+    MediaPluginExample* self = new MediaPluginExample( host_send_func, host_user_data );
+    *plugin_send_func = MediaPluginExample::staticReceiveMessage;
+    *plugin_user_data = ( void* )self;
 
-	return 0;
+    return 0;
 }
+
diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt
index 05f12366066ccd8881b75291ca412f2ee24065fe..3b1f6795401cd2a35558e492a07688d470e3ea3a 100644
--- a/indra/media_plugins/webkit/CMakeLists.txt
+++ b/indra/media_plugins/webkit/CMakeLists.txt
@@ -27,6 +27,7 @@ include_directories(
     ${LLIMAGE_INCLUDE_DIRS}
     ${LLRENDER_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
+    ${LLQTWEBKIT_INCLUDE_DIR}
 )
 
 
diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp
index bd1a44a930702c885b62fd99853dd87e4517d979..d6f8ae3e16ae6d3b6a6aa58310cd06c4fe554ec3 100644
--- a/indra/media_plugins/webkit/media_plugin_webkit.cpp
+++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp
@@ -341,7 +341,7 @@ class MediaPluginWebKit :
 		url << std::setfill('0') << std::setw(2) << std::hex << int(mBackgroundB * 255.0f);
 		url << "%22%3E%3C/body%3E%3C/html%3E";
 		
-		lldebugs << "data url is: " << url.str() << llendl;
+		//lldebugs << "data url is: " << url.str() << llendl;
 					
 		LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, url.str() );
 //		LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "about:blank" );
@@ -407,6 +407,8 @@ class MediaPluginWebKit :
 		{
 			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin");
 			message.setValue("uri", event.getEventUri());
+			message.setValueBoolean("history_back_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK));
+			message.setValueBoolean("history_forward_available", LLQtWebKit::getInstance()->userActionIsEnabled( mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD));
 			sendMessage(message);
 		
 			setStatus(STATUS_LOADING);
@@ -569,6 +571,57 @@ class MediaPluginWebKit :
 		return blockingPickFile();
 	}
 	
+	std::string mAuthUsername;
+	std::string mAuthPassword;
+	bool mAuthOK;
+	
+	////////////////////////////////////////////////////////////////////////////////
+	// virtual
+	bool onAuthRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password)
+	{
+		mAuthOK = false;
+
+		LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "auth_request");
+		message.setValue("url", in_url);
+		message.setValue("realm", in_realm);
+		message.setValueBoolean("blocking_request", true);
+				
+		// The "blocking_request" key in the message means this sendMessage call will block until a response is received.
+		sendMessage(message);
+		
+		if(mAuthOK)
+		{
+			out_username = mAuthUsername;
+			out_password = mAuthPassword;
+		}
+		
+		return mAuthOK;
+	}
+	
+	void authResponse(LLPluginMessage &message)
+	{
+		mAuthOK = message.getValueBoolean("ok");
+		if(mAuthOK)
+		{
+			mAuthUsername = message.getValue("username");
+			mAuthPassword = message.getValue("password");
+		}
+	}
+	
+	////////////////////////////////////////////////////////////////////////////////
+	// virtual
+	void onLinkHovered(const EventType& event)
+	{
+		if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE)
+		{
+			LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "link_hovered");
+			message.setValue("link", event.getEventUri());
+			message.setValue("title", event.getStringValue());
+			message.setValue("text", event.getStringValue2());
+			sendMessage(message);
+		}
+	}
+	
 	LLQtWebKit::EKeyboardModifier decodeModifiers(std::string &modifiers)
 	{
 		int result = 0;
@@ -1096,6 +1149,10 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
 			{
 				onPickFileResponse(message_in.getValue("file"));
 			}
+			if(message_name == "auth_response")
+			{
+				authResponse(message_in);
+			}
 			else
 			{
 //				std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl;
@@ -1182,6 +1239,22 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
 				mUserAgent = message_in.getValue("user_agent");
 				LLQtWebKit::getInstance()->setBrowserAgentId( mUserAgent );
 			}
+			else if(message_name == "ignore_ssl_cert_errors")
+			{
+#if LLQTWEBKIT_API_VERSION >= 3
+				LLQtWebKit::getInstance()->setIgnoreSSLCertErrors( message_in.getValueBoolean("ignore") );
+#else
+				llwarns << "Ignoring ignore_ssl_cert_errors message (llqtwebkit version is too old)." << llendl;
+#endif
+			}
+			else if(message_name == "add_certificate_file_path")
+			{
+#if LLQTWEBKIT_API_VERSION >= 6
+				LLQtWebKit::getInstance()->addCAFile( message_in.getValue("path") );
+#else
+				llwarns << "Ignoring add_certificate_file_path message (llqtwebkit version is too old)." << llendl;
+#endif
+			}
 			else if(message_name == "init_history")
 			{
 				// Initialize browser history
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 2dd32b7aa420147eacb011132c67296a33375944..ce22a52460b4354ddb3bde5a0549a82a9e09c4c4 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -50,6 +50,7 @@ include_directories(
     ${LLCHARACTER_INCLUDE_DIRS}
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLIMAGE_INCLUDE_DIRS}
+    ${LLKDU_INCLUDE_DIRS}
     ${LLINVENTORY_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
@@ -222,6 +223,7 @@ set(viewer_SOURCE_FILES
     llfloaterurlentry.cpp
     llfloatervoiceeffect.cpp
     llfloaterwater.cpp
+    llfloaterwebcontent.cpp
     llfloaterwhitelistentry.cpp
     llfloaterwindlight.cpp
     llfloaterwindowsize.cpp
@@ -481,6 +483,7 @@ set(viewer_SOURCE_FILES
     llvectorperfoptions.cpp
     llversioninfo.cpp
     llviewchildren.cpp
+    llviewerassetstats.cpp
     llviewerassetstorage.cpp
     llviewerassettype.cpp
     llviewerattachmenu.cpp
@@ -757,6 +760,7 @@ set(viewer_HEADER_FILES
     llfloaterurlentry.h
     llfloatervoiceeffect.h
     llfloaterwater.h
+    llfloaterwebcontent.h
     llfloaterwhitelistentry.h
     llfloaterwindlight.h
     llfloaterwindowsize.h
@@ -1014,6 +1018,7 @@ set(viewer_HEADER_FILES
     llvectorperfoptions.h
     llversioninfo.h
     llviewchildren.h
+    llviewerassetstats.h
     llviewerassetstorage.h
     llviewerassettype.h
     llviewerattachmenu.h
@@ -1453,11 +1458,6 @@ if (WINDOWS)
     # In the meantime, if you have any ideas on how to easily maintain one list, either here or in viewer_manifest.py
     # and have the build deps get tracked *please* tell me about it.
 
-    if(LLKDU_LIBRARY)
-      # Configure a var for llkdu which may not exist for all builds.
-      set(LLKDU_DLL_SOURCE ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/llkdu.dll)
-    endif(LLKDU_LIBRARY)
-
     if(USE_GOOGLE_PERFTOOLS)
       # Configure a var for tcmalloc location, if used.
       # Note the need to specify multiple names explicitly.
@@ -1474,7 +1474,6 @@ if (WINDOWS)
       #${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libtcmalloc_minimal.dll => None ... Skipping libtcmalloc_minimal.dll
       ${CMAKE_SOURCE_DIR}/../etc/message.xml
       ${CMAKE_SOURCE_DIR}/../scripts/messages/message_template.msg
-      ${LLKDU_DLL_SOURCE}
       ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/llcommon.dll
       ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libapr-1.dll
       ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/libaprutil-1.dll
@@ -1660,7 +1659,6 @@ target_link_libraries(${VIEWER_BINARY_NAME}
     ${LLAUDIO_LIBRARIES}
     ${LLCHARACTER_LIBRARIES}
     ${LLIMAGE_LIBRARIES}
-    ${LLIMAGEJ2COJ_LIBRARIES}
     ${LLINVENTORY_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
     ${LLPLUGIN_LIBRARIES}
@@ -1696,6 +1694,17 @@ target_link_libraries(${VIEWER_BINARY_NAME}
     ${GOOGLE_PERFTOOLS_LIBRARIES}
     )
 
+if (USE_KDU)
+    target_link_libraries(${VIEWER_BINARY_NAME}
+        ${LLKDU_LIBRARIES}
+        ${KDU_LIBRARY}
+        )
+else (USE_KDU)
+    target_link_libraries(${VIEWER_BINARY_NAME}
+        ${LLIMAGEJ2COJ_LIBRARIES}
+        )
+endif (USE_KDU)
+    
 build_version(viewer)
 
 set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH
@@ -1843,29 +1852,31 @@ if (PACKAGE)
     set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest)
   endif (LINUX)
 
-  if(CMAKE_CFG_INTDIR STREQUAL ".")
+  if(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING)
+    if(CMAKE_CFG_INTDIR STREQUAL ".")
       set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE})
-  else(CMAKE_CFG_INTDIR STREQUAL ".")
+    else(CMAKE_CFG_INTDIR STREQUAL ".")
       # set LLBUILD_CONFIG to be a shell variable evaluated at build time
       # reflecting the configuration we are currently building.
       set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR})
-  endif(CMAKE_CFG_INTDIR STREQUAL ".")
-  add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
-    COMMAND "${PYTHON_EXECUTABLE}"
-    ARGS
-      "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py"
-      "${LLBUILD_CONFIG}"
-      "${VIEWER_DIST_DIR}"
-      "${VIEWER_EXE_GLOBS}"
-      "${VIEWER_LIB_GLOB}"
-      "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/bin/dump_syms"
-      "${VIEWER_SYMBOL_FILE}"
-    DEPENDS generate_breakpad_symbols.py
-    VERBATIM
-  )
-  add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}")
-  add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}")
-  add_dependencies(package generate_breakpad_symbols)
+    endif(CMAKE_CFG_INTDIR STREQUAL ".")
+    add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"
+      COMMAND "${PYTHON_EXECUTABLE}"
+      ARGS
+        "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py"
+        "${LLBUILD_CONFIG}"
+        "${VIEWER_DIST_DIR}"
+        "${VIEWER_EXE_GLOBS}"
+        "${VIEWER_LIB_GLOB}"
+        "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/bin/dump_syms"
+        "${VIEWER_SYMBOL_FILE}"
+        DEPENDS generate_breakpad_symbols.py
+        VERBATIM)
+
+    add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}")
+    add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}")
+    add_dependencies(package generate_breakpad_symbols)
+  endif(RELEASE_CRASH_REPORTING OR NON_RELEASE_CRASH_REPORTING)
 endif (PACKAGE)
 
 if (LL_TESTS)
@@ -1880,6 +1891,8 @@ if (LL_TESTS)
     llremoteparcelrequest.cpp
     llviewerhelputil.cpp
     llversioninfo.cpp
+    llworldmap.cpp
+    llworldmipmap.cpp
   )
 
   ##################################################
@@ -1955,10 +1968,18 @@ if (LL_TESTS)
     "${test_libs}"
     )
 
+  LL_ADD_INTEGRATION_TEST(llsimplestat
+	""
+    "${test_libs}"
+    )
+
+  LL_ADD_INTEGRATION_TEST(llviewerassetstats
+	llviewerassetstats.cpp
+    "${test_libs}"
+    )
+
   #ADD_VIEWER_BUILD_TEST(llmemoryview viewer)
   #ADD_VIEWER_BUILD_TEST(llagentaccess viewer)
-  #ADD_VIEWER_BUILD_TEST(llworldmap viewer)
-  #ADD_VIEWER_BUILD_TEST(llworldmipmap viewer)
   #ADD_VIEWER_BUILD_TEST(lltextureinfo viewer)
   #ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer)
   #ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer)
diff --git a/indra/newview/app_settings/lindenlab.pem b/indra/newview/app_settings/lindenlab.pem
new file mode 100644
index 0000000000000000000000000000000000000000..cf88d0e04799bf09835f2c78959e9e8bce3d75f9
--- /dev/null
+++ b/indra/newview/app_settings/lindenlab.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEUDCCA7mgAwIBAgIJAN4ppNGwj6yIMA0GCSqGSIb3DQEBBAUAMIHMMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j
+aXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5jLjEpMCcGA1UECxMgTGluZGVu
+IExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIExpbmRlbiBMYWIg
+Q2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZIhvcNAQkBFhBjYUBsaW5kZW5s
+YWIuY29tMB4XDTA1MDQyMTAyNDAzMVoXDTI1MDQxNjAyNDAzMVowgcwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp
+c2NvMRkwFwYDVQQKExBMaW5kZW4gTGFiLCBJbmMuMSkwJwYDVQQLEyBMaW5kZW4g
+TGFiIENlcnRpZmljYXRlIEF1dGhvcml0eTEpMCcGA1UEAxMgTGluZGVuIExhYiBD
+ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgkqhkiG9w0BCQEWEGNhQGxpbmRlbmxh
+Yi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKXh1MThucdTbMg9bYBO
+rAm8yWns32YojB0PRfbq8rUjepEhTm3/13s0u399Uc202v4ejcGhkIDWJZd2NZMF
+oKrhmRfxGHSKPCuFaXC3jh0lRECj7k8FoPkcmaPjSyodrDFDUUuv+C06oYJoI+rk
+8REyal9NwgHvqCzOrZtiTXAdAgMBAAGjggE2MIIBMjAdBgNVHQ4EFgQUO1zK2e1f
+1wO1fHAjq6DTJobKDrcwggEBBgNVHSMEgfkwgfaAFDtcytntX9cDtXxwI6ug0yaG
+yg63oYHSpIHPMIHMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
+MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5j
+LjEpMCcGA1UECxMgTGluZGVuIExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAn
+BgNVBAMTIExpbmRlbiBMYWIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZI
+hvcNAQkBFhBjYUBsaW5kZW5sYWIuY29tggkA3imk0bCPrIgwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQQFAAOBgQA/ZkgfvwHYqk1UIAKZS3kMCxz0HvYuEQtviwnu
+xA39CIJ65Zozs28Eg1aV9/Y+Of7TnWhW+U3J3/wD/GghaAGiKK6vMn9gJBIdBX/9
+e6ef37VGyiOEFFjnUIbuk0RWty0orN76q/lI/xjCi15XSA/VSq2j4vmnwfZcPTDu
+glmQ1A==
+-----END CERTIFICATE-----
+
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 06992d2b52b26f5b6a14145ba3d38f63dc4d2381..ea7ac6bedab5a401794e338bf4f9bfb81c91304f 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -686,6 +686,39 @@
       <key>Value</key>
       <string>http://www.secondlife.com</string>
     </map>
+    <key>BrowserIgnoreSSLCertErrors</key>
+    <map>
+      <key>Comment</key>
+      <string>FOR TESTING ONLY: Tell the built-in web browser to ignore SSL cert errors.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
+    <key>BrowserUseDefaultCAFile</key>
+    <map>
+      <key>Comment</key>
+      <string>Tell the built-in web browser to use the CA.pem file shipped with the client.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
+    <key>BrowserCAFilePath</key>
+    <map>
+      <key>Comment</key>
+      <string>Tell the built-in web browser the path to an alternative CA.pem file (only used if BrowserUseDefaultCAFile is false).</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string></string>
+    </map>  
     <key>BlockAvatarAppearanceMessages</key>
         <map>
         <key>Comment</key>
@@ -4747,6 +4780,17 @@
       <key>Value</key>
       <string>http://map.secondlife.com.s3.amazonaws.com/</string>
     </map>
+    <key>CurrentMapServerURL</key>
+    <map>
+      <key>Comment</key>
+      <string>Current Session World map URL</string>
+      <key>Persist</key>
+      <integer>0</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string></string>
+    </map>
     <key>MapShowEvents</key>
     <map>
       <key>Comment</key>
@@ -6572,7 +6616,18 @@
     <key>MediaBrowserWindowLimit</key>
     <map>
       <key>Comment</key>
-      <string>Maximum number of media brower windows that can be open at once (0 for no limit)</string>
+      <string>Maximum number of media brower windows that can be open at once in the media browser floater (0 for no limit)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>5</integer>
+    </map>
+    <key>WebContentWindowLimit</key>
+    <map>
+      <key>Comment</key>
+      <string>Maximum number of web brower windows that can be open at once in the Web content floater (0 for no limit)</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
@@ -9980,6 +10035,17 @@
       <key>Value</key>
       <real>500.0</real>
     </map>
+    <key>UpdaterMaximumBandwidth</key>
+    <map>
+      <key>Comment</key>
+      <string>Maximum allowable downstream bandwidth for updater service (kilo bits per second)</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>F32</string>
+      <key>Value</key>
+      <real>500.0</real>
+    </map>
     <key>ToolTipDelay</key>
     <map>
       <key>Comment</key>
@@ -11080,16 +11146,16 @@
       <key>Value</key>
       <integer>15</integer>
     </map>
-    <key>UpdaterServiceActive</key>
+    <key>UpdaterServiceSetting</key>
     <map>
       <key>Comment</key>
-      <string>Enable or disable the updater service.</string>
+      <string>Configure updater service.</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
-      <string>Boolean</string>
+      <string>U32</string>
       <key>Value</key>
-      <integer>1</integer>
+      <integer>3</integer>
     </map>
     <key>UpdaterServiceCheckPeriod</key>
     <map>
@@ -12368,5 +12434,16 @@
       <key>Value</key>
       <string>name</string>
     </map>
+    <key>ReleaseNotesURL</key>
+    <map>
+      <key>Comment</key>
+      <string>Release notes URL template</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string>http://secondlife.com/app/releasenotes/?channel=[CHANNEL]&amp;version=[VERSION]</string>
+    </map>
 </map>
 </llsd>
diff --git a/indra/newview/app_settings/shaders/shader_heirarchy.txt b/indra/newview/app_settings/shaders/shader_hierarchy.txt
similarity index 100%
rename from indra/newview/app_settings/shaders/shader_heirarchy.txt
rename to indra/newview/app_settings/shaders/shader_hierarchy.txt
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 001a6a8851ae6d5ef36b65b3238caef25269bf08..ea3c2eb312567fca46abcb563d771ee1581ec646 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -637,6 +637,9 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
 			// Update all of the regions.
 			LLWorld::getInstance()->updateAgentOffset(mAgentOriginGlobal);
 		}
+
+		// Pass new region along to metrics components that care about this level of detail.
+		LLAppViewer::metricsUpdateRegion(regionp->getHandle());
 	}
 	mRegionp = regionp;
 
diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp
index 15f8e7bf4dd67ab7c0f7dd0c29cf6e1899e3b5db..f01d5ff1f539121e87b6c3ce373b9fbdb6e2e2d5 100644
--- a/indra/newview/llagentcamera.cpp
+++ b/indra/newview/llagentcamera.cpp
@@ -2695,6 +2695,9 @@ void LLAgentCamera::lookAtLastChat()
 		new_camera_pos -= delta_pos * 0.4f;
 		new_camera_pos += left * 0.3f;
 		new_camera_pos += up * 0.2f;
+
+		setFocusOnAvatar(FALSE, FALSE);
+
 		if (chatter_av->mHeadp)
 		{
 			setFocusGlobal(gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), gAgent.getLastChatter());
@@ -2705,7 +2708,6 @@ void LLAgentCamera::lookAtLastChat()
 			setFocusGlobal(chatter->getPositionGlobal(), gAgent.getLastChatter());
 			mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
 		}
-		setFocusOnAvatar(FALSE, TRUE);
 	}
 	else
 	{
@@ -2725,9 +2727,10 @@ void LLAgentCamera::lookAtLastChat()
 		new_camera_pos += left * 0.3f;
 		new_camera_pos += up * 0.2f;
 
+		setFocusOnAvatar(FALSE, FALSE);
+
 		setFocusGlobal(chatter->getPositionGlobal(), gAgent.getLastChatter());
 		mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
-		setFocusOnAvatar(FALSE, TRUE);
 	}
 }
 
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index b460885a53134d533e1bc6589a1ea3bddda8f346..3a98c23e05d70a2a4b43a32ebf6b05a666577a06 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -80,6 +80,8 @@
 #include "llfeaturemanager.h"
 #include "llurlmatch.h"
 #include "lltextutil.h"
+#include "lllogininstance.h"
+#include "llprogressview.h"
 
 #include "llweb.h"
 #include "llsecondlifeurls.h"
@@ -192,6 +194,7 @@
 #include "llparcel.h"
 #include "llavatariconctrl.h"
 #include "llgroupiconctrl.h"
+#include "llviewerassetstats.h"
 
 // Include for security api initialization
 #include "llsecapi.h"
@@ -336,6 +339,14 @@ static std::string gWindowTitle;
 
 LLAppViewer::LLUpdaterInfo *LLAppViewer::sUpdaterInfo = NULL ;
 
+//----------------------------------------------------------------------------
+// Metrics logging control constants
+//----------------------------------------------------------------------------
+static const F32 METRICS_INTERVAL_DEFAULT = 600.0;
+static const F32 METRICS_INTERVAL_QA = 30.0;
+static F32 app_metrics_interval = METRICS_INTERVAL_DEFAULT;
+static bool app_metrics_qa_mode = false;
+
 void idle_afk_check()
 {
 	// check idle timers
@@ -593,10 +604,14 @@ LLAppViewer::LLAppViewer() :
 	setupErrorHandling();
 	sInstance = this;
 	gLoggedInTime.stop();
+	
+	LLLoginInstance::instance().setUpdaterService(mUpdater.get());
 }
 
 LLAppViewer::~LLAppViewer()
 {
+	LLLoginInstance::instance().setUpdaterService(0);
+	
 	destroyMainloopTimeout();
 
 	// If we got to this destructor somehow, the app didn't hang.
@@ -655,6 +670,21 @@ bool LLAppViewer::init()
     // Called before threads are created.
     LLCurl::initClass();
     LLMachineID::init();
+	
+	{
+		// Viewer metrics initialization
+		static LLCachedControl<bool> metrics_submode(gSavedSettings,
+													 "QAModeMetrics",
+													 false,
+													 "Enables QA features (logging, faster cycling) for metrics collector");
+
+		if (metrics_submode)
+		{
+			app_metrics_qa_mode = true;
+			app_metrics_interval = METRICS_INTERVAL_QA;
+		}
+		LLViewerAssetStatsFF::init();
+	}
 
     initThreads();
     writeSystemInfo();
@@ -1717,6 +1747,8 @@ bool LLAppViewer::cleanup()
 
 	LLWatchdog::getInstance()->cleanup();
 
+	LLViewerAssetStatsFF::cleanup();
+	
 	llinfos << "Shutting down message system" << llendflush;
 	end_messaging_system();
 
@@ -1783,7 +1815,10 @@ bool LLAppViewer::initThreads()
 	// Image decoding
 	LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true);
 	LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true);
-	LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true);
+	LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(),
+													sImageDecodeThread,
+													enable_threads && true,
+													app_metrics_qa_mode);
 	LLImage::initClass();
 
 	if (LLFastTimer::sLog || LLFastTimer::sMetricLog)
@@ -2403,26 +2438,120 @@ bool LLAppViewer::initConfiguration()
 }
 
 namespace {
-    // *TODO - decide if there's a better place for this function.
+    // *TODO - decide if there's a better place for these functions.
     // do we need a file llupdaterui.cpp or something? -brad
+
+	void apply_update_callback(LLSD const & notification, LLSD const & response)
+	{
+		lldebugs << "LLUpdate user response: " << response << llendl;
+		if(response["OK_okcancelbuttons"].asBoolean())
+		{
+			llinfos << "LLUpdate restarting viewer" << llendl;
+			static const bool install_if_ready = true;
+			// *HACK - this lets us launch the installer immediately for now
+			LLUpdaterService().startChecking(install_if_ready);
+		}
+	}
+	
+	void apply_update_ok_callback(LLSD const & notification, LLSD const & response)
+	{
+		llinfos << "LLUpdate restarting viewer" << llendl;
+		static const bool install_if_ready = true;
+		// *HACK - this lets us launch the installer immediately for now
+		LLUpdaterService().startChecking(install_if_ready);
+	}
+	
+	void on_update_downloaded(LLSD const & data)
+	{
+		std::string notification_name;
+		void (*apply_callback)(LLSD const &, LLSD const &) = NULL;
+
+		if(data["required"].asBoolean())
+		{
+			apply_callback = &apply_update_ok_callback;
+			if(LLStartUp::getStartupState() <= STATE_LOGIN_WAIT)
+			{
+				// The user never saw the progress bar.
+				notification_name = "RequiredUpdateDownloadedVerboseDialog";
+			}
+			else
+			{
+				notification_name = "RequiredUpdateDownloadedDialog";
+			}
+		}
+		else
+		{
+			apply_callback = &apply_update_callback;
+			if(LLStartUp::getStartupState() < STATE_STARTED)
+			{
+				// CHOP-262 we need to use a different notification
+				// method prior to login.
+				notification_name = "DownloadBackgroundDialog";
+			}
+			else
+			{
+				notification_name = "DownloadBackgroundTip";
+			}
+		}
+
+		LLSD substitutions;
+		substitutions["VERSION"] = data["version"];
+
+		// truncate version at the rightmost '.' 
+		std::string version_short(data["version"]);
+		size_t short_length = version_short.rfind('.');
+		if (short_length != std::string::npos)
+		{
+			version_short.resize(short_length);
+		}
+
+		LLUIString relnotes_url("[RELEASE_NOTES_BASE_URL][CHANNEL_URL]/[VERSION_SHORT]");
+		relnotes_url.setArg("[VERSION_SHORT]", version_short);
+
+		// *TODO thread the update service's response through to this point
+		std::string const & channel = LLVersionInfo::getChannel();
+		boost::shared_ptr<char> channel_escaped(curl_escape(channel.c_str(), channel.size()), &curl_free);
+
+		relnotes_url.setArg("[CHANNEL_URL]", channel_escaped.get());
+		relnotes_url.setArg("[RELEASE_NOTES_BASE_URL]", LLTrans::getString("RELEASE_NOTES_BASE_URL"));
+		substitutions["RELEASE_NOTES_FULL_URL"] = relnotes_url.getString();
+
+		LLNotificationsUtil::add(notification_name, substitutions, LLSD(), apply_callback);
+	}
+
+	void install_error_callback(LLSD const & notification, LLSD const & response)
+	{
+		LLAppViewer::instance()->forceQuit();
+	}
+	
     bool notify_update(LLSD const & evt)
     {
+		std::string notification_name;
 		switch (evt["type"].asInteger())
 		{
 			case LLUpdaterService::DOWNLOAD_COMPLETE:
-				LLNotificationsUtil::add("DownloadBackground");
+				on_update_downloaded(evt);
 				break;
 			case LLUpdaterService::INSTALL_ERROR:
-				LLNotificationsUtil::add("FailedUpdateInstall");
+				if(evt["required"].asBoolean()) {
+					LLNotificationsUtil::add("FailedRequiredUpdateInstall", LLSD(), LLSD(), &install_error_callback);
+				} else {
+					LLNotificationsUtil::add("FailedUpdateInstall");
+				}
 				break;
 			default:
-				llinfos << "unhandled update event " << evt << llendl;
 				break;
 		}
 
 		// let others also handle this event by default
         return false;
     }
+	
+	bool on_bandwidth_throttle(LLUpdaterService * updater, LLSD const & evt)
+	{
+		updater->setBandwidthLimit(evt.asInteger() * (1024/8));
+		return false; // Let others receive this event.
+	};
 };
 
 void LLAppViewer::initUpdater()
@@ -2445,7 +2574,10 @@ void LLAppViewer::initUpdater()
 						 channel, 
 						 version);
  	mUpdater->setCheckPeriod(check_period);
-	if(gSavedSettings.getBOOL("UpdaterServiceActive"))
+	mUpdater->setBandwidthLimit((int)gSavedSettings.getF32("UpdaterMaximumBandwidth") * (1024/8));
+	gSavedSettings.getControl("UpdaterMaximumBandwidth")->getSignal()->
+		connect(boost::bind(&on_bandwidth_throttle, mUpdater.get(), _2));
+	if(gSavedSettings.getU32("UpdaterServiceSetting"))
 	{
 		bool install_if_ready = true;
 		mUpdater->startChecking(install_if_ready);
@@ -2872,8 +3004,10 @@ void LLAppViewer::handleViewerCrash()
 		pApp->removeMarkerFile(false);
 	}
 	
+#if LL_SEND_CRASH_REPORTS
 	// Call to pure virtual, handled by platform specific llappviewer instance.
 	pApp->handleCrashReporting(); 
+#endif
     
 	return;
 }
@@ -3045,6 +3179,9 @@ void LLAppViewer::requestQuit()
 		return;
 	}
 
+	// Try to send metrics back to the grid
+	metricsSend(!gDisconnected);
+	
 	LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
 	effectp->setPositionGlobal(gAgent.getPositionGlobal());
 	effectp->setColor(LLColor4U(gAgent.getEffectColor()));
@@ -3081,7 +3218,7 @@ static LLNotificationFunctorRegistration finish_quit_reg("ConfirmQuit", finish_q
 
 void LLAppViewer::userQuit()
 {
-	if (gDisconnected)
+	if (gDisconnected || gViewerWindow->getProgressView()->getVisible())
 	{
 		requestQuit();
 	}
@@ -3822,6 +3959,11 @@ void LLAppViewer::idle()
 				llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl;
 				gObjectList.mNumUnknownUpdates = 0;
 			}
+
+			// ViewerMetrics FPS piggy-backing on the debug timer.
+			// 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);
 		}
 	}
 
@@ -3864,6 +4006,18 @@ void LLAppViewer::idle()
 		gInventory.idleNotifyObservers();
 	}
 	
+	// Metrics logging (LLViewerAssetStats, etc.)
+	{
+		static LLTimer report_interval;
+
+		// *TODO:  Add configuration controls for this
+		if (report_interval.getElapsedTimeF32() >= app_metrics_interval)
+		{
+			metricsSend(! gDisconnected);
+			report_interval.reset();
+		}
+	}
+
 	if (gDisconnected)
     {
 		return;
@@ -4580,6 +4734,35 @@ void LLAppViewer::loadEventHostModule(S32 listen_port)
 		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;
 
@@ -4588,13 +4771,13 @@ void LLAppViewer::loadEventHostModule(S32 listen_port)
 	apr_status_t rv = apr_dso_load(&eventhost_dso_handle,
 		dso_path.c_str(),
 		eventhost_dso_memory_pool);
-	ll_apr_assert_status(rv);
+	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");
 
-	ll_apr_assert_status(rv);
+	llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle));
 	llassert_always(ll_plugin_start_func != NULL);
 
 	LLSD args;
@@ -4766,3 +4949,75 @@ bool LLAppViewer::getMasterSystemAudioMute()
 {
 	return gSavedSettings.getBOOL("MuteAudio");
 }
+
+//----------------------------------------------------------------------------
+// Metrics-related methods (static and otherwise)
+//----------------------------------------------------------------------------
+
+/**
+ * LLViewerAssetStats collects data on a per-region (as defined by the agent's
+ * location) so we need to tell it about region changes which become a kind of
+ * hidden variable/global state in the collectors.  For collectors not running
+ * on the main thread, we need to send a message to move the data over safely
+ * and cheaply (amortized over a run).
+ */
+void LLAppViewer::metricsUpdateRegion(U64 region_handle)
+{
+	if (0 != region_handle)
+	{
+		LLViewerAssetStatsFF::set_region_main(region_handle);
+		if (LLAppViewer::sTextureFetch)
+		{
+			// Send a region update message into 'thread1' to get the new region.
+			LLAppViewer::sTextureFetch->commandSetRegion(region_handle);
+		}
+		else
+		{
+			// No 'thread1', a.k.a. TextureFetch, so update directly
+			LLViewerAssetStatsFF::set_region_thread1(region_handle);
+		}
+	}
+}
+
+
+/**
+ * Attempts to start a multi-threaded metrics report to be sent back to
+ * the grid for consumption.
+ */
+void LLAppViewer::metricsSend(bool enable_reporting)
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	if (LLAppViewer::sTextureFetch)
+	{
+		LLViewerRegion * regionp = gAgent.getRegion();
+
+		if (enable_reporting && regionp)
+		{
+			std::string	caps_url = regionp->getCapability("ViewerMetrics");
+
+			// Make a copy of the main stats to send into another thread.
+			// Receiving thread takes ownership.
+			LLViewerAssetStats * main_stats(new LLViewerAssetStats(*gViewerAssetStatsMain));
+			
+			// Send a report request into 'thread1' to get the rest of the data
+			// and provide some additional parameters while here.
+			LLAppViewer::sTextureFetch->commandSendMetrics(caps_url,
+														   gAgentSessionID,
+														   gAgentID,
+														   main_stats);
+			main_stats = 0;		// Ownership transferred
+		}
+		else
+		{
+			LLAppViewer::sTextureFetch->commandDataBreak();
+		}
+	}
+
+	// Reset even if we can't report.  Rather than gather up a huge chunk of
+	// data, we'll keep to our sampling interval and retain the data
+	// resolution in time.
+	gViewerAssetStatsMain->reset();
+}
+
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index 7c946b04a520914e258310447ab4fc11a8f78d5f..a18e6cbb028153a5fffa9eb225bbb50542d6218b 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -169,6 +169,10 @@ class LLAppViewer : public LLApp
 	// mute/unmute the system's master audio
 	virtual void setMasterSystemAudioMute(bool mute);
 	virtual bool getMasterSystemAudioMute();
+
+	// Metrics policy helper statics.
+	static void metricsUpdateRegion(U64 region_handle);
+	static void metricsSend(bool enable_reporting);
 	
 protected:
 	virtual bool initWindow(); // Initialize the viewer's window.
diff --git a/indra/newview/llbrowsernotification.cpp b/indra/newview/llbrowsernotification.cpp
index d6a813d60806ddefbbed85abc3a20d0eddb506a0..6e77d1e3363dd6ecede27859efe9f45b75eb2a44 100644
--- a/indra/newview/llbrowsernotification.cpp
+++ b/indra/newview/llbrowsernotification.cpp
@@ -29,8 +29,9 @@
 
 #include "llnotificationhandler.h"
 #include "llnotifications.h"
-#include "llfloaterreg.h"
 #include "llmediactrl.h"
+#include "llviewermedia.h"
+#include "llviewermediafocus.h"
 
 using namespace LLNotificationsUI;
 
@@ -39,10 +40,19 @@ bool LLBrowserNotification::processNotification(const LLSD& notify)
 	LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID());
 	if (!notification) return false;
 
-	LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(notification->getPayload()["media_id"].asUUID());
+	LLUUID media_id = notification->getPayload()["media_id"].asUUID();
+	LLMediaCtrl* media_instance = LLMediaCtrl::getInstance(media_id);
 	if (media_instance)
 	{
 		media_instance->showNotification(notification);
 	}
+	else if (LLViewerMediaFocus::instance().getControlsMediaID() == media_id)
+	{
+		LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(media_id);
+		if (impl)
+		{
+			impl->showNotification(notification);
+		}
+	}
 	return false;
 }
diff --git a/indra/newview/llbuycurrencyhtml.cpp b/indra/newview/llbuycurrencyhtml.cpp
index d35c9ed8535f658fd761abc0f8942f86a8e7aaad..e5a9be020311da047208e1646899560ae9b92915 100644
--- a/indra/newview/llbuycurrencyhtml.cpp
+++ b/indra/newview/llbuycurrencyhtml.cpp
@@ -33,6 +33,7 @@
 #include "llfloaterreg.h"
 #include "llcommandhandler.h"
 #include "llviewercontrol.h"
+#include "llstatusbar.h"
 
 // support for secondlife:///app/buycurrencyhtml/{ACTION}/{NEXT_ACTION}/{RETURN_CODE} SLapps
 class LLBuyCurrencyHTMLHandler : 
@@ -156,4 +157,7 @@ void LLBuyCurrencyHTML::closeDialog()
 	{
 		buy_currency_floater->closeFloater();
 	};
+	
+	// Update L$ balance in the status bar in case L$ were purchased
+	LLStatusBar::sendMoneyBalanceRequest();
 }
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 6e778de2d86df797872d62016b96136d7ff64768..c98bcbda45325b6abb9b1da5a565e8aaaac191a8 100644
--- a/indra/newview/llchathistory.cpp
+++ b/indra/newview/llchathistory.cpp
@@ -586,7 +586,7 @@ void LLChatHistory::initFromParams(const LLChatHistory::Params& p)
 	LLLayoutStack::Params layout_p;
 	layout_p.rect = stack_rect;
 	layout_p.follows.flags = FOLLOWS_ALL;
-	layout_p.orientation = "vertical";
+	layout_p.orientation = LLLayoutStack::VERTICAL;
 	layout_p.mouse_opaque = false;
 	
 	LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p, this);
diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp
index 8ae3ccbae3146d824c83a63b492c06b46ebf1060..2873bc00599e6fcd5c26e82be55841db738a42e4 100644
--- a/indra/newview/llfloaterabout.cpp
+++ b/indra/newview/llfloaterabout.cpp
@@ -272,7 +272,7 @@ LLSD LLFloaterAbout::getInfo()
 	}
 	
 	// TODO: Implement media plugin version query
-	info["QT_WEBKIT_VERSION"] = "4.6 (version number hard-coded)";
+	info["QT_WEBKIT_VERSION"] = "4.7.1 (version number hard-coded)";
 
 	if (gPacketsIn > 0)
 	{
diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp
index 58c79fdf151b10350cb293c69815168f17832c97..e21a8594bc0e4f37c3087a65dab274c874807f2b 100644
--- a/indra/newview/llfloaterbuycurrency.cpp
+++ b/indra/newview/llfloaterbuycurrency.cpp
@@ -267,17 +267,23 @@ void LLFloaterBuyCurrencyUI::onClickBuy()
 {
 	mManager.buy(getString("buy_currency"));
 	updateUI();
+	// Update L$ balance
+	LLStatusBar::sendMoneyBalanceRequest();
 }
 
 void LLFloaterBuyCurrencyUI::onClickCancel()
 {
 	closeFloater();
+	// Update L$ balance
+	LLStatusBar::sendMoneyBalanceRequest();
 }
 
 void LLFloaterBuyCurrencyUI::onClickErrorWeb()
 {
 	LLWeb::loadURLExternal(mManager.errorURI());
 	closeFloater();
+	// Update L$ balance
+	LLStatusBar::sendMoneyBalanceRequest();
 }
 
 // static
diff --git a/indra/newview/llfloaterbuycurrencyhtml.cpp b/indra/newview/llfloaterbuycurrencyhtml.cpp
index bde620d965bffe6fde637e4994daaac1f44a616a..013cf74c7bab3ee8260090ba21d204943a0134df 100644
--- a/indra/newview/llfloaterbuycurrencyhtml.cpp
+++ b/indra/newview/llfloaterbuycurrencyhtml.cpp
@@ -82,7 +82,7 @@ void LLFloaterBuyCurrencyHTML::navigateToFinalURL()
 	LLStringUtil::format( buy_currency_url, replace );
 
 	// write final URL to debug console
-	llinfos << "Buy currency HTML prased URL is " << buy_currency_url << llendl;
+	llinfos << "Buy currency HTML parsed URL is " << buy_currency_url << llendl;
 
 	// kick off the navigation
 	mBrowser->navigateTo( buy_currency_url, "text/html" );
@@ -105,7 +105,7 @@ void LLFloaterBuyCurrencyHTML::handleMediaEvent( LLPluginClassMedia* self, EMedi
 //
 void LLFloaterBuyCurrencyHTML::onClose( bool app_quitting )
 {
-	// update L$ balanace one more time
+	// Update L$ balance one more time
 	LLStatusBar::sendMoneyBalanceRequest();
 
 	destroy();
diff --git a/indra/newview/llfloaterhelpbrowser.cpp b/indra/newview/llfloaterhelpbrowser.cpp
index cec98e999235d04c9962a693a3cfc44ea8f504e7..a650886d89f60d08b31bdb2487349f0206ee38ad 100644
--- a/indra/newview/llfloaterhelpbrowser.cpp
+++ b/indra/newview/llfloaterhelpbrowser.cpp
@@ -132,9 +132,10 @@ void LLFloaterHelpBrowser::onClickOpenWebBrowser(void* user_data)
 
 void LLFloaterHelpBrowser::openMedia(const std::string& media_url)
 {
-	mBrowser->setHomePageUrl(media_url);
-	//mBrowser->navigateTo("data:text/html;charset=utf-8,I'd really love to be going to:<br><b>" + media_url + "</b>"); // tofu HACK for debugging =:)
-	mBrowser->navigateTo(media_url);
+	// explicitly make the media mime type for this floater since it will
+	// only ever display one type of content (Web).
+	mBrowser->setHomePageUrl(media_url, "text/html");
+	mBrowser->navigateTo(media_url, "text/html");
 	setCurrentURL(media_url);
 }
 
diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp
index 351b9ac5dad40a4423cfe24556ee7ba6acf5cccb..1b94d8cbcd2e7ccfe1bac70a41412eb07cc0f5f0 100644
--- a/indra/newview/llfloatermap.cpp
+++ b/indra/newview/llfloatermap.cpp
@@ -83,7 +83,6 @@ LLFloaterMap::~LLFloaterMap()
 BOOL LLFloaterMap::postBuild()
 {
 	mMap = getChild<LLNetMap>("Net Map");
-	mMap->setScale(gSavedSettings.getF32("MiniMapScale"));
 	mMap->setToolTipMsg(getString("ToolTipMsg"));	
 	sendChildToBack(mMap);
 	
@@ -288,7 +287,16 @@ void LLFloaterMap::handleZoom(const LLSD& userdata)
 	std::string level = userdata.asString();
 	
 	F32 scale = 0.0f;
-	if (level == std::string("close"))
+	if (level == std::string("default"))
+	{
+		LLControlVariable *pvar = gSavedSettings.getControl("MiniMapScale");
+		if(pvar)
+		{
+			pvar->resetToDefault();
+			scale = gSavedSettings.getF32("MiniMapScale");
+		}
+	}
+	else if (level == std::string("close"))
 		scale = LLNetMap::MAP_SCALE_MAX;
 	else if (level == std::string("medium"))
 		scale = LLNetMap::MAP_SCALE_MID;
@@ -296,7 +304,6 @@ void LLFloaterMap::handleZoom(const LLSD& userdata)
 		scale = LLNetMap::MAP_SCALE_MIN;
 	if (scale != 0.0f)
 	{
-		gSavedSettings.setF32("MiniMapScale", scale );
 		mMap->setScale(scale);
 	}
 }
diff --git a/indra/newview/llfloatermediabrowser.cpp b/indra/newview/llfloatermediabrowser.cpp
index d20092e344f2de809092226080ca4b738a1bd09b..7a670dd90cca7b108027d6911e5ced8d2086b502 100644
--- a/indra/newview/llfloatermediabrowser.cpp
+++ b/indra/newview/llfloatermediabrowser.cpp
@@ -306,17 +306,14 @@ void LLFloaterMediaBrowser::setCurrentURL(const std::string& url)
 {
 	mCurrentURL = url;
 
-	// redirects will navigate momentarily to about:blank, don't add to history
-	if (mCurrentURL != "about:blank")
-	{
-		mAddressCombo->remove(mCurrentURL);
-		mAddressCombo->add(mCurrentURL);
-		mAddressCombo->selectByValue(mCurrentURL);
+	mAddressCombo->remove(mCurrentURL);
+	mAddressCombo->add(mCurrentURL);
+	mAddressCombo->selectByValue(mCurrentURL);
+
+	// Serialize url history
+	LLURLHistory::removeURL("browser", mCurrentURL);
+	LLURLHistory::addURL("browser", mCurrentURL);
 
-		// Serialize url history
-		LLURLHistory::removeURL("browser", mCurrentURL);
-		LLURLHistory::addURL("browser", mCurrentURL);
-	}
 	getChildView("back")->setEnabled(mBrowser->canNavigateBack());
 	getChildView("forward")->setEnabled(mBrowser->canNavigateForward());
 	getChildView("reload")->setEnabled(TRUE);
@@ -334,8 +331,15 @@ void LLFloaterMediaBrowser::onClickRefresh(void* user_data)
 {
 	LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data;
 
-	self->mAddressCombo->remove(0);
-	self->mBrowser->navigateTo(self->mCurrentURL);
+	if( self->mBrowser->getMediaPlugin() &&  self->mBrowser->getMediaPlugin()->pluginSupportsMediaBrowser())
+	{
+		bool ignore_cache = true;
+		self->mBrowser->getMediaPlugin()->browse_reload( ignore_cache );
+	}
+	else
+	{
+		self->mBrowser->navigateTo(self->mCurrentURL);
+	}
 }
 
 //static 
diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp
index 054ab4538bf29c052d17018724483df35004322a..dd0b1d999c978ebe379ead3685cc4afdd1941d05 100644
--- a/indra/newview/llfloaterpostcard.cpp
+++ b/indra/newview/llfloaterpostcard.cpp
@@ -366,7 +366,9 @@ void LLFloaterPostcard::sendPostcard()
 	{
 		gAssetStorage->storeAssetData(mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)this, FALSE);
 	}
-
+	
+	// give user feedback of the event
+	gViewerWindow->playSnapshotAnimAndSound();
 	LLUploadDialog::modalUploadDialog(getString("upload_message"));
 
 	// don't destroy the window until the upload is done
diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp
index 3ed4aec89ab10cfc5e1d9f1bdf6f1c279e6c31cd..2041fac8d8d165cbfd878e5201c2443cceeb50ab 100644
--- a/indra/newview/llfloatersearch.cpp
+++ b/indra/newview/llfloatersearch.cpp
@@ -200,5 +200,5 @@ void LLFloaterSearch::search(const LLSD &key)
 	url = LLWeb::expandURLSubstitutions(url, subs);
 
 	// and load the URL in the web view
-	mBrowser->navigateTo(url);
+	mBrowser->navigateTo(url, "text/html");
 }
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 1aba5ef92f0f25c1d9f80886eee576e38f0a0645..0931f77281b6dd7a5e3250533a2aa850b3502af7 100644
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -908,8 +908,6 @@ BOOL LLSnapshotLivePreview::onIdle( void* snapshot_preview )
 			previewp->mPosTakenGlobal = gAgentCamera.getCameraPositionGlobal();
 			previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
 		}
-
-		gViewerWindow->playSnapshotAnimAndSound();
 	}
 	previewp->getWindow()->decBusyCount();
 	// only show fullscreen preview when in freeze frame mode
@@ -1006,6 +1004,7 @@ void LLSnapshotLivePreview::saveTexture()
 				    LLFloaterPerms::getEveryonePerms(),
 				    "Snapshot : " + pos_string,
 				    callback, expected_upload_cost, userdata);
+		gViewerWindow->playSnapshotAnimAndSound();
 	}
 	else
 	{
@@ -1027,6 +1026,10 @@ BOOL LLSnapshotLivePreview::saveLocal()
 	mDataSize = 0;
 	updateSnapshot(FALSE, FALSE);
 
+	if(success)
+	{
+		gViewerWindow->playSnapshotAnimAndSound();
+	}
 	return success;
 }
 
@@ -1046,6 +1049,8 @@ void LLSnapshotLivePreview::saveWeb()
 
 	LLLandmarkActions::getRegionNameAndCoordsFromPosGlobal(gAgentCamera.getCameraPositionGlobal(),
 		boost::bind(&LLSnapshotLivePreview::regionNameCallback, this, jpg, metadata, _1, _2, _3, _4));
+
+	gViewerWindow->playSnapshotAnimAndSound();
 }
 
 void LLSnapshotLivePreview::regionNameCallback(LLImageJPEG* snapshot, LLSD& metadata, const std::string& name, S32 x, S32 y, S32 z)
diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51726112a0fccb2c2081a701a8ddf16e9d2016bd
--- /dev/null
+++ b/indra/newview/llfloaterwebcontent.cpp
@@ -0,0 +1,402 @@
+/**
+ * @file llfloaterwebcontent.cpp
+ * @brief floater for displaying web content - e.g. profiles and search (eventually)
+ *
+ * $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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcombobox.h"
+#include "lliconctrl.h"
+#include "llfloaterreg.h"
+#include "lllayoutstack.h"
+#include "llpluginclassmedia.h"
+#include "llprogressbar.h"
+#include "lltextbox.h"
+#include "llurlhistory.h"
+#include "llviewercontrol.h"
+#include "llweb.h"
+#include "llwindow.h"
+
+#include "llfloaterwebcontent.h"
+
+LLFloaterWebContent::LLFloaterWebContent( const LLSD& key )
+	: LLFloater( key )
+{
+	mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this ));
+	mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this ));
+	mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this ));
+	mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this ));
+	mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this ));
+	mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this ));
+}
+
+BOOL LLFloaterWebContent::postBuild()
+{
+	// these are used in a bunch of places so cache them
+	mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" );
+	mAddressCombo = getChild< LLComboBox >( "address" );
+	mStatusBarText = getChild< LLTextBox >( "statusbartext" );
+	mStatusBarProgress = getChild<LLProgressBar>("statusbarprogress" );
+
+	// observe browser events
+	mWebBrowser->addObserver( this );
+
+	// these buttons are always enabled
+	getChildView("reload")->setEnabled( true );
+	getChildView("popexternal")->setEnabled( true );
+
+	// cache image for secure browsing
+	mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag");
+
+	// initialize the URL history using the system URL History manager
+	initializeURLHistory();
+
+	return TRUE;
+}
+
+void LLFloaterWebContent::initializeURLHistory()
+{
+	// start with an empty list
+	LLCtrlListInterface* url_list = childGetListInterface("address");
+	if (url_list)
+	{
+		url_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+	}
+
+	// Get all of the entries in the "browser" collection
+	LLSD browser_history = LLURLHistory::getURLHistory("browser");
+	LLSD::array_iterator iter_history =
+		browser_history.beginArray();
+	LLSD::array_iterator end_history =
+		browser_history.endArray();
+	for(; iter_history != end_history; ++iter_history)
+	{
+		std::string url = (*iter_history).asString();
+		if(! url.empty())
+			url_list->addSimpleElement(url);
+	}
+}
+
+//static
+void LLFloaterWebContent::create( const std::string &url, const std::string& target, const std::string& uuid )
+{
+	lldebugs << "url = " << url << ", target = " << target << ", uuid = " << uuid << llendl;
+
+	std::string tag = target;
+
+	if(target.empty() || target == "_blank")
+	{
+		if(!uuid.empty())
+		{
+			tag = uuid;
+		}
+		else
+		{
+			// create a unique tag for this instance
+			LLUUID id;
+			id.generate();
+			tag = id.asString();
+		}
+	}
+
+	S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit");
+
+	if(LLFloaterReg::findInstance("web_content", tag) != NULL)
+	{
+		// There's already a web browser for this tag, so we won't be opening a new window.
+	}
+	else if(browser_window_limit != 0)
+	{
+		// showInstance will open a new window.  Figure out how many web browsers are already open,
+		// and close the least recently opened one if this will put us over the limit.
+
+		LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList("web_content");
+		lldebugs << "total instance count is " << instances.size() << llendl;
+
+		for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++)
+		{
+			lldebugs << "    " << (*iter)->getKey() << llendl;
+		}
+
+		if(instances.size() >= (size_t)browser_window_limit)
+		{
+			// Destroy the least recently opened instance
+			(*instances.begin())->closeFloater();
+		}
+	}
+
+	LLFloaterWebContent *browser = dynamic_cast<LLFloaterWebContent*> (LLFloaterReg::showInstance("web_content", tag));
+	llassert(browser);
+	if(browser)
+	{
+		browser->mUUID = uuid;
+
+		// tell the browser instance to load the specified URL
+		browser->open_media(url, target);
+		LLViewerMedia::proxyWindowOpened(target, uuid);
+	}
+}
+
+//static
+void LLFloaterWebContent::closeRequest(const std::string &uuid)
+{
+	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content");
+	lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl;
+	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
+	{
+		LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter);
+		lldebugs << "    " << i->mUUID << llendl;
+		if (i && i->mUUID == uuid)
+		{
+			i->closeFloater(false);
+			return;
+ 		}
+ 	}
+}
+
+//static
+void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height)
+{
+	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content");
+	lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl;
+	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
+	{
+		LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter);
+		lldebugs << "    " << i->mUUID << llendl;
+		if (i && i->mUUID == uuid)
+		{
+			i->geometryChanged(x, y, width, height);
+			return;
+		}
+	}
+}
+
+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();
+
+	// TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc.
+	LLCoordWindow window_size;
+	getWindow()->getSize(&window_size);
+
+	// Adjust width and height for the size of the chrome on the web Browser window.
+	width += getRect().getWidth() - mWebBrowser->getRect().getWidth();
+	height += getRect().getHeight() - mWebBrowser->getRect().getHeight();
+
+	LLRect geom;
+	geom.setOriginAndSize(x, window_size.mY - (y + height), width, height);
+
+	lldebugs << "geometry change: " << geom << llendl;
+
+	handleReshape(geom,false);
+}
+
+void LLFloaterWebContent::open_media(const std::string& web_url, const std::string& target)
+{
+	// Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin.
+	mWebBrowser->setHomePageUrl(web_url, "text/html");
+	mWebBrowser->setTarget(target);
+	mWebBrowser->navigateTo(web_url, "text/html");
+	set_current_url(web_url);
+}
+
+//virtual
+void LLFloaterWebContent::onClose(bool app_quitting)
+{
+	LLViewerMedia::proxyWindowClosed(mUUID);
+	destroy();
+}
+
+// virtual
+void LLFloaterWebContent::draw()
+{
+	// this is asychronous so we need to keep checking
+	getChildView( "back" )->setEnabled( mWebBrowser->canNavigateBack() );
+	getChildView( "forward" )->setEnabled( mWebBrowser->canNavigateForward() );
+
+	LLFloater::draw();
+}
+
+// virtual
+void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
+{
+	if(event == MEDIA_EVENT_LOCATION_CHANGED)
+	{
+		const std::string url = self->getLocation();
+
+		if ( url.length() )
+			mStatusBarText->setText( url );
+
+		set_current_url( url );
+	}
+	else if(event == MEDIA_EVENT_NAVIGATE_BEGIN)
+	{
+		// flags are sent with this event
+		getChildView("back")->setEnabled( self->getHistoryBackAvailable() );
+		getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() );
+
+		// toggle visibility of these buttons based on browser state
+		getChildView("reload")->setVisible( false );
+		getChildView("stop")->setVisible( true );
+
+		// turn "on" progress bar now we're about to start loading
+		mStatusBarProgress->setVisible( true );
+	}
+	else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE)
+	{
+		// flags are sent with this event
+		getChildView("back")->setEnabled( self->getHistoryBackAvailable() );
+		getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() );
+
+		// toggle visibility of these buttons based on browser state
+		getChildView("reload")->setVisible( true );
+		getChildView("stop")->setVisible( false );
+
+		// turn "off" progress bar now we're loaded
+		mStatusBarProgress->setVisible( false );
+
+		// we populate the status bar with URLs as they change so clear it now we're done
+		const std::string end_str = "";
+		mStatusBarText->setText( end_str );
+
+		// decide if secure browsing icon should be displayed
+		std::string prefix =  std::string("https://");
+		std::string test_prefix = mCurrentURL.substr(0, prefix.length());
+		LLStringUtil::toLower(test_prefix);
+		if(test_prefix == prefix)
+		{
+			mSecureLockIcon->setVisible(true);
+		}
+		else
+		{
+			mSecureLockIcon->setVisible(false);
+		}
+	}
+	else if(event == MEDIA_EVENT_CLOSE_REQUEST)
+	{
+		// The browser instance wants its window closed.
+		closeFloater();
+	}
+	else if(event == MEDIA_EVENT_GEOMETRY_CHANGE)
+	{
+		geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight());
+	}
+	else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED )
+	{
+		const std::string text = self->getStatusText();
+		if ( text.length() )
+			mStatusBarText->setText( text );
+	}
+	else if(event == MEDIA_EVENT_PROGRESS_UPDATED )
+	{
+		int percent = (int)self->getProgressPercent();
+		mStatusBarProgress->setValue( percent );
+	}
+	else if(event == MEDIA_EVENT_NAME_CHANGED )
+	{
+		std::string page_title = self->getMediaName();
+		// simulate browser behavior - title is empty, use the current URL
+		if ( page_title.length() > 0 )
+			setTitle( page_title );
+		else
+			setTitle( mCurrentURL );
+	}
+	else if(event == MEDIA_EVENT_LINK_HOVERED )
+	{
+		const std::string link = self->getHoverLink();
+		mStatusBarText->setText( link );
+	}
+}
+
+void LLFloaterWebContent::set_current_url(const std::string& url)
+{
+	mCurrentURL = url;
+
+	// serialize url history into the system URL History manager
+	LLURLHistory::removeURL("browser", mCurrentURL);
+	LLURLHistory::addURL("browser", mCurrentURL);
+
+	mAddressCombo->remove( mCurrentURL );
+	mAddressCombo->add( mCurrentURL );
+	mAddressCombo->selectByValue( mCurrentURL );
+}
+
+void LLFloaterWebContent::onClickForward()
+{
+	mWebBrowser->navigateForward();
+}
+
+void LLFloaterWebContent::onClickBack()
+{
+	mWebBrowser->navigateBack();
+}
+
+void LLFloaterWebContent::onClickReload()
+{
+
+	if( mWebBrowser->getMediaPlugin() )
+	{
+		bool ignore_cache = true;
+		mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache );
+	}
+	else
+	{
+		mWebBrowser->navigateTo(mCurrentURL);
+	}
+}
+
+void LLFloaterWebContent::onClickStop()
+{
+	if( mWebBrowser->getMediaPlugin() )
+		mWebBrowser->getMediaPlugin()->browse_stop();
+
+	// still should happen when we catch the navigate complete event
+	// but sometimes (don't know why) that event isn't sent from Qt
+	// and we getto a point where the stop button stays active.
+	getChildView("reload")->setVisible( true );
+	getChildView("stop")->setVisible( false );
+}
+
+void LLFloaterWebContent::onEnterAddress()
+{
+	// make sure there is at least something there.
+	// (perhaps this test should be for minimum length of a URL)
+	std::string url = mAddressCombo->getValue().asString();
+	if ( url.length() > 0 )
+	{
+		mWebBrowser->navigateTo( url, "text/html");
+	};
+}
+
+void LLFloaterWebContent::onPopExternal()
+{
+	// make sure there is at least something there.
+	// (perhaps this test should be for minimum length of a URL)
+	std::string url = mAddressCombo->getValue().asString();
+	if ( url.length() > 0 )
+	{
+		LLWeb::loadURLExternal( url );
+	};
+}
diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h
new file mode 100644
index 0000000000000000000000000000000000000000..001d822ada48b918eb4b5e0b8243377984224c44
--- /dev/null
+++ b/indra/newview/llfloaterwebcontent.h
@@ -0,0 +1,82 @@
+/**
+ * @file llfloaterwebcontent.h
+ * @brief floater for displaying web content - e.g. profiles and search (eventually)
+ *
+ * $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$
+ */
+
+#ifndef LL_LLFLOATERWEBCONTENT_H
+#define LL_LLFLOATERWEBCONTENT_H
+
+#include "llfloater.h"
+#include "llmediactrl.h"
+
+class LLMediaCtrl;
+class LLComboBox;
+class LLTextBox;
+class LLProgressBar;
+class LLIconCtrl;
+
+class LLFloaterWebContent :
+	public LLFloater,
+	public LLViewerMediaObserver
+{
+public:
+    LOG_CLASS(LLFloaterWebContent);
+	LLFloaterWebContent(const LLSD& key);
+
+	void initializeURLHistory();
+
+	static void create(const std::string &url, const std::string& target, const std::string& uuid = LLStringUtil::null);
+
+	static void closeRequest(const std::string &uuid);
+	static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height);
+	void geometryChanged(S32 x, S32 y, S32 width, S32 height);
+
+	/* virtual */ BOOL postBuild();
+	/* virtual */ void onClose(bool app_quitting);
+	/* virtual */ void draw();
+
+	// inherited from LLViewerMediaObserver
+	/*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event);
+
+	void onClickBack();
+	void onClickForward();
+	void onClickReload();
+	void onClickStop();
+	void onEnterAddress();
+	void onPopExternal();
+
+private:
+	void open_media(const std::string& media_url, const std::string& target);
+	void set_current_url(const std::string& url);
+
+	LLMediaCtrl* mWebBrowser;
+	LLComboBox* mAddressCombo;
+	LLIconCtrl *mSecureLockIcon;
+	LLTextBox* mStatusBarText;
+	LLProgressBar* mStatusBarProgress;
+	std::string mCurrentURL;
+	std::string mUUID;
+};
+
+#endif  // LL_LLFLOATERWEBCONTENT_H
diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp
index bdc0dfa7e2f6f4bc57b30ba2f9e1d04980ae8257..f74ae92a7be67679acff65e255d8f6ad3008c8ec 100644
--- a/indra/newview/llimfloater.cpp
+++ b/indra/newview/llimfloater.cpp
@@ -470,7 +470,7 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id)
 }
 
 //static
-bool LLIMFloater::resetAllowedRectPadding(const LLSD& newvalue)
+bool LLIMFloater::resetAllowedRectPadding()
 {
 	//reset allowed rect right padding if "SidebarCameraMovement" option 
 	//or sidebar state changed
@@ -482,10 +482,10 @@ void LLIMFloater::getAllowedRect(LLRect& rect)
 {
 	if (sAllowedRectRightPadding == RECT_PADDING_NOT_INIT) //wasn't initialized
 	{
-		gSavedSettings.getControl("SidebarCameraMovement")->getSignal()->connect(boost::bind(&LLIMFloater::resetAllowedRectPadding, _2));
+		gSavedSettings.getControl("SidebarCameraMovement")->getSignal()->connect(boost::bind(&LLIMFloater::resetAllowedRectPadding));
 
 		LLSideTray*	side_bar = LLSideTray::getInstance();
-		side_bar->getCollapseSignal().connect(boost::bind(&LLIMFloater::resetAllowedRectPadding, _2));
+		side_bar->setVisibleWidthChangeCallback(boost::bind(&LLIMFloater::resetAllowedRectPadding));
 		sAllowedRectRightPadding = RECT_PADDING_NEED_RECALC;
 	}
 
@@ -500,10 +500,7 @@ void LLIMFloater::getAllowedRect(LLRect& rect)
 
 		if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE)
 		{
-			LLSideTray*	side_bar = LLSideTray::getInstance();
-
-			if (side_bar->getVisible() && !side_bar->getCollapsed())
-				sAllowedRectRightPadding += side_bar->getRect().getWidth();
+			sAllowedRectRightPadding += LLSideTray::getInstance()->getVisibleWidth();
 		}
 	}
 	rect.mRight -= sAllowedRectRightPadding;
diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h
index e80e45e64ae8c48fd3f56ad5cce0dfe5eb6c9aea..5158f6c1f788d76243b28ada549d9f848d89f721 100644
--- a/indra/newview/llimfloater.h
+++ b/indra/newview/llimfloater.h
@@ -156,7 +156,7 @@ class LLIMFloater : public LLTransientDockableFloater
 
 	static void closeHiddenIMToasts();
 
-	static bool resetAllowedRectPadding(const LLSD& newvalue);
+	static bool resetAllowedRectPadding();
 	//need to keep this static for performance issues
 	static S32 sAllowedRectRightPadding;
 
diff --git a/indra/newview/llinspecttoast.cpp b/indra/newview/llinspecttoast.cpp
index 58b3f0309fd795d10718af884d17214822b98903..d7b82667d17732dacbfa5a7e6521c011f4f8af3f 100644
--- a/indra/newview/llinspecttoast.cpp
+++ b/indra/newview/llinspecttoast.cpp
@@ -46,6 +46,7 @@ class LLInspectToast: public LLInspect
 	virtual ~LLInspectToast();
 
 	/*virtual*/ void onOpen(const LLSD& notification_id);
+	/*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask);
 private:
 	void onToastDestroy(LLToast * toast);
 
@@ -73,6 +74,7 @@ LLInspectToast::~LLInspectToast()
 	LLTransientFloaterMgr::getInstance()->removeControlView(this);
 }
 
+// virtual
 void LLInspectToast::onOpen(const LLSD& notification_id)
 {
 	LLInspect::onOpen(notification_id);
@@ -103,6 +105,15 @@ void LLInspectToast::onOpen(const LLSD& notification_id)
 	LLUI::positionViewNearMouse(this);
 }
 
+// virtual
+BOOL LLInspectToast::handleToolTip(S32 x, S32 y, MASK mask)
+{
+	// We don't like the way LLInspect handles tooltips
+	// (black tooltips look weird),
+	// so force using the default implementation (STORM-511).
+	return LLFloater::handleToolTip(x, y, mask);
+}
+
 void LLInspectToast::onToastDestroy(LLToast * toast)
 {
 	closeFloater(false);
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 5ba87423c7bae91222a0fa4ebe526acc332580a8..5108f6859220e4e158756847f52298c8e6a4d3d7 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -104,6 +104,7 @@ void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_
 bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*);
 bool confirm_attachment_rez(const LLSD& notification, const LLSD& response);
 void teleport_via_landmark(const LLUUID& asset_id);
+static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit);
 
 // +=================================================+
 // |        LLInvFVBridge                            |
@@ -2341,6 +2342,10 @@ void LLFolderBridge::pasteFromClipboard()
 	LLInventoryModel* model = getInventoryModel();
 	if(model && isClipboardPasteable())
 	{
+		const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
+		const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
+		const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
+
 		const LLUUID parent_id(mUUID);
 
 		LLDynamicArray<LLUUID> objects;
@@ -2353,7 +2358,14 @@ void LLFolderBridge::pasteFromClipboard()
 			LLInventoryItem *item = model->getItem(item_id);
 			if (item)
 			{
-				if(LLInventoryClipboard::instance().isCutMode())
+				if (move_is_into_current_outfit || move_is_into_outfit)
+				{
+					if (can_move_to_outfit(item, move_is_into_current_outfit))
+					{
+						dropToOutfit(item, move_is_into_current_outfit);
+					}
+				}
+				else if(LLInventoryClipboard::instance().isCutMode())
 				{
 					// move_inventory_item() is not enough,
 					//we have to update inventory locally too
@@ -2381,9 +2393,13 @@ void LLFolderBridge::pasteFromClipboard()
 
 void LLFolderBridge::pasteLinkFromClipboard()
 {
-	const LLInventoryModel* model = getInventoryModel();
+	LLInventoryModel* model = getInventoryModel();
 	if(model)
 	{
+		const LLUUID &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
+		const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
+		const BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLFolderType::FT_OUTFIT);
+
 		const LLUUID parent_id(mUUID);
 
 		LLDynamicArray<LLUUID> objects;
@@ -2393,7 +2409,15 @@ void LLFolderBridge::pasteLinkFromClipboard()
 			 ++iter)
 		{
 			const LLUUID &object_id = (*iter);
-			if (LLInventoryCategory *cat = model->getCategory(object_id))
+			if (move_is_into_current_outfit || move_is_into_outfit)
+			{
+				LLInventoryItem *item = model->getItem(object_id);
+				if (item && can_move_to_outfit(item, move_is_into_current_outfit))
+				{
+					dropToOutfit(item, move_is_into_current_outfit);
+				}
+			}
+			else if (LLInventoryCategory *cat = model->getCategory(object_id))
 			{
 				const std::string empty_description = "";
 				link_inventory_item(
@@ -5320,11 +5344,6 @@ void LLRecentItemsFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 
 	menuentry_vec_t disabled_items, items = getMenuItems();
 
-	items.erase(std::remove(items.begin(), items.end(), std::string("New Body Parts")), items.end());
-	items.erase(std::remove(items.begin(), items.end(), std::string("New Clothes")), items.end());
-	items.erase(std::remove(items.begin(), items.end(), std::string("New Note")), items.end());
-	items.erase(std::remove(items.begin(), items.end(), std::string("New Gesture")), items.end());
-	items.erase(std::remove(items.begin(), items.end(), std::string("New Script")), items.end());
 	items.erase(std::remove(items.begin(), items.end(), std::string("New Folder")), items.end());
 
 	hide_context_entries(menu, items, disabled_items);
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 0546803784f190386de1b557a0322754cf906da8..d866db1829240ac0ffbae648b380cacdaf8f39cc 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -49,18 +49,421 @@
 #include "llnotifications.h"
 #include "llwindow.h"
 #include "llviewerwindow.h"
+#include "llprogressview.h"
 #if LL_LINUX || LL_SOLARIS
 #include "lltrans.h"
 #endif
 #include "llsecapi.h"
 #include "llstartup.h"
 #include "llmachineid.h"
+#include "llupdaterservice.h"
+#include "llevents.h"
+#include "llnotificationsutil.h"
+#include "llappviewer.h"
+
+#include <boost/scoped_ptr.hpp>
+#include <sstream>
+
+class LLLoginInstance::Disposable {
+public:
+	virtual ~Disposable() {}
+};
+
+namespace {
+	class MandatoryUpdateMachine:
+		public LLLoginInstance::Disposable
+	{
+	public:
+		MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService);
+		
+		void start(void);
+		
+	private:
+		class State;
+		class CheckingForUpdate;
+		class Error;
+		class ReadyToInstall; 
+		class StartingUpdaterService;
+		class WaitingForDownload;
+		
+		LLLoginInstance & mLoginInstance;
+		boost::scoped_ptr<State> mState;
+		LLUpdaterService & mUpdaterService;
+		
+		void setCurrentState(State * newState);
+	};
+
+	
+	class MandatoryUpdateMachine::State {
+	public:
+		virtual ~State() {}
+		virtual void enter(void) {}
+		virtual void exit(void) {}
+	};
+	
+	
+	class MandatoryUpdateMachine::CheckingForUpdate:
+	public MandatoryUpdateMachine::State
+	{
+	public:
+		CheckingForUpdate(MandatoryUpdateMachine & machine);
+		
+		virtual void enter(void);
+		virtual void exit(void);
+		
+	private:
+		LLTempBoundListener mConnection;
+		MandatoryUpdateMachine & mMachine;
+		LLProgressView * mProgressView;
+		
+		bool onEvent(LLSD const & event);
+	};
+	
+	
+	class MandatoryUpdateMachine::Error:
+	public MandatoryUpdateMachine::State
+	{
+	public:
+		Error(MandatoryUpdateMachine & machine);
+		
+		virtual void enter(void);
+		virtual void exit(void);
+		void onButtonClicked(const LLSD &, const LLSD &);
+		
+	private:
+		MandatoryUpdateMachine & mMachine;
+	};
+	
+	
+	class MandatoryUpdateMachine::ReadyToInstall:
+	public MandatoryUpdateMachine::State
+	{
+	public:
+		ReadyToInstall(MandatoryUpdateMachine & machine);
+		
+		virtual void enter(void);
+		virtual void exit(void);
+		
+	private:
+		MandatoryUpdateMachine & mMachine;
+	};
+	
+	
+	class MandatoryUpdateMachine::StartingUpdaterService:
+	public MandatoryUpdateMachine::State
+	{
+	public:
+		StartingUpdaterService(MandatoryUpdateMachine & machine);
+		
+		virtual void enter(void);
+		virtual void exit(void);
+		void onButtonClicked(const LLSD & uiform, const LLSD & result);
+	private:
+		MandatoryUpdateMachine & mMachine;
+	};
+	
+	
+	class MandatoryUpdateMachine::WaitingForDownload:
+		public MandatoryUpdateMachine::State
+	{
+	public:
+		WaitingForDownload(MandatoryUpdateMachine & machine);
+		
+		virtual void enter(void);
+		virtual void exit(void);
+		
+	private:
+		LLTempBoundListener mConnection;
+		MandatoryUpdateMachine & mMachine;
+		LLProgressView * mProgressView;
+		
+		bool onEvent(LLSD const & event);
+	};
+}
 
 static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback";
 static const char * const TOS_LISTENER_NAME = "lllogininstance_tos";
 
 std::string construct_start_string();
 
+
+
+// MandatoryUpdateMachine
+//-----------------------------------------------------------------------------
+
+
+MandatoryUpdateMachine::MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService):
+	mLoginInstance(loginInstance),
+	mUpdaterService(updaterService)
+{
+	; // No op.
+}
+
+
+void MandatoryUpdateMachine::start(void)
+{
+	llinfos << "starting manditory update machine" << llendl;
+	
+	if(mUpdaterService.isChecking()) {
+		switch(mUpdaterService.getState()) {
+			case LLUpdaterService::UP_TO_DATE:
+				mUpdaterService.stopChecking();
+				mUpdaterService.startChecking();
+				// Fall through.
+			case LLUpdaterService::INITIAL:
+			case LLUpdaterService::CHECKING_FOR_UPDATE:
+				setCurrentState(new CheckingForUpdate(*this));
+				break;
+			case LLUpdaterService::DOWNLOADING:
+				setCurrentState(new WaitingForDownload(*this));
+				break;
+			case LLUpdaterService::TERMINAL:
+				if(LLUpdaterService::updateReadyToInstall()) {
+					setCurrentState(new ReadyToInstall(*this));
+				} else {
+					setCurrentState(new Error(*this));
+				}
+				break;
+			case LLUpdaterService::FAILURE:
+				setCurrentState(new Error(*this));
+				break;
+			default:
+				llassert(!"unpossible case");
+				break;
+		}
+	} else {
+		setCurrentState(new StartingUpdaterService(*this));
+	}
+}
+
+
+void MandatoryUpdateMachine::setCurrentState(State * newStatePointer)
+{
+	{
+		boost::scoped_ptr<State> newState(newStatePointer);
+		if(mState != 0) mState->exit();
+		mState.swap(newState);
+		
+		// Old state will be deleted on exit from this block before the new state
+		// is entered.
+	}
+	if(mState != 0) mState->enter();
+}
+
+
+
+// MandatoryUpdateMachine::CheckingForUpdate
+//-----------------------------------------------------------------------------
+
+
+MandatoryUpdateMachine::CheckingForUpdate::CheckingForUpdate(MandatoryUpdateMachine & machine):
+	mMachine(machine)
+{
+	; // No op.
+}
+
+
+void MandatoryUpdateMachine::CheckingForUpdate::enter(void)
+{
+	llinfos << "entering checking for update" << llendl;
+	
+	mProgressView = gViewerWindow->getProgressView();
+	mProgressView->setMessage("Looking for update...");
+	mProgressView->setText("There is a required update for your Second Life installation.");
+	mProgressView->setPercent(0);
+	mProgressView->setVisible(true);
+	mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).
+		listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::CheckingForUpdate::onEvent, this, _1));
+}
+
+
+void MandatoryUpdateMachine::CheckingForUpdate::exit(void)
+{
+}
+
+
+bool MandatoryUpdateMachine::CheckingForUpdate::onEvent(LLSD const & event)
+{
+	if(event["type"].asInteger() == LLUpdaterService::STATE_CHANGE) {
+		switch(event["state"].asInteger()) {
+			case LLUpdaterService::DOWNLOADING:
+				mMachine.setCurrentState(new WaitingForDownload(mMachine));
+				break;
+			case LLUpdaterService::UP_TO_DATE:
+			case LLUpdaterService::TERMINAL:
+			case LLUpdaterService::FAILURE:
+				mProgressView->setVisible(false);
+				mMachine.setCurrentState(new Error(mMachine));
+				break;
+			case LLUpdaterService::INSTALLING:
+				llassert(!"can't possibly be installing");
+				break;
+			default:
+				break;
+		}
+	} else {
+		; // Ignore.
+	}
+	
+	return false;
+}
+
+
+
+// MandatoryUpdateMachine::Error
+//-----------------------------------------------------------------------------
+
+
+MandatoryUpdateMachine::Error::Error(MandatoryUpdateMachine & machine):
+	mMachine(machine)
+{
+	; // No op.
+}
+
+
+void MandatoryUpdateMachine::Error::enter(void)
+{
+	llinfos << "entering error" << llendl;
+	LLNotificationsUtil::add("FailedUpdateInstall", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::Error::onButtonClicked, this, _1, _2));
+}
+
+
+void MandatoryUpdateMachine::Error::exit(void)
+{
+	LLAppViewer::instance()->forceQuit();
+}
+
+
+void MandatoryUpdateMachine::Error::onButtonClicked(const LLSD &, const LLSD &)
+{
+	mMachine.setCurrentState(0);
+}
+
+
+
+// MandatoryUpdateMachine::ReadyToInstall
+//-----------------------------------------------------------------------------
+
+
+MandatoryUpdateMachine::ReadyToInstall::ReadyToInstall(MandatoryUpdateMachine & machine):
+	mMachine(machine)
+{
+	; // No op.
+}
+
+
+void MandatoryUpdateMachine::ReadyToInstall::enter(void)
+{
+	llinfos << "entering ready to install" << llendl;
+	// Open update ready dialog.
+}
+
+
+void MandatoryUpdateMachine::ReadyToInstall::exit(void)
+{
+	// Restart viewer.
+}
+
+
+
+// MandatoryUpdateMachine::StartingUpdaterService
+//-----------------------------------------------------------------------------
+
+
+MandatoryUpdateMachine::StartingUpdaterService::StartingUpdaterService(MandatoryUpdateMachine & machine):
+	mMachine(machine)
+{
+	; // No op.
+}
+
+
+void MandatoryUpdateMachine::StartingUpdaterService::enter(void)
+{
+	llinfos << "entering start update service" << llendl;
+	LLNotificationsUtil::add("UpdaterServiceNotRunning", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked, this, _1, _2));
+}
+
+
+void MandatoryUpdateMachine::StartingUpdaterService::exit(void)
+{
+	; // No op.
+}
+
+
+void MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked(const LLSD & uiform, const LLSD & result)
+{
+	if(result["OK_okcancelbuttons"].asBoolean()) {
+		mMachine.mUpdaterService.startChecking(false);
+		mMachine.setCurrentState(new CheckingForUpdate(mMachine));
+	} else {
+		LLAppViewer::instance()->forceQuit();
+	}
+}
+
+
+
+// MandatoryUpdateMachine::WaitingForDownload
+//-----------------------------------------------------------------------------
+
+
+MandatoryUpdateMachine::WaitingForDownload::WaitingForDownload(MandatoryUpdateMachine & machine):
+	mMachine(machine),
+	mProgressView(0)
+{
+	; // No op.
+}
+
+
+void MandatoryUpdateMachine::WaitingForDownload::enter(void)
+{
+	llinfos << "entering waiting for download" << llendl;
+	mProgressView = gViewerWindow->getProgressView();
+	mProgressView->setMessage("Downloading update...");
+	std::ostringstream stream;
+	stream << "There is a required update for your Second Life installation." << std::endl <<
+		"Version " << mMachine.mUpdaterService.updatedVersion();
+	mProgressView->setText(stream.str());
+	mProgressView->setPercent(0);
+	mProgressView->setVisible(true);
+	mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).
+		listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::WaitingForDownload::onEvent, this, _1));
+}
+
+
+void MandatoryUpdateMachine::WaitingForDownload::exit(void)
+{
+	mProgressView->setVisible(false);
+}
+
+
+bool MandatoryUpdateMachine::WaitingForDownload::onEvent(LLSD const & event)
+{
+	switch(event["type"].asInteger()) {
+		case LLUpdaterService::DOWNLOAD_COMPLETE:
+			mMachine.setCurrentState(new ReadyToInstall(mMachine));
+			break;
+		case LLUpdaterService::DOWNLOAD_ERROR:
+			mMachine.setCurrentState(new Error(mMachine));
+			break;
+		case LLUpdaterService::PROGRESS: {
+			double downloadSize = event["download_size"].asReal();
+			double bytesDownloaded = event["bytes_downloaded"].asReal();
+			mProgressView->setPercent(100. * bytesDownloaded / downloadSize);
+			break;
+		}
+		default:
+			break;
+	}
+
+	return false;
+}
+
+
+
+// LLLoginInstance
+//-----------------------------------------------------------------------------
+
+
 LLLoginInstance::LLLoginInstance() :
 	mLoginModule(new LLLogin()),
 	mNotifications(NULL),
@@ -69,7 +472,8 @@ LLLoginInstance::LLLoginInstance() :
 	mSkipOptionalUpdate(false),
 	mAttemptComplete(false),
 	mTransferRate(0.0f),
-	mDispatcher("LLLoginInstance", "change")
+	mDispatcher("LLLoginInstance", "change"),
+	mUpdaterService(0)
 {
 	mLoginModule->getEventPump().listen("lllogininstance", 
 		boost::bind(&LLLoginInstance::handleLoginEvent, this, _1));
@@ -354,6 +758,15 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
 
 void LLLoginInstance::updateApp(bool mandatory, const std::string& auth_msg)
 {
+	if(mandatory)
+	{
+		gViewerWindow->setShowProgress(false);
+		MandatoryUpdateMachine * machine = new MandatoryUpdateMachine(*this, *mUpdaterService);
+		mUpdateStateMachine.reset(machine);
+		machine->start();
+		return;
+	}
+	
 	// store off config state, as we might quit soon
 	gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE);	
 	LLUIColorTable::instance().saveUserSettings();
diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h
index 159e05046c40767c01fc8a75bfb4d8025b044fad..b872d7d1b1a417b2e78025e4ba7f9aa2f34a4e6f 100644
--- a/indra/newview/lllogininstance.h
+++ b/indra/newview/lllogininstance.h
@@ -34,12 +34,15 @@
 class LLLogin;
 class LLEventStream;
 class LLNotificationsInterface;
+class LLUpdaterService;
 
 // This class hosts the login module and is used to 
 // negotiate user authentication attempts.
 class LLLoginInstance : public LLSingleton<LLLoginInstance>
 {
 public:
+	class Disposable;
+
 	LLLoginInstance();
 	~LLLoginInstance();
 
@@ -75,6 +78,7 @@ class LLLoginInstance : public LLSingleton<LLLoginInstance>
 	typedef boost::function<void()> UpdaterLauncherCallback;
 	void setUpdaterLauncher(const UpdaterLauncherCallback& ulc) { mUpdaterLauncher = ulc; }
 
+	void setUpdaterService(LLUpdaterService * updaterService) { mUpdaterService = updaterService; }
 private:
 	void constructAuthParams(LLPointer<LLCredential> user_credentials);
 	void updateApp(bool mandatory, const std::string& message);
@@ -104,6 +108,8 @@ class LLLoginInstance : public LLSingleton<LLLoginInstance>
 	int mLastExecEvent;
 	UpdaterLauncherCallback mUpdaterLauncher;
 	LLEventDispatcher mDispatcher;
+	LLUpdaterService * mUpdaterService;	
+	boost::scoped_ptr<Disposable> mUpdateStateMachine;
 };
 
 #endif
diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp
index 0f66713ab09673eda55f08aa450debc3756a0eec..9493fddf5044107e0aa502e02ffeea1b967737e8 100644
--- a/indra/newview/llmediactrl.cpp
+++ b/indra/newview/llmediactrl.cpp
@@ -25,7 +25,7 @@
  */
 
 #include "llviewerprecompiledheaders.h"
-
+#include "lltooltip.h"
 
 #include "llmediactrl.h"
 
@@ -54,6 +54,10 @@
 #include "llbutton.h"
 #include "llcheckboxctrl.h"
 #include "llnotifications.h"
+#include "lllineeditor.h"
+#include "llfloatermediabrowser.h"
+#include "llfloaterwebcontent.h"
+#include "llwindowshade.h"
 
 extern BOOL gRestoreGL;
 
@@ -98,7 +102,9 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) :
 	mTextureHeight ( 1024 ),
 	mClearCache(false),
 	mHomePageMimeType(p.initial_mime_type),
-	mTrusted(p.trusted_content)
+	mTrusted(p.trusted_content),
+	mWindowShade(NULL),
+	mHoverTextChanged(false)
 {
 	{
 		LLColor4 color = p.caret_color().get();
@@ -127,7 +133,7 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) :
 		setTextureSize(screen_width, screen_height);
 	}
 	
-	mMediaTextureID.generate();
+	mMediaTextureID = getKey();
 	
 	// We don't need to create the media source up front anymore unless we have a non-empty home URL to navigate to.
 	if(!mHomePageUrl.empty())
@@ -141,8 +147,6 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) :
 //	addChild( mBorder );
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// note: this is now a singleton and destruction happens via initClass() now
 LLMediaCtrl::~LLMediaCtrl()
 {
 
@@ -182,6 +186,13 @@ BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask )
 		mMediaSource->mouseMove(x, y, mask);
 		gViewerWindow->setCursor(mMediaSource->getLastSetCursor());
 	}
+	
+	// TODO: Is this the right way to handle hover text changes driven by the plugin?
+	if(mHoverTextChanged)
+	{
+		mHoverTextChanged = false;
+		handleToolTip(x, y, mask);
+	}
 
 	return TRUE;
 }
@@ -197,6 +208,35 @@ BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks )
 	return TRUE;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+//	virtual 
+BOOL LLMediaCtrl::handleToolTip(S32 x, S32 y, MASK mask)
+{
+	std::string hover_text;
+	
+	if (mMediaSource && mMediaSource->hasMedia())
+		hover_text = mMediaSource->getMediaPlugin()->getHoverText();
+	
+	if(hover_text.empty())
+	{
+		return FALSE;
+	}
+	else
+	{
+		S32 screen_x, screen_y;
+
+		localPointToScreen(x, y, &screen_x, &screen_y);
+		LLRect sticky_rect_screen;
+		sticky_rect_screen.setCenterAndSize(screen_x, screen_y, 20, 20);
+
+		LLToolTipMgr::instance().show(LLToolTip::Params()
+			.message(hover_text)
+			.sticky_rect(sticky_rect_screen));		
+	}
+
+	return TRUE;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //
 BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask )
@@ -338,85 +378,6 @@ void LLMediaCtrl::onFocusLost()
 //
 BOOL LLMediaCtrl::postBuild ()
 {
-	LLLayoutStack::Params layout_p;
-	layout_p.name = "notification_stack";
-	layout_p.rect = LLRect(0,getLocalRect().mTop,getLocalRect().mRight, 30);
-	layout_p.follows.flags = FOLLOWS_ALL;
-	layout_p.mouse_opaque = false;
-	layout_p.orientation = "vertical";
-
-	LLLayoutStack* stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
-	addChild(stackp);
-
-	LLLayoutPanel::Params panel_p;
-	panel_p.rect = LLRect(0, 30, 800, 0);
-	panel_p.min_height = 30;
-	panel_p.name = "notification_area";
-	panel_p.visible = false;
-	panel_p.user_resize = false;
-	panel_p.background_visible = true;
-	panel_p.bg_alpha_image.name = "Yellow_Gradient";
-	panel_p.auto_resize = false;
-	LLLayoutPanel* notification_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
-	stackp->addChild(notification_panel);
-
-	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
-	panel_p.auto_resize = true;
-	panel_p.mouse_opaque = false;
-	LLLayoutPanel* dummy_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
-	stackp->addChild(dummy_panel);
-
-	layout_p = LLUICtrlFactory::getDefaultParams<LLLayoutStack>();
-	layout_p.rect = LLRect(0, 30, 800, 0);
-	layout_p.follows.flags = FOLLOWS_ALL;
-	layout_p.orientation = "horizontal";
-	stackp = LLUICtrlFactory::create<LLLayoutStack>(layout_p);
-	notification_panel->addChild(stackp);
-
-	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
-	panel_p.rect.height = 30;
-	LLLayoutPanel* panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
-	stackp->addChild(panel);
-
-	LLIconCtrl::Params icon_p;
-	icon_p.name = "notification_icon";
-	icon_p.rect = LLRect(5, 23, 21, 8);
-	panel->addChild(LLUICtrlFactory::create<LLIconCtrl>(icon_p));
-
-	LLTextBox::Params text_p;
-	text_p.rect = LLRect(31, 20, 430, 0);
-	text_p.text_color = LLColor4::black;
-	text_p.font = LLFontGL::getFontSansSerif();
-	text_p.font.style = "BOLD";
-	text_p.name = "notification_text";
-	text_p.use_ellipses = true;
-	panel->addChild(LLUICtrlFactory::create<LLTextBox>(text_p));
-
-	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
-	panel_p.auto_resize = false;
-	panel_p.user_resize = false;
-	panel_p.name="form_elements";
-	panel_p.rect = LLRect(0, 30, 130, 0);
-	LLLayoutPanel* form_elements_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
-	stackp->addChild(form_elements_panel);
-
-	panel_p = LLUICtrlFactory::getDefaultParams<LLLayoutPanel>();
-	panel_p.auto_resize = false;
-	panel_p.user_resize = false;
-	panel_p.rect = LLRect(0, 30, 25, 0);
-	LLLayoutPanel* close_panel = LLUICtrlFactory::create<LLLayoutPanel>(panel_p);
-	stackp->addChild(close_panel);
-
-	LLButton::Params button_p;
-	button_p.name = "close_notification";
-	button_p.rect = LLRect(5, 23, 21, 7);
-	button_p.image_color=LLUIColorTable::instance().getColor("DkGray_66");
-    button_p.image_unselected.name="Icon_Close_Foreground";
-	button_p.image_selected.name="Icon_Close_Press";
-	button_p.click_callback.function = boost::bind(&LLMediaCtrl::onCloseNotification, this);
-
-	close_panel->addChild(LLUICtrlFactory::create<LLButton>(button_p));
-
 	setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2));
 	return TRUE;
 }
@@ -425,13 +386,15 @@ BOOL LLMediaCtrl::postBuild ()
 //
 BOOL LLMediaCtrl::handleKeyHere( KEY key, MASK mask )
 {
-	if (LLPanel::handleKeyHere(key, mask)) return TRUE;
 	BOOL result = FALSE;
 	
 	if (mMediaSource)
 	{
 		result = mMediaSource->handleKeyHere(key, mask);
 	}
+	
+	if ( ! result )
+		result = LLPanel::handleKeyHere(key, mask);
 		
 	return result;
 }
@@ -451,7 +414,6 @@ void LLMediaCtrl::handleVisibilityChange ( BOOL new_visibility )
 //
 BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char)
 {
-	if (LLPanel::handleUnicodeCharHere(uni_char)) return TRUE;
 	BOOL result = FALSE;
 	
 	if (mMediaSource)
@@ -459,6 +421,9 @@ BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char)
 		result = mMediaSource->handleUnicodeCharHere(uni_char);
 	}
 
+	if ( ! result )
+		result = LLPanel::handleUnicodeCharHere(uni_char);
+
 	return result;
 }
 
@@ -914,11 +879,6 @@ void LLMediaCtrl::draw()
 	if ( mBorder && mBorder->getVisible() )
 		mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) );
 
-	if (mCurNotification && !mCurNotification->isActive())
-	{
-		hideNotification();
-	}
-	
 	LLPanel::draw();
 
 	// Restore the previous values
@@ -1026,7 +986,7 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
 
 			LLNotification::Params notify_params;
 			notify_params.name = "PopupAttempt";
-			notify_params.payload = LLSD().with("target", target).with("url", url).with("uuid", uuid).with("media_id", getKey());
+			notify_params.payload = LLSD().with("target", target).with("url", url).with("uuid", uuid).with("media_id", mMediaTextureID);
 			notify_params.functor.function = boost::bind(&LLMediaCtrl::onPopup, this, _1, _2);
 
 			if (mTrusted)
@@ -1081,6 +1041,31 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
 			LL_DEBUGS("Media") << "Media event:  MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID() << LL_ENDL;
 		}
 		break;
+
+		case MEDIA_EVENT_AUTH_REQUEST:
+		{
+			LLNotification::Params auth_request_params;
+			auth_request_params.name = "AuthRequest";
+
+			// pass in host name and realm for site (may be zero length but will always exist)
+			LLSD args;
+			LLURL raw_url( self->getAuthURL().c_str() );
+			args["HOST_NAME"] = raw_url.getAuthority();
+			args["REALM"] = self->getAuthRealm();
+			auth_request_params.substitutions = args;
+
+			auth_request_params.payload = LLSD().with("media_id", mMediaTextureID);
+			auth_request_params.functor.function = boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2);
+			LLNotifications::instance().add(auth_request_params);
+		};
+		break;
+
+		case MEDIA_EVENT_LINK_HOVERED:
+		{
+			LL_DEBUGS("Media") <<  "Media event:  MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << LL_ENDL;
+			mHoverTextChanged = true;
+		};
+		break;
 	};
 
 	// chain all events to any potential observers of this object.
@@ -1098,109 +1083,85 @@ void LLMediaCtrl::onPopup(const LLSD& notification, const LLSD& response)
 {
 	if (response["open"])
 	{
-		LLWeb::loadURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]);
+		// name of default floater to open
+		std::string floater_name = "media_browser";
+
+		// look for parent floater name
+		if ( gFloaterView )
+		{
+			if ( gFloaterView->getParentFloater(this) )
+			{
+				floater_name = gFloaterView->getParentFloater(this)->getInstanceName();
+			}
+			else
+			{
+				lldebugs << "No gFloaterView->getParentFloater(this) for onPopuup()" << llendl;
+			};
+		}
+		else
+		{
+			lldebugs << "No gFloaterView for onPopuup()" << llendl;
+		};
+
+		// (for now) open web content floater if that's our parent, otherwise, open the current media floater
+		// (this will change soon)
+		if ( floater_name == "web_content" )
+		{
+			LLWeb::loadWebURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]);
+		}
+		else
+		{
+			LLWeb::loadURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]);
+		}
 	}
 	else
 	{
 		// Make sure the opening instance knows its window open request was denied, so it can clean things up.
 		LLViewerMedia::proxyWindowClosed(notification["payload"]["uuid"]);
 	}
-
 }
 
-void LLMediaCtrl::onCloseNotification()
-{
-	LLNotifications::instance().cancel(mCurNotification);
-}
-
-void LLMediaCtrl::onClickIgnore(LLUICtrl* ctrl)
+void LLMediaCtrl::showNotification(LLNotificationPtr notify)
 {
-	bool check = ctrl->getValue().asBoolean();
-	if (mCurNotification && mCurNotification->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN)
+	delete mWindowShade;
+
+	LLWindowShade::Params params;
+	params.name = "notification_shade";
+	params.rect = getLocalRect();
+	params.follows.flags = FOLLOWS_ALL;
+	params.notification = notify;
+	params.modal = true;
+	//HACK: don't hardcode this
+	if (notify->getIcon() == "Popup_Caution")
 	{
-		// question was "show again" so invert value to get "ignore"
-		check = !check;
+		params.bg_image.name = "Yellow_Gradient";
+		params.text_color = LLColor4::black;
 	}
-	mCurNotification->setIgnored(check);
-}
-
-void LLMediaCtrl::onClickNotificationButton(const std::string& name)
-{
-	if (!mCurNotification) return;
-
-	LLSD response = mCurNotification->getResponseTemplate();
-	response[name] = true;
-
-	mCurNotification->respond(response); 
-}
-
-void LLMediaCtrl::showNotification(LLNotificationPtr notify)
-{
-	mCurNotification = notify;
-
-	// add popup here
-	LLSD payload = notify->getPayload();
-
-	LLNotificationFormPtr formp = notify->getForm();
-	LLLayoutPanel& panel = getChildRef<LLLayoutPanel>("notification_area");
-	panel.setVisible(true);
-	panel.getChild<LLUICtrl>("notification_icon")->setValue(notify->getIcon());
-	panel.getChild<LLUICtrl>("notification_text")->setValue(notify->getMessage());
-	panel.getChild<LLUICtrl>("notification_text")->setToolTip(notify->getMessage());
-	LLNotificationForm::EIgnoreType ignore_type = formp->getIgnoreType(); 
-	LLLayoutPanel& form_elements = panel.getChildRef<LLLayoutPanel>("form_elements");
-	form_elements.deleteAllChildren();
-
-	const S32 FORM_PADDING_HORIZONTAL = 10;
-	const S32 FORM_PADDING_VERTICAL = 3;
-	S32 cur_x = FORM_PADDING_HORIZONTAL;
-
-	if (ignore_type != LLNotificationForm::IGNORE_NO)
+	else
+	//HACK: another one since XUI doesn't support what we need right now
+	if (notify->getName() == "AuthRequest")
 	{
-		LLCheckBoxCtrl::Params checkbox_p;
-		checkbox_p.name = "ignore_check";
-		checkbox_p.rect = LLRect(cur_x, form_elements.getRect().getHeight() - FORM_PADDING_VERTICAL, cur_x, FORM_PADDING_VERTICAL);
-		checkbox_p.label = formp->getIgnoreMessage();
-		checkbox_p.label_text.text_color = LLColor4::black;
-		checkbox_p.commit_callback.function = boost::bind(&LLMediaCtrl::onClickIgnore, this, _1);
-		checkbox_p.initial_value = formp->getIgnored();
-
-		LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p);
-		check->setRect(check->getBoundingRect());
-		form_elements.addChild(check);
-		cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL;
+		params.bg_image.name = "Yellow_Gradient";
+		params.text_color = LLColor4::black;
+		params.can_close = false;
 	}
-
-	for (S32 i = 0; i < formp->getNumElements(); i++)
+	else
 	{
-		LLSD form_element = formp->getElement(i);
-		if (form_element["type"].asString() == "button")
-		{
-			LLButton::Params button_p;
-			button_p.name = form_element["name"];
-			button_p.label = form_element["text"];
-			button_p.rect = LLRect(cur_x, form_elements.getRect().getHeight() - FORM_PADDING_VERTICAL, cur_x, FORM_PADDING_VERTICAL);
-			button_p.click_callback.function = boost::bind(&LLMediaCtrl::onClickNotificationButton, this, form_element["name"].asString());
-			button_p.auto_resize = true;
-
-			LLButton* button = LLUICtrlFactory::create<LLButton>(button_p);
-			button->autoResize();
-			form_elements.addChild(button);
-
-			cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL;
-		}
+		//HACK: make this a property of the notification itself, "cancellable"
+		params.can_close = false;
+		params.text_color.control = "LabelTextColor";
 	}
 
+	mWindowShade = LLUICtrlFactory::create<LLWindowShade>(params);
 
-	form_elements.reshape(cur_x, form_elements.getRect().getHeight());
-
-	//LLWeb::loadURL(payload["url"], payload["target"]);
+	addChild(mWindowShade);
+	mWindowShade->show();
 }
 
 void LLMediaCtrl::hideNotification()
 {
-	LLLayoutPanel& panel = getChildRef<LLLayoutPanel>("notification_area");
-	panel.setVisible(FALSE);
-
-	mCurNotification.reset();
+	if (mWindowShade)
+	{
+		mWindowShade->hide();
+	}
 }
diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h
index 96bb0c1df5c061aa553f102851ed0d94a0bddaa3..38a74f90d3cf6a2ed5040ced04d1e78bc2ce44d0 100644
--- a/indra/newview/llmediactrl.h
+++ b/indra/newview/llmediactrl.h
@@ -90,6 +90,7 @@ class LLMediaCtrl :
 		virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
 		virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
 		virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
+		virtual BOOL handleToolTip(S32 x, S32 y, MASK mask);
 
 		// navigation
 		void navigateTo( std::string url_in, std::string mime_type = "");
@@ -168,9 +169,6 @@ class LLMediaCtrl :
 	private:
 		void onVisibilityChange ( const LLSD& new_visibility );
 		void onPopup(const LLSD& notification, const LLSD& response);
-		void onCloseNotification();
-		void onClickNotificationButton(const std::string& name);
-		void onClickIgnore(LLUICtrl* ctrl);
 
 		const S32 mTextureDepthBytes;
 		LLUUID mMediaTextureID;
@@ -194,7 +192,8 @@ class LLMediaCtrl :
 		S32 mTextureWidth;
 		S32 mTextureHeight;
 		bool mClearCache;
-		boost::shared_ptr<class LLNotification> mCurNotification;
+		class LLWindowShade* mWindowShade;
+		bool mHoverTextChanged;
 };
 
 #endif // LL_LLMediaCtrl_H
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
index f084002385abd6952fd9398c163e4d24ea2c47ac..1a8ec4991d7dedef4b1f47cf0440dfce89b213e4 100644
--- a/indra/newview/llnetmap.cpp
+++ b/indra/newview/llnetmap.cpp
@@ -94,10 +94,12 @@ LLNetMap::LLNetMap (const Params & p)
 	mToolTipMsg()
 {
 	mDotRadius = llmax(DOT_SCALE * mPixelsPerMeter, MIN_DOT_RADIUS);
+	setScale(gSavedSettings.getF32("MiniMapScale"));
 }
 
 LLNetMap::~LLNetMap()
 {
+	gSavedSettings.setF32("MiniMapScale", mScale);
 }
 
 void LLNetMap::setScale( F32 scale )
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
index 1249d5d85600563ff67cf4c2954d5cfb39225fef..a9bcdef47cf93b70f13021e19742c43b26216e7e 100644
--- a/indra/newview/llpanelavatar.cpp
+++ b/indra/newview/llpanelavatar.cpp
@@ -341,10 +341,11 @@ LLPanelAvatarNotes::~LLPanelAvatarNotes()
 	if(getAvatarId().notNull())
 	{
 		LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
-		if(LLVoiceClient::instanceExists())
-		{
-			LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
-		}
+	}
+
+	if(LLVoiceClient::instanceExists())
+	{
+		LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
 	}
 }
 
@@ -758,10 +759,11 @@ LLPanelAvatarProfile::~LLPanelAvatarProfile()
 	if(getAvatarId().notNull())
 	{
 		LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this);
-		if(LLVoiceClient::instanceExists())
-		{
-			LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
-		}
+	}
+
+	if(LLVoiceClient::instanceExists())
+	{
+		LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this);
 	}
 }
 
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 2e4be78be18387c2a50d98a45f27420507d70b69..5f89097c1787c9f122d0adff740cee5af84e54c4 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -73,7 +73,6 @@
 #endif  // LL_WINDOWS
 
 #include "llsdserialize.h"
-#define USE_VIEWER_AUTH 0
 
 const S32 BLACK_BORDER_HEIGHT = 160;
 const S32 MAX_PASSWORD = 16;
@@ -190,10 +189,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
 
 	buildFromFile( "panel_login.xml");
 	
-#if USE_VIEWER_AUTH
-	//leave room for the login menu bar
-	setRect(LLRect(0, rect.getHeight()-18, rect.getWidth(), 0)); 
-#endif
 	// Legacy login web page is hidden under the menu bar.
 	// Adjust reg-in-client web browser widget to not be hidden.
 	if (gSavedSettings.getBOOL("RegInClient"))
@@ -205,16 +200,12 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
 		reshape(rect.getWidth(), rect.getHeight());
 	}
 
-#if !USE_VIEWER_AUTH
 	getChild<LLLineEditor>("password_edit")->setKeystrokeCallback(onPassKey, this);
 
 	// change z sort of clickable text to be behind buttons
 	//sendChildToBack(getChildView("channel_text"));
 	sendChildToBack(getChildView("forgot_password_text"));
 
-	LLLineEditor* edit = getChild<LLLineEditor>("password_edit");
-	if (edit) edit->setDrawAsterixes(TRUE);
-
 	if(LLStartUp::getStartSLURL().getType() != LLSLURL::LOCATION)
 	{
 		LLSLURL slurl(gSavedSettings.getString("LoginLocation"));
@@ -248,7 +239,6 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,
 
 	LLTextBox* need_help_text = getChild<LLTextBox>("login_help");
 	need_help_text->setClickedCallback(onClickHelp, NULL);
-#endif    
 	
 	// get the web browser control
 	LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("login_html");
@@ -275,15 +265,9 @@ void LLPanelLogin::reshapeBrowser()
 	LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("login_html");
 	LLRect rect = gViewerWindow->getWindowRectScaled();
 	LLRect html_rect;
-#if USE_VIEWER_AUTH
-	html_rect.setCenterAndSize( 
-		rect.getCenterX() - 2, rect.getCenterY(), 
-		rect.getWidth() + 6, rect.getHeight());
-#else
 	html_rect.setCenterAndSize(
 		rect.getCenterX() - 2, rect.getCenterY() + 40,
 		rect.getWidth() + 6, rect.getHeight() - 78 );
-#endif
 	web_browser->setRect( html_rect );
 	web_browser->reshape( html_rect.getWidth(), html_rect.getHeight(), TRUE );
 	reshape( rect.getWidth(), rect.getHeight(), 1 );
@@ -306,7 +290,6 @@ void LLPanelLogin::setSiteIsAlive( bool alive )
 	else
 	// the site is not available (missing page, server down, other badness)
 	{
-#if !USE_VIEWER_AUTH
 		if ( web_browser )
 		{
 			// hide browser control (revealing default one)
@@ -315,16 +298,6 @@ void LLPanelLogin::setSiteIsAlive( bool alive )
 			// mark as unavailable
 			mHtmlAvailable = FALSE;
 		}
-#else
-
-		if ( web_browser )
-		{	
-			web_browser->navigateToLocalPage( "loading-error" , "index.html" );
-
-			// mark as available
-			mHtmlAvailable = TRUE;
-		}
-#endif
 	}
 }
 
@@ -364,7 +337,6 @@ void LLPanelLogin::draw()
 
 		if ( mHtmlAvailable )
 		{
-#if !USE_VIEWER_AUTH
 			if (getChild<LLView>("login_widgets")->getVisible())
 			{
 				// draw a background box in black
@@ -373,7 +345,6 @@ void LLPanelLogin::draw()
 				// just the blue background to the native client UI
 				mLogoImage->draw(0, -264, width + 8, mLogoImage->getHeight());
 			}
-#endif
 		}
 		else
 		{
@@ -419,12 +390,6 @@ void LLPanelLogin::setFocus(BOOL b)
 // static
 void LLPanelLogin::giveFocus()
 {
-#if USE_VIEWER_AUTH
-	if (sInstance)
-	{
-		sInstance->setFocus(TRUE);
-	}
-#else
 	if( sInstance )
 	{
 		// Grab focus and move cursor to first blank input field
@@ -453,17 +418,18 @@ void LLPanelLogin::giveFocus()
 			edit->selectAll();
 		}
 	}
-#endif
 }
 
 // static
 void LLPanelLogin::showLoginWidgets()
 {
+	// *NOTE: Mani - This may or may not be obselete code.
+	// It seems to be part of the defunct? reg-in-client project.
 	sInstance->getChildView("login_widgets")->setVisible( true);
 	LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html");
 	sInstance->reshapeBrowser();
 	// *TODO: Append all the usual login parameters, like first_login=Y etc.
-	std::string splash_screen_url = sInstance->getString("real_url");
+	std::string splash_screen_url = LLGridManager::getInstance()->getLoginPage();
 	web_browser->navigateTo( splash_screen_url, "text/html" );
 	LLUICtrl* username_edit = sInstance->getChild<LLUICtrl>("username_edit");
 	username_edit->setFocus(TRUE);
@@ -833,73 +799,6 @@ void LLPanelLogin::loadLoginPage()
 	curl_free(curl_grid);
 	gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid());
 	gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor());
-
-
-#if USE_VIEWER_AUTH
-	LLURLSimString::sInstance.parse();
-
-	std::string location;
-	std::string region;
-	std::string password;
-	
-	if (LLURLSimString::parse())
-	{
-		std::ostringstream oRegionStr;
-		location = "specify";
-		oRegionStr << LLURLSimString::sInstance.mSimName << "/" << LLURLSimString::sInstance.mX << "/"
-			 << LLURLSimString::sInstance.mY << "/"
-			 << LLURLSimString::sInstance.mZ;
-		region = oRegionStr.str();
-	}
-	else
-	{
-		location = gSavedSettings.getString("LoginLocation");
-	}
-	
-	std::string username;
-
-    if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3)
-    {
-        LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo");
-		username = cmd_line_login[0].asString() + " " + cmd_line_login[1];
-        password = cmd_line_login[2].asString();
-    }
-    	
-	
-	char* curl_region = curl_escape(region.c_str(), 0);
-
-	oStr <<"username=" << username <<
-		 "&location=" << location <<	"&region=" << curl_region;
-	
-	curl_free(curl_region);
-
-	if (!password.empty())
-	{
-		oStr << "&password=" << password;
-	}
-	else if (!(password = load_password_from_disk()).empty())
-	{
-		oStr << "&password=$1$" << password;
-	}
-	if (gAutoLogin)
-	{
-		oStr << "&auto_login=TRUE";
-	}
-	if (gSavedSettings.getBOOL("ShowStartLocation"))
-	{
-		oStr << "&show_start_location=TRUE";
-	}	
-	if (gSavedSettings.getBOOL("RememberPassword"))
-	{
-		oStr << "&remember_password=TRUE";
-	}	
-#ifndef	LL_RELEASE_FOR_DOWNLOAD
-	oStr << "&show_grid=TRUE";
-#else
-	if (gSavedSettings.getBOOL("ForceShowGrid"))
-		oStr << "&show_grid=TRUE";
-#endif
-#endif
 	
 	LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html");
 
diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp
index 17433a557b91d805bf4bd601c67b5dfef2b13ce0..c83176d9800083d4640bae6668eb7fbcfea50824 100644
--- a/indra/newview/llpanelmaininventory.cpp
+++ b/indra/newview/llpanelmaininventory.cpp
@@ -506,9 +506,6 @@ void LLPanelMainInventory::onFilterSelected()
 		return;
 	}
 
-	BOOL recent_active = ("Recent Items" == mActivePanel->getName());
-	getChildView("add_btn_panel")->setVisible( !recent_active);
-
 	setFilterSubString(mFilterSubString);
 	LLInventoryFilter* filter = mActivePanel->getFilter();
 	LLFloaterInventoryFinder *finder = getFinder();
@@ -944,6 +941,11 @@ void LLPanelMainInventory::updateListCommands()
 
 void LLPanelMainInventory::onAddButtonClick()
 {
+// Gray out the "New Folder" option when the Recent tab is active as new folders will not be displayed
+// unless "Always show folders" is checked in the filter options.
+	bool recent_active = ("Recent Items" == mActivePanel->getName());
+	mMenuAdd->getChild<LLMenuItemGL>("New Folder")->setEnabled(!recent_active);
+
 	setUploadCostIfNeeded();
 
 	showActionMenu(mMenuAdd,"add_btn");
diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp
index 1869e92c8ce8528e861ddc636bb53b3efef005e1..00ac34efa5ce70cb9a6ba863fec87a190bbd3b35 100644
--- a/indra/newview/llpanelplaces.cpp
+++ b/indra/newview/llpanelplaces.cpp
@@ -753,6 +753,11 @@ void LLPanelPlaces::onOverflowButtonClicked()
 		// there is no landmark already pointing to that parcel in agent's inventory.
 		menu->getChild<LLMenuItemCallGL>("landmark")->setEnabled(is_agent_place_info_visible &&
 																 !LLLandmarkActions::landmarkAlreadyExists());
+		// STORM-411
+		// Creating landmarks for remote locations is impossible.
+		// So hide menu item "Make a Landmark" in "Teleport History Profile" panel.
+		menu->setItemVisible("landmark", mPlaceInfoType != TELEPORT_HISTORY_INFO_TYPE);
+		menu->arrangeAndClear();
 	}
 	else if (mPlaceInfoType == LANDMARK_INFO_TYPE && mLandmarkMenu != NULL)
 	{
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index 614700fb0a6661bc98d889afd1cf18a6e7a2e533..8ae3553857640f2461a665f6a341c3d340c50cfe 100644
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -59,6 +59,7 @@
 #include "llvovolume.h"
 #include "llweb.h"
 #include "llwindow.h"
+#include "llwindowshade.h"
 #include "llfloatertools.h"  // to enable hide if build tools are up
 
 // Functions pulled from pipeline.cpp
@@ -90,7 +91,8 @@ LLPanelPrimMediaControls::LLPanelPrimMediaControls() :
 	mTargetObjectNormal(LLVector3::zero),
 	mZoomObjectID(LLUUID::null),
 	mZoomObjectFace(0),
-	mVolumeSliderVisible(0)
+	mVolumeSliderVisible(0),
+	mWindowShade(NULL)
 {
 	mCommitCallbackRegistrar.add("MediaCtrl.Close",		boost::bind(&LLPanelPrimMediaControls::onClickClose, this));
 	mCommitCallbackRegistrar.add("MediaCtrl.Back",		boost::bind(&LLPanelPrimMediaControls::onClickBack, this));
@@ -205,6 +207,9 @@ BOOL LLPanelPrimMediaControls::postBuild()
 		
 	mMediaAddress->setFocusReceivedCallback(boost::bind(&LLPanelPrimMediaControls::onInputURL, _1, this ));
 	
+	LLWindowShade::Params window_shade_params;
+	window_shade_params.name = "window_shade";
+
 	mCurrentZoom = ZOOM_NONE;
 	// clicks on buttons do not remove keyboard focus from media
 	setIsChrome(TRUE);
@@ -698,6 +703,24 @@ void LLPanelPrimMediaControls::updateShape()
 /*virtual*/
 void LLPanelPrimMediaControls::draw()
 {
+	LLViewerMediaImpl* impl = getTargetMediaImpl();
+	if (impl)
+	{
+		LLNotificationPtr notification = impl->getCurrentNotification();
+		if (notification != mActiveNotification)
+		{
+			mActiveNotification = notification;
+			if (notification)
+			{
+				showNotification(notification);
+			}
+			else
+			{
+				hideNotification();
+			}
+		}
+	}
+
 	F32 alpha = getDrawContext().mAlpha;
 	if(mFadeTimer.getStarted())
 	{
@@ -1295,3 +1318,38 @@ bool LLPanelPrimMediaControls::shouldVolumeSliderBeVisible()
 {
 	return mVolumeSliderVisible > 0;
 }
+
+void LLPanelPrimMediaControls::showNotification(LLNotificationPtr notify)
+{
+	delete mWindowShade;
+	LLWindowShade::Params params;
+	params.rect = mMediaRegion->getLocalRect();
+	params.follows.flags = FOLLOWS_ALL;
+	params.notification = notify;
+
+	//HACK: don't hardcode this
+	if (notify->getIcon() == "Popup_Caution")
+	{
+		params.bg_image.name = "Yellow_Gradient";
+		params.text_color = LLColor4::black;
+	}
+	else
+	{
+		//HACK: make this a property of the notification itself, "cancellable"
+		params.can_close = false;
+		params.text_color.control = "LabelTextColor";
+	}
+
+	mWindowShade = LLUICtrlFactory::create<LLWindowShade>(params);
+
+	mMediaRegion->addChild(mWindowShade);
+	mWindowShade->show();
+}
+
+void LLPanelPrimMediaControls::hideNotification()
+{
+	if (mWindowShade)
+	{
+		mWindowShade->hide();
+	}
+}
diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h
index 3ec24f0e24a8a2d04420f1f75ff812b1693c1d87..0b9664359c6ff7728d2542f1de1901fb1587f1c6 100644
--- a/indra/newview/llpanelprimmediacontrols.h
+++ b/indra/newview/llpanelprimmediacontrols.h
@@ -29,6 +29,7 @@
 
 #include "llpanel.h"
 #include "llviewermedia.h"
+#include "llnotificationptr.h"
 
 class LLButton;
 class LLCoordWindow;
@@ -37,6 +38,7 @@ class LLLayoutStack;
 class LLProgressBar;
 class LLSliderCtrl;
 class LLViewerMediaImpl;
+class LLWindowShade;
 
 class LLPanelPrimMediaControls : public LLPanel
 {
@@ -54,6 +56,9 @@ class LLPanelPrimMediaControls : public LLPanel
 	void updateShape();
 	bool isMouseOver();
 	
+	void showNotification(LLNotificationPtr notify);
+	void hideNotification();
+
 	enum EZoomLevel
 	{
 		ZOOM_NONE = 0,
@@ -162,6 +167,7 @@ class LLPanelPrimMediaControls : public LLPanel
 	LLUICtrl *mRightBookend;
 	LLUIImage* mBackgroundImage;
 	LLUIImage* mVolumeSliderBackgroundImage;
+	LLWindowShade* mWindowShade;
 	F32 mSkipStep;
 	S32 mMinWidth;
 	S32 mMinHeight;
@@ -204,6 +210,8 @@ class LLPanelPrimMediaControls : public LLPanel
 	S32 mZoomObjectFace;
 	
 	S32 mVolumeSliderVisible;
+
+	LLNotificationPtr mActiveNotification;
 };
 
 #endif // LL_PANELPRIMMEDIACONTROLS_H
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 6038ab20d85a91ad7ee78b099074f9042003077a..b035d7d4733c007ea7c0088367afbf730c3f0513 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -114,11 +114,109 @@ class LLAgentHandler : public LLCommandHandler
 LLAgentHandler gAgentHandler;
 
 
+//-- LLPanelProfile::ChildStack begins ----------------------------------------
+LLPanelProfile::ChildStack::ChildStack()
+:	mParent(NULL)
+{
+}
+
+void LLPanelProfile::ChildStack::setParent(LLPanel* parent)
+{
+	llassert_always(parent != NULL);
+	mParent = parent;
+}
+
+/// Save current parent's child views and remove them from the child list.
+bool LLPanelProfile::ChildStack::push()
+{
+	view_list_t vlist = *mParent->getChildList();
+
+	for (view_list_t::const_iterator it = vlist.begin(); it != vlist.end(); ++it)
+	{
+		LLView* viewp = *it;
+		mParent->removeChild(viewp);
+	}
+
+	mStack.push_back(vlist);
+	dump();
+	return true;
+}
+
+/// Restore saved children (adding them back to the child list).
+bool LLPanelProfile::ChildStack::pop()
+{
+	if (mStack.size() == 0)
+	{
+		llwarns << "Empty stack" << llendl;
+		llassert(mStack.size() == 0);
+		return false;
+	}
+
+	view_list_t& top = mStack.back();
+	for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it)
+	{
+		LLView* viewp = *it;
+		mParent->addChild(viewp);
+	}
+
+	mStack.pop_back();
+	dump();
+	return true;
+}
+
+/// Temporarily add all saved children back.
+void LLPanelProfile::ChildStack::preParentReshape()
+{
+	mSavedStack = mStack;
+	while(mStack.size() > 0)
+	{
+		pop();
+	}
+}
+
+/// Add the temporarily saved children back.
+void LLPanelProfile::ChildStack::postParentReshape()
+{
+	mStack = mSavedStack;
+	mSavedStack = stack_t();
+
+	for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it)
+	{
+		const view_list_t& vlist = (*stack_it);
+		for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it)
+		{
+			LLView* viewp = *list_it;
+			lldebugs << "removing " << viewp->getName() << llendl;
+			mParent->removeChild(viewp);
+		}
+	}
+}
+
+void LLPanelProfile::ChildStack::dump()
+{
+	unsigned lvl = 0;
+	lldebugs << "child stack dump:" << llendl;
+	for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it, ++lvl)
+	{
+		std::ostringstream dbg_line;
+		dbg_line << "lvl #" << lvl << ":";
+		const view_list_t& vlist = (*stack_it);
+		for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it)
+		{
+			dbg_line << " " << (*list_it)->getName();
+		}
+		lldebugs << dbg_line.str() << llendl;
+	}
+}
+
+//-- LLPanelProfile::ChildStack ends ------------------------------------------
+
 LLPanelProfile::LLPanelProfile()
  : LLPanel()
  , mTabCtrl(NULL)
  , mAvatarId(LLUUID::null)
 {
+	mChildStack.setParent(this);
 }
 
 BOOL LLPanelProfile::postBuild()
@@ -136,6 +234,15 @@ BOOL LLPanelProfile::postBuild()
 	return TRUE;
 }
 
+// virtual
+void LLPanelProfile::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+	// Temporarily add saved children back and reshape them.
+	mChildStack.preParentReshape();
+	LLPanel::reshape(width, height, called_from_parent);
+	mChildStack.postParentReshape();
+}
+
 void LLPanelProfile::onOpen(const LLSD& key)
 {
 	// open the desired panel
@@ -177,7 +284,6 @@ void LLPanelProfile::onOpen(const LLSD& key)
 	}
 }
 
-//*TODO redo panel toggling
 void LLPanelProfile::togglePanel(LLPanel* panel, const LLSD& key)
 {
 	// TRUE - we need to open/expand "panel"
@@ -204,21 +310,10 @@ void LLPanelProfile::onTabSelected(const LLSD& param)
 	}
 }
 
-void LLPanelProfile::setAllChildrenVisible(BOOL visible)
-{
-	const child_list_t* child_list = getChildList();
-	child_list_const_iter_t child_it = child_list->begin();
-	for (; child_it != child_list->end(); ++child_it)
-	{
-		LLView* viewp = *child_it;
-		viewp->setVisible(visible);
-	}
-}
-
 void LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params)
 {
 	// Hide currently visible panel (STORM-690).
-	setAllChildrenVisible(FALSE);
+	mChildStack.push();
 
 	// Add the panel or bring it to front.
 	if (panel->getParent() != this)
@@ -231,7 +326,7 @@ void LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params)
 	}
 
 	panel->setVisible(TRUE);
-
+	panel->setFocus(TRUE); // prevent losing focus by the floater
 	panel->onOpen(params);
 
 	LLRect new_rect = getRect();
@@ -249,15 +344,17 @@ void LLPanelProfile::closePanel(LLPanel* panel)
 		removeChild(panel);
 
 		// Make the underlying panel visible.
+		mChildStack.pop();
+
+		// Prevent losing focus by the floater
 		const child_list_t* child_list = getChildList();
 		if (child_list->size() > 0)
 		{
-			child_list->front()->setVisible(TRUE);
-			child_list->front()->setFocus(TRUE); // prevent losing focus by the floater
+			child_list->front()->setFocus(TRUE);
 		}
 		else
 		{
-			llwarns << "No underlying panel to make visible." << llendl;
+			llwarns << "No underlying panel to focus." << llendl;
 		}
 	}
 }
diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h
index d2bcee80760344a64b5bd5c0bb1c4a6fd7ad8fba..0a572e6f25a58ae9326549f6b24f75dc72987b9b 100644
--- a/indra/newview/llpanelprofile.h
+++ b/indra/newview/llpanelprofile.h
@@ -41,7 +41,7 @@ class LLPanelProfile : public LLPanel
 
 public:
 	/*virtual*/ BOOL postBuild();
-
+	/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
 	/*virtual*/ void onOpen(const LLSD& key);
 
 	virtual void togglePanel(LLPanel*, const LLSD& key = LLSD());
@@ -58,8 +58,6 @@ class LLPanelProfile : public LLPanel
 
 	virtual void onTabSelected(const LLSD& param);
 
-	virtual void setAllChildrenVisible(BOOL visible);
-
 	LLTabContainer* getTabCtrl() { return mTabCtrl; }
 
 	const LLUUID& getAvatarId() { return mAvatarId; }
@@ -72,8 +70,34 @@ class LLPanelProfile : public LLPanel
 
 private:
 
+	//-- ChildStack begins ----------------------------------------------------
+	class ChildStack
+	{
+		LOG_CLASS(LLPanelProfile::ChildStack);
+	public:
+		ChildStack();
+		void setParent(LLPanel* parent);
+
+		bool push();
+		bool pop();
+		void preParentReshape();
+		void postParentReshape();
+
+	private:
+		void dump();
+
+		typedef LLView::child_list_t view_list_t;
+		typedef std::list<view_list_t> stack_t;
+
+		stack_t		mStack;
+		stack_t		mSavedStack;
+		LLPanel*	mParent;
+	};
+	//-- ChildStack ends ------------------------------------------------------
+
 	LLTabContainer* mTabCtrl;	
 	profile_tabs_t mTabContainer;
+	ChildStack		mChildStack;
 	LLUUID mAvatarId;
 };
 
diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp
index a9ca7314ce878429928420591a7e78b09993a0eb..30949f8f02e15d10e45ca07de268db9b3f657808 100644
--- a/indra/newview/llpaneltopinfobar.cpp
+++ b/indra/newview/llpaneltopinfobar.cpp
@@ -38,6 +38,7 @@
 #include "llsidetray.h"
 #include "llslurl.h"
 #include "llstatusbar.h"
+#include "lltrans.h"
 #include "llviewercontrol.h"
 #include "llviewerinventory.h"
 #include "llviewermenu.h"
@@ -102,6 +103,13 @@ void LLPanelTopInfoBar::initParcelIcons()
 	mParcelIcon[SCRIPTS_ICON] = getChild<LLIconCtrl>("scripts_icon");
 	mParcelIcon[DAMAGE_ICON] = getChild<LLIconCtrl>("damage_icon");
 
+	mParcelIcon[VOICE_ICON]->setToolTip(LLTrans::getString("LocationCtrlVoiceTooltip"));
+	mParcelIcon[FLY_ICON]->setToolTip(LLTrans::getString("LocationCtrlFlyTooltip"));
+	mParcelIcon[PUSH_ICON]->setToolTip(LLTrans::getString("LocationCtrlPushTooltip"));
+	mParcelIcon[BUILD_ICON]->setToolTip(LLTrans::getString("LocationCtrlBuildTooltip"));
+	mParcelIcon[SCRIPTS_ICON]->setToolTip(LLTrans::getString("LocationCtrlScriptsTooltip"));
+	mParcelIcon[DAMAGE_ICON]->setToolTip(LLTrans::getString("LocationCtrlDamageTooltip"));
+
 	mParcelIcon[VOICE_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, VOICE_ICON));
 	mParcelIcon[FLY_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, FLY_ICON));
 	mParcelIcon[PUSH_ICON]->setMouseDownCallback(boost::bind(&LLPanelTopInfoBar::onParcelIconClick, this, PUSH_ICON));
@@ -129,6 +137,7 @@ BOOL LLPanelTopInfoBar::postBuild()
 {
 	mInfoBtn = getChild<LLButton>("place_info_btn");
 	mInfoBtn->setClickedCallback(boost::bind(&LLPanelTopInfoBar::onInfoButtonClicked, this));
+	mInfoBtn->setToolTip(LLTrans::getString("LocationCtrlInfoBtnTooltip"));
 
 	mParcelInfoText = getChild<LLTextBox>("parcel_info_text");
 	mDamageText = getChild<LLTextBox>("damage_text");
diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp
index 69542764d2f03607a0c47bf437693e7004eae374..a90f23d63707224732b5e1ac10ce2be9dd66360d 100644
--- a/indra/newview/llpreview.cpp
+++ b/indra/newview/llpreview.cpp
@@ -454,12 +454,13 @@ LLMultiPreview::LLMultiPreview()
 	{
 		// start with a rect in the top-left corner ; will get resized
 		LLRect rect;
-		rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 200, 200);
+		rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 200, 400);
 		setRect(rect);
 	}
 	setTitle(LLTrans::getString("MultiPreviewTitle"));
 	buildTabContainer();
 	setCanResize(TRUE);
+	mAutoResize = FALSE;
 }
 
 void LLMultiPreview::onOpen(const LLSD& key)
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index 330e809c539106477037af7dad2eb11da5e4e15f..d0ebf047e8a314de691a4f8e7f301ca372eab3c5 100644
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -2034,7 +2034,17 @@ bool LLLiveLSLEditor::writeToFile(const std::string& filename)
 
 std::string LLLiveLSLEditor::getTmpFileName()
 {
-	return std::string(LLFile::tmpdir()) + "sl_script_" + mObjectUUID.asString() + ".lsl";
+	// Take script inventory item id (within the object inventory)
+	// to consideration so that it's possible to edit multiple scripts
+	// in the same object inventory simultaneously (STORM-781).
+	std::string script_id = mObjectUUID.asString() + "_" + mItemUUID.asString();
+
+	// Use MD5 sum to make the file name shorter and not exceed maximum path length.
+	char script_id_hash_str[33];               /* Flawfinder: ignore */
+	LLMD5 script_id_hash((const U8 *)script_id.c_str());
+	script_id_hash.hex_digest(script_id_hash_str);
+
+	return std::string(LLFile::tmpdir()) + "sl_script_" + script_id_hash_str + ".lsl";
 }
 
 void LLLiveLSLEditor::uploadAssetViaCaps(const std::string& url,
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
index fd6b326ef16fa7df9bb17e1f6480585e145bca8d..7657cccd4ea3b5f6602d15d6e98f6fd9e8d28ed3 100644
--- a/indra/newview/llpreviewtexture.cpp
+++ b/indra/newview/llpreviewtexture.cpp
@@ -318,7 +318,7 @@ void LLPreviewTexture::reshape(S32 width, S32 height, BOOL called_from_parent)
 		}
 	}
 
-	mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() +  (client_height / 2), client_width, client_height);	
+	mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() +  (client_height / 2), client_width, client_height);
 
 }
 
@@ -400,7 +400,6 @@ void LLPreviewTexture::updateDimensions()
 	{
 		return;
 	}
-
 	
 	mUpdateDimensions = FALSE;
 
@@ -408,80 +407,12 @@ void LLPreviewTexture::updateDimensions()
 	getChild<LLUICtrl>("dimensions")->setTextArg("[HEIGHT]", llformat("%d", mImage->getFullHeight()));
 
 	
-	LLRect dim_rect(getChildView("dimensions")->getRect());
-
-	S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE;
-
-	// add space for dimensions and aspect ratio
-	S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD;
-
-	S32 screen_width = gFloaterView->getSnapRect().getWidth();
-	S32 screen_height = gFloaterView->getSnapRect().getHeight();
-
-	S32 max_image_width = screen_width - 2*horiz_pad;
-	S32 max_image_height = screen_height - (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD) 
-		- (PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height);
-
-	S32 client_width = llmin(max_image_width,mImage->getFullWidth());
-	S32 client_height = llmin(max_image_height,mImage->getFullHeight());
-
-	if (mAspectRatio > 0.f)
-	{
-		if(mAspectRatio > 1.f)
-		{
-			client_height = llceil((F32)client_width / mAspectRatio);
-			if(client_height > max_image_height)
-			{
-				client_height = max_image_height;
-				client_width = llceil((F32)client_height * mAspectRatio);
-			}
-		}
-		else//mAspectRatio < 1.f
-		{
-			client_width = llceil((F32)client_height * mAspectRatio);
-			if(client_width > max_image_width)
-			{
-				client_width = max_image_width;
-				client_height = llceil((F32)client_width / mAspectRatio);
-			}
-		}
-	}
-	else
-	{
-
-		if(client_height > max_image_height)
-		{
-			F32 ratio = (F32)max_image_height/client_height;
-			client_height = max_image_height;
-			client_width = llceil((F32)client_height * ratio);
-		}
-		
-		if(client_width > max_image_width)
-		{
-			F32 ratio = (F32)max_image_width/client_width;
-			client_width = max_image_width;
-			client_height = llceil((F32)client_width * ratio);
-		}
-	}
-
-	//now back to whole floater
-	S32 floater_width = llmax(getMinWidth(),client_width + 2*horiz_pad);
-	S32 floater_height = llmax(getMinHeight(),client_height + (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD)
-		+ (PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height));
-
 	//reshape floater
-	reshape( floater_width, floater_height );
-	gFloaterView->adjustToFitScreen(this, FALSE);
+	reshape(getRect().getWidth(), getRect().getHeight());
 
-	//setup image rect...
-	LLRect client_rect(horiz_pad, getRect().getHeight(), getRect().getWidth() - horiz_pad, 0);
-	client_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD);
-	client_rect.mBottom += PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height ;
-
-	mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() +  (client_height / 2), client_width, client_height);	
+	gFloaterView->adjustToFitScreen(this, FALSE);
 
-	// Hide the aspect ratio label if the window is too narrow
-	// Assumes the label should be to the right of the dimensions
+	LLRect dim_rect(getChildView("dimensions")->getRect());
 	LLRect aspect_label_rect(getChildView("aspect_ratio")->getRect());
 	getChildView("aspect_ratio")->setVisible( dim_rect.mRight < aspect_label_rect.mLeft);
 }
diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp
index db02d76139ec334841ae87c63e46b6a38f489812..31fde5d58aedfd401c40c46aa6069055ad420a11 100644
--- a/indra/newview/llprogressview.cpp
+++ b/indra/newview/llprogressview.cpp
@@ -133,13 +133,13 @@ void LLProgressView::setVisible(BOOL visible)
 		mFadeTimer.start();
 	}
 	// showing progress view
-	else if (!getVisible() && visible)
+	else if (visible && (!getVisible() || mFadeTimer.getStarted()))
 	{
 		setFocus(TRUE);
 		mFadeTimer.stop();
 		mProgressTimer.start();
 		LLPanel::setVisible(TRUE);
-	}
+	} 
 }
 
 
diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp
index 0dff0875533743b50fba8d48709bf4621be93362..e5ef51bdd18c6b54c1ac0e801fa3567f6f15ec87 100644
--- a/indra/newview/llremoteparcelrequest.cpp
+++ b/indra/newview/llremoteparcelrequest.cpp
@@ -140,22 +140,25 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v
 	typedef std::vector<observer_multimap_t::iterator> deadlist_t;
 	deadlist_t dead_iters;
 
-	observer_multimap_t::iterator oi;
-	observer_multimap_t::iterator start = observers.lower_bound(parcel_data.parcel_id);
+	observer_multimap_t::iterator oi = observers.lower_bound(parcel_data.parcel_id);
 	observer_multimap_t::iterator end = observers.upper_bound(parcel_data.parcel_id);
 
-	for (oi = start; oi != end; ++oi)
+	while (oi != end)
 	{
-		LLRemoteParcelInfoObserver * observer = oi->second.get();
+		// increment the loop iterator now since it may become invalid below
+		observer_multimap_t::iterator cur_oi = oi++;
+
+		LLRemoteParcelInfoObserver * observer = cur_oi->second.get();
 		if(observer)
 		{
+			// may invalidate cur_oi if the observer removes itself 
 			observer->processParcelInfo(parcel_data);
 		}
 		else
 		{
 			// the handle points to an expired observer, so don't keep it
 			// around anymore
-			dead_iters.push_back(oi);
+			dead_iters.push_back(cur_oi);
 		}
 	}
 
diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp
index 9e0e10d66f54ad0eaa59ca52b6d105f4fbecb26d..e3bc67a4147993880551d641918da4ee67d719eb 100644
--- a/indra/newview/llscreenchannel.cpp
+++ b/indra/newview/llscreenchannel.cpp
@@ -83,11 +83,10 @@ bool  LLScreenChannelBase::isHovering()
 	return mHoveredToast->isHovered();
 }
 
-bool LLScreenChannelBase::resetPositionAndSize(const LLSD& newvalue)
+void LLScreenChannelBase::resetPositionAndSize()
 {
 	LLRect rc = gViewerWindow->getWorldViewRectScaled();
 	updatePositionAndSize(rc, rc);
-	return true;
 }
 
 void LLScreenChannelBase::updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect)
@@ -99,10 +98,7 @@ void LLScreenChannelBase::updatePositionAndSize(LLRect old_world_rect, LLRect ne
 	if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE
 		&& LLSideTray::instanceCreated	())
 	{
-		LLSideTray*	side_bar = LLSideTray::getInstance();
-
-		if (side_bar->getVisible() && !side_bar->getCollapsed())
-			world_rect_padding += side_bar->getRect().getWidth();
+		world_rect_padding += LLSideTray::getInstance()->getVisibleWidth();
 	}
 
 
@@ -133,7 +129,7 @@ void LLScreenChannelBase::init(S32 channel_left, S32 channel_right)
 	if(LLSideTray::instanceCreated())
 	{
 		LLSideTray*	side_bar = LLSideTray::getInstance();
-		side_bar->getCollapseSignal().connect(boost::bind(&LLScreenChannelBase::resetPositionAndSize, this, _2));
+		side_bar->setVisibleWidthChangeCallback(boost::bind(&LLScreenChannelBase::resetPositionAndSize, this));
 	}
 
 	// top and bottom set by updateBottom()
@@ -214,10 +210,7 @@ void LLScreenChannel::updatePositionAndSize(LLRect old_world_rect, LLRect new_wo
 	if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE 
 		&& LLSideTray::instanceCreated	())
 	{
-		LLSideTray*	side_bar = LLSideTray::getInstance();
-
-		if (side_bar->getVisible() && !side_bar->getCollapsed())
-			world_rect_padding += side_bar->getRect().getWidth();
+		world_rect_padding += LLSideTray::getInstance()->getVisibleWidth();
 	}
 
 
@@ -495,7 +488,7 @@ void LLScreenChannel::modifyToastByNotificationID(LLUUID id, LLPanel* panel)
 //--------------------------------------------------------------------------
 void LLScreenChannel::redrawToasts()
 {
-	if(mToastList.size() == 0 || isHovering())
+	if(mToastList.size() == 0)
 		return;
 
 	switch(mToastAlignment)
@@ -595,20 +588,15 @@ void LLScreenChannel::showToastsBottom()
 		}
 	}
 
+	// Dismiss toasts we don't have space for (STORM-391).
 	if(it != mToastList.rend())
 	{
 		mHiddenToastsNum = 0;
 		for(; it != mToastList.rend(); it++)
 		{
-			(*it).toast->stopTimer();
-			(*it).toast->setVisible(FALSE);
-			mHiddenToastsNum++;
+			(*it).toast->hide();
 		}
 	}
-	else
-	{
-		closeOverflowToastPanel();
-	}
 }
 
 //--------------------------------------------------------------------------
@@ -731,7 +719,6 @@ void LLNotificationsUI::LLScreenChannel::startToastTimer(LLToast* toast)
 //--------------------------------------------------------------------------
 void LLScreenChannel::hideToastsFromScreen()
 {
-	closeOverflowToastPanel();
 	for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++)
 		(*it).toast->setVisible(FALSE);
 }
@@ -847,8 +834,7 @@ void LLScreenChannel::onToastHover(LLToast* toast, bool mouse_enter)
 		}
 	}
 
-	if(!isHovering())
-		redrawToasts();
+	redrawToasts();
 }
 
 //--------------------------------------------------------------------------
diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h
index 023a65d8727b3caff599bb5ee337c88358bcac4b..d207d139819cc63242e3c490f12bac8b6a2c0097 100644
--- a/indra/newview/llscreenchannel.h
+++ b/indra/newview/llscreenchannel.h
@@ -59,8 +59,8 @@ class LLScreenChannelBase : public LLUICtrl
 	// Channel's outfit-functions
 	// update channel's size and position in the World View
 	virtual void		updatePositionAndSize(LLRect old_world_rect, LLRect new_world_rect);
+	void				resetPositionAndSize();
 
-	bool resetPositionAndSize(const LLSD& newvalue);
 	// initialization of channel's shape and position
 	virtual void		init(S32 channel_left, S32 channel_right);
 
@@ -81,9 +81,6 @@ class LLScreenChannelBase : public LLUICtrl
 	// show all toasts in a channel
 	virtual void		redrawToasts() {};
 
-	virtual void 		closeOverflowToastPanel() {};
-	virtual void 		hideOverflowToastPanel() {};
-
 	
 	// Channel's behavior-functions
 	// set whether a channel will control hovering inside itself or not
diff --git a/indra/newview/llscriptfloater.h b/indra/newview/llscriptfloater.h
index dc52baa11528a0c4d7b8ad6dd11e5c4fc5c766b2..8e959a3d0e22eb12c53d649e7d491252e7699a03 100644
--- a/indra/newview/llscriptfloater.h
+++ b/indra/newview/llscriptfloater.h
@@ -30,7 +30,7 @@
 #include "lltransientdockablefloater.h"
 #include "llnotificationptr.h"
 
-class LLToastNotifyPanel;
+class LLToastPanel;
 
 /**
  * Handles script notifications ("ScriptDialog" and "ScriptDialogGroup")
@@ -206,7 +206,7 @@ class LLScriptFloater : public LLDockableFloater
 private:
 	bool isScriptTextbox(LLNotificationPtr notification);
 
-	LLToastNotifyPanel* mScriptForm;
+	LLToastPanel* mScriptForm;
 	LLUUID mNotificationId;
 	LLUUID mObjectId;
 	bool mSaveFloaterPosition;
diff --git a/indra/newview/llscrollingpanelparam.cpp b/indra/newview/llscrollingpanelparam.cpp
index 05b273cd29c4f14b45bb731f6824442f075e8b1d..f8c20dada07429cef4a4b1e5689558b31efd2442 100644
--- a/indra/newview/llscrollingpanelparam.cpp
+++ b/indra/newview/llscrollingpanelparam.cpp
@@ -165,12 +165,16 @@ void LLScrollingPanelParam::draw()
 	getChildView("max param text")->setVisible( FALSE );
 	LLPanel::draw();
 
+	// If we're in a focused floater, don't apply the floater's alpha to visual param hint,
+	// making its behavior similar to texture controls'.
+	F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency();
+
 	// Draw the hints over the "less" and "more" buttons.
 	gGL.pushUIMatrix();
 	{
 		const LLRect& r = mHintMin->getRect();
 		gGL.translateUI((F32)r.mLeft, (F32)r.mBottom, 0.f);
-		mHintMin->draw();
+		mHintMin->draw(alpha);
 	}
 	gGL.popUIMatrix();
 
@@ -178,7 +182,7 @@ void LLScrollingPanelParam::draw()
 	{
 		const LLRect& r = mHintMax->getRect();
 		gGL.translateUI((F32)r.mLeft, (F32)r.mBottom, 0.f);
-		mHintMax->draw();
+		mHintMax->draw(alpha);
 	}
 	gGL.popUIMatrix();
 
diff --git a/indra/newview/llsearchcombobox.cpp b/indra/newview/llsearchcombobox.cpp
index db531b56959ce44d61584c96fa9b2c0b30c8e51e..6558c9a7fac6955672f3cc86f85232e4e79ca54c 100644
--- a/indra/newview/llsearchcombobox.cpp
+++ b/indra/newview/llsearchcombobox.cpp
@@ -131,6 +131,9 @@ void LLSearchComboBox::focusTextEntry()
 	if (mTextEntry)
 	{
 		gFocusMgr.setKeyboardFocus(mTextEntry);
+
+		// Let the editor handle editing hotkeys (STORM-431).
+		LLEditMenuHandler::gEditMenuHandler = mTextEntry;
 	}
 }
 
diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp
index be797ea937defcb084cf596abd66b6d21797f707..c8c6858b81d2f829a4a996fa8b1f4b19a3174350 100644
--- a/indra/newview/llsidepaneliteminfo.cpp
+++ b/indra/newview/llsidepaneliteminfo.cpp
@@ -71,12 +71,12 @@ void LLItemPropertiesObserver::changed(U32 mask)
 	const std::set<LLUUID>& mChangedItemIDs = gInventory.getChangedIDs();
 	std::set<LLUUID>::const_iterator it;
 
-	const LLUUID& object_id = mFloater->getObjectID();
+	const LLUUID& item_id = mFloater->getItemID();
 
 	for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++)
 	{
 		// set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288)
-		if (*it == object_id)
+		if (*it == item_id)
 		{
 			// if there's a change we're interested in.
 			if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
@@ -196,6 +196,11 @@ const LLUUID& LLSidepanelItemInfo::getObjectID() const
 	return mObjectID;
 }
 
+const LLUUID& LLSidepanelItemInfo::getItemID() const
+{
+	return mItemID;
+}
+
 void LLSidepanelItemInfo::reset()
 {
 	LLSidepanelInventorySubpanel::reset();
diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h
index 6416e2cfe40342b9a1922cd01e8c465d58139d73..25be145f64dd1e8154519830d048a1a6f726a714 100644
--- a/indra/newview/llsidepaneliteminfo.h
+++ b/indra/newview/llsidepaneliteminfo.h
@@ -55,6 +55,7 @@ class LLSidepanelItemInfo : public LLSidepanelInventorySubpanel
 	void setEditMode(BOOL edit);
 
 	const LLUUID& getObjectID() const;
+	const LLUUID& getItemID() const;
 
 protected:
 	/*virtual*/ void refresh();
diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp
index 3bc3959e0bf039620af35190f875943818ad87c7..aef665a35cd4db8079689d71296109b8f8dc4c40 100644
--- a/indra/newview/llsidetray.cpp
+++ b/indra/newview/llsidetray.cpp
@@ -561,7 +561,7 @@ BOOL LLSideTray::postBuild()
 	{
 		if ((*it).channel)
 		{
-			getCollapseSignal().connect(boost::bind(&LLScreenChannelBase::resetPositionAndSize, (*it).channel, _2));
+			setVisibleWidthChangeCallback(boost::bind(&LLScreenChannelBase::resetPositionAndSize, (*it).channel));
 		}
 	}
 
@@ -980,9 +980,6 @@ void LLSideTray::reflectCollapseChange()
 	}
 
 	gFloaterView->refresh();
-	
-	LLSD new_value = mCollapsed;
-	mCollapseSignal(this,new_value);
 }
 
 void LLSideTray::arrange()
@@ -1262,9 +1259,29 @@ bool		LLSideTray::isPanelActive(const std::string& panel_name)
 void	LLSideTray::updateSidetrayVisibility()
 {
 	// set visibility of parent container based on collapsed state
-	if (getParent())
+	LLView* parent = getParent();
+	if (parent)
 	{
-		getParent()->setVisible(!mCollapsed && !gAgentCamera.cameraMouselook());
+		bool old_visibility = parent->getVisible();
+		bool new_visibility = !mCollapsed && !gAgentCamera.cameraMouselook();
+
+		if (old_visibility != new_visibility)
+		{
+			parent->setVisible(new_visibility);
+
+			// Signal change of visible width.
+			llinfos << "Visible: " << new_visibility << llendl;
+			mVisibleWidthChangeSignal(this, new_visibility);
+		}
 	}
 }
 
+S32 LLSideTray::getVisibleWidth()
+{
+	return (isInVisibleChain() && !mCollapsed) ? getRect().getWidth() : 0;
+}
+
+void LLSideTray::setVisibleWidthChangeCallback(const commit_signal_t::slot_type& cb)
+{
+	mVisibleWidthChangeSignal.connect(cb);
+}
diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h
index 3c572dde9545a7e64ce088175b590a3954543dd5..184d78845f8a7fd7e88d26e00fe416394d87121f 100644
--- a/indra/newview/llsidetray.h
+++ b/indra/newview/llsidetray.h
@@ -165,9 +165,18 @@ class LLSideTray : public LLPanel, private LLDestroyClass<LLSideTray>
 	void		reshape			(S32 width, S32 height, BOOL called_from_parent = TRUE);
 
 
-	void		updateSidetrayVisibility();
+	/**
+	 * @return side tray width if it's visible and expanded, 0 otherwise.
+	 *
+	 * Not that width of the tab buttons is not included.
+	 *
+	 * @see setVisibleWidthChangeCallback()
+	 */
+	S32			getVisibleWidth();
+
+	void		setVisibleWidthChangeCallback(const commit_signal_t::slot_type& cb);
 
-	commit_signal_t& getCollapseSignal() { return mCollapseSignal; }
+	void		updateSidetrayVisibility();
 
 	void		handleLoginComplete();
 
@@ -216,7 +225,7 @@ class LLSideTray : public LLPanel, private LLDestroyClass<LLSideTray>
 	tab_order_vector_t				mOriginalTabOrder;
 	LLSideTrayTab*					mActiveTab;	
 	
-	commit_signal_t					mCollapseSignal;
+	commit_signal_t					mVisibleWidthChangeSignal;
 
 	LLButton*						mCollapseButton;
 	bool							mCollapsed;
diff --git a/indra/newview/llsimplestat.h b/indra/newview/llsimplestat.h
new file mode 100644
index 0000000000000000000000000000000000000000..a90e503adba7a429dd484ddf5eb13bce23e17d72
--- /dev/null
+++ b/indra/newview/llsimplestat.h
@@ -0,0 +1,158 @@
+/** 
+ * @file llsimplestat.h
+ * @brief Runtime statistics accumulation.
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_SIMPLESTAT_H
+#define LL_SIMPLESTAT_H
+
+// History
+//
+// The original source for this code is the server repositories'
+// llcommon/llstat.h file.  This particular code was added after the
+// viewer/server code schism but before the effort to convert common
+// code to libraries was complete.  Rather than add to merge issues,
+// the needed code was cut'n'pasted into this new header as it isn't
+// too awful a burden.  Post-modularization, we can look at removing
+// this redundancy.
+
+
+/**
+ * @class LLSimpleStatCounter
+ * @brief Just counts events.
+ *
+ * Really not needed but have a pattern in mind in the future.
+ * Interface limits what can be done at that's just fine.
+ *
+ * *TODO:  Update/transfer unit tests
+ * Unit tests:  indra/test/llcommon_llstat_tut.cpp
+ */
+class LLSimpleStatCounter
+{
+public:
+	inline LLSimpleStatCounter()		{ reset(); }
+	// Default destructor and assignment operator are valid
+
+	inline void reset()					{ mCount = 0; }
+
+	inline void merge(const LLSimpleStatCounter & src)
+										{ mCount += src.mCount; }
+	
+	inline U32 operator++()				{ return ++mCount; }
+
+	inline U32 getCount() const			{ return mCount; }
+		
+protected:
+	U32			mCount;
+};
+
+
+/**
+ * @class LLSimpleStatMMM
+ * @brief Templated collector of min, max and mean data for stats.
+ *
+ * Fed a stream of data samples, keeps a running account of the
+ * min, max and mean seen since construction or the last reset()
+ * call.  A freshly-constructed or reset instance returns counts
+ * and values of zero.
+ *
+ * Overflows and underflows (integer, inf or -inf) and NaN's
+ * are the caller's problem.  As is loss of precision when
+ * the running sum's exponent (when parameterized by a floating
+ * point of some type) differs from a given data sample's.
+ *
+ * Unit tests:  indra/test/llcommon_llstat_tut.cpp
+ */
+template <typename VALUE_T = F32>
+class LLSimpleStatMMM
+{
+public:
+	typedef VALUE_T Value;
+	
+public:
+	LLSimpleStatMMM()				{ reset(); }
+	// Default destructor and assignment operator are valid
+
+	/**
+	 * Resets the object returning all counts and derived
+	 * values back to zero.
+	 */
+	void reset()
+		{
+			mCount = 0;
+			mMin = Value(0);
+			mMax = Value(0);
+			mTotal = Value(0);
+		}
+
+	void record(Value v)
+		{
+			if (mCount)
+			{
+				mMin = llmin(mMin, v);
+				mMax = llmax(mMax, v);
+			}
+			else
+			{
+				mMin = v;
+				mMax = v;
+			}
+			mTotal += v;
+			++mCount;
+		}
+
+	void merge(const LLSimpleStatMMM<VALUE_T> & src)
+		{
+			if (! mCount)
+			{
+				*this = src;
+			}
+			else if (src.mCount)
+			{
+				mMin = llmin(mMin, src.mMin);
+				mMax = llmax(mMax, src.mMax);
+				mCount += src.mCount;
+				mTotal += src.mTotal;
+			}
+		}
+	
+	inline U32 getCount() const		{ return mCount; }
+	inline Value getMin() const		{ return mMin; }
+	inline Value getMax() const		{ return mMax; }
+	inline Value getMean() const	{ return mCount ? mTotal / mCount : mTotal; }
+		
+protected:
+	U32			mCount;
+	Value		mMin;
+	Value		mMax;
+	Value		mTotal;
+};
+
+#endif // LL_SIMPLESTAT_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index d945af0776f450376769e1ff1740a2a3b18970ca..611f9de2e6db8dcdb24f2edf9fe9789c0bcd6684 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -3095,7 +3095,16 @@ bool process_login_success_response()
 	std::string map_server_url = response["map-server-url"];
 	if(!map_server_url.empty())
 	{
-		gSavedSettings.setString("MapServerURL", map_server_url); 
+		// We got an answer from the grid -> use that for map for the current session
+		gSavedSettings.setString("CurrentMapServerURL", map_server_url); 
+		LL_INFOS("LLStartup") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL;
+	}
+	else
+	{
+		// No answer from the grid -> use the default setting for current session 
+		map_server_url = gSavedSettings.getString("MapServerURL"); 
+		gSavedSettings.setString("CurrentMapServerURL", map_server_url); 
+		LL_INFOS("LLStartup") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL;
 	}
 	
 	// Default male and female avatars allowing the user to choose their avatar on first login.
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
index e9fc25404a56cce34049a97b6a918d95fbec8442..1b8be7a5b23805be39b9b9e530ecbadf52055fe1 100644
--- a/indra/newview/llstatusbar.cpp
+++ b/indra/newview/llstatusbar.cpp
@@ -115,6 +115,7 @@ LLStatusBar::LLStatusBar(const LLRect& rect)
 	mSGBandwidth(NULL),
 	mSGPacketLoss(NULL),
 	mBtnVolume(NULL),
+	mBoxBalance(NULL),
 	mBalance(0),
 	mHealth(100),
 	mSquareMetersCredit(0),
@@ -168,6 +169,9 @@ BOOL LLStatusBar::postBuild()
 	getChild<LLUICtrl>("buyL")->setCommitCallback(
 		boost::bind(&LLStatusBar::onClickBuyCurrency, this));
 
+	mBoxBalance = getChild<LLTextBox>("balance");
+	mBoxBalance->setClickedCallback( &LLStatusBar::onClickBalance, this );
+
 	mBtnVolume = getChild<LLButton>( "volume_btn" );
 	mBtnVolume->setClickedCallback( onClickVolume, this );
 	mBtnVolume->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterVolume, this));
@@ -304,6 +308,7 @@ void LLStatusBar::setVisibleForMouselook(bool visible)
 {
 	mTextTime->setVisible(visible);
 	getChild<LLUICtrl>("balance_bg")->setVisible(visible);
+	mBoxBalance->setVisible(visible);
 	mBtnVolume->setVisible(visible);
 	mMediaToggle->setVisible(visible);
 	mSGBandwidth->setVisible(visible);
@@ -330,16 +335,15 @@ void LLStatusBar::setBalance(S32 balance)
 
 	std::string money_str = LLResMgr::getInstance()->getMonetaryString( balance );
 
-	LLTextBox* balance_box = getChild<LLTextBox>("balance");
 	LLStringUtil::format_map_t string_args;
 	string_args["[AMT]"] = llformat("%s", money_str.c_str());
 	std::string label_str = getString("buycurrencylabel", string_args);
-	balance_box->setValue(label_str);
+	mBoxBalance->setValue(label_str);
 
 	// Resize the L$ balance background to be wide enough for your balance plus the buy button
 	{
 		const S32 HPAD = 24;
-		LLRect balance_rect = balance_box->getTextBoundingRect();
+		LLRect balance_rect = mBoxBalance->getTextBoundingRect();
 		LLRect buy_rect = getChildView("buyL")->getRect();
 		LLView* balance_bg_view = getChildView("balance_bg");
 		LLRect balance_bg_rect = balance_bg_view->getRect();
@@ -505,6 +509,14 @@ static void onClickVolume(void* data)
 	LLAppViewer::instance()->setMasterSystemAudioMute(!mute_audio);	
 }
 
+//static 
+void LLStatusBar::onClickBalance(void* )
+{
+	// Force a balance request message:
+	LLStatusBar::sendMoneyBalanceRequest();
+	// The refresh of the display (call to setBalance()) will be done by process_money_balance_reply()
+}
+
 //static 
 void LLStatusBar::onClickMediaToggle(void* data)
 {
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
index 2388aeb0c85e74c9f0b2fb25947a5a8e195e150c..4ea3183d1820a4c606bbadb45e78ff64923bdd66 100644
--- a/indra/newview/llstatusbar.h
+++ b/indra/newview/llstatusbar.h
@@ -94,6 +94,7 @@ class LLStatusBar
 	void onClickScreen(S32 x, S32 y);
 
 	static void onClickMediaToggle(void* data);
+	static void onClickBalance(void* data);
 
 private:
 	LLTextBox	*mTextTime;
@@ -102,6 +103,7 @@ class LLStatusBar
 	LLStatGraph *mSGPacketLoss;
 
 	LLButton	*mBtnVolume;
+	LLTextBox	*mBoxBalance;
 	LLButton	*mMediaToggle;
 	LLView*		mScriptOut;
 	LLFrameTimer	mClockUpdateTimer;
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 13fd51f473f9aa44f43adee8068fb8d1f94e7105..4f63abb152dc59faaca838d5938d28e056034f81 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -27,6 +27,7 @@
 #include "llviewerprecompiledheaders.h"
 
 #include <iostream>
+#include <map>
 
 #include "llstl.h"
 
@@ -49,6 +50,7 @@
 #include "llviewertexture.h"
 #include "llviewerregion.h"
 #include "llviewerstats.h"
+#include "llviewerassetstats.h"
 #include "llworld.h"
 
 //////////////////////////////////////////////////////////////////////////////
@@ -143,7 +145,7 @@ class LLTextureFetchWorker : public LLWorkerClass
 	/*virtual*/ bool deleteOK(); // called from update() (WORK THREAD)
 
 	~LLTextureFetchWorker();
-	void relese() { --mActiveCount; }
+	// void relese() { --mActiveCount; }
 
 	S32 callbackHttpGet(const LLChannelDescriptors& channels,
 						 const LLIOPipe::buffer_ptr_t& buffer,
@@ -161,9 +163,11 @@ class LLTextureFetchWorker : public LLWorkerClass
 		mGetReason = reason;
 	}
 
-	void setCanUseHTTP(bool can_use_http) {mCanUseHTTP = can_use_http;}
-	bool getCanUseHTTP()const {return mCanUseHTTP ;}
+	void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; }
+	bool getCanUseHTTP() const { return mCanUseHTTP; }
 
+	LLTextureFetch & getFetcher() { return *mFetcher; }
+	
 protected:
 	LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
 						 F32 priority, S32 discard, S32 size);
@@ -277,6 +281,8 @@ class LLTextureFetchWorker : public LLWorkerClass
 	S32 mLastPacket;
 	U16 mTotalPackets;
 	U8 mImageCodec;
+
+	LLViewerAssetStats::duration_t mMetricsStartTime;
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -344,6 +350,18 @@ class HTTPGetResponder : public LLCurl::Responder
 			}
 
 			mFetcher->removeFromHTTPQueue(mID, data_size);
+
+			if (worker->mMetricsStartTime)
+			{
+				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
+															  true,
+															  LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
+															  LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
+				worker->mMetricsStartTime = 0;
+			}
+			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 true,
+														 LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
 		}
 		else
 		{
@@ -366,6 +384,229 @@ class HTTPGetResponder : public LLCurl::Responder
 	bool mFollowRedir;
 };
 
+//////////////////////////////////////////////////////////////////////////////
+
+// Cross-thread messaging for asset metrics.
+
+/**
+ * @brief Base class for cross-thread requests made of the fetcher
+ *
+ * I believe the intent of the LLQueuedThread class was to
+ * have these operations derived from LLQueuedThread::QueuedRequest
+ * but the texture fetcher has elected to manage the queue
+ * in its own manner.  So these are free-standing objects which are
+ * managed in simple FIFO order on the mCommands queue of the
+ * LLTextureFetch object.
+ *
+ * What each represents is a simple command sent from an
+ * outside thread into the TextureFetch thread to be processed
+ * in order and in a timely fashion (though not an absolute
+ * higher priority than other operations of the thread).
+ * Each operation derives a new class from the base customizing
+ * members, constructors and the doWork() method to effect
+ * the command.
+ *
+ * The flow is one-directional.  There are two global instances
+ * of the LLViewerAssetStats collector, one for the main program's
+ * thread pointed to by gViewerAssetStatsMain and one for the
+ * TextureFetch thread pointed to by gViewerAssetStatsThread1.
+ * Common operations has each thread recording metrics events
+ * into the respective collector unconcerned with locking and
+ * the state of any other thread.  But when the agent moves into
+ * a different region or the metrics timer expires and a report
+ * needs to be sent back to the grid, messaging across threads
+ * is required to distribute data and perform global actions.
+ * In pseudo-UML, it looks like:
+ *
+ *                       Main                 Thread1
+ *                        .                      .
+ *                        .                      .
+ *                     +-----+                   .
+ *                     | AM  |                   .
+ *                     +--+--+                   .
+ *      +-------+         |                      .
+ *      | Main  |      +--+--+                   .
+ *      |       |      | SRE |---.               .
+ *      | Stats |      +-----+    \              .
+ *      |       |         |        \  (uuid)  +-----+
+ *      | Coll. |      +--+--+      `-------->| SR  |
+ *      +-------+      | MSC |                +--+--+
+ *         | ^         +-----+                   |
+ *         | |  (uuid)  / .                   +-----+ (uuid)
+ *         |  `--------'  .                   | MSC |---------.
+ *         |              .                   +-----+         |
+ *         |           +-----+                   .            v
+ *         |           | TE  |                   .        +-------+
+ *         |           +--+--+                   .        | Thd1  |
+ *         |              |                      .        |       |
+ *         |           +-----+                   .        | Stats |
+ *          `--------->| RSC |                   .        |       |
+ *                     +--+--+                   .        | Coll. |
+ *                        |                      .        +-------+
+ *                     +--+--+                   .            |
+ *                     | SME |---.               .            |
+ *                     +-----+    \              .            |
+ *                        .        \ (clone)  +-----+         |
+ *                        .         `-------->| SM  |         |
+ *                        .                   +--+--+         |
+ *                        .                      |            |
+ *                        .                   +-----+         |
+ *                        .                   | RSC |<--------'
+ *                        .                   +-----+
+ *                        .                      |
+ *                        .                   +-----+
+ *                        .                   | CP  |--> HTTP POST
+ *                        .                   +-----+
+ *                        .                      .
+ *                        .                      .
+ *
+ *
+ * Key:
+ *
+ * SRE - Set Region Enqueued.  Enqueue a 'Set Region' command in
+ *       the other thread providing the new UUID of the region.
+ *       TFReqSetRegion carries the data.
+ * SR  - Set Region.  New region UUID is sent to the thread-local
+ *       collector.
+ * SME - Send Metrics Enqueued.  Enqueue a 'Send Metrics' command
+ *       including an ownership transfer of a cloned LLViewerAssetStats.
+ *       TFReqSendMetrics carries the data.
+ * SM  - Send Metrics.  Global metrics reporting operation.  Takes
+ *       the cloned stats from the command, merges it with the
+ *       thread's local stats, converts to LLSD and sends it on
+ *       to the grid.
+ * AM  - Agent Moved.  Agent has completed some sort of move to a
+ *       new region.
+ * TE  - Timer Expired.  Metrics timer has expired (on the order
+ *       of 10 minutes).
+ * CP  - CURL Post
+ * MSC - Modify Stats Collector.  State change in the thread-local
+ *       collector.  Typically a region change which affects the
+ *       global pointers used to find the 'current stats'.
+ * RSC - Read Stats Collector.  Extract collector data cloning it
+ *       (i.e. deep copy) when necessary.
+ *
+ */
+class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
+{
+public:
+	// Default ctors and assignment operator are correct.
+
+	virtual ~TFRequest()
+		{}
+
+	// Patterned after QueuedRequest's method but expected behavior
+	// is different.  Always expected to complete on the first call
+	// and work dispatcher will assume the same and delete the
+	// request after invocation.
+	virtual bool doWork(LLTextureFetch * fetcher) = 0;
+};
+
+namespace 
+{
+
+/**
+ * @brief Implements a 'Set Region' cross-thread command.
+ *
+ * When an agent moves to a new region, subsequent metrics need
+ * to be binned into a new or existing stats collection in 1:1
+ * relationship with the region.  We communicate this region
+ * change across the threads involved in the communication with
+ * this message.
+ *
+ * Corresponds to LLTextureFetch::commandSetRegion()
+ */
+class TFReqSetRegion : public LLTextureFetch::TFRequest
+{
+public:
+	TFReqSetRegion(U64 region_handle)
+		: LLTextureFetch::TFRequest(),
+		  mRegionHandle(region_handle)
+		{}
+	TFReqSetRegion & operator=(const TFReqSetRegion &);	// Not defined
+
+	virtual ~TFReqSetRegion()
+		{}
+
+	virtual bool doWork(LLTextureFetch * fetcher);
+		
+public:
+	const U64 mRegionHandle;
+};
+
+
+/**
+ * @brief Implements a 'Send Metrics' cross-thread command.
+ *
+ * This is the big operation.  The main thread gathers metrics
+ * for a period of minutes into LLViewerAssetStats and other
+ * objects then makes a snapshot of the data by cloning the
+ * collector.  This command transfers the clone, along with a few
+ * additional arguments (UUIDs), handing ownership to the
+ * TextureFetch thread.  It then merges its own data into the
+ * cloned copy, converts to LLSD and kicks off an HTTP POST of
+ * the resulting data to the currently active metrics collector.
+ *
+ * Corresponds to LLTextureFetch::commandSendMetrics()
+ */
+class TFReqSendMetrics : public LLTextureFetch::TFRequest
+{
+public:
+    /**
+	 * Construct the 'Send Metrics' command to have the TextureFetch
+	 * thread add and log metrics data.
+	 *
+	 * @param	caps_url		URL of a "ViewerMetrics" Caps target
+	 *							to receive the data.  Does not have to
+	 *							be associated with a particular region.
+	 *
+	 * @param	session_id		UUID of the agent's session.
+	 *
+	 * @param	agent_id		UUID of the agent.  (Being pure here...)
+	 *
+	 * @param	main_stats		Pointer to a clone of the main thread's
+	 *							LLViewerAssetStats data.  Thread1 takes
+	 *							ownership of the copy and disposes of it
+	 *							when done.
+	 */
+	TFReqSendMetrics(const std::string & caps_url,
+					 const LLUUID & session_id,
+					 const LLUUID & agent_id,
+					 LLViewerAssetStats * main_stats)
+		: LLTextureFetch::TFRequest(),
+		  mCapsURL(caps_url),
+		  mSessionID(session_id),
+		  mAgentID(agent_id),
+		  mMainStats(main_stats)
+		{}
+	TFReqSendMetrics & operator=(const TFReqSendMetrics &);	// Not defined
+
+	virtual ~TFReqSendMetrics();
+
+	virtual bool doWork(LLTextureFetch * fetcher);
+		
+public:
+	const std::string mCapsURL;
+	const LLUUID mSessionID;
+	const LLUUID mAgentID;
+	LLViewerAssetStats * mMainStats;
+};
+
+/*
+ * Examines the merged viewer metrics report and if found to be too long,
+ * will attempt to truncate it in some reasonable fashion.
+ *
+ * @param		max_regions		Limit of regions allowed in report.
+ *
+ * @param		metrics			Full, merged viewer metrics report.
+ *
+ * @returns		If data was truncated, returns true.
+ */
+bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
+
+} // end of anonymous namespace
+
+
 //////////////////////////////////////////////////////////////////////////////
 
 //static
@@ -385,6 +626,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = {
 	"DONE",
 };
 
+// static
+volatile bool LLTextureFetch::svMetricsDataBreak(true);	// Start with a data break
+
 // called from MAIN THREAD
 
 LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
@@ -434,7 +678,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
 	  mFirstPacket(0),
 	  mLastPacket(-1),
 	  mTotalPackets(0),
-	  mImageCodec(IMG_CODEC_INVALID)
+	  mImageCodec(IMG_CODEC_INVALID),
+	  mMetricsStartTime(0)
 {
 	mCanUseNET = mUrl.empty() ;
 
@@ -602,6 +847,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			return true; // abort
 		}
 	}
+
 	if(mImagePriority < F_ALMOST_ZERO)
 	{
 		if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR)
@@ -811,7 +1057,15 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			mRequestedDiscard = mDesiredDiscard;
 			mSentRequest = QUEUED;
 			mFetcher->addToNetworkQueue(this);
+			if (! mMetricsStartTime)
+			{
+				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+			}
+			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 false,
+														 LLImageBase::TYPE_AVATAR_BAKE == mType);
 			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			
 			return false;
 		}
 		else
@@ -820,6 +1074,12 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			//llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
 			// Make certain this is in the network queue
 			//mFetcher->addToNetworkQueue(this);
+			//if (! mMetricsStartTime)
+			//{
+			//   mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+			//}
+			//LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
+			//                                             LLImageBase::TYPE_AVATAR_BAKE == mType);
 			//setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 			return false;
 		}
@@ -843,11 +1103,30 @@ bool LLTextureFetchWorker::doWork(S32 param)
 			}
 			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 			mState = DECODE_IMAGE;
-			mWriteToCacheState = SHOULD_WRITE ;
+			mWriteToCacheState = SHOULD_WRITE;
+
+			if (mMetricsStartTime)
+			{
+				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
+															  false,
+															  LLImageBase::TYPE_AVATAR_BAKE == mType,
+															  LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
+				mMetricsStartTime = 0;
+			}
+			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 false,
+														 LLImageBase::TYPE_AVATAR_BAKE == mType);
 		}
 		else
 		{
 			mFetcher->addToNetworkQueue(this); // failsafe
+			if (! mMetricsStartTime)
+			{
+				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+			}
+			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+														 false,
+														 LLImageBase::TYPE_AVATAR_BAKE == mType);
 			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 		}
 		return false;
@@ -909,6 +1188,14 @@ bool LLTextureFetchWorker::doWork(S32 param)
 				mState = WAIT_HTTP_REQ;	
 
 				mFetcher->addToHTTPQueue(mID);
+				if (! mMetricsStartTime)
+				{
+					mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+				}
+				LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
+															 true,
+															 LLImageBase::TYPE_AVATAR_BAKE == mType);
+
 				// Will call callbackHttpGet when curl request completes
 				std::vector<std::string> headers;
 				headers.push_back("Accept: image/x-j2c");
@@ -1523,7 +1810,7 @@ bool LLTextureFetchWorker::writeToCacheComplete()
 //////////////////////////////////////////////////////////////////////////////
 // public
 
-LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded)
+LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode)
 	: LLWorkerThread("TextureFetch", threaded),
 	  mDebugCount(0),
 	  mDebugPause(FALSE),
@@ -1535,8 +1822,10 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
 	  mImageDecodeThread(imagedecodethread),
 	  mTextureBandwidth(0),
 	  mHTTPTextureBits(0),
-	  mCurlGetRequest(NULL)
+	  mCurlGetRequest(NULL),
+	  mQAMode(qa_mode)
 {
+	mCurlPOSTRequestCount = 0;
 	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
 	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
 }
@@ -1545,6 +1834,13 @@ LLTextureFetch::~LLTextureFetch()
 {
 	clearDeleteList() ;
 
+	while (! mCommands.empty())
+	{
+		TFRequest * req(mCommands.front());
+		mCommands.erase(mCommands.begin());
+		delete req;
+	}
+	
 	// ~LLQueuedThread() called here
 }
 
@@ -1825,8 +2121,76 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
 	return res;
 }
 
+// Replicates and expands upon the base class's
+// getPending() implementation.  getPending() and
+// runCondition() replicate one another's logic to
+// an extent and are sometimes used for the same
+// function (deciding whether or not to sleep/pause
+// a thread).  So the implementations need to stay
+// in step, at least until this can be refactored and
+// the redundancy eliminated.
+//
+// May be called from any thread
+
+//virtual
+S32 LLTextureFetch::getPending()
+{
+	S32 res;
+	lockData();
+    {
+        LLMutexLock lock(&mQueueMutex);
+        
+        res = mRequestQueue.size();
+        res += mCurlPOSTRequestCount;
+        res += mCommands.size();
+    }
+	unlockData();
+	return res;
+}
+
+// virtual
+bool LLTextureFetch::runCondition()
+{
+	// Caller is holding the lock on LLThread's condition variable.
+	
+	// LLQueuedThread, unlike its base class LLThread, makes this a
+	// private method which is unfortunate.  I want to use it directly
+	// but I'm going to have to re-implement the logic here (or change
+	// declarations, which I don't want to do right now).
+	//
+	// Changes here may need to be reflected in getPending().
+	
+	bool have_no_commands(false);
+	{
+		LLMutexLock lock(&mQueueMutex);
+		
+		have_no_commands = mCommands.empty();
+	}
+	
+    bool have_no_curl_requests(0 == mCurlPOSTRequestCount);
+	
+	return ! (have_no_commands
+			  && have_no_curl_requests
+			  && (mRequestQueue.empty() && mIdleThread));		// From base class
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
+// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
+void LLTextureFetch::commonUpdate()
+{
+	// Run a cross-thread command, if any.
+	cmdDoWork();
+	
+	// Update Curl on same thread as mCurlGetRequest was constructed
+	S32 processed = mCurlGetRequest->process();
+	if (processed > 0)
+	{
+		lldebugs << "processed: " << processed << " messages." << llendl;
+	}
+}
+
+
 // MAIN THREAD
 //virtual
 S32 LLTextureFetch::update(U32 max_time_ms)
@@ -1852,12 +2216,7 @@ S32 LLTextureFetch::update(U32 max_time_ms)
 
 	if (!mThreaded)
 	{
-		// Update Curl on same thread as mCurlGetRequest was constructed
-		S32 processed = mCurlGetRequest->process();
-		if (processed > 0)
-		{
-			lldebugs << "processed: " << processed << " messages." << llendl;
-		}
+		commonUpdate();
 	}
 
 	return res;
@@ -1912,12 +2271,7 @@ void LLTextureFetch::threadedUpdate()
 	}
 	process_timer.reset();
 	
-	// Update Curl on same thread as mCurlGetRequest was constructed
-	S32 processed = mCurlGetRequest->process();
-	if (processed > 0)
-	{
-		lldebugs << "processed: " << processed << " messages." << llendl;
-	}
+	commonUpdate();
 
 #if 0
 	const F32 INFO_TIME = 1.0f; 
@@ -2367,3 +2721,280 @@ void LLTextureFetch::dump()
 	}
 }
 
+//////////////////////////////////////////////////////////////////////////////
+
+// cross-thread command methods
+
+void LLTextureFetch::commandSetRegion(U64 region_handle)
+{
+	TFReqSetRegion * req = new TFReqSetRegion(region_handle);
+
+	cmdEnqueue(req);
+}
+
+void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
+										const LLUUID & session_id,
+										const LLUUID & agent_id,
+										LLViewerAssetStats * main_stats)
+{
+	TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
+
+	cmdEnqueue(req);
+}
+
+void LLTextureFetch::commandDataBreak()
+{
+	// The pedantically correct way to implement this is to create a command
+	// request object in the above fashion and enqueue it.  However, this is
+	// simple data of an advisorial not operational nature and this case
+	// of shared-write access is tolerable.
+
+	LLTextureFetch::svMetricsDataBreak = true;
+}
+
+void LLTextureFetch::cmdEnqueue(TFRequest * req)
+{
+	lockQueue();
+	mCommands.push_back(req);
+	unlockQueue();
+
+	unpause();
+}
+
+LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
+{
+	TFRequest * ret = 0;
+	
+	lockQueue();
+	if (! mCommands.empty())
+	{
+		ret = mCommands.front();
+		mCommands.erase(mCommands.begin());
+	}
+	unlockQueue();
+
+	return ret;
+}
+
+void LLTextureFetch::cmdDoWork()
+{
+	if (mDebugPause)
+	{
+		return;  // debug: don't do any work
+	}
+
+	TFRequest * req = cmdDequeue();
+	if (req)
+	{
+		// One request per pass should really be enough for this.
+		req->doWork(this);
+		delete req;
+	}
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Private (anonymous) class methods implementing the command scheme.
+
+namespace
+{
+
+/**
+ * Implements the 'Set Region' command.
+ *
+ * Thread:  Thread1 (TextureFetch)
+ */
+bool
+TFReqSetRegion::doWork(LLTextureFetch *)
+{
+	LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
+
+	return true;
+}
+
+
+TFReqSendMetrics::~TFReqSendMetrics()
+{
+	delete mMainStats;
+	mMainStats = 0;
+}
+
+
+/**
+ * Implements the 'Send Metrics' command.  Takes over
+ * ownership of the passed LLViewerAssetStats pointer.
+ *
+ * Thread:  Thread1 (TextureFetch)
+ */
+bool
+TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
+{
+	/*
+	 * HTTP POST responder.  Doesn't do much but tries to
+	 * detect simple breaks in recording the metrics stream.
+	 *
+	 * The 'volatile' modifiers don't indicate signals,
+	 * mmap'd memory or threads, really.  They indicate that
+	 * the referenced data is part of a pseudo-closure for
+	 * this responder rather than being required for correct
+	 * operation.
+     *
+     * We don't try very hard with the POST request.  We give
+     * it one shot and that's more-or-less it.  With a proper
+     * refactoring of the LLQueuedThread usage, these POSTs
+     * could be put in a request object and made more reliable.
+	 */
+	class lcl_responder : public LLCurl::Responder
+	{
+	public:
+		lcl_responder(LLTextureFetch * fetcher,
+					  S32 expected_sequence,
+                      volatile const S32 & live_sequence,
+                      volatile bool & reporting_break,
+					  volatile bool & reporting_started)
+			: LLCurl::Responder(),
+			  mFetcher(fetcher),
+              mExpectedSequence(expected_sequence),
+              mLiveSequence(live_sequence),
+			  mReportingBreak(reporting_break),
+			  mReportingStarted(reporting_started)
+			{
+                mFetcher->incrCurlPOSTCount();
+            }
+        
+        ~lcl_responder()
+            {
+                mFetcher->decrCurlPOSTCount();
+            }
+
+		// virtual
+		void error(U32 status_num, const std::string & reason)
+			{
+                if (mLiveSequence == mExpectedSequence)
+                {
+                    mReportingBreak = true;
+                }
+				LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service.  Reason:  "
+									<< reason << LL_ENDL;
+			}
+
+		// virtual
+		void result(const LLSD & content)
+			{
+                if (mLiveSequence == mExpectedSequence)
+                {
+                    mReportingBreak = false;
+                    mReportingStarted = true;
+                }
+			}
+
+	private:
+		LLTextureFetch * mFetcher;
+        S32 mExpectedSequence;
+        volatile const S32 & mLiveSequence;
+		volatile bool & mReportingBreak;
+		volatile bool & mReportingStarted;
+
+	}; // class lcl_responder
+	
+	if (! gViewerAssetStatsThread1)
+		return true;
+
+	static volatile bool reporting_started(false);
+	static volatile S32 report_sequence(0);
+    
+	// We've taken over ownership of the stats copy at this
+	// point.  Get a working reference to it for merging here
+	// but leave it in 'this'.  Destructor will rid us of it.
+	LLViewerAssetStats & main_stats = *mMainStats;
+
+	// Merge existing stats into those from main, convert to LLSD
+	main_stats.merge(*gViewerAssetStatsThread1);
+	LLSD merged_llsd = main_stats.asLLSD(true);
+
+	// Add some additional meta fields to the content
+	merged_llsd["session_id"] = mSessionID;
+	merged_llsd["agent_id"] = mAgentID;
+	merged_llsd["message"] = "ViewerAssetMetrics";					// Identifies the type of metrics
+	merged_llsd["sequence"] = report_sequence;						// Sequence number
+	merged_llsd["initial"] = ! reporting_started;					// Initial data from viewer
+	merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak;		// Break in data prior to this report
+		
+	// Update sequence number
+	if (S32_MAX == ++report_sequence)
+		report_sequence = 0;
+
+	// Limit the size of the stats report if necessary.
+	merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
+
+	if (! mCapsURL.empty())
+	{
+		LLCurlRequest::headers_t headers;
+		fetcher->getCurlRequest().post(mCapsURL,
+									   headers,
+									   merged_llsd,
+									   new lcl_responder(fetcher,
+														 report_sequence,
+                                                         report_sequence,
+                                                         LLTextureFetch::svMetricsDataBreak,
+														 reporting_started));
+	}
+	else
+	{
+		LLTextureFetch::svMetricsDataBreak = true;
+	}
+
+	// In QA mode, Metrics submode, log the result for ease of testing
+	if (fetcher->isQAMode())
+	{
+		LL_INFOS("Textures") << merged_llsd << LL_ENDL;
+	}
+
+	gViewerAssetStatsThread1->reset();
+
+	return true;
+}
+
+
+bool
+truncate_viewer_metrics(int max_regions, LLSD & metrics)
+{
+	static const LLSD::String reg_tag("regions");
+	static const LLSD::String duration_tag("duration");
+	
+	LLSD & reg_map(metrics[reg_tag]);
+	if (reg_map.size() <= max_regions)
+	{
+		return false;
+	}
+
+	// Build map of region hashes ordered by duration
+	typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
+	reg_ordered_list_t regions_by_duration;
+
+	int ind(0);
+	LLSD::array_const_iterator it_end(reg_map.endArray());
+	for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
+	{
+		LLSD::Real duration = (*it)[duration_tag].asReal();
+		regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
+	}
+
+	// Build a replacement regions array with the longest-persistence regions
+	LLSD new_region(LLSD::emptyArray());
+	reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
+	reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
+	for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
+	{
+		new_region.append(reg_map[it2->second]);
+	}
+	reg_map = new_region;
+	
+	return true;
+}
+
+} // end of anonymous namespace
+
+
+
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 796109df067b87d0d0214ad9d4ead01bffe705b1..ad00a7ea36fdb6aec9d13e549e1f26b8bcb25f47 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -33,6 +33,7 @@
 #include "llworkerthread.h"
 #include "llcurl.h"
 #include "lltextureinfo.h"
+#include "llapr.h"
 
 class LLViewerTexture;
 class LLTextureFetchWorker;
@@ -40,6 +41,7 @@ class HTTPGetResponder;
 class LLTextureCache;
 class LLImageDecodeThread;
 class LLHost;
+class LLViewerAssetStats;
 
 // Interface class
 class LLTextureFetch : public LLWorkerThread
@@ -48,9 +50,11 @@ class LLTextureFetch : public LLWorkerThread
 	friend class HTTPGetResponder;
 	
 public:
-	LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded);
+	LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode);
 	~LLTextureFetch();
 
+	class TFRequest;
+	
 	/*virtual*/ S32 update(U32 max_time_ms);	
 	void shutDownTextureCacheThread() ; //called in the main thread after the TextureCacheThread shuts down.
 	void shutDownImageDecodeThread() ;  //called in the main thread after the ImageDecodeThread shuts down.
@@ -77,28 +81,77 @@ class LLTextureFetch : public LLWorkerThread
 	S32 getNumHTTPRequests() ;
 	
 	// Public for access by callbacks
+    S32 getPending();
 	void lockQueue() { mQueueMutex.lock(); }
 	void unlockQueue() { mQueueMutex.unlock(); }
 	LLTextureFetchWorker* getWorker(const LLUUID& id);
 	LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id);
 
 	LLTextureInfo* getTextureInfo() { return &mTextureInfo; }
-	
+
+	// Commands available to other threads to control metrics gathering operations.
+	void commandSetRegion(U64 region_handle);
+	void commandSendMetrics(const std::string & caps_url,
+							const LLUUID & session_id,
+							const LLUUID & agent_id,
+							LLViewerAssetStats * main_stats);
+	void commandDataBreak();
+
+	LLCurlRequest & getCurlRequest()	{ return *mCurlGetRequest; }
+
+	bool isQAMode() const				{ return mQAMode; }
+
+	// Curl POST counter maintenance
+	inline void incrCurlPOSTCount()		{ mCurlPOSTRequestCount++; }
+	inline void decrCurlPOSTCount()		{ mCurlPOSTRequestCount--; }
+
 protected:
 	void addToNetworkQueue(LLTextureFetchWorker* worker);
 	void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel);
 	void addToHTTPQueue(const LLUUID& id);
 	void removeFromHTTPQueue(const LLUUID& id, S32 received_size = 0);
 	void removeRequest(LLTextureFetchWorker* worker, bool cancel);
-	// Called from worker thread (during doWork)
-	void processCurlRequests();	
+
+	// Overrides from the LLThread tree
+	bool runCondition();
 
 private:
 	void sendRequestListToSimulators();
 	/*virtual*/ void startThread(void);
 	/*virtual*/ void endThread(void);
 	/*virtual*/ void threadedUpdate(void);
-
+	void commonUpdate();
+
+	// Metrics command helpers
+	/**
+	 * Enqueues a command request at the end of the command queue
+	 * and wakes up the thread as needed.
+	 *
+	 * Takes ownership of the TFRequest object.
+	 *
+	 * Method locks the command queue.
+	 */
+	void cmdEnqueue(TFRequest *);
+
+	/**
+	 * Returns the first TFRequest object in the command queue or
+	 * NULL if none is present.
+	 *
+	 * Caller acquires ownership of the object and must dispose of it.
+	 *
+	 * Method locks the command queue.
+	 */
+	TFRequest * cmdDequeue();
+
+	/**
+	 * Processes the first command in the queue disposing of the
+	 * request on completion.  Successive calls are needed to perform
+	 * additional commands.
+	 *
+	 * Method locks the command queue.
+	 */
+	void cmdDoWork();
+	
 public:
 	LLUUID mDebugID;
 	S32 mDebugCount;
@@ -107,7 +160,7 @@ class LLTextureFetch : public LLWorkerThread
 	S32 mBadPacketCount;
 	
 private:
-	LLMutex mQueueMutex;        //to protect mRequestMap only
+	LLMutex mQueueMutex;        //to protect mRequestMap and mCommands only
 	LLMutex mNetworkQueueMutex; //to protect mNetworkQueue, mHTTPTextureQueue and mCancelQueue.
 
 	LLTextureCache* mTextureCache;
@@ -129,6 +182,29 @@ class LLTextureFetch : public LLWorkerThread
 	LLTextureInfo mTextureInfo;
 
 	U32 mHTTPTextureBits;
+
+	// Out-of-band cross-thread command queue.  This command queue
+	// is logically tied to LLQueuedThread's list of
+	// QueuedRequest instances and so must be covered by the
+	// same locks.
+	typedef std::vector<TFRequest *> command_queue_t;
+	command_queue_t mCommands;
+
+	// If true, modifies some behaviors that help with QA tasks.
+	const bool mQAMode;
+
+	// Count of POST requests outstanding.  We maintain the count
+	// indirectly in the CURL request responder's ctor and dtor and
+	// use it when determining whether or not to sleep the thread.  Can't
+	// use the LLCurl module's request counter as it isn't thread compatible.
+	// *NOTE:  Don't mix Atomic and static, apr_initialize must be called first.
+	LLAtomic32<S32> mCurlPOSTRequestCount;
+	
+public:
+	// A probabilistically-correct indicator that the current
+	// 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;
 };
 
 #endif // LL_LLTEXTUREFETCH_H
diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp
index 563c27c4d739ddbf535a2f01bc2ced256e778a01..75178a6ef8a6fcaa8a560d795d90742be6084636 100644
--- a/indra/newview/lltoastgroupnotifypanel.cpp
+++ b/indra/newview/lltoastgroupnotifypanel.cpp
@@ -77,6 +77,7 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification
 	from << from_name << "/" << groupData.mName;
 	LLTextBox* pTitleText = getChild<LLTextBox>("title");
 	pTitleText->setValue(from.str());
+	pTitleText->setToolTip(from.str());
 
 	//message subject
 	const std::string& subject = payload["subject"].asString();
diff --git a/indra/newview/lltoastscripttextbox.cpp b/indra/newview/lltoastscripttextbox.cpp
index c013f521cc46289ee5d76bdb665a278f049b5fd1..2529ec865a9b937f6dd4c96a3c91bd7909d5cfca 100644
--- a/indra/newview/lltoastscripttextbox.cpp
+++ b/indra/newview/lltoastscripttextbox.cpp
@@ -46,11 +46,16 @@
 
 const S32 LLToastScriptTextbox::DEFAULT_MESSAGE_MAX_LINE_COUNT= 7;
 
-LLToastScriptTextbox::LLToastScriptTextbox(LLNotificationPtr& notification)
-:	LLToastNotifyPanel(notification)
+LLToastScriptTextbox::LLToastScriptTextbox(const LLNotificationPtr& notification)
+:	LLToastPanel(notification)
 {
 	buildFromFile( "panel_notify_textbox.xml");
 
+	LLTextEditor* text_editorp = getChild<LLTextEditor>("text_editor_box");
+	text_editorp->setValue(notification->getMessage());
+
+	getChild<LLButton>("ignore_btn")->setClickedCallback(boost::bind(&LLToastScriptTextbox::onClickIgnore, this));
+
 	const LLSD& payload = notification->getPayload();
 
 	//message body
@@ -107,3 +112,10 @@ void LLToastScriptTextbox::onClickSubmit()
 		llwarns << response << llendl;
 	}
 }
+
+void LLToastScriptTextbox::onClickIgnore()
+{
+	LLSD response = mNotification->getResponseTemplate();
+	mNotification->respond(response);
+	close();
+}
diff --git a/indra/newview/lltoastscripttextbox.h b/indra/newview/lltoastscripttextbox.h
index ae3b545e0a8b3e4ff7bb5e8b4117b02c6996044d..8e69d8834d8c6572050fdd75883de5594ed71612 100644
--- a/indra/newview/lltoastscripttextbox.h
+++ b/indra/newview/lltoastscripttextbox.h
@@ -30,13 +30,11 @@
 #include "lltoastnotifypanel.h"
 #include "llnotificationptr.h"
 
-class LLButton;
-
 /**
  * Toast panel for scripted llTextbox notifications.
  */
 class LLToastScriptTextbox
-:	public LLToastNotifyPanel
+:	public LLToastPanel
 {
 public:
 	void close();
@@ -46,12 +44,15 @@ class LLToastScriptTextbox
 	// Non-transient messages.  You can specify non-default button
 	// layouts (like one for script dialogs) by passing various
 	// numbers in for "layout".
-	LLToastScriptTextbox(LLNotificationPtr& notification);
+	LLToastScriptTextbox(const LLNotificationPtr& notification);
 
 	/*virtual*/ ~LLToastScriptTextbox();
-protected:
-	void onClickSubmit();
+
 private:
+
+	void onClickSubmit();
+	void onClickIgnore();
+
 	static const S32 DEFAULT_MESSAGE_MAX_LINE_COUNT;
 };
 
diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp
index ca80a1db795752f7439e45b9e16e37df007840aa..964b17d3a665b1bc1f3cadc4d12cb86a30db15d8 100644
--- a/indra/newview/lltoolmorph.cpp
+++ b/indra/newview/lltoolmorph.cpp
@@ -244,13 +244,13 @@ BOOL LLVisualParamHint::render()
 //-----------------------------------------------------------------------------
 // draw()
 //-----------------------------------------------------------------------------
-void LLVisualParamHint::draw()
+void LLVisualParamHint::draw(F32 alpha)
 {
 	if (!mIsVisible) return;
 
 	gGL.getTexUnit(0)->bind(this);
 
-	gGL.color4f(1.f, 1.f, 1.f, 1.f);
+	gGL.color4f(1.f, 1.f, 1.f, alpha);
 
 	LLGLSUIDefault gls_ui;
 	gGL.begin(LLRender::QUADS);
diff --git a/indra/newview/lltoolmorph.h b/indra/newview/lltoolmorph.h
index 59201233ae721ee7a714b7d84a83027d4c2fefa6..a6889be1510c0b57901a95c4d558ecee13e6990d 100644
--- a/indra/newview/lltoolmorph.h
+++ b/indra/newview/lltoolmorph.h
@@ -68,7 +68,7 @@ class LLVisualParamHint : public LLViewerDynamicTexture
 	BOOL					render();
 	void					requestUpdate( S32 delay_frames ) {mNeedsUpdate = TRUE; mDelayFrames = delay_frames; }
 	void					setUpdateDelayFrames( S32 delay_frames ) { mDelayFrames = delay_frames; }
-	void					draw();
+	void					draw(F32 alpha);
 	
 	LLViewerVisualParam*	getVisualParam() { return mVisualParam; }
 	F32						getVisualParamWeight() { return mVisualParamWeight; }
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ad7725b3e381b79d06e61bbb86accf1f7d27abd
--- /dev/null
+++ b/indra/newview/llviewerassetstats.cpp
@@ -0,0 +1,619 @@
+/** 
+ * @file llviewerassetstats.cpp
+ * @brief 
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerassetstats.h"
+#include "llregionhandle.h"
+
+#include "stdtypes.h"
+
+/*
+ * Classes and utility functions for per-thread and per-region
+ * asset and experiential metrics to be aggregated grid-wide.
+ *
+ * The basic metrics grouping is LLViewerAssetStats::PerRegionStats.
+ * This provides various counters and simple statistics for asset
+ * fetches binned into a few categories.  One of these is maintained
+ * for each region encountered and the 'current' region is available
+ * as a simple reference.  Each thread (presently two) interested
+ * in participating in these stats gets an instance of the
+ * LLViewerAssetStats class so that threads are completely
+ * independent.
+ *
+ * The idea of a current region is used for simplicity and speed
+ * of categorization.  Each metrics event could have taken a
+ * region uuid argument resulting in a suitable lookup.  Arguments
+ * against this design include:
+ *
+ *  -  Region uuid not trivially available to caller.
+ *  -  Cost (cpu, disruption in real work flow) too high.
+ *  -  Additional precision not really meaningful.
+ *
+ * By itself, the LLViewerAssetStats class is thread- and
+ * viewer-agnostic and can be used anywhere without assumptions
+ * of global pointers and other context.  For the viewer,
+ * a set of free functions are provided in the namespace
+ * LLViewerAssetStatsFF which *do* implement viewer-native
+ * policies about per-thread globals and will do correct
+ * defensive tests of same.
+ *
+ * References
+ *
+ * Project:
+ *   <TBD>
+ *
+ * Test Plan:
+ *   <TBD>
+ *
+ * Jiras:
+ *   <TBD>
+ *
+ * Unit Tests:
+ *   indra/newview/tests/llviewerassetstats_test.cpp
+ *
+ */
+
+
+// ------------------------------------------------------
+// Global data definitions
+// ------------------------------------------------------
+LLViewerAssetStats * gViewerAssetStatsMain(0);
+LLViewerAssetStats * gViewerAssetStatsThread1(0);
+
+
+// ------------------------------------------------------
+// Local declarations
+// ------------------------------------------------------
+namespace
+{
+
+static LLViewerAssetStats::EViewerAssetCategories
+asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp);
+
+}
+
+// ------------------------------------------------------
+// LLViewerAssetStats::PerRegionStats struct definition
+// ------------------------------------------------------
+void
+LLViewerAssetStats::PerRegionStats::reset()
+{
+	for (int i(0); i < LL_ARRAY_SIZE(mRequests); ++i)
+	{
+		mRequests[i].mEnqueued.reset();
+		mRequests[i].mDequeued.reset();
+		mRequests[i].mResponse.reset();
+	}
+	mFPS.reset();
+	
+	mTotalTime = 0;
+	mStartTimestamp = LLViewerAssetStatsFF::get_timestamp();
+}
+
+
+void
+LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionStats & src)
+{
+	// mRegionHandle, mTotalTime, mStartTimestamp are left alone.
+	
+	// mFPS
+	if (src.mFPS.getCount() && mFPS.getCount())
+	{
+		mFPS.merge(src.mFPS);
+	}
+
+	// Requests
+	for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i)
+	{
+		mRequests[i].mEnqueued.merge(src.mRequests[i].mEnqueued);
+		mRequests[i].mDequeued.merge(src.mRequests[i].mDequeued);
+		mRequests[i].mResponse.merge(src.mRequests[i].mResponse);
+	}
+}
+
+
+void
+LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now)
+{
+	mTotalTime += (now - mStartTimestamp);
+	mStartTimestamp = now;
+}
+
+
+// ------------------------------------------------------
+// LLViewerAssetStats class definition
+// ------------------------------------------------------
+LLViewerAssetStats::LLViewerAssetStats()
+	: mRegionHandle(U64(0))
+{
+	reset();
+}
+
+
+LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src)
+	: mRegionHandle(src.mRegionHandle),
+	  mResetTimestamp(src.mResetTimestamp)
+{
+	const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
+	for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
+	{
+		mRegionStats[it->first] = new PerRegionStats(*it->second);
+	}
+	mCurRegionStats = mRegionStats[mRegionHandle];
+}
+
+
+void
+LLViewerAssetStats::reset()
+{
+	// Empty the map of all region stats
+	mRegionStats.clear();
+
+	// If we have a current stats, reset it, otherwise, as at construction,
+	// create a new one as we must always have a current stats block.
+	if (mCurRegionStats)
+	{
+		mCurRegionStats->reset();
+	}
+	else
+	{
+		mCurRegionStats = new PerRegionStats(mRegionHandle);
+	}
+
+	// And add reference to map
+	mRegionStats[mRegionHandle] = mCurRegionStats;
+
+	// Start timestamp consistent with per-region collector
+	mResetTimestamp = mCurRegionStats->mStartTimestamp;
+}
+
+
+void
+LLViewerAssetStats::setRegion(region_handle_t region_handle)
+{
+	if (region_handle == mRegionHandle)
+	{
+		// Already active, ignore.
+		return;
+	}
+
+	// Get duration for current set
+	const duration_t now = LLViewerAssetStatsFF::get_timestamp();
+	mCurRegionStats->accumulateTime(now);
+
+	// Prepare new set
+	PerRegionContainer::iterator new_stats = mRegionStats.find(region_handle);
+	if (mRegionStats.end() == new_stats)
+	{
+		// Haven't seen this region_id before, create a new block and make it current.
+		mCurRegionStats = new PerRegionStats(region_handle);
+		mRegionStats[region_handle] = mCurRegionStats;
+	}
+	else
+	{
+		mCurRegionStats = new_stats->second;
+	}
+	mCurRegionStats->mStartTimestamp = now;
+	mRegionHandle = region_handle;
+}
+
+
+void
+LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp)
+{
+	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
+	
+	++(mCurRegionStats->mRequests[int(eac)].mEnqueued);
+}
+	
+void
+LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp)
+{
+	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
+
+	++(mCurRegionStats->mRequests[int(eac)].mDequeued);
+}
+
+void
+LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration)
+{
+	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
+
+	mCurRegionStats->mRequests[int(eac)].mResponse.record(duration);
+}
+
+void
+LLViewerAssetStats::recordFPS(F32 fps)
+{
+	mCurRegionStats->mFPS.record(fps);
+}
+
+LLSD
+LLViewerAssetStats::asLLSD(bool compact_output)
+{
+	// Top-level tags
+	static const LLSD::String tags[EVACCount] = 
+		{
+			LLSD::String("get_texture_temp_http"),
+			LLSD::String("get_texture_temp_udp"),
+			LLSD::String("get_texture_non_temp_http"),
+			LLSD::String("get_texture_non_temp_udp"),
+			LLSD::String("get_wearable_udp"),
+			LLSD::String("get_sound_udp"),
+			LLSD::String("get_gesture_udp"),
+			LLSD::String("get_other")
+		};
+
+	// Stats Group Sub-tags.
+	static const LLSD::String enq_tag("enqueued");
+	static const LLSD::String deq_tag("dequeued");
+	static const LLSD::String rcnt_tag("resp_count");
+	static const LLSD::String rmin_tag("resp_min");
+	static const LLSD::String rmax_tag("resp_max");
+	static const LLSD::String rmean_tag("resp_mean");
+
+	// MMM Group Sub-tags.
+	static const LLSD::String cnt_tag("count");
+	static const LLSD::String min_tag("min");
+	static const LLSD::String max_tag("max");
+	static const LLSD::String mean_tag("mean");
+
+	const duration_t now = LLViewerAssetStatsFF::get_timestamp();
+	mCurRegionStats->accumulateTime(now);
+
+	LLSD regions = LLSD::emptyArray();
+	for (PerRegionContainer::iterator it = mRegionStats.begin();
+		 mRegionStats.end() != it;
+		 ++it)
+	{
+		if (0 == it->first)
+		{
+			// Never emit NULL UUID/handle in results.
+			continue;
+		}
+
+		PerRegionStats & stats = *it->second;
+		
+		LLSD reg_stat = LLSD::emptyMap();
+		
+		for (int i = 0; i < LL_ARRAY_SIZE(tags); ++i)
+		{
+			PerRegionStats::prs_group & group(stats.mRequests[i]);
+			
+			if ((! compact_output) ||
+				group.mEnqueued.getCount() ||
+				group.mDequeued.getCount() ||
+				group.mResponse.getCount())
+			{
+				LLSD & slot = reg_stat[tags[i]];
+				slot = LLSD::emptyMap();
+				slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount()));
+				slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount()));
+				slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount()));
+				slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin() * 1.0e-6));
+				slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax() * 1.0e-6));
+				slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6));
+			}
+		}
+
+		if ((! compact_output) || stats.mFPS.getCount())
+		{
+			LLSD & slot = reg_stat["fps"];
+			slot = LLSD::emptyMap();
+			slot[cnt_tag] = LLSD(S32(stats.mFPS.getCount()));
+			slot[min_tag] = LLSD(F64(stats.mFPS.getMin()));
+			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);
+		reg_stat["grid_y"] = LLSD::Integer(grid_y);
+		reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6);		
+		regions.append(reg_stat);
+	}
+
+	LLSD ret = LLSD::emptyMap();
+	ret["regions"] = regions;
+	ret["duration"] = LLSD::Real((now - mResetTimestamp) * 1.0e-6);
+	
+	return ret;
+}
+
+void
+LLViewerAssetStats::merge(const LLViewerAssetStats & src)
+{
+	// mRegionHandle, mCurRegionStats and mResetTimestamp are left untouched.
+	// Just merge the stats bodies
+
+	const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
+	for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
+	{
+		PerRegionContainer::iterator dst(mRegionStats.find(it->first));
+		if (mRegionStats.end() == dst)
+		{
+			// Destination is missing data, just make a private copy
+			mRegionStats[it->first] = new PerRegionStats(*it->second);
+		}
+		else
+		{
+			dst->second->merge(*it->second);
+		}
+	}
+}
+
+
+// ------------------------------------------------------
+// Global free-function definitions (LLViewerAssetStatsFF namespace)
+// ------------------------------------------------------
+
+namespace LLViewerAssetStatsFF
+{
+
+//
+// Target thread is elaborated in the function name.  This could
+// have been something 'templatey' like specializations iterated
+// over a set of constants but with so few, this is clearer I think.
+//
+// As for the threads themselves... rather than do fine-grained
+// locking as we gather statistics, this code creates a collector
+// for each thread, allocated and run independently.  Logging
+// happens at relatively infrequent intervals and at that time
+// the data is sent to a single thread to be aggregated into
+// a single entity with locks, thread safety and other niceties.
+//
+// A particularly fussy implementation would distribute the
+// per-thread pointers across separate cache lines.  But that should
+// be beyond current requirements.
+//
+
+// 'main' thread - initial program thread
+
+void
+set_region_main(LLViewerAssetStats::region_handle_t region_handle)
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	gViewerAssetStatsMain->setRegion(region_handle);
+}
+
+void
+record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	gViewerAssetStatsMain->recordGetEnqueued(at, with_http, is_temp);
+}
+
+void
+record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	gViewerAssetStatsMain->recordGetDequeued(at, with_http, is_temp);
+}
+
+void
+record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration)
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	gViewerAssetStatsMain->recordGetServiced(at, with_http, is_temp, duration);
+}
+
+void
+record_fps_main(F32 fps)
+{
+	if (! gViewerAssetStatsMain)
+		return;
+
+	gViewerAssetStatsMain->recordFPS(fps);
+}
+
+
+// 'thread1' - should be for TextureFetch thread
+
+void
+set_region_thread1(LLViewerAssetStats::region_handle_t region_handle)
+{
+	if (! gViewerAssetStatsThread1)
+		return;
+
+	gViewerAssetStatsThread1->setRegion(region_handle);
+}
+
+void
+record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
+{
+	if (! gViewerAssetStatsThread1)
+		return;
+
+	gViewerAssetStatsThread1->recordGetEnqueued(at, with_http, is_temp);
+}
+
+void
+record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
+{
+	if (! gViewerAssetStatsThread1)
+		return;
+
+	gViewerAssetStatsThread1->recordGetDequeued(at, with_http, is_temp);
+}
+
+void
+record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration)
+{
+	if (! gViewerAssetStatsThread1)
+		return;
+
+	gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration);
+}
+
+
+void
+init()
+{
+	if (! gViewerAssetStatsMain)
+	{
+		gViewerAssetStatsMain = new LLViewerAssetStats();
+	}
+	if (! gViewerAssetStatsThread1)
+	{
+		gViewerAssetStatsThread1 = new LLViewerAssetStats();
+	}
+}
+
+void
+cleanup()
+{
+	delete gViewerAssetStatsMain;
+	gViewerAssetStatsMain = 0;
+
+	delete gViewerAssetStatsThread1;
+	gViewerAssetStatsThread1 = 0;
+}
+
+
+} // namespace LLViewerAssetStatsFF
+
+
+// ------------------------------------------------------
+// Local function definitions
+// ------------------------------------------------------
+
+namespace
+{
+
+LLViewerAssetStats::EViewerAssetCategories
+asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp)
+{
+	// For statistical purposes, we divide GETs into several
+	// populations of asset fetches:
+	//  - textures which are de-prioritized in the asset system
+	//  - wearables (clothing, bodyparts) which directly affect
+	//    user experiences when they log in
+	//  - sounds
+	//  - gestures
+	//  - everything else.
+	//
+	llassert_always(26 == LLViewerAssetType::AT_COUNT);
+
+	// Multiple asset definitions are floating around so this requires some
+	// maintenance and attention.
+	static const LLViewerAssetStats::EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] =
+		{
+			LLViewerAssetStats::EVACTextureTempHTTPGet,			// (0) AT_TEXTURE
+			LLViewerAssetStats::EVACSoundUDPGet,				// AT_SOUND
+			LLViewerAssetStats::EVACOtherGet,					// AT_CALLINGCARD
+			LLViewerAssetStats::EVACOtherGet,					// AT_LANDMARK
+			LLViewerAssetStats::EVACOtherGet,					// AT_SCRIPT
+			LLViewerAssetStats::EVACWearableUDPGet,				// AT_CLOTHING
+			LLViewerAssetStats::EVACOtherGet,					// AT_OBJECT
+			LLViewerAssetStats::EVACOtherGet,					// AT_NOTECARD
+			LLViewerAssetStats::EVACOtherGet,					// AT_CATEGORY
+			LLViewerAssetStats::EVACOtherGet,					// AT_ROOT_CATEGORY
+			LLViewerAssetStats::EVACOtherGet,					// (10) AT_LSL_TEXT
+			LLViewerAssetStats::EVACOtherGet,					// AT_LSL_BYTECODE
+			LLViewerAssetStats::EVACOtherGet,					// AT_TEXTURE_TGA
+			LLViewerAssetStats::EVACWearableUDPGet,				// AT_BODYPART
+			LLViewerAssetStats::EVACOtherGet,					// AT_TRASH
+			LLViewerAssetStats::EVACOtherGet,					// AT_SNAPSHOT_CATEGORY
+			LLViewerAssetStats::EVACOtherGet,					// AT_LOST_AND_FOUND
+			LLViewerAssetStats::EVACSoundUDPGet,				// AT_SOUND_WAV
+			LLViewerAssetStats::EVACOtherGet,					// AT_IMAGE_TGA
+			LLViewerAssetStats::EVACOtherGet,					// AT_IMAGE_JPEG
+			LLViewerAssetStats::EVACGestureUDPGet,				// (20) AT_ANIMATION
+			LLViewerAssetStats::EVACGestureUDPGet,				// AT_GESTURE
+			LLViewerAssetStats::EVACOtherGet,					// AT_SIMSTATE
+			LLViewerAssetStats::EVACOtherGet,					// AT_FAVORITE
+			LLViewerAssetStats::EVACOtherGet,					// AT_LINK
+			LLViewerAssetStats::EVACOtherGet,					// AT_LINK_FOLDER
+#if 0
+			// When LLViewerAssetType::AT_COUNT == 49
+			LLViewerAssetStats::EVACOtherGet,					// AT_FOLDER_ENSEMBLE_START
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// (30)
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// (40)
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// 
+			LLViewerAssetStats::EVACOtherGet,					// AT_FOLDER_ENSEMBLE_END
+			LLViewerAssetStats::EVACOtherGet,					// AT_CURRENT_OUTFIT
+			LLViewerAssetStats::EVACOtherGet,					// AT_OUTFIT
+			LLViewerAssetStats::EVACOtherGet					// AT_MY_OUTFITS
+#endif
+		};
+	
+	if (at < 0 || at >= LLViewerAssetType::AT_COUNT)
+	{
+		return LLViewerAssetStats::EVACOtherGet;
+	}
+	LLViewerAssetStats::EViewerAssetCategories ret(asset_to_bin_map[at]);
+	if (LLViewerAssetStats::EVACTextureTempHTTPGet == ret)
+	{
+		// Indexed with [is_temp][with_http]
+		static const LLViewerAssetStats::EViewerAssetCategories texture_bin_map[2][2] =
+			{
+				{
+					LLViewerAssetStats::EVACTextureNonTempUDPGet,
+					LLViewerAssetStats::EVACTextureNonTempHTTPGet,
+				},
+				{
+					LLViewerAssetStats::EVACTextureTempUDPGet,
+					LLViewerAssetStats::EVACTextureTempHTTPGet,
+				}
+			};
+
+		ret = texture_bin_map[is_temp][with_http];
+	}
+	return ret;
+}
+
+} // anonymous namespace
diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h
new file mode 100644
index 0000000000000000000000000000000000000000..905ceefad511a45850b78315c12512a2b1e4614b
--- /dev/null
+++ b/indra/newview/llviewerassetstats.h
@@ -0,0 +1,334 @@
+/** 
+ * @file llviewerassetstats.h
+ * @brief Client-side collection of asset request statistics
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLVIEWERASSETSTATUS_H
+#define	LL_LLVIEWERASSETSTATUS_H
+
+
+#include "linden_common.h"
+
+#include "llpointer.h"
+#include "llrefcount.h"
+#include "llviewerassettype.h"
+#include "llviewerassetstorage.h"
+#include "llsimplestat.h"
+#include "llsd.h"
+
+/**
+ * @class LLViewerAssetStats
+ * @brief Records performance aspects of asset access operations.
+ *
+ * This facility is derived from a very similar simulator-based
+ * one, LLSimAssetStats.  It's function is to count asset access
+ * operations and characterize response times.  Collected data
+ * are binned in several dimensions:
+ *
+ *  - Asset types collapsed into a few aggregated categories
+ *  - By simulator UUID
+ *  - By transport mechanism (HTTP vs MessageSystem)
+ *  - By persistence (temp vs non-temp)
+ *
+ * Statistics collected are fairly basic at this point:
+ *
+ *  - Counts of enqueue and dequeue operations
+ *  - Min/Max/Mean of asset transfer operations
+ *
+ * This collector differs from the simulator-based on in a
+ * number of ways:
+ *
+ *  - The front-end/back-end distinction doesn't exist in viewer
+ *    code
+ *  - Multiple threads must be safely accomodated in the viewer
+ *
+ * Access to results is by conversion to an LLSD with some standardized
+ * key names.  The intent of this structure is that it be emitted as
+ * standard syslog-based metrics formatting where it can be picked
+ * up by interested parties.
+ *
+ * For convenience, a set of free functions in namespace
+ * LLViewerAssetStatsFF is provided for conditional test-and-call
+ * operations.
+ */
+class LLViewerAssetStats
+{
+public:
+	enum EViewerAssetCategories
+	{
+		EVACTextureTempHTTPGet,			//< Texture GETs - temp/baked, HTTP
+		EVACTextureTempUDPGet,			//< Texture GETs - temp/baked, UDP
+		EVACTextureNonTempHTTPGet,		//< Texture GETs - perm, HTTP
+		EVACTextureNonTempUDPGet,		//< Texture GETs - perm, UDP
+		EVACWearableUDPGet,				//< Wearable GETs
+		EVACSoundUDPGet,				//< Sound GETs
+		EVACGestureUDPGet,				//< Gesture GETs
+		EVACOtherGet,					//< Other GETs
+		
+		EVACCount						// Must be last
+	};
+
+	/**
+	 * Type for duration and other time values in the metrics.  Selected
+	 * for compatibility with the pre-existing timestamp on the texture
+	 * fetcher class, LLTextureFetch.
+	 */
+	typedef U64 duration_t;
+
+	/**
+	 * Type for the region identifier used in stats.  Currently uses
+	 * the region handle's type (a U64) rather than the regions's LLUUID
+	 * as the latter isn't available immediately.
+	 */
+	typedef U64 region_handle_t;
+
+	/**
+	 * @brief Collected data for a single region visited by the avatar.
+	 *
+	 * Fairly simple, for each asset bin enumerated above a count
+	 * of enqueue and dequeue operations and simple stats on response
+	 * times for completed requests.
+	 */
+	class PerRegionStats : public LLRefCount
+	{
+	public:
+		PerRegionStats(const region_handle_t region_handle)
+			: LLRefCount(),
+			  mRegionHandle(region_handle)
+			{
+				reset();
+			}
+
+		PerRegionStats(const PerRegionStats & src)
+			: LLRefCount(),
+			  mRegionHandle(src.mRegionHandle),
+			  mTotalTime(src.mTotalTime),
+			  mStartTimestamp(src.mStartTimestamp),
+			  mFPS(src.mFPS)
+			{
+				for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i)
+				{
+					mRequests[i] = src.mRequests[i];
+				}
+			}
+
+		// Default assignment and destructor are correct.
+		
+		void reset();
+
+		void merge(const PerRegionStats & src);
+		
+		// Apply current running time to total and reset start point.
+		// Return current timestamp as a convenience.
+		void accumulateTime(duration_t now);
+		
+	public:
+		region_handle_t		mRegionHandle;
+		duration_t			mTotalTime;
+		duration_t			mStartTimestamp;
+		LLSimpleStatMMM<>	mFPS;
+		
+		struct prs_group
+		{
+			LLSimpleStatCounter			mEnqueued;
+			LLSimpleStatCounter			mDequeued;
+			LLSimpleStatMMM<duration_t>	mResponse;
+		}
+		mRequests [EVACCount];
+	};
+
+public:
+	LLViewerAssetStats();
+	LLViewerAssetStats(const LLViewerAssetStats &);
+	// Default destructor is correct.
+	LLViewerAssetStats & operator=(const LLViewerAssetStats &);			// Not defined
+
+	// Clear all metrics data.  This leaves the currently-active region
+	// in place but with zero'd data for all metrics.  All other regions
+	// are removed from the collection map.
+	void reset();
+
+	// Set hidden region argument and establish context for subsequent
+	// collection calls.
+	void setRegion(region_handle_t region_handle);
+
+	// Asset GET Requests
+	void recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp);
+	void recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp);
+	void recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration);
+
+	// Frames-Per-Second Samples
+	void recordFPS(F32 fps);
+
+	// Merge a source instance into a destination instance.  This is
+	// conceptually an 'operator+=()' method:
+	// - counts are added
+	// - minimums are min'd
+	// - maximums are max'd
+	// - other scalars are ignored ('this' wins)
+	//
+	void merge(const LLViewerAssetStats & src);
+	
+	// Retrieve current metrics for all visited regions (NULL region UUID/handle excluded)
+    // Returned LLSD is structured as follows:
+	//
+	// &stats_group = {
+	//   enqueued   : int,
+	//   dequeued   : int,
+	//   resp_count : int,
+	//   resp_min   : float,
+	//   resp_max   : float,
+	//   resp_mean  : float
+	// }
+	//
+	// &mmm_group = {
+	//   count : int,
+	//   min   : float,
+	//   max   : float,
+	//   mean  : float
+	// }
+	//
+	// {
+	//   duration: int
+	//   regions: {
+	//     $: {			// Keys are strings of the region's handle in hex
+	//       duration:                 : int,
+	//		 fps:					   : &mmm_group,
+	//       get_texture_temp_http     : &stats_group,
+	//       get_texture_temp_udp      : &stats_group,
+	//       get_texture_non_temp_http : &stats_group,
+	//       get_texture_non_temp_udp  : &stats_group,
+	//       get_wearable_udp          : &stats_group,
+	//       get_sound_udp             : &stats_group,
+	//       get_gesture_udp           : &stats_group,
+	//       get_other                 : &stats_group
+	//     }
+	//   }
+	// }
+	//
+	// @param	compact_output		If true, omits from conversion any mmm_block
+	//								or stats_block that would contain all zero data.
+	//								Useful for transmission when the receiver knows
+	//								what is expected and will assume zero for missing
+	//								blocks.
+	LLSD asLLSD(bool compact_output);
+
+protected:
+	typedef std::map<region_handle_t, LLPointer<PerRegionStats> > PerRegionContainer;
+
+	// Region of the currently-active region.  Always valid but may
+	// be zero after construction or when explicitly set.  Unchanged
+	// by a reset() call.
+	region_handle_t mRegionHandle;
+
+	// Pointer to metrics collection for currently-active region.  Always
+	// valid and unchanged after reset() though contents will be changed.
+	// Always points to a collection contained in mRegionStats.
+	LLPointer<PerRegionStats> mCurRegionStats;
+
+	// Metrics data for all regions during one collection cycle
+	PerRegionContainer mRegionStats;
+
+	// Time of last reset
+	duration_t mResetTimestamp;
+};
+
+
+/**
+ * Global stats collectors one for each independent thread where
+ * assets and other statistics are gathered.  The globals are
+ * expected to be created at startup time and then picked up by
+ * their respective threads afterwards.  A set of free functions
+ * are provided to access methods behind the globals while both
+ * minimally disrupting visual flow and supplying a description
+ * of intent.
+ *
+ * Expected thread assignments:
+ *
+ *  - Main:  main() program execution thread
+ *  - Thread1:  TextureFetch worker thread
+ */
+extern LLViewerAssetStats * gViewerAssetStatsMain;
+
+extern LLViewerAssetStats * gViewerAssetStatsThread1;
+
+namespace LLViewerAssetStatsFF
+{
+/**
+ * @brief Allocation and deallocation of globals.
+ *
+ * init() should be called before threads are started that will access it though
+ * you'll likely get away with calling it afterwards.  cleanup() should only be
+ * called after threads are shutdown to prevent races on the global pointers.
+ */
+void init();
+
+void cleanup();
+
+/**
+ * We have many timers, clocks etc. in the runtime.  This is the
+ * canonical timestamp for these metrics which is compatible with
+ * the pre-existing timestamping in the texture fetcher.
+ */
+inline LLViewerAssetStats::duration_t get_timestamp()
+{
+	return LLTimer::getTotalTime();
+}
+
+/**
+ * Region context, event and duration loggers for the Main thread.
+ */
+void set_region_main(LLViewerAssetStats::region_handle_t region_handle);
+
+void record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp);
+
+void record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp);
+
+void record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp,
+						  LLViewerAssetStats::duration_t duration);
+
+void record_fps_main(F32 fps);
+
+
+/**
+ * Region context, event and duration loggers for Thread 1.
+ */
+void set_region_thread1(LLViewerAssetStats::region_handle_t region_handle);
+
+void record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp);
+
+void record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp);
+
+void record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp,
+						  LLViewerAssetStats::duration_t duration);
+
+} // namespace LLViewerAssetStatsFF
+
+#endif // LL_LLVIEWERASSETSTATUS_H
diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp
index 2e7ef0fec3dffa8f356be07ec73100e5f6be0260..36c8b42a5223eab5369e9d4b2bca9728da8bc03f 100644
--- a/indra/newview/llviewerassetstorage.cpp
+++ b/indra/newview/llviewerassetstorage.cpp
@@ -33,6 +33,61 @@
 #include "message.h"
 
 #include "llagent.h"
+#include "lltransfersourceasset.h"
+#include "lltransfertargetvfile.h"
+#include "llviewerassetstats.h"
+
+///----------------------------------------------------------------------------
+/// LLViewerAssetRequest
+///----------------------------------------------------------------------------
+
+/**
+ * @brief Local class to encapsulate asset fetch requests with a timestamp.
+ *
+ * Derived from the common LLAssetRequest class, this is currently used
+ * only for fetch/get operations and its only function is to wrap remote
+ * asset fetch requests so that they can be timed.
+ */
+class LLViewerAssetRequest : public LLAssetRequest
+{
+public:
+	LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type)
+		: LLAssetRequest(uuid, type),
+		  mMetricsStartTime(0)
+		{
+		}
+	
+	LLViewerAssetRequest & operator=(const LLViewerAssetRequest &);	// Not defined
+	// Default assignment operator valid
+	
+	// virtual
+	~LLViewerAssetRequest()
+		{
+			recordMetrics();
+		}
+
+protected:
+	void recordMetrics()
+		{
+			if (mMetricsStartTime)
+			{
+				// Okay, it appears this request was used for useful things.  Record
+				// the expected dequeue and duration of request processing.
+				LLViewerAssetStatsFF::record_dequeue_main(mType, false, false);
+				LLViewerAssetStatsFF::record_response_main(mType, false, false,
+														   (LLViewerAssetStatsFF::get_timestamp()
+															- mMetricsStartTime));
+				mMetricsStartTime = 0;
+			}
+		}
+	
+public:
+	LLViewerAssetStats::duration_t		mMetricsStartTime;
+};
+
+///----------------------------------------------------------------------------
+/// LLViewerAssetStorage
+///----------------------------------------------------------------------------
 
 LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
 										   LLVFS *vfs, LLVFS *static_vfs, 
@@ -258,3 +313,77 @@ void LLViewerAssetStorage::storeAssetData(
 		}
 	}
 }
+
+
+/**
+ * @brief Allocate and queue an asset fetch request for the viewer
+ *
+ * This is a nearly-verbatim copy of the base class's implementation
+ * with the following changes:
+ *  -  Use a locally-derived request class
+ *  -  Start timing for metrics when request is queued
+ *
+ * This is an unfortunate implementation choice but it's forced by
+ * current conditions.  A refactoring that might clean up the layers
+ * of responsibility or introduce factories or more virtualization
+ * of methods would enable a more attractive solution.
+ *
+ * If LLAssetStorage::_queueDataRequest changes, this must change
+ * as well.
+ */
+
+// virtual
+void LLViewerAssetStorage::_queueDataRequest(
+	const LLUUID& uuid,
+	LLAssetType::EType atype,
+	LLGetAssetCallback callback,
+	void *user_data,
+	BOOL duplicate,
+	BOOL is_priority)
+{
+	if (mUpstreamHost.isOk())
+	{
+		// stash the callback info so we can find it after we get the response message
+		LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype);
+		req->mDownCallback = callback;
+		req->mUserData = user_data;
+		req->mIsPriority = is_priority;
+		if (!duplicate)
+		{
+			// Only collect metrics for non-duplicate requests.  Others 
+			// are piggy-backing and will artificially lower averages.
+			req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
+		}
+		
+		mPendingDownloads.push_back(req);
+	
+		if (!duplicate)
+		{
+			// send request message to our upstream data provider
+			// Create a new asset transfer.
+			LLTransferSourceParamsAsset spa;
+			spa.setAsset(uuid, atype);
+
+			// Set our destination file, and the completion callback.
+			LLTransferTargetParamsVFile tpvf;
+			tpvf.setAsset(uuid, atype);
+			tpvf.setCallback(downloadCompleteCallback, req);
+
+			llinfos << "Starting transfer for " << uuid << llendl;
+			LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
+			ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
+
+			LLViewerAssetStatsFF::record_enqueue_main(atype, false, false);
+		}
+	}
+	else
+	{
+		// uh-oh, we shouldn't have gotten here
+		llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
+		if (callback)
+		{
+			callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM);
+		}
+	}
+}
+
diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h
index 6346b79f0310b0797799e932c67ca5c639cd19e5..ca9b9943fa8decb19edb22c974233bf6e966a46f 100644
--- a/indra/newview/llviewerassetstorage.h
+++ b/indra/newview/llviewerassetstorage.h
@@ -63,6 +63,17 @@ class LLViewerAssetStorage : public LLAssetStorage
 		bool is_priority = false,
 		bool user_waiting=FALSE,
 		F64 timeout=LL_ASSET_STORAGE_TIMEOUT);
+
+protected:
+	using LLAssetStorage::_queueDataRequest;
+
+	// virtual
+	void _queueDataRequest(const LLUUID& uuid,
+						   LLAssetType::EType type,
+						   void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat),
+						   void *user_data,
+						   BOOL duplicate,
+						   BOOL is_priority);
 };
 
 #endif
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 622d09c60015b0639258bf9a96b05de66c173668..8c5a52c1878b3ba475d7761b4d96325a643711fc 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -504,9 +504,10 @@ bool toggle_show_object_render_cost(const LLSD& newvalue)
 
 void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value)
 {
-    if(new_value.asBoolean())
+    if(new_value.asInteger())
     {
-        LLUpdaterService().startChecking();
+		LLUpdaterService update_service;
+		if(!update_service.isChecking()) update_service.startChecking();
     }
     else
     {
@@ -661,7 +662,7 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2));
 	gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2));
 	gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2));
-	gSavedSettings.getControl("UpdaterServiceActive")->getSignal()->connect(&toggle_updater_service_active);
+	gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(&toggle_updater_service_active);
 	gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));
 	gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2));
 }
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index f573f25efe79727216cac4a46522f2a831cd3a27..dca1e33e609fac9f0539631c0c74511fee972b71 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -60,6 +60,7 @@
 #include "llfloaterhardwaresettings.h"
 #include "llfloaterhelpbrowser.h"
 #include "llfloatermediabrowser.h"
+#include "llfloaterwebcontent.h"
 #include "llfloatermediasettings.h"
 #include "llfloaterhud.h"
 #include "llfloaterimagepreview.h"
@@ -252,6 +253,7 @@ void LLViewerFloaterReg::registerFloaters()
 	LLFloaterReg::add("voice_controls", "floater_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLCallFloater>);
 	LLFloaterReg::add("voice_effect", "floater_voice_effect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterVoiceEffect>);
 
+	LLFloaterReg::add("web_content", "floater_web_content.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWebContent>);	
 	LLFloaterReg::add("whitelist_entry", "floater_whitelist_entry.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWhiteListEntry>);	
 	LLFloaterWindowSizeUtil::registerFloater();
 	LLFloaterReg::add("world_map", "floater_world_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterWorldMap>);	
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index fae4eb3c056029e2ea5085f299dbb7e8a14a9e1e..d3b6dcd86f2f937121d61890f9c997ae0869fd08 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -52,6 +52,7 @@
 #include "llviewerregion.h"
 #include "llwebsharing.h"	// For LLWebSharing::setOpenIDCookie(), *TODO: find a better way to do this!
 #include "llfilepicker.h"
+#include "llnotifications.h"
 
 #include "llevent.h"		// LLSimpleListener
 #include "llnotificationsutil.h"
@@ -62,6 +63,7 @@
 #include "llwindow.h"
 
 #include "llfloatermediabrowser.h"	// for handling window close requests and geometry change requests in media browser windows.
+#include "llfloaterwebcontent.h"	// for handling window close requests and geometry change requests in media browser windows.
 
 #include <boost/bind.hpp>	// for SkinFolder listener
 #include <boost/signals2.hpp>
@@ -293,6 +295,7 @@ LOG_CLASS(LLViewerMediaOpenIDResponder);
 LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL;
 LLURL LLViewerMedia::sOpenIDURL;
 std::string LLViewerMedia::sOpenIDCookie;
+LLPluginClassMedia* LLViewerMedia::sSpareBrowserMediaSource = NULL;
 static LLViewerMedia::impl_list sViewerMediaImplList;
 static LLViewerMedia::impl_id_map sViewerMediaTextureIDMap;
 static LLTimer sMediaCreateTimer;
@@ -742,6 +745,9 @@ void LLViewerMedia::updateMedia(void *dummy_arg)
 	// Enable/disable the plugin read thread
 	LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread"));
 	
+	// HACK: we always try to keep a spare running webkit plugin around to improve launch times.
+	createSpareBrowserMediaSource();
+	
 	sAnyMediaShowing = false;
 	sUpdatedCookies = getCookieStore()->getChangedCookies();
 	if(!sUpdatedCookies.empty())
@@ -759,6 +765,12 @@ void LLViewerMedia::updateMedia(void *dummy_arg)
 		pimpl->update();
 		pimpl->calculateInterest();
 	}
+	
+	// Let the spare media source actually launch
+	if(sSpareBrowserMediaSource)
+	{
+		sSpareBrowserMediaSource->idle();
+	}
 		
 	// Sort the static instance list using our interest criteria
 	sViewerMediaImplList.sort(priorityComparitor);
@@ -1034,6 +1046,26 @@ bool LLViewerMedia::isParcelAudioPlaying()
 	return (LLViewerMedia::hasParcelAudio() && gAudiop && LLAudioEngine::AUDIO_PLAYING == gAudiop->isInternetStreamPlaying());
 }
 
+void LLViewerMedia::onAuthSubmit(const LLSD& notification, const LLSD& response)
+{
+	LLViewerMediaImpl *impl = LLViewerMedia::getMediaImplFromTextureID(notification["payload"]["media_id"]);
+	if(impl)
+	{
+		LLPluginClassMedia* media = impl->getMediaPlugin();
+		if(media)
+		{
+			if (response["ok"])
+			{
+				media->sendAuthResponse(true, response["username"], response["password"]);
+			}
+			else
+			{
+				media->sendAuthResponse(false, "", "");
+			}
+		}
+	}
+}
+
 /////////////////////////////////////////////////////////////////////////////////////////
 // static
 void LLViewerMedia::clearAllCookies()
@@ -1400,6 +1432,29 @@ void LLViewerMedia::proxyWindowClosed(const std::string &uuid)
 	}
 }
 
+/////////////////////////////////////////////////////////////////////////////////////////
+// static
+void LLViewerMedia::createSpareBrowserMediaSource()
+{
+	if(!sSpareBrowserMediaSource)
+	{
+		// If we don't have a spare browser media source, create one.
+		// The null owner will keep the browser plugin from fully initializing 
+		// (specifically, it keeps LLPluginClassMedia from negotiating a size change, 
+		// which keeps MediaPluginWebkit::initBrowserWindow from doing anything until we have some necessary data, like the background color)
+		sSpareBrowserMediaSource = LLViewerMediaImpl::newSourceFromMediaType("text/html", NULL, 0, 0);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// static
+LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource() 
+{
+	LLPluginClassMedia* result = sSpareBrowserMediaSource;
+	sSpareBrowserMediaSource = NULL;
+	return result; 
+};
+
 bool LLViewerMedia::hasInWorldMedia()
 {
 	if (sInWorldMediaDisabled) return false;
@@ -1636,6 +1691,21 @@ void LLViewerMediaImpl::setMediaType(const std::string& media_type)
 LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target)
 {
 	std::string plugin_basename = LLMIMETypes::implType(media_type);
+	LLPluginClassMedia* media_source = NULL;
+	
+	// HACK: we always try to keep a spare running webkit plugin around to improve launch times.
+	if(plugin_basename == "media_plugin_webkit")
+	{
+		media_source = LLViewerMedia::getSpareBrowserMediaSource();
+		if(media_source)
+		{
+			media_source->setOwner(owner);
+			media_source->setTarget(target);
+			media_source->setSize(default_width, default_height);
+						
+			return media_source;
+		}
+	}
 	
 	if(plugin_basename.empty())
 	{
@@ -1673,7 +1743,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
 		}
 		else
 		{
-			LLPluginClassMedia* media_source = new LLPluginClassMedia(owner);
+			media_source = new LLPluginClassMedia(owner);
 			media_source->setSize(default_width, default_height);
 			media_source->setUserDataPath(user_data_path);
 			media_source->setLanguageCode(LLUI::getLanguage());
@@ -1753,6 +1823,22 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type)
 		media_source->focus(mHasFocus);
 		media_source->setBackgroundColor(mBackgroundColor);
 		
+		if(gSavedSettings.getBOOL("BrowserIgnoreSSLCertErrors"))
+		{
+			media_source->ignore_ssl_cert_errors(true);
+		}
+
+		// start by assuming the default CA file will be used
+		std::string ca_path = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "lindenlab.pem" );
+	
+		// default turned off so pick up the user specified path
+		if( ! gSavedSettings.getBOOL("BrowserUseDefaultCAFile"))
+		{
+			ca_path = gSavedSettings.getString("BrowserCAFilePath");
+		}
+		// set the path to the CA.pem file
+		media_source->addCertificateFilePath( ca_path );
+
 		media_source->proxy_setup(gSavedSettings.getBOOL("BrowserProxyEnabled"), gSavedSettings.getString("BrowserProxyAddress"), gSavedSettings.getS32("BrowserProxyPort"));
 		
 		if(mClearCache)
@@ -1848,6 +1934,18 @@ void LLViewerMediaImpl::setSize(int width, int height)
 	}
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////
+void LLViewerMediaImpl::showNotification(LLNotificationPtr notify)
+{
+	mNotification = notify;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+void LLViewerMediaImpl::hideNotification()
+{
+	mNotification.reset();
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 void LLViewerMediaImpl::play()
 {
@@ -2850,7 +2948,6 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
 			LL_DEBUGS("Media") << "MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is: " << plugin->getClickURL() << LL_ENDL; 
 			std::string url = plugin->getClickURL();
 			LLURLDispatcher::dispatch(url, NULL, mTrustedBrowser);
-
 		}
 		break;
 		case MEDIA_EVENT_CLICK_LINK_HREF:
@@ -2913,6 +3010,7 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
 		case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_BEGIN:
 		{
 			LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_BEGIN, uri is: " << plugin->getNavigateURI() << LL_ENDL;
+			hideNotification();
 
 			if(getNavState() == MEDIANAVSTATE_SERVER_SENT)
 			{
@@ -3003,7 +3101,26 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
 			plugin->sendPickFileResponse(response);
 		}
 		break;
-		
+
+
+		case LLViewerMediaObserver::MEDIA_EVENT_AUTH_REQUEST:
+		{
+			LLNotification::Params auth_request_params;
+			auth_request_params.name = "AuthRequest";
+
+			// pass in host name and realm for site (may be zero length but will always exist)
+			LLSD args;
+			LLURL raw_url( plugin->getAuthURL().c_str() );
+			args["HOST_NAME"] = raw_url.getAuthority();
+			args["REALM"] = plugin->getAuthRealm();
+			auth_request_params.substitutions = args;
+
+			auth_request_params.payload = LLSD().with("media_id", mTextureId);
+			auth_request_params.functor.function = boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2);
+			LLNotifications::instance().add(auth_request_params);
+		};
+		break;
+
 		case LLViewerMediaObserver::MEDIA_EVENT_CLOSE_REQUEST:
 		{
 			std::string uuid = plugin->getClickUUID();
@@ -3019,6 +3136,7 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
 				// This close request is directed at another instance
 				pass_through = false;
 				LLFloaterMediaBrowser::closeRequest(uuid);
+				LLFloaterWebContent::closeRequest(uuid);
 			}
 		}
 		break;
@@ -3038,6 +3156,7 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
 				// This request is directed at another instance
 				pass_through = false;
 				LLFloaterMediaBrowser::geometryChanged(uuid, plugin->getGeometryX(), plugin->getGeometryY(), plugin->getGeometryWidth(), plugin->getGeometryHeight());
+				LLFloaterWebContent::geometryChanged(uuid, plugin->getGeometryX(), plugin->getGeometryY(), plugin->getGeometryWidth(), plugin->getGeometryHeight());
 			}
 		}
 		break;
@@ -3521,6 +3640,11 @@ bool LLViewerMediaImpl::isInAgentParcel() const
 	return result;
 }
 
+LLNotificationPtr LLViewerMediaImpl::getCurrentNotification() const
+{
+	return mNotification;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 //
 // static
diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h
index 4025a4484ff0437edb7d708893b8fb1e93c3f772..e2e342cc454e7e73892979012eed48434a33ef76 100644
--- a/indra/newview/llviewermedia.h
+++ b/indra/newview/llviewermedia.h
@@ -37,6 +37,7 @@
 
 #include "llpluginclassmedia.h"
 #include "v4color.h"
+#include "llnotificationptr.h"
 
 #include "llurl.h"
 
@@ -130,6 +131,8 @@ class LLViewerMedia
 	static bool isParcelMediaPlaying();
 	static bool isParcelAudioPlaying();
 	
+	static void onAuthSubmit(const LLSD& notification, const LLSD& response);
+
 	// Clear all cookies for all plugins
 	static void clearAllCookies();
 	
@@ -155,6 +158,9 @@ class LLViewerMedia
 	static void proxyWindowOpened(const std::string &target, const std::string &uuid);
 	static void proxyWindowClosed(const std::string &uuid);
 	
+	static void createSpareBrowserMediaSource();
+	static LLPluginClassMedia* getSpareBrowserMediaSource();
+	
 private:
 	static void setOpenIDCookie();
 	static void onTeleportFinished();
@@ -162,6 +168,7 @@ class LLViewerMedia
 	static LLPluginCookieStore *sCookieStore;
 	static LLURL sOpenIDURL;
 	static std::string sOpenIDCookie;
+	static LLPluginClassMedia* sSpareBrowserMediaSource;
 };
 
 // Implementation functions not exported into header file
@@ -195,6 +202,9 @@ class LLViewerMediaImpl
 	LLPluginClassMedia* getMediaPlugin() { return mMediaSource; }
 	void setSize(int width, int height);
 
+	void showNotification(LLNotificationPtr notify);
+	void hideNotification();
+
 	void play();
 	void stop();
 	void pause();
@@ -387,6 +397,9 @@ class LLViewerMediaImpl
 	// Is this media in the agent's parcel?
 	bool isInAgentParcel() const;
 
+	// get currently active notification associated with this media instance
+	LLNotificationPtr getCurrentNotification() const;
+
 private:
 	bool isAutoPlayable() const;
 	bool shouldShowBasedOnClass() const;
@@ -444,7 +457,8 @@ class LLViewerMediaImpl
 	bool mNavigateSuspendedDeferred;
 	bool mTrustedBrowser;
 	std::string mTarget;
-	
+	LLNotificationPtr mNotification;
+
 private:
 	BOOL mIsUpdated ;
 	std::list< LLVOVolume* > mObjectList ;
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index e18b4ec414aac3557716cb6aa89ff66c1f41c9ea..9e16bf2fbb0fc362ceae59b1e2e7a523f4c3d07f 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -7223,6 +7223,12 @@ void handle_web_browser_test(const LLSD& param)
 	LLWeb::loadURLInternal(url);
 }
 
+void handle_web_content_test(const LLSD& param)
+{
+	std::string url = param.asString();
+	LLWeb::loadWebURLInternal(url);
+}
+
 void handle_buy_currency_test(void*)
 {
 	std::string url =
@@ -7973,7 +7979,8 @@ void initialize_menus()
 	view_listener_t::addMenu(new LLAdvancedDumpRegionObjectCache(), "Advanced.DumpRegionObjectCache");
 
 	// Advanced > UI
-	commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2));
+	commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test,	_2));	// sigh! this one opens the MEDIA browser
+	commit.add("Advanced.WebContentTest", boost::bind(&handle_web_content_test, _2));	// this one opens the Web Content floater
 	view_listener_t::addMenu(new LLAdvancedBuyCurrencyTest(), "Advanced.BuyCurrencyTest");
 	view_listener_t::addMenu(new LLAdvancedDumpSelectMgr(), "Advanced.DumpSelectMgr");
 	view_listener_t::addMenu(new LLAdvancedDumpInventory(), "Advanced.DumpInventory");
diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp
index b7be3bc5b3b679fb789a4c6c250a856ce22101f6..fda291f3c1ce9e29f0e70b44a5ba7c60469ba77f 100644
--- a/indra/newview/llviewermenufile.cpp
+++ b/indra/newview/llviewermenufile.cpp
@@ -404,6 +404,8 @@ class LLFileTakeSnapshotToDisk : public view_listener_t
 									   gSavedSettings.getBOOL("RenderUIInSnapshot"),
 									   FALSE))
 		{
+			gViewerWindow->playSnapshotAnimAndSound();
+			
 			LLPointer<LLImageFormatted> formatted;
 			switch(LLFloaterSnapshot::ESnapshotFormat(gSavedSettings.getS32("SnapshotFormat")))
 			{
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index b7f72a2e4cbb6d51a5834a6b883952847934af24..7dc5d96689d4449471ee7a736ca676137f444bf4 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -171,6 +171,31 @@ const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] =
 	FALSE	// ControlYourCamera
 };
 
+// Extract channel and version from a string like "SL Web Viewer Beta 10.11.29.215604".
+// (channel: "SL Web Viewer Beta", version: "10.11.29.215604")
+static bool parse_version_info(const std::string& version_info, std::string& channel, std::string& ver)
+{
+	size_t last_space = version_info.rfind(" ");
+	channel = version_info;
+
+	if (last_space != std::string::npos)
+	{
+		try
+		{
+			ver = version_info.substr(last_space + 1);
+			channel.replace(last_space, ver.length() + 1, ""); // strip version
+		}
+		catch (std::out_of_range)
+		{
+			return false;
+		}
+
+		return true;
+	}
+
+	return false;
+}
+
 bool friendship_offer_callback(const LLSD& notification, const LLSD& response)
 {
 	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
@@ -3825,28 +3850,22 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
 
 	if (!gLastVersionChannel.empty())
 	{
-		// work out the URL for this server's Release Notes
-		std::string url ="http://wiki.secondlife.com/wiki/Release_Notes/";
-		std::string server_version = version_channel;
-		std::vector<std::string> s_vect;
-		boost::algorithm::split(s_vect, server_version, isspace);
-		for(U32 i = 0; i < s_vect.size(); i++)
+		std::string url = regionp->getCapability("ServerReleaseNotes");
+		if (url.empty())
 		{
-			if (i != (s_vect.size() - 1))
+			// The capability hasn't arrived yet or is not supported,
+			// fall back to parsing server version channel.
+			std::string channel, ver;
+			if (!parse_version_info(version_channel, channel, ver))
 			{
-				if(i != (s_vect.size() - 2))
-				{
-				   url += s_vect[i] + "_";
-				}
-				else
-				{
-					url += s_vect[i] + "/";
-				}
-			}
-			else
-			{
-				url += s_vect[i].substr(0,4);
+				llwarns << "Failed to parse server version channel (" << version_channel << ")" << llendl;
 			}
+
+			url = gSavedSettings.getString("ReleaseNotesURL");
+			LLSD args;
+			args["CHANNEL"] = LLWeb::escapeURL(channel);
+			args["VERSION"] = LLWeb::escapeURL(ver);
+			LLStringUtil::format(url, args);
 		}
 
 		LLSD args;
@@ -6306,6 +6325,9 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response)
 				payload["from_id"] = target_id;
 				payload["SUPPRESS_TOAST"] = true;
 				LLNotificationsUtil::add("TeleportOfferSent", args, payload);
+
+				// Add the recepient to the recent people list.
+				LLRecentPeople::instance().add(target_id);
 			}
 		}
 		gAgent.sendReliableMessage();
diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp
index 99e869dafc153ad29347b5dbfbdde184920b1987..40f0b433132389ca31151802c440b2e80fdfd1b8 100644
--- a/indra/newview/llviewerparcelmedia.cpp
+++ b/indra/newview/llviewerparcelmedia.cpp
@@ -586,6 +586,18 @@ void LLViewerParcelMedia::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent
 			LL_DEBUGS("Media") << "Media event:  MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID() << LL_ENDL;
 		}
 		break;
+
+		case MEDIA_EVENT_AUTH_REQUEST:
+		{
+			LL_DEBUGS("Media") <<  "Media event:  MEDIA_EVENT_AUTH_REQUEST, url " << self->getAuthURL() << ", realm " << self->getAuthRealm() << LL_ENDL;
+		}
+		break;
+
+		case MEDIA_EVENT_LINK_HOVERED:
+		{
+			LL_DEBUGS("Media") <<  "Media event:  MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << LL_ENDL;
+		};
+		break;
 	};
 }
 
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index ca07e7c4cf1886ecb7979e04af7e84c954b7949a..551ba18dd549cbd5aa92ec01460d6a6915850c44 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -1414,6 +1414,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
 	capabilityNames.append("UpdateNotecardTaskInventory");
 	capabilityNames.append("UpdateScriptTask");
 	capabilityNames.append("UploadBakedTexture");
+	capabilityNames.append("ViewerMetrics");
 	capabilityNames.append("ViewerStartAuction");
 	capabilityNames.append("ViewerStats");
 	capabilityNames.append("WebFetchInventoryDescendents");
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index b8fd9443218823139ba8959c99228b100b4e7e37..5eeb02b080409719e36de2205151a313fc430e13 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -186,11 +186,6 @@ class LLViewerWindow : public LLWindowCallbacks
 	/*virtual*/ std::string translateString(const char* tag,
 					const std::map<std::string, std::string>& args);
 	
-	// signal on bottom tray width changed
-	typedef boost::function<void (void)> bottom_tray_callback_t;
-	typedef boost::signals2::signal<void (void)> bottom_tray_signal_t;
-	bottom_tray_signal_t mOnBottomTrayWidthChanged;
-	boost::signals2::connection setOnBottomTrayWidthChanged(bottom_tray_callback_t cb) { return mOnBottomTrayWidthChanged.connect(cb); }
 	// signal on update of WorldView rect
 	typedef boost::function<void (LLRect old_world_rect, LLRect new_world_rect)> world_rect_callback_t;
 	typedef boost::signals2::signal<void (LLRect old_world_rect, LLRect new_world_rect)> world_rect_signal_t;
diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp
index 6028a8fbeac8f18057360e628806eed94e4a2f8d..b73017a51a2141cb525a1001e35c4bdfaa29152b 100644
--- a/indra/newview/llweb.cpp
+++ b/indra/newview/llweb.cpp
@@ -35,6 +35,7 @@
 #include "llagent.h"
 #include "llappviewer.h"
 #include "llfloatermediabrowser.h"
+#include "llfloaterwebcontent.h"
 #include "llfloaterreg.h"
 #include "lllogininstance.h"
 #include "llparcel.h"
@@ -95,6 +96,23 @@ void LLWeb::loadURL(const std::string& url, const std::string& target, const std
 	}
 }
 
+// static
+void LLWeb::loadWebURL(const std::string& url, const std::string& target, const std::string& uuid)
+{
+	if(target == "_internal")
+	{
+		// Force load in the internal browser, as if with a blank target.
+		loadWebURLInternal(url, "", uuid);
+	}
+	else if (gSavedSettings.getBOOL("UseExternalBrowser") || (target == "_external"))
+	{
+		loadURLExternal(url);
+	}
+	else
+	{
+		loadWebURLInternal(url, target, uuid);
+	}
+}
 
 // static
 void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid)
@@ -102,6 +120,13 @@ void LLWeb::loadURLInternal(const std::string &url, const std::string& target, c
 	LLFloaterMediaBrowser::create(url, target, uuid);
 }
 
+// static
+// Explicitly open a Web URL using the Web content floater
+void LLWeb::loadWebURLInternal(const std::string &url, const std::string& target, const std::string& uuid)
+{
+	LLFloaterWebContent::create(url, target, uuid);
+}
+
 
 // static
 void LLWeb::loadURLExternal(const std::string& url, const std::string& uuid)
diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h
index 291537658329e4c7f4f80fe2a012010764bba407..dc5958e57fb26cfdedc7e7fc38ec5489471ec0e7 100644
--- a/indra/newview/llweb.h
+++ b/indra/newview/llweb.h
@@ -57,6 +57,11 @@ class LLWeb
 	static void loadURLExternal(const std::string& url, const std::string& uuid);
 	static void loadURLExternal(const std::string& url, bool async, const std::string& uuid = LLStringUtil::null);
 
+	// Explicitly open a Web URL using the Web content floater vs. the more general media browser
+	static void loadWebURL(const std::string& url, const std::string& target, const std::string& uuid);
+	static void loadWebURLInternal(const std::string &url, const std::string& target, const std::string& uuid);
+	static void loadWebURLInternal(const std::string &url) { loadWebURLInternal(url, LLStringUtil::null, LLStringUtil::null); }
+
 	/// Returns escaped url (eg, " " to "%20") - used by all loadURL methods
 	static std::string escapeURL(const std::string& url);
 	/// Expands various strings like [LANG], [VERSION], etc. in a URL
diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp
index be8298daab936af4d571a831640154e2f4b6f6cc..74ed844376e2fb58e2382ae82cfa1fddb38d15a8 100644
--- a/indra/newview/llworldmipmap.cpp
+++ b/indra/newview/llworldmipmap.cpp
@@ -181,8 +181,7 @@ LLPointer<LLViewerFetchedTexture> LLWorldMipmap::getObjectsTile(U32 grid_x, U32
 LLPointer<LLViewerFetchedTexture> LLWorldMipmap::loadObjectsTile(U32 grid_x, U32 grid_y, S32 level)
 {
 	// Get the grid coordinates
-	std::string imageurl = gSavedSettings.getString("MapServerURL") + llformat("map-%d-%d-%d-objects.jpg", level, grid_x, grid_y);
-
+	std::string imageurl = gSavedSettings.getString("CurrentMapServerURL") + llformat("map-%d-%d-%d-objects.jpg", level, grid_x, grid_y);
 
 	// DO NOT COMMIT!! DEBUG ONLY!!!
 	// Use a local jpeg for every tile to test map speed without S3 access
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index a51a096482da2b0aa6259dc1f9848323294ef49d..89611d8899d35a535d1aed8c3009cad078b9b3d8 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -392,7 +392,7 @@ with the same filename but different name
   <texture name="RadioButton_On_Disabled" file_name="widgets/RadioButton_On_Disabled.png" preload="true" />
 
 
-  <texture name="Refresh_Off" file_name="icons/Refresh_Off.png" preload="false" />
+  <texture name="Refresh_Off" file_name="icons/Refresh_Off.png" preload="true" />
 
   <texture name="Resize_Corner" file_name="windows/Resize_Corner.png" preload="true" />
 
@@ -468,7 +468,7 @@ with the same filename but different name
   <texture name="Stepper_Up_Off" file_name="widgets/Stepper_Up_Off.png" preload="false" />
   <texture name="Stepper_Up_Press" file_name="widgets/Stepper_Up_Press.png" preload="false" />
 
-  <texture name="Stop_Off" file_name="icons/Stop_Off.png" preload="false" />
+  <texture name="Stop_Off" file_name="icons/Stop_Off.png" preload="true" />
   <texture name="StopReload_Off" file_name="icons/StopReload_Off.png" preload="false" />
   <texture name="StopReload_Over" file_name="icons/StopReload_Over.png" preload="false" />
 
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 837923bcf606c48dce7a36eb858eda8d2e36e324..02e50ee5844931593ad4894d0777b9f2ceff68c8 100644
--- a/indra/newview/skins/default/xui/en/floater_help_browser.xml
+++ b/indra/newview/skins/default/xui/en/floater_help_browser.xml
@@ -36,7 +36,8 @@
          user_resize="false"
          width="620">
             <web_browser
-              trusted_content="true" 
+             trusted_content="true" 
+             initial_mime_type="text/html" 
              bottom="-25"
              follows="left|right|top|bottom"
              layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/floater_media_browser.xml b/indra/newview/skins/default/xui/en/floater_media_browser.xml
index 49e835cce4d8cb1b79b7dcc3331ee8f19d8fddad..43729d7c9fd0da284a6a19b7e50fb6932f270cc9 100644
--- a/indra/newview/skins/default/xui/en/floater_media_browser.xml
+++ b/indra/newview/skins/default/xui/en/floater_media_browser.xml
@@ -101,7 +101,7 @@
              left_pad="5"
              name="go"
              top_delta="0"
-             width="55">
+             width="50">
 				<button.commit_callback
 				function="MediaBrowser.Go" />
 			</button>
diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2ad46824c25def60bfef10ca725e16947a4184c4
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_web_content.xml
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+  legacy_header_height="18"
+  can_resize="true"
+  height="440"
+  layout="topleft"
+  min_height="140"
+  min_width="467"
+  name="floater_web_content"
+  help_topic="floater_web_content"
+  save_rect="true"
+  auto_tile="true"
+  title=""
+  initial_mime_type="text/html"
+  width="820">
+  <layout_stack
+    bottom="440"
+    follows="left|right|top|bottom"
+    layout="topleft"
+    left="5"
+    name="stack1"
+    orientation="vertical"
+    top="20"
+    width="810">
+    <layout_panel
+      auto_resize="false"
+      default_tab_group="1"
+      height="22"
+      layout="topleft"
+      left="0"
+      min_height="20"
+      name="nav_controls"
+      top="400"
+      user_resize="false"
+      width="800">
+      <button
+        image_overlay="Arrow_Left_Off"
+		    image_disabled="PushButton_Disabled"
+		    image_disabled_selected="PushButton_Disabled"
+		    image_selected="PushButton_Selected"
+		    image_unselected="PushButton_Off"
+		    hover_glow_amount="0.15"
+        tool_tip="Navigate back"
+        follows="left|top"
+        height="22"
+        layout="topleft"
+        left="1"
+        name="back"
+        top="0"
+        width="22">
+        <button.commit_callback
+          function="WebContent.Back" />
+      </button>
+      <button
+        image_overlay="Arrow_Right_Off"
+		    image_disabled="PushButton_Disabled"
+		    image_disabled_selected="PushButton_Disabled"
+		    image_selected="PushButton_Selected"
+		    image_unselected="PushButton_Off"
+        tool_tip="Navigate forward"
+        follows="left|top"
+        height="22"
+        layout="topleft"
+        left="27"
+        name="forward"
+        top_delta="0"
+        width="22">
+        <button.commit_callback
+          function="WebContent.Forward" />
+      </button>
+      <button
+        image_overlay="Stop_Off"
+		    image_disabled="PushButton_Disabled"
+		    image_disabled_selected="PushButton_Disabled"
+		    image_selected="PushButton_Selected"
+		    image_unselected="PushButton_Off"
+        tool_tip="Stop navigation"
+        enabled="true"
+        follows="left|top"
+        height="22"
+        layout="topleft"
+        left="51"
+        name="stop"
+        top_delta="0"
+        width="22">
+        <button.commit_callback
+          function="WebContent.Stop" />
+      </button>
+      <button
+        image_overlay="Refresh_Off"
+		    image_disabled="PushButton_Disabled"
+		    image_disabled_selected="PushButton_Disabled"
+		    image_selected="PushButton_Selected"
+		    image_unselected="PushButton_Off"
+        tool_tip="Reload page"
+        follows="left|top"
+        height="22"
+        layout="topleft"
+        left="51"
+        name="reload"
+        top_delta="0"
+        width="22">
+        <button.commit_callback
+          function="WebContent.Reload" />
+      </button>
+      <combo_box
+        allow_text_entry="true"
+        follows="left|top|right"
+        tab_group="1"
+        height="22"
+        layout="topleft"
+        left_pad="4"
+        max_chars="1024"
+        name="address"
+        combo_editor.select_on_focus="true"
+        tool_tip="Enter URL here"
+        top_delta="0"
+        width="702">
+        <combo_box.commit_callback
+          function="WebContent.EnterAddress" />
+      </combo_box>
+      <icon
+        name="media_secure_lock_flag"
+        height="16"
+        follows="top|right"
+        image_name="Lock2"
+        layout="topleft"
+        left_delta="656"
+        top_delta="2"
+        visible="false" 
+        tool_tip="Secured Browsing"
+        width="16" />
+      <button
+        image_overlay="ExternalBrowser_Off"
+		    image_disabled="PushButton_Disabled"
+		    image_disabled_selected="PushButton_Disabled"
+		    image_selected="PushButton_Selected"
+		    image_unselected="PushButton_Off"
+        tool_tip="Open current URL in your desktop browser"
+        follows="right|top"
+        enabled="true" 
+        height="22"
+        layout="topleft"
+        name="popexternal"
+        right="800"
+        top_delta="-2"
+        width="22">
+        <button.commit_callback
+          function="WebContent.PopExternal" />
+      </button>
+    </layout_panel>
+    <layout_panel
+      height="40"
+      layout="topleft"
+      left_delta="0"
+      name="external_controls"
+      top_delta="0"
+      user_resize="false"
+      width="540">
+      <web_browser
+        bottom="-22"
+        follows="all"
+        layout="topleft"
+        left="0"
+        name="webbrowser"
+        top="0"/>
+      <text
+        type="string"
+        length="100"
+        follows="bottom|left"
+        height="20"
+        layout="topleft"
+        left_delta="0"
+        name="statusbartext"
+        parse_urls="false"
+        text_color="0.4 0.4 0.4 1" 
+        top_pad="5"
+        width="452"/>
+      <progress_bar
+        color_bar="0.3 1.0 0.3 1"
+        follows="bottom|right"
+        height="16"
+        top_delta="-1"
+        left_pad="24"
+        layout="topleft"
+        name="statusbarprogress"
+        width="64"/>
+    </layout_panel>
+  </layout_stack>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_windlight_options.xml b/indra/newview/skins/default/xui/en/floater_windlight_options.xml
index 85a5be369c45b0d5aac1657bf4dfb42dc663ec2f..249ad95c41d1fc97630427f85315aae98999cdfc 100644
--- a/indra/newview/skins/default/xui/en/floater_windlight_options.xml
+++ b/indra/newview/skins/default/xui/en/floater_windlight_options.xml
@@ -594,6 +594,7 @@
              left_delta="14"
              top_pad="10"
              name="SkyDayCycle"
+             use_draw_context_alpha="false"
              width="148" />
             <slider
              control_name="WLSunAngle"
diff --git a/indra/newview/skins/default/xui/en/inspect_object.xml b/indra/newview/skins/default/xui/en/inspect_object.xml
index eb2e7ea7887fc79476e31c2234d38c9653cfb1b7..8d14c974b434b4ddef43e3b0a49a95732cbea132 100644
--- a/indra/newview/skins/default/xui/en/inspect_object.xml
+++ b/indra/newview/skins/default/xui/en/inspect_object.xml
@@ -76,13 +76,24 @@ L$30,000
   </text>
   <!-- Overlapping buttons for all default actions.  Show "Buy" if
   for sale, "Sit" if can sit, etc. -->
+  <icon
+   name="secure_browsing"
+   image_name="Lock"
+   left="0"
+   visible="false"
+   width="18"
+   height="18"
+   top="103"
+   tool_tip="Secure Browsing"
+   follows="left|top" />
    <text
    follows="all"
    font="SansSerifSmall"
    height="13"
    name="object_media_url"
-   width="220"
-   top_pad="0"
+   width="207"
+   left_pad="2"
+   top_delta="0"
    max_length = "50"
    use_ellipses="true">
    http://www.superdupertest.com
@@ -135,16 +146,6 @@ L$30,000
    name="open_btn"
    top_delta="0"
    width="80" />
-  <icon
-   name="secure_browsing"
-   image_name="Lock"
-   left_delta="80"
-   visible="false"
-   width="18"
-   height="18"
-   top_delta="0"
-   tool_tip="Secure Browsing"
-   follows="left|top" />
 
  <!--  non-overlapping buttons here -->
      <button
@@ -153,7 +154,7 @@ L$30,000
      label="More"
      layout="topleft"
      name="more_info_btn"
-     left_delta="10"
+     left_pad="10"
      top_delta="0"
      tab_stop="false"
      width="80" />
diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml
index 4f982cc8e9a6c69993754e6fc333b5a3d61d105f..0d4a095e14d4cb0c1ebf4241d7ac8ed1b33ffb00 100644
--- a/indra/newview/skins/default/xui/en/menu_login.xml
+++ b/indra/newview/skins/default/xui/en/menu_login.xml
@@ -176,13 +176,19 @@
              parameter="message_critical" />
         </menu_item_call>
         <menu_item_call
-         label="Web Browser Test"
+         label="Media Browser Test"
          name="Web Browser Test">
           <menu_item_call.on_click
            function="Advanced.WebBrowserTest"
            parameter="http://join.secondlife.com/"/>
         </menu_item_call>
-      <menu_item_separator/>
+      <menu_item_call
+       label="Web Content Floater Test"
+       name="Web Content Floater Test">
+        <menu_item_call.on_click
+         function="Advanced.WebContentTest"
+         parameter="http://www.google.com"/>
+      </menu_item_call>
       <menu_item_check
         label="Show Grid Picker"
         name="Show Grid Picker"
diff --git a/indra/newview/skins/default/xui/en/menu_mini_map.xml b/indra/newview/skins/default/xui/en/menu_mini_map.xml
index 8fe89d39343b3b75139bd74ca3ef921f7f4054e4..ea263d05ceff687978d60373687b44d8721e9c21 100644
--- a/indra/newview/skins/default/xui/en/menu_mini_map.xml
+++ b/indra/newview/skins/default/xui/en/menu_mini_map.xml
@@ -8,7 +8,7 @@
  top="724"
  visible="false"
  width="128">
-    <menu_item_call
+	<menu_item_call
      label="Zoom Close"
      name="Zoom Close">
         <menu_item_call.on_click
@@ -29,7 +29,14 @@
          function="Minimap.Zoom"
          parameter="far" />
     </menu_item_call>
-    <menu_item_separator />
+	<menu_item_call
+     label="Zoom Default"
+     name="Zoom Default">
+		<menu_item_call.on_click
+         function="Minimap.Zoom"
+         parameter="default" />
+	</menu_item_call>
+	<menu_item_separator />
     <menu_item_check
        label="Rotate Map"
        name="Rotate Map">
diff --git a/indra/newview/skins/default/xui/en/menu_place.xml b/indra/newview/skins/default/xui/en/menu_place.xml
index 1b96eb51f0ca2c6811bbafba0998a23e15274fbf..288811d2f6b020f3e1ee8aa5bf5d10748ad2b918 100644
--- a/indra/newview/skins/default/xui/en/menu_place.xml
+++ b/indra/newview/skins/default/xui/en/menu_place.xml
@@ -24,26 +24,4 @@
          function="Places.OverflowMenu.Enable"
          parameter="can_create_pick" />
     </menu_item_call>
-    <menu_item_separator
-     layout="topleft"/>
-    <menu_item_call
-     enabled="false"
-     label="Buy Pass"
-     layout="topleft"
-     name="pass">
-        <menu_item_call.on_click
-         function="Places.OverflowMenu.Action"
-         parameter="pass" />
-    </menu_item_call>
-    <menu_item_separator
-     layout="topleft"/>
-    <menu_item_call
-     enabled="false"
-     label="Edit"
-     layout="topleft"
-     name="edit">
-        <menu_item_call.on_click
-         function="Places.OverflowMenu.Action"
-         parameter="edit" />
-    </menu_item_call>
 </toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 907f68dc06e6d4c8e008131fc9902d0cee2b7c15..d997a262a820dae84ff8332bbf921cf10661a09a 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -976,6 +976,29 @@
                  parameter="perm_prefs" />
             </menu_item_call>
         </menu>
+        <menu_item_separator/>
+        <menu_item_call
+         enabled="false"
+         label="Undo"
+         name="Undo"
+         shortcut="control|Z">
+            <on_click
+             function="Edit.Undo"
+             userdata="" />
+            <on_enable
+             function="Edit.EnableUndo" />
+        </menu_item_call>
+        <menu_item_call
+         enabled="false"
+         label="Redo"
+         name="Redo"
+         shortcut="control|Y">
+            <on_click
+             function="Edit.Redo"
+             userdata="" />
+            <on_enable
+             function="Edit.EnableRedo" />
+        </menu_item_call>        
     </menu>
     <menu
      create_jump_keys="true"
@@ -2623,13 +2646,21 @@
                  parameter="BottomPanelNew" />
             </menu_item_check>-->
             <menu_item_call
-             label="Web Browser Test"
+             label="Media Browser Test"
              name="Web Browser Test">
                 <menu_item_call.on_click
                  function="Advanced.WebBrowserTest"
                  parameter="http://secondlife.com/app/search/slurls.html"/>
             </menu_item_call>
-            <menu_item_call
+          <menu_item_call
+           label="Web Content Browser"
+           name="Web Content Browser"
+           shortcut="control|alt|W">
+            <menu_item_call.on_click
+             function="Advanced.WebContentTest"
+             parameter="http://google.com"/>
+          </menu_item_call>
+          <menu_item_call
              label="Dump SelectMgr"
              name="Dump SelectMgr">
                 <menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index b1fd579c6fda57b86748c665230b33aa56211d93..63c030b0687cd85f33fdadfe9b4f57ab400e77f7 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -2901,12 +2901,80 @@ http://secondlife.com/download.
      name="okbutton"
      yestext="OK"/>
   </notification>
+
   <notification
-   icon="notifytip.tga"
-   name="DownloadBackground"
-   type="notifytip">
-An updated version of [APP_NAME] has been downloaded.
-It will be applied the next time you restart [APP_NAME]
+   icon="alertmodal.tga"
+   name="FailedRequiredUpdateInstall"
+   type="alertmodal">
+We were unable to install a required update. 
+You will be unable to log in until [APP_NAME] has been updated.
+
+Please download and install the latest viewer from
+http://secondlife.com/download.
+    <usetemplate
+     name="okbutton"
+     yestext="Quit"/>
+  </notification>
+
+  <notification
+   icon="alertmodal.tga"
+   name="UpdaterServiceNotRunning"
+   type="alertmodal">
+There is a required update for your Second Life Installation.
+
+You may download this update from http://www.secondlife.com/downloads
+or you can install it now.
+    <usetemplate
+     name="okcancelbuttons"
+     notext="Quit Second Life"
+     yestext="Download and install now"/>
+  </notification>
+
+  <notification
+   icon="notify.tga"
+   name="DownloadBackgroundTip"
+   type="notify">
+We have downloaded an update to your [APP_NAME] installation.
+Version [VERSION] [[RELEASE_NOTES_FULL_URL] Information about this update]
+    <usetemplate
+     name="okcancelbuttons"
+     notext="Later..."
+     yestext="Install now and restart [APP_NAME]"/>
+  </notification>
+
+  <notification
+ icon="alertmodal.tga"
+ name="DownloadBackgroundDialog"
+ type="alertmodal">
+We have downloaded an update to your [APP_NAME] installation.
+Version [VERSION] [[RELEASE_NOTES_FULL_URL] Information about this update]
+    <usetemplate
+     name="okcancelbuttons"
+     notext="Later..."
+     yestext="Install now and restart [APP_NAME]"/>
+  </notification>
+  
+  <notification
+ icon="alertmodal.tga"
+ name="RequiredUpdateDownloadedVerboseDialog"
+ type="alertmodal">
+We have downloaded a required software update.
+Version [VERSION]
+
+We must restart [APP_NAME] to install the update.
+    <usetemplate
+     name="okbutton"
+     yestext="OK"/>
+  </notification>
+  
+  <notification
+ icon="alertmodal.tga"
+ name="RequiredUpdateDownloadedDialog"
+ type="alertmodal">
+We must restart [APP_NAME] to install the update.
+    <usetemplate
+     name="okbutton"
+     yestext="OK"/>
   </notification>
 
   <notification
@@ -5014,7 +5082,7 @@ If you want to view streaming media on parcels that support it you should go to
    type="notify">
 No Media Plugin was found to handle the "[MIME_TYPE]" mime type.  Media of this type will be unavailable.
     <unique>
-      <context key="[MIME_TYPE]"/>
+      <context>MIME_TYPE</context>
     </unique>
 
   </notification>
@@ -5943,7 +6011,7 @@ You may only select up to [MAX_SELECT] items from this list.
 [NAME] is inviting you to a Voice Chat call.
 Click Accept to join the call or Decline to decline the invitation. Click Block to block this caller.
     <unique>
-      <context key="NAME"/>
+      <context>NAME</context>
     </unique>
     <form name="form">
       <button
@@ -5992,8 +6060,8 @@ Click Accept to join the call or Decline to decline the invitation. Click Block
 [NAME] has joined a Voice Chat call with the group [GROUP].
 Click Accept to join the call or Decline to decline the invitation. Click Block to block this caller.
     <unique>
-      <context key="NAME"/>
-      <context key="GROUP"/>
+      <context>NAME</context>
+      <context>GROUP</context>
     </unique>
     <form name="form">
       <button
@@ -6018,7 +6086,7 @@ Click Accept to join the call or Decline to decline the invitation. Click Block
 [NAME] has joined a voice chat call with a conference chat.
 Click Accept to join the call or Decline to decline the invitation. Click Block to block this caller.
     <unique>
-      <context key="NAME"/>
+      <context>NAME</context>
     </unique>
     <form name="form">
       <button
@@ -6043,7 +6111,7 @@ Click Accept to join the call or Decline to decline the invitation. Click Block
 [NAME] is inviting you to a conference chat.
 Click Accept to join the chat or Decline to decline the invitation. Click Block to block this caller.
     <unique>
-      <context key="NAME"/>
+      <context>NAME</context>
     </unique>
     <form name="form">
       <button
@@ -6067,7 +6135,7 @@ Click Accept to join the chat or Decline to decline the invitation. Click Block
    type="notifytip">
 The voice call you are trying to join, [VOICE_CHANNEL_NAME], has reached maximum capacity. Please try again later.
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6085,7 +6153,7 @@ We&apos;re sorry.  This area has reached maximum capacity for voice conversation
    type="notifytip">
 You have been disconnected from [VOICE_CHANNEL_NAME].  You will now be reconnected to Nearby Voice Chat.
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6095,7 +6163,7 @@ You have been disconnected from [VOICE_CHANNEL_NAME].  You will now be reconnect
    type="notifytip">
 [VOICE_CHANNEL_NAME] has ended the call.  You will now be reconnected to Nearby Voice Chat.
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6105,7 +6173,7 @@ You have been disconnected from [VOICE_CHANNEL_NAME].  You will now be reconnect
    type="notifytip">
 [VOICE_CHANNEL_NAME] has declined your call.  You will now be reconnected to Nearby Voice Chat.
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6115,7 +6183,7 @@ You have been disconnected from [VOICE_CHANNEL_NAME].  You will now be reconnect
    type="notifytip">
 [VOICE_CHANNEL_NAME] is not available to take your call.  You will now be reconnected to Nearby Voice Chat.
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6125,7 +6193,7 @@ You have been disconnected from [VOICE_CHANNEL_NAME].  You will now be reconnect
    type="notifytip">
 Failed to connect to [VOICE_CHANNEL_NAME], please try again later.  You will now be reconnected to Nearby Voice Chat.
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6211,7 +6279,7 @@ Cannot enter parcel, you are not on the access list.
    type="notifytip">
 You do not have permission to connect to voice chat for [VOICE_CHANNEL_NAME].
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6221,7 +6289,7 @@ You do not have permission to connect to voice chat for [VOICE_CHANNEL_NAME].
    type="notifytip">
 An error has occurred while trying to connect to voice chat for [VOICE_CHANNEL_NAME].  Please try again later.
     <unique>
-      <context key="VOICE_CHANNEL_NAME"/>
+      <context>VOICE_CHANNEL_NAME</context>
     </unique>
   </notification>
 
@@ -6628,6 +6696,23 @@ Mute everyone?
     </form>
   </notification>
 
+  <notification
+  name="AuthRequest"
+  type="browser">
+The site at &apos;&lt;nolink&gt;[HOST_NAME]&lt;/nolink&gt;&apos; in realm &apos;[REALM]&apos; requires a user name and password.
+    <form name="form">
+      <input name="username" type="text" text="User Name"/>
+      <input name="password" type="password" text="Password    "/>
+      <button default="true"
+              index="0"
+              name="ok"
+              text="Submit"/>
+      <button index="1"
+              name="cancel"
+              text="Cancel"/>
+    </form>
+  </notification>
+
   
   <global name="UnsupportedCPU">
 - Your CPU speed does not meet the minimum requirements.
diff --git a/indra/newview/skins/default/xui/en/panel_group_invite.xml b/indra/newview/skins/default/xui/en/panel_group_invite.xml
index 15a3191bdfcb48e43ffe7a54274040e3254c4fcc..cd834b61ce61230dd06315d08cc65502696aaac7 100644
--- a/indra/newview/skins/default/xui/en/panel_group_invite.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_invite.xml
@@ -94,7 +94,7 @@
      left_pad="2"
      name="cancel_button"
      top_delta="0"
-     width="70" />
+     width="65" />
 	 <string 
 	 name="GroupInvitation">
 	 Group Invitation
diff --git a/indra/newview/skins/default/xui/en/panel_group_land_money.xml b/indra/newview/skins/default/xui/en/panel_group_land_money.xml
index 1270a21710bfafc03a7b9c1be88f51588cc414e8..61d6cbb2d0e1c0d0936a5a14eb7c6ff9897706f0 100644
--- a/indra/newview/skins/default/xui/en/panel_group_land_money.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_land_money.xml
@@ -117,7 +117,7 @@
      name="map_button"
      top_delta="-4"
      left_pad="0"
-     width="60"
+     width="57"
      enabled="false" />
     <text
      type="string"
diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml
index e3cd61c5aa02258ce6d7eddbf403d170df38d006..257ed799da3e32f8971dc89f35c01b92e3c5a5a2 100644
--- a/indra/newview/skins/default/xui/en/panel_login.xml
+++ b/indra/newview/skins/default/xui/en/panel_login.xml
@@ -12,11 +12,7 @@ top="600"
      name="create_account_url">
        http://join.secondlife.com/
 </panel.string>
-<panel.string
-     name="real_url" translate="false">
-       http://secondlife.com/app/login/
-</panel.string>
-    <string name="reg_in_client_url" translate="false">
+<string name="reg_in_client_url" translate="false">
      http://secondlife.eniac15.lindenlab.com/reg-in-client/
 </string>
 <panel.string
@@ -92,6 +88,7 @@ follows="left|bottom"
   height="22"
   max_length_bytes="16"
 name="password_edit"
+is_password="true" 
 select_on_focus="true"
   top_pad="0"
   width="135" />
diff --git a/indra/newview/skins/default/xui/en/panel_my_profile.xml b/indra/newview/skins/default/xui/en/panel_my_profile.xml
index 1b41f602cd59e3a2bee32dd4742308efab218c1b..5b8abaca6f1b7467f72603bf44805969da26f482 100644
--- a/indra/newview/skins/default/xui/en/panel_my_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_my_profile.xml
@@ -185,7 +185,7 @@
               </expandable_text>
             </panel>
             <text
-             follows="left|top"
+             follows="left|top|right"
              height="15"
        font.style="BOLD"
        font="SansSerifMedium"
@@ -200,7 +200,7 @@
              use_ellipses="true"
          />
             <text
-             follows="left|top"
+             follows="left|top|right"
            font.style="BOLD"
              height="10"
              layout="topleft"
@@ -213,7 +213,7 @@
             <text_editor
              allow_scroll="false"
              bg_visible="false"
-             follows="left|top"
+             follows="left|top|right"
              h_pad="0"
              height="15"
              layout="topleft"
@@ -226,7 +226,7 @@
              width="300"
              word_wrap="true" />
             <text
-             follows="left|top"
+             follows="left|top|right"
        font.style="BOLD"
              height="15"
              layout="topleft"
@@ -250,7 +250,7 @@
             <text_editor
             allow_scroll="false"
             bg_visible="false"
-            follows="left|top"
+            follows="left|top|right"
             h_pad="0"
             height="28"
             layout="topleft"
@@ -266,7 +266,7 @@
               Linden.
             </text_editor>
             <text
-             follows="left|top"
+             follows="left|top|right"
        font.style="BOLD"
              height="15"
              layout="topleft"
@@ -277,7 +277,7 @@
              value="Partner:"
              width="300" />
             <panel
-             follows="left|top"
+             follows="left|top|right"
              height="15"
              layout="topleft"
              left="10"
@@ -285,7 +285,7 @@
              top_pad="0"
              width="300">
               <text
-               follows="left|top"
+               follows="left|top|right"
                height="10"
                initial_value="(retrieving)"
                layout="topleft"
@@ -297,7 +297,7 @@
            width="300" />
             </panel>
             <text
-             follows="left|top"
+             follows="left|top|right"
        font.style="BOLD"
              height="13"
              layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/panel_notify_textbox.xml b/indra/newview/skins/default/xui/en/panel_notify_textbox.xml
index 4634eeed4676ef840885b56894568c11d8bc0250..d5b6057233086b3289d54b5e68ebc8830547974d 100644
--- a/indra/newview/skins/default/xui/en/panel_notify_textbox.xml
+++ b/indra/newview/skins/default/xui/en/panel_notify_textbox.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <panel
    background_visible="true"
-   height="230"
+   height="220"
    label="instant_message"
    layout="topleft"
    left="0"
@@ -14,55 +14,81 @@
   <panel
    bevel_style="none"
    follows="left|right|top"
-   height="150"
+   height="185"
    label="info_panel"
    layout="topleft"
    left="0"
    name="info_panel"
    top="0"
-   width="305">
+   width="305">    
+    <text_editor
+     bg_readonly_color="0.0 0.0 0.0 0"
+     enabled="false"
+     follows="left|right|top|bottom"
+     font="SansSerif"
+     height="110" 
+     layout="topleft"
+     left="10"
+     mouse_opaque="false"
+     name="text_editor_box"
+     read_only="true"
+     text_color="white"
+     text_readonly_color="white"
+     top="10"
+     width="285"
+     wrap="true"
+     parse_highlights="true"
+     parse_urls="true"/>
     <text_editor
      parse_urls="true"
      enabled="true"
      follows="all"
-     height="60"
+     height="50"
      layout="topleft"
-     left="25"
+     left="10"
      max_length="250"
      name="message"
      parse_highlights="true"
      read_only="false"
-     top="40"
+     top_pad="10"
      type="string"
      use_ellipses="true"
      value="message"
-     width="260"
-     word_wrap="true" >
+     width="285"
+     word_wrap="true"
+     parse_url="false" >
     </text_editor>
-     parse_urls="false"
-    <button
-     top="110"
-     follows="top|left"
-     height="20"
-     label="Submit"
-     layout="topleft"
-     left="25"
-     name="btn_submit"
-     width="70" />
   </panel>
   <panel
      background_visible="false"
      follows="left|right|bottom"
-     height="0" 
+     height="25" 
      width="290"
      label="control_panel"
      layout="topleft"
      left="10"
      name="control_panel"
-     top_pad="5">
+     top_pad="0">
     <!-- 
 	 Notes:
 	 This panel holds the Ignore button and possibly other buttons of notification.
       -->
+    <button
+     top="0"
+     follows="top|left"
+     height="20"
+     label="Submit"
+     layout="topleft"
+     name="btn_submit"
+     width="70" />
+    <button
+     follows="top|right"
+     height="20"
+     label="Ignore"
+     layout="topleft"
+     left="215"
+     name="ignore_btn"
+     top="0"
+     width="70" />
   </panel>
 </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
index d6e4c5611363e08b7b435a383db2a0737b1c93e7..37aab059a93b2cb85a603c40189b39384be8740a 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml
@@ -82,7 +82,7 @@
      control_name="AllowMultipleViewers"
      follows="top|left"
      height="15"
-     label="Allow Multiple Viewer"
+     label="Allow Multiple Viewers"
      layout="topleft"
      left="30"
      name="allow_multiple_viewer_check"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
index 584bd1ea9d88430cd70301cf557b9570287739c0..901a1257e08104ddb80dfc182ccb6d17666ef796 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
@@ -142,7 +142,7 @@
    layout="topleft"
    left="80"
    name="Cache location"
-   top_delta="40"
+   top_delta="20"
    width="300">
     Cache location:
   </text>
@@ -341,20 +341,41 @@
    name="web_proxy_port"
    top_delta="0"
    width="145" />
-
-  <check_box
-    top_delta="2"
-    enabled="true"
-    follows="left|top"
-    height="18"
-    initial_value="true"
-    control_name="UpdaterServiceActive"
-    label="Automatically download and install [APP_NAME] updates"
-    left="30"
-    mouse_opaque="true"
-    name="updater_service_active"
-    radio_style="false"
-    width="400"
-    top_pad="10"/>
-
+  <text
+     type="string"
+     length="1"
+     follows="left|top"
+     height="10"
+     layout="topleft"
+     left="30"
+     name="Software updates:"
+     mouse_opaque="false"
+     top_pad="5"
+     width="300">
+    Software updates:
+  </text>
+  <combo_box
+     control_name="UpdaterServiceSetting"
+     follows="left|top"
+     height="23"
+     layout="topleft"
+     left_delta="50"
+	 top_pad="5"
+     name="updater_service_combobox"
+     width="300">
+        <combo_box.item
+         label="Install automatically"
+         name="Install_automatically"
+         value="3" />
+      <!--
+        <combo_box.item
+         label="Ask before installing"
+         name="Install_ask"
+         value="1" />
+      -->
+        <combo_box.item
+         label="Download and install updates manually"
+         name="Install_manual"
+         value="0" />
+  </combo_box>
 </panel>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
index da366f30aef79687850b22b734d8e380f433e25b..f0ce8b849a8c7a0698a4ab078ad826e7636668c3 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml
@@ -296,6 +296,7 @@
 	<check_box
 		name="media_auto_play_btn"
 		control_name="ParcelMediaAutoPlayEnable"
+		enabled_control="AudioStreamingMedia"
 		value="true"
 		follows="left|bottom|right"
 		height="15"
diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml
index 7caf425058f6d8516d8b0f58cdfd5b7952305b02..61e3bb354f492c8af9e0abcb99c9b86fbfa8a169 100644
--- a/indra/newview/skins/default/xui/en/panel_profile.xml
+++ b/indra/newview/skins/default/xui/en/panel_profile.xml
@@ -59,7 +59,7 @@
          left="0"
          name="profile_scroll"
          opaque="true"
-         height="527"
+         height="400"
          width="317"
          top="0">
           <panel
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 2f52ca660b833608d09f63c95d5c62e59d66484e..d756dfb7de6d53b13bdaabb3f059651fa0973fa0 100644
--- a/indra/newview/skins/default/xui/en/panel_status_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml
@@ -51,7 +51,7 @@
      height="18"
      left="0" 
      name="balance"
-     tool_tip="My Balance"
+     tool_tip="Click to refresh your L$ balance"
      v_pad="4"
      top="0"
      wrap="false" 
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 51fba470cb0653b6f5f76769dbfd7d5d51da99e7..752bb6ed3a03724cd290966def01c5a62eca4807 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -1716,8 +1716,8 @@ integer llGetRegionAgentCount()
 Returns the number of avatars in the region
 	</string>
 	<string name="LSLTipText_llTextBox" translate="false">
-llTextBox(key avatar, string message, integer chat_channel
-Shows a dialog box on the avatar&apos;s screen with the message.
+llTextBox(key avatar, string message, integer chat_channel)
+Shows a window on the avatar&apos;s screen with the message.
 It contains a text box for input, and if entered that text is chatted on chat_channel.
 	</string>
 	<string name="LSLTipText_llGetAgentLanguage" translate="false">
diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp
index 309e9e9ee3694f804b9a3ed8ed8e52a21e57a9f7..9e321db889941a0054cf133c03c2ba66ac8e582a 100644
--- a/indra/newview/tests/lllogininstance_test.cpp
+++ b/indra/newview/tests/lllogininstance_test.cpp
@@ -40,6 +40,7 @@
 
 #if defined(LL_WINDOWS)
 #pragma warning(disable: 4355)      // using 'this' in base-class ctor initializer expr
+#pragma warning(disable: 4702)      // disable 'unreachable code' so we can safely use skip().
 #endif
 
 // Constants
@@ -68,6 +69,7 @@ static bool gDisconnectCalled = false;
 
 #include "../llviewerwindow.h"
 void LLViewerWindow::setShowProgress(BOOL show) {}
+LLProgressView * LLViewerWindow::getProgressView(void) const { return 0; }
 
 LLViewerWindow* gViewerWindow;
 
@@ -184,6 +186,41 @@ void LLUIColorTable::saveUserSettings(void)const {}
 const std::string &LLVersionInfo::getChannelAndVersion() { return VIEWERLOGIN_VERSION_CHANNEL; }
 const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; }
 
+//-----------------------------------------------------------------------------
+#include "../llappviewer.h"
+void LLAppViewer::forceQuit(void) {}
+LLAppViewer * LLAppViewer::sInstance = 0;
+
+//-----------------------------------------------------------------------------
+#include "llnotificationsutil.h"
+LLNotificationPtr LLNotificationsUtil::add(const std::string& name, 
+					  const LLSD& substitutions, 
+					  const LLSD& payload, 
+					  boost::function<void (const LLSD&, const LLSD&)> functor) { return LLNotificationPtr((LLNotification*)0); }
+
+
+//-----------------------------------------------------------------------------
+#include "llupdaterservice.h"
+
+std::string const & LLUpdaterService::pumpName(void)
+{
+	static std::string wakka = "wakka wakka wakka";
+	return wakka;
+}
+bool LLUpdaterService::updateReadyToInstall(void) { return false; }
+void LLUpdaterService::initialize(const std::string& protocol_version,
+				const std::string& url, 
+				const std::string& path,
+				const std::string& channel,
+								  const std::string& version) {}
+
+void LLUpdaterService::setCheckPeriod(unsigned int seconds) {}
+void LLUpdaterService::startChecking(bool install_if_ready) {}
+void LLUpdaterService::stopChecking() {}
+bool LLUpdaterService::isChecking() { return false; }
+LLUpdaterService::eUpdaterState LLUpdaterService::getState() { return INITIAL; }
+std::string LLUpdaterService::updatedVersion() { return ""; }
+
 //-----------------------------------------------------------------------------
 #include "llnotifications.h"
 #include "llfloaterreg.h"
@@ -198,6 +235,12 @@ LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key,
 	return NULL;
 }
 
+//----------------------------------------------------------------------------
+#include "../llprogressview.h"
+void LLProgressView::setText(std::string const &){}
+void LLProgressView::setPercent(float){}
+void LLProgressView::setMessage(std::string const &){}
+
 //-----------------------------------------------------------------------------
 // LLNotifications
 class MockNotifications : public LLNotificationsInterface
@@ -435,6 +478,8 @@ namespace tut
     template<> template<>
     void lllogininstance_object::test<3>()
     {
+		skip();
+		
 		set_test_name("Test Mandatory Update User Accepts");
 
 		// Part 1 - Mandatory Update, with User accepts response.
@@ -462,6 +507,8 @@ namespace tut
 	template<> template<>
     void lllogininstance_object::test<4>()
     {
+		skip();
+		
 		set_test_name("Test Mandatory Update User Decline");
 
 		// Test connect with update needed.
diff --git a/indra/newview/tests/llsimplestat_test.cpp b/indra/newview/tests/llsimplestat_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..60a8cac995ba1ad1f280c21406a45f44b50f73d1
--- /dev/null
+++ b/indra/newview/tests/llsimplestat_test.cpp
@@ -0,0 +1,586 @@
+/** 
+ * @file llsimplestats_test.cpp
+ * @date 2010-10-22
+ * @brief Test cases for some of llsimplestat.h
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include <tut/tut.hpp>
+
+#include "lltut.h"
+#include "../llsimplestat.h"
+#include "llsd.h"
+#include "llmath.h"
+
+// @brief Used as a pointer cast type to get access to LLSimpleStatCounter
+class TutStatCounter: public LLSimpleStatCounter
+{
+public:
+	TutStatCounter();							// Not defined
+	~TutStatCounter();							// Not defined
+	void operator=(const TutStatCounter &);		// Not defined
+	
+	void setRawCount(U32 c)				{ mCount = c; }
+	U32 getRawCount() const				{ return mCount; }
+};
+
+
+namespace tut
+{
+	struct stat_counter_index
+	{};
+	typedef test_group<stat_counter_index> stat_counter_index_t;
+	typedef stat_counter_index_t::object stat_counter_index_object_t;
+	tut::stat_counter_index_t tut_stat_counter_index("stat_counter_test");
+
+	// Testing LLSimpleStatCounter's external interface
+	template<> template<>
+	void stat_counter_index_object_t::test<1>()
+	{
+		LLSimpleStatCounter c1;
+		ensure("Initialized counter is zero", (0 == c1.getCount()));
+
+		ensure("Counter increment return is 1", (1 == ++c1));
+		ensure("Counter increment return is 2", (2 == ++c1));
+
+		ensure("Current counter is 2", (2 == c1.getCount()));
+
+		c1.reset();
+		ensure("Counter is 0 after reset", (0 == c1.getCount()));
+		
+		ensure("Counter increment return is 1", (1 == ++c1));
+	}
+
+	// Testing LLSimpleStatCounter's internal state
+	template<> template<>
+	void stat_counter_index_object_t::test<2>()
+	{
+		LLSimpleStatCounter c1;
+		TutStatCounter * tc1 = (TutStatCounter *) &c1;
+		
+		ensure("Initialized private counter is zero", (0 == tc1->getRawCount()));
+
+		++c1;
+		++c1;
+		
+		ensure("Current private counter is 2", (2 == tc1->getRawCount()));
+
+		c1.reset();
+		ensure("Raw counter is 0 after reset", (0 == tc1->getRawCount()));
+	}
+
+	// Testing LLSimpleStatCounter's wrapping behavior
+	template<> template<>
+	void stat_counter_index_object_t::test<3>()
+	{
+		LLSimpleStatCounter c1;
+		TutStatCounter * tc1 = (TutStatCounter *) &c1;
+
+		tc1->setRawCount(U32_MAX);
+		ensure("Initialized private counter is zero", (U32_MAX == c1.getCount()));
+
+		ensure("Increment of max value wraps to 0", (0 == ++c1));
+	}
+
+	// Testing LLSimpleStatMMM's external behavior
+	template<> template<>
+	void stat_counter_index_object_t::test<4>()
+	{
+		LLSimpleStatMMM<> m1;
+		typedef LLSimpleStatMMM<>::Value lcl_float;
+		lcl_float zero(0);
+
+		// Freshly-constructed
+		ensure("Constructed MMM<> has 0 count", (0 == m1.getCount()));
+		ensure("Constructed MMM<> has 0 min", (zero == m1.getMin()));
+		ensure("Constructed MMM<> has 0 max", (zero == m1.getMax()));
+		ensure("Constructed MMM<> has 0 mean no div-by-zero", (zero == m1.getMean()));
+
+		// Single insert
+		m1.record(1.0);
+		ensure("Single insert MMM<> has 1 count", (1 == m1.getCount()));
+		ensure("Single insert MMM<> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("Single insert MMM<> has 1.0 max", (1.0 == m1.getMax()));
+		ensure("Single insert MMM<> has 1.0 mean", (1.0 == m1.getMean()));
+		
+		// Second insert
+		m1.record(3.0);
+		ensure("2nd insert MMM<> has 2 count", (2 == m1.getCount()));
+		ensure("2nd insert MMM<> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("2nd insert MMM<> has 3.0 max", (3.0 == m1.getMax()));
+		ensure_approximately_equals("2nd insert MMM<> has 2.0 mean", m1.getMean(), lcl_float(2.0), 1);
+
+		// Third insert
+		m1.record(5.0);
+		ensure("3rd insert MMM<> has 3 count", (3 == m1.getCount()));
+		ensure("3rd insert MMM<> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("3rd insert MMM<> has 5.0 max", (5.0 == m1.getMax()));
+		ensure_approximately_equals("3rd insert MMM<> has 3.0 mean", m1.getMean(), lcl_float(3.0), 1);
+
+		// Fourth insert
+		m1.record(1000000.0);
+		ensure("4th insert MMM<> has 4 count", (4 == m1.getCount()));
+		ensure("4th insert MMM<> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("4th insert MMM<> has 100000.0 max", (1000000.0 == m1.getMax()));
+		ensure_approximately_equals("4th insert MMM<> has 250002.0 mean", m1.getMean(), lcl_float(250002.0), 1);
+
+		// Reset
+		m1.reset();
+		ensure("Reset MMM<> has 0 count", (0 == m1.getCount()));
+		ensure("Reset MMM<> has 0 min", (zero == m1.getMin()));
+		ensure("Reset MMM<> has 0 max", (zero == m1.getMax()));
+		ensure("Reset MMM<> has 0 mean no div-by-zero", (zero == m1.getMean()));
+	}
+
+	// Testing LLSimpleStatMMM's response to large values
+	template<> template<>
+	void stat_counter_index_object_t::test<5>()
+	{
+		LLSimpleStatMMM<> m1;
+		typedef LLSimpleStatMMM<>::Value lcl_float;
+		lcl_float zero(0);
+
+		// Insert overflowing values
+		const lcl_float bignum(F32_MAX / 2);
+
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(zero);
+
+		ensure("Overflowed MMM<> has 8 count", (8 == m1.getCount()));
+		ensure("Overflowed MMM<> has 0 min", (zero == m1.getMin()));
+		ensure("Overflowed MMM<> has huge max", (bignum == m1.getMax()));
+		ensure("Overflowed MMM<> has fetchable mean", (1.0 == m1.getMean() || true));
+		// We should be infinte but not interested in proving the IEEE standard here.
+		LLSD sd1(m1.getMean());
+		// std::cout << "Thingy:  " << m1.getMean() << " and as LLSD:  " << sd1 << std::endl;
+		ensure("Overflowed MMM<> produces LLSDable Real", (sd1.isReal()));
+	}
+
+	// Testing LLSimpleStatMMM<F32>'s external behavior
+	template<> template<>
+	void stat_counter_index_object_t::test<6>()
+	{
+		LLSimpleStatMMM<F32> m1;
+		typedef LLSimpleStatMMM<F32>::Value lcl_float;
+		lcl_float zero(0);
+
+		// Freshly-constructed
+		ensure("Constructed MMM<F32> has 0 count", (0 == m1.getCount()));
+		ensure("Constructed MMM<F32> has 0 min", (zero == m1.getMin()));
+		ensure("Constructed MMM<F32> has 0 max", (zero == m1.getMax()));
+		ensure("Constructed MMM<F32> has 0 mean no div-by-zero", (zero == m1.getMean()));
+
+		// Single insert
+		m1.record(1.0);
+		ensure("Single insert MMM<F32> has 1 count", (1 == m1.getCount()));
+		ensure("Single insert MMM<F32> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("Single insert MMM<F32> has 1.0 max", (1.0 == m1.getMax()));
+		ensure("Single insert MMM<F32> has 1.0 mean", (1.0 == m1.getMean()));
+		
+		// Second insert
+		m1.record(3.0);
+		ensure("2nd insert MMM<F32> has 2 count", (2 == m1.getCount()));
+		ensure("2nd insert MMM<F32> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("2nd insert MMM<F32> has 3.0 max", (3.0 == m1.getMax()));
+		ensure_approximately_equals("2nd insert MMM<F32> has 2.0 mean", m1.getMean(), lcl_float(2.0), 1);
+
+		// Third insert
+		m1.record(5.0);
+		ensure("3rd insert MMM<F32> has 3 count", (3 == m1.getCount()));
+		ensure("3rd insert MMM<F32> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("3rd insert MMM<F32> has 5.0 max", (5.0 == m1.getMax()));
+		ensure_approximately_equals("3rd insert MMM<F32> has 3.0 mean", m1.getMean(), lcl_float(3.0), 1);
+
+		// Fourth insert
+		m1.record(1000000.0);
+		ensure("4th insert MMM<F32> has 4 count", (4 == m1.getCount()));
+		ensure("4th insert MMM<F32> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("4th insert MMM<F32> has 1000000.0 max", (1000000.0 == m1.getMax()));
+		ensure_approximately_equals("4th insert MMM<F32> has 250002.0 mean", m1.getMean(), lcl_float(250002.0), 1);
+
+		// Reset
+		m1.reset();
+		ensure("Reset MMM<F32> has 0 count", (0 == m1.getCount()));
+		ensure("Reset MMM<F32> has 0 min", (zero == m1.getMin()));
+		ensure("Reset MMM<F32> has 0 max", (zero == m1.getMax()));
+		ensure("Reset MMM<F32> has 0 mean no div-by-zero", (zero == m1.getMean()));
+	}
+
+	// Testing LLSimpleStatMMM's response to large values
+	template<> template<>
+	void stat_counter_index_object_t::test<7>()
+	{
+		LLSimpleStatMMM<F32> m1;
+		typedef LLSimpleStatMMM<F32>::Value lcl_float;
+		lcl_float zero(0);
+
+		// Insert overflowing values
+		const lcl_float bignum(F32_MAX / 2);
+
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(zero);
+
+		ensure("Overflowed MMM<F32> has 8 count", (8 == m1.getCount()));
+		ensure("Overflowed MMM<F32> has 0 min", (zero == m1.getMin()));
+		ensure("Overflowed MMM<F32> has huge max", (bignum == m1.getMax()));
+		ensure("Overflowed MMM<F32> has fetchable mean", (1.0 == m1.getMean() || true));
+		// We should be infinte but not interested in proving the IEEE standard here.
+		LLSD sd1(m1.getMean());
+		// std::cout << "Thingy:  " << m1.getMean() << " and as LLSD:  " << sd1 << std::endl;
+		ensure("Overflowed MMM<F32> produces LLSDable Real", (sd1.isReal()));
+	}
+
+	// Testing LLSimpleStatMMM<F64>'s external behavior
+	template<> template<>
+	void stat_counter_index_object_t::test<8>()
+	{
+		LLSimpleStatMMM<F64> m1;
+		typedef LLSimpleStatMMM<F64>::Value lcl_float;
+		lcl_float zero(0);
+
+		// Freshly-constructed
+		ensure("Constructed MMM<F64> has 0 count", (0 == m1.getCount()));
+		ensure("Constructed MMM<F64> has 0 min", (zero == m1.getMin()));
+		ensure("Constructed MMM<F64> has 0 max", (zero == m1.getMax()));
+		ensure("Constructed MMM<F64> has 0 mean no div-by-zero", (zero == m1.getMean()));
+
+		// Single insert
+		m1.record(1.0);
+		ensure("Single insert MMM<F64> has 1 count", (1 == m1.getCount()));
+		ensure("Single insert MMM<F64> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("Single insert MMM<F64> has 1.0 max", (1.0 == m1.getMax()));
+		ensure("Single insert MMM<F64> has 1.0 mean", (1.0 == m1.getMean()));
+		
+		// Second insert
+		m1.record(3.0);
+		ensure("2nd insert MMM<F64> has 2 count", (2 == m1.getCount()));
+		ensure("2nd insert MMM<F64> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("2nd insert MMM<F64> has 3.0 max", (3.0 == m1.getMax()));
+		ensure_approximately_equals("2nd insert MMM<F64> has 2.0 mean", m1.getMean(), lcl_float(2.0), 1);
+
+		// Third insert
+		m1.record(5.0);
+		ensure("3rd insert MMM<F64> has 3 count", (3 == m1.getCount()));
+		ensure("3rd insert MMM<F64> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("3rd insert MMM<F64> has 5.0 max", (5.0 == m1.getMax()));
+		ensure_approximately_equals("3rd insert MMM<F64> has 3.0 mean", m1.getMean(), lcl_float(3.0), 1);
+
+		// Fourth insert
+		m1.record(1000000.0);
+		ensure("4th insert MMM<F64> has 4 count", (4 == m1.getCount()));
+		ensure("4th insert MMM<F64> has 1.0 min", (1.0 == m1.getMin()));
+		ensure("4th insert MMM<F64> has 1000000.0 max", (1000000.0 == m1.getMax()));
+		ensure_approximately_equals("4th insert MMM<F64> has 250002.0 mean", m1.getMean(), lcl_float(250002.0), 1);
+
+		// Reset
+		m1.reset();
+		ensure("Reset MMM<F64> has 0 count", (0 == m1.getCount()));
+		ensure("Reset MMM<F64> has 0 min", (zero == m1.getMin()));
+		ensure("Reset MMM<F64> has 0 max", (zero == m1.getMax()));
+		ensure("Reset MMM<F64> has 0 mean no div-by-zero", (zero == m1.getMean()));
+	}
+
+	// Testing LLSimpleStatMMM's response to large values
+	template<> template<>
+	void stat_counter_index_object_t::test<9>()
+	{
+		LLSimpleStatMMM<F64> m1;
+		typedef LLSimpleStatMMM<F64>::Value lcl_float;
+		lcl_float zero(0);
+
+		// Insert overflowing values
+		const lcl_float bignum(F64_MAX / 2);
+
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(zero);
+
+		ensure("Overflowed MMM<F64> has 8 count", (8 == m1.getCount()));
+		ensure("Overflowed MMM<F64> has 0 min", (zero == m1.getMin()));
+		ensure("Overflowed MMM<F64> has huge max", (bignum == m1.getMax()));
+		ensure("Overflowed MMM<F64> has fetchable mean", (1.0 == m1.getMean() || true));
+		// We should be infinte but not interested in proving the IEEE standard here.
+		LLSD sd1(m1.getMean());
+		// std::cout << "Thingy:  " << m1.getMean() << " and as LLSD:  " << sd1 << std::endl;
+		ensure("Overflowed MMM<F64> produces LLSDable Real", (sd1.isReal()));
+	}
+
+	// Testing LLSimpleStatMMM<U64>'s external behavior
+	template<> template<>
+	void stat_counter_index_object_t::test<10>()
+	{
+		LLSimpleStatMMM<U64> m1;
+		typedef LLSimpleStatMMM<U64>::Value lcl_int;
+		lcl_int zero(0);
+
+		// Freshly-constructed
+		ensure("Constructed MMM<U64> has 0 count", (0 == m1.getCount()));
+		ensure("Constructed MMM<U64> has 0 min", (zero == m1.getMin()));
+		ensure("Constructed MMM<U64> has 0 max", (zero == m1.getMax()));
+		ensure("Constructed MMM<U64> has 0 mean no div-by-zero", (zero == m1.getMean()));
+
+		// Single insert
+		m1.record(1);
+		ensure("Single insert MMM<U64> has 1 count", (1 == m1.getCount()));
+		ensure("Single insert MMM<U64> has 1 min", (1 == m1.getMin()));
+		ensure("Single insert MMM<U64> has 1 max", (1 == m1.getMax()));
+		ensure("Single insert MMM<U64> has 1 mean", (1 == m1.getMean()));
+		
+		// Second insert
+		m1.record(3);
+		ensure("2nd insert MMM<U64> has 2 count", (2 == m1.getCount()));
+		ensure("2nd insert MMM<U64> has 1 min", (1 == m1.getMin()));
+		ensure("2nd insert MMM<U64> has 3 max", (3 == m1.getMax()));
+		ensure("2nd insert MMM<U64> has 2 mean", (2 == m1.getMean()));
+
+		// Third insert
+		m1.record(5);
+		ensure("3rd insert MMM<U64> has 3 count", (3 == m1.getCount()));
+		ensure("3rd insert MMM<U64> has 1 min", (1 == m1.getMin()));
+		ensure("3rd insert MMM<U64> has 5 max", (5 == m1.getMax()));
+		ensure("3rd insert MMM<U64> has 3 mean", (3 == m1.getMean()));
+
+		// Fourth insert
+		m1.record(U64L(1000000000000));
+		ensure("4th insert MMM<U64> has 4 count", (4 == m1.getCount()));
+		ensure("4th insert MMM<U64> has 1 min", (1 == m1.getMin()));
+		ensure("4th insert MMM<U64> has 1000000000000ULL max", (U64L(1000000000000) == m1.getMax()));
+		ensure("4th insert MMM<U64> has 250000000002ULL mean", (U64L( 250000000002) == m1.getMean()));
+
+		// Reset
+		m1.reset();
+		ensure("Reset MMM<U64> has 0 count", (0 == m1.getCount()));
+		ensure("Reset MMM<U64> has 0 min", (zero == m1.getMin()));
+		ensure("Reset MMM<U64> has 0 max", (zero == m1.getMax()));
+		ensure("Reset MMM<U64> has 0 mean no div-by-zero", (zero == m1.getMean()));
+	}
+
+	// Testing LLSimpleStatMMM's response to large values
+	template<> template<>
+	void stat_counter_index_object_t::test<11>()
+	{
+		LLSimpleStatMMM<U64> m1;
+		typedef LLSimpleStatMMM<U64>::Value lcl_int;
+		lcl_int zero(0);
+
+		// Insert overflowing values
+		const lcl_int bignum(U64L(0xffffffffffffffff) / 2);
+
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(bignum);
+		m1.record(zero);
+
+		ensure("Overflowed MMM<U64> has 8 count", (8 == m1.getCount()));
+		ensure("Overflowed MMM<U64> has 0 min", (zero == m1.getMin()));
+		ensure("Overflowed MMM<U64> has huge max", (bignum == m1.getMax()));
+		ensure("Overflowed MMM<U64> has fetchable mean", (zero == m1.getMean() || true));
+	}
+
+    // Testing LLSimpleStatCounter's merge() method
+	template<> template<>
+	void stat_counter_index_object_t::test<12>()
+	{
+		LLSimpleStatCounter c1;
+		LLSimpleStatCounter c2;
+
+		++c1;
+		++c1;
+		++c1;
+		++c1;
+
+		++c2;
+		++c2;
+		c2.merge(c1);
+		
+		ensure_equals("4 merged into 2 results in 6", 6, c2.getCount());
+
+		ensure_equals("Source of merge is undamaged", 4, c1.getCount());
+	}
+
+    // Testing LLSimpleStatMMM's merge() method
+	template<> template<>
+	void stat_counter_index_object_t::test<13>()
+	{
+		LLSimpleStatMMM<> m1;
+		LLSimpleStatMMM<> m2;
+
+		m1.record(3.5);
+		m1.record(4.5);
+		m1.record(5.5);
+		m1.record(6.5);
+
+		m2.record(5.0);
+		m2.record(7.0);
+		m2.record(9.0);
+		
+		m2.merge(m1);
+
+		ensure_equals("Count after merge (p1)", 7, m2.getCount());
+		ensure_approximately_equals("Min after merge (p1)", F32(3.5), m2.getMin(), 22);
+		ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22);
+		ensure_approximately_equals("Mean after merge (p1)", F32(41.000/7.000), m2.getMean(), 22);
+		
+
+		ensure_equals("Source count of merge is undamaged (p1)", 4, m1.getCount());
+		ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(3.5), m1.getMin(), 22);
+		ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(6.5), m1.getMax(), 22);
+		ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(5.0), m1.getMean(), 22);
+
+		m2.reset();
+
+		m2.record(-22.0);
+		m2.record(-1.0);
+		m2.record(30.0);
+		
+		m2.merge(m1);
+
+		ensure_equals("Count after merge (p2)", 7, m2.getCount());
+		ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22);
+		ensure_approximately_equals("Max after merge (p2)", F32(30.0), m2.getMax(), 22);
+		ensure_approximately_equals("Mean after merge (p2)", F32(27.000/7.000), m2.getMean(), 22);
+
+	}
+
+    // Testing LLSimpleStatMMM's merge() method when src contributes nothing
+	template<> template<>
+	void stat_counter_index_object_t::test<14>()
+	{
+		LLSimpleStatMMM<> m1;
+		LLSimpleStatMMM<> m2;
+
+		m2.record(5.0);
+		m2.record(7.0);
+		m2.record(9.0);
+		
+		m2.merge(m1);
+
+		ensure_equals("Count after merge (p1)", 3, m2.getCount());
+		ensure_approximately_equals("Min after merge (p1)", F32(5.0), m2.getMin(), 22);
+		ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22);
+		ensure_approximately_equals("Mean after merge (p1)", F32(7.000), m2.getMean(), 22);
+
+		ensure_equals("Source count of merge is undamaged (p1)", 0, m1.getCount());
+		ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(0), m1.getMin(), 22);
+		ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(0), m1.getMax(), 22);
+		ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(0), m1.getMean(), 22);
+
+		m2.reset();
+
+		m2.record(-22.0);
+		m2.record(-1.0);
+		
+		m2.merge(m1);
+
+		ensure_equals("Count after merge (p2)", 2, m2.getCount());
+		ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22);
+		ensure_approximately_equals("Max after merge (p2)", F32(-1.0), m2.getMax(), 22);
+		ensure_approximately_equals("Mean after merge (p2)", F32(-11.5), m2.getMean(), 22);
+	}
+
+    // Testing LLSimpleStatMMM's merge() method when dst contributes nothing
+	template<> template<>
+	void stat_counter_index_object_t::test<15>()
+	{
+		LLSimpleStatMMM<> m1;
+		LLSimpleStatMMM<> m2;
+
+		m1.record(5.0);
+		m1.record(7.0);
+		m1.record(9.0);
+		
+		m2.merge(m1);
+
+		ensure_equals("Count after merge (p1)", 3, m2.getCount());
+		ensure_approximately_equals("Min after merge (p1)", F32(5.0), m2.getMin(), 22);
+		ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22);
+		ensure_approximately_equals("Mean after merge (p1)", F32(7.000), m2.getMean(), 22);
+
+		ensure_equals("Source count of merge is undamaged (p1)", 3, m1.getCount());
+		ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(5.0), m1.getMin(), 22);
+		ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(9.0), m1.getMax(), 22);
+		ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(7.0), m1.getMean(), 22);
+
+		m1.reset();
+		m2.reset();
+		
+		m1.record(-22.0);
+		m1.record(-1.0);
+		
+		m2.merge(m1);
+
+		ensure_equals("Count after merge (p2)", 2, m2.getCount());
+		ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22);
+		ensure_approximately_equals("Max after merge (p2)", F32(-1.0), m2.getMax(), 22);
+		ensure_approximately_equals("Mean after merge (p2)", F32(-11.5), m2.getMean(), 22);
+	}
+
+    // Testing LLSimpleStatMMM's merge() method when neither dst nor src contributes
+	template<> template<>
+	void stat_counter_index_object_t::test<16>()
+	{
+		LLSimpleStatMMM<> m1;
+		LLSimpleStatMMM<> m2;
+
+		m2.merge(m1);
+
+		ensure_equals("Count after merge (p1)", 0, m2.getCount());
+		ensure_approximately_equals("Min after merge (p1)", F32(0), m2.getMin(), 22);
+		ensure_approximately_equals("Max after merge (p1)", F32(0), m2.getMax(), 22);
+		ensure_approximately_equals("Mean after merge (p1)", F32(0), m2.getMean(), 22);
+
+		ensure_equals("Source count of merge is undamaged (p1)", 0, m1.getCount());
+		ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(0), m1.getMin(), 22);
+		ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(0), m1.getMax(), 22);
+		ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(0), m1.getMean(), 22);
+	}
+}
diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1bb4fb7c0cf1b64080674751bd34b6d46693c862
--- /dev/null
+++ b/indra/newview/tests/llviewerassetstats_test.cpp
@@ -0,0 +1,990 @@
+/** 
+ * @file llviewerassetstats_tut.cpp
+ * @date 2010-10-28
+ * @brief Test cases for some of newview/llviewerassetstats.cpp
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include <tut/tut.hpp>
+#include <iostream>
+
+#include "lltut.h"
+#include "../llviewerassetstats.h"
+#include "lluuid.h"
+#include "llsdutil.h"
+#include "llregionhandle.h"
+
+static const char * all_keys[] = 
+{
+	"duration",
+	"fps",
+	"get_other",
+	"get_texture_temp_http",
+	"get_texture_temp_udp",
+	"get_texture_non_temp_http",
+	"get_texture_non_temp_udp",
+	"get_wearable_udp",
+	"get_sound_udp",
+	"get_gesture_udp"
+};
+
+static const char * resp_keys[] = 
+{
+	"get_other",
+	"get_texture_temp_http",
+	"get_texture_temp_udp",
+	"get_texture_non_temp_http",
+	"get_texture_non_temp_udp",
+	"get_wearable_udp",
+	"get_sound_udp",
+	"get_gesture_udp"
+};
+
+static const char * sub_keys[] =
+{
+	"dequeued",
+	"enqueued",
+	"resp_count",
+	"resp_max",
+	"resp_min",
+	"resp_mean"
+};
+
+static const char * mmm_resp_keys[] = 
+{
+	"fps"
+};
+
+static const char * mmm_sub_keys[] =
+{
+	"count",
+	"max",
+	"min",
+	"mean"
+};
+
+static const LLUUID region1("4e2d81a3-6263-6ffe-ad5c-8ce04bee07e8");
+static const LLUUID region2("68762cc8-b68b-4e45-854b-e830734f2d4a");
+static const U64 region1_handle(0x0000040000003f00ULL);
+static const U64 region2_handle(0x0000030000004200ULL);
+static const std::string region1_handle_str("0000040000003f00");
+static const std::string region2_handle_str("0000030000004200");
+
+#if 0
+static bool
+is_empty_map(const LLSD & sd)
+{
+	return sd.isMap() && 0 == sd.size();
+}
+
+static bool
+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);
+}
+
+static bool
+is_no_stats_map(const LLSD & sd)
+{
+	return is_double_key_map(sd, "duration", "regions");
+}
+
+static bool
+is_single_slot_array(const LLSD & sd, U64 region_handle)
+{
+	U32 grid_x(0), grid_y(0);
+	grid_from_region_handle(region_handle, &grid_x, &grid_y);
+	
+	return (sd.isArray() &&
+			1 == sd.size() &&
+			sd[0].has("grid_x") &&
+			sd[0].has("grid_y") &&
+			sd[0]["grid_x"].isInteger() &&
+			sd[0]["grid_y"].isInteger() &&
+			grid_x == sd[0]["grid_x"].asInteger() &&
+			grid_y == sd[0]["grid_y"].asInteger());
+}
+
+static bool
+is_double_slot_array(const LLSD & sd, U64 region_handle1, U64 region_handle2)
+{
+	U32 grid_x1(0), grid_y1(0);
+	U32 grid_x2(0), grid_y2(0);
+	grid_from_region_handle(region_handle1, &grid_x1, &grid_y1);
+	grid_from_region_handle(region_handle2, &grid_x2, &grid_y2);
+	
+	return (sd.isArray() &&
+			2 == sd.size() &&
+			sd[0].has("grid_x") &&
+			sd[0].has("grid_y") &&
+			sd[0]["grid_x"].isInteger() &&
+			sd[0]["grid_y"].isInteger() &&
+			sd[1].has("grid_x") &&
+			sd[1].has("grid_y") &&
+			sd[1]["grid_x"].isInteger() &&
+			sd[1]["grid_y"].isInteger() &&
+			((grid_x1 == sd[0]["grid_x"].asInteger() &&
+			  grid_y1 == sd[0]["grid_y"].asInteger() &&
+			  grid_x2 == sd[1]["grid_x"].asInteger() &&
+			  grid_y2 == sd[1]["grid_y"].asInteger()) ||
+			 (grid_x1 == sd[1]["grid_x"].asInteger() &&
+			  grid_y1 == sd[1]["grid_y"].asInteger() &&
+			  grid_x2 == sd[0]["grid_x"].asInteger() &&
+			  grid_y2 == sd[0]["grid_y"].asInteger())));
+}
+
+static LLSD
+get_region(const LLSD & sd, U64 region_handle1)
+{
+	U32 grid_x(0), grid_y(0);
+	grid_from_region_handle(region_handle1, &grid_x, &grid_y);
+
+	for (LLSD::array_const_iterator it(sd["regions"].beginArray());
+		 sd["regions"].endArray() != it;
+		 ++it)
+	{
+		if ((*it).has("grid_x") &&
+			(*it).has("grid_y") &&
+			(*it)["grid_x"].isInteger() &&
+			(*it)["grid_y"].isInteger() &&
+			(*it)["grid_x"].asInteger() == grid_x &&
+			(*it)["grid_y"].asInteger() == grid_y)
+		{
+			return *it;
+		}
+	}
+	return LLSD();
+}
+
+namespace tut
+{
+	struct tst_viewerassetstats_index
+	{};
+	typedef test_group<tst_viewerassetstats_index> tst_viewerassetstats_index_t;
+	typedef tst_viewerassetstats_index_t::object tst_viewerassetstats_index_object_t;
+	tut::tst_viewerassetstats_index_t tut_tst_viewerassetstats_index("tst_viewerassetstats_test");
+
+	// Testing free functions without global stats allocated
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<1>()
+	{
+		// Check that helpers aren't bothered by missing global stats
+		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain));
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+
+		LLViewerAssetStatsFF::record_response_main(LLViewerAssetType::AT_GESTURE, false, false, 12300000ULL);
+	}
+
+	// Create a non-global instance and check the structure
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<2>()
+	{
+		ensure("Global gViewerAssetStatsMain should be NULL", (NULL == gViewerAssetStatsMain));
+
+		LLViewerAssetStats * it = new LLViewerAssetStats();
+
+		ensure("Global gViewerAssetStatsMain should still be NULL", (NULL == gViewerAssetStatsMain));
+
+		LLSD sd_full = it->asLLSD(false);
+
+		// Default (NULL) region ID doesn't produce LLSD results so should
+		// get an empty map back from output
+		ensure("Stat-less LLSD initially", is_no_stats_map(sd_full));
+
+		// 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-slot LLSD array regions", is_single_slot_array(sd_full["regions"], region1_handle));
+		
+		LLSD sd = sd_full["regions"][0];
+
+		delete it;
+			
+		// Check the structure of the LLSD
+		for (int i = 0; i < LL_ARRAY_SIZE(all_keys); ++i)
+		{
+			std::string line = llformat("Has '%s' key", all_keys[i]);
+			ensure(line, sd.has(all_keys[i]));
+		}
+
+		for (int i = 0; i < LL_ARRAY_SIZE(resp_keys); ++i)
+		{
+			for (int j = 0; j < LL_ARRAY_SIZE(sub_keys); ++j)
+			{
+				std::string line = llformat("Key '%s' has '%s' key", resp_keys[i], sub_keys[j]);
+				ensure(line, sd[resp_keys[i]].has(sub_keys[j]));
+			}
+		}
+
+		for (int i = 0; i < LL_ARRAY_SIZE(mmm_resp_keys); ++i)
+		{
+			for (int j = 0; j < LL_ARRAY_SIZE(mmm_sub_keys); ++j)
+			{
+				std::string line = llformat("Key '%s' has '%s' key", mmm_resp_keys[i], mmm_sub_keys[j]);
+				ensure(line, sd[mmm_resp_keys[i]].has(mmm_sub_keys[j]));
+			}
+		}
+	}
+
+	// Create a non-global instance and check some content
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<3>()
+	{
+		LLViewerAssetStats * it = new LLViewerAssetStats();
+		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-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
+		sd = sd[0];
+		
+		delete it;
+
+		// Check a few points on the tree for content
+		ensure("sd[get_texture_temp_http][dequeued] is 0", (0 == sd["get_texture_temp_http"]["dequeued"].asInteger()));
+		ensure("sd[get_sound_udp][resp_min] is 0", (0.0 == sd["get_sound_udp"]["resp_min"].asReal()));
+	}
+
+	// Create a global instance and verify free functions do something useful
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<4>()
+	{
+		gViewerAssetStatsMain = new LLViewerAssetStats();
+		LLViewerAssetStatsFF::set_region_main(region1_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
+		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-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
+		sd = sd["regions"][0];
+		
+		// Check a few points on the tree for content
+		ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_texture_temp_udp][enqueued] is 0", (0 == sd["get_texture_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_texture_non_temp_http][enqueued] is 0", (0 == sd["get_texture_non_temp_http"]["enqueued"].asInteger()));
+		ensure("sd[get_texture_temp_http][enqueued] is 0", (0 == sd["get_texture_temp_http"]["enqueued"].asInteger()));
+		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
+
+		// Reset and check zeros...
+		// Reset leaves current region in place
+		gViewerAssetStatsMain->reset();
+		sd = gViewerAssetStatsMain->asLLSD(false)["regions"][region1_handle_str];
+		
+		delete gViewerAssetStatsMain;
+		gViewerAssetStatsMain = NULL;
+
+		ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
+	}
+
+	// Create two global instances and verify no interactions
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<5>()
+	{
+		gViewerAssetStatsThread1 = new LLViewerAssetStats();
+		gViewerAssetStatsMain = new LLViewerAssetStats();
+		LLViewerAssetStatsFF::set_region_main(region1_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
+
+		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-slot LLSD array regions", is_single_slot_array(sd["regions"], region1_handle));
+		sd = sd["regions"][0];
+		
+		// Check a few points on the tree for content
+		ensure("sd[get_texture_non_temp_udp][enqueued] is 1", (1 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_texture_temp_udp][enqueued] is 0", (0 == sd["get_texture_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_texture_non_temp_http][enqueued] is 0", (0 == sd["get_texture_non_temp_http"]["enqueued"].asInteger()));
+		ensure("sd[get_texture_temp_http][enqueued] is 0", (0 == sd["get_texture_temp_http"]["enqueued"].asInteger()));
+		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
+
+		// Reset and check zeros...
+		// Reset leaves current region in place
+		gViewerAssetStatsMain->reset();
+		sd = gViewerAssetStatsMain->asLLSD(false)["regions"][0];
+		
+		delete gViewerAssetStatsMain;
+		gViewerAssetStatsMain = NULL;
+		delete gViewerAssetStatsThread1;
+		gViewerAssetStatsThread1 = NULL;
+
+		ensure("sd[get_texture_non_temp_udp][enqueued] is reset", (0 == sd["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
+	}
+
+    // Check multiple region collection
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<6>()
+	{
+		gViewerAssetStatsMain = new LLViewerAssetStats();
+
+		LLViewerAssetStatsFF::set_region_main(region1_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
+
+		LLViewerAssetStatsFF::set_region_main(region2_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+
+		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
+
+		// std::cout << sd << std::endl;
+		
+		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		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);
+		ensure("Region1 is present in results", sd1.isMap());
+		ensure("Region2 is present in results", sd2.isMap());
+		
+		// Check a few points on the tree for content
+		ensure_equals("sd1[get_texture_non_temp_udp][enqueued] is 1", sd1["get_texture_non_temp_udp"]["enqueued"].asInteger(), 1);
+		ensure_equals("sd1[get_texture_temp_udp][enqueued] is 0", sd1["get_texture_temp_udp"]["enqueued"].asInteger(), 0);
+		ensure_equals("sd1[get_texture_non_temp_http][enqueued] is 0", sd1["get_texture_non_temp_http"]["enqueued"].asInteger(), 0);
+		ensure_equals("sd1[get_texture_temp_http][enqueued] is 0", sd1["get_texture_temp_http"]["enqueued"].asInteger(), 0);
+		ensure_equals("sd1[get_gesture_udp][dequeued] is 0", sd1["get_gesture_udp"]["dequeued"].asInteger(), 0);
+
+		// Check a few points on the tree for content
+		ensure("sd2[get_gesture_udp][enqueued] is 4", (4 == sd2["get_gesture_udp"]["enqueued"].asInteger()));
+		ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger()));
+		ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+
+		// Reset and check zeros...
+		// 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-slot LLSD array regions (p2)", is_single_slot_array(sd["regions"], region2_handle));
+		sd2 = sd["regions"][0];
+		
+		delete gViewerAssetStatsMain;
+		gViewerAssetStatsMain = NULL;
+
+		ensure("sd2[get_texture_non_temp_udp][enqueued] is reset", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd2[get_gesture_udp][enqueued] is reset", (0 == sd2["get_gesture_udp"]["enqueued"].asInteger()));
+	}
+
+    // Check multiple region collection jumping back-and-forth between regions
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<7>()
+	{
+		gViewerAssetStatsMain = new LLViewerAssetStats();
+
+		LLViewerAssetStatsFF::set_region_main(region1_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
+
+		LLViewerAssetStatsFF::set_region_main(region2_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+
+		LLViewerAssetStatsFF::set_region_main(region1_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, true, true);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, true, true);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
+
+		LLViewerAssetStatsFF::set_region_main(region2_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_GESTURE, false, false);
+
+		LLSD sd = gViewerAssetStatsMain->asLLSD(false);
+
+		ensure("Correct double-key LLSD map root", is_double_key_map(sd, "duration", "regions"));
+		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);
+		ensure("Region1 is present in results", sd1.isMap());
+		ensure("Region2 is present in results", sd2.isMap());
+		
+		// Check a few points on the tree for content
+		ensure("sd1[get_texture_non_temp_udp][enqueued] is 1", (1 == sd1["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd1[get_texture_temp_udp][enqueued] is 0", (0 == sd1["get_texture_temp_udp"]["enqueued"].asInteger()));
+		ensure("sd1[get_texture_non_temp_http][enqueued] is 0", (0 == sd1["get_texture_non_temp_http"]["enqueued"].asInteger()));
+		ensure("sd1[get_texture_temp_http][enqueued] is 1", (1 == sd1["get_texture_temp_http"]["enqueued"].asInteger()));
+		ensure("sd1[get_gesture_udp][dequeued] is 0", (0 == sd1["get_gesture_udp"]["dequeued"].asInteger()));
+
+		// Check a few points on the tree for content
+		ensure("sd2[get_gesture_udp][enqueued] is 8", (8 == sd2["get_gesture_udp"]["enqueued"].asInteger()));
+		ensure("sd2[get_gesture_udp][dequeued] is 0", (0 == sd2["get_gesture_udp"]["dequeued"].asInteger()));
+		ensure("sd2[get_texture_non_temp_udp][enqueued] is 0", (0 == sd2["get_texture_non_temp_udp"]["enqueued"].asInteger()));
+
+		// Reset and check zeros...
+		// 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-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());
+		
+		delete gViewerAssetStatsMain;
+		gViewerAssetStatsMain = NULL;
+
+		ensure_equals("sd2[get_texture_non_temp_udp][enqueued] is reset", sd2["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0);
+		ensure_equals("sd2[get_gesture_udp][enqueued] is reset", sd2["get_gesture_udp"]["enqueued"].asInteger(), 0);
+	}
+
+	// Non-texture assets ignore transport and persistence flags
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<8>()
+	{
+		gViewerAssetStatsThread1 = new LLViewerAssetStats();
+		gViewerAssetStatsMain = new LLViewerAssetStats();
+		LLViewerAssetStatsFF::set_region_main(region1_handle);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_TEXTURE, false, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, false, true);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, false, true);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, false);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_BODYPART, true, true);
+		LLViewerAssetStatsFF::record_dequeue_main(LLViewerAssetType::AT_BODYPART, true, true);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, false, true);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, false);
+
+		LLViewerAssetStatsFF::record_enqueue_main(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		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-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());
+		
+		// Check a few points on the tree for content
+		ensure("sd[get_gesture_udp][enqueued] is 0", (0 == sd["get_gesture_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger()));
+
+		ensure("sd[get_wearable_udp][enqueued] is 4", (4 == sd["get_wearable_udp"]["enqueued"].asInteger()));
+		ensure("sd[get_wearable_udp][dequeued] is 4", (4 == sd["get_wearable_udp"]["dequeued"].asInteger()));
+
+		ensure("sd[get_other][enqueued] is 4", (4 == sd["get_other"]["enqueued"].asInteger()));
+		ensure("sd[get_other][dequeued] is 0", (0 == sd["get_other"]["dequeued"].asInteger()));
+
+		// Reset and check zeros...
+		// Reset leaves current region in place
+		gViewerAssetStatsMain->reset();
+		sd = get_region(gViewerAssetStatsMain->asLLSD(false), region1_handle);
+		ensure("Region1 is present in results", sd.isMap());
+		
+		delete gViewerAssetStatsMain;
+		gViewerAssetStatsMain = NULL;
+		delete gViewerAssetStatsThread1;
+		gViewerAssetStatsThread1 = NULL;
+
+		ensure_equals("sd[get_texture_non_temp_udp][enqueued] is reset", sd["get_texture_non_temp_udp"]["enqueued"].asInteger(), 0);
+		ensure_equals("sd[get_gesture_udp][dequeued] is reset", sd["get_gesture_udp"]["dequeued"].asInteger(), 0);
+	}
+
+
+	// LLViewerAssetStats::merge() basic functions work
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<9>()
+	{
+		LLViewerAssetStats s1;
+		LLViewerAssetStats s2;
+
+		s1.setRegion(region1_handle);
+		s2.setRegion(region1_handle);
+
+		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 5000000);
+		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 6000000);
+		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 8000000);
+		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 7000000);
+		s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 9000000);
+		
+		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 2000000);
+		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 3000000);
+		s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 4000000);
+
+		s2.merge(s1);
+
+		LLSD s2_llsd = get_region(s2.asLLSD(false), region1_handle);
+		ensure("Region1 is present in results", s2_llsd.isMap());
+		
+		ensure_equals("count after merge", s2_llsd["get_texture_temp_http"]["resp_count"].asInteger(), 8);
+		ensure_approximately_equals("min after merge", s2_llsd["get_texture_temp_http"]["resp_min"].asReal(), 2.0, 22);
+		ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_max"].asReal(), 9.0, 22);
+		ensure_approximately_equals("max after merge", s2_llsd["get_texture_temp_http"]["resp_mean"].asReal(), 5.5, 22);
+	}
+
+	// LLViewerAssetStats::merge() basic functions work without corrupting source data
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<10>()
+	{
+		LLViewerAssetStats s1;
+		LLViewerAssetStats s2;
+
+		s1.setRegion(region1_handle);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900);
+
+		
+		s2.setRegion(region2_handle);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000);
+		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000);
+
+		{
+			s2.merge(s1);
+			
+			LLSD src = s1.asLLSD(false);
+			LLSD dst = s2.asLLSD(false);
+
+			ensure_equals("merge src has single region", src["regions"].size(), 1);
+			ensure_equals("merge dst has dual regions", dst["regions"].size(), 2);
+			
+			// Remove time stamps, they're a problem
+			src.erase("duration");
+			src["regions"][0].erase("duration");
+			dst.erase("duration");
+			dst["regions"][0].erase("duration");
+			dst["regions"][1].erase("duration");
+
+			LLSD s1_llsd = get_region(src, region1_handle);
+			ensure("Region1 is present in src", s1_llsd.isMap());
+			LLSD s2_llsd = get_region(dst, region1_handle);
+			ensure("Region1 is present in dst", s2_llsd.isMap());
+
+			ensure("result from src is in dst", llsd_equals(s1_llsd, s2_llsd));
+		}
+
+		s1.setRegion(region1_handle);
+		s2.setRegion(region1_handle);
+		s1.reset();
+		s2.reset();
+		
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900);
+
+		
+		s2.setRegion(region1_handle);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000);
+		s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000);
+
+		{
+			s2.merge(s1);
+			
+			LLSD src = s1.asLLSD(false);
+			LLSD dst = s2.asLLSD(false);
+
+			ensure_equals("merge src has single region (p2)", src["regions"].size(), 1);
+			ensure_equals("merge dst has single region (p2)", dst["regions"].size(), 1);
+
+			// Remove time stamps, they're a problem
+			src.erase("duration");
+			src["regions"][0].erase("duration");
+			dst.erase("duration");
+			dst["regions"][0].erase("duration");
+			
+			LLSD s1_llsd = get_region(src, region1_handle);
+			ensure("Region1 is present in src", s1_llsd.isMap());
+			LLSD s2_llsd = get_region(dst, region1_handle);
+			ensure("Region1 is present in dst", s2_llsd.isMap());
+
+			ensure_equals("src counts okay (enq)", s1_llsd["get_other"]["enqueued"].asInteger(), 4);
+			ensure_equals("src counts okay (deq)", s1_llsd["get_other"]["dequeued"].asInteger(), 4);
+			ensure_equals("src resp counts okay", s1_llsd["get_other"]["resp_count"].asInteger(), 2);
+			ensure_approximately_equals("src respmin okay", s1_llsd["get_other"]["resp_min"].asReal(), 0.2829, 20);
+			ensure_approximately_equals("src respmax okay", s1_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20);
+			
+			ensure_equals("dst counts okay (enq)", s2_llsd["get_other"]["enqueued"].asInteger(), 12);
+			ensure_equals("src counts okay (deq)", s2_llsd["get_other"]["dequeued"].asInteger(), 11);
+			ensure_equals("dst resp counts okay", s2_llsd["get_other"]["resp_count"].asInteger(), 4);
+			ensure_approximately_equals("dst respmin okay", s2_llsd["get_other"]["resp_min"].asReal(), 0.010, 20);
+			ensure_approximately_equals("dst respmax okay", s2_llsd["get_other"]["resp_max"].asReal(), 23.2892, 20);
+		}
+	}
+
+
+    // Maximum merges are interesting when one side contributes nothing
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<11>()
+	{
+		LLViewerAssetStats s1;
+		LLViewerAssetStats s2;
+
+		s1.setRegion(region1_handle);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		// Want to test negative numbers here but have to work in U64
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
+
+		s2.setRegion(region1_handle);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		{
+			s2.merge(s1);
+			
+			LLSD src = s1.asLLSD(false);
+			LLSD dst = s2.asLLSD(false);
+
+			ensure_equals("merge src has single region", src["regions"].size(), 1);
+			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
+			
+			// Remove time stamps, they're a problem
+			src.erase("duration");
+			src["regions"][0].erase("duration");
+			dst.erase("duration");
+			dst["regions"][0].erase("duration");
+
+			LLSD s2_llsd = get_region(dst, region1_handle);
+			ensure("Region1 is present in dst", s2_llsd.isMap());
+			
+			ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
+
+			ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum",
+										s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20);
+		}
+
+		// Other way around
+		s1.setRegion(region1_handle);
+		s2.setRegion(region1_handle);
+		s1.reset();
+		s2.reset();
+
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		// Want to test negative numbers here but have to work in U64
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0);
+
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		{
+			s1.merge(s2);
+			
+			LLSD src = s2.asLLSD(false);
+			LLSD dst = s1.asLLSD(false);
+
+			ensure_equals("merge src has single region", src["regions"].size(), 1);
+			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
+			
+			// Remove time stamps, they're a problem
+			src.erase("duration");
+			src["regions"][0].erase("duration");
+			dst.erase("duration");
+			dst["regions"][0].erase("duration");
+
+			LLSD s2_llsd = get_region(dst, region1_handle);
+			ensure("Region1 is present in dst", s2_llsd.isMap());
+
+			ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
+
+			ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum (flipped)",
+										s2_llsd["get_other"]["resp_max"].asReal(), F64(0.0), 20);
+		}
+	}
+
+    // Minimum merges are interesting when one side contributes nothing
+	template<> template<>
+	void tst_viewerassetstats_index_object_t::test<12>()
+	{
+		LLViewerAssetStats s1;
+		LLViewerAssetStats s2;
+
+		s1.setRegion(region1_handle);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000);
+
+		s2.setRegion(region1_handle);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		{
+			s2.merge(s1);
+			
+			LLSD src = s1.asLLSD(false);
+			LLSD dst = s2.asLLSD(false);
+
+			ensure_equals("merge src has single region", src["regions"].size(), 1);
+			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
+			
+			// Remove time stamps, they're a problem
+			src.erase("duration");
+			src["regions"][0].erase("duration");
+			dst.erase("duration");
+			dst["regions"][0].erase("duration");
+
+			LLSD s2_llsd = get_region(dst, region1_handle);
+			ensure("Region1 is present in dst", s2_llsd.isMap());
+
+			ensure_equals("dst counts come from src only", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
+
+			ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum",
+										s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20);
+		}
+
+		// Other way around
+		s1.setRegion(region1_handle);
+		s2.setRegion(region1_handle);
+		s1.reset();
+		s2.reset();
+
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000);
+		s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000);
+
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+		s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true);
+
+		{
+			s1.merge(s2);
+			
+			LLSD src = s2.asLLSD(false);
+			LLSD dst = s1.asLLSD(false);
+
+			ensure_equals("merge src has single region", src["regions"].size(), 1);
+			ensure_equals("merge dst has single region", dst["regions"].size(), 1);
+			
+			// Remove time stamps, they're a problem
+			src.erase("duration");
+			src["regions"][0].erase("duration");
+			dst.erase("duration");
+			dst["regions"][0].erase("duration");
+
+			LLSD s2_llsd = get_region(dst, region1_handle);
+			ensure("Region1 is present in dst", s2_llsd.isMap());
+
+			ensure_equals("dst counts come from src only (flipped)", s2_llsd["get_other"]["resp_count"].asInteger(), 3);
+
+			ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum (flipped)",
+										s2_llsd["get_other"]["resp_min"].asReal(), F64(2.7), 20);
+		}
+	}
+
+}
diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp
index b976ac5ea900a9010d06b9da08498e1dd8480a8f..acc6e814bc7a196d1f071cc6c79d4a15b9f68123 100644
--- a/indra/newview/tests/llworldmap_test.cpp
+++ b/indra/newview/tests/llworldmap_test.cpp
@@ -25,13 +25,16 @@
  * $/LicenseInfo$
  */
 
-// Precompiled header: almost always required for newview cpp files
-#include "../llviewerprecompiledheaders.h"
-// Class to test
-#include "../llworldmap.h"
 // Dependencies
-#include "../llviewerimagelist.h"
+#include "linden_common.h"
+#include "llapr.h"
+#include "llsingleton.h"
+#include "lltrans.h"
+#include "lluistring.h"
+#include "../llviewertexture.h"
 #include "../llworldmapmessage.h"
+// Class to test
+#include "../llworldmap.h"
 // Tut header
 #include "../test/lltut.h"
 
@@ -44,34 +47,29 @@
 // * A simulator for a class can be implemented here. Please comment and document thoroughly.
 
 // Stub image calls
-LLViewerImageList::LLViewerImageList() { }
-LLViewerImageList::~LLViewerImageList() { }
-LLViewerImageList gImageList;
-LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id,
-												   BOOL usemipmaps,
-												   BOOL level_immediate,
-												   LLGLint internal_format,
-												   LLGLenum primary_format,
-												   LLHost request_from_host)
-{ return NULL; }
-void LLViewerImage::setBoostLevel(S32 level) { }
-void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode) { }
+void LLViewerTexture::setBoostLevel(S32 ) { }
+void LLViewerTexture::setAddressMode(LLTexUnit::eTextureAddressMode ) { }
+LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTexture(const LLUUID&, BOOL, LLViewerTexture::EBoostLevel, S8,
+																  LLGLint, LLGLenum, LLHost ) { return NULL; }
 
 // Stub related map calls
 LLWorldMapMessage::LLWorldMapMessage() { }
 LLWorldMapMessage::~LLWorldMapMessage() { }
 void LLWorldMapMessage::sendItemRequest(U32 type, U64 handle) { }
 void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent) { }
+
 LLWorldMipmap::LLWorldMipmap() { }
 LLWorldMipmap::~LLWorldMipmap() { }
 void LLWorldMipmap::reset() { }
 void LLWorldMipmap::dropBoostLevels() { }
 void LLWorldMipmap::equalizeBoostLevels() { }
-LLPointer<LLViewerImage> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load)
-{ return NULL; }
+LLPointer<LLViewerFetchedTexture> LLWorldMipmap::getObjectsTile(U32 grid_x, U32 grid_y, S32 level, bool load) { return NULL; }
 
 // Stub other stuff
-BOOL gPacificDaylightTime;
+std::string LLTrans::getString(const std::string &, const LLStringUtil::format_map_t& ) { return std::string("test_trans"); }
+void LLUIString::updateResult() const { }
+void LLUIString::setArg(const std::string& , const std::string& ) { }
+void LLUIString::assign(const std::string& ) { }
 
 // End Stubbing
 // -------------------------------------------------------------------------------------------
@@ -237,7 +235,7 @@ namespace tut
 		// Test 9 : setLandForSaleImage() / getLandForSaleImage()
 		LLUUID id;
 		mSim->setLandForSaleImage(id);
-		LLPointer<LLViewerImage> image = mSim->getLandForSaleImage();
+		LLPointer<LLViewerFetchedTexture> image = mSim->getLandForSaleImage();
 		ensure("LLSimInfo::getLandForSaleImage() test failed", image.isNull());
 		// Test 10 : isPG()
 		mSim->setAccess(SIM_ACCESS_PG);
@@ -370,7 +368,7 @@ namespace tut
  		}
 		// Test 7 : getObjectsTile()
 		try {
-			LLPointer<LLViewerImage> image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1);
+			LLPointer<LLViewerFetchedTexture> image = mWorld->getObjectsTile((U32)(X_WORLD_TEST/REGION_WIDTH_METERS), (U32)(Y_WORLD_TEST/REGION_WIDTH_METERS), 1);
 			ensure("LLWorldMap::getObjectsTile() failed", image.isNull());
 		} catch (...) {
 			fail("LLWorldMap::getObjectsTile() test failed with exception");
diff --git a/indra/newview/tests/llworldmipmap_test.cpp b/indra/newview/tests/llworldmipmap_test.cpp
index 54887ae219c1636b10e876025cef00ac2f9b2ac1..4c0959d1a939205a414fa5a8c7662529a713a07c 100644
--- a/indra/newview/tests/llworldmipmap_test.cpp
+++ b/indra/newview/tests/llworldmipmap_test.cpp
@@ -25,12 +25,12 @@
  * $/LicenseInfo$
  */
 
-// Precompiled header: almost always required for newview cpp files
-#include "../llviewerprecompiledheaders.h"
+// Dependencies
+#include "linden_common.h"
+#include "../llviewertexture.h"
+#include "../llviewercontrol.h"
 // Class to test
 #include "../llworldmipmap.h"
-// Dependencies
-#include "../llviewerimagelist.h"
 // Tut header
 #include "../test/lltut.h"
 
@@ -42,19 +42,14 @@
 // * Do not make any assumption as to how those classes or methods work (i.e. don't copy/paste code)
 // * A simulator for a class can be implemented here. Please comment and document thoroughly.
 
-LLViewerImageList::LLViewerImageList() { }
-LLViewerImageList::~LLViewerImageList() { }
-
-LLViewerImageList gImageList;
+void LLViewerTexture::setBoostLevel(S32 ) { }
+LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const std::string&, BOOL, LLViewerTexture::EBoostLevel, S8, 
+																		 LLGLint, LLGLenum, const LLUUID& ) { return NULL; }
 
-LLViewerImage* LLViewerImageList::getImageFromUrl(const std::string& url,
-												   BOOL usemipmaps,
-												   BOOL level_immediate,
-												   LLGLint internal_format,
-												   LLGLenum primary_format, 
-												   const LLUUID& force_id)
-{ return NULL; }
-void LLViewerImage::setBoostLevel(S32 level) { }
+LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker<LLControlGroup, std::string>(name) { }
+LLControlGroup::~LLControlGroup() { }
+std::string LLControlGroup::getString(const std::string& ) { return std::string("test_url"); }
+LLControlGroup gSavedSettings("test_settings");
 
 // End Stubbing
 // -------------------------------------------------------------------------------------------
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 6c77f8ec38b485ada06fcb15caff09b1816bd6d5..338c62b9fb3a7193971a511872c108a16fd69be4 100644
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -255,12 +255,6 @@ def construct(self):
 
             self.enable_crt_manifest_check()
 
-            # Get kdu dll, continue if missing.
-            try:
-                self.path('llkdu.dll', dst='llkdu.dll')
-            except RuntimeError:
-                print "Skipping llkdu.dll"
-
             # Get llcommon and deps. If missing assume static linkage and continue.
             try:
                 self.path('llcommon.dll')
@@ -625,21 +619,21 @@ def construct(self):
                 libdir = "../../libraries/universal-darwin/lib_release"
                 dylibs = {}
 
-                # need to get the kdu dll from any of the build directories as well
-                for lib in "llkdu", "llcommon":
-                    libfile = "lib%s.dylib" % lib
-                    try:
-                        self.path(self.find_existing_file(os.path.join(os.pardir,
-                                                                       lib,
-                                                                       self.args['configuration'],
-                                                                       libfile),
-                                                          os.path.join(libdir, libfile)),
-                                  dst=libfile)
-                    except RuntimeError:
-                        print "Skipping %s" % libfile
-                        dylibs[lib] = False
-                    else:
-                        dylibs[lib] = True
+                # Need to get the llcommon dll from any of the build directories as well
+                lib = "llcommon"
+                libfile = "lib%s.dylib" % lib
+                try:
+                    self.path(self.find_existing_file(os.path.join(os.pardir,
+                                                                    lib,
+                                                                    self.args['configuration'],
+                                                                    libfile),
+                                                      os.path.join(libdir, libfile)),
+                                                      dst=libfile)
+                except RuntimeError:
+                    print "Skipping %s" % libfile
+                    dylibs[lib] = False
+                else:
+                    dylibs[lib] = True
 
                 if dylibs["llcommon"]:
                     for libfile in ("libapr-1.0.3.7.dylib",
@@ -931,15 +925,6 @@ class Linux_i686Manifest(LinuxManifest):
     def construct(self):
         super(Linux_i686Manifest, self).construct()
 
-        # install either the libllkdu we just built, or a prebuilt one, in
-        # decreasing order of preference.  for linux package, this goes to bin/
-        try:
-            self.path(self.find_existing_file('../llkdu/libllkdu.so',
-                '../../libraries/i686-linux/lib_release_client/libllkdu.so'),
-                  dst='bin/libllkdu.so')
-        except:
-            print "Skipping libllkdu.so - not found"
-
         if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"):
             self.path("libapr-1.so.0")
             self.path("libaprutil-1.so.0")
@@ -955,12 +940,6 @@ def construct(self):
             self.path("libalut.so")
             self.path("libopenal.so", "libopenal.so.1")
             self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname
-            try:
-                    self.path("libkdu.so")
-                    pass
-            except:
-                    print "Skipping libkdu.so - not found"
-                    pass
             try:
                     self.path("libfmod-3.75.so")
                     pass
diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt
index 66c78a86c4e8d3ba17e9c0ef952c4f24d4d44b8a..e9eb3c188447b73ed11313459a250a2d05bebada 100644
--- a/indra/test/CMakeLists.txt
+++ b/indra/test/CMakeLists.txt
@@ -27,6 +27,7 @@ include_directories(
     ${LLXML_INCLUDE_DIRS}
     ${LSCRIPT_INCLUDE_DIRS}
     ${GOOGLEMOCK_INCLUDE_DIRS}
+    ${TUT_INCLUDE_DIR}
     )
 
 set(test_SOURCE_FILES
diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp
index 873fa23db8675b55c8e5cfca4c3311a9411fafee..4a2272032b0a7fd2d1c85136da9822e8e5f805a7 100644
--- a/indra/test_apps/llplugintest/llmediaplugintest.cpp
+++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp
@@ -2220,6 +2220,21 @@ void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent e
 				<< ", height = " << self->getGeometryHeight() 
 				<< std::endl;
 		break;
+
+		case MEDIA_EVENT_AUTH_REQUEST:
+		{
+			//std::cerr <<  "Media event:  MEDIA_EVENT_AUTH_REQUEST, url " << self->getAuthURL() ", realm " << self->getAuthRealm() << std::endl;
+
+			// TODO: display an auth dialog
+			self->sendAuthResponse(false, "", "");
+		}
+		break;
+
+		case MEDIA_EVENT_LINK_HOVERED:
+		{
+			std::cerr <<  "Media event:  MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << std::endl;
+		};
+		break;
 	}
 }
 
diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp
index c17a50e24245aeb3d5846ab5fb569696b744dd7d..e88d1bf811d726056cf63c03ccb5a1910f20b3a4 100644
--- a/indra/viewer_components/updater/llupdatedownloader.cpp
+++ b/indra/viewer_components/updater/llupdatedownloader.cpp
@@ -24,17 +24,20 @@
  */
 
 #include "linden_common.h"
+
+#include "llupdatedownloader.h"
+
 #include <stdexcept>
 #include <boost/format.hpp>
 #include <boost/lexical_cast.hpp>
 #include <curl/curl.h>
 #include "lldir.h"
+#include "llevents.h"
 #include "llfile.h"
 #include "llmd5.h"
 #include "llsd.h"
 #include "llsdserialize.h"
 #include "llthread.h"
-#include "llupdatedownloader.h"
 #include "llupdaterservice.h"
 
 
@@ -45,18 +48,25 @@ class LLUpdateDownloader::Implementation:
 	Implementation(LLUpdateDownloader::Client & client);
 	~Implementation();
 	void cancel(void);
-	void download(LLURI const & uri, std::string const & hash);
+	void download(LLURI const & uri,
+				  std::string const & hash,
+				  std::string const & updateVersion,
+				  bool required);
 	bool isDownloading(void);
 	size_t onHeader(void * header, size_t size);
 	size_t onBody(void * header, size_t size);
+	int onProgress(double downloadSize, double bytesDownloaded);
 	void resume(void);
+	void setBandwidthLimit(U64 bytesPerSecond);
 	
 private:
+	curl_off_t mBandwidthLimit;
 	bool mCancelled;
 	LLUpdateDownloader::Client & mClient;
 	CURL * mCurl;
 	LLSD mDownloadData;
 	llofstream mDownloadStream;
+	unsigned char mDownloadPercent;
 	std::string mDownloadRecordPath;
 	curl_slist * mHeaderList;
 	
@@ -113,9 +123,12 @@ void LLUpdateDownloader::cancel(void)
 }
 
 
-void LLUpdateDownloader::download(LLURI const & uri, std::string const & hash)
+void LLUpdateDownloader::download(LLURI const & uri,
+								  std::string const & hash,
+								  std::string const & updateVersion,
+								  bool required)
 {
-	mImplementation->download(uri, hash);
+	mImplementation->download(uri, hash, updateVersion, required);
 }
 
 
@@ -131,6 +144,12 @@ void LLUpdateDownloader::resume(void)
 }
 
 
+void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond)
+{
+	mImplementation->setBandwidthLimit(bytesPerSecond);
+}
+
+
 
 // LLUpdateDownloader::Implementation
 //-----------------------------------------------------------------------------
@@ -149,14 +168,27 @@ namespace {
 		size_t bytes = blockSize * blocks;
 		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes);
 	}
+
+
+	int progress_callback(void * downloader,
+						  double dowloadTotal,
+						  double downloadNow,
+						  double uploadTotal,
+						  double uploadNow)
+	{
+		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->
+			onProgress(dowloadTotal, downloadNow);
+	}
 }
 
 
 LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client):
 	LLThread("LLUpdateDownloader"),
+	mBandwidthLimit(0),
 	mCancelled(false),
 	mClient(client),
 	mCurl(0),
+	mDownloadPercent(0),
 	mHeaderList(0)
 {
 	CURLcode code = curl_global_init(CURL_GLOBAL_ALL); // Just in case.
@@ -182,12 +214,17 @@ void LLUpdateDownloader::Implementation::cancel(void)
 }
 	
 
-void LLUpdateDownloader::Implementation::download(LLURI const & uri, std::string const & hash)
+void LLUpdateDownloader::Implementation::download(LLURI const & uri,
+												  std::string const & hash,
+												  std::string const & updateVersion,
+												  bool required)
 {
 	if(isDownloading()) mClient.downloadError("download in progress");
 
 	mDownloadRecordPath = downloadMarkerPath();
 	mDownloadData = LLSD();
+	mDownloadData["required"] = required;
+	mDownloadData["update_version"] = updateVersion;
 	try {
 		startDownloading(uri, hash);
 	} catch(DownloadError const & e) {
@@ -233,12 +270,18 @@ void LLUpdateDownloader::Implementation::resume(void)
 				resumeDownloading(fileStatus.st_size);
 			} else if(!validateDownload()) {
 				LLFile::remove(filePath);
-				download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString());
+				download(LLURI(mDownloadData["url"].asString()), 
+						 mDownloadData["hash"].asString(),
+						 mDownloadData["update_version"].asString(),
+						 mDownloadData["required"].asBoolean());
 			} else {
 				mClient.downloadComplete(mDownloadData);
 			}
 		} else {
-			download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString());
+			download(LLURI(mDownloadData["url"].asString()), 
+					 mDownloadData["hash"].asString(),
+					 mDownloadData["update_version"].asString(),
+					 mDownloadData["required"].asBoolean());
 		}
 	} catch(DownloadError & e) {
 		mClient.downloadError(e.what());
@@ -246,6 +289,20 @@ void LLUpdateDownloader::Implementation::resume(void)
 }
 
 
+void LLUpdateDownloader::Implementation::setBandwidthLimit(U64 bytesPerSecond)
+{
+	if((mBandwidthLimit != bytesPerSecond) && isDownloading() && !mDownloadData["required"].asBoolean()) {
+		llassert(mCurl != 0);
+		mBandwidthLimit = bytesPerSecond;
+		CURLcode code = curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, &mBandwidthLimit);
+		if(code != CURLE_OK) LL_WARNS("UpdateDownload") << 
+			"unable to change dowload bandwidth" << LL_ENDL;
+	} else {
+		mBandwidthLimit = bytesPerSecond;
+	}
+}
+
+
 size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size)
 {
 	char const * headerPtr = reinterpret_cast<const char *> (buffer);
@@ -290,6 +347,30 @@ size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size)
 }
 
 
+int LLUpdateDownloader::Implementation::onProgress(double downloadSize, double bytesDownloaded)
+{
+	int downloadPercent = static_cast<int>(100. * (bytesDownloaded / downloadSize));
+	if(downloadPercent > mDownloadPercent) {
+		mDownloadPercent = downloadPercent;
+		
+		LLSD event;
+		event["pump"] = LLUpdaterService::pumpName();
+		LLSD payload;
+		payload["type"] = LLSD(LLUpdaterService::PROGRESS);
+		payload["download_size"] = downloadSize;
+		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;
+}
+
+
 void LLUpdateDownloader::Implementation::run(void)
 {
 	CURLcode code = curl_easy_perform(mCurl);
@@ -343,6 +424,14 @@ void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & u
 	}
 	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPGET, true));
 	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str()));
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSFUNCTION, &progress_callback));
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSDATA, this));
+	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOPROGRESS, false));
+	// 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;
 }
 
 
diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h
index 1b3d7480fd1785747b5d5f6e5509667a03f791ac..0d635640cf2e1d6734f6745dbe51e5b132c94309 100644
--- a/indra/viewer_components/updater/llupdatedownloader.h
+++ b/indra/viewer_components/updater/llupdatedownloader.h
@@ -52,7 +52,10 @@ class LLUpdateDownloader
 	void cancel(void);
 	
 	// Start a new download.
-	void download(LLURI const & uri, std::string const & hash);
+	void download(LLURI const & uri,
+				  std::string const & hash, 
+				  std::string const & updateVersion,
+				  bool required=false);
 	
 	// Returns true if a download is in progress.
 	bool isDownloading(void);
@@ -60,6 +63,9 @@ class LLUpdateDownloader
 	// Resume a partial download.
 	void resume(void);
 	
+	// Set a limit on the dowload rate.
+	void setBandwidthLimit(U64 bytesPerSecond);
+	
 private:
 	boost::shared_ptr<Implementation> mImplementation;
 };
@@ -76,6 +82,7 @@ class LLUpdateDownloader::Client {
 	// url - source (remote) location
 	// hash - the md5 sum that should match the installer file.
 	// path - destination (local) location
+	// required - boolean indicating if this is a required update.
 	// size - the size of the installer in bytes
 	virtual void downloadComplete(LLSD const & data) = 0;
 	
diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp
index 6e69bcf28b8b9d201b701709855dd1e431162b73..d450c068ade8b4bc0c481505a8057a379ec83701 100644
--- a/indra/viewer_components/updater/llupdateinstaller.cpp
+++ b/indra/viewer_components/updater/llupdateinstaller.cpp
@@ -31,6 +31,12 @@
 #include "lldir.h"
 
 
+#if defined(LL_WINDOWS)
+#pragma warning(disable: 4702)      // disable 'unreachable code' so we can use lexical_cast (really!).
+#endif
+#include <boost/lexical_cast.hpp>
+
+
 namespace {
 	class RelocateError {};
 	
@@ -47,7 +53,10 @@ namespace {
 }
 
 
-int ll_install_update(std::string const & script, std::string const & updatePath, LLInstallScriptMode mode)
+int ll_install_update(std::string const & script,
+					  std::string const & updatePath,
+					  bool required,
+					  LLInstallScriptMode mode)
 {
 	std::string actualScriptPath;
 	switch(mode) {
@@ -73,6 +82,7 @@ int ll_install_update(std::string const & script, std::string const & updatePath
 	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();
 	
diff --git a/indra/viewer_components/updater/llupdateinstaller.h b/indra/viewer_components/updater/llupdateinstaller.h
index 6ce08ce6fa51ece998fc77469b473c2fd3f3742d..fe5b1d19b52f2fb7b0651f5ec21acd21efac4ebb 100644
--- a/indra/viewer_components/updater/llupdateinstaller.h
+++ b/indra/viewer_components/updater/llupdateinstaller.h
@@ -42,9 +42,10 @@ enum LLInstallScriptMode {
 // that the current application terminate once this function is called.
 //
 int ll_install_update(
-					   std::string const & script, // Script to execute.
-					   std::string const & updatePath, // Path to update file.
-					   LLInstallScriptMode mode=LL_COPY_INSTALL_SCRIPT_TO_TEMP); // Run in place or copy to temp?
+					  std::string const & script, // Script to execute.
+					  std::string const & updatePath, // Path to update file.
+					  bool required, // Is the update required.
+					  LLInstallScriptMode mode=LL_COPY_INSTALL_SCRIPT_TO_TEMP); // Run in place or copy to temp?
 
 
 //
diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp
index cc60eaead27a9ba49e5804bce09f4e415c277fb5..aa4983a3b6634984df873f706787e7a4a1c128d9 100644
--- a/indra/viewer_components/updater/llupdaterservice.cpp
+++ b/indra/viewer_components/updater/llupdaterservice.cpp
@@ -25,10 +25,11 @@
 
 #include "linden_common.h"
 
+#include "llupdaterservice.h"
+
 #include "llupdatedownloader.h"
 #include "llevents.h"
 #include "lltimer.h"
-#include "llupdaterservice.h"
 #include "llupdatechecker.h"
 #include "llupdateinstaller.h"
 #include "llversionviewer.h"
@@ -98,6 +99,8 @@ class LLUpdaterServiceImpl :
 
 	LLUpdaterService::app_exit_callback_t mAppExitCallback;
 	
+	LLUpdaterService::eUpdaterState mState;
+	
 	LOG_CLASS(LLUpdaterServiceImpl);
 	
 public:
@@ -111,12 +114,15 @@ class LLUpdaterServiceImpl :
 				   const std::string& version);
 	
 	void setCheckPeriod(unsigned int seconds);
+	void setBandwidthLimit(U64 bytesPerSecond);
 
 	void startChecking(bool install_if_ready);
 	void stopChecking();
 	bool isChecking();
+	LLUpdaterService::eUpdaterState getState();
 	
 	void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;}
+	std::string updatedVersion(void);
 
 	bool checkForInstall(bool launchInstaller); // Test if a local install is ready.
 	bool checkForResume(); // Test for resumeable d/l.
@@ -138,7 +144,10 @@ class LLUpdaterServiceImpl :
 	bool onMainLoop(LLSD const & event);
 
 private:
+	std::string mNewVersion;
+	
 	void restartTimer(unsigned int seconds);
+	void setState(LLUpdaterService::eUpdaterState state);
 	void stopTimer();
 };
 
@@ -149,7 +158,8 @@ LLUpdaterServiceImpl::LLUpdaterServiceImpl() :
 	mIsDownloading(false),
 	mCheckPeriod(0),
 	mUpdateChecker(*this),
-	mUpdateDownloader(*this)
+	mUpdateDownloader(*this),
+	mState(LLUpdaterService::INITIAL)
 {
 }
 
@@ -183,6 +193,11 @@ void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds)
 	mCheckPeriod = seconds;
 }
 
+void LLUpdaterServiceImpl::setBandwidthLimit(U64 bytesPerSecond)
+{
+	mUpdateDownloader.setBandwidthLimit(bytesPerSecond);
+}
+
 void LLUpdaterServiceImpl::startChecking(bool install_if_ready)
 {
 	if(mUrl.empty() || mChannel.empty() || mVersion.empty())
@@ -201,10 +216,16 @@ void LLUpdaterServiceImpl::startChecking(bool install_if_ready)
 
 		if(!mIsDownloading)
 		{
+			setState(LLUpdaterService::CHECKING_FOR_UPDATE);
+			
 			// Checking can only occur during the mainloop.
 			// reset the timer to 0 so that the next mainloop event 
 			// triggers a check;
 			restartTimer(0); 
+		} 
+		else
+		{
+			setState(LLUpdaterService::DOWNLOADING);
 		}
 	}
 }
@@ -222,6 +243,8 @@ void LLUpdaterServiceImpl::stopChecking()
         mUpdateDownloader.cancel();
 		mIsDownloading = false;
     }
+	
+	setState(LLUpdaterService::TERMINAL);
 }
 
 bool LLUpdaterServiceImpl::isChecking()
@@ -229,6 +252,16 @@ bool LLUpdaterServiceImpl::isChecking()
 	return mIsChecking;
 }
 
+LLUpdaterService::eUpdaterState LLUpdaterServiceImpl::getState()
+{
+	return mState;
+}
+
+std::string LLUpdaterServiceImpl::updatedVersion(void)
+{
+	return mNewVersion;
+}
+
 bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller)
 {
 	bool foundInstall = false; // return true if install is found.
@@ -266,10 +299,13 @@ bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller)
 		{
 			if(launchInstaller)
 			{
+				setState(LLUpdaterService::INSTALLING);
+				
 				LLFile::remove(update_marker_path());
 
 				int result = ll_install_update(install_script_path(),
 											   update_info["path"].asString(),
+											   update_info["required"].asBoolean(),
 											   install_script_mode());	
 				
 				if((result == 0) && mAppExitCallback)
@@ -304,6 +340,7 @@ bool LLUpdaterServiceImpl::checkForResume()
 			if(download_info["current_version"].asString() == ll_get_version())
 			{
 				mIsDownloading = true;
+				mNewVersion = download_info["update_version"].asString();
 				mUpdateDownloader.resume();
 				result = true;
 			}
@@ -333,8 +370,11 @@ void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion,
 										  std::string const & hash)
 {
 	stopTimer();
+	mNewVersion = newVersion;
 	mIsDownloading = true;
-	mUpdateDownloader.download(uri, hash);
+	mUpdateDownloader.download(uri, hash, newVersion, false);
+	
+	setState(LLUpdaterService::DOWNLOADING);
 }
 
 void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion,
@@ -342,8 +382,11 @@ void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion,
 										  std::string const & hash)
 {
 	stopTimer();
+	mNewVersion = newVersion;
 	mIsDownloading = true;
-	mUpdateDownloader.download(uri, hash);
+	mUpdateDownloader.download(uri, hash, newVersion, true);
+	
+	setState(LLUpdaterService::DOWNLOADING);
 }
 
 void LLUpdaterServiceImpl::upToDate(void)
@@ -352,6 +395,8 @@ void LLUpdaterServiceImpl::upToDate(void)
 	{
 		restartTimer(mCheckPeriod);
 	}
+	
+	setState(LLUpdaterService::UP_TO_DATE);
 }
 
 void LLUpdaterServiceImpl::downloadComplete(LLSD const & data) 
@@ -367,8 +412,12 @@ void LLUpdaterServiceImpl::downloadComplete(LLSD const & data)
 	event["pump"] = LLUpdaterService::pumpName();
 	LLSD payload;
 	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE);
+	payload["required"] = data["required"];
+	payload["version"] = mNewVersion;
 	event["payload"] = payload;
 	LLEventPumps::instance().obtain("mainlooprepeater").post(event);
+	
+	setState(LLUpdaterService::TERMINAL);
 }
 
 void LLUpdaterServiceImpl::downloadError(std::string const & message) 
@@ -390,6 +439,8 @@ void LLUpdaterServiceImpl::downloadError(std::string const & message)
 	payload["message"] = message;
 	event["payload"] = payload;
 	LLEventPumps::instance().obtain("mainlooprepeater").post(event);
+
+	setState(LLUpdaterService::FAILURE);
 }
 
 void LLUpdaterServiceImpl::restartTimer(unsigned int seconds)
@@ -402,6 +453,28 @@ void LLUpdaterServiceImpl::restartTimer(unsigned int seconds)
 		sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1));
 }
 
+void LLUpdaterServiceImpl::setState(LLUpdaterService::eUpdaterState state)
+{
+	if(state != mState)
+	{
+		mState = state;
+		
+		LLSD event;
+		event["pump"] = LLUpdaterService::pumpName();
+		LLSD payload;
+		payload["type"] = LLSD(LLUpdaterService::STATE_CHANGE);
+		payload["state"] = state;
+		event["payload"] = payload;
+		LLEventPumps::instance().obtain("mainlooprepeater").post(event);
+		
+		LL_INFOS("UpdaterService") << "setting state to " << state << LL_ENDL;
+	}
+	else 
+	{
+		; // State unchanged; noop.
+	}
+}
+
 void LLUpdaterServiceImpl::stopTimer()
 {
 	mTimer.stop();
@@ -417,6 +490,12 @@ bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event)
 		// Check for failed install.
 		if(LLFile::isfile(ll_install_failed_marker_path()))
 		{
+			int requiredValue = 0; 
+			{
+				llifstream stream(ll_install_failed_marker_path());
+				stream >> requiredValue;
+				if(stream.fail()) requiredValue = 0;
+			}
 			// TODO: notify the user.
 			llinfos << "found marker " << ll_install_failed_marker_path() << llendl;
 			llinfos << "last install attempt failed" << llendl;
@@ -424,11 +503,15 @@ bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event)
 			
 			LLSD event;
 			event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR);
+			event["required"] = LLSD(requiredValue);
 			LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event);
+			
+			setState(LLUpdaterService::TERMINAL);
 		}
 		else
 		{
 			mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion);
+			setState(LLUpdaterService::CHECKING_FOR_UPDATE);
 		}
 	} 
 	else 
@@ -449,6 +532,11 @@ std::string const & LLUpdaterService::pumpName(void)
 	return name;
 }
 
+bool LLUpdaterService::updateReadyToInstall(void)
+{
+	return LLFile::isfile(update_marker_path());
+}
+
 LLUpdaterService::LLUpdaterService()
 {
 	if(gUpdater.expired())
@@ -480,6 +568,11 @@ void LLUpdaterService::setCheckPeriod(unsigned int seconds)
 {
 	mImpl->setCheckPeriod(seconds);
 }
+
+void LLUpdaterService::setBandwidthLimit(U64 bytesPerSecond)
+{
+	mImpl->setBandwidthLimit(bytesPerSecond);
+}
 	
 void LLUpdaterService::startChecking(bool install_if_ready)
 {
@@ -496,11 +589,21 @@ bool LLUpdaterService::isChecking()
 	return mImpl->isChecking();
 }
 
+LLUpdaterService::eUpdaterState LLUpdaterService::getState()
+{
+	return mImpl->getState();
+}
+
 void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb)
 {
 	return mImpl->setAppExitCallback(aecb);
 }
 
+std::string LLUpdaterService::updatedVersion(void)
+{
+	return mImpl->updatedVersion();
+}
+
 
 std::string const & ll_get_version(void) {
 	static std::string version("");
diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h
index 752a6f834b4f93a7204a9e8573d30812f9d7de90..421481bc43d0adfe44ad6d6bb12dbd8bc22e2d93 100644
--- a/indra/viewer_components/updater/llupdaterservice.h
+++ b/indra/viewer_components/updater/llupdaterservice.h
@@ -43,12 +43,27 @@ class LLUpdaterService
 	// Name of the event pump through which update events will be delivered.
 	static std::string const & pumpName(void);
 	
+	// Returns true if an update has been completely downloaded and is now ready to install.
+	static bool updateReadyToInstall(void);
+	
 	// Type codes for events posted by this service.  Stored the event's 'type' element.
-	enum eUpdateEvent {
+	enum eUpdaterEvent {
 		INVALID,
 		DOWNLOAD_COMPLETE,
 		DOWNLOAD_ERROR,
-		INSTALL_ERROR
+		INSTALL_ERROR,
+		PROGRESS,
+		STATE_CHANGE
+	};
+	
+	enum eUpdaterState {
+		INITIAL,
+		CHECKING_FOR_UPDATE,
+		DOWNLOADING,
+		INSTALLING,
+		UP_TO_DATE,
+		TERMINAL,
+		FAILURE
 	};
 
 	LLUpdaterService();
@@ -61,10 +76,12 @@ class LLUpdaterService
 				    const std::string& version);
 
 	void setCheckPeriod(unsigned int seconds);
+	void setBandwidthLimit(U64 bytesPerSecond);
 	
 	void startChecking(bool install_if_ready = false);
 	void stopChecking();
 	bool isChecking();
+	eUpdaterState getState();
 
 	typedef boost::function<void (void)> app_exit_callback_t;
 	template <typename F>
@@ -73,6 +90,11 @@ class LLUpdaterService
 		app_exit_callback_t aecb = callable;
 		setImplAppExitCallback(aecb);
 	}
+	
+	// If an update is or has been downloaded, this method will return the
+	// version string for that update.  An empty string will be returned
+	// otherwise.
+	std::string updatedVersion(void);
 
 private:
 	boost::shared_ptr<LLUpdaterServiceImpl> mImpl;
diff --git a/indra/viewer_components/updater/scripts/darwin/update_install b/indra/viewer_components/updater/scripts/darwin/update_install
index b174b3570a2bf4854c11d021d4523db931449a59..6a95f96d86f520e5543ac3925544d55b7204716a 100644
--- a/indra/viewer_components/updater/scripts/darwin/update_install
+++ b/indra/viewer_components/updater/scripts/darwin/update_install
@@ -5,5 +5,6 @@
 # to a marker file which should be created if the installer fails.q
 #
 
-open ../Resources/mac-updater.app --args -dmg "$1" -name "Second Life Viewer 2" -marker "$2"
+cd "$(dirname "$0")"
+(../Resources/mac-updater.app/Contents/MacOS/mac-updater -dmg "$1" -name "Second Life Viewer 2"; if [ $? -ne 0 ]; then echo $3 >> "$2"; fi;) &
 exit 0
diff --git a/indra/viewer_components/updater/scripts/linux/update_install b/indra/viewer_components/updater/scripts/linux/update_install
index fef5ef7d09a95393d8e9bf8edf1a21fedd2e8416..88451340eca36ace15d01f845bba765b9933a7aa 100644
--- a/indra/viewer_components/updater/scripts/linux/update_install
+++ b/indra/viewer_components/updater/scripts/linux/update_install
@@ -1,10 +1,10 @@
 #! /bin/bash
-INSTALL_DIR=$(cd "$(dirname $0)/.." ; pwd)
-export LD_LIBRARY_PATH=$INSTALL_DIR/lib
+INSTALL_DIR=$(cd "$(dirname "$0")/.." ; pwd)
+export LD_LIBRARY_PATH="$INSTALL_DIR/lib"
 bin/linux-updater.bin --file "$1" --dest "$INSTALL_DIR" --name "Second Life Viewer 2" --stringsdir "$INSTALL_DIR/skins/default/xui/en" --stringsfile "strings.xml"
 
 if [ $? -ne 0 ]
-   then touch $2
+   then echo $3 >> "$2"
 fi
 
-rm -f $1
+rm -f "$1"
diff --git a/indra/viewer_components/updater/scripts/windows/update_install.bat b/indra/viewer_components/updater/scripts/windows/update_install.bat
index 42e148a707e11433798ac33e3d2e56e86acac394..96687226a8d3d243cdb268707a4eecefbb1d33e1 100644
--- a/indra/viewer_components/updater/scripts/windows/update_install.bat
+++ b/indra/viewer_components/updater/scripts/windows/update_install.bat
@@ -1,3 +1,3 @@
 start /WAIT %1 /SKIP_DIALOGS
-IF ERRORLEVEL 1 ECHO %ERRORLEVEL% > %2
+IF ERRORLEVEL 1 ECHO %3 > %2
 DEL %1
diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp
index 04ed4e63640b24aab81ef74db2960cfe5dc7cbdb..5f8cd28f2976501f23f6eef2360ce4c228f6d215 100644
--- a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp
+++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp
@@ -48,7 +48,7 @@ void LLUpdateChecker::check(std::string const & protocolVersion, std::string con
 								  std::string const & servicePath, std::string channel, std::string version)
 {}
 LLUpdateDownloader::LLUpdateDownloader(Client & ) {}
-void LLUpdateDownloader::download(LLURI const & , std::string const &){}
+void LLUpdateDownloader::download(LLURI const & , std::string const &, std::string const &, bool){}
 
 class LLDir_Mock : public LLDir
 {
@@ -101,8 +101,9 @@ std::string LLUpdateDownloader::downloadMarkerPath(void)
 
 void LLUpdateDownloader::resume(void) {}
 void LLUpdateDownloader::cancel(void) {}
+void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) {}
 
-int ll_install_update(std::string const &, std::string const &, LLInstallScriptMode)
+int ll_install_update(std::string const &, std::string const &, bool, LLInstallScriptMode)
 {
 	return 0;
 }
diff --git a/install.xml b/install.xml
index 98e983299e413afa5c879da1852f30389cf162fa..13abaac1c1ce5a6ae5c9eecb3e2c00b2c7e42ad4 100644
--- a/install.xml
+++ b/install.xml
@@ -233,16 +233,16 @@
           <key>darwin</key>
           <map>
             <key>md5sum</key>
-            <string>752e295ccb17f0dcb7c0167db3ad1e69</string>
+            <string>ca8f0134fa5ab6f34a6eeb8d0896c9b0</string>
             <key>url</key>
-            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.20.1-darwin-20100606.tar.bz2</uri>
+            <uri>https://s3.amazonaws.com/automated-builds-secondlife-com/hg/repo/brad_curl-autobuild/rev/216961/arch/Darwin/installer/curl-7.21.1-darwin-20101214.tar.bz2</uri>
           </map>
           <key>linux</key>
           <map>
             <key>md5sum</key>
-            <string>a20e73f2e7d6a032ff25a5161b1b7394</string>
+            <string>9c9b629b62bf874d550c430ad678dc04</string>
             <key>url</key>
-            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.20.1-linux-20100527.tar.bz2</uri>
+            <uri>https://s3.amazonaws.com/automated-builds-secondlife-com/hg/repo/brad_curl-autobuild/rev/216961/arch/Linux/installer/curl-7.21.1-linux-20101215.tar.bz2</uri>
           </map>
           <key>linux64</key>
           <map>
@@ -254,9 +254,9 @@
           <key>windows</key>
           <map>
             <key>md5sum</key>
-            <string>b28856d3d02ee680353ae440561a6579</string>
+            <string>48691883065a82d53691d73aae81d4c1</string>
             <key>url</key>
-            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/curl-7.20.1-windows-20100611.tar.bz2</uri>
+            <uri>https://s3.amazonaws.com/automated-builds-secondlife-com/hg/repo/brad_curl-autobuild/rev/216961/arch/CYGWIN/installer/curl-7.21.1-windows-20101214.tar.bz2</uri>
           </map>
         </map>
       </map>
@@ -830,23 +830,23 @@
           <key>darwin</key>
           <map>
             <key>md5sum</key>
-            <string>ae18dd120807a46ac961b881a631ad94</string>
+            <string>8261994de5af6581e08c26fefe1b2810</string>
             <key>url</key>
-            <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/indra_private-2.1.1-darwin-20100820.tar.bz2</uri>
+            <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/kdu-6.4.1-darwin-20101123.tar.bz2</uri>
           </map>
           <key>linux</key>
           <map>
             <key>md5sum</key>
-            <string>b1f15bbabb68445e55ce23a2aeaca598</string>
+            <string>ed3e58899a424684dad49c94ba3813e7</string>
             <key>url</key>
-            <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/indra_private-2.1.1-linux-20100826.tar.bz2</uri>
+            <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/kdu-6.4.1-linux-20101124.tar.bz2</uri>
           </map>
           <key>windows</key>
           <map>
             <key>md5sum</key>
-            <string>0e2fe621ce99085eba00d86d9a3bc130</string>
+            <string>066e089a5d9faeaf131e1f4e4860a163</string>
             <key>url</key>
-            <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/indra_private-2.1.1-windows-20100820.tar.bz2</uri>
+            <uri>scp:install-packages.lindenlab.com:/local/www/install-packages/doc/kdu-6.4.1-windows-20101123.tar.bz2</uri>
           </map>
         </map>
       </map>
@@ -981,9 +981,9 @@ anguage Infrstructure (CLI) international standard</string>
           <key>darwin</key>
           <map>
             <key>md5sum</key>
-            <string>34d9e4c93678a422cf80521bf0cd7628</string>
+            <string>66c46841825ab4969ec875b5c8f9b24c</string>
             <key>url</key>
-            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/llqtwebkit-4.6-darwin-20100914.tar.bz2</uri>
+            <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-darwin-qt4.7.1-20101221.tar.bz2</uri>
           </map>
           <key>linux</key>
           <map>
@@ -995,9 +995,9 @@ anguage Infrstructure (CLI) international standard</string>
           <key>windows</key>
           <map>
             <key>md5sum</key>
-            <string>4b8412833c00f8cdaba26808f0ddb404</string>
+            <string>b678c4d18ea8e4fab42b20f8d0b2629a</string>
             <key>url</key>
-            <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.6-20100916.tar.bz2</uri>
+            <uri>http://viewer-source-downloads.s3.amazonaws.com/install_pkgs/llqtwebkit-windows-qt4.7.1-20101221.tar.bz2</uri>
           </map>
         </map>
       </map>
diff --git a/scripts/install.py b/scripts/install.py
index c2adf4d0a27ce79b9edfb4a28ea594141a13039b..d3bdf52283778899aab8476a71862f012389bb20 100755
--- a/scripts/install.py
+++ b/scripts/install.py
@@ -486,7 +486,7 @@ def _uninstall(self, installables):
         for filename in remove_file_list:
             print "rm",filename
             if not self._dryrun:
-                if os.path.exists(filename):
+                if os.path.lexists(filename):
                     remove_dir_set.add(os.path.dirname(filename))
                     try:
                         os.remove(filename)