diff --git a/indra/llui/llsdparam.cpp b/indra/llui/llsdparam.cpp
index f97f80ab6cb63c3c99b7e0950107a03e2a69f947..fae20a473e175872a15914ba213a06c5ec2dd56b 100644
--- a/indra/llui/llsdparam.cpp
+++ b/indra/llui/llsdparam.cpp
@@ -45,6 +45,7 @@ LLParamSDParser::LLParamSDParser()
 
 	if (sReadFuncs.empty())
 	{
+		registerParserFuncs<LLInitParam::NoValue>(readNoValue, &LLParamSDParser::writeNoValue);
 		registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>);
 		registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param);
 		registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>);
@@ -71,6 +72,18 @@ bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const voi
 	return true;
 }
 
+bool LLParamSDParser::writeNoValue(LLParamSDParser::parser_t& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack)
+{
+	LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
+	if (!sdparser.mWriteRootSD) return false;
+
+	LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack);
+	if (!sd_to_write) return false;
+
+	return true;
+}
+
+
 void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent)
 {
 	mCurReadSD = NULL;
@@ -87,6 +100,8 @@ void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block)
 	block.serializeBlock(*this);
 }
 
+const LLSD NO_VALUE_MARKER;
+
 void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block)
 {
 	if (sd.isMap())
@@ -110,6 +125,11 @@ void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block
 			readSDValues(*it, block);
 		}
 	}
+	else if (sd.isUndefined())
+	{
+		mCurReadSD = &NO_VALUE_MARKER;
+		block.submitValue(mNameStack, *this);
+	}
 	else
 	{
 		mCurReadSD = &sd;
@@ -206,6 +226,13 @@ LLSD* LLParamSDParser::getSDWriteNode(const parser_t::name_stack_t& name_stack)
 	return sd_to_write;
 }
 
+bool LLParamSDParser::readNoValue(Parser& parser, void* val_ptr)
+{
+	LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+	return self.mCurReadSD == &NO_VALUE_MARKER;
+}
+
+
 bool LLParamSDParser::readS32(Parser& parser, void* val_ptr)
 {
 	LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
diff --git a/indra/llui/llsdparam.h b/indra/llui/llsdparam.h
index 97e8b58e492bd61d574ad0b61e479d2e80f3a8cb..69dab2b411cf5159934f4b011f5fcc9702f1caa4 100644
--- a/indra/llui/llsdparam.h
+++ b/indra/llui/llsdparam.h
@@ -63,7 +63,9 @@ typedef LLInitParam::Parser parser_t;
 	LLSD* getSDWriteNode(const parser_t::name_stack_t& name_stack);
 
 	static bool writeU32Param(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack);
+	static bool writeNoValue(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack);
 
+	static bool readNoValue(Parser& parser, void* val_ptr);
 	static bool readS32(Parser& parser, void* val_ptr);
 	static bool readU32(Parser& parser, void* val_ptr);
 	static bool readF32(Parser& parser, void* val_ptr);
diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llxuixml/llinitparam.cpp
index 2c92539387a98560b616785c464b77aa0adf0b11..7ffcd9187999cd06d3be7a884046cd59c49c263f 100644
--- a/indra/llxuixml/llinitparam.cpp
+++ b/indra/llxuixml/llinitparam.cpp
@@ -312,6 +312,13 @@ namespace LLInitParam
 			}
 		}
 
+		// if no match, and no names left on stack, this is just an existence assertion of this block
+		// verify by calling readValue with NoValue type, an inherently unparseable type
+		if (!names_left)
+		{
+			return p.readValue(NoValue());
+		}
+
 		return false;
 	}
 
diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h
index ad9b584632c2c1067d4929cf16ac0fb8cda04c5b..66ef8e65cd582e2988f2ceec9a5977bb6f522d19 100644
--- a/indra/llxuixml/llinitparam.h
+++ b/indra/llxuixml/llinitparam.h
@@ -278,6 +278,9 @@ namespace LLInitParam
 		S32	mParseGeneration;
 	};
 
+	// used to indicate no matching value to a given name when parsing
+	struct NoValue{};
+
 	class BaseBlock;
 
 	class Param
diff --git a/indra/llxuixml/llxuiparser.cpp b/indra/llxuixml/llxuiparser.cpp
index e1ad9a5c7143b931625d813cd1322718cf0faee9..723a20f3825519ba74e3e9d71716595b72d32f97 100644
--- a/indra/llxuixml/llxuiparser.cpp
+++ b/indra/llxuixml/llxuiparser.cpp
@@ -382,6 +382,7 @@ LLXUIParser::LLXUIParser()
 {
 	if (sXUIReadFuncs.empty())
 	{
+		registerParserFuncs<LLInitParam::NoValue>(readNoValue, writeNoValue);
 		registerParserFuncs<bool>(readBoolValue, writeBoolValue);
 		registerParserFuncs<std::string>(readStringValue, writeStringValue);
 		registerParserFuncs<U8>(readU8Value, writeU8Value);
@@ -400,6 +401,7 @@ LLXUIParser::LLXUIParser()
 }
 
 static LLFastTimer::DeclareTimer FTM_PARSE_XUI("XUI Parsing");
+const LLXMLNodePtr DUMMY_NODE = new LLXMLNode();
 
 void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, const std::string& filename, bool silent)
 {
@@ -426,6 +428,17 @@ bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
 	boost::char_separator<char> sep(".");
 
 	bool values_parsed = false;
+	bool silent = mCurReadDepth > 0;
+
+	if (nodep->getFirstChild().isNull() 
+		&& nodep->mAttributes.empty() 
+		&& nodep->getSanitizedValue().empty())
+	{
+		// empty node, just parse as NoValue
+		mCurReadNode = DUMMY_NODE;
+		return block.submitValue(mNameStack, *this, silent);
+	}
+
 
 	// submit attributes for current node
 	values_parsed |= readAttributes(nodep, block);
@@ -438,7 +451,6 @@ bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
 		mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
 		// child nodes are not necessarily valid parameters (could be a child widget)
 		// so don't complain once we've recursed
-		bool silent = mCurReadDepth > 0;
 		if (!block.submitValue(mNameStack, *this, true))
 		{
 			mNameStack.pop_back();
@@ -543,6 +555,7 @@ bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& blo
 	boost::char_separator<char> sep(".");
 
 	bool any_parsed = false;
+	bool silent = mCurReadDepth > 0;
 
 	for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); 
 		attribute_it != nodep->mAttributes.end(); 
@@ -561,7 +574,6 @@ bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& blo
 		}
 
 		// child nodes are not necessarily valid attributes, so don't complain once we've recursed
-		bool silent = mCurReadDepth > 0;
 		any_parsed |= block.submitValue(mNameStack, *this, silent);
 		
 		while(num_tokens_pushed-- > 0)
@@ -628,6 +640,19 @@ LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
 	return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
 }
 
+bool LLXUIParser::readNoValue(Parser& parser, void* val_ptr)
+{
+	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+	return self.mCurReadNode == DUMMY_NODE;
+}
+
+bool LLXUIParser::writeNoValue(Parser& parser, const void* val_ptr, const name_stack_t& stack)
+{
+	// just create node
+	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
+	LLXMLNodePtr node = self.getNode(stack);
+	return node.notNull();
+}
 
 bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr)
 {
@@ -1043,6 +1068,8 @@ static 	LLInitParam::Parser::parser_read_func_map_t sSimpleXUIReadFuncs;
 static 	LLInitParam::Parser::parser_write_func_map_t sSimpleXUIWriteFuncs;
 static 	LLInitParam::Parser::parser_inspect_func_map_t sSimpleXUIInspectFuncs;
 
+const char* NO_VALUE_MARKER = "no_value";
+
 LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t element_cb)
 :	Parser(sSimpleXUIReadFuncs, sSimpleXUIWriteFuncs, sSimpleXUIInspectFuncs),
 	mLastWriteGeneration(-1),
@@ -1051,6 +1078,7 @@ LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t
 {
 	if (sSimpleXUIReadFuncs.empty())
 	{
+		registerParserFuncs<LLInitParam::NoValue>(readNoValue);
 		registerParserFuncs<bool>(readBoolValue);
 		registerParserFuncs<std::string>(readStringValue);
 		registerParserFuncs<U8>(readU8Value);
@@ -1114,6 +1142,8 @@ bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBl
 		return false;
 	}
 	
+	mEmptyLeafNode.push_back(false);
+
 	if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) )
 	{
 		LL_WARNS("ReadXUI") << "Error while parsing file  " << filename << LL_ENDL;
@@ -1121,6 +1151,8 @@ bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBl
 		return false;
 	}
 
+	mEmptyLeafNode.pop_back();
+
 	XML_ParserFree( mParser );
 	return true;
 }
@@ -1205,8 +1237,14 @@ void LLSimpleXUIParser::startElement(const char *name, const char **atts)
 		}
 	}
 
+	// parent node is not empty
+	mEmptyLeafNode.back() = false;
+	// we are empty if we have no attributes
+	mEmptyLeafNode.push_back(atts[0] == NULL);
+
 	mTokenSizeStack.push_back(num_tokens_pushed);
 	readAttributes(atts);
+
 }
 
 bool LLSimpleXUIParser::readAttributes(const char **atts)
@@ -1240,7 +1278,7 @@ bool LLSimpleXUIParser::readAttributes(const char **atts)
 	return any_parsed;
 }
 
-void LLSimpleXUIParser::processText()
+bool LLSimpleXUIParser::processText()
 {
 	if (!mTextContents.empty())
 	{
@@ -1253,12 +1291,22 @@ void LLSimpleXUIParser::processText()
 			mNameStack.pop_back();
 		}
 		mTextContents.clear();
+		return true;
 	}
+	return false;
 }
 
 void LLSimpleXUIParser::endElement(const char *name)
 {
-	processText();
+	bool has_text = processText();
+
+	// no text, attributes, or children
+	if (!has_text && mEmptyLeafNode.back())
+	{
+		// submit this as a valueless name (even though there might be text contents we haven't seen yet)
+		mCurAttributeValueBegin = NO_VALUE_MARKER;
+		mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
+	}
 
 	if (--mOutputStack.back().second == 0)
 	{
@@ -1276,6 +1324,7 @@ void LLSimpleXUIParser::endElement(const char *name)
 		mNameStack.pop_back();
 	}
 	mScope.pop_back();
+	mEmptyLeafNode.pop_back();
 }
 
 void LLSimpleXUIParser::characterData(const char *s, int len)
@@ -1322,6 +1371,12 @@ void LLSimpleXUIParser::parserError(const std::string& message)
 #endif
 }
 
+bool LLSimpleXUIParser::readNoValue(Parser& parser, void* val_ptr)
+{
+	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
+	return self.mCurAttributeValueBegin == NO_VALUE_MARKER;
+}
+
 bool LLSimpleXUIParser::readBoolValue(Parser& parser, void* val_ptr)
 {
 	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
diff --git a/indra/llxuixml/llxuiparser.h b/indra/llxuixml/llxuiparser.h
index 5c613b0c6998ed1d5a518d4f61eff284dc32d459..7a748d8aea770562f7312b2686fbe74de0fb10b8 100644
--- a/indra/llxuixml/llxuiparser.h
+++ b/indra/llxuixml/llxuiparser.h
@@ -116,6 +116,7 @@ LOG_CLASS(LLXUIParser);
 	bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
 
 	//reader helper functions
+	static bool readNoValue(Parser& parser, void* val_ptr);
 	static bool readBoolValue(Parser& parser, void* val_ptr);
 	static bool readStringValue(Parser& parser, void* val_ptr);
 	static bool readU8Value(Parser& parser, void* val_ptr);
@@ -132,6 +133,7 @@ LOG_CLASS(LLXUIParser);
 	static bool readSDValue(Parser& parser, void* val_ptr);
 
 	//writer helper functions
+	static bool writeNoValue(Parser& parser, const void* val_ptr, const name_stack_t&);
 	static bool writeBoolValue(Parser& parser, const void* val_ptr, const name_stack_t&);
 	static bool writeStringValue(Parser& parser, const void* val_ptr, const name_stack_t&);
 	static bool writeU8Value(Parser& parser, const void* val_ptr, const name_stack_t&);
@@ -194,6 +196,7 @@ LOG_CLASS(LLSimpleXUIParser);
 
 private:
 	//reader helper functions
+	static bool readNoValue(Parser&, void* val_ptr);
 	static bool readBoolValue(Parser&, void* val_ptr);
 	static bool readStringValue(Parser&, void* val_ptr);
 	static bool readU8Value(Parser&, void* val_ptr);
@@ -218,7 +221,7 @@ LOG_CLASS(LLSimpleXUIParser);
 	void endElement(const char *name);
 	void characterData(const char *s, int len);
 	bool readAttributes(const char **atts);
-	void processText();
+	bool processText();
 
 	Parser::name_stack_t			mNameStack;
 	struct XML_ParserStruct*		mParser;
@@ -230,6 +233,7 @@ LOG_CLASS(LLSimpleXUIParser);
 	const char*						mCurAttributeValueBegin;
 	std::vector<S32>				mTokenSizeStack;
 	std::vector<std::string>		mScope;
+	std::vector<bool>				mEmptyLeafNode;
 	element_start_callback_t		mElementCB;
 
 	std::vector<std::pair<LLInitParam::BaseBlock*, S32> > mOutputStack;