diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 251df03a4a9616147fdceabed6d05925d83d64fd..b0211dd85b6dbe12c23a6760a38c7ea84839738b 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11417,5 +11417,16 @@
       <key>Value</key>
       <integer>1</integer>
     </map>
+    <key>OutfitOperationsTimeout</key>
+    <map>
+      <key>Comment</key>
+      <string>Timeout for outfit related operations.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>180</integer>
+    </map>
 </map>
 </llsd>
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index d823a3cbbb61b1682ba72225bfee14121a3ea45f..cfa0a4d7294a632bdf58e461193f90d6b301f664 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -64,6 +64,25 @@ BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE;
 
 using namespace LLVOAvatarDefines;
 
+///////////////////////////////////////////////////////////////////////////////
+
+// Callback to wear and start editing an item that has just been created.
+class LLWearAndEditCallback : public LLInventoryCallback
+{
+	void fire(const LLUUID& inv_item)
+	{
+		if (inv_item.isNull()) return;
+
+		// Request editing the item after it gets worn.
+		gAgentWearables.requestEditingWearable(inv_item);
+
+		// Wear it.
+		LLAppearanceMgr::instance().wearItemOnAvatar(inv_item);
+	}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 // HACK: For EXT-3923: Pants item shows in inventory with skin icon and messes with "current look"
 // Some db items are corrupted, have inventory flags = 0, implying wearable type = shape, even though
 // wearable type stored in asset is some other value.
@@ -1990,7 +2009,7 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con
 	LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
 	LLAssetType::EType asset_type = wearable->getAssetType();
 	LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE;
-	LLPointer<LLInventoryCallback> cb = wear ? new WearOnAvatarCallback : NULL;
+	LLPointer<LLInventoryCallback> cb = wear ? new LLWearAndEditCallback : NULL;
 	LLUUID folder_id;
 
 	if (parent_id.notNull())
@@ -2013,17 +2032,44 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con
 // static
 void LLAgentWearables::editWearable(const LLUUID& item_id)
 {
-	LLViewerInventoryItem* item;
-	LLWearable* wearable;
+	LLViewerInventoryItem* item = gInventory.getLinkedItem(item_id);
+	if (!item)
+	{
+		llwarns << "Failed to get linked item" << llendl;
+		return;
+	}
+
+	LLWearable* wearable = gAgentWearables.getWearableFromItemID(item_id);
+	if (!wearable)
+	{
+		llwarns << "Cannot get wearable" << llendl;
+		return;
+	}
+
+	if (!gAgentWearables.isWearableModifiable(item->getUUID()))
+	{
+		llwarns << "Cannot modify wearable" << llendl;
+		return;
+	}
+
+	LLPanel* panel = LLSideTray::getInstance()->getPanel("sidepanel_appearance");
+	LLSidepanelAppearance::editWearable(wearable, panel);
+}
+
+// Request editing the item after it gets worn.
+void LLAgentWearables::requestEditingWearable(const LLUUID& item_id)
+{
+	mItemToEdit = gInventory.getLinkedItemID(item_id);
+}
 
-	if ((item = gInventory.getLinkedItem(item_id)) &&
-		(wearable = gAgentWearables.getWearableFromAssetID(item->getAssetUUID())) &&
-		gAgentWearables.isWearableModifiable(item->getUUID()) &&
-		item->isFinished())
+// Start editing the item if previously requested.
+void LLAgentWearables::editWearableIfRequested(const LLUUID& item_id)
+{
+	if (mItemToEdit.notNull() &&
+		mItemToEdit == gInventory.getLinkedItemID(item_id))
 	{
-		LLPanel* panel = LLSideTray::getInstance()->showPanel("panel_outfit_edit", LLSD());
-		// copied from LLPanelOutfitEdit::onEditWearableClicked()
-		LLSidepanelAppearance::editWearable(wearable, panel->getParent());
+		LLAgentWearables::editWearable(item_id);
+		mItemToEdit.setNull();
 	}
 }
 
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 679ecefa6fa76553e157a415f402c826b5d0ed1c..a41b949be62217edadfa94aee851d1754d3211e0 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -148,6 +148,12 @@ class LLAgentWearables
 	static void		editWearable(const LLUUID& item_id);
 	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
 
+	void			requestEditingWearable(const LLUUID& item_id);
+	void			editWearableIfRequested(const LLUUID& item_id);
+
+private:
+	LLUUID			mItemToEdit;
+
 	//--------------------------------------------------------------------
 	// Removing wearables
 	//--------------------------------------------------------------------
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index a899926938ce2266c87f7d2843c55ee7f2bcb658..135629bb8df18bf945becf1768cae4ea593f2156 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -38,11 +38,13 @@
 #include "llagentwearables.h"
 #include "llappearancemgr.h"
 #include "llcommandhandler.h"
+#include "lleventtimer.h"
 #include "llgesturemgr.h"
 #include "llinventorybridge.h"
 #include "llinventoryfunctions.h"
 #include "llinventoryobserver.h"
 #include "llnotificationsutil.h"
+#include "lloutfitobserver.h"
 #include "llpaneloutfitsinventory.h"
 #include "llselectmgr.h"
 #include "llsidepanelappearance.h"
@@ -55,6 +57,34 @@
 
 char ORDER_NUMBER_SEPARATOR('@');
 
+class LLOutfitUnLockTimer: public LLEventTimer
+{
+public:
+	LLOutfitUnLockTimer(F32 period) : LLEventTimer(period)
+	{
+		// restart timer on BOF changed event
+		LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(
+				&LLOutfitUnLockTimer::reset, this));
+		stop();
+	}
+
+	/*virtual*/
+	BOOL tick()
+	{
+		if(mEventTimer.hasExpired())
+		{
+			LLAppearanceMgr::instance().setOutfitLocked(false);
+		}
+		return FALSE;
+	}
+	void stop() { mEventTimer.stop(); }
+	void start() { mEventTimer.start(); }
+	void reset() { mEventTimer.reset(); }
+	BOOL getStarted() { return mEventTimer.getStarted(); }
+
+	LLTimer&  getEventTimer() { return mEventTimer;}
+};
+
 // support for secondlife:///app/appearance SLapps
 class LLAppearanceHandler : public LLCommandHandler
 {
@@ -762,6 +792,27 @@ void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& respo
 	}
 }
 
+void LLAppearanceMgr::setOutfitLocked(bool locked)
+{
+	if (mOutfitLocked == locked)
+	{
+		return;
+	}
+
+	mOutfitLocked = locked;
+	if (locked)
+	{
+		mUnlockOutfitTimer->reset();
+		mUnlockOutfitTimer->start();
+	}
+	else
+	{
+		mUnlockOutfitTimer->stop();
+	}
+
+	LLOutfitObserver::instance().notifyOutfitLockChanged();
+}
+
 void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id)
 {
 	LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
@@ -1810,6 +1861,14 @@ void LLAppearanceMgr::onFirstFullyVisible()
 
 bool LLAppearanceMgr::updateBaseOutfit()
 {
+	if (isOutfitLocked())
+	{
+		// don't allow modify locked outfit
+		llassert(!isOutfitLocked());
+		return false;
+	}
+	setOutfitLocked(true);
+
 	const LLUUID base_outfit_id = getBaseOutfitUUID();
 	if (base_outfit_id.isNull()) return false;
 
@@ -2138,6 +2197,14 @@ LLAppearanceMgr::LLAppearanceMgr():
 	mAttachmentInvLinkEnabled(false),
 	mOutfitIsDirty(false)
 {
+	LLOutfitObserver& outfit_observer = LLOutfitObserver::instance();
+
+	// unlock outfit on save operation completed
+	outfit_observer.addCOFSavedCallback(boost::bind(
+			&LLAppearanceMgr::setOutfitLocked, this, false));
+
+	mUnlockOutfitTimer.reset(new LLOutfitUnLockTimer(gSavedSettings.getS32(
+			"OutfitOperationsTimeout")));
 }
 
 LLAppearanceMgr::~LLAppearanceMgr()
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index f1beef5857fd732fb5dab31f0c03cf725e0c440d..2227a43cd88d4a6ef8890e2665217b06a89faf4c 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -42,10 +42,12 @@
 class LLWearable;
 class LLWearableHoldingPattern;
 class LLInventoryCallback;
+class LLOutfitUnLockTimer;
 
 class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 {
 	friend class LLSingleton<LLAppearanceMgr>;
+	friend class LLOutfitUnLockTimer;
 	
 public:
 	typedef std::vector<LLInventoryModel::item_array_t> wearables_by_type_t;
@@ -162,6 +164,8 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 	// COF is processed if cat_id is not specified
 	void updateClothingOrderingInfo(LLUUID cat_id = LLUUID::null);
 
+	bool isOutfitLocked() { return mOutfitLocked; }
+
 protected:
 	LLAppearanceMgr();
 	~LLAppearanceMgr();
@@ -186,10 +190,20 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 
 	static void onOutfitRename(const LLSD& notification, const LLSD& response);
 
+	void setOutfitLocked(bool locked);
+
 	std::set<LLUUID> mRegisteredAttachments;
 	bool mAttachmentInvLinkEnabled;
 	bool mOutfitIsDirty;
 
+	/**
+	 * Lock for blocking operations on outfit until server reply or timeout exceed
+	 * to avoid unsynchronized outfit state or performing duplicate operations.
+	 */
+	bool mOutfitLocked;
+
+	std::auto_ptr<LLOutfitUnLockTimer> mUnlockOutfitTimer;
+
 	//////////////////////////////////////////////////////////////////////////////////
 	// Item-specific convenience functions 
 public:
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 6fc5804a488ef225951daf46faf6023b36f6918f..15760bf15545777ad6d29cb50c6efc26a0a0e64e 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -721,8 +721,20 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
 			// Valid UUID; set the item UUID and rename it
 			new_item->setCreator(id);
 			std::string avatar_name;
-			// Fetch the currect name
-			gCacheName->get(id, FALSE, boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(), _1, _2, _3));
+
+			if (gCacheName->getFullName(id, avatar_name))
+			{
+				new_item->rename(avatar_name);
+				mask |= LLInventoryObserver::LABEL;
+			}
+			else
+			{
+				// Fetch the current name
+				gCacheName->get(id, FALSE,
+					boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(),
+					_1, _2, _3));
+			}
+
 		}
 	}
 	else if (new_item->getType() == LLAssetType::AT_GESTURE)
diff --git a/indra/newview/lloutfitobserver.cpp b/indra/newview/lloutfitobserver.cpp
index 848b59561322b7c42e4d7a5ddea5fac3c076096f..5652a98981f5772f04a64705ad378d2f0fd3c309 100644
--- a/indra/newview/lloutfitobserver.cpp
+++ b/indra/newview/lloutfitobserver.cpp
@@ -56,9 +56,9 @@ void LLOutfitObserver::changed(U32 mask)
 	if (!gInventory.isInventoryUsable())
 		return;
 
-	bool panel_updated = checkCOF();
+	bool COF_changed = checkCOF();
 
-	if (!panel_updated)
+	if (!COF_changed)
 	{
 		checkBaseOutfit();
 	}
@@ -87,6 +87,7 @@ bool LLOutfitObserver::checkCOF()
 
 	mCOFLastVersion = cof_version;
 
+	// dirtiness state should be updated before sending signal
 	LLAppearanceMgr::getInstance()->updateIsDirty();
 	mCOFChanged();
 
@@ -120,6 +121,16 @@ void LLOutfitObserver::checkBaseOutfit()
 	}
 
 	LLAppearanceMgr& app_mgr = LLAppearanceMgr::instance();
+	// dirtiness state should be updated before sending signal
 	app_mgr.updateIsDirty();
 	mBOFChanged();
+
+	if (mLastOutfitDirtiness != app_mgr.isOutfitDirty())
+	{
+		if(!app_mgr.isOutfitDirty())
+		{
+			mCOFSaved();
+		}
+		mLastOutfitDirtiness = app_mgr.isOutfitDirty();
+	}
 }
diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h
index 4cb40ead15544a6ad771306ceb5428c94035494b..a4b5fbe04acf1c66d0122959dc0d11056f0619ba 100644
--- a/indra/newview/lloutfitobserver.h
+++ b/indra/newview/lloutfitobserver.h
@@ -48,6 +48,8 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO
 
 	virtual void changed(U32 mask);
 
+	void notifyOutfitLockChanged() { mOutfitLockChanged();  }
+
 	typedef boost::signals2::signal<void (void)> signal_t;
 
 	void addBOFReplacedCallback(const signal_t::slot_type& cb) { mBOFReplaced.connect(cb); }
@@ -56,6 +58,10 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO
 
 	void addCOFChangedCallback(const signal_t::slot_type& cb) { mCOFChanged.connect(cb); }
 
+	void addCOFSavedCallback(const signal_t::slot_type& cb) { mCOFSaved.connect(cb); }
+
+	void addOutfitLockChangedCallback(const signal_t::slot_type& cb) { mOutfitLockChanged.connect(cb); }
+
 protected:
 	LLOutfitObserver();
 
@@ -73,10 +79,18 @@ class LLOutfitObserver: public LLInventoryObserver, public LLSingleton<LLOutfitO
 
 	S32 mBaseOutfitLastVersion;
 
+	bool mLastOutfitDirtiness;
+
 private:
 	signal_t mBOFReplaced;
 	signal_t mBOFChanged;
 	signal_t mCOFChanged;
+	signal_t mCOFSaved;
+
+	/**
+	 * Signal for changing state of outfit lock.
+	 */
+	signal_t mOutfitLockChanged;
 };
 
 #endif /* LL_OUTFITOBSERVER_H */
diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp
index ce382541c671335d3627523a0112237058f8d928..e07d5c064b6b9b0881736dad096acddc95c2e7e8 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -198,6 +198,7 @@ LLPanelOutfitEdit::LLPanelOutfitEdit()
 	LLOutfitObserver& observer = LLOutfitObserver::instance();
 	observer.addBOFReplacedCallback(boost::bind(&LLPanelOutfitEdit::updateCurrentOutfitName, this));
 	observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this));
+	observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitEdit::updateVerbs, this));
 	observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitEdit::update, this));
 	
 	mLookItemTypes.reserve(NUM_LOOK_ITEM_TYPES);
@@ -494,35 +495,10 @@ void LLPanelOutfitEdit::onRemoveFromOutfitClicked(void)
 
 void LLPanelOutfitEdit::onEditWearableClicked(void)
 {
-	LLUUID id_to_edit = mCOFWearables->getSelectedUUID();
-	LLViewerInventoryItem * item_to_edit = gInventory.getItem(id_to_edit);
-
-	if (item_to_edit)
+	LLUUID selected_item_id = mCOFWearables->getSelectedUUID();
+	if (selected_item_id.notNull())
 	{
-		// returns null if not a wearable (attachment, etc).
-		LLWearable* wearable_to_edit = gAgentWearables.getWearableFromAssetID(item_to_edit->getAssetUUID());
-		if(wearable_to_edit)
-		{
-			bool can_modify = false;
-			bool is_complete = item_to_edit->isFinished();
-			// if item_to_edit is a link, its properties are not appropriate, 
-			// lets get original item with actual properties
-			LLViewerInventoryItem* original_item = gInventory.getItem(wearable_to_edit->getItemID());
-			if(original_item)
-			{
-				can_modify = original_item->getPermissions().allowModifyBy(gAgentID);
-				is_complete = original_item->isFinished();
-			}
-
-			if (can_modify && is_complete)
-			{											 
-				LLSidepanelAppearance::editWearable(wearable_to_edit, getParent());
-				if (mEditWearableBtn->getVisible())
-				{
-					mEditWearableBtn->setVisible(FALSE);
-				}
-			}
-		}
+		gAgentWearables.editWearable(selected_item_id);
 	}
 }
 
@@ -666,12 +642,13 @@ void LLPanelOutfitEdit::updateCurrentOutfitName()
 void LLPanelOutfitEdit::updateVerbs()
 {
 	bool outfit_is_dirty = LLAppearanceMgr::getInstance()->isOutfitDirty();
+	bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked();
 	bool has_baseoutfit = LLAppearanceMgr::getInstance()->getBaseOutfitUUID().notNull();
 
-	mSaveComboBtn->setSaveBtnEnabled(outfit_is_dirty);
+	mSaveComboBtn->setSaveBtnEnabled(!outfit_locked && outfit_is_dirty);
 	childSetEnabled(REVERT_BTN, outfit_is_dirty && has_baseoutfit);
 
-	mSaveComboBtn->setMenuItemEnabled("save_outfit", outfit_is_dirty);
+	mSaveComboBtn->setMenuItemEnabled("save_outfit", !outfit_locked && outfit_is_dirty);
 
 	mStatus->setText(outfit_is_dirty ? getString("unsaved_changes") : getString("now_editing"));
 
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index 8836672f91e39d3e0f496ae682c7769d2e77568d..8b451c156c3da71f20ddff0235be301dcc1ec1a5 100644
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -209,6 +209,7 @@ LLPanelOutfitsInventory::LLPanelOutfitsInventory() :
 	LLOutfitObserver& observer = LLOutfitObserver::instance();
 	observer.addBOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this));
 	observer.addCOFChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this));
+	observer.addOutfitLockChangedCallback(boost::bind(&LLPanelOutfitsInventory::updateVerbs, this));
 }
 
 LLPanelOutfitsInventory::~LLPanelOutfitsInventory()
@@ -522,7 +523,7 @@ void LLPanelOutfitsInventory::updateListCommands()
 {
 	bool trash_enabled = isActionEnabled("delete");
 	bool wear_enabled = isActionEnabled("wear");
-	bool make_outfit_enabled = isActionEnabled("make_outfit");
+	bool make_outfit_enabled = isActionEnabled("save_outfit");
 
 	mListCommands->childSetEnabled("trash_btn", trash_enabled);
 	mListCommands->childSetEnabled("wear_btn", wear_enabled);
@@ -668,9 +669,12 @@ BOOL LLPanelOutfitsInventory::isActionEnabled(const LLSD& userdata)
 			return FALSE;
 		}
 	}
-	if (command_name == "make_outfit")
+	if (command_name == "save_outfit")
 	{
-		return LLAppearanceMgr::getInstance()->isOutfitDirty();
+		bool outfit_locked = LLAppearanceMgr::getInstance()->isOutfitLocked();
+		bool outfit_dirty =LLAppearanceMgr::getInstance()->isOutfitDirty();
+		// allow save only if outfit isn't locked and is dirty
+		return !outfit_locked && outfit_dirty;
 	}
    
 	if (command_name == "edit" || 
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index face7124c21ed7df07e2a476a4f435ca73abce35..40f15fe86a521b901f99b029503ef9ddda76a247 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -883,12 +883,8 @@ void ModifiedCOFCallback::fire(const LLUUID& inv_item)
 {
 	LLAppearanceMgr::instance().updateAppearanceFromCOF();
 
-	if (LLSideTray::getInstance()->isPanelActive("sidepanel_appearance"))
-	{
-		// *HACK: Edit the wearable that has just been worn
-		//        only if the Appearance SP is currently opened.
-		LLAgentWearables::editWearable(inv_item);
-	}
+	// Start editing the item if previously requested.
+	gAgentWearables.editWearableIfRequested(inv_item);
 
 	// TODO: camera mode may not be changed if a debug setting is tweaked
 	if( gAgentCamera.cameraCustomizeAvatar() )