Replace DomUtil with SafeHtmlBuilder

The SafeHtmlBuilder utility is much easier to keep clean content with,
as the objects use (some) Java type safety to help protect the safe
strings from unsafe ones.

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gerrit/Gerrit.gwt.xml b/src/main/java/com/google/gerrit/Gerrit.gwt.xml
index e5dde02..8d73d5c 100644
--- a/src/main/java/com/google/gerrit/Gerrit.gwt.xml
+++ b/src/main/java/com/google/gerrit/Gerrit.gwt.xml
@@ -12,7 +12,7 @@
 
 
   <entry-point class='com.google.gerrit.client.Gerrit'/>
-  <stylesheet src='gerrit1.cache.css' />
+  <stylesheet src='gerrit2.cache.css' />
 
   <servlet path='/Gerrit'
            class='com.google.gerrit.server.HostPageServlet'/>
diff --git a/src/main/java/com/google/gerrit/client/account/AgreementPanel.java b/src/main/java/com/google/gerrit/client/account/AgreementPanel.java
index a6f7bf5..6fbbcd2 100644
--- a/src/main/java/com/google/gerrit/client/account/AgreementPanel.java
+++ b/src/main/java/com/google/gerrit/client/account/AgreementPanel.java
@@ -27,6 +27,8 @@
 import com.google.gwt.user.client.ui.SourcesTableEvents;
 import com.google.gwt.user.client.ui.TableListener;
 import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 class AgreementPanel extends Composite {
   private AgreementTable agreements;
@@ -127,8 +129,12 @@
         }
         table.setText(row, 3, cla.getShortDescription());
       }
-      table.setHTML(row, 4, FormatUtil.mediumFormat(k.getAcceptedOn())
-          + "<br>\n" + FormatUtil.mediumFormat(k.getReviewedOn()));
+
+      final SafeHtmlBuilder b = new SafeHtmlBuilder();
+      b.append(FormatUtil.mediumFormat(k.getAcceptedOn()));
+      b.br();
+      b.append(FormatUtil.mediumFormat(k.getReviewedOn()));
+      SafeHtml.set(table, row, 4, b);
 
       final FlexCellFormatter fmt = table.getFlexCellFormatter();
       for (int c = 1; c <= 4; c++) {
diff --git a/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java b/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
index 595f024..e5c42b1 100644
--- a/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
+++ b/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.client.ui.AccountDashboardLink;
 import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
 import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.AccountSuggestOracle;
 import com.google.gerrit.client.ui.AddMemberBox;
 import com.google.gerrit.client.ui.FancyFlexTable;
 import com.google.gerrit.client.ui.SmallHeading;
@@ -31,7 +30,6 @@
 import com.google.gwt.user.client.ui.CheckBox;
 import com.google.gwt.user.client.ui.ClickListener;
 import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FocusListenerAdapter;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.Panel;
 import com.google.gwt.user.client.ui.SourcesTableEvents;
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java b/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java
index 1558446..658b66a 100644
--- a/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java
+++ b/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.client.rpc.Common;
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gerrit.client.ui.FancyFlexTable;
 import com.google.gerrit.client.ui.SmallHeading;
 import com.google.gwt.user.client.ui.Button;
@@ -43,6 +42,8 @@
 import com.google.gwt.user.client.ui.TextBox;
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 import com.google.gwtjsonrpc.client.VoidResult;
 
 import java.util.HashSet;
@@ -383,17 +384,17 @@
       }
 
       {
-        final StringBuilder m = new StringBuilder();
+        final SafeHtmlBuilder m = new SafeHtmlBuilder();
         final ApprovalCategoryValue min, max;
         min = ar != null ? ar.getValue(k.getMinValue()) : null;
         max = ar != null ? ar.getValue(k.getMaxValue()) : null;
 
         formatValue(m, k.getMinValue(), min);
         if (k.getMinValue() != k.getMaxValue()) {
-          m.append("<br>");
+          m.br();
           formatValue(m, k.getMaxValue(), max);
         }
-        table.setHTML(row, 4, m.toString());
+        SafeHtml.set(table, row, 4, m);
       }
 
       final FlexCellFormatter fmt = table.getFlexCellFormatter();
@@ -406,19 +407,20 @@
       setRowItem(row, k);
     }
 
-    private void formatValue(final StringBuilder m, final short v,
+    private void formatValue(final SafeHtmlBuilder m, final short v,
         final ApprovalCategoryValue e) {
-      m.append("<span class=\"gerrit-ProjectAdmin-ApprovalCategoryValue\">");
+      m.openSpan();
+      m.setStyleName("gerrit-ProjectAdmin-ApprovalCategoryValue");
       if (v == 0) {
         m.append(' ');
       } else if (v > 0) {
         m.append('+');
       }
       m.append(v);
-      m.append("</span>");
+      m.closeSpan();
       if (e != null) {
         m.append(": ");
-        m.append(DomUtil.escape(e.getName()));
+        m.append(e.getName());
       }
     }
   }
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java b/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java
index c975b5e..cdb7a97b 100644
--- a/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java
+++ b/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java
@@ -17,12 +17,13 @@
 import com.google.gerrit.client.data.AccountInfoCache;
 import com.google.gerrit.client.reviewdb.Change;
 import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.DisclosurePanel;
 import com.google.gwt.user.client.ui.HTML;
 import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwt.user.client.ui.Label;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 public class ChangeDescriptionBlock extends Composite {
   private final DisclosurePanel descriptionPanel;
@@ -50,7 +51,8 @@
   public void display(final Change chg, final PatchSetInfo info,
       final AccountInfoCache acc) {
     infoBlock.display(chg, acc);
-    description.setHTML(DomUtil.linkify(DomUtil.escape(info.getMessage())));
+    SafeHtml.set(description, new SafeHtmlBuilder().append(info.getMessage())
+        .linkify());
     descriptionPanel.setOpen(true);
   }
 }
diff --git a/src/main/java/com/google/gerrit/client/changes/MessagePanel.java b/src/main/java/com/google/gerrit/client/changes/MessagePanel.java
index f1e29ef..fbb23d5 100644
--- a/src/main/java/com/google/gerrit/client/changes/MessagePanel.java
+++ b/src/main/java/com/google/gerrit/client/changes/MessagePanel.java
@@ -15,15 +15,17 @@
 package com.google.gerrit.client.changes;
 
 import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 public class MessagePanel extends Composite {
   boolean isRecent;
 
   public MessagePanel(final ChangeMessage msg) {
-    final HTML l = new HTML(DomUtil.wikify(msg.getMessage().trim()));
+    final Widget l =
+        new SafeHtmlBuilder().append(msg.getMessage().trim()).wikify()
+            .toBlockWidget();
     l.setStyleName("gerrit-ChangeMessage-Message");
     initWidget(l);
   }
diff --git a/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java b/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java
index 34b7c0a..4d110b5 100644
--- a/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java
+++ b/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.client.reviewdb.UserIdentity;
 import com.google.gerrit.client.rpc.Common;
 import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gerrit.client.ui.RefreshListener;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.ui.Button;
@@ -45,6 +44,8 @@
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
 import com.google.gwtexpui.clippy.client.CopyableLabel;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 import com.google.gwtjsonrpc.client.VoidResult;
 
 import java.util.ArrayList;
@@ -228,38 +229,37 @@
       return;
     }
 
-    final StringBuilder r = new StringBuilder();
+    final SafeHtmlBuilder m = new SafeHtmlBuilder();
 
     if (who.getName() != null) {
       final Account.Id aId = who.getAccount();
       if (aId != null) {
-        r.append("<a href=\"#");
-        r.append(Link.toAccountDashboard(aId));
-        r.append("\">");
+        m.openAnchor();
+        m.setAttribute("href", "#" + Link.toAccountDashboard(aId));
       }
-      r.append(DomUtil.escape(who.getName()));
+      m.append(who.getName());
       if (aId != null) {
-        r.append("</a>");
+        m.closeAnchor();
       }
     }
 
     if (who.getEmail() != null) {
-      if (r.length() > 0) {
-        r.append(' ');
+      if (m.hasContent()) {
+        m.append(' ');
       }
-      r.append("&lt;");
-      r.append(DomUtil.escape(who.getEmail()));
-      r.append("&gt;");
+      m.append('<');
+      m.append(who.getEmail());
+      m.append('>');
     }
 
     if (who.getDate() != null) {
-      if (r.length() > 0) {
-        r.append(' ');
+      if (m.hasContent()) {
+        m.append(' ');
       }
-      r.append(DomUtil.escape(FormatUtil.mediumFormat(who.getDate())));
+      m.append(FormatUtil.mediumFormat(who.getDate()));
     }
 
-    infoTable.setHTML(row, 1, r.toString());
+    SafeHtml.set(infoTable, row, 1, m);
   }
 
   private void populateActions(final PatchSetDetail detail) {
diff --git a/src/main/java/com/google/gerrit/client/changes/PatchTable.java b/src/main/java/com/google/gerrit/client/changes/PatchTable.java
index 3c60775..2dd821e 100644
--- a/src/main/java/com/google/gerrit/client/changes/PatchTable.java
+++ b/src/main/java/com/google/gerrit/client/changes/PatchTable.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.client.Link;
 import com.google.gerrit.client.reviewdb.Patch;
 import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gerrit.client.ui.FancyFlexTable;
 import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.History;
@@ -25,6 +24,7 @@
 import com.google.gwt.user.client.ui.SourcesTableEvents;
 import com.google.gwt.user.client.ui.TableListener;
 import com.google.gwtexpui.progress.client.ProgressBar;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 import java.util.List;
 
@@ -50,44 +50,66 @@
     }
   }
 
-  private void appendHeader(final StringBuilder nc) {
-    nc.append("<tr>");
-    nc.append("<td class=\"" + S_ICON_HEADER + " LeftMostCell\">&nbsp;</td>");
-    nc.append("<td class=\"" + S_ICON_HEADER + "\">&nbsp;</td>");
+  private void appendHeader(final SafeHtmlBuilder m) {
+    m.openTr();
 
-    nc.append("<td class=\"" + S_DATA_HEADER + "\">");
-    nc.append(Util.C.patchTableColumnName());
-    nc.append("</td>");
+    m.openTd();
+    m.addStyleName(S_ICON_HEADER);
+    m.addStyleName("LeftMostCell");
+    m.nbsp();
+    m.closeTd();
 
-    nc.append("<td class=\"" + S_DATA_HEADER + "\">");
-    nc.append(Util.C.patchTableColumnComments());
-    nc.append("</td>");
+    m.openTd();
+    m.setStyleName(S_ICON_HEADER);
+    m.nbsp();
+    m.closeTd();
 
-    nc.append("<td class=\"" + S_DATA_HEADER + "\" colspan=\"2\">");
-    nc.append(Util.C.patchTableColumnDiff());
-    nc.append("</td>");
+    m.openTd();
+    m.setStyleName(S_DATA_HEADER);
+    m.append(Util.C.patchTableColumnName());
+    m.closeTd();
 
-    nc.append("</tr>");
+    m.openTd();
+    m.setStyleName(S_DATA_HEADER);
+    m.append(Util.C.patchTableColumnComments());
+    m.closeTd();
+
+    m.openTd();
+    m.setStyleName(S_DATA_HEADER);
+    m.setAttribute("colspan", 2);
+    m.append(Util.C.patchTableColumnDiff());
+    m.closeTd();
+
+    m.closeTr();
   }
 
-  private void appendRow(final StringBuilder nc, final Patch p) {
-    nc.append("<tr>");
-    nc.append("<td class=\"" + S_ICON_CELL + " LeftMostCell\">&nbsp;</td>");
+  private void appendRow(final SafeHtmlBuilder m, final Patch p) {
+    m.openTr();
 
-    nc.append("<td class=\"ChangeTypeCell\">");
-    nc.append(p.getChangeType().getCode());
-    nc.append("</td>");
+    m.openTd();
+    m.addStyleName(S_ICON_CELL);
+    m.addStyleName("LeftMostCell");
+    m.nbsp();
+    m.closeTd();
 
-    nc.append("<td class=\"" + S_DATA_CELL + " FilePathCell\">");
-    nc.append("<a href=\"#");
+    m.openTd();
+    m.setStyleName("ChangeTypeCell");
+    m.append(p.getChangeType().getCode());
+    m.closeTd();
+
+    m.openTd();
+    m.addStyleName(S_DATA_CELL);
+    m.addStyleName("FilePathCell");
+
+    m.openAnchor();
     if (p.getPatchType() == Patch.PatchType.UNIFIED) {
-      nc.append(Link.toPatchSideBySide(p.getKey()));
+      m.setAttribute("href", "#" + Link.toPatchSideBySide(p.getKey()));
     } else {
-      nc.append(Link.toPatchUnified(p.getKey()));
+      m.setAttribute("href", "#" + Link.toPatchUnified(p.getKey()));
     }
-    nc.append("\">");
-    nc.append(DomUtil.escape(p.getFileName()));
-    nc.append("</a>");
+    m.append(p.getFileName());
+    m.closeAnchor();
+
     if (p.getSourceFileName() != null) {
       final String secondLine;
       if (p.getChangeType() == Patch.ChangeType.RENAMED) {
@@ -99,48 +121,54 @@
       } else {
         secondLine = Util.M.otherFrom(p.getSourceFileName());
       }
-      nc.append("<br>");
-      nc.append("<span class=\"SourceFilePath\">");
-      nc.append(DomUtil.escape(secondLine));
-      nc.append("</span>");
+      m.br();
+      m.openSpan();
+      m.setStyleName("SourceFilePath");
+      m.append(secondLine);
+      m.closeSpan();
     }
-    nc.append("</td>");
+    m.closeTd();
 
-    nc.append("<td class=\"" + S_DATA_CELL + " CommentCell\">");
+    m.openTd();
+    m.addStyleName(S_DATA_CELL);
+    m.addStyleName("CommentCell");
     if (p.getCommentCount() > 0) {
-      nc.append(Util.M.patchTableComments(p.getCommentCount()));
+      m.append(Util.M.patchTableComments(p.getCommentCount()));
     }
     if (p.getDraftCount() > 0) {
       if (p.getCommentCount() > 0) {
-        nc.append(", ");
+        m.append(", ");
       }
-      nc.append("<span class=\"Drafts\">");
-      nc.append(Util.M.patchTableDrafts(p.getDraftCount()));
-      nc.append("</span>");
+      m.openSpan();
+      m.setStyleName("Drafts");
+      m.append(Util.M.patchTableDrafts(p.getDraftCount()));
+      m.closeSpan();
     }
-    nc.append("</td>");
+    m.closeTd();
 
-    nc.append("<td class=\"" + S_DATA_CELL + " DiffLinkCell\">");
+    m.openTd();
+    m.addStyleName(S_DATA_CELL);
+    m.addStyleName("DiffLinkCell");
     if (p.getPatchType() == Patch.PatchType.UNIFIED) {
-      nc.append("<a href=\"#");
-      nc.append(Link.toPatchSideBySide(p.getKey()));
-      nc.append("\">");
-      nc.append(Util.C.patchTableDiffSideBySide());
-      nc.append("</a>");
+      m.openAnchor();
+      m.setAttribute("href", "#" + Link.toPatchSideBySide(p.getKey()));
+      m.append(Util.C.patchTableDiffSideBySide());
+      m.closeAnchor();
     } else {
-      nc.append("&nbsp;");
+      m.nbsp();
     }
-    nc.append("</td>");
+    m.closeTd();
 
-    nc.append("<td class=\"" + S_DATA_CELL + " DiffLinkCell\">");
-    nc.append("<a href=\"#");
-    nc.append(Link.toPatchUnified(p.getKey()));
-    nc.append("\">");
-    nc.append(Util.C.patchTableDiffUnified());
-    nc.append("</a>");
-    nc.append("</td>");
+    m.openTd();
+    m.addStyleName(S_DATA_CELL);
+    m.addStyleName("DiffLinkCell");
+    m.openAnchor();
+    m.setAttribute("href", "#" + Link.toPatchUnified(p.getKey()));
+    m.append(Util.C.patchTableDiffUnified());
+    m.closeAnchor();
+    m.closeTd();
 
-    nc.append("</tr>");
+    m.closeTr();
   }
 
   @Override
@@ -156,7 +184,7 @@
   private final class DisplayCommand implements IncrementalCommand {
     private final List<Patch> list;
     private boolean attached;
-    private StringBuilder nc = new StringBuilder();
+    private SafeHtmlBuilder nc = new SafeHtmlBuilder();
     private int stage;
     private int row;
     private double start;
@@ -169,7 +197,7 @@
     @SuppressWarnings("fallthrough")
     public boolean execute() {
       final boolean attachedNow = isAttached();
-      if(!attached && attachedNow){
+      if (!attached && attachedNow) {
         // Remember that we have been attached at least once. If
         // later we find we aren't attached we should stop running.
         //
@@ -194,7 +222,7 @@
               return true;
             }
           }
-          resetHtml(nc.toString());
+          resetHtml(nc);
           nc = null;
           meter = null;
 
@@ -215,7 +243,13 @@
 
     void initMeter() {
       if (meter == null) {
-        resetHtml("<tr><td></td></tr>");
+        final SafeHtmlBuilder b = new SafeHtmlBuilder();
+        b.openTr();
+        b.openTd();
+        b.closeTd();
+        b.closeTr();
+        resetHtml(b);
+
         meter = new ProgressBar(Util.M.loadingPatchSet(psid.get()));
         table.setWidget(0, 0, meter);
       }
diff --git a/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java b/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
index a3a1e50..7f77472 100644
--- a/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
+++ b/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
@@ -47,6 +47,7 @@
 import com.google.gwt.user.client.ui.TextArea;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
 import com.google.gwtjsonrpc.client.VoidResult;
 
 import java.util.ArrayList;
@@ -238,7 +239,7 @@
         panel.add(m);
 
         m = new DoubleClickLinkLabel(patchKey);
-        DOM.setInnerHTML(m.getElement(), LineCommentPanel.toHTML(c));
+        SafeHtml.set(m.getElement(), LineCommentPanel.toSafeHtml(c));
         m.setStyleName("gerrit-PatchLineComment");
         panel.add(m);
       }
diff --git a/src/main/java/com/google/gerrit/client/patches/LineCommentPanel.java b/src/main/java/com/google/gerrit/client/patches/LineCommentPanel.java
index 5e05656..97a1225 100644
--- a/src/main/java/com/google/gerrit/client/patches/LineCommentPanel.java
+++ b/src/main/java/com/google/gerrit/client/patches/LineCommentPanel.java
@@ -15,13 +15,14 @@
 package com.google.gerrit.client.patches;
 
 import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 public class LineCommentPanel extends Composite {
-  public static String toHTML(final PatchLineComment comment) {
-    return DomUtil.wikify(comment.getMessage().trim());
+  public static SafeHtml toSafeHtml(final PatchLineComment msg) {
+    return new SafeHtmlBuilder().append(msg.getMessage().trim()).wikify();
   }
 
   PatchLineComment comment;
@@ -29,7 +30,7 @@
 
   public LineCommentPanel(final PatchLineComment msg) {
     comment = msg;
-    final HTML l = new HTML(toHTML(comment));
+    final Widget l = toSafeHtml(msg).toBlockWidget();
     l.setStyleName("gerrit-PatchLineComment");
     initWidget(l);
   }
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchSideBySideScreen.java b/src/main/java/com/google/gerrit/client/patches/PatchSideBySideScreen.java
index 5d4ad9d..0a0f0bd 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchSideBySideScreen.java
+++ b/src/main/java/com/google/gerrit/client/patches/PatchSideBySideScreen.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.rpc.NoDifferencesException;
 import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gerrit.client.ui.FancyFlexTable;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Event;
@@ -32,6 +31,7 @@
 import com.google.gwt.user.client.ui.SourcesTableEvents;
 import com.google.gwt.user.client.ui.TableListener;
 import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 import com.google.gwtjsonrpc.client.RemoteJsonException;
 
 import java.util.ArrayList;
@@ -208,14 +208,14 @@
     void display(final List<Patch> result) {
       all.clear();
 
-      final StringBuilder nc = new StringBuilder();
+      final SafeHtmlBuilder nc = new SafeHtmlBuilder();
       appendHeader(nc);
       for (int p = result.size() - 1; p >= 0; p--) {
         final Patch k = result.get(p);
         appendRow(nc, k);
       }
       appendRow(nc, null);
-      resetHtml(nc.toString());
+      resetHtml(nc);
 
       final int fileCnt = sbsTable.getFileCount();
       int row = 1;
@@ -250,43 +250,72 @@
       return 2 + file;
     }
 
-    private void appendHeader(final StringBuilder nc) {
-      nc.append("<tr>");
-      nc.append("<td class=\"" + S_ICON_HEADER + " LeftMostCell\">&nbsp;</td>");
-      nc.append("<td class=\"" + S_DATA_HEADER + "\">&nbsp;</td>");
+    private void appendHeader(final SafeHtmlBuilder m) {
+      m.openTr();
+
+      m.openTd();
+      m.addStyleName(S_ICON_HEADER);
+      m.addStyleName("LeftMostCell");
+      m.nbsp();
+      m.closeTd();
+
+      m.openTd();
+      m.setStyleName(S_DATA_HEADER);
+      m.nbsp();
+      m.closeTd();
+
       for (int file = 0; file < sbsTable.getFileCount(); file++) {
-        nc.append("<td class=\"" + S_DATA_HEADER + "\">");
-        nc.append(DomUtil.escape(sbsTable.getFileTitle(file)));
-        nc.append("</td>");
+        m.openTd();
+        m.setStyleName(S_DATA_HEADER);
+        m.append(sbsTable.getFileTitle(file));
+        m.closeTd();
       }
-      nc.append("<td class=\"" + S_DATA_HEADER + "\">");
-      nc.append(DomUtil.escape("Comments"));
-      nc.append("</td>");
-      nc.append("</tr>");
+
+      m.openTd();
+      m.setStyleName(S_DATA_HEADER);
+      m.append(Util.C.patchTableColumnComments());
+      m.closeTd();
+
+      m.closeTr();
     }
 
-    private void appendRow(final StringBuilder nc, final Patch k) {
-      nc.append("<tr>");
-      nc.append("<td class=\"" + S_ICON_CELL + " LeftMostCell\">&nbsp;</td>");
-      nc.append("<td class=\"" + S_DATA_CELL + "\" align=\"right\">");
+    private void appendRow(final SafeHtmlBuilder m, final Patch k) {
+      m.openTr();
+
+      m.openTd();
+      m.addStyleName(S_ICON_CELL);
+      m.addStyleName("LeftMostCell");
+      m.nbsp();
+      m.closeTd();
+
+      m.openTd();
+      m.setStyleName(S_DATA_CELL);
+      m.setAttribute("align", "right");
       if (k != null) {
         final PatchSet.Id psId = k.getKey().getParentKey();
-        nc.append(Util.M.patchSetHeader(psId.get()));
+        m.append(Util.M.patchSetHeader(psId.get()));
       } else {
-        nc.append("Base");
+        m.append("Base");
       }
-      nc.append("</td>");
+      m.closeTd();
+
       for (int file = 0; file < sbsTable.getFileCount(); file++) {
-        nc.append("<td class=\"" + S_DATA_CELL + "\">&nbsp;</td>");
+        m.openTd();
+        m.setStyleName(S_DATA_CELL);
+        m.nbsp();
+        m.closeTd();
       }
-      nc.append("<td class=\"" + S_DATA_CELL + "\">");
+
+      m.openTd();
+      m.setStyleName(S_DATA_CELL);
       if (k != null && k.getCommentCount() > 0) {
-        nc.append(Util.M.patchTableComments(k.getCommentCount()));
+        m.append(Util.M.patchTableComments(k.getCommentCount()));
       } else {
-        nc.append("&nbsp;");
+        m.nbsp();
       }
-      nc.append("</td>");
-      nc.append("</tr>");
+      m.closeTd();
+
+      m.closeTr();
     }
 
     private class HistoryRadio extends RadioButton {
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchUtil.java b/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
index 1e95950..70206c7 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
+++ b/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
@@ -14,8 +14,9 @@
 
 package com.google.gerrit.client.patches;
 
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gwt.core.client.GWT;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 import com.google.gwtjsonrpc.client.JsonUtil;
 
 public class PatchUtil {
@@ -29,11 +30,11 @@
     JsonUtil.bind(DETAIL_SVC, "rpc/PatchDetailService");
   }
 
-  public static String lineToHTML(final String src, final int lineLength,
+  public static SafeHtml lineToSafeHtml(final String src, final int lineLength,
       final boolean showWhiteSpaceErrors) {
     final boolean hasTab = src.indexOf('\t') >= 0;
     String brokenSrc = wrapLines(src, hasTab, lineLength);
-    String html = DomUtil.escape(brokenSrc);
+    SafeHtml html = new SafeHtmlBuilder().append(brokenSrc);
     if (showWhiteSpaceErrors) {
       html = showTabAfterSpace(html);
       html = showTrailingWhitespace(html);
@@ -81,15 +82,23 @@
     return r.toString();
   }
 
-  private native static String expandTabs(String src)
-  /*-{ return src.replace(/\t/g, '<span title="Visual Tab" class="gerrit-visualtab">&raquo;</span>\t'); }-*/;
+  private static SafeHtml expandTabs(SafeHtml src) {
+    return src
+        .replaceAll("\t",
+            "<span title=\"Visual Tab\" class=\"gerrit-visualtab\">&raquo;</span>\t");
+  }
 
-  private native static String expandLFs(String src)
-  /*-{ return src.replace(/\n/g, '<br>'); }-*/;
+  private static SafeHtml expandLFs(SafeHtml src) {
+    return src.replaceAll("\n", "<br />");
+  }
 
-  private native static String showTabAfterSpace(String src)
-  /*-{ return src.replace(/^(  *\t)/, '<span class="gerrit-whitespaceerror">$1</span>'); }-*/;
+  private static SafeHtml showTabAfterSpace(SafeHtml src) {
+    return src.replaceFirst("^(  *\t)",
+        "<span class=\"gerrit-whitespaceerror\">$1</span>");
+  }
 
-  private native static String showTrailingWhitespace(String src)
-  /*-{ return src.replace(/([ \t][ \t]*)(\r?\n?)$/, '<span class="gerrit-whitespaceerror">$1</span>$2'); }-*/;
+  private static SafeHtml showTrailingWhitespace(SafeHtml src) {
+    return src.replaceFirst("([ \t][ \t]*)(\r?\n?)$",
+        "<span class=\"gerrit-whitespaceerror\">$1</span>$2");
+  }
 }
diff --git a/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java b/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
index d051b1b..f343479 100644
--- a/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
+++ b/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
@@ -18,10 +18,10 @@
 import com.google.gerrit.client.data.SideBySidePatchDetail;
 import com.google.gerrit.client.reviewdb.PatchLineComment;
 import com.google.gerrit.client.ui.ComplexDisclosurePanel;
-import com.google.gerrit.client.ui.DomUtil;
 import com.google.gwt.user.client.ui.FlowPanel;
 import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 import java.util.Iterator;
 import java.util.List;
@@ -128,7 +128,7 @@
     // This pass does not include the line comments; they need full
     // GWT widgets and are relatively infrequent. We do them later.
     //
-    final StringBuilder nc = new StringBuilder();
+    final SafeHtmlBuilder nc = new SafeHtmlBuilder();
     appendHeader(nc);
     for (final List<SideBySideLine> pLine : detail.getLines()) {
       if (skipped(prior, pLine) > 0) {
@@ -140,7 +140,7 @@
     if (skipped(prior, null) > 0) {
       appendSkipLine(nc);
     }
-    resetHtml(nc.toString());
+    resetHtml(nc);
 
     // Insert the comment widgets now that the table DOM has been
     // parsed out of the HTML by the browser. We also bind each
@@ -201,37 +201,57 @@
     }
   }
 
-  private void appendHeader(final StringBuilder nc) {
+  private void appendHeader(final SafeHtmlBuilder m) {
     final String width = (100 / fileCnt) + "%";
-    nc.append("<tr>");
-    nc.append("<td class=\"FileColumnHeader " + S_ICON_CELL + "\">&nbsp;</td>");
+    m.openTr();
+
+    m.openTd();
+    m.addStyleName(S_ICON_CELL);
+    m.addStyleName("FileColumnHeader");
+    m.nbsp();
+    m.closeTd();
 
     if (fileCnt == 2) {
-      nc.append("<td class=\"FileColumnHeader LineNumber\">&nbsp;</td>");
-      nc.append("<td class=\"FileColumnHeader\" width=\"");
-      nc.append(width);
-      nc.append("\">");
-      nc.append(DomUtil.escape(PatchUtil.C.patchHeaderOld()));
-      nc.append("</td>");
+      m.openTd();
+      m.addStyleName("FileColumnHeader");
+      m.addStyleName("LineNumber");
+      m.nbsp();
+      m.closeTd();
+
+      m.openTd();
+      m.setStyleName("FileColumnHeader");
+      m.setAttribute("width", width);
+      m.append(PatchUtil.C.patchHeaderOld());
+      m.closeTd();
     } else {
       for (int fileId = 0; fileId < fileCnt - 1; fileId++) {
-        nc.append("<td class=\"FileColumnHeader LineNumber\">&nbsp;</td>");
-        nc.append("<td class=\"FileColumnHeader\" width=\"");
-        nc.append(width);
-        nc.append("\">");
-        nc.append(DomUtil.escape(PatchUtil.M.patchHeaderAncestor(fileId + 1)));
-        nc.append("</td>");
+        m.openTd();
+        m.addStyleName("FileColumnHeader");
+        m.addStyleName("LineNumber");
+        m.nbsp();
+        m.closeTd();
+
+        m.openTd();
+        m.setStyleName("FileColumnHeader");
+        m.setAttribute("width", width);
+        m.append(PatchUtil.M.patchHeaderAncestor(fileId + 1));
+        m.closeTd();
       }
     }
 
-    nc.append("<td class=\"FileColumnHeader LineNumber\">&nbsp;</td>");
-    nc.append("<td class=\"FileColumnHeader\" width=\"");
-    nc.append(width);
-    nc.append("\">");
-    nc.append(DomUtil.escape(PatchUtil.C.patchHeaderNew()));
-    nc.append("</td>");
+    m.openTd();
+    m.addStyleName("FileColumnHeader");
+    m.addStyleName("LineNumber");
+    m.nbsp();
+    m.closeTd();
 
-    nc.append("</tr>");
+    m.openTd();
+    m.setStyleName("FileColumnHeader");
+    m.setAttribute("width", width);
+    m.append(PatchUtil.C.patchHeaderNew());
+    m.closeTd();
+
+    m.closeTr();
   }
 
   private int skipped(List<SideBySideLine> prior,
@@ -279,14 +299,19 @@
     return existCnt == gapCnt ? lines : 0;
   }
 
-  private void appendSkipLine(final StringBuilder body) {
-    body.append("<tr>");
-    body.append("<td class=\"" + S_ICON_CELL + "\">&nbsp;</td>");
-    body.append("<td class=\"SkipLine\" colspan=\"");
-    body.append(fileCnt * 2);
-    body.append("\">");
-    body.append("</td>");
-    body.append("</tr>");
+  private void appendSkipLine(final SafeHtmlBuilder m) {
+    m.openTr();
+
+    m.openTd();
+    m.setStyleName(S_ICON_CELL);
+    m.nbsp();
+    m.closeTd();
+
+    m.openTd();
+    m.setStyleName("SkipLine");
+    m.setAttribute("colspan", fileCnt * 2);
+    m.closeTd();
+    m.closeTr();
   }
 
   private void bindSkipLine(int row, final int skipCnt) {
@@ -295,21 +320,27 @@
     table.setWidget(row, 1, skipPanel);
   }
 
-  private void appendFileLine(final StringBuilder nc,
+  private void appendFileLine(final SafeHtmlBuilder m,
       final List<SideBySideLine> line) {
-    nc.append("<tr valign=\"top\">");
-    nc.append("<td class=\"" + S_ICON_CELL + "\">&nbsp;</td>");
+    m.openTr();
+    m.setAttribute("valign", "top");
+
+    m.openTd();
+    m.setStyleName(S_ICON_CELL);
+    m.nbsp();
+    m.closeTd();
 
     for (int fileId = 0; fileId < fileCnt; fileId++) {
       final SideBySideLine s = line.get(fileId);
       if (s != null) {
-        nc.append("<td class=\"LineNumber\">");
-        nc.append(s.getLineNumber());
-        nc.append("</td>");
+        m.openTd();
+        m.setStyleName("LineNumber");
+        m.append(s.getLineNumber());
+        m.closeTd();
 
-        nc.append("<td class=\"FileLine FileLine-");
-        nc.append(s.getType().name());
-        nc.append("\">");
+        m.openTd();
+        m.addStyleName("FileLine");
+        m.addStyleName("FileLine-" + s.getType().name());
         if (!"".equals(s.getText())) {
           boolean showWhitespaceErrors = false;
           if (fileId == fileCnt - 1
@@ -319,19 +350,27 @@
             //
             showWhitespaceErrors = true;
           }
-          nc.append(PatchUtil.lineToHTML(s.getText(),
+          m.append(PatchUtil.lineToSafeHtml(s.getText(),
               PatchUtil.DEFAULT_LINE_LENGTH, showWhitespaceErrors));
         } else {
-          nc.append("&nbsp;");
+          m.nbsp();
         }
-        nc.append("</td>");
+        m.closeTd();
       } else {
-        nc.append("<td class=\"LineNumber\">&nbsp;</td>");
-        nc.append("<td class=\"FileLine FileLineNone\">&nbsp;</td>");
+        m.openTd();
+        m.setStyleName("LineNumber");
+        m.nbsp();
+        m.closeTd();
+
+        m.openTd();
+        m.addStyleName("FileLine");
+        m.addStyleName("FileLineNone");
+        m.nbsp();
+        m.closeTd();
       }
     }
 
-    nc.append("</tr>");
+    m.closeTr();
   }
 
   private static class SideBySideLineList {
diff --git a/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java b/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
index 68d3bda..c071e88 100644
--- a/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
+++ b/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.client.data.PatchLine;
 import com.google.gerrit.client.reviewdb.PatchLineComment;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 import java.util.Iterator;
 import java.util.List;
@@ -81,11 +82,11 @@
   public void display(final List<PatchLine> list) {
     initVersions(2);
 
-    final StringBuilder nc = new StringBuilder();
+    final SafeHtmlBuilder nc = new SafeHtmlBuilder();
     for (final PatchLine pLine : list) {
       appendLine(nc, pLine);
     }
-    resetHtml(nc.toString());
+    resetHtml(nc);
 
     int row = 0;
     for (final PatchLine pLine : list) {
@@ -106,41 +107,54 @@
     }
   }
 
-  private void appendLine(final StringBuilder nc, final PatchLine line) {
-    nc.append("<tr>");
-    nc.append("<td class=\"" + S_ICON_CELL + "\">&nbsp;</td>");
+  private void appendLine(final SafeHtmlBuilder m, final PatchLine line) {
+    m.openTr();
 
+    m.openTd();
+    m.setStyleName(S_ICON_CELL);
+    m.nbsp();
+    m.closeTd();
 
     switch (line.getType()) {
       case FILE_HEADER:
       case HUNK_HEADER:
-        nc.append("<td class=\"LineNumber\">&nbsp;</td>");
-        nc.append("<td class=\"LineNumber\">&nbsp;</td>");
+        m.openTd();
+        m.setStyleName("LineNumber");
+        m.nbsp();
+        m.closeTd();
+
+        m.openTd();
+        m.setStyleName("LineNumber");
+        m.nbsp();
+        m.closeTd();
         break;
+
       default:
-        nc.append("<td class=\"LineNumber\">");
+        m.openTd();
+        m.setStyleName("LineNumber");
         if (line.getOldLineNumber() != 0
             && (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.PRE_IMAGE)) {
-          nc.append(line.getOldLineNumber());
+          m.append(line.getOldLineNumber());
         } else {
-          nc.append("&nbsp;");
+          m.nbsp();
         }
-        nc.append("</td>");
+        m.closeTd();
 
-        nc.append("<td class=\"LineNumber\">");
+        m.openTd();
+        m.setStyleName("LineNumber");
         if (line.getNewLineNumber() != 0
             && (line.getType() == PatchLine.Type.CONTEXT || line.getType() == PatchLine.Type.POST_IMAGE)) {
-          nc.append(line.getNewLineNumber());
+          m.append(line.getNewLineNumber());
         } else {
-          nc.append("&nbsp;");
+          m.nbsp();
         }
-        nc.append("</td>");
+        m.closeTd();
         break;
     }
 
-    nc.append("<td class=\"DiffText DiffText-");
-    nc.append(line.getType().name());
-    nc.append("\">");
+    m.openTd();
+    m.addStyleName("DiffText");
+    m.addStyleName("DiffText-" + line.getType().name());
     if (!"".equals(line.getText())) {
       boolean showWhitespaceErrors = false;
       switch (line.getType()) {
@@ -150,12 +164,12 @@
           showWhitespaceErrors = true;
           break;
       }
-      nc.append(PatchUtil.lineToHTML(line.getText(), 0, showWhitespaceErrors));
+      m.append(PatchUtil.lineToSafeHtml(line.getText(), 0, showWhitespaceErrors));
     } else {
-      nc.append("&nbsp;");
+      m.nbsp();
     }
-    nc.append("</td>");
+    m.closeTd();
 
-    nc.append("</tr>");
+    m.closeTr();
   }
 }
diff --git a/src/main/java/com/google/gerrit/client/ui/DomUtil.java b/src/main/java/com/google/gerrit/client/ui/DomUtil.java
deleted file mode 100644
index 3ae0abf..0000000
--- a/src/main/java/com/google/gerrit/client/ui/DomUtil.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2008 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.client.ui;
-
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-/** Utilities for dealing with the DOM. */
-public abstract class DomUtil {
-  /** Escape XML/HTML special characters in the input string. */
-  public static String escape(final String in) {
-    return new SafeHtmlBuilder().append(in).asString();
-  }
-
-  /** Convert bare URLs into &lt;a href&gt; tags. */
-  public static String linkify(final String in) {
-    return in.replaceAll("(https?://[^ \n\r\t]*)", "<a href=\"$1\">$1</a>");
-  }
-
-  /** Do wiki style formatting to make it pretty */
-  public static String wikify(String in) {
-    in = escape(in);
-    in = linkify(in);
-    in = in.replaceAll("(^|\n)([ \t][^\n]*)", "$1<span class=\"gerrit-preformat\">$2</span><br />");
-    in = in.replaceAll("\n\n", "<p>\n");
-    return in;
-  }
-
-  private DomUtil() {
-  }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java b/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
index 37bf00b..a3beabb 100644
--- a/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
+++ b/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
@@ -32,6 +32,7 @@
 import com.google.gwt.user.client.ui.UIObject;
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
 
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -110,7 +111,7 @@
     setRowItem(table.getCellFormatter().getElement(row, 0), item);
   }
 
-  protected void resetHtml(final String body) {
+  protected void resetHtml(final SafeHtml body) {
     for (final Iterator<Widget> i = table.iterator(); i.hasNext();) {
       i.next();
       i.remove();
diff --git a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java b/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
index 02b752e..d11919e 100644
--- a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
+++ b/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
@@ -15,13 +15,13 @@
 package com.google.gerrit.client.ui;
 
 import com.google.gerrit.client.ui.FancyFlexTable.MyFlexTable;
-import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.ui.HTMLTable;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
 
 public class FancyFlexTableImpl {
-  public void resetHtml(final MyFlexTable myTable, final String body) {
-    DOM.setInnerHTML(getBodyElement(myTable), body);
+  public void resetHtml(final MyFlexTable myTable, final SafeHtml body) {
+    SafeHtml.set(getBodyElement(myTable), body);
   }
 
   protected static native Element getBodyElement(HTMLTable myTable)
diff --git a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java b/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java
index 2b3eb50..de0c07d 100644
--- a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java
+++ b/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java
@@ -18,10 +18,12 @@
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.ui.HTMLTable;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
 
 public class FancyFlexTableImplIE6 extends FancyFlexTableImpl {
   @Override
-  public void resetHtml(final MyFlexTable myTable, final String bodyHtml) {
+  public void resetHtml(final MyFlexTable myTable, final SafeHtml bodyHtml) {
     final Element oldBody = getBodyElement(myTable);
     final Element newBody = parseBody(bodyHtml);
     assert newBody != null;
@@ -32,11 +34,13 @@
     DOM.appendChild(tableElem, newBody);
   }
 
-  private static Element parseBody(final String body) {
-    final Element div = DOM.createDiv();
-    DOM.setInnerHTML(div, "<table>" + body + "</table>");
+  private static Element parseBody(final SafeHtml body) {
+    final SafeHtmlBuilder b = new SafeHtmlBuilder();
+    b.openElement("table");
+    b.append(body);
+    b.closeElement("table");
 
-    final Element newTable = DOM.getChild(div, 0);
+    final Element newTable = SafeHtml.parse(b);
     for (Element e = DOM.getFirstChild(newTable); e != null; e =
         DOM.getNextSibling(e)) {
       if ("tbody".equals(e.getTagName().toLowerCase())) {
diff --git a/src/main/java/com/google/gerrit/public/gerrit1.cache.css b/src/main/java/com/google/gerrit/public/gerrit2.cache.css
similarity index 99%
rename from src/main/java/com/google/gerrit/public/gerrit1.cache.css
rename to src/main/java/com/google/gerrit/public/gerrit2.cache.css
index 59d1f4f..69512fe 100644
--- a/src/main/java/com/google/gerrit/public/gerrit1.cache.css
+++ b/src/main/java/com/google/gerrit/public/gerrit2.cache.css
@@ -57,12 +57,6 @@
   background: red;
 }
 
-.gerrit-preformat {
-  white-space: pre;
-  font-family: monospace;
-  font-size: small;
-}
-
 .gerrit-InputFieldTypeHint {
   color: grey;
 }
@@ -185,6 +179,7 @@
   padding-right: 5px;
   border-right: 1px solid #d4e9a9;
   border-bottom: 1px solid #d4e9a9;
+  vertical-align: top;
 }
 
 .gerrit-ChangeTable .CommentCell {