diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
index c300fe55d9388f46b99e9346dda5bba9e7279b82..87669574c2543860de653344ef14484115fcb17e 100644
--- a/indra/llui/llui.cpp
+++ b/indra/llui/llui.cpp
@@ -61,6 +61,8 @@
 // for XUIParse
 #include "llquaternion.h"
 #include <boost/tokenizer.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
 
 //
 // Globals
@@ -2020,6 +2022,53 @@ void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y)
 	view->translateIntoRectWithExclusion( virtual_window_rect, mouse_rect, FALSE );
 }
 
+LLView* LLUI::resolvePath(LLView* context, const std::string& path)
+{
+	// Nothing about resolvePath() should require non-const LLView*. If caller
+	// wants non-const, call the const flavor and then cast away const-ness.
+	return const_cast<LLView*>(resolvePath(const_cast<const LLView*>(context), path));
+}
+
+const LLView* LLUI::resolvePath(const LLView* context, const std::string& path)
+{
+	// Create an iterator over slash-separated parts of 'path'. Dereferencing
+	// this iterator returns an iterator_range over the substring. Unlike
+	// LLStringUtil::getTokens(), this split_iterator doesn't combine adjacent
+	// delimiters: leading/trailing slash produces an empty substring, double
+	// slash produces an empty substring. That's what we need.
+	boost::split_iterator<std::string::const_iterator> ti(path, boost::first_finder("/")), tend;
+
+	if (ti == tend)
+	{
+		// 'path' is completely empty, no navigation
+		return context;
+	}
+
+	// leading / means "start at root"
+	if (ti->empty())
+	{
+		context = getRootView();
+		++ti;
+	}
+
+	bool recurse = false;
+	for (; ti != tend && context; ++ti)
+	{
+		if (ti->empty()) 
+		{
+			recurse = true;
+		}
+		else
+		{
+			std::string part(ti->begin(), ti->end());
+			context = context->findChildView(part, recurse);
+			recurse = false;
+		}
+	}
+
+	return context;
+}
+
 
 // LLLocalClipRect and LLScreenClipRect moved to lllocalcliprect.h/cpp
 
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
index 62d10df8b28852db94925ffdffa401f9c12c7d46..50cb9e663200f91c5b50ed2fc8f559c273c3b236 100644
--- a/indra/llui/llui.h
+++ b/indra/llui/llui.h
@@ -185,6 +185,33 @@ class LLUI
 	//helper functions (should probably move free standing rendering helper functions here)
 	static LLView* getRootView() { return sRootView; }
 	static void setRootView(LLView* view) { sRootView = view; }
+	/**
+	 * Walk the LLView tree to resolve a path
+	 * Paths can be discovered using Develop > XUI > Show XUI Paths
+	 *
+	 * A leading "/" indicates the root of the tree is the starting
+	 * position of the search, (otherwise the context node is used)
+	 *
+	 * Adjacent "//" mean that the next level of the search is done
+	 * recursively ("descendant" rather than "child").
+	 *
+	 * Return values: If no match is found, NULL is returned,
+	 * otherwise the matching LLView* is returned.
+	 *
+	 * Examples:
+	 *
+	 * "/" -> return the root view
+	 * "/foo" -> find "foo" as a direct child of the root
+	 * "foo" -> find "foo" as a direct child of the context node
+	 * "//foo" -> find the first "foo" child anywhere in the tree
+	 * "/foo/bar" -> find "foo" as direct child of the root, and
+	 *      "bar" as a direct child of "foo"
+	 * "//foo//bar/baz" -> find the first "foo" anywhere in the
+	 *      tree, the first "bar" anywhere under it, and "baz"
+	 *      as a direct child of that
+	 */
+	static const LLView* resolvePath(const LLView* context, const std::string& path);
+	static LLView* resolvePath(LLView* context, const std::string& path);
 	static std::string locateSkin(const std::string& filename);
 	static void setMousePositionScreen(S32 x, S32 y);
 	static void getMousePositionScreen(S32 *x, S32 *y);
diff --git a/indra/newview/lluilistener.cpp b/indra/newview/lluilistener.cpp
index d02d126cb5493bfdd906f2c2c5121cb16daacacc..6b2cd71d406ac4a493eb6de9dd66a6b9d2b2d2b5 100644
--- a/indra/newview/lluilistener.cpp
+++ b/indra/newview/lluilistener.cpp
@@ -32,10 +32,9 @@
 #include "lluilistener.h"
 // STL headers
 // std headers
-#include <sstream>
 // external library headers
 // other Linden headers
-#include "llviewerwindow.h" // to get root view
+#include "llui.h" // getRootView(), resolvePath()
 #include "lluictrl.h"
 #include "llerror.h"
 
@@ -55,7 +54,7 @@ LLUIListener::LLUIListener():
         "For the UI control identified by the path in [\"path\"], return the control's\n"
         "current value as [\"value\"] reply.",
         &LLUIListener::getValue,
-        LLSD().with("path", LLSD()));
+        LLSDMap("path", LLSD())("reply", LLSD()));
 }
 
 void LLUIListener::call(const LLSD& event) const
@@ -81,92 +80,12 @@ void LLUIListener::call(const LLSD& event) const
     }
 }
 
-// Split string given a single character delimiter.
-// Note that this returns empty strings for leading, trailing, and adjacent
-// delimiters, such as "/foo/bar//baz/" -> ["", "foo", "bar", "", "baz", "" ]
-std::vector<std::string> split(const std::string& s, char delim)
-{
-	std::stringstream ss(s);
-	std::string item;
-	std::vector<std::string> items;
-	while (std::getline(ss, item, delim))
-	{
-		items.push_back(item);
-	}
-	return items;
-}
-
-// Walk the LLView tree to resolve a path
-// Paths can be discovered using Develop > XUI > Show XUI Paths
-//
-// A leading "/" indicates the root of the tree is the starting
-// position of the search, (otherwise the context node is used)
-//
-// Adjacent "//" mean that the next level of the search is done
-// recursively ("descendant" rather than "child").
-//
-// Return values: If no match is found, NULL is returned,
-// otherwise the matching LLView* is returned.
-//
-// Examples:
-//
-// "/" -> return the root view
-// "/foo" -> find "foo" as a direct child of the root
-// "foo" -> find "foo" as a direct child of the context node
-// "//foo" -> find the first "foo" child anywhere in the tree
-// "/foo/bar" -> find "foo" as direct child of the root, and
-//      "bar" as a direct child of "foo"
-// "//foo//bar/baz" -> find the first "foo" anywhere in the
-//      tree, the first "bar" anywhere under it, and "baz"
-//      as a direct child of that
-//
-const LLView* resolve_path(const LLView* context, const std::string path) 
-{
-	std::vector<std::string> parts = split(path, '/');
-	
-	if (parts.size() == 0)
-	{
-		return context;
-	}
-
-	std::vector<std::string>::iterator it = parts.begin();
-	
-	// leading / means "start at root"
-	if ((*it).length() == 0)
-	{
-		context = (LLView*)(gViewerWindow->getRootView());
-		it++;
-	}
-	
-	bool recurse = false;
-	for (; it != parts.end() && context; it++) 
-    {
-		std::string part = *it;
-
-		if (part.length() == 0) 
-        {
-			recurse = true;
-        }
-		else
-        {
-			const LLView* found = context->findChildView(part, recurse);
-			if (!found) 
-				return NULL;
-
-			context = found;
-			recurse = false;
-        }
-    }
-	
-    return context;
-}
-
 void LLUIListener::getValue(const LLSD&event) const
 {
     LLSD reply = LLSD::emptyMap();
 
-    const LLView* root = (LLView*)(gViewerWindow->getRootView());
-    const LLView* view = resolve_path(root, event["path"].asString());
+    const LLView* root = LLUI::getRootView();
+    const LLView* view = LLUI::resolvePath(root, event["path"].asString());
     const LLUICtrl* ctrl(dynamic_cast<const LLUICtrl*>(view));
 
     if (ctrl)