Bug 1040910 - Support XHTML in feed titles r=Neil a=IanN CLOSED TREE
authorPhilip Chee <philip.chee@gmail.com>
Tue, 23 Jun 2015 22:15:21 +0800
changeset 22804 cd3b2d6056ab91b53edead971c71205a55ed2cf4
parent 22803 33c0832f86e06bcb95b626f06e1cd12f6029db15
child 22805 4c0a2ce3eed4e4970eccf32754ee2b52dff8024e
push id1443
push usermbanner@mozilla.com
push dateMon, 10 Aug 2015 18:31:17 +0000
treeherdercomm-beta@8fe07d686c22 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeil, IanN
bugs1040910
Bug 1040910 - Support XHTML in feed titles r=Neil a=IanN CLOSED TREE
suite/browser/test/mochitest/bug436801-data.xml
suite/browser/test/mochitest/mochitest.ini
suite/browser/test/mochitest/test_bug436801.html
suite/feeds/src/FeedWriter.js
new file mode 100644
--- /dev/null
+++ b/suite/browser/test/mochitest/bug436801-data.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://www.example.com/">
+
+  <title type="xhtml" xml:base="/foo/bar/">
+    <div xmlns="http://www.w3.org/1999/xhtml">Example of a <em>special</em> feed (<img height="20px" src="baz.png" alt="base test sprite"/>)</div>
+  </title>
+
+  <subtitle type="html" xml:base="/foo/bar/">
+    <![CDATA[
+      With a <em>special</em> subtitle (<img height="20px" src="baz.png" alt="base test sprite"/>)
+    ]]>
+  </subtitle>
+
+  <link href="http://example.org/"/>
+
+  <updated>2010-09-02T18:30:02Z</updated>
+
+  <author>
+    <name>John Doe</name>
+  </author>
+
+  <id>urn:uuid:22906062-ecbd-46e2-b6a7-3039506a398f</id>
+
+  <entry>
+    <title type="xhtml" xml:base="/foo/bar/">
+      <div xmlns="http://www.w3.org/1999/xhtml">Some <abbr title="Extensible Hyper-text Mark-up Language">XHTML</abbr> examples (<img height="20px" src="baz.png" alt="base test sprite"/>)</div>
+    </title>
+    <id>urn:uuid:b48083a7-71a7-4c9c-8515-b7c0d22955e7</id>
+    <updated>2010-09-02T18:30:02Z</updated>
+    <summary>Some text.</summary>
+  </entry>
+
+  <entry>
+    <title type="html" xml:base="/foo/bar/">
+      <![CDATA[
+        Some <abbr title="Hyper-text Mark-up Language">HTML</abbr> examples (<img height="20px" src="baz.png" alt="base test sprite"/>)
+      ]]>
+    </title>
+    <id>urn:uuid:1424967a-280a-414d-b0ab-8b11c4ac1bb7</id>
+    <updated>2010-09-02T18:30:02Z</updated>
+    <summary>Some text.</summary>
+  </entry>
+
+</feed>
--- a/suite/browser/test/mochitest/mochitest.ini
+++ b/suite/browser/test/mochitest/mochitest.ini
@@ -2,14 +2,16 @@
 support-files =
   valid-feed.xml
   valid-unsniffable-feed.xml
 
 [test_bug364677.html]
 support-files = bug364677-data.xml bug364677-data.xml^headers^
 [test_bug395533.html]
 support-files = bug395533-data.txt
+[test_bug436801.html]
+support-files = bug436801-data.xml
 [test_contextmenu.html]
 support-files = audio.ogg ctxmenu-image.png subtst_contextmenu.html video.ogg
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" || (os == 'mac' && os_version != '10.6') # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304
 [test_feed_discovery.html]
 support-files = feed_discovery.html
 [test_registerHandler.html]
new file mode 100644
--- /dev/null
+++ b/suite/browser/test/mochitest/test_bug436801.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=436801
+-->
+<head>
+  <title>Test feed preview subscribe UI</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=436801">Mozilla Bug 436801</a>
+<p id="display"><iframe id="testFrame" src="bug436801-data.xml"></iframe></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function () {
+  var doc = SpecialPowers.wrap($("testFrame")).contentDocument;
+
+  checkNode(doc.getElementById("feedTitleText"), [
+    "ELEMENT", "h1", { "xml:base": "http://www.example.com/foo/bar/" }, [
+      ["TEXT", "Example of a "],
+      ["ELEMENT", "em", [
+        ["TEXT", "special"],
+      ]],
+      ["TEXT", " feed ("],
+      ["ELEMENT", "img", { "src": "baz.png" }],
+      ["TEXT", ")"],
+    ]
+  ]);
+
+  checkNode(doc.getElementById("feedSubtitleText"), [
+    "ELEMENT", "h2", { "xml:base": "http://www.example.com/foo/bar/" }, [
+      ["TEXT", "With a "],
+      ["ELEMENT", "em", [
+        ["TEXT", "special"],
+      ]],
+      ["TEXT", " subtitle ("],
+      ["ELEMENT", "img", { "src": "baz.png" }],
+      ["TEXT", ")"],
+    ]
+  ]);
+
+  checkNode(doc.querySelector(".entry").firstChild.firstChild.firstChild, [
+    "ELEMENT", "span", { "xml:base": "http://www.example.com/foo/bar/" }, [
+      ["TEXT", "Some "],
+      ["ELEMENT", "abbr", { title: "Extensible Hyper-text Mark-up Language" }, [
+        ["TEXT", "XHTML"],
+      ]],
+      ["TEXT", " examples ("],
+      ["ELEMENT", "img", { "src": "baz.png" }],
+      ["TEXT", ")"],
+    ]
+  ]);
+
+  checkNode(doc.querySelectorAll(".entry")[1].firstChild.firstChild.firstChild, [
+    "ELEMENT", "span", { "xml:base": "http://www.example.com/foo/bar/" }, [
+      ["TEXT", "Some "],
+      ["ELEMENT", "abbr", { title: "Hyper-text Mark-up Language" }, [
+        ["TEXT", "HTML"],
+      ]],
+      ["TEXT", " examples ("],
+      ["ELEMENT", "img", { "src": "baz.png" }],
+      ["TEXT", ")"],
+    ]
+  ]);
+});
+
+addLoadEvent(SimpleTest.finish);
+
+function checkNode(node, schema) {
+  var typeName = schema.shift() + "_NODE";
+  var type = Node[typeName];
+  is(node.nodeType, type, "Node should be expected type " + typeName);
+  if (type == Node.TEXT_NODE) {
+    var text = schema.shift();
+    is(node.data, text, "Text should match");
+    return;
+  }
+  // type == Node.ELEMENT_NODE
+  var tag = schema.shift();
+  is(node.localName, tag, "Element should have expected tag");
+  while (schema.length) {
+    var val = schema.shift();
+    if (Array.isArray(val))
+      var childSchema = val;
+    else
+      var attrSchema = val;
+  }
+  if (attrSchema) {
+    var nsTable = {
+      xml: "http://www.w3.org/XML/1998/namespace",
+    };
+    for (var name in attrSchema) {
+      var [ns, nsName] = name.split(":");
+      var val = nsName ? node.getAttributeNS(nsTable[ns], nsName) :
+                node.getAttribute(name);
+      is(val, attrSchema[name], "Attribute " + name + " should match");
+    }
+  }
+  if (childSchema) {
+    var numChildren = node.childNodes.length;
+    is(childSchema.length, numChildren,
+       "Element should have expected number of children");
+    for (var i = 0; i < numChildren; i++)
+      checkNode(node.childNodes[i], childSchema[i]);
+  }
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/suite/feeds/src/FeedWriter.js
+++ b/suite/feeds/src/FeedWriter.js
@@ -162,20 +162,33 @@ FeedWriter.prototype = {
     try {
       return container.fields.getPropertyAsAString(property);
     }
     catch (e) {
     }
     return "";
   },
 
+  /**
+   * @param   element
+   *          The element to add the text content to.
+   * @param   text
+   *          An nsIFeedTextConstruct
+   */
   _setContentText: function setContentText(element, text) {
     if (typeof element == "string")
       element = this._document.getElementById(element);
-    element.textContent = text.plainText();
+
+    // Takes the content of the nsIFeedTextConstruct and creates a
+    // sanitized documentFragment.
+    var docFragment = text.createDocumentFragment(element);
+    element.innerHTML = "";
+    element.appendChild(docFragment);
+    if (text.base)
+      element.setAttributeNS(XML_NS, "base", text.base.spec);
   },
 
   /**
    * Safely sets the href attribute on an anchor tag, providing the URI
    * specified can be loaded according to rules.
    * @param   element
    *          The element to set a URI attribute on
    * @param   attribute
@@ -302,27 +315,27 @@ FeedWriter.prototype = {
       default:
         return TYPE_MAYBE_FEED;
     }
   },
 
   /**
    * Writes the feed title into the preview document.
    * @param   container
-   *          The feed container
+   *          The feed container, an nsIFeedContainer
    */
   _setTitleText: function setTitleText(container) {
     if (container.title) {
       this._setContentText(TITLE_ID, container.title);
       this._document.title = container.title.plainText();
     }
 
     var feed = container.QueryInterface(Components.interfaces.nsIFeed);
     if (feed && feed.subtitle)
-      this._setContentText(SUBTITLE_ID, container.subtitle);
+      this._setContentText(SUBTITLE_ID, feed.subtitle);
   },
 
   /**
    * Writes the title image into the preview document if one is present.
    * @param   container
    *          The feed container
    */
   _setTitleImage: function setTitleImage(container) {
@@ -369,17 +382,19 @@ FeedWriter.prototype = {
       entry.QueryInterface(Components.interfaces.nsIFeedContainer);
 
       let entryContainer = this._document.createElementNS(HTML_NS, "div");
       entryContainer.className = "entry";
 
       // If the entry has a title, make it a link
       if (entry.title) {
         let a = this._document.createElementNS(HTML_NS, "a");
-        this._setContentText(a, entry.title);
+        let span = this._document.createElementNS(HTML_NS, "span");
+        a.appendChild(span);
+        this._setContentText(span, entry.title);
 
         // Entries are not required to have links, so entry.link can be null.
         if (entry.link)
           this._safeSetURIAttribute(a, "href", entry.link.spec);
 
         let title = this._document.createElementNS(HTML_NS, "h3");
         title.appendChild(a);