Bug 1595328 - Add browser.experiments.urlbar.lastBrowserUpdateDate webextension experiment API. r=mak,mixedpuppy
☠☠ backed out by cdd42a287972 ☠ ☠
authorDrew Willcoxon <adw@mozilla.com>
Thu, 14 Nov 2019 20:00:46 +0000
changeset 502032 289a3851e8c71110ffcb64425fef17a8cce04af9
parent 502031 aa9499a196e3aca565d76ad4a65f16ef9377f3d0
child 502033 96b4cb315770c2037cb2cee9c7ffd76ef252ed2b
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, mixedpuppy
bugs1595328
milestone72.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 1595328 - Add browser.experiments.urlbar.lastBrowserUpdateDate webextension experiment API. r=mak,mixedpuppy Differential Revision: https://phabricator.services.mozilla.com/D52948
browser/components/urlbar/tests/ext/api.js
browser/components/urlbar/tests/ext/browser/browser.ini
browser/components/urlbar/tests/ext/browser/browser_ext_urlbar_lastBrowserUpdateDate.js
browser/components/urlbar/tests/ext/schema.json
--- a/browser/components/urlbar/tests/ext/api.js
+++ b/browser/components/urlbar/tests/ext/api.js
@@ -1,18 +1,20 @@
 /* global ExtensionAPI */
 
 "use strict";
 
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
+
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  ProfileAge: "resource://gre/modules/ProfileAge.jsm",
   Services: "resource://gre/modules/Services.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "updateService",
   "@mozilla.org/updates/update-service;1",
   "nsIApplicationUpdateService"
@@ -101,13 +103,26 @@ this.experiments_urlbar = class extends 
               return true;
             }
             // If the state is pending and there is an error, staging failed and
             // Firefox can be restarted to apply the update without staging.
             let update = updateManager.activeUpdate;
             let errorCode = update ? update.errorCode : 0;
             return updateStateIs("pending") && errorCode != 0;
           },
+
+          async lastBrowserUpdateDate() {
+            // Get the newest update in the update history.  This isn't perfect
+            // because these dates are when updates are applied, not when the
+            // user restarts with the update.  See bug 1595328.
+            if (updateManager.updateCount) {
+              let update = updateManager.getUpdateAt(0);
+              return update.installDate;
+            }
+            // Fall back to the profile age.
+            let age = await ProfileAge();
+            return (await age.firstUse) || age.created;
+          },
         },
       },
     };
   }
 };
--- a/browser/components/urlbar/tests/ext/browser/browser.ini
+++ b/browser/components/urlbar/tests/ext/browser/browser.ini
@@ -6,8 +6,9 @@
 support-files =
   ../../browser/head-common.js
   ../api.js
   ../schema.json
   head.js
 
 [browser_ext_urlbar_isBrowserShowingNotification.js]
 [browser_ext_urlbar_isBrowserUpdateReadyToInstall.js]
+[browser_ext_urlbar_lastBrowserUpdateDate.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/tests/ext/browser/browser_ext_urlbar_lastBrowserUpdateDate.js
@@ -0,0 +1,131 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* global browser */
+
+// This tests the browser.experiments.urlbar.lastBrowserUpdateDate WebExtension
+// Experiment API.  The parts related to the updates.xml file are adapted from
+// browser_policy_override_postupdatepage.js and other similar tests.
+
+"use strict";
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  ProfileAge: "resource://gre/modules/ProfileAge.jsm",
+});
+
+XPCOMUtils.defineLazyServiceGetter(
+  this,
+  "updateManager",
+  "@mozilla.org/updates/update-manager;1",
+  "nsIUpdateManager"
+);
+
+const DATE_MS = 1368255600000;
+
+add_task(async function setUp() {
+  let originalTimes = (await ProfileAge())._times;
+  registerCleanupFunction(async () => {
+    removeUpdatesFile();
+    let age = await ProfileAge();
+    age._times = originalTimes;
+    await age.writeTimes();
+  });
+});
+
+// no update history, same profile created and first-use date
+add_task(async function noHistory_sameCreateAndFirstUse() {
+  removeUpdatesFile();
+  reloadUpdateManagerData();
+  Assert.equal(updateManager.updateCount, 0);
+  await updateProfileAge(DATE_MS, DATE_MS);
+  await checkExtension(DATE_MS);
+});
+
+// no update history, different profile created and first-use dates
+add_task(async function noHistory_differentCreateAndFirstUse() {
+  removeUpdatesFile();
+  reloadUpdateManagerData();
+  Assert.equal(updateManager.updateCount, 0);
+  await updateProfileAge(DATE_MS - 30000, DATE_MS);
+  await checkExtension(DATE_MS);
+});
+
+// update history
+add_task(async function history() {
+  removeUpdatesFile();
+  writeUpdatesFile(DATE_MS);
+  reloadUpdateManagerData();
+  Assert.equal(updateManager.updateCount, 1);
+  await updateProfileAge(DATE_MS - 60000, DATE_MS - 30000);
+  await checkExtension(DATE_MS);
+});
+
+async function checkExtension(expectedDate) {
+  let ext = await loadExtension(async () => {
+    let date = await browser.experiments.urlbar.lastBrowserUpdateDate();
+    browser.test.sendMessage("date", date);
+  });
+  let date = await ext.awaitMessage("date");
+  Assert.equal(date, expectedDate);
+  await ext.unload();
+}
+
+function getUpdatesFile() {
+  let updateRootDir = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
+  let updatesFile = updateRootDir.clone();
+  updatesFile.append("updates.xml");
+  return updatesFile;
+}
+
+function removeUpdatesFile() {
+  try {
+    getUpdatesFile().remove(false);
+  } catch (e) {}
+}
+
+function reloadUpdateManagerData() {
+  updateManager
+    .QueryInterface(Ci.nsIObserver)
+    .observe(null, "um-reload-update-data", "");
+}
+
+function getUpdatesXML(installDate) {
+  return `<?xml version="1.0"?>
+  <updates xmlns="http://www.mozilla.org/2005/app-update">
+    <update appVersion="1.0" buildID="20080811053724" channel="nightly"
+            displayVersion="Version 1.0" installDate="${installDate}"
+            isCompleteUpdate="true" name="Update Test 1.0" type="minor"
+            detailsURL="http://example.com/" previousAppVersion="1.0"
+            serviceURL="https://example.com/"
+            statusText="The Update was successfully installed"
+            foregroundDownload="true">
+      <patch type="complete" URL="http://example.com/" size="775"
+             selected="true" state="succeeded"/>
+    </update>
+  </updates>`;
+}
+
+function writeUpdatesFile(installDate) {
+  const PERMS_FILE = 0o644;
+  const MODE_WRONLY = 0x02;
+  const MODE_CREATE = 0x08;
+  const MODE_TRUNCATE = 0x20;
+  let file = getUpdatesFile();
+  if (!file.exists()) {
+    file.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+  }
+  let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
+    Ci.nsIFileOutputStream
+  );
+  let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+  fos.init(file, flags, PERMS_FILE, 0);
+  let xml = getUpdatesXML(installDate);
+  fos.write(xml, xml.length);
+  fos.close();
+}
+
+async function updateProfileAge(created, firstUse) {
+  let age = await ProfileAge();
+  age._times = { created, firstUse };
+  await age.writeTimes();
+}
--- a/browser/components/urlbar/tests/ext/schema.json
+++ b/browser/components/urlbar/tests/ext/schema.json
@@ -11,12 +11,19 @@
         "parameters": []
       },
       {
         "name": "isBrowserUpdateReadyToInstall",
         "type": "function",
         "async": true,
         "description": "Returns true if there is an update ready to install.",
         "parameters": []
+      },
+      {
+        "name": "lastBrowserUpdateDate",
+        "type": "function",
+        "async": true,
+        "description": "Returns the date of the last browser update. If there's no update history, then the date the profile was first used is returned instead. The return value is milliseconds since 1 January 1970 UTC (i.e., suitable for passing to <code>new Date()</code>).",
+        "parameters": []
       }
     ]
   }
 ]