diff --git a/include/class.format.php b/include/class.format.php
index d13c2dd8b39a292698b73beda6272052d127eee6..81bb58d91ce56b6defeea027a178be85d27dffb4 100644
--- a/include/class.format.php
+++ b/include/class.format.php
@@ -116,11 +116,79 @@ class Format {
         return $len ? wordwrap($text, $len, "\n", true) : $text;
     }
 
-    function html($html, $config=array('balance'=>1)) {
+    function html_balance($html, $remove_empty=true) {
+        if (!extension_loaded('dom'))
+            return $html;
+
+        if (!trim($html))
+            return $html;
+
+        $doc = new DomDocument();
+        $xhtml = '<?xml encoding="utf-8"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>'
+            // Wrap the content in a <div> because libxml would use a <p>
+            . "<div>$html</div>";
+        $doc->encoding = 'utf-8';
+        $doc->preserveWhitespace = false;
+        $doc->recover = true;
+        if (false === @$doc->loadHTML($xhtml))
+            return $html;
+
+        if ($remove_empty) {
+            // Remove empty nodes
+            $xpath = new DOMXPath($doc);
+            static $eE = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1,
+                'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);
+            do {
+                $done = true;
+                $nodes = $xpath->query('//*[not(text()) and not(node())]');
+                foreach ($nodes as $n) {
+                    if (isset($eE[$n->nodeName]))
+                        continue;
+                    $n->parentNode->removeChild($n);
+                    $done = false;
+                }
+            } while (!$done);
+        }
+
+        static $phpversion;
+        if (!isset($phpversion))
+            $phpversion = phpversion();
+
+        $body = $doc->getElementsByTagName('body');
+        if (!$body->length)
+            return $html;
+
+        if ($phpversion > '5.3.6') {
+            $html = $doc->saveHTML($doc->getElementsByTagName('body')->item(0)->firstChild);
+        }
+        else {
+            $html = $doc->saveHTML();
+            $html = preg_replace('`^<!DOCTYPE.+?>|<\?xml .+?>|</?html>|</?body>|</?head>|<meta .+?/?>`', '', $html); # <?php
+        }
+        return preg_replace('`^<div>|</div>$`', '', $html);
+    }
+
+    function html($html, $config=array()) {
         require_once(INCLUDE_DIR.'htmLawed.php');
         $spec = false;
         if (isset($config['spec']))
             $spec = $config['spec'];
+
+        // Add in htmLawed defaults
+        $config += array(
+            'balance' => 1,
+        );
+
+        // Attempt to balance using libxml. htmLawed will corrupt HTML with
+        // balancing to fix improper HTML at the same time. For instance,
+        // some email clients may wrap block elements inside inline
+        // elements. htmLawed will change such block elements to inlines to
+        // make the HTML correct.
+        if ($config['balance'] && extension_loaded('dom')) {
+            $html = self::html_balance($html);
+            $config['balance'] = 0;
+        }
+
         return htmLawed($html, $config, $spec);
     }
 
@@ -201,7 +269,7 @@ class Format {
         }
     }
 
-    function safe_html($html, $spec=false) {
+    function safe_html($html, $spec=false, $balance=1) {
         // Remove HEAD and STYLE sections
         $html = preg_replace(
             array(':<(head|style|script).+?</\1>:is', # <head> and <style> sections
@@ -213,7 +281,7 @@ class Format {
             $html);
         $config = array(
             'safe' => 1, //Exclude applet, embed, iframe, object and script tags.
-            'balance' => 0, // No balance — corrupts poorly formatted Outlook html
+            'balance' => $balance,
             'comment' => 1, //Remove html comments (OUTLOOK LOVE THEM)
             'tidy' => -1,
             'deny_attribute' => 'id',
@@ -296,6 +364,7 @@ class Format {
             $text);
 
         //make urls clickable.
+        $text = self::html_balance($text, false);
         $text = Format::clickableurls($text);
 
         if ($inline_images)
diff --git a/include/class.forms.php b/include/class.forms.php
index ea0947e44fbd2db2450f7a5c65d96ce15f229cef..59ea16f7b47d6a08f080ab2c1b61abc3bd24f3fa 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -2700,7 +2700,7 @@ class PasswordWidget extends TextboxWidget {
         parent::parseValue();
         // Show empty box unless failed POST
         if ($_SERVER['REQUEST_METHOD'] != 'POST'
-                || $this->field->getForm()->isValid())
+                || !$this->field->getForm()->isValid())
             $this->value = '';
     }
 }
diff --git a/setup/test/tests/stubs.php b/setup/test/tests/stubs.php
index 24670dbdea13641aa632e59d82231af895c4c611..78078ed919aa21b575ba480d69d46e5c6c92671f 100644
--- a/setup/test/tests/stubs.php
+++ b/setup/test/tests/stubs.php
@@ -54,8 +54,10 @@ class DomElement {
 }
 
 class DomDocument {
+    function getElementsByTagName() {}
     function loadHTML() {}
     function loadXML() {}
+    function saveHTML() {}
 }
 
 class Exception {