diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
index 0c4c857022da7c7aa1ddc48103fd843ffce7e829..ff9080627130698105e093dd3af92fc42c3b1ba9 100644
--- a/indra/llui/llfloater.cpp
+++ b/indra/llui/llfloater.cpp
@@ -519,6 +519,36 @@ void LLFloater::storeDockStateControl()
 	}
 }
 
+LLRect LLFloater::getSavedRect() const
+{
+	LLRect rect;
+
+	if (mRectControl.size() > 1)
+	{
+		rect = LLUI::sSettingGroups["floater"]->getRect(mRectControl);
+	}
+
+	return rect;
+}
+
+bool LLFloater::hasSavedRect() const
+{
+	return !getSavedRect().isEmpty();
+}
+
+// static
+std::string LLFloater::getControlName(const std::string& name, const LLSD& key)
+{
+	std::string ctrl_name = name;
+
+	// Add the key to the control name if appropriate.
+	if (key.isString() && !key.asString().empty())
+	{
+		ctrl_name += "_" + key.asString();
+	}
+
+	return ctrl_name;
+}
 
 void LLFloater::setVisible( BOOL visible )
 {
@@ -2664,13 +2694,7 @@ void LLFloater::setInstanceName(const std::string& name)
 	mInstanceName = name;
 	if (!mInstanceName.empty())
 	{
-		std::string ctrl_name = mInstanceName;
-
-		// Add the key to the control name if appropriate.
-		if (mKey.isString() && !mKey.asString().empty())
-		{
-			ctrl_name += "_" + mKey.asString();
-		}
+		std::string ctrl_name = getControlName(mInstanceName, mKey);
 
 		// save_rect and save_visibility only apply to registered floaters
 		if (!mRectControl.empty())
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
index 5e482cbac342dcb9ac8d17ab51fe7c8b9979cd26..ed1f0715afbd06798583383f9b62cd7a26f69a38 100644
--- a/indra/llui/llfloater.h
+++ b/indra/llui/llfloater.h
@@ -203,6 +203,10 @@ public:
 	BOOL			isResizable() const				{ return mResizable; }
 	void			setResizeLimits( S32 min_width, S32 min_height );
 	void			getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; }
+	LLRect			getSavedRect() const;
+	bool			hasSavedRect() const;
+
+	static std::string	getControlName(const std::string& name, const LLSD& key);
 
 	bool			isMinimizeable() const{ return mCanMinimize; }
 	bool			isCloseable() const{ return mCanClose; }
diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp
index 9e989ed052a0500c878031747bb2bc6f61293e7d..a143318763d4b6dd6ea3d3679b1bd2e0a663942d 100644
--- a/indra/newview/llsidetray.cpp
+++ b/indra/newview/llsidetray.cpp
@@ -111,7 +111,11 @@ public:
 	};
 protected:
 	LLSideTrayTab(const Params& params);
-	
+
+	void			dock();
+	void			undock(LLFloater* floater_tab);
+
+	LLSideTray*		getSideTray();
 	
 public:
 	virtual ~LLSideTrayTab();
@@ -211,6 +215,28 @@ void	LLSideTrayTab::onOpen		(const LLSD& key)
 		panel->onOpen(key);
 }
 
+// Attempts to get the existing side tray instance.
+// Needed to avoid recursive calls of LLSideTray::getInstance().
+LLSideTray* LLSideTrayTab::getSideTray()
+{
+	// First, check if the side tray is our parent (i.e. we're attached).
+	LLSideTray* side_tray = dynamic_cast<LLSideTray*>(getParent());
+	if (!side_tray)
+	{
+		// Detached? Ok, check if the instance exists at all/
+		if (LLSideTray::instanceCreated())
+		{
+			side_tray = LLSideTray::getInstance();
+		}
+		else
+		{
+			llerrs << "No safe way to get the side tray instance" << llendl;
+		}
+	}
+
+	return side_tray;
+}
+
 void LLSideTrayTab::toggleTabDocked()
 {
 	std::string tab_name = getName();
@@ -220,70 +246,99 @@ void LLSideTrayTab::toggleTabDocked()
 
 	LLFloaterReg::toggleInstance("side_bar_tab", tab_name);
 
-	LLSideTray* side_tray = LLSideTray::getInstance();
-
-	bool is_tab_undocked = LLFloater::isShown(floater_tab);
+	bool docking = !LLFloater::isShown(floater_tab);
 
 	// Hide the "Tear Off" button when a tab gets undocked
 	// and show "Dock" button instead.
-	getChild<LLButton>("undock")->setVisible(!is_tab_undocked);
-	getChild<LLButton>("dock")->setVisible(is_tab_undocked);
+	getChild<LLButton>("undock")->setVisible(docking);
+	getChild<LLButton>("dock")->setVisible(!docking);
 
-	if (is_tab_undocked)
+	if (docking)
 	{
-		// Remove the tab from Side Tray's tabs list.
-		// We have to do it despite removing the tab from Side Tray's child view tree
-		// by addChild(). Otherwise the tab could be accessed by the pointer in LLSideTray::mTabs.
-		if (!side_tray->removeTab(this))
-		{
-			llwarns << "Failed to remove tab " << getName() << " from side tray" << llendl;
-			return;
-		}
+		dock();
+	}
+	else
+	{
+		undock(floater_tab);
+	}
+}
 
-		setVisible(true); // *HACK: restore visibility after being hidden by LLSideTray::selectTabByName().
-		floater_tab->addChild(this);
-		floater_tab->setTitle(mTabTitle);
+void LLSideTrayTab::dock()
+{
+	LLSideTray* side_tray = getSideTray();
+	if (!side_tray) return;
 
-		LLRect rect = side_tray->getLocalRect();
-		floater_tab->reshape(rect.getWidth(), rect.getHeight());
+	if (!side_tray->addTab(this))
+	{
+		llwarns << "Failed to add tab " << getName() << " to side tray" << llendl;
+		return;
+	}
 
-		rect.mTop -= floater_tab->getHeaderHeight();
-		setRect(rect);
-		reshape(rect.getWidth(), rect.getHeight());
+	setRect(side_tray->getLocalRect());
+	reshape(getRect().getWidth(), getRect().getHeight());
 
-		// Set FOLLOWS_ALL flag for the tab to follow floater dimensions upon resizing.
-		setFollowsAll();
+	// Select the re-docked tab.
+	side_tray->selectTabByName(getName());
 
-		if (!side_tray->getCollapsed())
-		{
-			side_tray->collapseSideBar();
-		}
+	if (side_tray->getCollapsed())
+	{
+		side_tray->expandSideBar();
+	}
+}
 
-		if (side_tray->getActiveTab() != this)
-		{
-			// When a tab other then current active tab is detached from Side Tray
-			// onOpen() should be called as tab visibility is changed.
-			onOpen(LLSD());
-		}
+void LLSideTrayTab::undock(LLFloater* floater_tab)
+{
+	LLSideTray* side_tray = getSideTray();
+	if (!side_tray) return;
+
+	// Remove the tab from Side Tray's tabs list.
+	// We have to do it despite removing the tab from Side Tray's child view tree
+	// by addChild(). Otherwise the tab could be accessed by the pointer in LLSideTray::mTabs.
+	if (!side_tray->removeTab(this))
+	{
+		llwarns << "Failed to remove tab " << getName() << " from side tray" << llendl;
+		return;
+	}
+
+	setVisible(true); // *HACK: restore visibility after being hidden by LLSideTray::selectTabByName().
+	floater_tab->addChild(this);
+	floater_tab->setTitle(mTabTitle);
+
+	// Reshape the floater if needed.
+	LLRect floater_rect;
+	if (floater_tab->hasSavedRect())
+	{
+		// We've got saved rect for the floater, hence no need to reshape it.
+		floater_rect = floater_tab->getLocalRect();
 	}
 	else
 	{
-		if (!side_tray->addTab(this))
-		{
-			llwarns << "Failed to add tab " << getName() << " to side tray" << llendl;
-			return;
-		}
+		// Detaching for the first time. Reshape the floater.
+		floater_rect = side_tray->getLocalRect();
+		floater_tab->reshape(floater_rect.getWidth(), floater_rect.getHeight());
+	}
 
-		setRect(side_tray->getLocalRect());
-		reshape(getRect().getWidth(), getRect().getHeight());
+	// Reshape the panel.
+	{
+		LLRect panel_rect = floater_rect;
+		panel_rect.mTop -= floater_tab->getHeaderHeight();
+		setRect(panel_rect);
+		reshape(panel_rect.getWidth(), panel_rect.getHeight());
+	}
 
-		// Select the re-docked tab.
-		side_tray->selectTabByName(getName());
+	// Set FOLLOWS_ALL flag for the tab to follow floater dimensions upon resizing.
+	setFollowsAll();
 
-		if (side_tray->getCollapsed())
-		{
-			side_tray->expandSideBar();
-		}
+	if (!side_tray->getCollapsed())
+	{
+		side_tray->collapseSideBar();
+	}
+
+	if (side_tray->getActiveTab() != this)
+	{
+		// When a tab other then current active tab is detached from Side Tray
+		// onOpen() should be called as tab visibility is changed.
+		onOpen(LLSD());
 	}
 }
 
@@ -460,6 +515,7 @@ BOOL LLSideTray::postBuild()
 			getCollapseSignal().connect(boost::bind(&LLScreenChannelBase::resetPositionAndSize, (*it).channel, _2));
 		}
 	}
+
 	return true;
 }
 
@@ -467,6 +523,8 @@ void LLSideTray::handleLoginComplete()
 {
 	//reset tab to "home" tab if it was changesd during login process
 	selectTabByName("sidebar_home");
+
+	detachTabs();
 }
 
 LLSideTrayTab* LLSideTray::getTab(const std::string& name)
@@ -901,6 +959,28 @@ void LLSideTray::arrange()
 	mButtonsPanel->setVisible(hasTabs());
 }
 
+// Detach those tabs that were detached when the viewer exited last time.
+void LLSideTray::detachTabs()
+{
+	// copy mTabs because LLSideTray::toggleTabDocked() modifies it.
+	child_vector_t tabs = mTabs;
+
+	for (child_vector_const_iter_t it = tabs.begin(); it != tabs.end(); ++it)
+	{
+		LLSideTrayTab* tab = *it;
+
+		std::string floater_ctrl_name = LLFloater::getControlName("side_bar_tab", LLSD(tab->getName()));
+		std::string vis_ctrl_name = LLFloaterReg::getVisibilityControlName(floater_ctrl_name);
+		if (!LLUI::sSettingGroups["floater"]->controlExists(vis_ctrl_name)) continue;
+
+		bool is_visible = LLUI::sSettingGroups["floater"]->getBOOL(vis_ctrl_name);
+		if (!is_visible) continue;
+
+		llassert(isTabAttached(tab->getName()));
+		tab->toggleTabDocked();
+	}
+}
+
 void LLSideTray::collapseSideBar()
 {
 	mCollapsed = true;
diff --git a/indra/newview/llsidetray.h b/indra/newview/llsidetray.h
index f60c72e7a37334e18634315708136aa9e125e81f..248def8e3d506a7eb395734d66d413b4002329e8 100644
--- a/indra/newview/llsidetray.h
+++ b/indra/newview/llsidetray.h
@@ -173,6 +173,7 @@ protected:
 	LLButton*	createButton	(const std::string& name,const std::string& image,const std::string& tooltip,
 									LLUICtrl::commit_callback_t callback);
 	void		arrange			();
+	void		detachTabs		();
 	void		reflectCollapseChange();
 
 	void		toggleTabButton	(LLSideTrayTab* tab);
diff --git a/indra/newview/skins/default/xui/en/floater_side_bar_tab.xml b/indra/newview/skins/default/xui/en/floater_side_bar_tab.xml
index 35df6a37f6f514439af1638cc5d67c8df4a35886..9ec3410afd1f6bcac459cb235ea1b0b030dd86e7 100644
--- a/indra/newview/skins/default/xui/en/floater_side_bar_tab.xml
+++ b/indra/newview/skins/default/xui/en/floater_side_bar_tab.xml
@@ -3,5 +3,6 @@
  can_close="false"
  can_resize="true"
  save_rect="true"
+ save_visibility="true"
  >
 </floater>