diff --git a/indra/llcommon/lleventapi.cpp b/indra/llcommon/lleventapi.cpp
index 4270c8b511e6f652d96b4a0928cd7398cbfdded9..ff5459c1eb843c4476979aeeedd420a5080ee7cd 100644
--- a/indra/llcommon/lleventapi.cpp
+++ b/indra/llcommon/lleventapi.cpp
@@ -34,6 +34,7 @@
 // std headers
 // external library headers
 // other Linden headers
+#include "llerror.h"
 
 LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
     lbase(name, field),
@@ -45,3 +46,32 @@ LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const s
 LLEventAPI::~LLEventAPI()
 {
 }
+
+LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
+    mResp(seed),
+    mReq(request),
+    mKey(replyKey)
+{}
+
+LLEventAPI::Response::~Response()
+{
+    // When you instantiate a stack Response object, if the original
+    // request requested a reply, send it when we leave this block, no
+    // matter how.
+    sendReply(mResp, mReq, mKey);
+}
+
+void LLEventAPI::Response::warn(const std::string& warning)
+{
+    LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL;
+    mResp["warnings"].append(warning);
+}
+
+void LLEventAPI::Response::error(const std::string& error)
+{
+    // Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut
+    // down altogether.
+    LL_WARNS("LLEventAPI::Response") << error << LL_ENDL;
+
+    mResp["error"] = error;
+}
diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h
index d75d521e8ee9e6cf9950d8b63c5f7a4eecaf02f0..64d038ade41a14140c8ee899265db47fabda47a7 100644
--- a/indra/llcommon/lleventapi.h
+++ b/indra/llcommon/lleventapi.h
@@ -76,6 +76,84 @@ class LL_COMMON_API LLEventAPI: public LLDispatchListener,
         LLEventDispatcher::add(name, desc, callable, required);
     }
 
+    /**
+     * Instantiate a Response object in any LLEventAPI subclass method that
+     * wants to guarantee a reply (if requested) will be sent on exit from the
+     * method. The reply will be sent if request.has(@a replyKey), default
+     * "reply". If specified, the value of request[replyKey] is the name of
+     * the LLEventPump on which to send the reply. Conventionally you might
+     * code something like:
+     *
+     * @code
+     * void MyEventAPI::someMethod(const LLSD& request)
+     * {
+     *     // Send a reply event as long as request.has("reply")
+     *     Response response(LLSD(), request);
+     *     // ...
+     *     // will be sent in reply event
+     *     response["somekey"] = some_data;
+     * }
+     * @endcode
+     */
+    class LL_COMMON_API Response
+    {
+    public:
+        /**
+         * Instantiating a Response object in an LLEventAPI subclass method
+         * ensures that, if desired, a reply event will be sent.
+         *
+         * @a seed is the initial reply LLSD that will be further decorated before
+         * being sent as the reply
+         *
+         * @a request is the incoming request LLSD; we particularly care about
+         * [replyKey] and ["reqid"]
+         *
+         * @a replyKey [default "reply"] is the string name of the LLEventPump
+         * on which the caller wants a reply. If <tt>(!
+         * request.has(replyKey))</tt>, no reply will be sent.
+         */
+        Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply");
+        ~Response();
+
+        /**
+         * @code
+         * if (some condition)
+         * {
+         *     response.warn("warnings are logged and collected in [\"warnings\"]");
+         * }
+         * @endcode
+         */
+        void warn(const std::string& warning);
+        /**
+         * @code
+         * if (some condition isn't met)
+         * {
+         *     // In a function returning void, you can validly 'return
+         *     // expression' if the expression is itself of type void. But
+         *     // returning is up to you; response.error() has no effect on
+         *     // flow of control.
+         *     return response.error("error message, logged and also sent as [\"error\"]");
+         * }
+         * @endcode
+         */
+        void error(const std::string& error);
+
+        /**
+         * set other keys...
+         *
+         * @code
+         * // set any attributes you want to be sent in the reply
+         * response["info"] = some_value;
+         * // ...
+         * response["ok"] = went_well;
+         * @endcode
+         */
+        LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
+
+        LLSD mResp, mReq;
+        LLSD::String mKey;
+    };
+
 private:
     std::string mDesc;
 };
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index ff03506e841a8771913895f0a065934cd5f840ef..db1ea4792b32546971a14e2c0cd30b7eae50802c 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -591,6 +591,17 @@ void LLReqID::stamp(LLSD& response) const
 
 bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
 {
+    // If the original request has no value for replyKey, it's pointless to
+    // construct or send a reply event: on which LLEventPump should we send
+    // it? Allow that to be optional: if the caller wants to require replyKey,
+    // it can so specify when registering the operation method.
+    if (! request.has(replyKey))
+    {
+        return false;
+    }
+
+    // Here the request definitely contains replyKey; reasonable to proceed.
+
     // Copy 'reply' to modify it.
     LLSD newreply(reply);
     // Get the ["reqid"] element from request
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index b3b2f4ae56410eb1ae3ef4233ea0f71db23b99f4..0ab883cb7055d54ba265cf9dae7f80bf8c943972 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -112,6 +112,7 @@ set(llui_SOURCE_FILES
     llurlmatch.cpp
     llurlregistry.cpp
     llviewborder.cpp
+    llviewinject.cpp
     llviewmodel.cpp
     llview.cpp
     llviewquery.cpp
@@ -216,6 +217,7 @@ set(llui_HEADER_FILES
     llurlmatch.h
     llurlregistry.h
     llviewborder.h
+    llviewinject.h
     llviewmodel.h
     llview.h
     llviewquery.h
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index 659a54cc6e00d62cbba4b5c376351c662d985541..a630a03c92e89d1151e6cf45fc756c9cfab062d1 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -31,7 +31,10 @@
 #include "llview.h"
 
 #include <cassert>
+#include <sstream>
 #include <boost/tokenizer.hpp>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
 
 #include "llrender.h"
 #include "llevent.h"
@@ -66,6 +69,8 @@ S32		LLView::sLastLeftXML = S32_MIN;
 S32		LLView::sLastBottomXML = S32_MIN;
 std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;
 
+LLView::DrilldownFunc LLView::sDrilldown =
+	boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);
 
 //#if LL_DEBUG
 BOOL LLView::sIsDrawing = FALSE;
@@ -346,13 +351,11 @@ void LLView::removeChild(LLView* child)
 LLView::ctrl_list_t LLView::getCtrlList() const
 {
 	ctrl_list_t controls;
-	for(child_list_const_iter_t iter = mChildList.begin();
-		iter != mChildList.end();
-		iter++)
+	BOOST_FOREACH(LLView* viewp, mChildList)
 	{
-		if((*iter)->isCtrl())
+		if(viewp->isCtrl())
 		{
-			controls.push_back(static_cast<LLUICtrl*>(*iter));
+			controls.push_back(static_cast<LLUICtrl*>(viewp));
 		}
 	}
 	return controls;
@@ -428,6 +431,36 @@ BOOL LLView::isInEnabledChain() const
 	return enabled;
 }
 
+static void buildPathname(std::ostream& out, const LLView* view)
+{
+	if (! (view && view->getParent()))
+	{
+		return; // Don't include root in the path.
+	}
+
+	buildPathname(out, view->getParent());
+
+	// Build pathname into ostream on the way back from recursion.
+	out << '/' << view->getName();
+}
+
+std::string LLView::getPathname() const
+{
+	std::ostringstream out;
+	buildPathname(out, this);
+	return out.str();
+}
+
+//static
+std::string LLView::getPathname(const LLView* view)
+{
+    if (! view)
+    {
+        return "NULL";
+    }
+    return view->getPathname();
+}
+
 // virtual
 BOOL LLView::canFocusChildren() const
 {
@@ -574,9 +607,8 @@ void LLView::deleteAllChildren()
 
 void LLView::setAllChildrenEnabled(BOOL b)
 {
-	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+	BOOST_FOREACH(LLView* viewp, mChildList)
 	{
-		LLView* viewp = *child_it;
 		viewp->setEnabled(b);
 	}
 }
@@ -602,9 +634,8 @@ void LLView::setVisible(BOOL visible)
 // virtual
 void LLView::handleVisibilityChange ( BOOL new_visibility )
 {
-	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+	BOOST_FOREACH(LLView* viewp, mChildList)
 	{
-		LLView* viewp = *child_it;
 		// only views that are themselves visible will have their overall visibility affected by their ancestors
 		if (viewp->getVisible())
 		{
@@ -646,56 +677,173 @@ void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
 	//llinfos << "Mouse left " << getName() << llendl;
 }
 
+bool LLView::visibleAndContains(S32 local_x, S32 local_y)
+{
+	return sDrilldown(this, local_x, local_y)
+		&& getVisible();
+}
+
+bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y)
+{
+	return visibleAndContains(local_x, local_y)
+		&& getEnabled();
+}
+
+void LLView::logMouseEvent()
+{
+	if (sDebugMouseHandling)
+	{
+		sMouseHandlerMessage = std::string("/") + mName + sMouseHandlerMessage;
+	}
+}
+
+template <typename METHOD, typename CHARTYPE>
+LLView* LLView::childrenHandleCharEvent(const std::string& desc, const METHOD& method,
+										CHARTYPE c, MASK mask)
+{
+	if ( getVisible() && getEnabled() )
+	{
+		BOOST_FOREACH(LLView* viewp, mChildList)
+		{
+			if ((viewp->*method)(c, mask, TRUE))
+			{
+				if (LLView::sDebugKeys)
+				{
+					llinfos << desc << " handled by " << viewp->getName() << llendl;
+				}
+				return viewp;
+			}
+		}
+	}
+    return NULL;
+}
+
+// XDATA might be MASK, or S32 clicks
+template <typename METHOD, typename XDATA>
+LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra)
+{
+	BOOST_FOREACH(LLView* viewp, mChildList)
+	{
+		S32 local_x = x - viewp->getRect().mLeft;
+		S32 local_y = y - viewp->getRect().mBottom;
+
+		if (!viewp->visibleEnabledAndContains(local_x, local_y))
+		{
+			continue;
+		}
+
+		if ((viewp->*method)( local_x, local_y, extra )
+			|| viewp->blockMouseEvent( local_x, local_y ))
+		{
+			viewp->logMouseEvent();
+			return viewp;
+		}
+	}
+	return NULL;
+}
 
 LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+	BOOST_FOREACH(LLView* viewp, mChildList)
 	{
-		LLView* viewp = *child_it;
 		S32 local_x = x - viewp->getRect().mLeft;
 		S32 local_y = y - viewp->getRect().mBottom;
-		if(!viewp->pointInView(local_x, local_y) 
-			|| !viewp->getVisible())
+		// Differs from childrenHandleMouseEvent() in that we want to offer
+		// tooltips even for disabled widgets.
+		if(!viewp->visibleAndContains(local_x, local_y))
 		{
 			continue;
 		}
 
-		if (viewp->handleToolTip(local_x, local_y, mask) )
+		if (viewp->handleToolTip(local_x, local_y, mask) 
+			|| viewp->blockMouseEvent(local_x, local_y))
 		{
-			if (sDebugMouseHandling)
-			{
-				sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-			}
+			viewp->logMouseEvent();
+			return viewp;
+		}
+	}
+	return NULL;
+}
 
-			handled_view = viewp;
-			break;
+LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
+									   BOOL drop,
+									   EDragAndDropType cargo_type,
+									   void* cargo_data,
+									   EAcceptance* accept,
+									   std::string& tooltip_msg)
+{
+	// default to not accepting drag and drop, will be overridden by handler
+	*accept = ACCEPT_NO;
+
+	BOOST_FOREACH(LLView* viewp, mChildList)
+	{
+		S32 local_x = x - viewp->getRect().mLeft;
+		S32 local_y = y - viewp->getRect().mBottom;
+		if( !viewp->visibleEnabledAndContains(local_x, local_y))
+		{
+			continue;
 		}
 
-		if (viewp->blockMouseEvent(local_x, local_y))
+		// Differs from childrenHandleMouseEvent() simply in that this virtual
+		// method call diverges pretty radically from the usual (x, y, int).
+		if (viewp->handleDragAndDrop(local_x, local_y, mask, drop,
+									 cargo_type,
+									 cargo_data,
+									 accept,
+									 tooltip_msg)
+			|| viewp->blockMouseEvent(local_x, local_y))
 		{
-			handled_view = viewp;
-			break;
+			return viewp;
 		}
 	}
-	return handled_view;
+	return NULL;
 }
 
+LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
+{
+	BOOST_FOREACH(LLView* viewp, mChildList)
+	{
+		S32 local_x = x - viewp->getRect().mLeft;
+		S32 local_y = y - viewp->getRect().mBottom;
+		if(!viewp->visibleEnabledAndContains(local_x, local_y))
+		{
+			continue;
+		}
 
-LLView*	LLView::childFromPoint(S32 x, S32 y)
+		// This call differentiates this method from childrenHandleMouseEvent().
+		LLUI::sWindow->setCursor(viewp->getHoverCursor());
+
+		if (viewp->handleHover(local_x, local_y, mask)
+			|| viewp->blockMouseEvent(local_x, local_y))
+		{
+			viewp->logMouseEvent();
+			return viewp;
+		}
+	}
+	return NULL;
+}
+
+LLView*	LLView::childFromPoint(S32 x, S32 y, bool recur)
 {
-	if (!getVisible()  )
+	if (!getVisible())
 		return false;
-	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+
+	BOOST_FOREACH(LLView* viewp, mChildList)
 	{
-		LLView* viewp = *child_it;
 		S32 local_x = x - viewp->getRect().mLeft;
 		S32 local_y = y - viewp->getRect().mBottom;
-		if (!viewp->pointInView(local_x, local_y) 
-			|| !viewp->getVisible() )
+		if (!viewp->visibleAndContains(local_x, local_y))
 		{
 			continue;
 		}
+		// Here we've found the first (frontmost) visible child at this level
+		// containing the specified point. Is the caller asking us to drill
+		// down and return the innermost leaf child at this point, or just the
+		// top-level child?
+		if (recur)
+		{
+			return viewp->childFromPoint(local_x, local_y, recur);
+		}
 		return viewp;
 
 	}
@@ -815,45 +963,6 @@ BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 	return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;
 }
 
-LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
-									   BOOL drop,
-									   EDragAndDropType cargo_type,
-									   void* cargo_data,
-									   EAcceptance* accept,
-									   std::string& tooltip_msg)
-{
-	LLView* handled_view = NULL;
-	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-	{
-		LLView* viewp = *child_it;
-		S32 local_x = x - viewp->getRect().mLeft;
-		S32 local_y = y - viewp->getRect().mBottom;
-		if( !viewp->pointInView(local_x, local_y) ||
-			!viewp->getVisible() ||
-			!viewp->getEnabled())
-		{
-			continue;
-		}
-		if (viewp->handleDragAndDrop(local_x, local_y, mask, drop,
-									 cargo_type,
-									 cargo_data,
-									 accept,
-									 tooltip_msg))
-		{
-			handled_view = viewp;
-			break;
-		}
-
-		if (viewp->blockMouseEvent(x, y))
-		{
-			*accept = ACCEPT_NO;
-			handled_view = viewp;
-			break;
-		}
-	}
-	return handled_view;
-}
-
 void LLView::onMouseCaptureLost()
 {
 }
@@ -903,391 +1012,57 @@ BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
 	return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
 }
 
-
 LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
 {
-	LLView* handled_view = NULL;
-	if (getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-			if (!viewp->pointInView(local_x, local_y) 
-				|| !viewp->getVisible()
-				|| !viewp->getEnabled())
-			{
-				continue;
-			}
-
-			if (viewp->handleScrollWheel( local_x, local_y, clicks ))
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
-}
-
-LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
-{
-	LLView* handled_view = NULL;
-	if (getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-			if(!viewp->pointInView(local_x, local_y) 
-				|| !viewp->getVisible() 
-				|| !viewp->getEnabled())
-			{
-				continue;
-			}
-
-			if (viewp->handleHover(local_x, local_y, mask) )
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-
-				handled_view = viewp;
-				break;
-			}
-
-			if (viewp->blockMouseEvent(local_x, local_y))
-			{
-				LLUI::sWindow->setCursor(viewp->getHoverCursor());
-
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks);
 }
 
 // Called during downward traversal
 LLView* LLView::childrenHandleKey(KEY key, MASK mask)
 {
-	LLView* handled_view = NULL;
-
-	if ( getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			if (viewp->handleKey(key, mask, TRUE))
-			{
-				if (LLView::sDebugKeys)
-				{
-					llinfos << "Key handled by " << viewp->getName() << llendl;
-				}
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-
-	return handled_view;
+	return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask);
 }
 
 // Called during downward traversal
 LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)
 {
-	LLView* handled_view = NULL;
-
-	if ( getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			if (viewp->handleUnicodeChar(uni_char, TRUE))
-			{
-				if (LLView::sDebugKeys)
-				{
-					llinfos << "Unicode character handled by " << viewp->getName() << llendl;
-				}
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-
-	return handled_view;
+	return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask,
+								   uni_char, MASK_NONE);
 }
 
 LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-
-	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-	{
-		LLView* viewp = *child_it;
-		S32 local_x = x - viewp->getRect().mLeft;
-		S32 local_y = y - viewp->getRect().mBottom;
-
-		if (!viewp->pointInView(local_x, local_y) 
-			|| !viewp->getVisible() 
-			|| !viewp->getEnabled())
-		{
-			continue;
-		}
-
-		if(viewp->handleMouseDown( local_x, local_y, mask ))
-		{
-			if (sDebugMouseHandling)
-			{
-				sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-			}
-			handled_view = viewp;
-			break;
-		}
-
-		if(viewp->blockMouseEvent(local_x, local_y))
-		{
-			handled_view = viewp;
-			break;
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask);
 }
 
 LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-
-	if (getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-
-			if (!viewp->pointInView(local_x, local_y)
-				|| !viewp->getVisible() 
-				|| !viewp->getEnabled())
-			{
-				continue;
-			}
-
-			if (viewp->handleRightMouseDown( local_x, local_y, mask ))
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-
-				handled_view = viewp;
-				break;
-			}
-
-			if (viewp->blockMouseEvent(local_x, local_y))
-			{
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask);
 }
 
 LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-
-	if (getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-			if (!viewp->pointInView(local_x, local_y)
-				|| !viewp->getVisible() 
-				|| !viewp->getEnabled())
-			{
-				continue;
-			}
-
-			if(viewp->handleMiddleMouseDown( local_x, local_y, mask ))
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-				handled_view = viewp;
-				break;
-			}
-
-			if (viewp->blockMouseEvent(local_x, local_y))
-			{
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask);
 }
 
 LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-
-	if (getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-
-			if (!viewp->pointInView(local_x, local_y) 
-				|| !viewp->getVisible() 
-				|| !viewp->getEnabled())
-			{
-				continue;
-			}
-
-			if (viewp->handleDoubleClick( local_x, local_y, mask ))
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-				handled_view = viewp;
-				break;
-			}
-
-			if (viewp->blockMouseEvent(local_x, local_y))
-			{
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask);
 }
 
 LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-	if( getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-			if (!viewp->pointInView(local_x, local_y)
-				|| !viewp->getVisible()
-				|| !viewp->getEnabled())
-			{
-				continue;
-			}
-			
-			if (viewp->handleMouseUp( local_x, local_y, mask ))
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-				handled_view = viewp;
-				break;
-			}
-
-			if (viewp->blockMouseEvent(local_x, local_y))
-			{
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
 }
 
 LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-	if( getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-			if (!viewp->pointInView(local_x, local_y) 
-				|| !viewp->getVisible() 
-				|| !viewp->getEnabled() )
-			{
-				continue;
-			}
-
-			if(viewp->handleRightMouseUp( local_x, local_y, mask ))
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-				handled_view = viewp;
-				break;
-			}
-
-			if(viewp->blockMouseEvent(local_x, local_y))
-			{
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask);
 }
 
 LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
 {
-	LLView* handled_view = NULL;
-	if( getVisible() && getEnabled() )
-	{
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
-		{
-			LLView* viewp = *child_it;
-			S32 local_x = x - viewp->getRect().mLeft;
-			S32 local_y = y - viewp->getRect().mBottom;
-			if (!viewp->pointInView(local_x, local_y) 
-				|| !viewp->getVisible() 
-				|| !viewp->getEnabled())
-			{
-				continue;
-			}
-				
-			if(viewp->handleMiddleMouseUp( local_x, local_y, mask ))
-			{
-				if (sDebugMouseHandling)
-				{
-					sMouseHandlerMessage = std::string("/") + viewp->mName + sMouseHandlerMessage;
-				}
-				handled_view = viewp;
-				break;
-			}
-
-			if (viewp->blockMouseEvent(local_x, local_y))
-			{
-				handled_view = viewp;
-				break;
-			}
-		}
-	}
-	return handled_view;
+	return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask);
 }
 
 void LLView::draw()
@@ -1460,9 +1235,8 @@ void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
 		mRect.mTop = getRect().mBottom + height;
 
 		// move child views according to reshape flags
-		for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+		BOOST_FOREACH(LLView* viewp, mChildList)
 		{
-			LLView* viewp = *child_it;
 			LLRect child_rect( viewp->mRect );
 
 			if (viewp->followsRight() && viewp->followsLeft())
@@ -1525,10 +1299,8 @@ LLRect LLView::calcBoundingRect()
 {
 	LLRect local_bounding_rect = LLRect::null;
 
-	child_list_const_iter_t child_it;
-	for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+	BOOST_FOREACH(LLView* childp, mChildList)
 	{
-		LLView* childp = *child_it;
 		// ignore invisible and "top" children when calculating bounding rect
 		// such as combobox popups
 		if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl()) 
@@ -1693,11 +1465,9 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
 	//richard: should we allow empty names?
 	//if(name.empty())
 	//	return NULL;
-	child_list_const_iter_t child_it;
 	// Look for direct children *first*
-	for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+	BOOST_FOREACH(LLView* childp, mChildList)
 	{
-		LLView* childp = *child_it;
 		llassert(childp);
 		if (childp->getName() == name)
 		{
@@ -1707,9 +1477,8 @@ LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
 	if (recurse)
 	{
 		// Look inside each child as well.
-		for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+		BOOST_FOREACH(LLView* childp, mChildList)
 		{
-			LLView* childp = *child_it;
 			llassert(childp);
 			LLView* viewp = childp->findChildView(name, recurse);
 			if ( viewp )
@@ -2815,9 +2584,9 @@ S32	LLView::notifyParent(const LLSD& info)
 bool	LLView::notifyChildren(const LLSD& info)
 {
 	bool ret = false;
-	for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+	BOOST_FOREACH(LLView* childp, mChildList)
 	{
-		ret |= (*child_it)->notifyChildren(info);
+		ret = ret || childp->notifyChildren(info);
 	}
 	return ret;
 }
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
index 594a5eec6be9b8927f45b76d66c9730ad326dae7..fcae75c447d0ccff45bb5498bfb707b497151fd3 100644
--- a/indra/llui/llview.h
+++ b/indra/llui/llview.h
@@ -50,6 +50,8 @@
 #include "llfocusmgr.h"
 
 #include <list>
+#include <boost/function.hpp>
+#include <boost/noncopyable.hpp>
 
 class LLSD;
 
@@ -437,12 +439,15 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 	/*virtual*/ void	screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
 	/*virtual*/ void	localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
 
-	virtual		LLView*	childFromPoint(S32 x, S32 y);
+	virtual		LLView*	childFromPoint(S32 x, S32 y, bool recur=false);
 
 	// view-specific handlers 
 	virtual void	onMouseEnter(S32 x, S32 y, MASK mask);
 	virtual void	onMouseLeave(S32 x, S32 y, MASK mask);
 
+	std::string getPathname() const;
+	// static method handles NULL pointer too
+	static std::string getPathname(const LLView*);
 
 	template <class T> T* findChild(const std::string& name, BOOL recurse = TRUE) const
 	{
@@ -516,6 +521,9 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 	void			drawDebugRect();
 	void			drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE);
 	void			drawChildren();
+	bool			visibleAndContains(S32 local_x, S32 local_Y);
+	bool			visibleEnabledAndContains(S32 local_x, S32 local_y);
+	void			logMouseEvent();
 
 	LLView*	childrenHandleKey(KEY key, MASK mask);
 	LLView* childrenHandleUnicodeChar(llwchar uni_char);
@@ -541,6 +549,20 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 	
 private:
 
+	template <typename METHOD, typename XDATA>
+	LLView* childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra);
+
+	template <typename METHOD, typename CHARTYPE>
+	LLView* childrenHandleCharEvent(const std::string& desc, const METHOD& method,
+									CHARTYPE c, MASK mask);
+
+	// adapter to blur distinction between handleKey() and handleUnicodeChar()
+	// for childrenHandleCharEvent()
+	BOOL	handleUnicodeCharWithDummyMask(llwchar uni_char, MASK /* dummy */, BOOL from_parent)
+	{
+		return handleUnicodeChar(uni_char, from_parent);
+	}
+
 	LLView*		mParentView;
 	child_list_t mChildList;
 
@@ -582,7 +604,35 @@ class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElem
 
 	LLView& getDefaultWidgetContainer() const;
 
+	// This allows special mouse-event targeting logic for testing.
+	typedef boost::function<bool(const LLView*, S32 x, S32 y)> DrilldownFunc;
+	static DrilldownFunc sDrilldown;
+
 public:
+	// This is the only public accessor to alter sDrilldown. This is not
+	// an accident. The intended usage pattern is like:
+	// {
+	//     LLView::TemporaryDrilldownFunc scoped_func(myfunctor);
+	//     // ... test with myfunctor ...
+	// } // exiting block restores original LLView::sDrilldown
+	class TemporaryDrilldownFunc: public boost::noncopyable
+	{
+	public:
+		TemporaryDrilldownFunc(const DrilldownFunc& func):
+			mOldDrilldown(sDrilldown)
+		{
+			sDrilldown = func;
+		}
+
+		~TemporaryDrilldownFunc()
+		{
+			sDrilldown = mOldDrilldown;
+		}
+
+	private:
+		DrilldownFunc mOldDrilldown;
+	};
+
 	// Depth in view hierarchy during rendering
 	static S32	sDepth;
 
diff --git a/indra/llui/llviewinject.cpp b/indra/llui/llviewinject.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..46c5839f8ee659062c45a716d08162990287d407
--- /dev/null
+++ b/indra/llui/llviewinject.cpp
@@ -0,0 +1,49 @@
+/**
+ * @file   llviewinject.cpp
+ * @author Nat Goodspeed
+ * @date   2011-08-16
+ * @brief  Implementation for llviewinject.
+ * 
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llviewinject.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+
+llview::TargetEvent::TargetEvent(LLView* view)
+{
+    // Walk up the view tree from target LLView to the root (NULL). If
+    // passed NULL, iterate 0 times.
+    for (; view; view = view->getParent())
+    {
+        // At each level, operator() is going to ask: for a particular parent
+        // LLView*, which of its children should I select? So for this view's
+        // parent, select this view.
+        mChildMap[view->getParent()] = view;
+    }
+}
+
+bool llview::TargetEvent::operator()(const LLView* view, S32 /*x*/, S32 /*y*/) const
+{
+    // We are being called to decide whether to direct an incoming mouse event
+    // to this child view. (Normal LLView processing is to check whether the
+    // incoming (x, y) is within the view.) Look up the parent to decide
+    // whether, for that parent, this is the previously-selected child.
+    ChildMap::const_iterator found(mChildMap.find(view->getParent()));
+    // If we're looking at a child whose parent isn't even in the map, never
+    // mind.
+    if (found == mChildMap.end())
+    {
+        return false;
+    }
+    // So, is this the predestined child for this parent?
+    return (view == found->second);
+}
diff --git a/indra/llui/llviewinject.h b/indra/llui/llviewinject.h
new file mode 100644
index 0000000000000000000000000000000000000000..0de3d155c41ba819b491ac4d93e1ed894e587c04
--- /dev/null
+++ b/indra/llui/llviewinject.h
@@ -0,0 +1,56 @@
+/**
+ * @file   llviewinject.h
+ * @author Nat Goodspeed
+ * @date   2011-08-16
+ * @brief  Supplemental LLView functionality used for simulating UI events.
+ * 
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLVIEWINJECT_H)
+#define LL_LLVIEWINJECT_H
+
+#include "llview.h"
+#include <map>
+
+namespace llview
+{
+
+    /**
+     * TargetEvent is a callable with state, specifically intended for use as
+     * an LLView::TemporaryDrilldownFunc. Instantiate it with the desired
+     * target LLView*; pass it to a TemporaryDrilldownFunc instance;
+     * TargetEvent::operator() will then attempt to direct subsequent mouse
+     * events to the desired target LLView*. (This is an "attempt" because
+     * LLView will still balk unless the target LLView and every parent are
+     * visible and enabled.)
+     */
+    class TargetEvent
+    {
+    public:
+        /**
+         * Construct TargetEvent with the desired target LLView*. (See
+         * LLUI::resolvePath() to obtain an LLView* given a string pathname.)
+         * This sets up for operator().
+         */
+        TargetEvent(LLView* view);
+
+        /**
+         * This signature must match LLView::DrilldownFunc. When you install
+         * this TargetEvent instance using LLView::TemporaryDrilldownFunc,
+         * LLView will call this method to decide whether to propagate an
+         * incoming mouse event to the passed child LLView*.
+         */
+        bool operator()(const LLView*, S32 x, S32 y) const;
+
+    private:
+        // For a given parent LLView, identify which child to select.
+        typedef std::map<LLView*, LLView*> ChildMap;
+        ChildMap mChildMap;
+    };
+
+} // llview namespace
+
+#endif /* ! defined(LL_LLVIEWINJECT_H) */
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index 3d89867bc1d626dce8a183047a687b295e7cbd07..341bddfffdb81bc83a134ea3d7323a47feb7e6c3 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -38,7 +38,6 @@ set(llwindow_SOURCE_FILES
     llkeyboardheadless.cpp
     llwindowheadless.cpp
     llwindowcallbacks.cpp
-    llwindowlistener.cpp
     )
 
 set(llwindow_HEADER_FILES
@@ -48,7 +47,6 @@ set(llwindow_HEADER_FILES
     llkeyboardheadless.h
     llwindowheadless.h
     llwindowcallbacks.h
-    llwindowlistener.h
     )
 
 set(viewer_SOURCE_FILES
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
index 71a5df910dabf4e1f7c2b5db32d23741f5f5e78b..dc3a1099b112a56ddeef08e54c82e65023391c84 100644
--- a/indra/llwindow/llwindow.cpp
+++ b/indra/llwindow/llwindow.cpp
@@ -41,8 +41,6 @@
 #include "llkeyboard.h"
 #include "linked_lists.h"
 #include "llwindowcallbacks.h"
-#include "llwindowlistener.h"
-#include <boost/lambda/core.hpp>
 
 
 //
@@ -118,17 +116,10 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags)
 	  mFlags(flags),
 	  mHighSurrogate(0)
 {
-	// gKeyboard is still NULL, so it doesn't do LLWindowListener any good to
-	// pass its value right now. Instead, pass it a nullary function that
-	// will, when we later need it, return the value of gKeyboard.
-	// boost::lambda::var() constructs such a functor on the fly.
-	mListener = new LLWindowListener(callbacks, boost::lambda::var(gKeyboard));
 }
 
 LLWindow::~LLWindow()
 {
-	delete mListener;
-	mListener = NULL;
 }
 
 //virtual
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index 6bdc01ae88fcd0fa45af3fd804635b7965c54527..e8a86a188072c90f6563f47681d266a4e9eea196 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -36,7 +36,6 @@
 class LLSplashScreen;
 class LLPreeditor;
 class LLWindowCallbacks;
-class LLWindowListener;
 
 // Refer to llwindow_test in test/common/llwindow for usage example
 
@@ -189,7 +188,6 @@ class LLWindow
 	BOOL		mHideCursorPermanent;
 	U32			mFlags;
 	U16			mHighSurrogate;
-	LLWindowListener* mListener;
 
  	// Handle a UTF-16 encoding unit received from keyboard.
  	// Converting the series of UTF-16 encoding units to UTF-32 data,
diff --git a/indra/llwindow/llwindowlistener.cpp b/indra/llwindow/llwindowlistener.cpp
deleted file mode 100644
index 91b99d83c6a2ebaa0191a0f4973be8c1705d1b18..0000000000000000000000000000000000000000
--- a/indra/llwindow/llwindowlistener.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-/** 
- * @file llwindowlistener.cpp
- * @brief EventAPI interface for injecting input into LLWindow
- *
- * $LicenseInfo:firstyear=2001&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 "llwindowlistener.h"
-
-#include "llcoord.h"
-#include "llkeyboard.h"
-#include "llwindowcallbacks.h"
-#include <map>
-
-LLWindowListener::LLWindowListener(LLWindowCallbacks *window, const KeyboardGetter& kbgetter)
-	: LLEventAPI("LLWindow", "Inject input events into the LLWindow instance"),
-	  mWindow(window),
-	  mKbGetter(kbgetter)
-{
-	std::string keySomething =
-		"Given [\"keysym\"], [\"keycode\"] or [\"char\"], inject the specified ";
-	std::string keyExplain =
-		"(integer keycode values, or keysym \"XXXX\" from any KEY_XXXX, in\n"
-		"http://hg.secondlife.com/viewer-development/src/tip/indra/llcommon/indra_constants.h )";
-	std::string mask =
-		"Specify optional [\"mask\"] as an array containing any of \"CONTROL\", \"ALT\",\n"
-		"\"SHIFT\" or \"MAC_CONTROL\"; the corresponding modifier bits will be combined\n"
-		"to form the mask used with the event.";
-
-	std::string mouseSomething =
-		"Given [\"button\"], [\"x\"] and [\"y\"], inject the given mouse ";
-	std::string mouseExplain =
-		"(button values \"LEFT\", \"MIDDLE\", \"RIGHT\")";
-
-	add("keyDown",
-		keySomething + "keypress event.\n" + keyExplain + '\n' + mask,
-		&LLWindowListener::keyDown);
-	add("keyUp",
-		keySomething + "key release event.\n" + keyExplain + '\n' + mask,
-		&LLWindowListener::keyUp);
-	add("mouseDown",
-		mouseSomething + "click event.\n" + mouseExplain + '\n' + mask,
-		&LLWindowListener::mouseDown);
-	add("mouseUp",
-		mouseSomething + "release event.\n" + mouseExplain + '\n' + mask,
-		&LLWindowListener::mouseUp);
-	add("mouseMove",
-		std::string("Given [\"x\"] and [\"y\"], inject the given mouse movement event.\n") +
-		mask,
-		&LLWindowListener::mouseMove);
-	add("mouseScroll",
-		"Given an integer number of [\"clicks\"], inject the given mouse scroll event.\n"
-		"(positive clicks moves downward through typical content)",
-		&LLWindowListener::mouseScroll);
-}
-
-template <typename MAPPED>
-class StringLookup
-{
-private:
-	std::string mDesc;
-	typedef std::map<std::string, MAPPED> Map;
-	Map mMap;
-
-public:
-	StringLookup(const std::string& desc): mDesc(desc) {}
-
-	MAPPED lookup(const typename Map::key_type& key) const
-	{
-		typename Map::const_iterator found = mMap.find(key);
-		if (found == mMap.end())
-		{
-			LL_WARNS("LLWindowListener") << "Unknown " << mDesc << " '" << key << "'" << LL_ENDL;
-			return MAPPED();
-		}
-		return found->second;
-	}
-
-protected:
-	void add(const typename Map::key_type& key, const typename Map::mapped_type& value)
-	{
-		mMap.insert(typename Map::value_type(key, value));
-	}
-};
-
-// for WhichKeysym. KeyProxy is like the typedef KEY, except that KeyProxy()
-// (default-constructed) is guaranteed to have the value KEY_NONE.
-class KeyProxy
-{
-public:
-	KeyProxy(KEY k): mKey(k) {}
-	KeyProxy(): mKey(KEY_NONE) {}
-	operator KEY() const { return mKey; }
-
-private:
-	KEY mKey;
-};
-
-struct WhichKeysym: public StringLookup<KeyProxy>
-{
-	WhichKeysym(): StringLookup<KeyProxy>("keysym")
-	{
-		add("RETURN",		KEY_RETURN);
-		add("LEFT",			KEY_LEFT);
-		add("RIGHT",		KEY_RIGHT);
-		add("UP",			KEY_UP);
-		add("DOWN",			KEY_DOWN);
-		add("ESCAPE",		KEY_ESCAPE);
-		add("BACKSPACE",	KEY_BACKSPACE);
-		add("DELETE",		KEY_DELETE);
-		add("SHIFT",		KEY_SHIFT);
-		add("CONTROL",		KEY_CONTROL);
-		add("ALT",			KEY_ALT);
-		add("HOME",			KEY_HOME);
-		add("END",			KEY_END);
-		add("PAGE_UP",		KEY_PAGE_UP);
-		add("PAGE_DOWN",	KEY_PAGE_DOWN);
-		add("HYPHEN",		KEY_HYPHEN);
-		add("EQUALS",		KEY_EQUALS);
-		add("INSERT",		KEY_INSERT);
-		add("CAPSLOCK",		KEY_CAPSLOCK);
-		add("TAB",			KEY_TAB);
-		add("ADD",			KEY_ADD);
-		add("SUBTRACT",		KEY_SUBTRACT);
-		add("MULTIPLY",		KEY_MULTIPLY);
-		add("DIVIDE",		KEY_DIVIDE);
-		add("F1",			KEY_F1);
-		add("F2",			KEY_F2);
-		add("F3",			KEY_F3);
-		add("F4",			KEY_F4);
-		add("F5",			KEY_F5);
-		add("F6",			KEY_F6);
-		add("F7",			KEY_F7);
-		add("F8",			KEY_F8);
-		add("F9",			KEY_F9);
-		add("F10",			KEY_F10);
-		add("F11",			KEY_F11);
-		add("F12",			KEY_F12);
-
-		add("PAD_UP",		KEY_PAD_UP);
-		add("PAD_DOWN",		KEY_PAD_DOWN);
-		add("PAD_LEFT",		KEY_PAD_LEFT);
-		add("PAD_RIGHT",	KEY_PAD_RIGHT);
-		add("PAD_HOME",		KEY_PAD_HOME);
-		add("PAD_END",		KEY_PAD_END);
-		add("PAD_PGUP",		KEY_PAD_PGUP);
-		add("PAD_PGDN",		KEY_PAD_PGDN);
-		add("PAD_CENTER",	KEY_PAD_CENTER); // the 5 in the middle
-		add("PAD_INS",		KEY_PAD_INS);
-		add("PAD_DEL",		KEY_PAD_DEL);
-		add("PAD_RETURN",	KEY_PAD_RETURN);
-		add("PAD_ADD",		KEY_PAD_ADD); // not used
-		add("PAD_SUBTRACT", KEY_PAD_SUBTRACT); // not used
-		add("PAD_MULTIPLY", KEY_PAD_MULTIPLY); // not used
-		add("PAD_DIVIDE",	KEY_PAD_DIVIDE); // not used
-
-		add("BUTTON0",		KEY_BUTTON0);
-		add("BUTTON1",		KEY_BUTTON1);
-		add("BUTTON2",		KEY_BUTTON2);
-		add("BUTTON3",		KEY_BUTTON3);
-		add("BUTTON4",		KEY_BUTTON4);
-		add("BUTTON5",		KEY_BUTTON5);
-		add("BUTTON6",		KEY_BUTTON6);
-		add("BUTTON7",		KEY_BUTTON7);
-		add("BUTTON8",		KEY_BUTTON8);
-		add("BUTTON9",		KEY_BUTTON9);
-		add("BUTTON10",		KEY_BUTTON10);
-		add("BUTTON11",		KEY_BUTTON11);
-		add("BUTTON12",		KEY_BUTTON12);
-		add("BUTTON13",		KEY_BUTTON13);
-		add("BUTTON14",		KEY_BUTTON14);
-		add("BUTTON15",		KEY_BUTTON15);
-	}
-};
-static WhichKeysym keysyms;
-
-struct WhichMask: public StringLookup<MASK>
-{
-	WhichMask(): StringLookup<MASK>("shift mask")
-	{
-		add("NONE",			MASK_NONE);
-		add("CONTROL",		MASK_CONTROL); // Mapped to cmd on Macs
-		add("ALT",			MASK_ALT);
-		add("SHIFT",		MASK_SHIFT);
-		add("MAC_CONTROL",	MASK_MAC_CONTROL); // Un-mapped Ctrl key on Macs, not used on Windows
-	}
-};
-static WhichMask masks;
-
-static MASK getMask(const LLSD& event)
-{
-	MASK mask(MASK_NONE);
-	LLSD masknames(event["mask"]);
-	for (LLSD::array_const_iterator ai(masknames.beginArray()), aend(masknames.endArray());
-		 ai != aend; ++ai)
-	{
-		mask |= masks.lookup(*ai);
-	}
-	return mask;
-}
-
-static KEY getKEY(const LLSD& event)
-{
-    if (event.has("keysym"))
-	{
-		return keysyms.lookup(event["keysym"]);
-	}
-	else if (event.has("keycode"))
-	{
-		return KEY(event["keycode"].asInteger());
-	}
-	else
-	{
-		return KEY(event["char"].asString()[0]);
-	}
-}
-
-void LLWindowListener::keyDown(LLSD const & evt)
-{
-	mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt));
-}
-
-void LLWindowListener::keyUp(LLSD const & evt)
-{
-	mKbGetter()->handleTranslatedKeyUp(getKEY(evt), getMask(evt));
-}
-
-// for WhichButton
-typedef BOOL (LLWindowCallbacks::*MouseFunc)(LLWindow *, LLCoordGL, MASK);
-struct Actions
-{
-	Actions(const MouseFunc& d, const MouseFunc& u): down(d), up(u), valid(true) {}
-	Actions(): valid(false) {}
-	MouseFunc down, up;
-	bool valid;
-};
-
-struct WhichButton: public StringLookup<Actions>
-{
-	WhichButton(): StringLookup<Actions>("mouse button")
-	{
-		add("LEFT",		Actions(&LLWindowCallbacks::handleMouseDown,
-								&LLWindowCallbacks::handleMouseUp));
-		add("RIGHT",	Actions(&LLWindowCallbacks::handleRightMouseDown,
-								&LLWindowCallbacks::handleRightMouseUp));
-		add("MIDDLE",	Actions(&LLWindowCallbacks::handleMiddleMouseDown,
-								&LLWindowCallbacks::handleMiddleMouseUp));
-	}
-};
-static WhichButton buttons;
-
-static LLCoordGL getPos(const LLSD& event)
-{
-	return LLCoordGL(event["x"].asInteger(), event["y"].asInteger());
-}
-
-void LLWindowListener::mouseDown(LLSD const & evt)
-{
-	Actions actions(buttons.lookup(evt["button"]));
-	if (actions.valid)
-	{
-		(mWindow->*(actions.down))(NULL, getPos(evt), getMask(evt));
-	}
-}
-
-void LLWindowListener::mouseUp(LLSD const & evt)
-{
-	Actions actions(buttons.lookup(evt["button"]));
-	if (actions.valid)
-	{
-		(mWindow->*(actions.up))(NULL, getPos(evt), getMask(evt));
-	}
-}
-
-void LLWindowListener::mouseMove(LLSD const & evt)
-{
-	mWindow->handleMouseMove(NULL, getPos(evt), getMask(evt));
-}
-
-void LLWindowListener::mouseScroll(LLSD const & evt)
-{
-	S32 clicks = evt["clicks"].asInteger();
-
-	mWindow->handleScrollWheel(NULL, clicks);
-}
-
diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h
index 050d4b729f5a4aa3b3a3755e2181efbd9e8ee8d3..bf38a8b062dbcddacdf34e3faa751abe0ecf3960 100644
--- a/indra/llxml/llcontrol.h
+++ b/indra/llxml/llcontrol.h
@@ -185,9 +185,10 @@ class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string>
 	ctrl_name_table_t mNameTable;
 	std::string mTypeString[TYPE_COUNT];
 
+public:
 	eControlType typeStringToEnum(const std::string& typestr);
 	std::string typeEnumToString(eControlType typeenum);	
-public:
+
 	LLControlGroup(const std::string& name);
 	~LLControlGroup();
 	void cleanup();
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index a117d9a593dcab2959e46b8343d64e294f5265f2..7a6cb3d4a0bfd0e122be14469400864ab4f7f0c2 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -601,6 +601,7 @@ set(viewer_SOURCE_FILES
     llweb.cpp
     llwebsharing.cpp
     llwind.cpp
+    llwindowlistener.cpp
     llwlanimator.cpp
     llwldaycycle.cpp
     llwlhandlers.cpp
@@ -1157,6 +1158,7 @@ set(viewer_HEADER_FILES
     llweb.h
     llwebsharing.h
     llwind.h
+    llwindowlistener.h
     llwlanimator.h
     llwldaycycle.h
     llwlhandlers.h
diff --git a/indra/newview/llviewercontrollistener.cpp b/indra/newview/llviewercontrollistener.cpp
index 8bc25fa28176404d79f5ea9d02c20e4152d0656a..361b96221ca893904744aebfe4dd7f2ab5a6df04 100644
--- a/indra/newview/llviewercontrollistener.cpp
+++ b/indra/newview/llviewercontrollistener.cpp
@@ -31,99 +31,196 @@
 #include "llviewercontrollistener.h"
 
 #include "llviewercontrol.h"
+#include "llcontrol.h"
+#include "llerror.h"
+#include "llsdutil.h"
+#include "stringize.h"
+#include <sstream>
 
-LLViewerControlListener gSavedSettingsListener;
+namespace {
+
+LLViewerControlListener sSavedSettingsListener;
+
+} // unnamed namespace
 
 LLViewerControlListener::LLViewerControlListener()
 	: LLEventAPI("LLViewerControl",
-                 "LLViewerControl listener: set, toggle or set default for various controls",
-                 "group")
+				 "LLViewerControl listener: set, toggle or set default for various controls")
 {
-	add("Global",
-        "Set gSavedSettings control [\"key\"] to value [\"value\"]",
-        boost::bind(&LLViewerControlListener::set, &gSavedSettings, _1));
-	add("PerAccount",
-        "Set gSavedPerAccountSettings control [\"key\"] to value [\"value\"]",
-        boost::bind(&LLViewerControlListener::set, &gSavedPerAccountSettings, _1));
-	add("Warning",
-        "Set gWarningSettings control [\"key\"] to value [\"value\"]",
-        boost::bind(&LLViewerControlListener::set, &gWarningSettings, _1));
-	add("Crash",
-        "Set gCrashSettings control [\"key\"] to value [\"value\"]",
-        boost::bind(&LLViewerControlListener::set, &gCrashSettings, _1));
-
-#if 0
-	add(/*"toggleControl",*/ "Global", boost::bind(&LLViewerControlListener::toggleControl, &gSavedSettings, _1));
-	add(/*"toggleControl",*/ "PerAccount", boost::bind(&LLViewerControlListener::toggleControl, &gSavedPerAccountSettings, _1));
-	add(/*"toggleControl",*/ "Warning", boost::bind(&LLViewerControlListener::toggleControl, &gWarningSettings, _1));
-	add(/*"toggleControl",*/ "Crash", boost::bind(&LLViewerControlListener::toggleControl, &gCrashSettings, _1));
-
-	add(/*"setDefault",*/ "Global", boost::bind(&LLViewerControlListener::setDefault, &gSavedSettings, _1));
-	add(/*"setDefault",*/ "PerAccount", boost::bind(&LLViewerControlListener::setDefault, &gSavedPerAccountSettings, _1));
-	add(/*"setDefault",*/ "Warning", boost::bind(&LLViewerControlListener::setDefault, &gWarningSettings, _1));
-	add(/*"setDefault",*/ "Crash", boost::bind(&LLViewerControlListener::setDefault, &gCrashSettings, _1));
-#endif // 0
+	std::ostringstream groupnames;
+	groupnames << "[\"group\"] is one of ";
+	const char* delim = "";
+	for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()),
+								  cgkend(LLControlGroup::endKeys());
+		 cgki != cgkend; ++cgki)
+	{
+		groupnames << delim << '"' << *cgki << '"';
+		delim = ", ";
+	}
+	groupnames << '\n';
+	std::string grouphelp(groupnames.str());
+	std::string replyhelp("If [\"reply\"] requested, send new [\"value\"] on specified LLEventPump\n");
+
+	add("set",
+		std::string("Set [\"group\"] control [\"key\"] to optional value [\"value\"]\n"
+					"If [\"value\"] omitted, set to control's defined default value\n") +
+		grouphelp + replyhelp,
+		&LLViewerControlListener::set,
+		LLSDMap("group", LLSD())("key", LLSD()));
+	add("toggle",
+		std::string("Toggle [\"group\"] control [\"key\"], if boolean\n") + grouphelp + replyhelp,
+		&LLViewerControlListener::toggle,
+		LLSDMap("group", LLSD())("key", LLSD()));
+	add("get",
+		std::string("Query [\"group\"] control [\"key\"], replying on LLEventPump [\"reply\"]\n") +
+		grouphelp,
+		&LLViewerControlListener::get,
+		LLSDMap("group", LLSD())("key", LLSD())("reply", LLSD()));
+	add("groups",
+		"Send on LLEventPump [\"reply\"] an array [\"groups\"] of valid group names",
+		&LLViewerControlListener::groups,
+		LLSDMap("reply", LLSD()));
+	add("vars",
+		std::string("For [\"group\"], send on LLEventPump [\"reply\"] an array [\"vars\"],\n"
+					"each of whose entries looks like:\n"
+					"  [\"name\"], [\"type\"], [\"value\"], [\"comment\"]\n") + grouphelp,
+		&LLViewerControlListener::vars,
+		LLSDMap("group", LLSD())("reply", LLSD()));
 }
 
-//static
-void LLViewerControlListener::set(LLControlGroup * controls, LLSD const & event_data)
+struct Info
 {
-	if(event_data.has("key"))
+	Info(const LLSD& request):
+		response(LLSD(), request),
+		groupname(request["group"]),
+		group(LLControlGroup::getInstance(groupname)),
+		key(request["key"]),
+		control(NULL)
 	{
-		std::string key(event_data["key"]);
+		if (! group)
+		{
+			response.error(STRINGIZE("Unrecognized group '" << groupname << "'"));
+			return;
+		}
 
-		if(controls->controlExists(key))
+		control = group->getControl(key);
+		if (! control)
 		{
-			controls->setUntypedValue(key, event_data["value"]);
+			response.error(STRINGIZE("In group '" << groupname
+									 << "', unrecognized control key '" << key << "'"));
 		}
-		else
+	}
+
+	~Info()
+	{
+		// If in fact the request passed to our constructor names a valid
+		// group and key, grab the final value of the indicated control and
+		// stuff it in our response. Since this outer destructor runs before
+		// the contained Response destructor, this data will go into the
+		// response we send.
+		if (control)
 		{
-			llwarns << "requested unknown control: \"" << key << '\"' << llendl;
+			response["name"]	= control->getName();
+			response["type"]	= group->typeEnumToString(control->type());
+			response["value"]	= control->get();
+			response["comment"] = control->getComment();
 		}
 	}
+
+	LLEventAPI::Response response;
+	std::string groupname;
+	LLControlGroup* group;
+	std::string key;
+	LLControlVariable* control;
+};
+
+//static
+void LLViewerControlListener::set(LLSD const & request)
+{
+	Info info(request);
+	if (! info.control)
+		return;
+
+	if (request.has("value"))
+	{
+		info.control->setValue(request["value"]);
+	}
+	else
+	{
+		info.control->resetToDefault();
+	}
 }
 
 //static
-void LLViewerControlListener::toggleControl(LLControlGroup * controls, LLSD const & event_data)
+void LLViewerControlListener::toggle(LLSD const & request)
 {
-	if(event_data.has("key"))
+	Info info(request);
+	if (! info.control)
+		return;
+
+	if (info.control->isType(TYPE_BOOLEAN))
+	{
+		info.control->set(! info.control->get().asBoolean());
+	}
+	else
 	{
-		std::string key(event_data["key"]);
+		info.response.error(STRINGIZE("toggle of non-boolean '" << info.groupname
+									  << "' control '" << info.key
+									  << "', type is "
+									  << info.group->typeEnumToString(info.control->type())));
+	}
+}
 
-		if(controls->controlExists(key))
-		{
-			LLControlVariable * control = controls->getControl(key);
-			if(control->isType(TYPE_BOOLEAN))
-			{
-				control->set(!control->get().asBoolean());
-			}
-			else
-			{
-				llwarns << "requested toggle of non-boolean control: \"" << key << "\", type is " << control->type() << llendl;
-			}
-		}
-		else
-		{
-			llwarns << "requested unknown control: \"" << key << '\"' << llendl;
-		}
+void LLViewerControlListener::get(LLSD const & request)
+{
+	// The Info constructor and destructor actually do all the work here.
+	Info info(request);
+}
+
+void LLViewerControlListener::groups(LLSD const & request)
+{
+	// No Info, we're not looking up either a group or a control name.
+	Response response(LLSD(), request);
+	for (LLControlGroup::key_iter cgki(LLControlGroup::beginKeys()),
+								  cgkend(LLControlGroup::endKeys());
+		 cgki != cgkend; ++cgki)
+	{
+		response["groups"].append(*cgki);
 	}
 }
 
-//static
-void LLViewerControlListener::setDefault(LLControlGroup * controls, LLSD const & event_data)
+struct CollectVars: public LLControlGroup::ApplyFunctor
 {
-	if(event_data.has("key"))
+	CollectVars(LLControlGroup* g):
+		mGroup(g)
+	{}
+
+	virtual void apply(const std::string& name, LLControlVariable* control)
 	{
-		std::string key(event_data["key"]);
+		vars.append(LLSDMap
+					("name", name)
+					("type", mGroup->typeEnumToString(control->type()))
+					("value", control->get())
+					("comment", control->getComment()));
+	}
 
-		if(controls->controlExists(key))
-		{
-			LLControlVariable * control = controls->getControl(key);
-			control->resetToDefault();
-		}
-		else
-		{
-			llwarns << "requested unknown control: \"" << key << '\"' << llendl;
-		}
+	LLControlGroup* mGroup;
+	LLSD vars;
+};
+
+void LLViewerControlListener::vars(LLSD const & request)
+{
+	// This method doesn't use Info, because we're not looking up a specific
+	// control name.
+	Response response(LLSD(), request);
+	std::string groupname(request["group"]);
+	LLControlGroup* group(LLControlGroup::getInstance(groupname));
+	if (! group)
+	{
+		return response.error(STRINGIZE("Unrecognized group '" << groupname << "'"));
 	}
+
+	CollectVars collector(group);
+	group->applyToAll(&collector);
+	response["vars"] = collector.vars;
 }
diff --git a/indra/newview/llviewercontrollistener.h b/indra/newview/llviewercontrollistener.h
index fd211b97af7280dbd1fe14f0e3a09097356e0feb..2e72046924cd8fa5638078b0a3ecfe74708d207e 100644
--- a/indra/newview/llviewercontrollistener.h
+++ b/indra/newview/llviewercontrollistener.h
@@ -40,11 +40,11 @@ class  LLViewerControlListener : public LLEventAPI
 	LLViewerControlListener();
 
 private:
-	static void set(LLControlGroup *controls, LLSD const & event_data);
-	static void toggleControl(LLControlGroup *controls, LLSD const & event_data);
-	static void setDefault(LLControlGroup *controls, LLSD const & event_data);
+	static void set(LLSD const & event_data);
+	static void toggle(LLSD const & event_data);
+	static void get(LLSD const & event_data);
+	static void groups(LLSD const & event_data);
+	static void vars(LLSD const & event_data);
 };
 
-extern LLViewerControlListener gSavedSettingsListener;
-
 #endif // LL_LLVIEWERCONTROLLISTENER_H
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 5893259d964bcc17116c95dda20706800884624a..57bd1afb895b8c7db66ca4dfaa2094e6750d7ed0 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -36,6 +36,7 @@
 #include <iostream>
 #include <fstream>
 #include <algorithm>
+#include <boost/lambda/core.hpp>
 
 #include "llagent.h"
 #include "llagentcamera.h"
@@ -198,6 +199,7 @@
 #include "llfloaternotificationsconsole.h"
 
 #include "llnearbychat.h"
+#include "llwindowlistener.h"
 #include "llviewerwindowlistener.h"
 #include "llpaneltopinfobar.h"
 
@@ -1548,7 +1550,12 @@ LLViewerWindow::LLViewerWindow(
 	mResDirty(false),
 	mStatesDirty(false),
 	mCurrResolutionIndex(0),
-    mViewerWindowListener(new LLViewerWindowListener(this)),
+	// gKeyboard is still NULL, so it doesn't do LLWindowListener any good to
+	// pass its value right now. Instead, pass it a nullary function that
+	// will, when we later need it, return the value of gKeyboard.
+	// boost::lambda::var() constructs such a functor on the fly.
+	mWindowListener(new LLWindowListener(this, boost::lambda::var(gKeyboard))),
+	mViewerWindowListener(new LLViewerWindowListener(this)),
 	mProgressView(NULL)
 {
 	LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert"));
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index edd241a742aeae6c6e9cf5379c916b98a7a1762d..d35feb46671a5179688459ad5d36eaac6a7b0724 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -62,6 +62,7 @@ class LLImageFormatted;
 class LLHUDIcon;
 class LLWindow;
 class LLRootView;
+class LLWindowListener;
 class LLViewerWindowListener;
 class LLPopupView;
 
@@ -456,7 +457,8 @@ class LLViewerWindow : public LLWindowCallbacks
 	bool			mStatesDirty;
 	U32			mCurrResolutionIndex;
 
-    boost::scoped_ptr<LLViewerWindowListener> mViewerWindowListener;
+	boost::scoped_ptr<LLWindowListener> mWindowListener;
+	boost::scoped_ptr<LLViewerWindowListener> mViewerWindowListener;
 
 protected:
 	static std::string sSnapshotBaseName;
diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3e3287032c6e3e1d452be8c2b60980f02df5ca4d
--- /dev/null
+++ b/indra/newview/llwindowlistener.cpp
@@ -0,0 +1,450 @@
+/** 
+ * @file llwindowlistener.cpp
+ * @brief EventAPI interface for injecting input into LLWindow
+ *
+ * $LicenseInfo:firstyear=2001&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 "linden_common.h"
+
+#include "llwindowlistener.h"
+
+#include "llcoord.h"
+#include "llfocusmgr.h"
+#include "llkeyboard.h"
+#include "llwindowcallbacks.h"
+#include "llui.h"
+#include "llview.h"
+#include "llviewinject.h"
+#include "llviewerwindow.h"
+#include "llviewerkeyboard.h"
+#include "llrootview.h"
+#include "llsdutil.h"
+#include "stringize.h"
+#include <typeinfo>
+#include <map>
+#include <boost/scoped_ptr.hpp>
+#include <boost/lambda/core.hpp>
+#include <boost/lambda/bind.hpp>
+
+namespace bll = boost::lambda;
+
+LLWindowListener::LLWindowListener(LLViewerWindow *window, const KeyboardGetter& kbgetter)
+	: LLEventAPI("LLWindow", "Inject input events into the LLWindow instance"),
+	  mWindow(window),
+	  mKbGetter(kbgetter)
+{
+	std::string keySomething =
+		"Given [\"keysym\"], [\"keycode\"] or [\"char\"], inject the specified ";
+	std::string keyExplain =
+		"(integer keycode values, or keysym string from any addKeyName() call in\n"
+		"http://hg.secondlife.com/viewer-development/src/tip/indra/llwindow/llkeyboard.cpp )\n";
+	std::string mask =
+		"Specify optional [\"mask\"] as an array containing any of \"CTL\", \"ALT\",\n"
+		"\"SHIFT\" or \"MAC_CONTROL\"; the corresponding modifier bits will be combined\n"
+		"to form the mask used with the event.";
+
+	std::string given = "Given ";
+	std::string mouseParams =
+		"optional [\"path\"], optional [\"x\"] and [\"y\"], inject the requested mouse ";
+	std::string buttonParams =
+		std::string("[\"button\"], ") + mouseParams;
+	std::string buttonExplain =
+		"(button values \"LEFT\", \"MIDDLE\", \"RIGHT\")\n";
+	std::string paramsExplain =
+		"[\"path\"] is as for LLUI::resolvePath(), described in\n"
+		"http://hg.secondlife.com/viewer-development/src/tip/indra/llui/llui.h\n"
+		"If you omit [\"path\"], you must specify both [\"x\"] and [\"y\"].\n"
+		"If you specify [\"path\"] without both [\"x\"] and [\"y\"], will synthesize (x, y)\n"
+		"in the center of the LLView selected by [\"path\"].\n"
+		"You may specify [\"path\"] with both [\"x\"] and [\"y\"], will use your (x, y).\n"
+		"This may cause the LLView selected by [\"path\"] to reject the event.\n"
+		"Optional [\"reply\"] requests a reply event on the named LLEventPump.\n"
+		"reply[\"error\"] isUndefined (None) on success, else an explanatory message.\n";
+
+	add("keyDown",
+		keySomething + "keypress event.\n" + keyExplain + mask,
+		&LLWindowListener::keyDown);
+	add("keyUp",
+		keySomething + "key release event.\n" + keyExplain + mask,
+		&LLWindowListener::keyUp);
+	add("mouseDown",
+		given + buttonParams + "click event.\n" + buttonExplain + paramsExplain + mask,
+		&LLWindowListener::mouseDown);
+	add("mouseUp",
+		given + buttonParams + "release event.\n" + buttonExplain + paramsExplain + mask,
+		&LLWindowListener::mouseUp);
+	add("mouseMove",
+		given + mouseParams + "movement event.\n" + paramsExplain + mask,
+		&LLWindowListener::mouseMove);
+	add("mouseScroll",
+		"Given an integer number of [\"clicks\"], inject the requested mouse scroll event.\n"
+		"(positive clicks moves downward through typical content)",
+		&LLWindowListener::mouseScroll);
+}
+
+template <typename MAPPED>
+class StringLookup
+{
+private:
+	std::string mDesc;
+	typedef std::map<std::string, MAPPED> Map;
+	Map mMap;
+
+public:
+	StringLookup(const std::string& desc): mDesc(desc) {}
+
+	MAPPED lookup(const typename Map::key_type& key) const
+	{
+		typename Map::const_iterator found = mMap.find(key);
+		if (found == mMap.end())
+		{
+			LL_WARNS("LLWindowListener") << "Unknown " << mDesc << " '" << key << "'" << LL_ENDL;
+			return MAPPED();
+		}
+		return found->second;
+	}
+
+protected:
+	void add(const typename Map::key_type& key, const typename Map::mapped_type& value)
+	{
+		mMap.insert(typename Map::value_type(key, value));
+	}
+};
+
+namespace {
+
+void insertViewInformation(LLEventAPI::Response & response, LLView * target)
+{
+	// Get info about this LLView* for when we send response.
+	response["path"] = target->getPathname();
+	response["class"] = typeid(*target).name();
+	response["visible"] = target->getVisible();
+	response["visible_chain"] = target->isInVisibleChain();
+	response["enabled"] = target->getEnabled();
+	response["enabled_chain"] = target->isInEnabledChain();
+	response["available"] = target->isAvailable();
+}
+
+// helper for getMask()
+MASK lookupMask_(const std::string& maskname)
+{
+	// It's unclear to me whether MASK_MAC_CONTROL is important, but it's not
+	// supported by maskFromString(). Handle that specially.
+	if (maskname == "MAC_CONTROL")
+	{
+		return MASK_MAC_CONTROL;
+	}
+	else
+	{
+		// In case of lookup failure, return MASK_NONE, which won't affect our
+		// caller's OR.
+		MASK mask(MASK_NONE);
+		LLKeyboard::maskFromString(maskname, &mask);
+		return mask;
+	}
+}
+
+MASK getMask(const LLSD& event)
+{
+	LLSD masknames(event["mask"]);
+	if (! masknames.isArray())
+	{
+		// If event["mask"] is a single string, perform normal lookup on it.
+		return lookupMask_(masknames);
+	}
+
+	// Here event["mask"] is an array of mask-name strings. OR together their
+	// corresponding bits.
+	MASK mask(MASK_NONE);
+	for (LLSD::array_const_iterator ai(masknames.beginArray()), aend(masknames.endArray());
+		 ai != aend; ++ai)
+	{
+		mask |= lookupMask_(*ai);
+	}
+	return mask;
+}
+
+KEY getKEY(const LLSD& event)
+{
+    if (event.has("keysym"))
+	{
+		// Initialize to KEY_NONE; that way we can ignore the bool return from
+		// keyFromString() and, in the lookup-fail case, simply return KEY_NONE.
+		KEY key(KEY_NONE);
+		LLKeyboard::keyFromString(event["keysym"], &key);
+		return key;
+	}
+	else if (event.has("keycode"))
+	{
+		return KEY(event["keycode"].asInteger());
+	}
+	else
+	{
+		return KEY(event["char"].asString()[0]);
+	}
+}
+
+} // namespace
+
+void LLWindowListener::keyDown(LLSD const & evt)
+{
+	Response response(LLSD(), evt);
+	
+	if (evt.has("path"))
+	{
+		std::string path(evt["path"]);
+		LLView * target_view = 
+			LLUI::resolvePath(gViewerWindow->getRootView(), path);
+		if (target_view == 0) 
+		{
+			response.error(STRINGIZE(evt["op"].asString() << " request "
+											"specified invalid \"path\": '" << path << "'"));
+		}
+		else if(target_view->isAvailable())
+		{
+			insertViewInformation(response, target_view);
+			
+			gFocusMgr.setKeyboardFocus(target_view);
+			KEY key = getKEY(evt);
+			MASK mask = getMask(evt);
+			gViewerKeyboard.handleKey(key, mask, false);
+			if(key < 0x80) mWindow->handleUnicodeChar(key, mask);
+		}
+		else 
+		{
+			response.error(STRINGIZE(evt["op"].asString() << " request "
+											"element specified by \"path\": '" << path << "'" 
+											<< " is not visible"));
+		}
+	}
+	else 
+	{
+		mKbGetter()->handleTranslatedKeyDown(getKEY(evt), getMask(evt));
+	}
+}
+
+void LLWindowListener::keyUp(LLSD const & evt)
+{
+	Response response(LLSD(), evt);
+
+	if (evt.has("path"))
+	{
+		std::string path(evt["path"]);
+		LLView * target_view = 
+			LLUI::resolvePath(gViewerWindow->getRootView(), path);
+		if (target_view == 0 )
+		{
+			response.error(STRINGIZE(evt["op"].asString() << " request "
+											"specified invalid \"path\": '" << path << "'"));
+		}
+		else if (target_view->isAvailable())
+		{
+			insertViewInformation(response, target_view);
+
+			gFocusMgr.setKeyboardFocus(target_view);
+			mKbGetter()->handleTranslatedKeyUp(getKEY(evt), getMask(evt));
+		}
+		else 
+		{
+			response.error(STRINGIZE(evt["op"].asString() << " request "
+											"element specified byt \"path\": '" << path << "'" 
+											<< " is not visible"));
+		}
+	}
+	else 
+	{
+		mKbGetter()->handleTranslatedKeyUp(getKEY(evt), getMask(evt));
+	}
+}
+
+// for WhichButton
+typedef BOOL (LLWindowCallbacks::*MouseMethod)(LLWindow *, LLCoordGL, MASK);
+struct Actions
+{
+	Actions(const MouseMethod& d, const MouseMethod& u): down(d), up(u), valid(true) {}
+	Actions(): valid(false) {}
+	MouseMethod down, up;
+	bool valid;
+};
+
+struct WhichButton: public StringLookup<Actions>
+{
+	WhichButton(): StringLookup<Actions>("mouse button")
+	{
+		add("LEFT",		Actions(&LLWindowCallbacks::handleMouseDown,
+								&LLWindowCallbacks::handleMouseUp));
+		add("RIGHT",	Actions(&LLWindowCallbacks::handleRightMouseDown,
+								&LLWindowCallbacks::handleRightMouseUp));
+		add("MIDDLE",	Actions(&LLWindowCallbacks::handleMiddleMouseDown,
+								&LLWindowCallbacks::handleMiddleMouseUp));
+	}
+};
+static WhichButton buttons;
+
+typedef boost::function<bool(LLCoordGL, MASK)> MouseFunc;
+
+static void mouseEvent(const MouseFunc& func, const LLSD& request)
+{
+	// Ensure we send response
+	LLEventAPI::Response response(LLSD(), request);
+	// We haven't yet established whether the incoming request has "x" and "y",
+	// but capture this anyway, with 0 for omitted values.
+	LLCoordGL pos(request["x"].asInteger(), request["y"].asInteger());
+	bool has_pos(request.has("x") && request.has("y"));
+
+	boost::scoped_ptr<LLView::TemporaryDrilldownFunc> tempfunc;
+
+	// Documentation for mouseDown(), mouseUp() and mouseMove() claims you
+	// must either specify ["path"], or both of ["x"] and ["y"]. You MAY
+	// specify all. Let's say that passing "path" as an empty string is
+	// equivalent to not passing it at all.
+	std::string path(request["path"]);
+	if (path.empty())
+	{
+		// Without "path", you must specify both "x" and "y".
+		if (! has_pos)
+		{
+			return response.error(STRINGIZE(request["op"].asString() << " request "
+											"without \"path\" must specify both \"x\" and \"y\": "
+											<< request));
+		}
+	}
+	else // ! path.empty()
+	{
+		LLView* root   = LLUI::getRootView();
+		LLView* target = LLUI::resolvePath(root, path);
+		if (! target)
+		{
+			return response.error(STRINGIZE(request["op"].asString() << " request "
+											"specified invalid \"path\": '" << path << "'"));
+		}
+
+		insertViewInformation(response, target);
+
+		// Don't show caller the LLView's own relative rectangle; that only
+		// tells its dimensions. Provide actual location on screen.
+		LLRect rect(target->calcScreenRect());
+		response["rect"] = LLSDMap("left", rect.mLeft)("top", rect.mTop)("right", rect.mRight)("bottom", rect.mBottom);
+
+		// The intent of this test is to prevent trying to drill down to a
+		// widget in a hidden floater, or on a tab that's not current, etc.
+		if (! target->isInVisibleChain())
+		{
+			return response.error(STRINGIZE(request["op"].asString() << " request "
+											"specified \"path\" not currently visible: '"
+											<< path << "'"));
+		}
+
+		// This test isn't folded in with the above error case since you can
+		// (e.g.) pop up a tooltip even for a disabled widget.
+		if (! target->isInEnabledChain())
+		{
+			response.warn(STRINGIZE(request["op"].asString() << " request "
+									"specified \"path\" not currently enabled: '"
+									<< path << "'"));
+		}
+
+		if (! has_pos)
+		{
+			pos.set(rect.getCenterX(), rect.getCenterY());
+			// nonstandard warning tactic: probably usual case; we want event
+			// sender to know synthesized (x, y), but maybe don't need to log?
+			response["warnings"].append(STRINGIZE("using center point ("
+												  << pos.mX << ", " << pos.mY << ")"));
+		}
+
+		// recursive childFromPoint() should give us the frontmost, leafmost
+		// widget at the specified (x, y).
+		LLView* frontmost = root->childFromPoint(pos.mX, pos.mY, true);
+		if (frontmost != target)
+		{
+			response.warn(STRINGIZE(request["op"].asString() << " request "
+									"specified \"path\" = '" << path
+									<< "', but frontmost LLView at (" << pos.mX << ", " << pos.mY
+									<< ") is '" << LLView::getPathname(frontmost) << "'"));
+		}
+
+		// Instantiate a TemporaryDrilldownFunc to route incoming mouse events
+		// to the target LLView*. But put it on the heap since "path" is
+		// optional. Nonetheless, manage it with a boost::scoped_ptr so it
+		// will be destroyed when we leave.
+		tempfunc.reset(new LLView::TemporaryDrilldownFunc(llview::TargetEvent(target)));
+	}
+
+	// The question of whether the requested LLView actually handled the
+	// specified event is important enough, and its handling unclear enough,
+	// to warrant a separate response attribute. Instead of deciding here to
+	// make it a warning, or an error, let caller decide.
+	response["handled"] = func(pos, getMask(request));
+
+	// On exiting this scope, response will send, tempfunc will restore the
+	// normal pointInView(x, y) containment logic, etc.
+}
+
+void LLWindowListener::mouseDown(LLSD const & request)
+{
+	Actions actions(buttons.lookup(request["button"]));
+	if (actions.valid)
+	{
+		// Normally you can pass NULL to an LLWindow* without compiler
+		// complaint, but going through boost::lambda::bind() evidently
+		// bypasses that special case: it only knows you're trying to pass an
+		// int to a pointer. Explicitly cast NULL to the desired pointer type.
+		mouseEvent(bll::bind(actions.down, mWindow,
+							 static_cast<LLWindow*>(NULL), bll::_1, bll::_2),
+				   request);
+	}
+}
+
+void LLWindowListener::mouseUp(LLSD const & request)
+{
+	Actions actions(buttons.lookup(request["button"]));
+	if (actions.valid)
+	{
+		mouseEvent(bll::bind(actions.up, mWindow,
+							 static_cast<LLWindow*>(NULL), bll::_1, bll::_2),
+				   request);
+	}
+}
+
+void LLWindowListener::mouseMove(LLSD const & request)
+{
+	// We want to call the same central mouseEvent() routine for
+	// handleMouseMove() as for button clicks. But handleMouseMove() returns
+	// void, whereas mouseEvent() accepts a function returning bool -- and
+	// uses that bool return. Use (void-lambda-expression, true) to construct
+	// a callable that returns bool anyway. Pass 'true' because we expect that
+	// our caller will usually treat 'false' as a problem.
+	mouseEvent((bll::bind(&LLWindowCallbacks::handleMouseMove, mWindow,
+						  static_cast<LLWindow*>(NULL), bll::_1, bll::_2),
+				true),
+			   request);
+}
+
+void LLWindowListener::mouseScroll(LLSD const & request)
+{
+	S32 clicks = request["clicks"].asInteger();
+
+	mWindow->handleScrollWheel(NULL, clicks);
+}
diff --git a/indra/llwindow/llwindowlistener.h b/indra/newview/llwindowlistener.h
similarity index 92%
rename from indra/llwindow/llwindowlistener.h
rename to indra/newview/llwindowlistener.h
index 74e577ff93a5ac3145108b91cced5707dce9c2e5..26adff35ff76a3d00b43d19b50b14db5247a2da6 100644
--- a/indra/llwindow/llwindowlistener.h
+++ b/indra/newview/llwindowlistener.h
@@ -31,13 +31,13 @@
 #include <boost/function.hpp>
 
 class LLKeyboard;
-class LLWindowCallbacks;
+class LLViewerWindow;
 
 class LLWindowListener : public LLEventAPI
 {
 public:
 	typedef boost::function<LLKeyboard*()> KeyboardGetter;
-	LLWindowListener(LLWindowCallbacks * window, const KeyboardGetter& kbgetter);
+	LLWindowListener(LLViewerWindow * window, const KeyboardGetter& kbgetter);
 
 	void keyDown(LLSD const & evt);
 	void keyUp(LLSD const & evt);
@@ -47,7 +47,7 @@ class LLWindowListener : public LLEventAPI
 	void mouseScroll(LLSD const & evt);
 
 private:
-	LLWindowCallbacks * mWindow;
+	LLViewerWindow * mWindow;
 	KeyboardGetter mKbGetter;
 };