diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index fc9fcb2d9ecb49455d30436133da24a58101b4b1..fc3ce6df7ec790ecb927ff47cf28c79729cf5448 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -35,8 +35,8 @@
 
 const S32 LL_VERSION_MAJOR = 2;
 const S32 LL_VERSION_MINOR = 0;
-const S32 LL_VERSION_PATCH = 1;
-const S32 LL_VERSION_BUILD = 203110;
+const S32 LL_VERSION_PATCH = 2;
+const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";
 
diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp
index e524c88cf832d2db02a114f35e65c51509e90ba1..89f8b445695ae327a87722a039fda6b61e022e80 100644
--- a/indra/llplugin/llpluginmessagepipe.cpp
+++ b/indra/llplugin/llpluginmessagepipe.cpp
@@ -96,11 +96,14 @@ void LLPluginMessagePipeOwner::killMessagePipe(void)
 	}
 }
 
-LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket)
+LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket):
+	mInputMutex(gAPRPoolp),
+	mOutputMutex(gAPRPoolp),
+	mOwner(owner),
+	mSocket(socket)
 {
-	mOwner = owner;
+	
 	mOwner->setMessagePipe(this);
-	mSocket = socket;
 }
 
 LLPluginMessagePipe::~LLPluginMessagePipe()
@@ -114,6 +117,7 @@ LLPluginMessagePipe::~LLPluginMessagePipe()
 bool LLPluginMessagePipe::addMessage(const std::string &message)
 {
 	// queue the message for later output
+	LLMutexLock lock(&mOutputMutex);
 	mOutput += message;
 	mOutput += MESSAGE_DELIMITER;	// message separator
 	
@@ -148,6 +152,18 @@ void LLPluginMessagePipe::setSocketTimeout(apr_interval_time_t timeout_usec)
 }
 
 bool LLPluginMessagePipe::pump(F64 timeout)
+{
+	bool result = pumpOutput();
+	
+	if(result)
+	{
+		result = pumpInput(timeout);
+	}
+	
+	return result;
+}
+
+bool LLPluginMessagePipe::pumpOutput()
 {
 	bool result = true;
 	
@@ -156,6 +172,7 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 		apr_status_t status;
 		apr_size_t size;
 		
+		LLMutexLock lock(&mOutputMutex);
 		if(!mOutput.empty())
 		{
 			// write any outgoing messages
@@ -183,6 +200,17 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 				// remove the written part from the buffer and try again later.
 				mOutput = mOutput.substr(size);
 			}
+			else if(APR_STATUS_IS_EOF(status))
+			{
+				// This is what we normally expect when a plugin exits.
+				llinfos << "Got EOF from plugin socket. " << llendl;
+
+				if(mOwner)
+				{
+					mOwner->socketError(status);
+				}
+				result = false;
+			}
 			else 
 			{
 				// some other error
@@ -196,6 +224,19 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 				result = false;
 			}
 		}
+	}
+	
+	return result;
+}
+
+bool LLPluginMessagePipe::pumpInput(F64 timeout)
+{
+	bool result = true;
+
+	if(mSocket)
+	{
+		apr_status_t status;
+		apr_size_t size;
 
 		// FIXME: For some reason, the apr timeout stuff isn't working properly on windows.
 		// Until such time as we figure out why, don't try to use the socket timeout -- just sleep here instead.
@@ -216,8 +257,16 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 			char input_buf[1024];
 			apr_size_t request_size;
 			
-			// Start out by reading one byte, so that any data received will wake us up.
-			request_size = 1;
+			if(timeout == 0.0f)
+			{
+				// If we have no timeout, start out with a full read.
+				request_size = sizeof(input_buf);
+			}
+			else
+			{
+				// Start out by reading one byte, so that any data received will wake us up.
+				request_size = 1;
+			}
 			
 			// and use the timeout so we'll sleep if no data is available.
 			setSocketTimeout((apr_interval_time_t)(timeout * 1000000));
@@ -236,11 +285,14 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 //				LL_INFOS("Plugin") << "after apr_socket_recv, size = " << size << LL_ENDL;
 				
 				if(size > 0)
+				{
+					LLMutexLock lock(&mInputMutex);
 					mInput.append(input_buf, size);
+				}
 
 				if(status == APR_SUCCESS)
 				{
-//					llinfos << "success, read " << size << llendl;
+					LL_DEBUGS("PluginSocket") << "success, read " << size << LL_ENDL;
 
 					if(size != request_size)
 					{
@@ -250,16 +302,28 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 				}
 				else if(APR_STATUS_IS_TIMEUP(status))
 				{
-//					llinfos << "TIMEUP, read " << size << llendl;
+					LL_DEBUGS("PluginSocket") << "TIMEUP, read " << size << LL_ENDL;
 
 					// Timeout was hit.  Since the initial read is 1 byte, this should never be a partial read.
 					break;
 				}
 				else if(APR_STATUS_IS_EAGAIN(status))
 				{
-//					llinfos << "EAGAIN, read " << size << llendl;
+					LL_DEBUGS("PluginSocket") << "EAGAIN, read " << size << LL_ENDL;
 
-					// We've been doing partial reads, and we're done now.
+					// Non-blocking read returned immediately.
+					break;
+				}
+				else if(APR_STATUS_IS_EOF(status))
+				{
+					// This is what we normally expect when a plugin exits.
+					LL_INFOS("PluginSocket") << "Got EOF from plugin socket. " << LL_ENDL;
+
+					if(mOwner)
+					{
+						mOwner->socketError(status);
+					}
+					result = false;
 					break;
 				}
 				else
@@ -276,22 +340,18 @@ bool LLPluginMessagePipe::pump(F64 timeout)
 					break;
 				}
 
-				// Second and subsequent reads should not use the timeout
-				setSocketTimeout(0);
-				// and should try to fill the input buffer
-				request_size = sizeof(input_buf);
+				if(timeout != 0.0f)
+				{
+					// Second and subsequent reads should not use the timeout
+					setSocketTimeout(0);
+					// and should try to fill the input buffer
+					request_size = sizeof(input_buf);
+				}
 			}
 			
 			processInput();
 		}
 	}
-
-	if(!result)
-	{
-		// If we got an error, we're done.
-		LL_INFOS("Plugin") << "Error from socket, cleaning up." << LL_ENDL;
-		delete this;
-	}
 	
 	return result;	
 }
@@ -300,6 +360,7 @@ void LLPluginMessagePipe::processInput(void)
 {
 	// Look for input delimiter(s) in the input buffer.
 	int delim;
+	mInputMutex.lock();
 	while((delim = mInput.find(MESSAGE_DELIMITER)) != std::string::npos)
 	{	
 		// Let the owner process this message
@@ -310,12 +371,15 @@ void LLPluginMessagePipe::processInput(void)
 			// and this guarantees that the messages will get dequeued correctly.
 			std::string message(mInput, 0, delim);
 			mInput.erase(0, delim + 1);
+			mInputMutex.unlock();
 			mOwner->receiveMessageRaw(message);
+			mInputMutex.lock();
 		}
 		else
 		{
 			LL_WARNS("Plugin") << "!mOwner" << LL_ENDL;
 		}
 	}
+	mInputMutex.unlock();
 }
 
diff --git a/indra/llplugin/llpluginmessagepipe.h b/indra/llplugin/llpluginmessagepipe.h
index 1ddb38de6883f63b6e7e090b6ff3c77be0b25d6d..1b0a08254bb1cff36bb31d09d24dc4276b06df8e 100644
--- a/indra/llplugin/llpluginmessagepipe.h
+++ b/indra/llplugin/llpluginmessagepipe.h
@@ -35,6 +35,7 @@
 #define LL_LLPLUGINMESSAGEPIPE_H
 
 #include "lliosocket.h"
+#include "llthread.h"
 
 class LLPluginMessagePipe;
 
@@ -51,7 +52,7 @@ class LLPluginMessagePipeOwner
 	virtual apr_status_t socketError(apr_status_t error);
 
 	// called from LLPluginMessagePipe to manage the connection with LLPluginMessagePipeOwner -- do not use!
-	virtual void setMessagePipe(LLPluginMessagePipe *message_pipe) ;
+	virtual void setMessagePipe(LLPluginMessagePipe *message_pipe);
 
 protected:
 	// returns false if writeMessageRaw() would drop the message
@@ -76,14 +77,18 @@ class LLPluginMessagePipe
 	void clearOwner(void);
 	
 	bool pump(F64 timeout = 0.0f);
-	
+	bool pumpOutput();
+	bool pumpInput(F64 timeout = 0.0f);
+		
 protected:	
 	void processInput(void);
 
 	// used internally by pump()
 	void setSocketTimeout(apr_interval_time_t timeout_usec);
 	
+	LLMutex mInputMutex;
 	std::string mInput;
+	LLMutex mOutputMutex;
 	std::string mOutput;
 
 	LLPluginMessagePipeOwner *mOwner;
diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp
index 2d078cd6ed71d4c150c4d4b5a6eda4f0d7708291..d1cf91b2537e21a7a18c43bb75d6ebf4d00397c0 100644
--- a/indra/llplugin/llpluginprocesschild.cpp
+++ b/indra/llplugin/llpluginprocesschild.cpp
@@ -85,9 +85,14 @@ void LLPluginProcessChild::idle(void)
 	bool idle_again;
 	do
 	{
-		if(mSocketError != APR_SUCCESS)
+		if(APR_STATUS_IS_EOF(mSocketError))
 		{
-			LL_INFOS("Plugin") << "message pipe is in error state, moving to STATE_ERROR"<< LL_ENDL;
+			// Plugin socket was closed.  This covers both normal plugin termination and host crashes.
+			setState(STATE_ERROR);
+		}
+		else if(mSocketError != APR_SUCCESS)
+		{
+			LL_INFOS("Plugin") << "message pipe is in error state (" << mSocketError << "), moving to STATE_ERROR"<< LL_ENDL;
 			setState(STATE_ERROR);
 		}	
 
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index e273410a1d5f60ae3c01eaf2d4338dbaa9e56147..3589b22a77164004f036f2962d8c694ecf60c68e 100644
--- a/indra/llplugin/llpluginprocessparent.cpp
+++ b/indra/llplugin/llpluginprocessparent.cpp
@@ -45,8 +45,51 @@ LLPluginProcessParentOwner::~LLPluginProcessParentOwner()
 	
 }
 
-LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner)
+bool LLPluginProcessParent::sUseReadThread = false;
+apr_pollset_t *LLPluginProcessParent::sPollSet = NULL;
+bool LLPluginProcessParent::sPollsetNeedsRebuild = false;
+LLMutex *LLPluginProcessParent::sInstancesMutex;
+std::list<LLPluginProcessParent*> LLPluginProcessParent::sInstances;
+LLThread *LLPluginProcessParent::sReadThread = NULL;
+
+
+class LLPluginProcessParentPollThread: public LLThread
 {
+public:
+	LLPluginProcessParentPollThread() :
+		LLThread("LLPluginProcessParentPollThread", gAPRPoolp)
+	{
+	}
+protected:
+	// Inherited from LLThread
+	/*virtual*/ void run(void)
+	{
+		while(!isQuitting() && LLPluginProcessParent::getUseReadThread())
+		{
+			LLPluginProcessParent::poll(0.1f);
+			checkPause();
+		}
+		
+		// Final poll to clean up the pollset, etc.
+		LLPluginProcessParent::poll(0.0f);
+	} 
+
+	// Inherited from LLThread
+	/*virtual*/ bool runCondition(void)
+	{
+		return(LLPluginProcessParent::canPollThreadRun());
+	}
+
+};
+
+LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner):
+	mIncomingQueueMutex(gAPRPoolp)
+{
+	if(!sInstancesMutex)
+	{
+		sInstancesMutex = new LLMutex(gAPRPoolp);
+	}
+	
 	mOwner = owner;
 	mBoundPort = 0;
 	mState = STATE_UNINITIALIZED;
@@ -55,18 +98,36 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner)
 	mDisableTimeout = false;
 	mDebug = false;
 	mBlocked = false;
+	mPolledInput = false;
+	mPollFD.client_data = NULL;
 
 	mPluginLaunchTimeout = 60.0f;
 	mPluginLockupTimeout = 15.0f;
 	
 	// Don't start the timer here -- start it when we actually launch the plugin process.
 	mHeartbeat.stop();
+	
+	// Don't add to the global list until fully constructed.
+	{
+		LLMutexLock lock(sInstancesMutex);
+		sInstances.push_back(this);
+	}
 }
 
 LLPluginProcessParent::~LLPluginProcessParent()
 {
 	LL_DEBUGS("Plugin") << "destructor" << LL_ENDL;
 
+	// Remove from the global list before beginning destruction.
+	{
+		// Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll()
+		LLMutexLock lock(sInstancesMutex);
+		{
+			LLMutexLock lock2(&mIncomingQueueMutex);
+			sInstances.remove(this);
+		}
+	}
+
 	// Destroy any remaining shared memory regions
 	sharedMemoryRegionsType::iterator iter;
 	while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end())
@@ -78,15 +139,17 @@ LLPluginProcessParent::~LLPluginProcessParent()
 		mSharedMemoryRegions.erase(iter);
 	}
 	
-	// orphaning the process means it won't be killed when the LLProcessLauncher is destructed.
-	// This is what we want -- it should exit cleanly once it notices the sockets have been closed.
-	mProcess.orphan();
+	mProcess.kill();
 	killSockets();
 }
 
 void LLPluginProcessParent::killSockets(void)
 {
-	killMessagePipe();
+	{
+		LLMutexLock lock(&mIncomingQueueMutex);
+		killMessagePipe();
+	}
+
 	mListenSocket.reset();
 	mSocket.reset();
 }
@@ -160,21 +223,47 @@ void LLPluginProcessParent::idle(void)
 
 	do
 	{
+		// process queued messages
+		mIncomingQueueMutex.lock();
+		while(!mIncomingQueue.empty())
+		{
+			LLPluginMessage message = mIncomingQueue.front();
+			mIncomingQueue.pop();
+			mIncomingQueueMutex.unlock();
+				
+			receiveMessage(message);
+			
+			mIncomingQueueMutex.lock();
+		}
+
+		mIncomingQueueMutex.unlock();
+		
 		// Give time to network processing
 		if(mMessagePipe)
 		{
-			if(!mMessagePipe->pump())
+			// Drain any queued outgoing messages
+			mMessagePipe->pumpOutput();
+			
+			// Only do input processing here if this instance isn't in a pollset.
+			if(!mPolledInput)
 			{
-//				LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL;
-				errorState();
+				mMessagePipe->pumpInput();
 			}
 		}
-
-		if((mSocketError != APR_SUCCESS) && (mState <= STATE_RUNNING))
+		
+		if(mState <= STATE_RUNNING)
 		{
-			// The socket is in an error state -- the plugin is gone.
-			LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL;
-			errorState();
+			if(APR_STATUS_IS_EOF(mSocketError))
+			{
+				// Plugin socket was closed.  This covers both normal plugin termination and plugin crashes.
+				errorState();
+			}
+			else if(mSocketError != APR_SUCCESS)
+			{
+				// The socket is in an error state -- the plugin is gone.
+				LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL;
+				errorState();
+			}
 		}	
 		
 		// If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState().
@@ -355,7 +444,7 @@ void LLPluginProcessParent::idle(void)
 			break;
 
 			case STATE_HELLO:
-				LL_DEBUGS("Plugin") << "received hello message" << llendl;
+				LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL;
 				
 				// Send the message to load the plugin
 				{
@@ -389,7 +478,7 @@ void LLPluginProcessParent::idle(void)
 				}
 				else if(pluginLockedUp())
 				{
-					LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl;
+					LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL;
 					errorState();
 				}
 			break;
@@ -411,8 +500,7 @@ void LLPluginProcessParent::idle(void)
 			break;
 			
 			case STATE_CLEANUP:
-				// Don't do a kill here anymore -- closing the sockets is the new 'kill'.
-				mProcess.orphan();
+				mProcess.kill();
 				killSockets();
 				setState(STATE_DONE);
 			break;
@@ -491,29 +579,317 @@ void LLPluginProcessParent::sendMessage(const LLPluginMessage &message)
 	std::string buffer = message.generate();
 	LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL;	
 	writeMessageRaw(buffer);
+	
+	// Try to send message immediately.
+	if(mMessagePipe)
+	{
+		mMessagePipe->pumpOutput();
+	}
+}
+
+//virtual 
+void LLPluginProcessParent::setMessagePipe(LLPluginMessagePipe *message_pipe)
+{
+	bool update_pollset = false;
+	
+	if(mMessagePipe)
+	{
+		// Unsetting an existing message pipe -- remove from the pollset		
+		mPollFD.client_data = NULL;
+
+		// pollset needs an update
+		update_pollset = true;
+	}
+	if(message_pipe != NULL)
+	{
+		// Set up the apr_pollfd_t
+		mPollFD.p = gAPRPoolp;
+		mPollFD.desc_type = APR_POLL_SOCKET;
+		mPollFD.reqevents = APR_POLLIN|APR_POLLERR|APR_POLLHUP;
+		mPollFD.rtnevents = 0;
+		mPollFD.desc.s = mSocket->getSocket();
+		mPollFD.client_data = (void*)this;	
+		
+		// pollset needs an update
+		update_pollset = true;
+	}
+
+	mMessagePipe = message_pipe;
+	
+	if(update_pollset)
+	{
+		dirtyPollSet();
+	}
+}
+
+//static 
+void LLPluginProcessParent::dirtyPollSet()
+{
+	sPollsetNeedsRebuild = true;
+	
+	if(sReadThread)
+	{
+		LL_DEBUGS("PluginPoll") << "unpausing read thread " << LL_ENDL;
+		sReadThread->unpause();
+	}
+}
+
+void LLPluginProcessParent::updatePollset()
+{
+	if(!sInstancesMutex)
+	{
+		// No instances have been created yet.  There's no work to do.
+		return;
+	}
+		
+	LLMutexLock lock(sInstancesMutex);
+
+	if(sPollSet)
+	{
+		LL_DEBUGS("PluginPoll") << "destroying pollset " << sPollSet << LL_ENDL;
+		// delete the existing pollset.
+		apr_pollset_destroy(sPollSet);
+		sPollSet = NULL;
+	}
+	
+	std::list<LLPluginProcessParent*>::iterator iter;
+	int count = 0;
+	
+	// Count the number of instances that want to be in the pollset
+	for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
+	{
+		(*iter)->mPolledInput = false;
+		if((*iter)->mPollFD.client_data)
+		{
+			// This instance has a socket that needs to be polled.
+			++count;
+		}
+	}
+
+	if(sUseReadThread && sReadThread && !sReadThread->isQuitting())
+	{
+		if(!sPollSet && (count > 0))
+		{
+#ifdef APR_POLLSET_NOCOPY
+			// The pollset doesn't exist yet.  Create it now.
+			apr_status_t status = apr_pollset_create(&sPollSet, count, gAPRPoolp, APR_POLLSET_NOCOPY);
+			if(status != APR_SUCCESS)
+			{
+#endif // APR_POLLSET_NOCOPY
+				LL_WARNS("PluginPoll") << "Couldn't create pollset.  Falling back to non-pollset mode." << LL_ENDL;
+				sPollSet = NULL;
+#ifdef APR_POLLSET_NOCOPY
+			}
+			else
+			{
+				LL_DEBUGS("PluginPoll") << "created pollset " << sPollSet << LL_ENDL;
+				
+				// Pollset was created, add all instances to it.
+				for(iter = sInstances.begin(); iter != sInstances.end(); iter++)
+				{
+					if((*iter)->mPollFD.client_data)
+					{
+						status = apr_pollset_add(sPollSet, &((*iter)->mPollFD));
+						if(status == APR_SUCCESS)
+						{
+							(*iter)->mPolledInput = true;
+						}
+						else
+						{
+							LL_WARNS("PluginPoll") << "apr_pollset_add failed with status " << status << LL_ENDL;
+						}
+					}
+				}
+			}
+#endif // APR_POLLSET_NOCOPY
+		}
+	}
+}
+
+void LLPluginProcessParent::setUseReadThread(bool use_read_thread)
+{
+	if(sUseReadThread != use_read_thread)
+	{
+		sUseReadThread = use_read_thread;
+		
+		if(sUseReadThread)
+		{
+			if(!sReadThread)
+			{
+				// start up the read thread
+				LL_INFOS("PluginPoll") << "creating read thread " << LL_ENDL;
+
+				// make sure the pollset gets rebuilt.
+				sPollsetNeedsRebuild = true;
+				
+				sReadThread = new LLPluginProcessParentPollThread;
+				sReadThread->start();
+			}
+		}
+		else
+		{
+			if(sReadThread)
+			{
+				// shut down the read thread
+				LL_INFOS("PluginPoll") << "destroying read thread " << LL_ENDL;
+				delete sReadThread;
+				sReadThread = NULL;
+			}
+		}
+
+	}
+}
+
+void LLPluginProcessParent::poll(F64 timeout)
+{
+	if(sPollsetNeedsRebuild || !sUseReadThread)
+	{
+		sPollsetNeedsRebuild = false;
+		updatePollset();
+	}
+	
+	if(sPollSet)
+	{
+		apr_status_t status;
+		apr_int32_t count;
+		const apr_pollfd_t *descriptors;
+		status = apr_pollset_poll(sPollSet, (apr_interval_time_t)(timeout * 1000000), &count, &descriptors);
+		if(status == APR_SUCCESS)
+		{
+			// One or more of the descriptors signalled.  Call them.
+			for(int i = 0; i < count; i++)
+			{
+				LLPluginProcessParent *self = (LLPluginProcessParent *)(descriptors[i].client_data);
+				// NOTE: the descriptor returned here is actually a COPY of the original (even though we create the pollset with APR_POLLSET_NOCOPY).
+				// This means that even if the parent has set its mPollFD.client_data to NULL, the old pointer may still there in this descriptor.
+				// It's even possible that the old pointer no longer points to a valid LLPluginProcessParent.
+				// This means that we can't safely dereference the 'self' pointer here without some extra steps...
+				if(self)
+				{
+					// Make sure this pointer is still in the instances list
+					bool valid = false;
+					{
+						LLMutexLock lock(sInstancesMutex);
+						for(std::list<LLPluginProcessParent*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter)
+						{
+							if(*iter == self)
+							{
+								// Lock the instance's mutex before unlocking the global mutex.  
+								// This avoids a possible race condition where the instance gets deleted between this check and the servicePoll() call.
+								self->mIncomingQueueMutex.lock();
+								valid = true;
+								break;
+							}
+						}
+					}
+					
+					if(valid)
+					{
+						// The instance is still valid.
+						// Pull incoming messages off the socket
+						self->servicePoll();
+						self->mIncomingQueueMutex.unlock();
+					}
+					else
+					{
+						LL_DEBUGS("PluginPoll") << "detected deleted instance " << self << LL_ENDL;
+					}
+
+				}
+			}
+		}
+		else if(APR_STATUS_IS_TIMEUP(status))
+		{
+			// timed out with no incoming data.  Just return.
+		}
+		else if(status == EBADF)
+		{
+			// This happens when one of the file descriptors in the pollset is destroyed, which happens whenever a plugin's socket is closed.
+			// The pollset has been or will be recreated, so just return.
+			LL_DEBUGS("PluginPoll") << "apr_pollset_poll returned EBADF" << LL_ENDL;
+		}
+		else if(status != APR_SUCCESS)
+		{
+			LL_WARNS("PluginPoll") << "apr_pollset_poll failed with status " << status << LL_ENDL;
+		}
+	}
 }
 
+void LLPluginProcessParent::servicePoll()
+{
+	bool result = true;
+	
+	// poll signalled on this object's socket.  Try to process incoming messages.
+	if(mMessagePipe)
+	{
+		result = mMessagePipe->pumpInput(0.0f);
+	}
+
+	if(!result)
+	{
+		// If we got a read error on input, remove this pipe from the pollset
+		apr_pollset_remove(sPollSet, &mPollFD);
+
+		// and tell the code not to re-add it
+		mPollFD.client_data = NULL;
+	}
+}
 
 void LLPluginProcessParent::receiveMessageRaw(const std::string &message)
 {
 	LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL;
-
-	// FIXME: should this go into a queue instead?
 	
 	LLPluginMessage parsed;
 	if(parsed.parse(message) != -1)
 	{
-		receiveMessage(parsed);
+		if(parsed.hasValue("blocking_request"))
+		{
+			mBlocked = true;
+		}
+
+		if(mPolledInput)
+		{
+			// This is being called on the polling thread -- only do minimal processing/queueing.
+			receiveMessageEarly(parsed);
+		}
+		else
+		{
+			// This is not being called on the polling thread -- do full message processing at this time.
+			receiveMessage(parsed);
+		}
 	}
 }
 
-void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message)
+void LLPluginProcessParent::receiveMessageEarly(const LLPluginMessage &message)
 {
-	if(message.hasValue("blocking_request"))
+	// NOTE: this function will be called from the polling thread.  It will be called with mIncomingQueueMutex _already locked_. 
+
+	bool handled = false;
+	
+	std::string message_class = message.getClass();
+	if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
+	{
+		// no internal messages need to be handled early.
+	}
+	else
 	{
-		mBlocked = true;
+		// Call out to the owner and see if they to reply
+		// TODO: Should this only happen when blocked?
+		if(mOwner != NULL)
+		{
+			handled = mOwner->receivePluginMessageEarly(message);
+		}
 	}
+	
+	if(!handled)
+	{
+		// any message that wasn't handled early needs to be queued.
+		mIncomingQueue.push(message);
+	}
+}
 
+void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message)
+{
 	std::string message_class = message.getClass();
 	if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL)
 	{
@@ -704,12 +1080,12 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit()
 	
 	if(!mProcess.isRunning())
 	{
-		LL_WARNS("Plugin") << "child exited" << llendl;
+		LL_WARNS("Plugin") << "child exited" << LL_ENDL;
 		result = true;
 	}
 	else if(pluginLockedUp())
 	{
-		LL_WARNS("Plugin") << "timeout" << llendl;
+		LL_WARNS("Plugin") << "timeout" << LL_ENDL;
 		result = true;
 	}
 	
diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h
index 31f125bfb313eff2e9a09c2522611c2bee879e21..4dff835b6aff1769c8986d596ec1a99d617f6036 100644
--- a/indra/llplugin/llpluginprocessparent.h
+++ b/indra/llplugin/llpluginprocessparent.h
@@ -41,12 +41,14 @@
 #include "llpluginsharedmemory.h"
 
 #include "lliosocket.h"
+#include "llthread.h"
 
 class LLPluginProcessParentOwner
 {
 public:
 	virtual ~LLPluginProcessParentOwner();
 	virtual void receivePluginMessage(const LLPluginMessage &message) = 0;
+	virtual bool receivePluginMessageEarly(const LLPluginMessage &message) {return false;};
 	// This will only be called when the plugin has died unexpectedly 
 	virtual void pluginLaunchFailed() {};
 	virtual void pluginDied() {};
@@ -90,7 +92,9 @@ class LLPluginProcessParent : public LLPluginMessagePipeOwner
 	void receiveMessage(const LLPluginMessage &message);
 	
 	// Inherited from LLPluginMessagePipeOwner
-	void receiveMessageRaw(const std::string &message);
+	/*virtual*/ void receiveMessageRaw(const std::string &message);
+	/*virtual*/ void receiveMessageEarly(const LLPluginMessage &message);
+	/*virtual*/ void setMessagePipe(LLPluginMessagePipe *message_pipe) ;
 	
 	// This adds a memory segment shared with the client, generating a name for the segment.  The name generated is guaranteed to be unique on the host.
 	// The caller must call removeSharedMemory first (and wait until getSharedMemorySize returns 0 for the indicated name) before re-adding a segment with the same name.
@@ -113,7 +117,11 @@ class LLPluginProcessParent : public LLPluginMessagePipeOwner
 	void setLockupTimeout(F32 timeout) { mPluginLockupTimeout = timeout; };
 
 	F64 getCPUUsage() { return mCPUUsage; };
-
+	
+	static void poll(F64 timeout);
+	static bool canPollThreadRun() { return (sPollSet || sPollsetNeedsRebuild || sUseReadThread); };
+	static void setUseReadThread(bool use_read_thread);
+	static bool getUseReadThread() { return sUseReadThread; };
 private:
 
 	enum EState
@@ -164,12 +172,26 @@ class LLPluginProcessParent : public LLPluginMessagePipeOwner
 	bool mDisableTimeout;
 	bool mDebug;
 	bool mBlocked;
+	bool mPolledInput;
 
 	LLProcessLauncher mDebugger;
 	
 	F32 mPluginLaunchTimeout;		// Somewhat longer timeout for initial launch.
 	F32 mPluginLockupTimeout;		// If we don't receive a heartbeat in this many seconds, we declare the plugin locked up.
 
+	static bool sUseReadThread;
+	apr_pollfd_t mPollFD;
+	static apr_pollset_t *sPollSet;
+	static bool sPollsetNeedsRebuild;
+	static LLMutex *sInstancesMutex;
+	static std::list<LLPluginProcessParent*> sInstances;
+	static void dirtyPollSet();
+	static void updatePollset();
+	void servicePoll();
+	static LLThread *sReadThread;
+	
+	LLMutex mIncomingQueueMutex;
+	std::queue<LLPluginMessage> mIncomingQueue;
 };
 
 #endif // LL_LLPLUGINPROCESSPARENT_H
diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp
index 3c706ce90edbbdebefa60a470e3173b5a61bc7c9..596da782ce8852efee16eac65f7759ef80ccb6b0 100644
--- a/indra/llui/llaccordionctrltab.cpp
+++ b/indra/llui/llaccordionctrltab.cpp
@@ -436,6 +436,34 @@ void LLAccordionCtrlTab::setAccordionView(LLView* panel)
 	addChild(panel,0);
 }
 
+void LLAccordionCtrlTab::setTitle(const std::string& title)
+{
+	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+	if (header)
+	{
+		header->setTitle(title);
+	}
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
+{
+	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+	if (header)
+	{
+		return header->setFocusReceivedCallback(cb);
+	}
+	return boost::signals2::connection();
+}
+
+boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb)
+{
+	LLAccordionCtrlTabHeader* header = findChild<LLAccordionCtrlTabHeader>(DD_HEADER_NAME);
+	if (header)
+	{
+		return header->setFocusLostCallback(cb);
+	}
+	return boost::signals2::connection();
+}
 
 LLView*	LLAccordionCtrlTab::findContainerView()
 {
diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h
index fb19d17e99a644a88a642e1349ed9bd6ceed1c63..de254ed3ebb2a7ec2096f81acbb15fcf17fc97f2 100644
--- a/indra/llui/llaccordionctrltab.h
+++ b/indra/llui/llaccordionctrltab.h
@@ -113,6 +113,12 @@ class LLAccordionCtrlTab : public LLUICtrl
 	void		setAccordionView(LLView* panel);
 	LLView*		getAccordionView() { return mContainerPanel; };
 
+	// Set text in LLAccordionCtrlTabHeader
+	void setTitle(const std::string& title);
+
+	boost::signals2::connection setFocusReceivedCallback(const focus_signal_t::slot_type& cb);
+	boost::signals2::connection setFocusLostCallback(const focus_signal_t::slot_type& cb);
+
 	bool getCollapsible() {return mCollapsible;};
 
 	void setCollapsible(bool collapsible) {mCollapsible = collapsible;};
diff --git a/indra/media_plugins/webkit/mac_volume_catcher.cpp b/indra/media_plugins/webkit/mac_volume_catcher.cpp
index 9788f10a582b8c8f30195f1090889bb857e93685..38727e5965fbbf99e6936486355497bce98723a1 100644
--- a/indra/media_plugins/webkit/mac_volume_catcher.cpp
+++ b/indra/media_plugins/webkit/mac_volume_catcher.cpp
@@ -1,5 +1,5 @@
 /** 
- * @file dummy_volume_catcher.cpp
+ * @file mac_volume_catcher.cpp
  * @brief A Mac OS X specific hack to control the volume level of all audio channels opened by a process.
  *
  * @cond
@@ -98,7 +98,7 @@ VolumeCatcherImpl *VolumeCatcherImpl::getInstance()
 VolumeCatcherImpl::VolumeCatcherImpl()
 {
 	mVolume = 1.0;	// default to full volume
-	mPan = 0.5;		// and center pan
+	mPan = 0.0;		// and center pan
 		
 	ComponentDescription desc;
 	desc.componentType = kAudioUnitType_Output;
diff --git a/indra/media_plugins/winmmshim/winmm_shim.cpp b/indra/media_plugins/winmmshim/winmm_shim.cpp
index f7df3b19a06b163f67a70a948642dd2ee477097f..54bfa652e9d460f81dd0aa093bc5afbf1b1b39fa 100644
--- a/indra/media_plugins/winmmshim/winmm_shim.cpp
+++ b/indra/media_plugins/winmmshim/winmm_shim.cpp
@@ -144,10 +144,11 @@ extern "C"
 
 						// copy volume level 4 times into 64 bit MMX register
 						__m64 volume_64 = _mm_set_pi16(volume_16, volume_16, volume_16, volume_16);
-						__m64 *sample_64;
+						__m64* sample_64;
+						__m64* last_sample_64 =  (__m64*)(pwh->lpData + pwh->dwBufferLength - sizeof(__m64));
 						// for everything that can be addressed in 64 bit multiples...
 						for (sample_64 = (__m64*)pwh->lpData;
-							sample_64 < (__m64*)(pwh->lpData + pwh->dwBufferLength);
+							sample_64 <= last_sample_64;
 							++sample_64)
 						{
 							//...multiply the samples by the volume...
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
index 02c3dfc6e0c16d31b7079ce4cccbfa585f9e7dbc..4831dc7273cb3a1622a33099652468b9cd24eb60 100644
--- a/indra/newview/English.lproj/InfoPlist.strings
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -2,6 +2,6 @@
 
 CFBundleName = "Second Life";
 
-CFBundleShortVersionString = "Second Life version 2.0.0.203110";
-CFBundleGetInfoString = "Second Life version 2.0.0.203110, Copyright 2004-2009 Linden Research, Inc.";
+CFBundleShortVersionString = "Second Life version 2.0.2.0";
+CFBundleGetInfoString = "Second Life version 2.0.2.0, Copyright 2004-2009 Linden Research, Inc.";
 
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
index 4cb01a0f338759a56d18093eadfb9a75691592b3..a7241cac89481bb2afff50e052a0b7660cc8a1f2 100644
--- a/indra/newview/Info-SecondLife.plist
+++ b/indra/newview/Info-SecondLife.plist
@@ -32,7 +32,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>2.0.0.203110</string>
+	<string>2.0.2.0</string>
 	<key>CSResourcesFileMapped</key>
 	<true/>
 </dict>
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 266ed2b5fd56c6d7a7eae471121d64ff6fb2bfe2..6cc06bab09fae2162c9920ce810ede2b264d2803 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5585,6 +5585,19 @@
       <key>Value</key>
       <integer>8</integer>
     </map>
+
+   <key>PluginUseReadThread</key>
+    <map>
+      <key>Comment</key>
+      <string>Use a separate thread to read incoming messages from plugins</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
+
     <key>PrecachingDelay</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 2f9bbb14079d0834ddc9253928bbb8668b73c18b..4f7c0c3549b93ed5ae9a6ba629f47d55546b2dd3 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1530,6 +1530,9 @@ bool LLAppViewer::cleanup()
 	
 	LLViewerMedia::saveCookieFile();
 
+	// Stop the plugin read thread if it's running.
+	LLPluginProcessParent::setUseReadThread(false);
+
 	llinfos << "Shutting down Threads" << llendflush;
 
 	// Let threads finish
diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp
index 4f9434030f0a7588b8ab99c64ac990dcd8359173..fafa315a59d7eea7c5ac641de48e42bc0b8d336b 100644
--- a/indra/newview/llchannelmanager.cpp
+++ b/indra/newview/llchannelmanager.cpp
@@ -243,3 +243,19 @@ void LLChannelManager::killToastsFromChannel(const LLUUID& channel_id, const LLS
 	}
 }
 
+// static
+LLNotificationsUI::LLScreenChannel* LLChannelManager::getNotificationScreenChannel()
+{
+	LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*>
+	(LLNotificationsUI::LLChannelManager::getInstance()->
+										findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
+
+	if (channel == NULL)
+	{
+		llwarns << "Can't find screen channel by NotificationChannelUUID" << llendl;
+		llassert(!"Can't find screen channel by NotificationChannelUUID");
+	}
+
+	return channel;
+}
+
diff --git a/indra/newview/llchannelmanager.h b/indra/newview/llchannelmanager.h
index c2be39122f4d106937b718b0f1479d9440af0108..8c725f26609ddce9927043aacc1ae74cce511d87 100644
--- a/indra/newview/llchannelmanager.h
+++ b/indra/newview/llchannelmanager.h
@@ -114,6 +114,11 @@ class LLChannelManager : public LLSingleton<LLChannelManager>
 	 */
 	void killToastsFromChannel(const LLUUID& channel_id, const LLScreenChannel::Matcher& matcher);
 
+	/**
+	 * Returns notification screen channel.
+	 */
+	static LLNotificationsUI::LLScreenChannel* getNotificationScreenChannel();
+
 private:
 
 	LLScreenChannel* createChannel(LLChannelManager::Params& p);
diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp
index e21644e119754f853cd7d875a262bb2bb44efde9..b8222ebb18f0eb548440022bf2f7021d4efc992c 100644
--- a/indra/newview/llcofwearables.cpp
+++ b/indra/newview/llcofwearables.cpp
@@ -89,6 +89,7 @@ void LLCOFWearables::onSelectionChange(LLFlatListView* selected_list)
 	onCommit();
 }
 
+#include "llwearableitemslist.h"
 void LLCOFWearables::refresh()
 {
 	clear();
@@ -117,16 +118,15 @@ void LLCOFWearables::populateAttachmentsAndBodypartsLists(const LLInventoryModel
 
 		const LLAssetType::EType item_type = item->getType();
 		if (item_type == LLAssetType::AT_CLOTHING) continue;
-
-		LLPanelInventoryListItem* item_panel = LLPanelInventoryListItem::createItemPanel(item);
-		if (!item_panel) continue;
-
+		LLPanelInventoryListItemBase* item_panel = NULL;
 		if (item_type == LLAssetType::AT_OBJECT)
 		{
+				item_panel = LLPanelInventoryListItemBase::create(item);
 			mAttachments->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
 		}
 		else if (item_type == LLAssetType::AT_BODYPART)
 		{
+			item_panel = LLPanelBodyPartsListItem::create(item);
 			mBodyParts->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
 			addWearableTypeSeparator(mBodyParts);
 		}
@@ -165,7 +165,7 @@ void LLCOFWearables::populateClothingList(LLAppearanceMgr::wearables_by_type_t&
 		{
 			LLViewerInventoryItem* item = clothing_by_type[type][i];
 
-			LLPanelInventoryListItem* item_panel = LLPanelInventoryListItem::createItemPanel(item);
+			LLPanelInventoryListItemBase* item_panel = LLPanelClothingListItem::create(item);
 			if (!item_panel) continue;
 
 			mClothing->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
@@ -204,10 +204,10 @@ void LLCOFWearables::addClothingTypesDummies(const LLAppearanceMgr::wearables_by
 		U32 size = clothing_by_type[type].size();
 		if (size) continue;
 
-		//*TODO create dummy item panel
-		
-		//*TODO add dummy item panel -> mClothing->addItem(dummy_item_panel, item->getUUID(), ADD_BOTTOM, false);
-
+		EWearableType w_type = static_cast<EWearableType>(type);
+		LLPanelInventoryListItemBase* item_panel = LLPanelDummyClothingListItem::create(w_type);
+		if(!item_panel) continue;
+		mClothing->addItem(item_panel, LLUUID::null, ADD_BOTTOM, false);
 		addWearableTypeSeparator(mClothing);
 	}
 }
diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp
index 19dbc564d17fa61fa1e541fb804f731eac5ea3d2..c0cc3f198594b9232ed2082b89d9910ed2c576cb 100644
--- a/indra/newview/llimfloater.cpp
+++ b/indra/newview/llimfloater.cpp
@@ -53,6 +53,7 @@
 #include "llsyswellwindow.h"
 #include "lltrans.h"
 #include "llchathistory.h"
+#include "llnotifications.h"
 #include "llviewerwindow.h"
 #include "llvoicechannel.h"
 #include "lltransientfloatermgr.h"
@@ -371,6 +372,8 @@ void LLIMFloater::onSlide()
 //static
 LLIMFloater* LLIMFloater::show(const LLUUID& session_id)
 {
+	closeHiddenIMToasts();
+
 	if (!gIMMgr->hasSession(session_id)) return NULL;
 
 	if(!isChatMultiTab())
@@ -1083,6 +1086,26 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info)
 	}
 }
 
+// static
+void LLIMFloater::closeHiddenIMToasts()
+{
+	class IMToastMatcher: public LLNotificationsUI::LLScreenChannel::Matcher
+	{
+	public:
+		bool matches(const LLNotificationPtr notification) const
+		{
+			// "notifytoast" type of notifications is reserved for IM notifications
+			return "notifytoast" == notification->getType();
+		}
+	};
+
+	LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getNotificationScreenChannel();
+	if (channel != NULL)
+	{
+		channel->closeHiddenToasts(IMToastMatcher());
+	}
+}
+
 // static
 bool LLIMFloater::isChatMultiTab()
 {
diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h
index 763dd5655ba9c85e122651ece71327a42de9e687..f9dd8b9b85206d13628b3c5b33b7910ad1b99dd5 100644
--- a/indra/newview/llimfloater.h
+++ b/indra/newview/llimfloater.h
@@ -148,6 +148,8 @@ class LLIMFloater : public LLTransientDockableFloater
 	// Remove the "User is typing..." indicator.
 	void removeTypingIndicator(const LLIMInfo* im_info = NULL);
 
+	static void closeHiddenIMToasts();
+
 	LLPanelChatControlPanel* mControlPanel;
 	LLUUID mSessionID;
 	S32 mLastMessageIndex;
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index c6f806178c6ea66520181a2e4aefeda2062b6b27..2d08c0a01ae40f6788310e57e597e5a9c7c8347a 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -853,21 +853,7 @@ void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
 									 const LLUUID& new_parent_id,
 									 BOOL restamp)
 {
-	if (item->getParentUUID() != new_parent_id)
-	{
-		LLInventoryModel::update_list_t update;
-		LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
-		update.push_back(old_folder);
-		LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
-		update.push_back(new_folder);
-		gInventory.accountForUpdate(update);
-
-		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
-		new_item->setParent(new_parent_id);
-		new_item->updateParentOnServer(restamp);
-		model->updateItem(new_item);
-		model->notifyObservers();
-	}
+	change_item_parent(model, item, new_parent_id, restamp);
 }
 
 // static
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 8487588404c4d5dbe8af6b20c212f5b829b9d164..8010d1f43de2e331bbf272804c52e1ee2ca69b88 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -582,3 +582,26 @@ BOOL get_is_item_worn(const LLUUID& id)
 	}
 	return FALSE;
 }
+
+
+void change_item_parent(LLInventoryModel* model,
+									 LLViewerInventoryItem* item,
+									 const LLUUID& new_parent_id,
+									 BOOL restamp)
+{
+	if (item->getParentUUID() != new_parent_id)
+	{
+		LLInventoryModel::update_list_t update;
+		LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+		update.push_back(old_folder);
+		LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1);
+		update.push_back(new_folder);
+		gInventory.accountForUpdate(update);
+
+		LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+		new_item->setParent(new_parent_id);
+		new_item->updateParentOnServer(restamp);
+		model->updateItem(new_item);
+		model->notifyObservers();
+	}
+}
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index e3cd988e39c07ff35723f8d538830d499a8880b6..6f373f7392551a487b8f9026347842ee063fce15 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -310,6 +310,12 @@ LLUIImagePtr get_item_icon(LLAssetType::EType asset_type,
 // Is this item or its baseitem is worn, attached, etc...
 BOOL get_is_item_worn(const LLUUID& id);
 
+
+void change_item_parent(LLInventoryModel* model,
+									 LLViewerInventoryItem* item,
+									 const LLUUID& new_parent_id,
+									 BOOL restamp);
+
 #endif // LL_LLINVENTORYFUNCTIONS_H
 
 
diff --git a/indra/newview/llinventoryitemslist.cpp b/indra/newview/llinventoryitemslist.cpp
index dca130c672996f010685abca88455f4e60f3bafd..8dfdb0788a263594002a009585b823ebb575ee4b 100644
--- a/indra/newview/llinventoryitemslist.cpp
+++ b/indra/newview/llinventoryitemslist.cpp
@@ -50,76 +50,224 @@
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
-// static
-LLPanelInventoryListItem* LLPanelInventoryListItem::createItemPanel(const LLViewerInventoryItem* item)
+static const S32 WIDGET_SPACING = 3;
+
+LLPanelInventoryListItemBase* LLPanelInventoryListItemBase::create(LLViewerInventoryItem* item)
 {
+	LLPanelInventoryListItemBase* list_item = NULL;
 	if (item)
 	{
-		return new LLPanelInventoryListItem(item);
+		list_item = new LLPanelInventoryListItemBase(item);
+		list_item->init();
 	}
-	else
+	return list_item;
+}
+
+void LLPanelInventoryListItemBase::updateItem()
+{
+	setIconImage(mIconImage);
+	setTitle(mItem->getName(), mHighlightedText);
+}
+
+void LLPanelInventoryListItemBase::addWidgetToLeftSide(const std::string& name, bool show_widget/* = true*/)
+{
+	LLUICtrl* ctrl = findChild<LLUICtrl>(name);
+	if(ctrl)
 	{
-		return NULL;
+		addWidgetToLeftSide(ctrl, show_widget);
 	}
 }
 
-LLPanelInventoryListItem::~LLPanelInventoryListItem()
-{}
+void LLPanelInventoryListItemBase::addWidgetToLeftSide(LLUICtrl* ctrl, bool show_widget/* = true*/)
+{
+	mLeftSideWidgets.push_back(ctrl);
+	setShowWidget(ctrl, show_widget);
+}
+
+void LLPanelInventoryListItemBase::addWidgetToRightSide(const std::string& name, bool show_widget/* = true*/)
+{
+	LLUICtrl* ctrl = findChild<LLUICtrl>(name);
+	if(ctrl)
+	{
+		addWidgetToRightSide(ctrl, show_widget);
+	}
+}
+
+void LLPanelInventoryListItemBase::addWidgetToRightSide(LLUICtrl* ctrl, bool show_widget/* = true*/)
+{
+	mRightSideWidgets.push_back(ctrl);
+	setShowWidget(ctrl, show_widget);
+}
+
+void LLPanelInventoryListItemBase::setShowWidget(const std::string& name, bool show)
+{
+	LLUICtrl* widget = findChild<LLUICtrl>(name);
+	if(widget)
+	{
+		setShowWidget(widget, show);
+	}
+}
+
+void LLPanelInventoryListItemBase::setShowWidget(LLUICtrl* ctrl, bool show)
+{
+	// Enable state determines whether widget may become visible in setWidgetsVisible()
+	ctrl->setEnabled(show);
+}
 
-//virtual
-BOOL LLPanelInventoryListItem::postBuild()
+BOOL LLPanelInventoryListItemBase::postBuild()
 {
-	mIcon = getChild<LLIconCtrl>("item_icon");
-	mTitle = getChild<LLTextBox>("item_name");
+	setIconCtrl(getChild<LLIconCtrl>("item_icon"));
+	setTitleCtrl(getChild<LLTextBox>("item_name"));
+
+	mIconImage = get_item_icon(mItem->getType(), mItem->getInventoryType(), mItem->getFlags(), FALSE);
 
 	updateItem();
 
+	setWidgetsVisible(false);
+	reshapeWidgets();
+
 	return TRUE;
 }
 
-//virtual
-void LLPanelInventoryListItem::setValue(const LLSD& value)
+void LLPanelInventoryListItemBase::setValue(const LLSD& value)
 {
 	if (!value.isMap()) return;
 	if (!value.has("selected")) return;
 	childSetVisible("selected_icon", value["selected"]);
 }
 
-void LLPanelInventoryListItem::updateItem()
+void LLPanelInventoryListItemBase::onMouseEnter(S32 x, S32 y, MASK mask)
 {
-	if (mItemIcon.notNull())
-		mIcon->setImage(mItemIcon);
+	childSetVisible("hovered_icon", true);
+	LLPanel::onMouseEnter(x, y, mask);
+}
 
+void LLPanelInventoryListItemBase::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+	childSetVisible("hovered_icon", false);
+	LLPanel::onMouseLeave(x, y, mask);
+}
+
+LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem* item)
+: LLPanel()
+, mItem(item)
+, mIconCtrl(NULL)
+, mTitleCtrl(NULL)
+, mWidgetSpacing(WIDGET_SPACING)
+, mLeftWidgetsWidth(0)
+, mRightWidgetsWidth(0)
+{
+}
+
+void LLPanelInventoryListItemBase::init()
+{
+	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_inventory_item.xml");
+}
+
+class WidgetVisibilityChanger
+{
+public:
+	WidgetVisibilityChanger(bool visible) : mVisible(visible){}
+	void operator()(LLUICtrl* widget)
+	{
+		// Disabled widgets never become visible. see LLPanelInventoryListItemBase::setShowWidget()
+		widget->setVisible(mVisible && widget->getEnabled());
+	}
+private:
+	bool mVisible;
+};
+
+void LLPanelInventoryListItemBase::setWidgetsVisible(bool visible)
+{
+	std::for_each(mLeftSideWidgets.begin(), mLeftSideWidgets.end(), WidgetVisibilityChanger(visible));
+	std::for_each(mRightSideWidgets.begin(), mRightSideWidgets.end(), WidgetVisibilityChanger(visible));
+}
+
+void LLPanelInventoryListItemBase::reshapeWidgets()
+{
+	// disabled reshape left for now to reserve space for 'delete' button in LLPanelClothingListItem
+	/*reshapeLeftWidgets();*/
+	reshapeRightWidgets();
+	reshapeMiddleWidgets();
+}
+
+void LLPanelInventoryListItemBase::setIconImage(const LLUIImagePtr& image)
+{
+	if(image)
+	{
+		mIconImage = image; 
+		mIconCtrl->setImage(mIconImage);
+	}
+}
+
+void LLPanelInventoryListItemBase::setTitle(const std::string& title, const std::string& highlit_text)
+{
 	LLTextUtil::textboxSetHighlightedVal(
-		mTitle,
+		mTitleCtrl,
 		LLStyle::Params(),
-		mItemName,
-		mHighlightedText);
+		title,
+		highlit_text);
 }
 
-void LLPanelInventoryListItem::onMouseEnter(S32 x, S32 y, MASK mask)
+void LLPanelInventoryListItemBase::reshapeLeftWidgets()
 {
-	childSetVisible("hovered_icon", true);
+	S32 widget_left = 0;
+	mLeftWidgetsWidth = 0;
 
-	LLPanel::onMouseEnter(x, y, mask);
+	widget_array_t::const_iterator it = mLeftSideWidgets.begin();
+	const widget_array_t::const_iterator it_end = mLeftSideWidgets.end();
+	for( ; it_end != it; ++it)
+	{
+		LLUICtrl* widget = *it;
+		if(!widget->getVisible())
+		{
+			continue;
+		}
+		LLRect widget_rect(widget->getRect());
+		widget_rect.setLeftTopAndSize(widget_left, widget_rect.mTop, widget_rect.getWidth(), widget_rect.getHeight());
+		widget->setShape(widget_rect);
+
+		widget_left += widget_rect.getWidth() + getWidgetSpacing();
+		mLeftWidgetsWidth = widget_rect.mRight;
+	}
 }
 
-void LLPanelInventoryListItem::onMouseLeave(S32 x, S32 y, MASK mask)
+void LLPanelInventoryListItemBase::reshapeRightWidgets()
 {
-	childSetVisible("hovered_icon", false);
+	S32 widget_right = getLocalRect().getWidth();
+	S32 widget_left = widget_right;
 
-	LLPanel::onMouseLeave(x, y, mask);
+	widget_array_t::const_reverse_iterator it = mRightSideWidgets.rbegin();
+	const widget_array_t::const_reverse_iterator it_end = mRightSideWidgets.rend();
+	for( ; it_end != it; ++it)
+	{
+		LLUICtrl* widget = *it;
+		if(!widget->getVisible())
+		{
+			continue;
+		}
+		LLRect widget_rect(widget->getRect());
+		widget_left = widget_right - widget_rect.getWidth();
+		widget_rect.setLeftTopAndSize(widget_left, widget_rect.mTop, widget_rect.getWidth(), widget_rect.getHeight());
+		widget->setShape(widget_rect);
+
+		widget_right = widget_left - getWidgetSpacing();
+	}
+	mRightWidgetsWidth = getLocalRect().getWidth() - widget_left;
 }
 
-LLPanelInventoryListItem::LLPanelInventoryListItem(const LLViewerInventoryItem* item)
-:	 LLPanel()
-	,mIcon(NULL)
-	,mTitle(NULL)
+void LLPanelInventoryListItemBase::reshapeMiddleWidgets()
 {
-	mItemName = item->getName();
-	mItemIcon = get_item_icon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE);
-
-	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_inventory_item.xml");
+	LLRect icon_rect(mIconCtrl->getRect());
+	icon_rect.setLeftTopAndSize(mLeftWidgetsWidth + getWidgetSpacing(), icon_rect.mTop, 
+		icon_rect.getWidth(), icon_rect.getHeight());
+	mIconCtrl->setShape(icon_rect);
+
+	S32 name_left = icon_rect.mRight + getWidgetSpacing();
+	S32 name_right = getLocalRect().getWidth() - mRightWidgetsWidth - getWidgetSpacing();
+	LLRect name_rect(mTitleCtrl->getRect());
+	name_rect.set(name_left, name_rect.mTop, name_right, name_rect.mBottom);
+	mTitleCtrl->setShape(name_rect);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -220,17 +368,18 @@ void LLInventoryItemsList::addNewItem(LLViewerInventoryItem* item)
 	if (!item)
 	{
 		llwarns << "No inventory item. Couldn't create flat list item." << llendl;
-		llassert(!"No inventory item. Couldn't create flat list item.");
+		llassert(item != NULL);
 	}
 
-	LLPanelInventoryListItem *list_item = LLPanelInventoryListItem::createItemPanel(item);
+	LLPanelInventoryListItemBase *list_item = LLPanelInventoryListItemBase::create(item);
 	if (!list_item)
 		return;
 
-	if (!addItem(list_item, item->getUUID()))
+	bool is_item_added = addItem(list_item, item->getUUID());
+	if (!is_item_added)
 	{
 		llwarns << "Couldn't add flat list item." << llendl;
-		llassert(!"Couldn't add flat list item.");
+		llassert(is_item_added);
 	}
 }
 
diff --git a/indra/newview/llinventoryitemslist.h b/indra/newview/llinventoryitemslist.h
index b496f4b9e94c74a24e16997154fa31bd010f4616..152aafbd7e0b896192ac57f2c0e756b900141857 100644
--- a/indra/newview/llinventoryitemslist.h
+++ b/indra/newview/llinventoryitemslist.h
@@ -47,33 +47,141 @@ class LLIconCtrl;
 class LLTextBox;
 class LLViewerInventoryItem;
 
-class LLPanelInventoryListItem : public LLPanel
+/**
+ * @class LLPanelInventoryListItemBase
+ *
+ * Base class for Inventory flat list item. Panel consists of inventory icon
+ * and inventory item name.
+ * This class is able to display widgets(buttons) on left(before icon) and right(after text-box) sides 
+ * of panel.
+ *
+ * How to use (see LLPanelClothingListItem for example):
+ * - implement init() to build panel from xml
+ * - create new xml file, fill it with widgets you want to dynamically show/hide/reshape on left/right sides
+ * - redefine postBuild()(call base implementation) and add needed widgets to needed sides,
+ *
+ */
+class LLPanelInventoryListItemBase : public LLPanel
 {
 public:
-	static LLPanelInventoryListItem* createItemPanel(const LLViewerInventoryItem* item);
 
-	virtual ~LLPanelInventoryListItem();
+	static LLPanelInventoryListItemBase* create(LLViewerInventoryItem* item);
 
+	/**
+	 * Called after inventory item was updated, update panel widgets to reflect inventory changes.
+	 */
+	virtual void updateItem();
+
+	/**
+	 * Add widget to left side
+	 */
+	void addWidgetToLeftSide(const std::string& name, bool show_widget = true);
+	void addWidgetToLeftSide(LLUICtrl* ctrl, bool show_widget = true);
+
+	/**
+	 * Add widget to right side, widget is supposed to be child of calling panel
+	 */
+	void addWidgetToRightSide(const std::string& name, bool show_widget = true);
+	void addWidgetToRightSide(LLUICtrl* ctrl, bool show_widget = true);
+
+	/**
+	 * Mark widgets as visible. Only visible widgets take part in reshaping children
+	 */
+	void setShowWidget(const std::string& name, bool show);
+	void setShowWidget(LLUICtrl* ctrl, bool show);
+
+	/**
+	 * Set spacing between widgets during reshape
+	 */
+	void setWidgetSpacing(S32 spacing) { mWidgetSpacing = spacing; }
+
+	S32 getWidgetSpacing() { return mWidgetSpacing; }
+
+	/**
+	 * Inheritors need to call base implementation of postBuild()
+	 */
 	/*virtual*/ BOOL postBuild();
+
+	/**
+	 * Handles item selection
+	 */
 	/*virtual*/ void setValue(const LLSD& value);
 
-	void updateItem();
+	 /* Highlights item */
+	/*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
+	/* Removes item highlight */
+	/*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
 
-	void onMouseEnter(S32 x, S32 y, MASK mask);
-	void onMouseLeave(S32 x, S32 y, MASK mask);
+	virtual ~LLPanelInventoryListItemBase(){}
 
 protected:
-	LLPanelInventoryListItem(const LLViewerInventoryItem* item);
+
+	LLPanelInventoryListItemBase(LLViewerInventoryItem* item);
+
+	typedef std::vector<LLUICtrl*> widget_array_t;
+
+	/**
+	 * Use it from a factory function to build panel, do not build panel in constructor
+	 */
+	virtual void init();
+
+	/** setter for mIconCtrl */
+	void setIconCtrl(LLIconCtrl* icon) { mIconCtrl = icon; }
+	/** setter for MTitleCtrl */
+	void setTitleCtrl(LLTextBox* tb) { mTitleCtrl = tb; }
+
+	void setLeftWidgetsWidth(S32 width) { mLeftWidgetsWidth = width; }
+	void setRightWidgetsWidth(S32 width) { mRightWidgetsWidth = width; }
+
+	/**
+	 * Set all widgets from both side visible/invisible. Only enabled widgets
+	 * (see setShowWidget()) can become visible
+	 */
+	virtual void setWidgetsVisible(bool visible);
+
+	/**
+	 * Reshape all child widgets - icon, text-box and side widgets
+	 */
+	virtual void reshapeWidgets();
+
+	/** set wearable type icon image */
+	void setIconImage(const LLUIImagePtr& image);
+
+	/** Set item title - inventory item name usually */
+	void setTitle(const std::string& title, const std::string& highlit_text);
 
 private:
-	LLIconCtrl*		mIcon;
-	LLTextBox*		mTitle;
 
-	LLUIImagePtr	mItemIcon;
-	std::string		mItemName;
+	/** reshape left side widgets
+	 * Deprecated for now. Disabled reshape left for now to reserve space for 'delete' 
+	 * button in LLPanelClothingListItem according to Neal's comment (https://codereview.productengine.com/secondlife/r/325/)
+	 */
+	void reshapeLeftWidgets();
+
+	/** reshape right side widgets */
+	void reshapeRightWidgets();
+
+	/** reshape remaining widgets */
+	void reshapeMiddleWidgets();
+
+	LLViewerInventoryItem* mItem;
+
+	LLIconCtrl*		mIconCtrl;
+	LLTextBox*		mTitleCtrl;
+
+	LLUIImagePtr	mIconImage;
 	std::string		mHighlightedText;
+
+	widget_array_t	mLeftSideWidgets;
+	widget_array_t	mRightSideWidgets;
+	S32				mWidgetSpacing;
+
+	S32				mLeftWidgetsWidth;
+	S32				mRightWidgetsWidth;
 };
 
+//////////////////////////////////////////////////////////////////////////
+
 class LLInventoryItemsList : public LLFlatListView
 {
 public:
@@ -117,7 +225,7 @@ class LLInventoryItemsList : public LLFlatListView
 	/**
 	 * Add an item to the list
 	 */
-	void addNewItem(LLViewerInventoryItem* item);
+	virtual void addNewItem(LLViewerInventoryItem* item);
 
 private:
 	uuid_vec_t mIDs; // IDs of items that were added in refreshList().
diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp
index 214b5d317a530f6773fe545693ed972d136d7b91..86147d65e6b4f86c5e96dd5adf48fe124d3e26f5 100644
--- a/indra/newview/llinventoryobserver.cpp
+++ b/indra/newview/llinventoryobserver.cpp
@@ -215,7 +215,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask)
 
 void fetch_items_from_llsd(const LLSD& items_llsd)
 {
-	if (!items_llsd.size()) return;
+	if (!items_llsd.size() || gDisconnected) return;
 	LLSD body;
 	body[0]["cap_name"] = "FetchInventory";
 	body[1]["cap_name"] = "FetchLib";
@@ -235,6 +235,11 @@ void fetch_items_from_llsd(const LLSD& items_llsd)
 		
 	for (S32 i=0; i<body.size(); i++)
 	{
+		if(!gAgent.getRegion())
+		{
+			llwarns<<"Agent's region is null"<<llendl;
+			break;
+		}
 		if (0 >= body[i].size()) continue;
 		std::string url = gAgent.getRegion()->getCapability(body[i]["cap_name"].asString());
 
@@ -664,36 +669,87 @@ void LLInventoryCategoriesObserver::changed(U32 mask)
 		if (!category)
 			continue;
 
-		S32 version = category->getVersion();
-		if (version != (*iter).second.mVersion)
+		const S32 version = category->getVersion();
+		const S32 expected_num_descendents = category->getDescendentCount();
+		if ((version == LLViewerInventoryCategory::VERSION_UNKNOWN) ||
+			(expected_num_descendents == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN))
 		{
-			// Update category version in map.
-			(*iter).second.mVersion = version;
-			(*iter).second.mCallback();
+			continue;
+		}
+
+		// Check number of known descendents to find out whether it has changed.
+		LLInventoryModel::cat_array_t* cats;
+		LLInventoryModel::item_array_t* items;
+		gInventory.getDirectDescendentsOf((*iter).first, cats, items);
+		if (!cats || !items)
+		{
+			llwarns << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << llendl;
+			// NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean
+			// that the cat just doesn't have any items or subfolders).
+			// Unrecoverable, so just skip this category.
+
+			llassert(cats != NULL && items != NULL);
+		}
+		const S32 current_num_known_descendents = cats->count() + items->count();
+
+		LLCategoryData cat_data = (*iter).second;
+
+		// If category version or descendents count has changed
+		// update category data in mCategoryMap and fire a callback.
+		if (version != cat_data.mVersion || current_num_known_descendents != cat_data.mDescendentsCount)
+		{
+			cat_data.mVersion = version;
+			cat_data.mDescendentsCount = current_num_known_descendents;
+
+			cat_data.mCallback();
 		}
 	}
 }
 
-void LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t cb)
+bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t cb)
 {
 	S32 version;
+	S32 current_num_known_descendents;
+	bool can_be_added = true;
+
 	LLViewerInventoryCategory* category = gInventory.getCategory(cat_id);
 	if (category)
 	{
 		// Inventory category version is used to find out if some changes
 		// to a category have been made.
 		version = category->getVersion();
+
+		LLInventoryModel::cat_array_t* cats;
+		LLInventoryModel::item_array_t* items;
+		gInventory.getDirectDescendentsOf(cat_id, cats, items);
+		if (!cats || !items)
+		{
+			llwarns << "Category '" << category->getName() << "' descendents corrupted, fetch failed." << llendl;
+			// NULL means the call failed -- cats/items map doesn't exist (note: this does NOT mean
+			// that the cat just doesn't have any items or subfolders).
+			// Unrecoverable, so just return "false" meaning that the category can't be observed.
+			can_be_added = false;
+
+			llassert(cats != NULL && items != NULL);
+		}
+		current_num_known_descendents = cats->count() + items->count();
 	}
 	else
 	{
 		// If category could not be retrieved it might mean that
 		// inventory is unusable at the moment so the category is
-		// stored with VERSION_UNKNOWN and it may be updated later.
+		// stored with VERSION_UNKNOWN and DESCENDENT_COUNT_UNKNOWN,
+		// it may be updated later.
 		version = LLViewerInventoryCategory::VERSION_UNKNOWN;
+		current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN;
+	}
+
+	if (can_be_added)
+	{
+		mCategoryMap.insert(category_map_value_t(cat_id, LLCategoryData(cb, version, current_num_known_descendents)));
 	}
 
-	version = category->getVersion();
-	mCategoryMap.insert(category_map_value_t(cat_id, LLCategoryData(cb, version)));
+	return can_be_added;
 }
 
 void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id)
diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h
index e63b67d2adcba09b55ff21e314aeab3e369286cd..036e6ca40da2d46a297acdee707f447212ea4e9f 100644
--- a/indra/newview/llinventoryobserver.h
+++ b/indra/newview/llinventoryobserver.h
@@ -276,19 +276,28 @@ class LLInventoryCategoriesObserver : public LLInventoryObserver
 	LLInventoryCategoriesObserver() {};
 	virtual void changed(U32 mask);
 
-	void addCategory(const LLUUID& cat_id, callback_t cb);
+	/**
+	 * Add cat_id to the list of observed categories with a
+	 * callback fired on category being changed.
+	 *
+	 * @return "true" if category was added, "false" if it could
+	 * not be found.
+	 */
+	bool addCategory(const LLUUID& cat_id, callback_t cb);
 	void removeCategory(const LLUUID& cat_id);
 
 protected:
 	struct LLCategoryData
 	{
-		LLCategoryData(callback_t cb, S32 version)
+		LLCategoryData(callback_t cb, S32 version, S32 num_descendents)
 		: mCallback(cb)
 		, mVersion(version)
+		, mDescendentsCount(num_descendents)
 		{}
 
 		callback_t	mCallback;
 		S32			mVersion;
+		S32			mDescendentsCount;
 	};
 
 	typedef	std::map<LLUUID, LLCategoryData>	category_map_t;
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 12152726850c0cf081609a2c867bc09e96bd3678..b103ec45d0d7df380ee5ed194667dab805a3b4d6 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -41,6 +41,7 @@
 
 #include "llaccordionctrl.h"
 #include "llaccordionctrltab.h"
+#include "llappearancemgr.h"
 #include "llinventoryfunctions.h"
 #include "llinventorymodel.h"
 #include "llwearableitemslist.h"
@@ -51,6 +52,7 @@ LLOutfitsList::LLOutfitsList()
 	:	LLPanel()
 	,	mAccordion(NULL)
 	,	mListCommands(NULL)
+	,	mSelectedList(NULL)
 {
 	mCategoriesObserver = new LLInventoryCategoriesObserver();
 	gInventory.addObserver(mCategoriesObserver);
@@ -135,30 +137,37 @@ void LLOutfitsList::refreshList(const LLUUID& category_id)
 	{
 		const LLUUID cat_id = (*iter);
 		LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
-		if (!cat)
-			continue;
+		if (!cat) continue;
 
 		std::string name = cat->getName();
 
 		static LLXMLNodePtr accordionXmlNode = getAccordionTabXMLNode();
-
-		accordionXmlNode->setAttributeString("name", name);
-		accordionXmlNode->setAttributeString("title", name);
 		LLAccordionCtrlTab* tab = LLUICtrlFactory::defaultBuilder<LLAccordionCtrlTab>(accordionXmlNode, NULL, NULL);
 
+		tab->setName(name);
+		tab->setTitle(name);
+
 		// *TODO: LLUICtrlFactory::defaultBuilder does not use "display_children" from xml. Should be investigated.
 		tab->setDisplayChildren(false);
 		mAccordion->addCollapsibleCtrl(tab);
 
+		// Start observing the new outfit category.
+		LLWearableItemsList* list  = tab->getChild<LLWearableItemsList>("wearable_items_list");
+		if (!mCategoriesObserver->addCategory(cat_id, boost::bind(&LLWearableItemsList::updateList, list, cat_id)))
+		{
+			// Remove accordion tab if category could not be added to observer.
+			mAccordion->removeCollapsibleCtrl(tab);
+			continue;
+		}
+
 		// Map the new tab with outfit category UUID.
 		mOutfitsMap.insert(LLOutfitsList::outfits_map_value_t(cat_id, tab));
 
-		// Start observing the new outfit category.
-		LLWearableItemsList* list  = tab->getChild<LLWearableItemsList>("wearable_items_list");
-		mCategoriesObserver->addCategory(cat_id, boost::bind(&LLWearableItemsList::updateList, list, cat_id));
+		// Setting tab focus callback to monitor currently selected outfit.
+		tab->setFocusReceivedCallback(boost::bind(&LLOutfitsList::changeOutfitSelection, this, list, cat_id));
 
-		// Setting drop down callback to monitor currently selected outfit.
-		tab->setDropDownStateChangedCallback(boost::bind(&LLOutfitsList::onTabExpandedCollapsed, this, list));
+		// Setting list commit callback to monitor currently selected wearable item.
+		list->setCommitCallback(boost::bind(&LLOutfitsList::onSelectionChange, this, _1));
 
 		// Fetch the new outfit contents.
 		cat->fetch();
@@ -178,10 +187,18 @@ void LLOutfitsList::refreshList(const LLUUID& category_id)
 			// 1. Remove outfit accordion tab from accordion.
 			mAccordion->removeCollapsibleCtrl(outfits_iter->second);
 
+			const LLUUID& outfit_id = outfits_iter->first;
+
 			// 2. Remove outfit category from observer to stop monitoring its changes.
-			mCategoriesObserver->removeCategory(outfits_iter->first);
+			mCategoriesObserver->removeCategory(outfit_id);
 
-			// 3. Remove category UUID to accordion tab mapping.
+			// 3. Reset selection if selected outfit is being removed.
+			if (mSelectedOutfitUUID == outfit_id)
+			{
+				changeOutfitSelection(NULL, LLUUID());
+			}
+
+			// 4. Remove category UUID to accordion tab mapping.
 			mOutfitsMap.erase(outfits_iter);
 		}
 	}
@@ -199,40 +216,30 @@ void LLOutfitsList::refreshList(const LLUUID& category_id)
 	mAccordion->arrange();
 }
 
-void LLOutfitsList::updateOutfitTab(const LLUUID& category_id)
+void LLOutfitsList::onSelectionChange(LLUICtrl* ctrl)
 {
-	outfits_map_t::iterator outfits_iter = mOutfitsMap.find(category_id);
-	if (outfits_iter != mOutfitsMap.end())
-	{
-		LLViewerInventoryCategory *cat = gInventory.getCategory(category_id);
-		if (!cat)
-			return;
+	LLWearableItemsList* list = dynamic_cast<LLWearableItemsList*>(ctrl);
+	if (!list) return;
 
-		std::string name = cat->getName();
+	LLViewerInventoryItem *item = gInventory.getItem(list->getSelectedUUID());
+	if (!item) return;
 
-		// Update tab name with the new category name.
-		LLAccordionCtrlTab* tab = outfits_iter->second;
-		if (tab)
-		{
-			tab->setName(name);
-		}
-
-		// Update tab title with the new category name using textbox
-		// in accordion tab header.
-		LLTextBox* tab_title = tab->findChild<LLTextBox>("dd_textbox");
-		if (tab_title)
-		{
-			tab_title->setText(name);
-		}
-	}
+	changeOutfitSelection(list, item->getParentUUID());
 }
 
-void LLOutfitsList::onTabExpandedCollapsed(LLWearableItemsList* list)
+void LLOutfitsList::performAction(std::string action)
 {
-	if (!list)
-		return;
+	LLViewerInventoryCategory* cat = gInventory.getCategory(mSelectedOutfitUUID);
+	if (!cat) return;
 
-	// TODO: Add outfit selection handling.
+	if ("replaceoutfit" == action)
+	{
+		LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, FALSE );
+	}
+	else if ("addtooutfit" == action)
+	{
+		LLAppearanceMgr::instance().wearInventoryCategory( cat, FALSE, TRUE );
+	}
 }
 
 void LLOutfitsList::setFilterSubString(const std::string& string)
@@ -240,7 +247,6 @@ void LLOutfitsList::setFilterSubString(const std::string& string)
 	mFilterSubString = string;
 }
 
-
 //////////////////////////////////////////////////////////////////////////
 // Private methods
 //////////////////////////////////////////////////////////////////////////
@@ -283,4 +289,37 @@ void LLOutfitsList::computeDifference(
 	LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved);
 }
 
+void LLOutfitsList::updateOutfitTab(const LLUUID& category_id)
+{
+	outfits_map_t::iterator outfits_iter = mOutfitsMap.find(category_id);
+	if (outfits_iter != mOutfitsMap.end())
+	{
+		LLViewerInventoryCategory *cat = gInventory.getCategory(category_id);
+		if (!cat) return;
+
+		std::string name = cat->getName();
+
+		// Update tab name with the new category name.
+		LLAccordionCtrlTab* tab = outfits_iter->second;
+		if (tab)
+		{
+			tab->setName(name);
+			tab->setTitle(name);
+		}
+	}
+}
+
+void LLOutfitsList::changeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id)
+{
+	// Reset selection in previously selected tab
+	// if a new one is selected.
+	if (list && mSelectedList && mSelectedList != list)
+	{
+		mSelectedList->resetSelection();
+	}
+
+	mSelectedList = list;
+	mSelectedOutfitUUID = category_id;
+}
+
 // EOF
diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h
index 2d103ea35658a495e4aafce5e16421e6b78ae502..d86cf5a70330e01a6cd93f0507b4d8b4679c4785 100644
--- a/indra/newview/lloutfitslist.h
+++ b/indra/newview/lloutfitslist.h
@@ -65,10 +65,9 @@ class LLOutfitsList : public LLPanel, public LLInventoryObserver
 
 	void refreshList(const LLUUID& category_id);
 
-	// Update tab displaying outfit identified by category_id.
-	void updateOutfitTab(const LLUUID& category_id);
+	void onSelectionChange(LLUICtrl* ctrl);
 
-	void onTabExpandedCollapsed(LLWearableItemsList* list);
+	void performAction(std::string action);
 
 	void setFilterSubString(const std::string& string);
 
@@ -85,12 +84,24 @@ class LLOutfitsList : public LLPanel, public LLInventoryObserver
 	 */
 	void computeDifference(const LLInventoryModel::cat_array_t& vcats, uuid_vec_t& vadded, uuid_vec_t& vremoved);
 
+	/**
+	 * Updates tab displaying outfit identified by category_id.
+	 */
+	void updateOutfitTab(const LLUUID& category_id);
+
+	/**
+	 * Resets previous selection and stores newly selected list and outfit id.
+	 */
+	void changeOutfitSelection(LLWearableItemsList* list, const LLUUID& category_id);
 
 	LLInventoryCategoriesObserver* 	mCategoriesObserver;
 
 	LLAccordionCtrl*				mAccordion;
 	LLPanel*						mListCommands;
 
+	LLWearableItemsList*			mSelectedList;
+	LLUUID							mSelectedOutfitUUID;
+
 	std::string 					mFilterSubString;
 
 	typedef	std::map<LLUUID, LLAccordionCtrlTab*>		outfits_map_t;
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index 789e85b46f14a557c0a7fd7db1b6b61daf2cadff..80964938f5f13c513b26e0f6027f69a2e4d42fb4 100644
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -188,19 +188,37 @@ void LLPanelOutfitsInventory::onSearchEdit(const std::string& string)
 
 void LLPanelOutfitsInventory::onWearButtonClick()
 {
-	LLFolderViewEventListener* listenerp = getCorrectListenerForAction();
-	if (listenerp)
+	// TODO: Remove if/else, add common interface
+	// for "My Outfits" and "Wearing" tabs.
+	if (!isCOFPanelActive())
+	{
+		mMyOutfitsPanel->performAction("replaceoutfit");
+	}
+	else
 	{
-		listenerp->performAction(NULL, "replaceoutfit");
+		LLFolderViewEventListener* listenerp = getCorrectListenerForAction();
+		if (listenerp)
+		{
+			listenerp->performAction(NULL, "replaceoutfit");
+		}
 	}
 }
 
 void LLPanelOutfitsInventory::onAdd()
 {
-	LLFolderViewEventListener* listenerp = getCorrectListenerForAction();
-	if (listenerp)
+	// TODO: Remove if/else, add common interface
+	// for "My Outfits" and "Wearing" tabs.
+	if (!isCOFPanelActive())
+	{
+		mMyOutfitsPanel->performAction("addtooutfit");
+	}
+	else
 	{
-		listenerp->performAction(NULL, "addtooutfit");
+		LLFolderViewEventListener* listenerp = getCorrectListenerForAction();
+		if (listenerp)
+		{
+			listenerp->performAction(NULL, "addtooutfit");
+		}
 	}
 }
 
diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp
index 75702dc8e52c1bf454e41729739316b55d8d41d5..fb7ac0d86b4208ee5cd07cce41c8088523483925 100644
--- a/indra/newview/llpreviewnotecard.cpp
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -35,6 +35,7 @@
 #include "llpreviewnotecard.h"
 
 #include "llinventory.h"
+#include "llinventoryfunctions.h" // for change_item_parent()
 
 #include "llagent.h"
 #include "llassetuploadresponders.h"
@@ -92,11 +93,17 @@ BOOL LLPreviewNotecard::postBuild()
 	childSetAction("Save", onClickSave, this);
 	childSetVisible("lock", FALSE);	
 
+	childSetAction("Delete", onClickDelete, this);
+	childSetEnabled("Delete", false);
+
 	const LLInventoryItem* item = getItem();
 
 	childSetCommitCallback("desc", LLPreview::onText, this);
 	if (item)
+	{
 		childSetText("desc", item->getDescription());
+		childSetEnabled("Delete", true);
+	}
 	childSetPrevalidate("desc", &LLTextValidate::validateASCIIPrintableNoPipe);
 
 	return LLPreview::postBuild();
@@ -374,6 +381,17 @@ void LLPreviewNotecard::onClickSave(void* user_data)
 	}
 }
 
+
+// static
+void LLPreviewNotecard::onClickDelete(void* user_data)
+{
+	LLPreviewNotecard* preview = (LLPreviewNotecard*)user_data;
+	if(preview)
+	{
+		preview->deleteNotecard();
+	}
+}
+
 struct LLSaveNotecardInfo
 {
 	LLPreviewNotecard* mSelf;
@@ -466,6 +484,18 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem)
 	return true;
 }
 
+void LLPreviewNotecard::deleteNotecard()
+{
+	LLViewerInventoryItem* item = gInventory.getItem(mItemUUID);
+	if (item != NULL)
+	{
+		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+		change_item_parent(&gInventory, item, trash_id, FALSE);
+	}
+
+	closeFloater();
+}
+
 // static
 void LLPreviewNotecard::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status, LLExtStat ext_status) // StoreAssetData callback (fixed)
 {
diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h
index e0363eef548f8f9c79070744282dadaa75776f7b..98de99aa33ab0473cf4d64dfdfc4db3f9b77e026 100644
--- a/indra/newview/llpreviewnotecard.h
+++ b/indra/newview/llpreviewnotecard.h
@@ -83,6 +83,8 @@ class LLPreviewNotecard : public LLPreview
 	virtual void loadAsset();
 	bool saveIfNeeded(LLInventoryItem* copyitem = NULL);
 
+	void deleteNotecard();
+
 	static void onLoadComplete(LLVFS *vfs,
 							   const LLUUID& asset_uuid,
 							   LLAssetType::EType type,
@@ -90,6 +92,8 @@ class LLPreviewNotecard : public LLPreview
 
 	static void onClickSave(void* data);
 
+	static void onClickDelete(void* data);
+
 	static void onSaveComplete(const LLUUID& asset_uuid,
 							   void* user_data,
 							   S32 status, LLExtStat ext_status);
diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp
index af440a36891800ec427227a44c140c0937cb7cff..de1da248c12ca34935d498469e5d29a641039481 100644
--- a/indra/newview/llscreenchannel.cpp
+++ b/indra/newview/llscreenchannel.cpp
@@ -706,6 +706,31 @@ void LLScreenChannel::hideToast(const LLUUID& notification_id)
 	}
 }
 
+void LLScreenChannel::closeHiddenToasts(const Matcher& matcher)
+{
+	// since we can't guarantee that close toast operation doesn't change mToastList
+	// we collect matched toasts that should be closed into separate list
+	std::list<ToastElem> toasts;
+	for (std::vector<ToastElem>::iterator it = mToastList.begin(); it
+			!= mToastList.end(); it++)
+	{
+		LLToast * toast = it->toast;
+		// add to list valid toast that match to provided matcher criteria
+		if (toast != NULL && !toast->isDead() && toast->getNotification() != NULL
+				&& !toast->getVisible() && matcher.matches(toast->getNotification()))
+		{
+			toasts.push_back(*it);
+		}
+	}
+
+	// close collected toasts
+	for (std::list<ToastElem>::iterator it = toasts.begin(); it
+			!= toasts.end(); it++)
+	{
+		it->toast->closeFloater();
+	}
+}
+
 //--------------------------------------------------------------------------
 void LLScreenChannel::removeToastsFromChannel()
 {
diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h
index 88053d87d9dedacad96f6cb5a71c55cc4f1e0184..46c5fed7b69c7b2cac93560a57ac2a8b33171d60 100644
--- a/indra/newview/llscreenchannel.h
+++ b/indra/newview/llscreenchannel.h
@@ -173,6 +173,12 @@ class LLScreenChannel : public LLScreenChannelBase
 	void		hideToastsFromScreen();
 	// hide toast by notification id
 	void		hideToast(const LLUUID& notification_id);
+
+	/**
+	 * Closes hidden matched toasts from channel.
+	 */
+	void closeHiddenToasts(const Matcher& matcher);
+
 	// removes all toasts from a channel
 	void		removeToastsFromChannel();
 	// show all toasts in a channel
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 076a9609e329ab344106ab819700f19f83ecdccf..c6f8ce2e158a5b0f5bd9dc3ae2ea5fd2e7cd940a 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -733,10 +733,17 @@ static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMedi
 	}
 }
 
+static LLFastTimer::DeclareTimer FTM_MEDIA_UPDATE("Update Media");
+
 //////////////////////////////////////////////////////////////////////////////////////////
 // static
 void LLViewerMedia::updateMedia(void *dummy_arg)
 {
+	LLFastTimer t1(FTM_MEDIA_UPDATE);
+	
+	// Enable/disable the plugin read thread
+	LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread"));
+	
 	sAnyMediaShowing = false;
 	sUpdatedCookies = getCookieStore()->getChangedCookies();
 	if(!sUpdatedCookies.empty())
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index 3d110dcc786c9b456a049d6c7243f4a1b84a646a..56b279199334d4eb5c09f74a2417ad6d5b4971f9 100644
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -33,8 +33,11 @@
 
 #include "llwearableitemslist.h"
 
+#include "lliconctrl.h"
+
 #include "llinventoryfunctions.h"
 #include "llinventorymodel.h"
+#include "lltransutil.h"
 
 class LLFindOutfitItems : public LLInventoryCollectFunctor
 {
@@ -60,6 +63,239 @@ bool LLFindOutfitItems::operator()(LLInventoryCategory* cat,
 	return FALSE;
 }
 
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+void LLPanelWearableListItem::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+	LLPanelInventoryListItemBase::onMouseEnter(x, y, mask);
+	setWidgetsVisible(true);
+	reshapeWidgets();
+}
+
+void LLPanelWearableListItem::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+	LLPanelInventoryListItemBase::onMouseLeave(x, y, mask);
+	setWidgetsVisible(false);
+	reshapeWidgets();
+}
+
+LLPanelWearableListItem::LLPanelWearableListItem(LLViewerInventoryItem* item)
+: LLPanelInventoryListItemBase(item)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+// static
+LLPanelClothingListItem* LLPanelClothingListItem::create(LLViewerInventoryItem* item)
+{
+	LLPanelClothingListItem* list_item = NULL;
+	if(item)
+	{
+		list_item = new LLPanelClothingListItem(item);
+		list_item->init();
+	}
+	return list_item;
+}
+
+LLPanelClothingListItem::LLPanelClothingListItem(LLViewerInventoryItem* item)
+ : LLPanelWearableListItem(item)
+{
+}
+
+LLPanelClothingListItem::~LLPanelClothingListItem()
+{
+}
+
+void LLPanelClothingListItem::init()
+{
+	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_clothing_list_item.xml");
+}
+
+BOOL LLPanelClothingListItem::postBuild()
+{
+	LLPanelInventoryListItemBase::postBuild();
+
+	addWidgetToLeftSide("btn_delete");
+	addWidgetToRightSide("btn_move_up");
+	addWidgetToRightSide("btn_move_down");
+	addWidgetToRightSide("btn_lock");
+	addWidgetToRightSide("btn_edit");
+
+	LLButton* delete_btn = getChild<LLButton>("btn_delete");
+	// Reserve space for 'delete' button event if it is invisible.
+	setLeftWidgetsWidth(delete_btn->getRect().mRight);
+
+	setWidgetsVisible(false);
+	reshapeWidgets();
+
+	return TRUE;
+}
+
+void LLPanelClothingListItem::setShowDeleteButton(bool show)
+{
+	setShowWidget("btn_delete", show);
+}
+
+void LLPanelClothingListItem::setShowMoveUpButton(bool show)
+{
+	setShowWidget("btn_move_up", show);
+}
+
+void LLPanelClothingListItem::setShowMoveDownButton(bool show)
+{
+	setShowWidget("btn_move_down", show);
+}
+
+void LLPanelClothingListItem::setShowLockButton(bool show)
+{
+	setShowWidget("btn_lock", show);
+}
+
+void LLPanelClothingListItem::setShowEditButton(bool show)
+{
+	setShowWidget("btn_edit", show);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+// static
+LLPanelBodyPartsListItem* LLPanelBodyPartsListItem::create(LLViewerInventoryItem* item)
+{
+	LLPanelBodyPartsListItem* list_item = NULL;
+	if(item)
+	{
+		list_item = new LLPanelBodyPartsListItem(item);
+		list_item->init();
+	}
+	return list_item;
+}
+
+LLPanelBodyPartsListItem::LLPanelBodyPartsListItem(LLViewerInventoryItem* item)
+: LLPanelWearableListItem(item)
+{
+}
+
+LLPanelBodyPartsListItem::~LLPanelBodyPartsListItem()
+{
+}
+
+void LLPanelBodyPartsListItem::init()
+{
+	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_body_parts_list_item.xml");
+}
+
+BOOL LLPanelBodyPartsListItem::postBuild()
+{
+	LLPanelInventoryListItemBase::postBuild();
+
+	addWidgetToRightSide("btn_lock");
+	addWidgetToRightSide("btn_edit");
+
+	return TRUE;
+}
+
+void LLPanelBodyPartsListItem::setShowLockButton(bool show)
+{
+	setShowWidget("btn_lock", show);
+}
+
+void LLPanelBodyPartsListItem::setShowEditButton(bool show)
+{
+	setShowWidget("btn_edit", show);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+LLPanelDummyClothingListItem* LLPanelDummyClothingListItem::create(EWearableType w_type)
+{
+	LLPanelDummyClothingListItem* list_item = new LLPanelDummyClothingListItem(w_type);
+	list_item->init();
+	return list_item;
+}
+
+void LLPanelDummyClothingListItem::updateItem()
+{
+	std::string title = wearableTypeToString(mWearableType);
+	setTitle(title, LLStringUtil::null);
+}
+
+BOOL LLPanelDummyClothingListItem::postBuild()
+{
+	LLIconCtrl* icon = getChild<LLIconCtrl>("item_icon");
+	setIconCtrl(icon);
+	setTitleCtrl(getChild<LLTextBox>("item_name"));
+
+	addWidgetToRightSide("btn_add");
+
+	setIconImage(get_item_icon(LLAssetType::AT_CLOTHING, LLInventoryType::IT_NONE, mWearableType, FALSE));
+	updateItem();
+
+	// Make it look loke clothing item - reserve space for 'delete' button
+	setLeftWidgetsWidth(icon->getRect().mLeft);
+
+	setWidgetsVisible(false);
+	reshapeWidgets();
+
+	return TRUE;
+}
+
+LLPanelDummyClothingListItem::LLPanelDummyClothingListItem(EWearableType w_type)
+ : LLPanelWearableListItem(NULL)
+ , mWearableType(w_type)
+{
+}
+
+void LLPanelDummyClothingListItem::init()
+{
+	LLUICtrlFactory::getInstance()->buildPanel(this, "panel_dummy_clothing_list_item.xml");
+}
+
+typedef std::map<EWearableType, std::string> clothing_to_string_map_t;
+
+clothing_to_string_map_t init_clothing_string_map()
+{
+	clothing_to_string_map_t w_map;
+	w_map.insert(std::make_pair(WT_SHIRT, "shirt_not_worn"));
+	w_map.insert(std::make_pair(WT_PANTS, "pants_not_worn"));
+	w_map.insert(std::make_pair(WT_SHOES, "shoes_not_worn"));
+	w_map.insert(std::make_pair(WT_SOCKS, "socks_not_worn"));
+	w_map.insert(std::make_pair(WT_JACKET, "jacket_not_worn"));
+	w_map.insert(std::make_pair(WT_GLOVES, "gloves_not_worn"));
+	w_map.insert(std::make_pair(WT_UNDERSHIRT, "undershirt_not_worn"));
+	w_map.insert(std::make_pair(WT_UNDERPANTS, "underpants_not_worn"));
+	w_map.insert(std::make_pair(WT_SKIRT, "skirt_not_worn"));
+	w_map.insert(std::make_pair(WT_ALPHA, "alpha_not_worn"));
+	w_map.insert(std::make_pair(WT_TATTOO, "tattoo_not_worn"));
+	return w_map;
+}
+
+std::string LLPanelDummyClothingListItem::wearableTypeToString(EWearableType w_type)
+{
+	static const clothing_to_string_map_t w_map = init_clothing_string_map();
+	static const std::string invalid_str = LLTrans::getString("invalid_not_worn");
+	
+	std::string type_str = invalid_str;
+	clothing_to_string_map_t::const_iterator it = w_map.find(w_type);
+	if(w_map.end() != it)
+	{
+		type_str = LLTrans::getString(it->second);
+	}
+	return type_str;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
 static const LLDefaultChildRegistry::Register<LLWearableItemsList> r("wearable_items_list");
 
 LLWearableItemsList::Params::Params()
@@ -89,3 +325,5 @@ void LLWearableItemsList::updateList(const LLUUID& category_id)
 
 	refreshList(item_array);
 }
+
+// EOF
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index e7ccba8e6c151b1ee64f8adebcfce0fe94341b2a..c4a415dfbf3b39fcaeebe16b2fa64ac345db0951 100644
--- a/indra/newview/llwearableitemslist.h
+++ b/indra/newview/llwearableitemslist.h
@@ -36,6 +36,115 @@
 
 // newview
 #include "llinventoryitemslist.h"
+#include "llinventorymodel.h"
+#include "llwearabledictionary.h"
+
+/**
+ * @class LLPanelWearableListItem
+ *
+ * Extends LLPanelInventoryListItemBase:
+ * - makes side widgets show on mouse_enter and hide on 
+ *   mouse_leave events.
+ * - provides callback for button clicks
+ */
+class LLPanelWearableListItem : public LLPanelInventoryListItemBase
+{
+	LOG_CLASS(LLPanelWearableListItem);
+public:
+
+	/**
+	* Shows buttons when mouse is over
+	*/
+	/*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask);
+
+	/**
+	* Hides buttons when mouse is out
+	*/
+	/*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask);
+
+protected:
+
+	LLPanelWearableListItem(LLViewerInventoryItem* item);
+};
+
+/**
+ * @class LLPanelClothingListItem
+ *
+ * Provides buttons for editing, moving, deleting a wearable.
+ */
+class LLPanelClothingListItem : public LLPanelWearableListItem
+{
+	LOG_CLASS(LLPanelClothingListItem);
+public:
+
+	static LLPanelClothingListItem* create(LLViewerInventoryItem* item);
+
+	virtual ~LLPanelClothingListItem();
+
+	/*virtual*/ BOOL postBuild();
+
+	/**
+	 * Make button visible during mouse over event.
+	 */
+	inline void setShowDeleteButton(bool show);
+	inline void setShowMoveUpButton(bool show);
+	inline void setShowMoveDownButton(bool show);
+	inline void setShowLockButton(bool show);
+	inline void setShowEditButton(bool show);
+
+protected:
+
+	LLPanelClothingListItem(LLViewerInventoryItem* item);
+	
+	/*virtual*/ void init();
+};
+
+class LLPanelBodyPartsListItem : public LLPanelWearableListItem
+{
+	LOG_CLASS(LLPanelBodyPartsListItem);
+public:
+
+	static LLPanelBodyPartsListItem* create(LLViewerInventoryItem* item);
+
+	virtual ~LLPanelBodyPartsListItem();
+
+	/*virtual*/ BOOL postBuild();
+
+	/**
+	* Make button visible during mouse over event.
+	*/
+	inline void setShowLockButton(bool show);
+	inline void setShowEditButton(bool show);
+
+protected:
+	LLPanelBodyPartsListItem(LLViewerInventoryItem* item);
+
+	/*virtual*/ void init();
+};
+
+/**
+ * @class LLPanelDummyClothingListItem
+ *
+ * A dummy item panel - displays grayed clothing icon, grayed title '<clothing> not worn' and 'add' button
+ */
+class LLPanelDummyClothingListItem : public LLPanelWearableListItem
+{
+public:
+	static LLPanelDummyClothingListItem* create(EWearableType w_type);
+
+	/*virtual*/ void updateItem();
+	/*virtual*/ BOOL postBuild();
+
+protected:
+	LLPanelDummyClothingListItem(EWearableType w_type);
+
+	/*virtual*/ void init();
+
+	static std::string wearableTypeToString(EWearableType w_type);
+
+private:
+	EWearableType mWearableType;
+};
 
 /**
  * @class LLWearableItemsList
diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc
index 12a09392f6eae4435982fb9471e5a030ddfd3de4..731953f9bbdf3912a51a595f7d979c134ee2707e 100644
--- a/indra/newview/res/viewerRes.rc
+++ b/indra/newview/res/viewerRes.rc
@@ -129,8 +129,8 @@ TOOLSIT                 CURSOR                  "toolsit.cur"
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,0,0,203110
- PRODUCTVERSION 2,0,0,203110
+ FILEVERSION 2,0,2,0
+ PRODUCTVERSION 2,0,2,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -147,12 +147,12 @@ BEGIN
         BEGIN
             VALUE "CompanyName", "Linden Lab"
             VALUE "FileDescription", "Second Life"
-            VALUE "FileVersion", "2.0.0.203110"
+            VALUE "FileVersion", "2.0.2.0"
             VALUE "InternalName", "Second Life"
             VALUE "LegalCopyright", "Copyright © 2001-2008, Linden Research, Inc."
             VALUE "OriginalFilename", "SecondLife.exe"
             VALUE "ProductName", "Second Life"
-            VALUE "ProductVersion", "2.0.0.203110"
+            VALUE "ProductVersion", "2.0.2.0"
         END
     END
     BLOCK "VarFileInfo"
diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml
index 14c0081c0df57675b6064f5f9af6ffd9db37fdf0..0e8eef2a21b80018df7aa20e6746c3a96523fb07 100644
--- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml
+++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml
@@ -84,8 +84,18 @@
      label="Save"
      label_selected="Save"
      layout="topleft"
-     left="288"
+     left="178"
      name="Save"
      top="332"
      width="100" />
+    <button
+     follows="right|bottom"
+     height="22"
+     label="Delete"
+     label_selected="Delete"
+     layout="topleft"
+     left="288"
+     name="Delete"
+     top="332"
+     width="100" />
 </floater>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 3af80f63fe28fefc468079fde7fa695a1114703c..df4f33adf048d48f74cc62f81b14f28e9d0af4f3 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -1339,6 +1339,16 @@
              function="ToggleControl"
              parameter="RunMultipleThreads" />
         </menu_item_check>
+        <menu_item_check
+         label="Use Plugin Read Thread"
+         name="Use Plugin Read Thread">
+            <menu_item_check.on_check
+             function="CheckControl"
+             parameter="PluginUseReadThread" />
+            <menu_item_check.on_click
+             function="ToggleControl"
+             parameter="PluginUseReadThread" />
+        </menu_item_check>
         <menu_item_call
          label="Clear Group Cache"
          name="ClearGroupCache">
diff --git a/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4313d450fbf7ea7ea723a7b13da9655e3c2dbf88
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_body_parts_list_item.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ follows="top|right|left"
+ height="20"
+ layout="topleft"
+ left="0"
+ name="wearable_item"
+ top="0"
+ width="380">
+    <icon
+     follows="top|right|left"
+     height="20"
+     image_name="ListItem_Over"
+     layout="topleft"
+     left="0"
+     name="hovered_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     height="20"
+     follows="top|right|left"
+     image_name="ListItem_Select"
+     layout="topleft"
+     left="0"
+     name="selected_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     height="16"
+     follows="top|left"
+     image_name="Inv_Object"
+     layout="topleft"
+     left="0"
+     name="item_icon"
+     top="2"
+     width="16" />
+    <text
+     follows="left|right"
+     height="16"
+     layout="topleft"
+     left_pad="5"
+     allow_html="false"
+     use_ellipses="true"
+     name="item_name"
+     text_color="white"
+     top="4"
+     value="..."
+     width="359" />
+    <icon 
+     name="btn_lock"
+     layout="topleft"
+     follows="top|right"
+     image_name="Lock2"
+     top="0"
+     left="0"
+     height="20"
+     width="20"
+     tab_stop="false" />
+    <button 
+     name="btn_edit"
+     layout="topleft"
+     follows="top|right"
+     image_unselected="Icon_Gear_Background"
+     image_selected="Icon_Gear_Background"
+     top="0"
+     left_pad="3"
+     height="20"
+     width="20"
+     tab_stop="false" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml b/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8dc67de06f484a43ff3f3818cd931723683b72ae
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_clothing_list_item.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ follows="top|right|left"
+ height="20"
+ layout="topleft"
+ left="0"
+ name="wearable_item"
+ top="0"
+ width="380">
+    <icon
+     follows="top|right|left"
+     height="20"
+     image_name="ListItem_Over"
+     layout="topleft"
+     left="0"
+     name="hovered_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     height="20"
+     follows="top|right|left"
+     image_name="ListItem_Select"
+     layout="topleft"
+     left="0"
+     name="selected_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <button 
+     name="btn_delete"
+     layout="topleft"
+     follows="top|left"
+     image_unselected="Toast_CloseBtn"
+     image_selected="Toast_CloseBtn"
+     top="0"
+     left="0"
+     height="20"
+     width="20"
+     tab_stop="false" />
+    <icon
+     height="16"
+     follows="top|left"
+     image_name="Inv_Object"
+     layout="topleft"
+     left_pad="3"
+     name="item_icon"
+     top="2"
+     width="16" />
+    <text
+     follows="left|right"
+     height="16"
+     layout="topleft"
+     left_pad="5"
+     allow_html="false"
+     use_ellipses="true"
+     name="item_name"
+     text_color="white"
+     top="4"
+     value="..."
+     width="359" />
+    <button 
+     name="btn_move_up"
+     layout="topleft"
+     follows="top|right"
+     image_unselected="Movement_Up_Off"
+     image_selected="Movement_Up_Off"
+     top="0"
+     left="0"
+     height="20"
+     width="20"
+     tab_stop="false" />
+    <button 
+     name="btn_move_down"
+     layout="topleft"
+     follows="top|right"
+     image_unselected="Movement_Down_Off"
+     image_selected="Movement_Down_Off"
+     top="0"
+     left_pad="3"
+     height="20"
+     width="20"
+     tab_stop="false" />
+    <icon
+     name="btn_lock"
+     layout="topleft"
+     follows="top|right"
+     image_name="Lock2"
+     top="0"
+     left_pad="3"
+     height="20"
+     width="20" />
+    <button 
+     name="btn_edit"
+     layout="topleft"
+     follows="top|right"
+     image_unselected="Icon_Gear_Background"
+     image_selected="Icon_Gear_Background"
+     top="0"
+     left_pad="3"
+     height="20"
+     width="20"
+     tab_stop="false" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml b/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dbbfa8f2e2433eccb61d76d606f15e7825ad4450
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_dummy_clothing_list_item.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ follows="top|right|left"
+ height="20"
+ layout="topleft"
+ left="0"
+ name="dummy_clothing_item"
+ top="0"
+ width="380">
+    <icon
+     follows="top|right|left"
+     height="20"
+     image_name="ListItem_Over"
+     layout="topleft"
+     left="0"
+     name="hovered_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     height="20"
+     follows="top|right|left"
+     image_name="ListItem_Select"
+     layout="topleft"
+     left="0"
+     name="selected_icon"
+     top="0"
+     visible="false"
+     width="380" />
+    <icon
+     height="16"
+     color="0.75 0.75 0.75 1"
+     follows="top|left"
+     image_name="Inv_Object"
+     layout="topleft"
+     left="20"
+     name="item_icon"
+     top="2"
+     width="16" />
+    <text
+     follows="left|right"
+     height="16"
+     layout="topleft"
+     left_pad="5"
+     allow_html="false"
+     use_ellipses="true"
+     name="item_name"
+     text_color="LtGray_50"
+     top="4"
+     value="..."
+     width="359" />
+    <button 
+     name="btn_add"
+     layout="topleft"
+     follows="top|right"
+     label="+"
+     top="0"
+     left="0"
+     height="20"
+     width="20"
+     tab_stop="false" />
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
index 73181392c9d7cf960b627ac4dc3a8c1a65491ca8..a9f588698ade69843dc8a65f8fd680912053c750 100644
--- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
@@ -234,6 +234,14 @@
                  name="move_further_btn"
                  top="1"
                  width="31" />
+                <icon
+                 follows="bottom|left"
+                 height="25"
+                 image_name="Toolbar_Middle_Off"
+                 layout="topleft"
+                 left_pad="1"
+                 name="dummy_icon"
+                 width="105" />
                 <button
                  follows="bottom|right"
                  height="25"
@@ -440,6 +448,22 @@
                  name="add_to_outfit_btn"
                  top="1"
                  width="31" />
+                <icon
+                 follows="bottom|left"
+                 height="25"
+                 image_name="Toolbar_Middle_Off"
+                 layout="topleft"
+                 left_pad="1"
+                 name="dummy_middle_icon"
+                 width="140" />
+                <icon
+                 follows="bottom|left"
+                 height="25"
+                 image_name="Toolbar_Right_Off"
+                 layout="topleft"
+                 left_pad="1"
+                 name="dummy_right_icon"
+                 width="31" />
             </panel>
         </layout_panel>
     </layout_stack>
diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml
index 066ea3be6e2a75aa8cb7edd94b18fab814d7069a..7e212c938395a9f7adc6453616a3af0a0f17f40a 100644
--- a/indra/newview/skins/default/xui/en/panel_people.xml
+++ b/indra/newview/skins/default/xui/en/panel_people.xml
@@ -490,6 +490,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M
          label="Share"
          layout="topleft"
          name="share_btn"
+         tool_tip="Share an inventory item"
          width="62" />
         <button
          follows="bottom|left"
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml
index 3ef16d2dec4ba515e1cb013056d8808a48e9cd9a..ba967d3e2c0961f2bb21b9c8c2c688a99dfbad16 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml
@@ -330,7 +330,7 @@
     <check_box
      enabled="false"
      height="16"
-     label="Enable plain text chat history"
+     label="Enable plain text IM and chat history"
      layout="topleft"
      left_delta="0"
      name="plain_text_chat_history"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 3cba76cbfa0f70188d7c999960b3572ef5ad1df1..e0d58a16c812d88e8fbf1346a222768658bd5ee1 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -1802,6 +1802,20 @@ Clears (deletes) the media and all params from the given face.
 	<string name="alpha">Alpha</string>
 	<string name="tattoo">Tattoo</string>
 	<string name="invalid">invalid</string>
+  
+  <!-- Not Worn Wearable Types -->
+	<string name="shirt_not_worn">Shirt not worn</string>
+	<string name="pants_not_worn">Pants not worn</string>
+	<string name="shoes_not_worn">Shoes not worn</string>
+	<string name="socks_not_worn">Socks not worn</string>
+	<string name="jacket_not_worn">Jacket not worn</string>
+	<string name="gloves_not_worn">Gloves not worn</string>
+	<string name="undershirt_not_worn">Undershirt not worn</string>
+	<string name="underpants_not_worn">Underpants not worn</string>
+	<string name="skirt_not_worn">Skirt not worn</string>
+	<string name="alpha_not_worn">Alpha not worn</string>
+	<string name="tattoo_not_worn">Tattoo not worn</string>
+	<string name="invalid_not_worn">invalid</string>
 
   <!-- Wearable List-->
   <string name="NewWearable">New [WEARABLE_ITEM]</string>
diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp
index 7e9a8336e77c6f0972cea5f6ee88d7f72c4720ee..7a544debb2b94cd811804e61250d0f05db125383 100644
--- a/indra/test_apps/llplugintest/llmediaplugintest.cpp
+++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp
@@ -241,6 +241,9 @@ LLMediaPluginTest::~LLMediaPluginTest()
 	{
 		remMediaPanel( mMediaPanels[ i ] );
 	};
+	
+	// Stop the plugin read thread if it's running.
+	LLPluginProcessParent::setUseReadThread(false);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1047,6 +1050,11 @@ void LLMediaPluginTest::gluiCallback( int control_id )
 		}
 	}
 	else
+	if ( control_id == mIdUsePluginReadThread )
+	{
+		LLPluginProcessParent::setUseReadThread(mUsePluginReadThread);
+	}
+	else
 	if ( control_id == mIdControlCrashPlugin )
 	{
 		// send message to plugin and ask it to crash
@@ -1431,6 +1439,12 @@ void LLMediaPluginTest::makeChrome()
 	glui_window_misc_control->set_main_gfx_window( mAppWindow );
 	glui_window_misc_control->add_column( true );
 
+	mIdUsePluginReadThread = start_id++;
+	mUsePluginReadThread = 0;
+	glui_window_misc_control->add_checkbox( "Use plugin read thread", &mUsePluginReadThread, mIdUsePluginReadThread, gluiCallbackWrapper );
+	glui_window_misc_control->set_main_gfx_window( mAppWindow );
+	glui_window_misc_control->add_column( true );
+
 	mIdLargePanelSpacing = start_id++;
 	mLargePanelSpacing = 0;
 	glui_window_misc_control->add_checkbox( "Large Panel Spacing", &mLargePanelSpacing, mIdLargePanelSpacing, gluiCallbackWrapper );
diff --git a/indra/test_apps/llplugintest/llmediaplugintest.h b/indra/test_apps/llplugintest/llmediaplugintest.h
index e7c769934303651d63a97382bed79c6a6ee83f37..5d08e4214891dc8e6bb88cb9ad924f7ad8269b46 100644
--- a/indra/test_apps/llplugintest/llmediaplugintest.h
+++ b/indra/test_apps/llplugintest/llmediaplugintest.h
@@ -164,6 +164,8 @@ class LLMediaPluginTest : public LLPluginClassMediaOwner
 		int mRandomBookmarks;
 		int mIdDisableTimeout;
 		int mDisableTimeout;
+		int mIdUsePluginReadThread;
+		int mUsePluginReadThread;
 		int mIdLargePanelSpacing;
 		int mLargePanelSpacing;
 		int mIdControlCrashPlugin;