ArpitB

Bug 1563226 - Download the Public Suffix List using Remote S

Aug 16th, 2019
133
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 10.26 KB | None | 0 0
  1. diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
  2. --- a/browser/components/BrowserGlue.jsm
  3. +++ b/browser/components/BrowserGlue.jsm
  4. @@ -481,6 +481,7 @@ XPCOMUtils.defineLazyModuleGetters(this,
  5.    PluralForm: "resource://gre/modules/PluralForm.jsm",
  6.    PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
  7.    ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
  8. +  PublicSuffixList: "resource://gre/modules/netwerk-dns/PublicSuffixList.jsm",
  9.    RemoteSettings: "resource://services-settings/remote-settings.js",
  10.    RemoteSecuritySettings: "resource://gre/modules/psm/RemoteSecuritySettings.jsm",
  11.    RFPHelper: "resource://gre/modules/RFPHelper.jsm",
  12. @@ -1856,6 +1857,10 @@ BrowserGlue.prototype = {
  13.      });
  14.  
  15.      Services.tm.idleDispatchToMainThread(() => {
  16. +      PublicSuffixList.init();
  17. +    });
  18. +
  19. +    Services.tm.idleDispatchToMainThread(() => {
  20.        RemoteSecuritySettings.init();
  21.      });
  22.    },
  23. diff --git a/netwerk/dns/PublicSuffixList.jsm b/netwerk/dns/PublicSuffixList.jsm
  24. new file mode 100644
  25. --- /dev/null
  26. +++ b/netwerk/dns/PublicSuffixList.jsm
  27. @@ -0,0 +1,83 @@
  28. +/* This Source Code Form is subject to the terms of the Mozilla Public
  29. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  30. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  31. +"use strict";
  32. +
  33. +const { RemoteSettings } = ChromeUtils.import("resource://services-settings/remote-settings.js");
  34. +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
  35. +const FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm").FileUtils;
  36. +
  37. +const EXPORTED_SYMBOLS = ["PublicSuffixList"];
  38. +
  39. +const RECORD_ID = "tld-dafsa";
  40. +const SIGNAL = "public-suffix-list-updated";
  41. +
  42. +const PublicSuffixList = {
  43. +  CLIENT: RemoteSettings("public-suffix-list"),
  44. +
  45. +  init() {
  46. +    this.CLIENT.on("sync", this.onUpdate.bind(this));
  47. +    /* We have a single record for this collection. Let's see if we already have it locally.
  48. +     * Note that on startup, we don't need to synchronize immediately on new profiles.
  49. +     */
  50. +    this.CLIENT.get({ syncIfEmpty: false, filters: { id: RECORD_ID } })
  51. +      .then(async records => {
  52. +        if (records.length == 1) {
  53. +          const fileURI = await this.CLIENT.attachments.download(records[0]);
  54. +          // Send a signal so that the C++ code loads the updated list on startup.
  55. +          this.notifyUpdate(fileURI);
  56. +        }
  57. +      })
  58. +      .catch(err => console.error(err));
  59. +  },
  60. +
  61. +  /*
  62. +   * This method returns the path to the file based on the file uri received
  63. +   * Example:
  64. +   * On windows "file://C:/Users/AppData/main/public-suffix-list/dafsa.bin"
  65. +   * will be converted to "C:\\Users\\main\\public-suffix-list\\dafsa.bin
  66. +   *
  67. +   * On macOS/linux "file:///home/main/public-suffix-list/dafsa.bin"
  68. +   * will be converted to "/home/main/public-suffix-list/dafsa.bin"
  69. +   */
  70. +  getFilePath(fileURI) {
  71. +    const uri = Services.io.newURI(fileURI);
  72. +    const file = uri.QueryInterface(Ci.nsIFileURL).file;
  73. +    return file.path;
  74. +  },
  75. +
  76. +  notifyUpdate(fileURI) {
  77. +    const filePath = this.getFilePath(fileURI);
  78. +    const nsifile = new FileUtils.File(filePath);
  79. +    /*
  80. +     * Send a signal to be read by the C++, the method
  81. +     * ::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
  82. +     * at netwerk/dns/nsEffectiveTLDService.cpp
  83. +     */
  84. +    Services.obs.notifyObservers(
  85. +      nsifile, // aSubject
  86. +      SIGNAL,  // aTopic
  87. +      filePath // aData
  88. +    );
  89. +  },
  90. +
  91. +  async onUpdate({ data: { created, updated, deleted } }) {
  92. +    // In theory, this will never happen, we will never delete the record.
  93. +    if (deleted.length == 1) {
  94. +      await this.CLIENT.attachments.delete(deleted[0]);
  95. +    }
  96. +    // Handle creation and update the same way
  97. +    const changed = created.concat(updated.map(u => u.new));
  98. +    /* In theory, we should never have more than one record. And if we receive
  99. +     * this event, it's because the single record was updated.
  100. +     */
  101. +    if (changed.length != 1) {
  102. +      console.warn("Unsupported sync event for Public Suffix List");
  103. +      return;
  104. +    }
  105. +    // Download the updated file.
  106. +    const fileURI = await this.CLIENT.attachments.download(changed[0]);
  107. +    // Notify the C++ part to reload it from disk.
  108. +    this.notifyUpdate(fileURI);
  109. +  },
  110. +};
  111. diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build
  112. --- a/netwerk/dns/moz.build
  113. +++ b/netwerk/dns/moz.build
  114. @@ -23,6 +23,12 @@ XPIDL_SOURCES += [
  115.  
  116.  XPIDL_MODULE = 'necko_dns'
  117.  
  118. +EXTRA_JS_MODULES['netwerk-dns'] += [
  119. +    'PublicSuffixList.jsm',
  120. +]
  121. +
  122. +XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
  123. +
  124.  EXPORTS += [
  125.      'nsEffectiveTLDService.h',
  126.  ]
  127. diff --git a/netwerk/dns/tests/unit/data/dafsa_xpcShellTest.bin b/netwerk/dns/tests/unit/data/dafsa_xpcShellTest.bin
  128. new file mode 100644
  129. --- /dev/null
  130. +++ b/netwerk/dns/tests/unit/data/dafsa_xpcShellTest.bin
  131. @@ -0,0 +1,1 @@
  132. +      
  133.        �barrea��ass��or��mil�%�ne��g��uv.b����ed��c��c��m��b��a��website.xpcshelltest�
  134. \ No newline at end of file
  135. diff --git a/netwerk/dns/tests/unit/test_PublicSuffixList.js b/netwerk/dns/tests/unit/test_PublicSuffixList.js
  136. new file mode 100644
  137. --- /dev/null
  138. +++ b/netwerk/dns/tests/unit/test_PublicSuffixList.js
  139. @@ -0,0 +1,164 @@
  140. +"use strict";
  141. +
  142. +const { PublicSuffixList } = ChromeUtils.import("resource://gre/modules/netwerk-dns/PublicSuffixList.jsm");
  143. +const { TestUtils } = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
  144. +
  145. +const CLIENT = PublicSuffixList.CLIENT;
  146. +const SIGNAL = "public-suffix-list-updated";
  147. +
  148. +const PAYLOAD_UPDATED_RECORDS = {
  149. +  current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }],
  150. +  created: [],
  151. +  updated: [
  152. +    {
  153. +      old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" },
  154. +      new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" },
  155. +    },
  156. +  ],
  157. +  deleted: [],
  158. +};
  159. +const PAYLOAD_CREATED_RECORDS = {
  160. +  current: [],
  161. +  created: [
  162. +    {
  163. +      id: "tld-dafsa",
  164. +      "commit-hash": "new-commit-hash",
  165. +      attachment: {},
  166. +    },
  167. +  ],
  168. +  updated: [],
  169. +  deleted: [],
  170. +};
  171. +const PAYLOAD_UPDATED_AND_CREATED_RECORDS = {
  172. +  current: [{ id: "tld-dafsa", "commit-hash": "current-commit-hash" }],
  173. +  created: [{ id: "tld-dafsa", "commit-hash": "another-commit-hash" }],
  174. +  updated: [
  175. +    {
  176. +      old: { id: "tld-dafsa", "commit-hash": "current-commit-hash" },
  177. +      new: { id: "tld-dafsa", "commit-hash": "new-commit-hash" },
  178. +    },
  179. +  ],
  180. +  deleted: [],
  181. +};
  182. +
  183. +const fakeDafsaBinFile = do_get_file("data/dafsa_xpcShellTest.bin");
  184. +const mockedFilePath = fakeDafsaBinFile.path;
  185. +
  186. +/*
  187. + * downloadCalled is used by mockDownload() and resetMockDownload()
  188. + * to keep track weather CLIENT.attachments.download is called or not
  189. + * downloadBackup will help restore CLIENT.attachments.download to original definition
  190. + * notifyUpdateBackup will help restore PublicSuffixList.notifyUpdate to original definition
  191. + */
  192. +let downloadCalled = false;
  193. +const downloadBackup = CLIENT.attachments.download;
  194. +
  195. +// returns a fake fileURI and sends a signal with filePath and no nsifile
  196. +const mockDownload = () => {
  197. +  downloadCalled = false;
  198. +  CLIENT.attachments.download = async rec => {
  199. +    downloadCalled = true;
  200. +    return `file://${mockedFilePath}`; // Create a fake file URI
  201. +  };
  202. +};
  203. +
  204. +// resetMockDownload() must be run at the end of the test that uses mockDownload()
  205. +const resetMockDownload = () => {
  206. +  CLIENT.attachments.download = downloadBackup;
  207. +};
  208. +
  209. +add_task(async () => {
  210. +  info("File path sent when record is in DB.");
  211. +
  212. +  // mockDownloading a local record
  213. +  const collection = await CLIENT.openCollection();
  214. +  await collection.clear(); // Make sure there's no record initially
  215. +  await collection.create(
  216. +    {
  217. +      id: "tld-dafsa",
  218. +      "commit-hash": "fake-commit-hash",
  219. +      attachment: {},
  220. +    },
  221. +    { synced: true }
  222. +  );
  223. +
  224. +  mockDownload();
  225. +
  226. +  const promiseSignal = TestUtils.topicObserved(SIGNAL);
  227. +  await PublicSuffixList.init();
  228. +  const observed = await promiseSignal;
  229. +
  230. +  Assert.equal(
  231. +    observed[1],
  232. +    mockedFilePath,
  233. +    "File path sent when record is in DB."
  234. +  );
  235. +  await collection.clear(); // Clean up the mockDownloaded record
  236. +  resetMockDownload();
  237. +});
  238. +
  239. +add_task(async () => {
  240. +  info("File path sent when record updated.");
  241. +
  242. +  mockDownload();
  243. +
  244. +  const promiseSignal = TestUtils.topicObserved(SIGNAL);
  245. +  await PublicSuffixList.init();
  246. +  await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS });
  247. +  const observed = await promiseSignal;
  248. +
  249. +  Assert.equal(
  250. +    observed[1],
  251. +    mockedFilePath,
  252. +    "File path sent when record updated."
  253. +  );
  254. +  resetMockDownload();
  255. +});
  256. +
  257. +add_task(async () => {
  258. +  info("Attachment downloaded when record created.");
  259. +
  260. +  mockDownload();
  261. +
  262. +  await PublicSuffixList.init();
  263. +  await CLIENT.emit("sync", { data: PAYLOAD_CREATED_RECORDS });
  264. +
  265. +  Assert.equal(
  266. +    downloadCalled,
  267. +    true,
  268. +    "Attachment downloaded when record created."
  269. +  );
  270. +  resetMockDownload();
  271. +});
  272. +
  273. +add_task(async () => {
  274. +  info("Attachment downloaded when record updated.");
  275. +
  276. +  mockDownload();
  277. +
  278. +  await PublicSuffixList.init();
  279. +  await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS });
  280. +
  281. +  Assert.equal(
  282. +    downloadCalled,
  283. +    true,
  284. +    "Attachment downloaded when record updated."
  285. +  );
  286. +  resetMockDownload();
  287. +});
  288. +
  289. +add_task(async () => {
  290. +  info("No download when more than one record is changed.");
  291. +
  292. +  mockDownload();
  293. +
  294. +  await PublicSuffixList.init();
  295. +  await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_AND_CREATED_RECORDS });
  296. +
  297. +  Assert.equal(
  298. +    downloadCalled,
  299. +    false,
  300. +    "No download when more than one record is changed."
  301. +  );
  302. +  resetMockDownload();
  303. +});
  304. diff --git a/netwerk/dns/tests/unit/xpcshell.ini b/netwerk/dns/tests/unit/xpcshell.ini
  305. new file mode 100644
  306. --- /dev/null
  307. +++ b/netwerk/dns/tests/unit/xpcshell.ini
  308. @@ -0,0 +1,6 @@
  309. +[DEFAULT]
  310. +head = ../../../../services/common/tests/unit/head_global.js ../../../../services/common/tests/unit/head_helpers.js
  311. +firefox-appdir = browser
  312. +support-files = data/**
  313. +
  314. +[test_PublicSuffixList.js]
Add Comment
Please, Sign In to add comment