Bug 1366420. r=standard8, a=ritu
authorMarco Bonardo <mbonardo@mozilla.com>
Wed, 04 Oct 2017 11:13:19 +0200
changeset 432317 c4235f5a700c
parent 432316 c953cae869d8
child 432318 83232ecf718c
push id7929
push userryanvm@gmail.com
push date2017-10-09 18:47 +0000
treeherdermozilla-beta@c4235f5a700c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersstandard8, ritu
bugs1366420
milestone57.0
Bug 1366420. r=standard8, a=ritu MozReview-Commit-ID: FOIqr5RdRjz
toolkit/components/places/BookmarkHTMLUtils.jsm
toolkit/components/places/tests/unit/test_bookmarks_html_escape_entities.js
toolkit/components/places/tests/unit/xpcshell.ini
--- a/toolkit/components/places/BookmarkHTMLUtils.jsm
+++ b/toolkit/components/places/BookmarkHTMLUtils.jsm
@@ -1138,17 +1138,17 @@ BookmarkExporter.prototype = {
         this._writeAttribute("POST_DATA", escapeHtmlEntities(aItem.postData));
     }
 
     if (aItem.annos && aItem.annos.some(anno => anno.name == LOAD_IN_SIDEBAR_ANNO))
       this._writeAttribute("WEB_PANEL", "true");
     if (aItem.charset)
       this._writeAttribute("LAST_CHARSET", escapeHtmlEntities(aItem.charset));
     if (aItem.tags)
-      this._writeAttribute("TAGS", aItem.tags);
+      this._writeAttribute("TAGS", escapeHtmlEntities(aItem.tags));
     this._writeLine(">" + escapeHtmlEntities(aItem.title) + "</A>");
     this._writeDescription(aItem, aIndent);
   },
 
   _writeDateAttributes(aItem) {
     if (aItem.dateAdded)
       this._writeAttribute("ADD_DATE",
                            Math.floor(aItem.dateAdded / MICROSEC_PER_SEC));
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/unit/test_bookmarks_html_escape_entities.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Checks that html entities are escaped in bookmarks.html files.
+
+const DESCRIPTION_ANNO = "bookmarkProperties/description";
+
+add_task(async function() {
+  // Removes bookmarks.html if the file already exists.
+  let HTMLFile = OS.Path.join(OS.Constants.Path.profileDir, "bookmarks.html");
+  if ((await OS.File.exists(HTMLFile))) {
+    await OS.File.remove(HTMLFile);
+  }
+
+  let unescaped = '<unescaped="test">';
+  // Adds bookmarks and tags to the database.
+  const url = 'http://www.google.it/"/';
+  let bm = await PlacesUtils.bookmarks.insert({
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+    url,
+    title: unescaped
+  });
+  await PlacesUtils.keywords.insert({ url, keyword: unescaped, postData: unescaped })
+  let uri = Services.io.newURI(url);
+  PlacesUtils.tagging.tagURI(uri, [unescaped]);
+  await PlacesUtils.setCharsetForURI(uri, unescaped);
+  PlacesUtils.annotations.setItemAnnotation(
+    await PlacesUtils.promiseItemId(bm.guid),
+    DESCRIPTION_ANNO, unescaped, 0, PlacesUtils.annotations.EXPIRE_NEVER);
+
+  // Exports the bookmarks as a HTML file.
+  await BookmarkHTMLUtils.exportToFile(HTMLFile);
+  await PlacesUtils.bookmarks.remove(bm);
+
+  // Check there are no unescaped entities in the html file.
+  let xml = await new Promise((resolve, reject) => {
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+                .createInstance(Ci.nsIXMLHttpRequest);
+    xhr.onload = () => {
+      try {
+        resolve(xhr.responseXML);
+      } catch (e) {
+        reject(e);
+      }
+    };
+    xhr.onabort = xhr.onerror = xhr.ontimeout = () => {
+      reject(new Error("xmlhttprequest failed"));
+    };
+    xhr.open("GET", OS.Path.toFileURI(HTMLFile));
+    xhr.responseType = "document";
+    xhr.overrideMimeType("text/html");
+    xhr.send();
+  });
+
+  let checksCount = 6;
+  for (let current = xml; current;
+    current = current.firstChild || current.nextSibling || current.parentNode.nextSibling) {
+    switch (current.nodeType) {
+      case Ci.nsIDOMNode.ELEMENT_NODE:
+        for (let {name, value} of current.attributes) {
+          do_print("Found attribute: " + name);
+          // Check tags, keyword, postData and charSet.
+          if (["tags", "last_charset", "shortcuturl", "post_data"].includes(name)) {
+            Assert.equal(value, unescaped, `Attribute ${name} should be complete`);
+            checksCount--;
+          }
+        }
+        break;
+      case Ci.nsIDOMNode.TEXT_NODE:
+        // Check Title and description.
+        if (!current.data.startsWith("\n") && !current.data.includes("Bookmarks")) {
+          Assert.equal(current.data.trim(), unescaped, "Text node should be complete");
+          checksCount--;
+        }
+        break;
+    }
+  }
+  Assert.equal(checksCount, 0, "All the checks ran")
+});
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -62,16 +62,17 @@ skip-if = os == "linux"
 [test_async_in_batchmode.js]
 [test_async_transactions.js]
 skip-if = (os == "win" && os_version == "5.1") # Bug 1158887
 [test_autocomplete_stopSearch_no_throw.js]
 [test_bookmark_catobs.js]
 [test_bookmarks_json.js]
 [test_bookmarks_html.js]
 [test_bookmarks_html_corrupt.js]
+[test_bookmarks_html_escape_entities.js]
 [test_bookmarks_html_import_tags.js]
 [test_bookmarks_html_singleframe.js]
 [test_bookmarks_restore_notification.js]
 [test_bookmarks_setNullTitle.js]
 [test_broken_folderShortcut_result.js]
 [test_browserhistory.js]
 [test_bug636917_isLivemark.js]
 [test_childlessTags.js]