Bug 436801 - Support XHTML in feed titles. r=sayrer,adw
authorNicholas Wilson <ncw33@cam.ac.uk>
Fri, 26 Apr 2013 23:57:49 -0700
changeset 130133 e3126e00ec9184c4d53217e0963e20bdcdcceff4
parent 130132 1421ddeb0348eca08d849a14266ef90c781e51a9
child 130134 28e2e2f4366c64fa3d5a2239487c80b25247492b
push id24599
push userryanvm@gmail.com
push dateSun, 28 Apr 2013 01:24:06 +0000
treeherdermozilla-central@9d8977cbbfc6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssayrer, adw
bugs436801
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 436801 - Support XHTML in feed titles. r=sayrer,adw
browser/components/feeds/src/FeedWriter.js
browser/components/feeds/test/Makefile.in
browser/components/feeds/test/bug436801-data.xml
browser/components/feeds/test/test_bug436801.html
--- a/browser/components/feeds/src/FeedWriter.js
+++ b/browser/components/feeds/src/FeedWriter.js
@@ -39,17 +39,17 @@ function makeURI(aURLSpec, aCharset) {
             getService(Ci.nsIIOService);
   try {
     return ios.newURI(aURLSpec, aCharset, null);
   } catch (ex) { }
 
   return null;
 }
 
-const XML_NS = "http://www.w3.org/XML/1998/namespace"
+const XML_NS = "http://www.w3.org/XML/1998/namespace";
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
 const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
 const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
 const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties";
 
 const PREF_SELECTED_APP = "browser.feeds.handlers.application";
@@ -164,21 +164,25 @@ FeedWriter.prototype = {
     }
     catch (e) {
     }
     return "";
   },
 
   _setContentText: function FW__setContentText(id, text) {
     this._contentSandbox.element = this._document.getElementById(id);
-    this._contentSandbox.textNode = this._document.createTextNode(text);
+    this._contentSandbox.textNode = text.createDocumentFragment(this._contentSandbox.element);
     var codeStr =
       "while (element.hasChildNodes()) " +
       "  element.removeChild(element.firstChild);" +
       "element.appendChild(textNode);";
+    if (text.base) {
+      this._contentSandbox.spec = text.base.spec;
+      codeStr += "element.setAttributeNS('" + XML_NS + "', 'base', spec);";
+    }
     Cu.evalInSandbox(codeStr, this._contentSandbox);
     this._contentSandbox.element = null;
     this._contentSandbox.textNode = null;
   },
 
   /**
    * Safely sets the href attribute on an anchor tag, providing the URI 
    * specified can be loaded according to rules. 
@@ -356,26 +360,26 @@ FeedWriter.prototype = {
   /**
    * Writes the feed title into the preview document.
    * @param   container
    *          The feed container
    */
   _setTitleText: function FW__setTitleText(container) {
     if (container.title) {
       var title = container.title.plainText();
-      this._setContentText(TITLE_ID, title);
+      this._setContentText(TITLE_ID, container.title);
       this._contentSandbox.document = this._document;
       this._contentSandbox.title = title;
       var codeStr = "document.title = title;"
       Cu.evalInSandbox(codeStr, this._contentSandbox);
     }
 
     var feed = container.QueryInterface(Ci.nsIFeed);
     if (feed && feed.subtitle)
-      this._setContentText(SUBTITLE_ID, container.subtitle.plainText());
+      this._setContentText(SUBTITLE_ID, container.subtitle);
   },
 
   /**
    * Writes the title image into the preview document if one is present.
    * @param   container
    *          The feed container
    */
   _setTitleImage: function FW__setTitleImage(container) {
@@ -434,17 +438,21 @@ FeedWriter.prototype = {
       entry.QueryInterface(Ci.nsIFeedContainer);
 
       var entryContainer = this._document.createElementNS(HTML_NS, "div");
       entryContainer.className = "entry";
 
       // If the entry has a title, make it a link
       if (entry.title) {
         var a = this._document.createElementNS(HTML_NS, "a");
-        a.appendChild(this._document.createTextNode(entry.title.plainText()));
+        var span = this._document.createElementNS(HTML_NS, "span");
+        a.appendChild(span);
+        if (entry.title.base)
+          span.setAttributeNS(XML_NS, "base", entry.title.base.spec);
+        span.appendChild(entry.title.createDocumentFragment(a));
 
         // Entries are not required to have links, so entry.link can be null.
         if (entry.link)
           this._safeSetURIAttribute(a, "href", entry.link.spec);
 
         var title = this._document.createElementNS(HTML_NS, "h3");
         title.appendChild(a);
 
--- a/browser/components/feeds/test/Makefile.in
+++ b/browser/components/feeds/test/Makefile.in
@@ -13,14 +13,16 @@ include $(DEPTH)/config/autoconf.mk
 XPCSHELL_TESTS	= unit
 
 MOCHITEST_FILES =	bug408328-data.xml \
 		bug368464-data.xml \
 		test_bug494328.html \
 		bug494328-data.xml \
 		test_bug589543.html \
 		bug589543-data.xml \
+		test_bug436801.html \
+		bug436801-data.xml \
 		test_registerHandler.html \
 		valid-feed.xml \
 		valid-unsniffable-feed.xml \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/feeds/test/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>
new file mode 100644
--- /dev/null
+++ b/browser/components/feeds/test/test_bug436801.html
@@ -0,0 +1,119 @@
+<!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 () {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var doc = $("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>