Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 09 Jun 2017 16:11:41 +0200
changeset 411373 46707f69b3aa27a874881824d782f8e8099c472b
parent 411372 eb644e17a1a425060663da2c2196084ca6fb47de (current diff)
parent 411322 eca8d0ea03af1d2424550a037f714f14c0f7b1be (diff)
child 411374 ae536496bc6d565a03bfcafa96cbe55c704596a0
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone55.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
Merge mozilla-central to mozilla-inbound
--- a/browser/components/migration/360seProfileMigrator.js
+++ b/browser/components/migration/360seProfileMigrator.js
@@ -13,16 +13,18 @@ Cu.import("resource://gre/modules/FileUt
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
                                   "resource://gre/modules/Sqlite.jsm");
 
+Cu.importGlobalProperties(["URL"]);
+
 const kBookmarksFileName = "360sefav.db";
 
 function copyToTempUTF8File(file, charset) {
   let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
                       .createInstance(Ci.nsIFileInputStream);
   inputStream.init(file, -1, -1, 0);
   let inputStr = NetUtil.readInputStreamToString(
     inputStream, inputStream.available(), { charset });
@@ -105,23 +107,18 @@ Bookmarks.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
 
   get exists() {
     return this._file.exists() && this._file.isReadable();
   },
 
   migrate(aCallback) {
     return (async () => {
-      let idToGuid = new Map();
-      let folderGuid = PlacesUtils.bookmarks.toolbarGuid;
-      if (!MigrationUtils.isStartupMigration) {
-        folderGuid =
-          await MigrationUtils.createImportedBookmarksFolder("360se", folderGuid);
-      }
-      idToGuid.set(0, folderGuid);
+      let folderMap = new Map();
+      let toolbarBMs = [];
 
       let connection = await Sqlite.openConnection({
         path: this._file.path
       });
 
       try {
         let rows = await connection.execute(
           `WITH RECURSIVE
@@ -137,49 +134,57 @@ Bookmarks.prototype = {
 
         for (let row of rows) {
           let id = parseInt(row.getResultByName("id"), 10);
           let parent_id = parseInt(row.getResultByName("parent_id"), 10);
           let is_folder = parseInt(row.getResultByName("is_folder"), 10);
           let title = row.getResultByName("title");
           let url = row.getResultByName("url");
 
-          let parentGuid = idToGuid.get(parent_id) || idToGuid.get("fallback");
-          if (!parentGuid) {
-            parentGuid = PlacesUtils.bookmarks.unfiledGuid;
-            if (!MigrationUtils.isStartupMigration) {
-              parentGuid =
-                await MigrationUtils.createImportedBookmarksFolder("360se", parentGuid);
+          let bmToInsert;
+
+          if (is_folder) {
+            bmToInsert = {
+              children: [],
+              title,
+              type: PlacesUtils.bookmarks.TYPE_FOLDER
+            };
+            folderMap.set(id, bmToInsert);
+          } else {
+            try {
+              new URL(url);
+            } catch (ex) {
+              Cu.reportError(`Ignoring ${url} when importing from 360se because of exception: ${ex}`);
+              continue;
             }
-            idToGuid.set("fallback", parentGuid);
+
+            bmToInsert = {
+              title,
+              url
+            };
           }
 
-          try {
-            if (is_folder == 1) {
-              let newFolderGuid = (await MigrationUtils.insertBookmarkWrapper({
-                parentGuid,
-                type: PlacesUtils.bookmarks.TYPE_FOLDER,
-                title
-              })).guid;
-
-              idToGuid.set(id, newFolderGuid);
-            } else {
-              await MigrationUtils.insertBookmarkWrapper({
-                parentGuid,
-                url,
-                title
-              });
-            }
-          } catch (ex) {
-            Cu.reportError(ex);
+          if (folderMap.has(parent_id)) {
+            folderMap.get(parent_id).children.push(bmToInsert);
+          } else if (parent_id === 0) {
+            toolbarBMs.push(bmToInsert);
           }
         }
       } finally {
         await connection.close();
       }
+
+      if (toolbarBMs.length) {
+        let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
+        if (!MigrationUtils.isStartupMigration) {
+          parentGuid =
+            await MigrationUtils.createImportedBookmarksFolder("360se", parentGuid);
+        }
+        await MigrationUtils.insertManyBookmarksWrapper(toolbarBMs, parentGuid);
+      }
     })().then(() => aCallback(true),
                         e => { Cu.reportError(e); aCallback(false) });
   }
 };
 
 function Qihoo360seProfileMigrator() {
   let paths = [
     // for v6 and above
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/AppData/Roaming/360se6/apps/data/users/0f3ab103a522f4463ecacc36d34eb996/360sefav.db
@@ -0,0 +1,1 @@
+Placeholder file to satisfy the resource existence check, not a real SQLite db.
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1835c33583b3f1c3ce21302e9cdccf30175d1733
GIT binary patch
literal 6144
zc%1E6Z%i9y7=Pcs*D|UoSxgX#@yJ5fO8*@NHvP1Yb%e=agJMQ1sYiPqmt9-dJ3ta+
zgaKn?4v34}0JDipK*_MUxilN|Pl8{z81YYxAB-Oi*J~3qH6bxs{NVfEwNM~N<CmG)
zU)txn_j%su_dM^PdwbXB!}Ywx(ZNVG#7eXiPyj-(ho%7l+rdU4{DoO<2>u}o&>uh#
z4*=vsU%*v3jV~WUyr?%6l^4SvJ{;g)>13l^Sn}}!pW2k#eL=R{P}AggdE9he!#?*B
zdQ%s=v4PgQnbthCtGUK}h{C+(uvKE)1x`FJ;N|j(-0?0h?BCqfrxSQwd+XiwCZ{RP
zhBymunfni*yrSY_I1o`ewXB+?HZXWKV;gPq@_{y7x5r)UZlVu0)g5#-wbC!RTWME|
zr?IX9M;&xGcr4zv(%Wc0EYSfj$aV=5?XY-x(HD#e0WR7`OMJMOc>Num|Cnjbfg@;>
z!@`)&7O%uh0@p@&vr&Hs8#Pr_RbyjUR9H7Lj*3cb>x_t7Qu(7Ciw7?8AxzQEAH{Bq
zS71fS7m5V<VDARcrW)HhQChDs%NONDF51m)9+<f=ALbR6l)yedGnpY?^rw|>%XAVe
zna^Wf@A_D(E6v%HYVmfnLRZ%CnTp-!zpiDUd%vrt-h*qg>I_A$5(v}{m0o_fONyv%
zUpl*wNneornJT~np#^~Mq0h0(A%`6P%h-;nvd3YdUA?h=LWB)4V5g3<GQ_4Ha$;{-
zJ>eK9Q^{0RIh@Yj6&1T27@!bRrD||D7Zv$Pm;pvW?*F?0-9<m3-yZbfa>yZve~Cg=
zw&7erPax}-LqYc#D$P0oAo=|_f}a8U7LB1=RIFRl1>qv>g$y=6WT-Jdg5Pc!J2V*Z
zOAc^WJLLIO^5AUZqtWEduzYd0Ly|h5x7$U|hJ7}F*bd>>Ki-es35*f_8Q<_S0Q>U^
zNtsI}p@4kyihMFA4^6Jz97%_VxgOER2f6T3n?Dk=hj_tmAn_A{9m^RFfNPt|vn$P`
z+#XL{cr(>Mugou`dAJbE3pS?OsnY04==mERzh*Q5eLM3W#T<R2Gfk2hn^H!`k~2f8
z>2d2y{Hi>8J2gEd4-P961In#Md2Cqo;bh9(MN8u9yUMBI)ZB$cd{A@9Q?ts=Pm(kJ
z%G8KOK6^<Xp41#G@zX1dmo0d*aI9t`$*xbQYn8ffVx*%>t&}1ap393bXEXpW8OUH;
zpUX_zmG_i0b5e*E1QKDjDq};56W5e;mlD@+;Z&NRP>Yzmr>DoZ*4K^*>%i%!U(aX&
zn)QVJusy^6@x1)Tgf%rWnmB(0OL;0LkB%#6V~Mw7sSl^t7pjNj+aoHej*z{^p8y#R
zfQ3@mr$E~*P>#oybF<pSsl8u)=b6Hc2A~#^-c{N{K(e*8>G@^xOmk!E?aRs7LiTXP
zthA*q-#aoIfW10GRGvko?NRxI#pLk&8IBG%vSEup_4|%J84bX*kZ_c0rBe`hq?O6I
zJa|p*`{a?YVnl!Q<-(`<-5d`UqE&!DIpmPT{{TjailO$`j*&e78_^O#NwkDk(LHn@
x{e<5HzC+9C&V#Nqha7VFd*~5<QZN!p<*Ra=D)UrnP^Dh&LlFs)Ds`$I{s}{pjCTM4
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/AppData/Roaming/360se6/apps/data/users/login.ini
@@ -0,0 +1,17 @@
+# This file contains Chinese characters encoded in GBK
+[NowLogin]
+NickName=ıǻ
+email=test@firefox.com.cn
+UserMd5=0f3ab103a522f4463ecacc36d34eb996
+IsLogined=1
+# Will be excluded from sourceProfiles due to missing files
+[20070606]
+NickName=ı
+email=test@mozillaonline.com
+UserMd5=46a579b8b64358fd45616247df4ea604
+# Will be excluded from sourceProfiles as duplication of NowLogin
+[20110303]
+NickName=ıǻ
+email=test@firefox.com.cn
+UserMd5=0f3ab103a522f4463ecacc36d34eb996
+# There's also a default profile (not included here) for anonymous users
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_360se_bookmarks.js
@@ -0,0 +1,58 @@
+"use strict";
+
+add_task(async function() {
+  registerFakePath("AppData", do_get_file("AppData/Roaming/"));
+
+  let migrator = MigrationUtils.getMigrator("360se");
+  // Sanity check for the source.
+  Assert.ok(migrator.sourceExists);
+
+  let profiles = migrator.sourceProfiles;
+  Assert.equal(profiles.length, 2, "Should present two profiles");
+  Assert.equal(profiles[0].name, "test@firefox.com.cn", "Current logged in user should be the first");
+  Assert.equal(profiles[profiles.length - 1].name, "Default", "Default user should be the last");
+
+  // Wait for the imported bookmarks.  Check that "From 360 Secure Browser"
+  // folders are created on the toolbar.
+  let source = MigrationUtils.getLocalizedString("sourceName360se");
+  let label = MigrationUtils.getLocalizedString("importedBookmarksFolder", [source]);
+
+  let expectedParents = [ PlacesUtils.toolbarFolderId ];
+  let itemCount = 0;
+
+  let gotFolder = false;
+  let bmObserver = {
+    onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
+      if (aTitle != label) {
+        itemCount++;
+      }
+      if (aItemType == PlacesUtils.bookmarks.TYPE_FOLDER && aTitle == "360 \u76f8\u5173") {
+        gotFolder = true;
+      }
+      if (expectedParents.length > 0 && aTitle == label) {
+        let index = expectedParents.indexOf(aParentId);
+        Assert.ok(index != -1, "Found expected parent");
+        expectedParents.splice(index, 1);
+      }
+    },
+    onBeginUpdateBatch() {},
+    onEndUpdateBatch() {},
+    onItemRemoved() {},
+    onItemChanged() {},
+    onItemVisited() {},
+    onItemMoved() {},
+  };
+  PlacesUtils.bookmarks.addObserver(bmObserver);
+
+  await promiseMigration(migrator, MigrationUtils.resourceTypes.BOOKMARKS, {
+    id: "default"
+  });
+  PlacesUtils.bookmarks.removeObserver(bmObserver);
+
+  // Check the bookmarks have been imported to all the expected parents.
+  Assert.ok(!expectedParents.length, "No more expected parents");
+  Assert.ok(gotFolder, "Should have seen the folder get imported");
+  Assert.equal(itemCount, 10, "Should import all 10 items.");
+  // Check that the telemetry matches:
+  Assert.equal(MigrationUtils._importQuantities.bookmarks, itemCount, "Telemetry reporting correct.");
+});
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -1,24 +1,26 @@
 [DEFAULT]
 head = head_migration.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 support-files =
   Library/**
   AppData/**
 
+[test_360se_bookmarks.js]
+skip-if = os != "win"
 [test_automigration.js]
 [test_Chrome_bookmarks.js]
 [test_Chrome_cookies.js]
 skip-if = os != "mac" # Relies on ULibDir
 [test_Chrome_passwords.js]
 skip-if = os != "win"
 [test_Edge_db_migration.js]
-skip-if = os != "win" || os_version == "5.1" || os_version == "5.2" # Relies on post-XP bits of ESEDB
+skip-if = os != "win"
 [test_fx_telemetry.js]
 [test_IE_bookmarks.js]
 skip-if = os != "win"
 [test_IE_cookies.js]
 skip-if = os != "win"
 [test_IE7_passwords.js]
 skip-if = os != "win"
 [test_Safari_bookmarks.js]
--- a/browser/components/resistfingerprinting/moz.build
+++ b/browser/components/resistfingerprinting/moz.build
@@ -5,8 +5,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "Security")
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini',
 ]
+
+MOCHITEST_MANIFESTS += [
+    'test/mochitest/mochitest.ini',
+]
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/.eslintrc.js
@@ -0,0 +1,5 @@
+module.exports = {
+  "rules": {
+    "no-eval": "off"
+  },
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/mochitest.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+  worker_child.js
+  worker_grandchild.js
+
+[test_reduce_time_precision.html]
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tor bug
+https://trac.torproject.org/projects/tor/ticket/1517
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Tor Bug 1517</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://trac.torproject.org/projects/tor/ticket/1517">Tor Bug 1517</a>
+
+<!-- Canvas for testing 'currentTime' -->
+<canvas id="test-canvas" width="100" height="100"></canvas>
+
+<!-- The main testing script -->
+<script type="application/javascript;version=1.7">
+  SimpleTest.requestFlakyTimeout("testing JS time-based fingerprinting");
+
+  // Prepare for test of AudioContext.currentTime
+  let audioContext = new AudioContext();
+  // Prepare for test of CanvasStream.currentTime
+  let canvas = document.getElementById("test-canvas");
+  let context = canvas.getContext('2d');
+  context.fillText("test", 20, 20);
+  let canvasStream = canvas.captureStream(25);
+
+  // Known ways to generate time stamps, in milliseconds
+  const timeStampCodes = [
+    'performance.now()',
+    'new Date().getTime()',
+    'new Event("").timeStamp',
+    'new File([], "").lastModified',
+    'new File([], "").lastModifiedDate.getTime()',
+  ];
+
+  const kExpectedResolution = 100;
+
+  function* checkWorker(worker) {
+    // The child worker will send the results back.
+    let checkTimeStamps = () => new Promise(function(resolve) {
+      let onMessage = function(event) {
+        worker.removeEventListener("message", onMessage);
+
+        let timeStamps = event.data;
+        for (let i = 0; i < timeStampCodes.length; i++) {
+          let timeStamp = timeStamps[i];
+          is(timeStamp % kExpectedResolution, 0,
+             "'" + timeStampCodes[i] +
+             "' should be rounded to nearest 100 ms in workers; saw " +
+             timeStamp);
+        }
+        resolve();
+      };
+      worker.addEventListener("message", onMessage);
+    });
+
+    // Send the codes to its child worker.
+    worker.postMessage(timeStampCodes);
+
+    // First, check the child's results.
+    yield checkTimeStamps();
+    // Then, check the grandchild's results.
+    yield checkTimeStamps();
+
+    worker.terminate();
+  }
+
+  add_task(function* testWorker() {
+    // Create one worker before setting the pref, and one after, in order to
+    // check that the resolution is updated whether or not the worker was
+    // already started
+    let worker1 = new Worker("worker_child.js");
+    yield SpecialPowers.pushPrefEnv({
+      "set": [["privacy.resistFingerprinting", true]]});
+    let worker2 = new Worker("worker_child.js");
+    // Allow ~550 ms to elapse, so we can get non-zero
+    // time values for all elements.
+    yield new Promise(resolve => window.setTimeout(resolve, 550));
+    yield checkWorker(worker1);
+    yield checkWorker(worker2);
+  });
+
+  add_task(function* testDOM() {
+    let timeStampCodesDOM = timeStampCodes.concat([
+      'audioContext.currentTime * 1000',
+      'canvasStream.currentTime * 1000',
+    ]);
+    // Loop through each timeStampCode, evaluate it,
+    // and check if it is rounded to the nearest 100 ms.
+    for (let timeStampCode of timeStampCodesDOM) {
+      let timeStamp = eval(timeStampCode);
+      is(timeStamp % kExpectedResolution, 0,
+         "'" + timeStampCode +
+         "' should be rounded to nearest 100 ms; saw " +
+         timeStamp);
+    }
+  });
+
+</script>
+
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/worker_child.js
@@ -0,0 +1,28 @@
+let timeStampCodes;
+let worker = new Worker("worker_grandchild.js");
+
+function listenToParent(event) {
+  self.removeEventListener("message", listenToParent);
+  timeStampCodes = event.data;
+
+  let timeStamps = [];
+  for (let timeStampCode of timeStampCodes) {
+    timeStamps.push(eval(timeStampCode));
+  }
+  // Send the timeStamps to the parent.
+  postMessage(timeStamps);
+
+  // Tell the grandchild to start.
+  worker.postMessage(timeStampCodes);
+}
+
+// The worker grandchild will send results back.
+function listenToChild(event) {
+  worker.removeEventListener("message", listenToChild);
+  // Pass the results to the parent.
+  postMessage(event.data);
+  worker.terminate();
+}
+
+worker.addEventListener("message", listenToChild);
+self.addEventListener("message", listenToParent);
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/mochitest/worker_grandchild.js
@@ -0,0 +1,10 @@
+self.addEventListener("message", function(event) {
+  let timeStampCodes = event.data;
+
+  let timeStamps = [];
+  for (let timeStampCode of timeStampCodes) {
+    timeStamps.push(eval(timeStampCode));
+  }
+  // Send the timeStamps to the parent.
+  postMessage(timeStamps);
+});
--- a/browser/extensions/formautofill/content/editProfile.js
+++ b/browser/extensions/formautofill/content/editProfile.js
@@ -4,18 +4,18 @@
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
-function EditDialog(address) {
-  this._address = address;
+function EditDialog() {
+  this._address = window.arguments && window.arguments[0];
   window.addEventListener("DOMContentLoaded", this, {once: true});
 }
 
 EditDialog.prototype = {
   init() {
     this.refs = {
       controlsContainer: document.getElementById("controls-container"),
       cancel: document.getElementById("cancel"),
@@ -132,9 +132,9 @@ EditDialog.prototype = {
    */
   detachEventListeners() {
     this.refs.controlsContainer.removeEventListener("click", this);
     document.removeEventListener("input", this);
   },
 };
 
 // Pass in argument from openDialog
-new EditDialog(window.arguments[0]);
+new EditDialog();
--- a/browser/extensions/formautofill/content/editProfile.xhtml
+++ b/browser/extensions/formautofill/content/editProfile.xhtml
@@ -1,20 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>Profile Autofill - Edit Profile</title>
-  <!-- common.css and dialog.css need to be included until this file can be
-     - loaded as a stacked subdialog. -->
-  <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" />
-  <link rel="stylesheet" href="chrome://browser/skin/preferences/in-content-new/dialog.css" />
   <link rel="stylesheet" href="chrome://formautofill-shared/skin/editProfile.css" />
   <link rel="stylesheet" href="chrome://formautofill/skin/editProfile.css" />
   <script src="chrome://formautofill/content/editProfile.js"></script>
 </head>
 <body>
   <form>
     <label id="given-name-container">
       <span>First Name</span>
--- a/browser/extensions/formautofill/content/manageProfiles.js
+++ b/browser/extensions/formautofill/content/manageProfiles.js
@@ -10,16 +10,17 @@ const EDIT_PROFILE_URL = "chrome://forma
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, "manageProfiles");
 
 function ManageProfileDialog() {
+  this.prefWin = window.opener;
   window.addEventListener("DOMContentLoaded", this, {once: true});
 }
 
 ManageProfileDialog.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
 
   _elements: {},
 
@@ -164,19 +165,17 @@ ManageProfileDialog.prototype = {
   },
 
   /**
    * Open the edit address dialog to create/edit an address.
    *
    * @param  {object} address [optional]
    */
   openEditDialog(address) {
-    window.openDialog(EDIT_PROFILE_URL, null,
-                      "chrome,centerscreen,modal,width=600,height=450",
-                      address);
+    this.prefWin.gSubDialog.open(EDIT_PROFILE_URL, null, address);
   },
 
   /**
    * Enable/disable the Edit and Remove buttons based on number of selected
    * options.
    *
    * @param  {number} selectedCount
    */
--- a/browser/extensions/formautofill/skin/linux/editProfile.css
+++ b/browser/extensions/formautofill/skin/linux/editProfile.css
@@ -1,5 +1,8 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Linux specific rules */
+body {
+  font-size: 0.85rem;
+}
\ No newline at end of file
--- a/browser/extensions/formautofill/skin/shared/editProfile.css
+++ b/browser/extensions/formautofill/skin/shared/editProfile.css
@@ -1,17 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 body {
   font-size: 1rem;
-  /* body needs padding until the edit profile dialog could be loaded as a
-     stacked subdialog */
-  padding: 2em;
 }
 
 form,
 label,
 div {
   display: flex;
 }
 
@@ -19,44 +16,42 @@ form {
   flex-wrap: wrap;
 }
 
 label {
   margin: 0 0 0.5em;
 }
 
 label > span {
+  box-sizing: border-box;
   flex: 0 0 8em;
   padding-inline-end: 0.5em;
   align-self: center;
   text-align: end;
 }
 
 input,
 select {
+  box-sizing: border-box;
   flex: 1 0 auto;
-  width: 9em;
+  width: calc(50% - 8em);
 }
 
 option {
-  padding: 6px;
+  padding: 5px 10px;
 }
 
 textarea {
   resize: none;
 }
 
 button {
   padding: 3px 2em;
 }
 
-#country-container {
-  width: 15em;
-}
-
 #given-name-container,
 #additional-name-container,
 #address-level1-container,
 #postal-code-container,
 #country-container {
   flex: 0 1 50%;
 }
 
@@ -74,15 +69,14 @@ button {
   justify-content: end;
 }
 
 #family-name,
 #organization,
 #address-level2,
 #tel{
   flex: 0 0 auto;
-  width: calc(50% - 10em);
 }
 
 #street-address,
 #email {
   flex: 1 0 auto;
 }
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -7,24 +7,26 @@
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 Cu.import("resource://gre/modules/Services.jsm");
 
 const ONBOARDING_CSS_URL = "resource://onboarding/onboarding.css";
 const ABOUT_HOME_URL = "about:home";
 const ABOUT_NEWTAB_URL = "about:newtab";
+const BUNDLE_URI = "chrome://onboarding/locale/onboarding.properties";
 
 /**
  * The script won't be initialized if we turned off onboarding by
  * setting "browser.onboarding.enabled" to false.
  */
 class Onboarding {
   constructor(contentWindow) {
     this.init(contentWindow);
+    this._bundle = Services.strings.createBundle(BUNDLE_URI);
   }
 
   async init(contentWindow) {
     this._window = contentWindow;
     // We want to create and append elements after CSS is loaded so
     // no flash of style changes and no additional reflow.
     await this._loadCSS();
     this._overlayIcon = this._renderOverlayIcon();
@@ -57,32 +59,38 @@ class Onboarding {
     this._overlay.remove();
   }
 
   toggleOverlay() {
     this._overlay.classList.toggle("opened");
   }
 
   _renderOverlay() {
+    const BRAND_SHORT_NAME = Services.strings
+                         .createBundle("chrome://branding/locale/brand.properties")
+                         .GetStringFromName("brandShortName");
     let div = this._window.document.createElement("div");
     div.id = "onboarding-overlay";
     // Here we use `innerHTML` is for more friendly reading.
     // The security should be fine because this is not from an external input.
     // We're not shipping yet so l10n strings is going to be closed for now.
     div.innerHTML = `
       <div id="onboarding-overlay-dialog">
         <span id="onboarding-overlay-close-btn"></span>
-        <header>Getting started?</header>
+        <header id="onboarding-header"></header>
         <nav>
           <ul></ul>
         </nav>
         <footer>
         </footer>
       </div>
     `;
+
+    div.querySelector("#onboarding-header").textContent =
+       this._bundle.formatStringFromName("onboarding.overlay-title", [BRAND_SHORT_NAME], 1);
     return div;
   }
 
   _renderOverlayIcon() {
     let img = this._window.document.createElement("div");
     img.id = "onboarding-overlay-icon";
     return img;
   }
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/locales/en-US/onboarding.properties
@@ -0,0 +1,7 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# LOCALIZATION NOTE(onboarding.tour-title): This string will be used in the overlay title
+# %S is brandShortName
+onboarding.overlay-title=Getting started with %S
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/locales/jar.mn
@@ -0,0 +1,8 @@
+#filter substitution
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+[features/onboarding@mozilla.org] @AB_CD@.jar:
+% locale onboarding @AB_CD@ %locale/@AB_CD@/
+  locale/@AB_CD@/onboarding.properties (%onboarding.properties)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/locales/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ['jar.mn']
--- a/browser/extensions/onboarding/moz.build
+++ b/browser/extensions/onboarding/moz.build
@@ -2,16 +2,18 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
 
+DIRS += ['locales']
+
 FINAL_TARGET_PP_FILES.features['onboarding@mozilla.org'] += [
   'install.rdf.in'
 ]
 
 FINAL_TARGET_FILES.features['onboarding@mozilla.org'] += [
   'bootstrap.js',
 ]
 
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -96,16 +96,19 @@ DEFINES += -DBOOKMARKS_INCLUDE_DIR=$(dir
 libs-%:
 	$(NSINSTALL) -D $(DIST)/install
 	@$(MAKE) -C ../../toolkit/locales libs-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -C ../../services/sync/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../extensions/spellcheck/locales AB_CD=$* XPI_NAME=locale-$*
 ifndef RELEASE_OR_BETA
 	@$(MAKE) -C ../extensions/formautofill/locale AB_CD=$* XPI_NAME=locale-$*
 endif
+ifdef NIGHTLY_BUILD
+	@$(MAKE) -C ../extensions/onboarding/locales AB_CD=$* XPI_NAME=locale-$*
+endif
 	@$(MAKE) -C ../extensions/pocket/locale AB_CD=$* XPI_NAME=locale-$*
 ifndef RELEASE_OR_BETA
 	@$(MAKE) -C ../extensions/presentation/locale AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../extensions/webcompat-reporter/locales AB_CD=$* XPI_NAME=locale-$*
 endif
 	@$(MAKE) -C ../../intl/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../devtools/client/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$*
--- a/browser/locales/filter.py
+++ b/browser/locales/filter.py
@@ -3,16 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 def test(mod, path, entity = None):
   import re
   # ignore anything but Firefox
   if mod not in ("netwerk", "dom", "toolkit", "security/manager",
                  "devtools/client", "devtools/shared",
                  "browser",
+                 "browser/extensions/onboarding",
                  "browser/extensions/webcompat-reporter",
                  "extensions/spellcheck",
                  "other-licenses/branding/firefox",
                  "browser/branding/official",
                  "services/sync"):
     return "ignore"
   if mod not in ("browser", "extensions/spellcheck"):
     # we only have exceptions for browser and extensions/spellcheck
--- a/browser/locales/l10n.ini
+++ b/browser/locales/l10n.ini
@@ -6,16 +6,17 @@
 depth = ../..
 all = browser/locales/all-locales
 
 [compare]
 dirs = browser
      other-licenses/branding/firefox
      browser/branding/official
      devtools/client
+     browser/extensions/onboarding
      browser/extensions/webcompat-reporter
 
 [includes]
 # non-central apps might want to use %(topsrcdir)s here, or other vars
 # RFE: that needs to be supported by compare-locales, too, though
 toolkit = toolkit/locales/l10n.ini
 services_sync = services/sync/locales/l10n.ini
 
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -27,16 +27,17 @@
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsJSEnvironment.h"
 #include "nsLayoutUtils.h"
 #include "nsPIWindowRoot.h"
+#include "nsRFPService.h"
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 static char *sPopupAllowedEvents;
 
 static bool sReturnHighResTimeStamp = false;
@@ -1089,17 +1090,17 @@ Event::DefaultPrevented(CallerType aCall
   // If preventDefault() has been called by content, return true.  Otherwise,
   // i.e., preventDefault() has been called by chrome, return true only when
   // this is called by chrome.
   return mEvent->DefaultPreventedByContent() ||
          aCallerType == CallerType::System;
 }
 
 double
-Event::TimeStamp() const
+Event::TimeStampImpl() const
 {
   if (!sReturnHighResTimeStamp) {
     return static_cast<double>(mEvent->mTime);
   }
 
   if (mEvent->mTimeStamp.IsNull()) {
     return 0.0;
   }
@@ -1124,16 +1125,22 @@ Event::TimeStamp() const
 
   workers::WorkerPrivate* workerPrivate =
     workers::GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
   return workerPrivate->TimeStampToDOMHighRes(mEvent->mTimeStamp);
 }
 
+double
+Event::TimeStamp() const
+{
+  return nsRFPService::ReduceTimePrecisionAsMSecs(TimeStampImpl());
+}
+
 bool
 Event::GetPreventDefault() const
 {
   nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(mOwner));
   if (win) {
     if (nsIDocument* doc = win->GetExtantDoc()) {
       doc->WarnOnceAbout(nsIDocument::eGetPreventDefault);
     }
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -58,16 +58,17 @@ public:
 
 protected:
   virtual ~Event();
 
 private:
   void ConstructorInit(EventTarget* aOwner,
                        nsPresContext* aPresContext,
                        WidgetEvent* aEvent);
+  double TimeStampImpl() const;
 
 public:
   static Event* FromSupports(nsISupports* aSupports)
   {
     nsIDOMEvent* event =
       static_cast<nsIDOMEvent*>(aSupports);
 #ifdef DEBUG
     {
--- a/dom/file/BaseBlobImpl.cpp
+++ b/dom/file/BaseBlobImpl.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/BaseBlobImpl.h"
+#include "nsRFPService.h"
 #include "prtime.h"
 
 namespace mozilla {
 namespace dom {
 
 void
 BaseBlobImpl::GetName(nsAString& aName) const
 {
@@ -58,17 +59,17 @@ BaseBlobImpl::GetType(nsAString& aType)
   aType = mContentType;
 }
 
 int64_t
 BaseBlobImpl::GetLastModified(ErrorResult& aRv)
 {
   MOZ_ASSERT(mIsFile, "Should only be called on files");
   if (IsDateUnknown()) {
-    mLastModificationDate = PR_Now();
+    mLastModificationDate = nsRFPService::ReduceTimePrecisionAsUSecs(PR_Now());
   }
 
   return mLastModificationDate / PR_USEC_PER_MSEC;
 }
 
 void
 BaseBlobImpl::SetLastModified(int64_t aLastModified)
 {
--- a/dom/file/MultipartBlobImpl.cpp
+++ b/dom/file/MultipartBlobImpl.cpp
@@ -6,16 +6,17 @@
 
 #include "MultipartBlobImpl.h"
 #include "jsfriendapi.h"
 #include "mozilla/dom/BlobSet.h"
 #include "mozilla/dom/FileBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIMultiplexInputStream.h"
+#include "nsRFPService.h"
 #include "nsStringStream.h"
 #include "nsTArray.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsIXPConnect.h"
 #include <algorithm>
 
@@ -264,18 +265,18 @@ MultipartBlobImpl::SetLengthAndModifiedD
 
   mLength = totalLength;
 
   if (mIsFile) {
     // We cannot use PR_Now() because bug 493756 and, for this reason:
     //   var x = new Date(); var f = new File(...);
     //   x.getTime() < f.dateModified.getTime()
     // could fail.
-    mLastModificationDate =
-      lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now();
+    mLastModificationDate = nsRFPService::ReduceTimePrecisionAsUSecs(
+      lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now());
   }
 }
 
 void
 MultipartBlobImpl::GetMozFullPathInternal(nsAString& aFilename,
                                           ErrorResult& aRv) const
 {
   if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMMediaStream.h"
 #include "nsContentUtils.h"
+#include "nsRFPService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIScriptError.h"
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackEvent.h"
 #include "mozilla/dom/LocalMediaStreamBinding.h"
@@ -548,18 +549,18 @@ DOMMediaStream::Constructor(const Global
 }
 
 double
 DOMMediaStream::CurrentTime()
 {
   if (!mPlaybackStream) {
     return 0.0;
   }
-  return mPlaybackStream->
-    StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() - mLogicalStreamStartTime);
+  return nsRFPService::ReduceTimePrecisionAsSecs(mPlaybackStream->
+    StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() - mLogicalStreamStartTime));
 }
 
 void
 DOMMediaStream::GetId(nsAString& aID) const
 {
   aID = mID;
 }
 
--- a/dom/media/gmp/GMPProcessParent.cpp
+++ b/dom/media/gmp/GMPProcessParent.cpp
@@ -6,16 +6,17 @@
 
 #include "GMPProcessParent.h"
 #include "GMPUtils.h"
 #include "nsIFile.h"
 #include "nsIRunnable.h"
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 #include "WinUtils.h"
 #endif
+#include "GMPLog.h"
 
 #include "base/string_util.h"
 #include "base/process_util.h"
 
 #include <string>
 
 using std::vector;
 using std::string;
@@ -46,20 +47,23 @@ GMPProcessParent::Launch(int32_t aTimeou
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   std::wstring wGMPPath = UTF8ToWide(mGMPPath.c_str());
 
   // The sandbox doesn't allow file system rules where the paths contain
   // symbolic links or junction points. Sometimes the Users folder has been
   // moved to another drive using a junction point, so allow for this specific
   // case. See bug 1236680 for details.
-  if (!widget::WinUtils::ResolveMovedUsersFolder(wGMPPath)) {
-    NS_WARNING("ResolveMovedUsersFolder failed for GMP path.");
+  if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(wGMPPath)) {
+    GMP_LOG("ResolveJunctionPointsAndSymLinks failed for GMP path=%S",
+            wGMPPath.c_str());
+    NS_WARNING("ResolveJunctionPointsAndSymLinks failed for GMP path.");
     return false;
   }
+  GMP_LOG("GMPProcessParent::Launch() resolved path to %S", wGMPPath.c_str());
 
   // If the GMP path is a network path that is not mapped to a drive letter,
   // then we need to fix the path format for the sandbox rule.
   wchar_t volPath[MAX_PATH];
   if (::GetVolumePathNameW(wGMPPath.c_str(), volPath, MAX_PATH) &&
       ::GetDriveTypeW(volPath) == DRIVE_REMOTE &&
       wGMPPath.compare(0, 2, L"\\\\") == 0) {
     std::wstring sandboxGMPPath(wGMPPath);
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -55,16 +55,17 @@
 #include "MediaStreamAudioDestinationNode.h"
 #include "MediaStreamAudioSourceNode.h"
 #include "MediaStreamGraph.h"
 #include "nsContentUtils.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
+#include "nsRFPService.h"
 #include "OscillatorNode.h"
 #include "PannerNode.h"
 #include "PeriodicWave.h"
 #include "ScriptProcessorNode.h"
 #include "StereoPannerNode.h"
 #include "WaveShaperNode.h"
 
 namespace mozilla {
@@ -628,17 +629,18 @@ AudioContext::DestinationStream() const
   }
   return nullptr;
 }
 
 double
 AudioContext::CurrentTime() const
 {
   MediaStream* stream = Destination()->Stream();
-  return stream->StreamTimeToSeconds(stream->GetCurrentTime());
+  return nsRFPService::ReduceTimePrecisionAsSecs(
+    stream->StreamTimeToSeconds(stream->GetCurrentTime()));
 }
 
 void AudioContext::DisconnectFromOwner()
 {
   mIsDisconnecting = true;
   Shutdown();
   DOMEventTargetHelper::DisconnectFromOwner();
 }
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Performance.h"
 
 #include "GeckoProfiler.h"
+#include "nsRFPService.h"
 #ifdef MOZ_GECKO_PROFILER
 #include "ProfilerMarkerPayload.h"
 #endif
 #include "PerformanceEntry.h"
 #include "PerformanceMainThread.h"
 #include "PerformanceMark.h"
 #include "PerformanceMeasure.h"
 #include "PerformanceObserver.h"
@@ -247,17 +248,18 @@ Performance::ClearResourceTimings()
 
 DOMHighResTimeStamp
 Performance::RoundTime(double aTime) const
 {
   // Round down to the nearest 5us, because if the timer is too accurate people
   // can do nasty timing attacks with it.  See similar code in the worker
   // Performance implementation.
   const double maxResolutionMs = 0.005;
-  return floor(aTime / maxResolutionMs) * maxResolutionMs;
+  return nsRFPService::ReduceTimePrecisionAsMSecs(
+    floor(aTime / maxResolutionMs) * maxResolutionMs);
 }
 
 
 void
 Performance::Mark(const nsAString& aName, ErrorResult& aRv)
 {
   // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003.
   if (mUserEntries.Length() >= mResourceTimingBufferSize) {
--- a/dom/smil/test/db_smilCSSFromTo.js
+++ b/dom/smil/test/db_smilCSSFromTo.js
@@ -326,17 +326,17 @@ var gFromToBundles = [
     new AnimTestcaseFromTo("small-caps", "normal"),
   ]),
   new TestcaseBundle(gPropList.font_weight, [
     new AnimTestcaseFromTo("100", "900", { midComp: "500" }),
     new AnimTestcaseFromTo("700", "100", { midComp: "400" }),
     new AnimTestcaseFromTo("inherit", "200",
                            { fromComp: "400", midComp: "300" }),
     new AnimTestcaseFromTo("normal", "bold",
-                           { fromComp: "400", midComp: "500", toComp: "700" }),
+                           { fromComp: "400", midComp: "600", toComp: "700" }),
     new AnimTestcaseFromTo("lighter", "bolder", {},
                            "need support for animating between " +
                            "relative 'font-weight' values"),
   ]),
   // NOTE: Mozilla doesn't currently support "glyph-orientation-horizontal" or
   // "glyph-orientation-vertical", but I'm testing them here in case we ever
   // add support for them, because they're explicitly not animatable in the SVG
   // spec.
--- a/js/public/Date.h
+++ b/js/public/Date.h
@@ -168,11 +168,16 @@ DayFromYear(double year);
 
 // Takes an integer number of milliseconds since the epoch and an integer year,
 // returns the number of days in that year. If |time| is nonfinite, returns NaN.
 // Otherwise |time| *must* correspond to a time within the valid year |year|.
 // This should usually be ensured by computing |year| as |JS::DayFromYear(time)|.
 JS_PUBLIC_API(double)
 DayWithinYear(double time, double year);
 
+// Sets the time resolution for fingerprinting protection.
+// If it's set to zero, then no rounding will happen.
+JS_PUBLIC_API(void)
+SetTimeResolutionUsec(uint32_t resolution);
+
 } // namespace JS
 
 #endif /* js_Date_h */
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -13,16 +13,17 @@
  *  might have been left to the operator."
  *
  * Frederick Brooks, 'The Second-System Effect'.
  */
 
 #include "jsdate.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Sprintf.h"
 
 #include <ctype.h>
 #include <math.h>
 #include <string.h>
 
 #include "jsapi.h"
@@ -43,27 +44,32 @@
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 #include "vm/Time.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
+using mozilla::Atomic;
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 using mozilla::NumbersAreIdentical;
+using mozilla::ReleaseAcquire;
 
 using JS::AutoCheckCannotGC;
 using JS::ClippedTime;
 using JS::GenericNaN;
 using JS::TimeClip;
 using JS::ToInteger;
 
+// When this value is non-zero, we'll round the time by this resolution.
+static Atomic<uint32_t, ReleaseAcquire> sResolutionUsec;
+
 /*
  * The JS 'Date' object is patterned after the Java 'Date' object.
  * Here is a script:
  *
  *    today = new Date();
  *
  *    print(today.toLocaleString());
  *
@@ -394,16 +400,22 @@ JS::DayFromYear(double year)
 }
 
 JS_PUBLIC_API(double)
 JS::DayWithinYear(double time, double year)
 {
     return ::DayWithinYear(time, year);
 }
 
+JS_PUBLIC_API(void)
+JS::SetTimeResolutionUsec(uint32_t resolution)
+{
+    sResolutionUsec = resolution;
+}
+
 /*
  * Find a year for which any given date will fall on the same weekday.
  *
  * This function should be used with caution when used other than
  * for determining DST; it hasn't been proven not to produce an
  * incorrect year for times near year boundaries.
  */
 static int
@@ -1245,17 +1257,21 @@ date_parse(JSContext* cx, unsigned argc,
 
     args.rval().set(TimeValue(result));
     return true;
 }
 
 static ClippedTime
 NowAsMillis()
 {
-    return TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC);
+    double now = PRMJ_Now();
+    if (sResolutionUsec) {
+        now = floor(now / sResolutionUsec) * sResolutionUsec;
+    }
+    return TimeClip(now / PRMJ_USEC_PER_MSEC);
 }
 
 bool
 js::date_now(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().set(TimeValue(NowAsMillis()));
     return true;
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -2920,22 +2920,20 @@ StyleAnimationValue::AddWeighted(nsCSSPr
       return true;
     }
     case eUnit_Integer: {
       // https://drafts.csswg.org/css-transitions/#animtype-integer
       double interpolatedValue = aCoeff1 * double(aValue1.GetIntValue()) +
                                  aCoeff2 * double(aValue2.GetIntValue());
       int32_t result = floor(interpolatedValue + 0.5);
       if (aProperty == eCSSProperty_font_weight) {
-        if (result < 100) {
-          result = 100;
-        } else if (result > 900) {
-          result = 900;
-        }
+        // https://drafts.csswg.org/css-transitions/#animtype-font-weight
+        result += 50;
         result -= result % 100;
+        result = Clamp(result, 100, 900);
       } else {
         result = RestrictValue(aProperty, result);
       }
       aResultValue.SetIntValue(result, eUnit_Integer);
       return true;
     }
     case eUnit_Coord: {
       aResultValue.SetCoordValue(RestrictValue(aProperty, NSToCoordRound(
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -8,16 +8,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventForwards.h"
 #include "AnimationCommon.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/Keyframe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
+#include "nsRFPService.h"
 
 class nsIGlobalObject;
 class nsStyleContext;
 struct nsStyleDisplay;
 struct ServoComputedValues;
 
 namespace mozilla {
 namespace css {
@@ -46,17 +47,18 @@ struct AnimationEventInfo {
                      dom::Animation* aAnimation)
     : mElement(aElement)
     , mAnimation(aAnimation)
     , mEvent(true, aMessage)
     , mTimeStamp(aTimeStamp)
   {
     // XXX Looks like nobody initialize WidgetEvent::time
     mEvent.mAnimationName = aAnimationName;
-    mEvent.mElapsedTime = aElapsedTime.ToSeconds();
+    mEvent.mElapsedTime =
+      nsRFPService::ReduceTimePrecisionAsSecs(aElapsedTime.ToSeconds());
     mEvent.mPseudoElement =
       AnimationCollection<dom::CSSAnimation>::PseudoTypeAsString(aPseudoType);
   }
 
   // InternalAnimationEvent doesn't support copy-construction, so we need
   // to ourselves in order to work with nsTArray
   AnimationEventInfo(const AnimationEventInfo& aOther)
     : mElement(aOther.mElement)
--- a/servo/components/net/image_cache.rs
+++ b/servo/components/net/image_cache.rs
@@ -102,17 +102,17 @@ fn is_image_opaque(format: webrender_tra
         webrender_traits::ImageFormat::A8 => false,
         webrender_traits::ImageFormat::Invalid | webrender_traits::ImageFormat::RGBAF32 => unreachable!(),
     }
 }
 
 fn premultiply(data: &mut [u8]) {
     let length = data.len();
 
-    for i in (0..length).step_by(4) {
+    for i in Iterator::step_by(0..length, 4) {
         let b = data[i + 0] as u32;
         let g = data[i + 1] as u32;
         let r = data[i + 2] as u32;
         let a = data[i + 3] as u32;
 
         data[i + 0] = (b * a / 255) as u8;
         data[i + 1] = (g * a / 255) as u8;
         data[i + 2] = (r * a / 255) as u8;
--- a/servo/components/net/lib.rs
+++ b/servo/components/net/lib.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![deny(unsafe_code)]
 #![feature(box_syntax)]
-#![feature(step_by)]
+#![feature(iterator_step_by)]
 
 extern crate base64;
 extern crate brotli;
 extern crate cookie as cookie_rs;
 extern crate devtools_traits;
 extern crate flate2;
 extern crate hyper;
 extern crate hyper_openssl;
--- a/servo/components/net_traits/image/base.rs
+++ b/servo/components/net_traits/image/base.rs
@@ -37,17 +37,17 @@ pub struct ImageMetadata {
 
 // FIXME: Images must not be copied every frame. Instead we should atomically
 // reference count them.
 
 // TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this.
 fn byte_swap_and_premultiply(data: &mut [u8]) {
     let length = data.len();
 
-    for i in (0..length).step_by(4) {
+    for i in Iterator::step_by(0..length, 4) {
         let r = data[i + 2];
         let g = data[i + 1];
         let b = data[i + 0];
 
         data[i + 0] = r;
         data[i + 1] = g;
         data[i + 2] = b;
     }
--- a/servo/components/net_traits/lib.rs
+++ b/servo/components/net_traits/lib.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![feature(box_syntax)]
-#![feature(step_by)]
+#![feature(iterator_step_by)]
 
 #![deny(unsafe_code)]
 
 extern crate cookie as cookie_rs;
 extern crate heapsize;
 #[macro_use]
 extern crate heapsize_derive;
 extern crate hyper;
--- a/servo/components/script_plugins/unrooted_must_root.rs
+++ b/servo/components/script_plugins/unrooted_must_root.rs
@@ -135,26 +135,26 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> fo
             visit::FnKind::Method(n, _, _, _) => {
                 &*n.as_str() == "new" || n.as_str().starts_with("new_")
             }
             visit::FnKind::Closure(_) => return,
         };
 
         if !in_derive_expn(span) {
             let def_id = cx.tcx.hir.local_def_id(id);
-            let ty = cx.tcx.type_of(def_id);
+            let sig = cx.tcx.type_of(def_id).fn_sig();
 
-            for (arg, ty) in decl.inputs.iter().zip(ty.fn_args().0.iter()) {
+            for (arg, ty) in decl.inputs.iter().zip(sig.inputs().0.iter()) {
                 if is_unrooted_ty(cx, ty, false) {
                     cx.span_lint(UNROOTED_MUST_ROOT, arg.span, "Type must be rooted")
                 }
             }
 
             if !in_new_function {
-                if is_unrooted_ty(cx, ty.fn_ret().0, false) {
+                if is_unrooted_ty(cx, sig.output().0, false) {
                     cx.span_lint(UNROOTED_MUST_ROOT, decl.output.span(), "Type must be rooted")
                 }
             }
         }
 
         let mut visitor = FnDefVisitor {
             cx: cx,
             in_new_function: in_new_function,
@@ -213,21 +213,14 @@ impl<'a, 'b, 'tcx> visit::Visitor<'tcx> 
                             pat.span,
                             &format!("Expression of type {:?} must be rooted", ty))
             }
         }
 
         visit::walk_pat(self, pat);
     }
 
-    fn visit_fn(&mut self, kind: visit::FnKind<'tcx>, decl: &'tcx hir::FnDecl,
-                body: hir::BodyId, span: codemap::Span, id: ast::NodeId) {
-        if let visit::FnKind::Closure(_) = kind {
-            visit::walk_fn(self, kind, decl, body, span, id);
-        }
-    }
+    fn visit_ty(&mut self, _: &'tcx hir::Ty) {}
 
-    fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem) {}
-    fn visit_ty(&mut self, _: &'tcx hir::Ty) { }
     fn nested_visit_map<'this>(&'this mut self) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> {
         hir::intravisit::NestedVisitorMap::OnlyBodies(&self.cx.tcx.hir)
     }
 }
--- a/servo/components/style/properties/longhand/svg.mako.rs
+++ b/servo/components/style/properties/longhand/svg.mako.rs
@@ -23,30 +23,30 @@
     "stop-color", "RGBAColor",
     "RGBA::new(0, 0, 0, 255)",
     products="gecko",
     animation_value_type="none",
     spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty")}
 
 ${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
                           products="gecko",
-                          animation_value_type="none",
+                          animation_value_type="ComputedValue",
                           spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity")}
 
 // Section 15 - Filter Effects
 
 ${helpers.predefined_type(
     "flood-color", "RGBAColor",
     "RGBA::new(0, 0, 0, 255)",
     products="gecko",
     animation_value_type="none",
     spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty")}
 
 ${helpers.predefined_type("flood-opacity", "Opacity",
-                          "1.0", products="gecko", animation_value_type="none",
+                          "1.0", products="gecko", animation_value_type="ComputedValue",
                           spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}
 
 ${helpers.predefined_type(
     "lighting-color", "RGBAColor",
     "RGBA::new(255, 255, 255, 255)",
     products="gecko",
     animation_value_type="none",
     spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty")}
--- a/servo/rust-commit-hash
+++ b/servo/rust-commit-hash
@@ -1,1 +1,1 @@
-03bed655142dd5e42ba4539de53b3663d8a123e0
+2416e222ecae76a36e958a8f7c4ac8083eb1ca45
--- a/testing/geckodriver/CHANGES.md
+++ b/testing/geckodriver/CHANGES.md
@@ -1,19 +1,37 @@
 # Change log
 
 All notable changes to this program is documented in this file.
 
-## 0.16.1 (2016-04-26)
+## 0.17.0 (2017-06-09)
+
+### Added
+- Added endpoints:
+  - POST `/session/{session id}/window/fullscreen` to invoke the window manager-specific `full screen` operation
+  - POST `/session/{session id}/moz/addon/install` to install an extension [Gecko only]
+  - POST `/session/{session id}/moz/addon/uninstall` to uninstall an extension [Gecko only]
+
+### Changed
+- Increasing the length of the `network.http.phishy-userpass-length` preference will cause Firefox to not prompt when navigating to a website with a username or password in the URL
+- Library dependencies upgraded to mozrunner 0.4 and mozprofile 0.3 to allow overriding of preferences via capabilities if those have been already set in the profile
+- Library dependencies upgraded to mozversion 0.1.2 to only use the normalized path of the Firefox binary for version checks but not to actually start the browser, which broke several components in Firefox on Windows
+
+### Fixed
+- The SetWindowRect command now returns the WindowRect when it is done
+- Use ASCII versions of array symbols to properly display them in the Windows command prompt
+- Use [`SessionNotCreated`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.SessionNotCreated) error instead of [`UnknownError`](https://docs.rs/webdriver/0.25.0/webdriver/error/enum.ErrorStatus.html#variant.UnknownError) if there is no current session
+
+## 0.16.1 (2017-04-26)
 
 ### Fixed
 - Read Firefox version number from stdout when failing to look for the application .ini file (fixes [Selenium #3884](https://github.com/SeleniumHQ/selenium/issues/3884))
 - Session is now ended when closing the last Firefox window (fixes [#613](https://github.com/mozilla/geckodriver/issues/613))
 
-## 0.16.0 (2016-04-21)
+## 0.16.0 (2017-04-21)
 
 Note that geckodriver v0.16.0 is only compatible with Selenium 3.4 and greater.
 
 ### Added
 - Support for WebDriver-conforming [New Session](https://w3c.github.io/webdriver/webdriver-spec.html#dfn-new-session) negotiation, with `desiredCapabilities`/`requiredCapabilities` negotiation as fallback
 - Added two new endpoints:
   - GET `/session/{session id}/window/rect` for [Get Window Rect](https://w3c.github.io/webdriver/webdriver-spec.html#get-window-rect)
   - POST `/session/{session id}/window/rect` for [Set Window Rect](https://w3c.github.io/webdriver/webdriver-spec.html#set-window-rect)
@@ -22,17 +40,17 @@ Note that geckodriver v0.16.0 is only co
   - Removes `ElementNotVisible` and `InvalidElementCoordinates` errors
 
 ### Removed
 - Removed following list of unused endpoints:
   - GET `/session/{session id}/alert_text`
   - POST `/session/{session id}/alert_text`
   - POST `/session/{session id}/accept_alert`
   - POST `/session/{session id}/dismiss_alert`
-  - GET `/session/{session id}/window_handle` 
+  - GET `/session/{session id}/window_handle`
   - DELETE `/session/{session id}/window_handle`
   - POST `/session/{session id}/execute_async`
   - POST `/session/{session id}/execute`
 
 ### Changed
 - [`SendKeysParameters`](https://docs.rs/webdriver/0.25.0/webdriver/command/struct.SendKeysParameters.html), which is used for the [Element Send Keys](https://w3c.github.io/webdriver/webdriver-spec.html#element-send-keys) and [Send Alert Text](https://w3c.github.io/webdriver/webdriver-spec.html#send-alert-text) commands, has been updated to take a string `text` field
 - [`CookieResponse`](https://docs.rs/webdriver/0.25.0/webdriver/response/struct.CookieResponse.html) and [`CloseWindowResponse`](https://docs.rs/webdriver/0.25.0/webdriver/response/struct.CloseWindowResponse.html) fixed to be properly wrapped in a `value` field, like other responses
 - Allow negative numbers for `x` and `y` fields in `pointerMove` action
--- a/testing/geckodriver/Cargo.lock
+++ b/testing/geckodriver/Cargo.lock
@@ -1,11 +1,11 @@
 [root]
 name = "geckodriver"
-version = "0.16.1"
+version = "0.17.0"
 dependencies = [
  "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozprofile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mozrunner 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/testing/geckodriver/Cargo.toml
+++ b/testing/geckodriver/Cargo.toml
@@ -1,11 +1,11 @@
 [package]
 name = "geckodriver"
-version = "0.16.1"
+version = "0.17.0"
 authors = [
   "James Graham <james@hoppipolla.co.uk>",
   "Andreas Tolfsen <ato@mozilla.com>",
 ]
 description = "Proxy for using WebDriver clients to interact with Gecko-based browsers."
 keywords = ["webdriver", "w3c", "httpd", "mozilla", "firefox"]
 repository = "https://github.com/mozilla/geckodriver"
 readme = "README.md"
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -220014,17 +220014,17 @@
    "eee8ff07b3ec5e83e5f18305f5bc00eb72468443",
    "testharness"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property.html": [
    "55100f7d505bc8cbc966ced0d1337ed78534a553",
    "testharness"
   ],
   "web-animations/animation-model/animation-types/property-list.js": [
-   "c13757ba6b500eebe826a0cb62f9924fd6d23496",
+   "9dfb34806dfd264bb1155830b0548d53fcae9007",
    "support"
   ],
   "web-animations/animation-model/animation-types/property-types.js": [
    "ee3e5ae1923027f7e9dd80da11e765085a02c367",
    "support"
   ],
   "web-animations/animation-model/animation-types/spacing-keyframes-filters.html": [
    "bd771a8a18245560221d92ea3495f81918c09848",
--- a/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js
+++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js
@@ -511,18 +511,17 @@ var gCSSProperties = {
     ]
   },
   'flood-color': {
     // https://drafts.fxtf.org/filters/#FloodColorProperty
     types: [ 'color' ]
   },
   'flood-opacity': {
     // https://drafts.fxtf.org/filters/#propdef-flood-opacity
-    types: [
-    ]
+    types: [ 'opacity' ]
   },
   'font-size': {
     // https://drafts.csswg.org/css-fonts-3/#propdef-font-size
     types: [
     ]
   },
   'font-size-adjust': {
     // https://drafts.csswg.org/css-fonts-3/#propdef-font-size-adjust
@@ -1218,18 +1217,17 @@ var gCSSProperties = {
     ]
   },
   'stop-color': {
     // https://svgwg.org/svg2-draft/pservers.html#StopColorProperty
     types: [ 'color' ]
   },
   'stop-opacity': {
     // https://svgwg.org/svg2-draft/pservers.html#StopOpacityProperty
-    types: [
-    ]
+    types: [ 'opacity' ]
   },
   'stroke': {
     // https://svgwg.org/svg2-draft/painting.html#StrokeProperty
     types: [
     ]
   },
   'stroke-dasharray': {
     // https://svgwg.org/svg2-draft/painting.html#StrokeDasharrayProperty
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -16,25 +16,28 @@
 
 #include "nsIObserverService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsJSUtils.h"
 
 #include "prenv.h"
 
+#include "js/Date.h"
+
 using namespace mozilla;
 
 #define RESIST_FINGERPRINTING_PREF "privacy.resistFingerprinting"
 
 NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
 
 static StaticRefPtr<nsRFPService> sRFPService;
 static bool sInitialized = false;
-bool nsRFPService::sPrivacyResistFingerprinting = false;
+Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyResistFingerprinting;
+static uint32_t kResolutionUSec = 100000;
 
 /* static */
 nsRFPService*
 nsRFPService::GetOrCreate()
 {
   if (!sInitialized) {
     sRFPService = new nsRFPService();
     nsresult rv = sRFPService->Init();
@@ -46,16 +49,54 @@ nsRFPService::GetOrCreate()
 
     ClearOnShutdown(&sRFPService);
     sInitialized = true;
   }
 
   return sRFPService;
 }
 
+/* static */
+double
+nsRFPService::ReduceTimePrecisionAsMSecs(double aTime)
+{
+  if (!IsResistFingerprintingEnabled()) {
+    return aTime;
+  }
+  const double resolutionMSec = kResolutionUSec / 1000.0;
+  return floor(aTime / resolutionMSec) * resolutionMSec;
+}
+
+/* static */
+double
+nsRFPService::ReduceTimePrecisionAsUSecs(double aTime)
+{
+  if (!IsResistFingerprintingEnabled()) {
+    return aTime;
+  }
+  return floor(aTime / kResolutionUSec) * kResolutionUSec;
+}
+
+/* static */
+double
+nsRFPService::ReduceTimePrecisionAsSecs(double aTime)
+{
+  if (!IsResistFingerprintingEnabled()) {
+    return aTime;
+  }
+  if (kResolutionUSec < 1000000) {
+    // The resolution is smaller than one sec.  Use the reciprocal to avoid
+    // floating point error.
+    const double resolutionSecReciprocal = 1000000.0 / kResolutionUSec;
+    return floor(aTime * resolutionSecReciprocal) / resolutionSecReciprocal;
+  }
+  const double resolutionSec = kResolutionUSec / 1000000.0;
+  return floor(aTime / resolutionSec) * resolutionSec;
+}
+
 nsresult
 nsRFPService::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsresult rv;
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -80,21 +121,24 @@ nsRFPService::Init()
   // and set the timezone.
   UpdatePref();
   return rv;
 }
 
 void
 nsRFPService::UpdatePref()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   sPrivacyResistFingerprinting = Preferences::GetBool(RESIST_FINGERPRINTING_PREF);
 
   if (sPrivacyResistFingerprinting) {
     PR_SetEnv("TZ=UTC");
+    JS::SetTimeResolutionUsec(kResolutionUSec);
   } else if (sInitialized) {
+    JS::SetTimeResolutionUsec(0);
     // We will not touch the TZ value if 'privacy.resistFingerprinting' is false during
     // the time of initialization.
     if (!mInitialTZValue.IsEmpty()) {
       nsAutoCString tzValue = NS_LITERAL_CSTRING("TZ=") + mInitialTZValue;
       PR_SetEnv(tzValue.get());
     } else {
 #if defined(XP_LINUX) || defined (XP_MACOSX)
       // For POSIX like system, we reset the TZ to the /etc/localtime, which is the
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __nsRFPService_h__
 #define __nsRFPService_h__
 
+#include "mozilla/Atomics.h"
 #include "nsIObserver.h"
 
 #include "nsString.h"
 
 namespace mozilla {
 
 class nsRFPService final : public nsIObserver
 {
@@ -19,26 +20,31 @@ public:
   NS_DECL_NSIOBSERVER
 
   static nsRFPService* GetOrCreate();
   static bool IsResistFingerprintingEnabled()
   {
     return sPrivacyResistFingerprinting;
   }
 
+  // The following Reduce methods can be called off main thread.
+  static double ReduceTimePrecisionAsMSecs(double aTime);
+  static double ReduceTimePrecisionAsUSecs(double aTime);
+  static double ReduceTimePrecisionAsSecs(double aTime);
+
 private:
   nsresult Init();
 
   nsRFPService() {}
 
   ~nsRFPService() {}
 
   void UpdatePref();
   void StartShutdown();
 
-  static bool sPrivacyResistFingerprinting;
+  static Atomic<bool, ReleaseAcquire> sPrivacyResistFingerprinting;
 
   nsCString mInitialTZValue;
 };
 
 } // mozilla namespace
 
 #endif /* __nsRFPService_h__ */
--- a/toolkit/content/aboutAbout.xhtml
+++ b/toolkit/content/aboutAbout.xhtml
@@ -8,18 +8,20 @@
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>&aboutAbout.title;</title>
-  <link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css"/>
   <script type="application/javascript" src="chrome://global/content/aboutAbout.js"></script>
 </head>
 
 <body dir="&locale.dir;">
-  <h1>&aboutAbout.title;</h1>
-  <p><em>&aboutAbout.note;</em></p>
-  <ul id="abouts" class="columns"></ul>
+  <div class="container">
+    <h1>&aboutAbout.title;</h1>
+    <p><em>&aboutAbout.note;</em></p>
+    <ul id="abouts" class="columns"></ul>
+  </div>
 </body>
 </html>
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
@@ -253,12 +253,12 @@
      being a link to an external site. -->
 <!ENTITY disabledUnsigned.devInfo.start "Developers interested in getting their add-ons verified can continue by reading our ">
 <!ENTITY disabledUnsigned.devInfo.linkToManual "manual">
 <!ENTITY disabledUnsigned.devInfo.end ".">
 
 <!ENTITY pluginDeprecation.description "Missing something? Some plugins are no longer supported by &brandShortName;.">
 <!ENTITY pluginDeprecation.learnMore "Learn More.">
 
-<!ENTITY legacyWarning.description "Missing something?  Some extensions are no longer supported by Firefox.">
+<!ENTITY legacyWarning.description "Missing something? Some extensions are no longer supported by &brandShortName;.">
 <!ENTITY legacyWarning.showLegacy "Show legacy extensions">
-<!ENTITY legacyExtensions.description "These extensions do not meet current Firefox standards so we deactivated them.">
-<!ENTITY legacyExtensions.learnMore "Learn about the changes to Firefox add-ons">
+<!ENTITY legacyExtensions.description "These extensions do not meet current &brandShortName; standards so they have been deactivated.">
+<!ENTITY legacyExtensions.learnMore "Learn about the changes to add-ons">
--- a/toolkit/themes/shared/about.css
+++ b/toolkit/themes/shared/about.css
@@ -51,13 +51,8 @@ ul {
 
 ul > li {
   margin-top: .5em;
 }
 
 th, td {
   padding: 0 5px;
 }
-
-.columns {
-  -moz-column-width: 20em;
-  -moz-column-gap: 3em;
-}
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -190,16 +190,27 @@ xul|menulist {
   -moz-border-top-colors: none !important;
   -moz-border-right-colors: none !important;
   -moz-border-bottom-colors: none !important;
   -moz-border-left-colors: none !important;
   border-radius: 2px;
   background-color: var(--in-content-page-background);
 }
 
+html|select:not([size]):not([multiple]) {
+  background-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown");
+  background-position: right 3px center;
+  background-repeat: no-repeat;
+  background-size: auto 18px;
+  font-size: inherit;
+  padding-inline-start: 5px;
+  padding-inline-end: 24px;
+  text-overflow: ellipsis;
+}
+
 html|button:enabled:hover,
 html|select:not([size]):not([multiple]):enabled:hover,
 xul|button:not([disabled="true"]):hover,
 xul|colorpicker[type="button"]:not([disabled="true"]):hover,
 xul|menulist:not([disabled="true"]):hover {
   background-color: var(--in-content-box-background-hover);
 }
 
--- a/toolkit/themes/shared/in-content/info-pages.inc.css
+++ b/toolkit/themes/shared/in-content/info-pages.inc.css
@@ -10,17 +10,17 @@
 }
 
 /* Body and container */
 body {
   display: flex;
   flex-direction: column;
   box-sizing: border-box;
   min-height: 100vh;
-  padding: 0 48px;
+  padding: 40px 48px;
   align-items: center;
   justify-content: center;
 }
 
 .container {
   min-width: var(--in-content-container-min-width);
   max-width: var(--in-content-container-max-width);
 }
@@ -78,16 +78,27 @@ ul > li, ol > li {
 ul {
   list-style: disc;
 }
 
 dt {
   font-weight: bold;
 }
 
+ul.columns {
+  column-count: 2;
+  column-gap: 5em;
+}
+
+@media (max-width: 35em) {
+  ul.columns {
+    column-count: 1;
+  }
+}
+
 /* Buttons */
 .button-container {
   margin-top: 1.2em;
 }
 
 button {
   padding: 0 1.5em;
   border-radius: 0;
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -996,17 +996,40 @@ private:
 /******************************************************************/
 
 class TSFStaticSink final : public ITfInputProcessorProfileActivationSink
 {
 public:
   static TSFStaticSink* GetInstance()
   {
     if (!sInstance) {
-      sInstance = new TSFStaticSink();
+      RefPtr<ITfThreadMgr> threadMgr = TSFTextStore::GetThreadMgr();
+      if (NS_WARN_IF(!threadMgr)) {
+        MOZ_LOG(sTextStoreLog, LogLevel::Error,
+          ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
+           "instance due to no ThreadMgr instance"));
+        return nullptr;
+      }
+      RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
+        TSFTextStore::GetInputProcessorProfiles();
+      if (NS_WARN_IF(!inputProcessorProfiles)) {
+        MOZ_LOG(sTextStoreLog, LogLevel::Error,
+          ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
+           "instance due to no InputProcessorProfiles instance"));
+        return nullptr;
+      }
+      RefPtr<TSFStaticSink> staticSink = new TSFStaticSink();
+      if (NS_WARN_IF(!staticSink->Init(threadMgr, inputProcessorProfiles))) {
+        staticSink->Destroy();
+        MOZ_LOG(sTextStoreLog, LogLevel::Error,
+          ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
+           "instance"));
+        return nullptr;
+      }
+      sInstance = staticSink.forget();
     }
     return sInstance;
   }
 
   static void Shutdown()
   {
     if (sInstance) {
       sInstance->Destroy();
@@ -1034,203 +1057,245 @@ public:
 
   const nsString& GetActiveTIPKeyboardDescription() const
   {
     return mActiveTIPKeyboardDescription;
   }
 
   static bool IsIMM_IMEActive()
   {
+    // Use IMM API until TSFStaticSink starts to work.
     if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
       return IsIMM_IME(::GetKeyboardLayout(0));
     }
     return sInstance->mIsIMM_IME;
   }
 
   static bool IsIMM_IME(HKL aHKL)
   {
      return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0);
   }
 
-  bool EnsureInitActiveTIPKeyboard();
-
+#define DECL_AND_IMPL_IS_TIP_ACTIVE(aMethod)                                   \
+  static bool aMethod()                                                        \
+  {                                                                            \
+    RefPtr<TSFStaticSink> staticSink = GetInstance();                          \
+    if (NS_WARN_IF(!staticSink) ||                                             \
+        NS_WARN_IF(!staticSink->EnsureInitActiveTIPKeyboard())) {              \
+      return false;                                                            \
+    }                                                                          \
+    return staticSink->aMethod ## Internal();                                  \
+  }
+
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSJapaneseIMEActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSOfficeJapaneseIME2010Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOKActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2011Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2012Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2013Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2014Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2015Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2016Active)
+
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSChangJieActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSQuickQuickActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsFreeChangJieActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsEasyChangjeiActive)
+
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSPinyinActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSWubiActive)
+
+#undef DECL_AND_IMPL_IS_TIP_ACTIVE
+
+  // Note that ATOK 2011 - 2016 refers native caret position for deciding its
+  // popup window position.
+  static bool IsATOKReferringNativeCaretActive()
+  {
+    RefPtr<TSFStaticSink> staticSink = GetInstance();
+    if (NS_WARN_IF(!staticSink) ||
+        NS_WARN_IF(!staticSink->EnsureInitActiveTIPKeyboard())) {
+      return false;
+    }
+    return staticSink->IsATOKActiveInternal() &&
+           (staticSink->IsATOK2011ActiveInternal() ||
+            staticSink->IsATOK2012ActiveInternal() ||
+            staticSink->IsATOK2013ActiveInternal() ||
+            staticSink->IsATOK2014ActiveInternal() ||
+            staticSink->IsATOK2015ActiveInternal() ||
+            staticSink->IsATOK2016ActiveInternal());
+  }
+
+private:
   /****************************************************************************
    * Japanese TIP
    ****************************************************************************/
 
   // Note that TIP name may depend on the language of the environment.
   // For example, some TIP may use localized name for its target language
   // environment but English name for the others.
 
-  bool IsMSJapaneseIMEActive() const
+  bool IsMSJapaneseIMEActiveInternal() const
   {
     // FYI: Name of MS-IME for Japanese is same as MS-IME for Korean.
     //      Therefore, we need to check the langid too.
     return mLangID == 0x411 &&
       (mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft IME") ||
        mActiveTIPKeyboardDescription.Equals(
          NS_LITERAL_STRING(u"Microsoft \xC785\xB825\xAE30")) ||
        mActiveTIPKeyboardDescription.Equals(
          NS_LITERAL_STRING(u"\x5FAE\x8F6F\x8F93\x5165\x6CD5")) ||
        mActiveTIPKeyboardDescription.Equals(
          NS_LITERAL_STRING(u"\x5FAE\x8EDF\x8F38\x5165\x6CD5")));
   }
 
-  bool IsMSOfficeJapaneseIME2010Active() const
+  bool IsMSOfficeJapaneseIME2010ActiveInternal() const
   {
     // {54EDCC94-1524-4BB1-9FB7-7BABE4F4CA64}
     static const GUID kGUID = {
       0x54EDCC94, 0x1524, 0x4BB1,
         { 0x9F, 0xB7, 0x7B, 0xAB, 0xE4, 0xF4, 0xCA, 0x64 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOKActive() const
+  bool IsATOKActiveInternal() const
   {
     // FYI: Name of ATOK includes the release year like "ATOK 2015".
     return StringBeginsWith(mActiveTIPKeyboardDescription,
                             NS_LITERAL_STRING("ATOK "));
   }
 
-  bool IsATOK2011Active() const
+  bool IsATOK2011ActiveInternal() const
   {
     // {F9C24A5C-8A53-499D-9572-93B2FF582115}
     static const GUID kGUID = {
       0xF9C24A5C, 0x8A53, 0x499D,
         { 0x95, 0x72, 0x93, 0xB2, 0xFF, 0x58, 0x21, 0x15 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2012Active() const
+  bool IsATOK2012ActiveInternal() const
   {
     // {1DE01562-F445-401B-B6C3-E5B18DB79461}
     static const GUID kGUID = {
       0x1DE01562, 0xF445, 0x401B,
         { 0xB6, 0xC3, 0xE5, 0xB1, 0x8D, 0xB7, 0x94, 0x61 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2013Active() const
+  bool IsATOK2013ActiveInternal() const
   {
     // {3C4DB511-189A-4168-B6EA-BFD0B4C85615}
     static const GUID kGUID = {
       0x3C4DB511, 0x189A, 0x4168,
         { 0xB6, 0xEA, 0xBF, 0xD0, 0xB4, 0xC8, 0x56, 0x15 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2014Active() const
+  bool IsATOK2014ActiveInternal() const
   {
     // {4EF33B79-6AA9-4271-B4BF-9321C279381B}
     static const GUID kGUID = {
       0x4EF33B79, 0x6AA9, 0x4271,
         { 0xB4, 0xBF, 0x93, 0x21, 0xC2, 0x79, 0x38, 0x1B }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2015Active() const
+  bool IsATOK2015ActiveInternal() const
   {
     // {EAB4DC00-CE2E-483D-A86A-E6B99DA9599A}
     static const GUID kGUID = {
       0xEAB4DC00, 0xCE2E, 0x483D,
         { 0xA8, 0x6A, 0xE6, 0xB9, 0x9D, 0xA9, 0x59, 0x9A }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2016Active() const
+  bool IsATOK2016ActiveInternal() const
   {
     // {0B557B4C-5740-4110-A60A-1493FA10BF2B}
     static const GUID kGUID = {
       0x0B557B4C, 0x5740, 0x4110,
         { 0xA6, 0x0A, 0x14, 0x93, 0xFA, 0x10, 0xBF, 0x2B }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  // Note that ATOK 2011 - 2016 refers native caret position for deciding its
-  // popup window position.
-  bool IsATOKReferringNativeCaretActive() const
-  {
-    return IsATOKActive() &&
-           (IsATOK2011Active() || IsATOK2012Active() || IsATOK2013Active() ||
-            IsATOK2014Active() || IsATOK2015Active() || IsATOK2016Active());
-  }
-
   /****************************************************************************
    * Traditional Chinese TIP
    ****************************************************************************/
 
-  bool IsMSChangJieActive() const
+  bool IsMSChangJieActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft ChangJie") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x4ED3\x9889")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x5009\x9821"));
   }
 
-  bool IsMSQuickQuickActive() const
+  bool IsMSQuickQuickActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Quick") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x901F\x6210")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x901F\x6210"));
   }
 
-  bool IsFreeChangJieActive() const
+  bool IsFreeChangJieActiveInternal() const
   {
     // FYI: The TIP name is misspelled...
     return mActiveTIPKeyboardDescription.EqualsLiteral("Free CangJie IME 10");
   }
 
-  bool IsEasyChangjeiActive() const
+  bool IsEasyChangjeiActiveInternal() const
   {
     return
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(
           u"\x4E2D\x6587 (\x7E41\x9AD4) - \x6613\x9821\x8F38\x5165\x6CD5"));
   }
 
   /****************************************************************************
    * Simplified Chinese TIP
    ****************************************************************************/
 
-  bool IsMSPinyinActive() const
+  bool IsMSPinyinActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Pinyin") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x62FC\x97F3")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x62FC\x97F3"));
   }
 
-  bool IsMSWubiActive() const
+  bool IsMSWubiActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Wubi") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x4E94\x7B14")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x4E94\x7B46"));
   }
 
 public: // ITfInputProcessorProfileActivationSink
   STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID,
                            HKL, DWORD);
 
 private:
   TSFStaticSink();
   virtual ~TSFStaticSink() {}
 
+  bool EnsureInitActiveTIPKeyboard();
+
   void Destroy();
 
   void GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
                          REFGUID aProfile, nsAString& aDescription);
   bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
                              REFGUID aProfile);
 
   // Cookie of installing ITfInputProcessorProfileActivationSink
@@ -1558,18 +1623,16 @@ TSFTextStore::Init(nsWindowBase* aWidget
   if (NS_WARN_IF(!aWidget) || NS_WARN_IF(aWidget->Destroyed())) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED due to being initialized with "
        "destroyed widget",
        this));
     return false;
   }
 
-  TSFStaticSink::GetInstance()->EnsureInitActiveTIPKeyboard();
-
   if (mDocumentMgr) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED due to already initialized",
        this));
     return false;
   }
 
   mWidget = aWidget;
@@ -2288,22 +2351,23 @@ TSFTextStore::QueryInsert(LONG acpTestSt
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::QueryInsert() FAILED due to "
        "wrong argument", this));
     return E_INVALIDARG;
   }
 
   // XXX need to adjust to cluster boundary
   // Assume we are given good offsets for now
-  const TSFStaticSink* kSink = TSFStaticSink::GetInstance();
   if (IsWin8OrLater() && !mComposition.IsComposing() &&
       ((sHackQueryInsertForMSTraditionalTIP &&
-         (kSink->IsMSChangJieActive() || kSink->IsMSQuickQuickActive())) ||
+         (TSFStaticSink::IsMSChangJieActive() ||
+          TSFStaticSink::IsMSQuickQuickActive())) ||
        (sHackQueryInsertForMSSimplifiedTIP &&
-         (kSink->IsMSPinyinActive() || kSink->IsMSWubiActive())))) {
+         (TSFStaticSink::IsMSPinyinActive() ||
+          TSFStaticSink::IsMSWubiActive())))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Warning,
       ("0x%p   TSFTextStore::QueryInsert() WARNING using different "
        "result for the TIP", this));
     // Chinese TIPs of Microsoft assume that QueryInsert() returns selected
     // range which should be removed.
     *pacpResultStart = acpTestStart;
     *pacpResultEnd = acpTestEnd;
   } else {
@@ -2583,31 +2647,37 @@ TSFTextStore::GetDisplayAttribute(ITfPro
   if (VT_I4 != propValue.vt) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
        "ITfProperty::GetValue() returns non-VT_I4 value", this));
     ::VariantClear(&propValue);
     return E_FAIL;
   }
 
-  NS_ENSURE_TRUE(sCategoryMgr, E_FAIL);
+  RefPtr<ITfCategoryMgr> categoryMgr = GetCategoryMgr();
+  if (NS_WARN_IF(!categoryMgr)) {
+    return E_FAIL;
+  }
   GUID guid;
-  hr = sCategoryMgr->GetGUID(DWORD(propValue.lVal), &guid);
+  hr = categoryMgr->GetGUID(DWORD(propValue.lVal), &guid);
   ::VariantClear(&propValue);
   if (FAILED(hr)) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
        "ITfCategoryMgr::GetGUID() failed", this));
     return hr;
   }
 
-  NS_ENSURE_TRUE(sDisplayAttrMgr, E_FAIL);
+  RefPtr<ITfDisplayAttributeMgr> displayAttrMgr = GetDisplayAttributeMgr();
+  if (NS_WARN_IF(!displayAttrMgr)) {
+    return E_FAIL;
+  }
   RefPtr<ITfDisplayAttributeInfo> info;
-  hr = sDisplayAttrMgr->GetDisplayAttributeInfo(guid, getter_AddRefs(info),
-                                                nullptr);
+  hr = displayAttrMgr->GetDisplayAttributeInfo(guid, getter_AddRefs(info),
+                                               nullptr);
   if (FAILED(hr) || !info) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
        "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed", this));
     return hr;
   }
 
   hr = info->GetAttributeInfo(aResult);
@@ -3966,35 +4036,34 @@ TSFTextStore::GetTextExt(TsViewCookie vc
   mWaitingQueryLayout = false;
 
   // NOTE: TSF (at least on Win 8.1) doesn't return TS_E_NOLAYOUT to the
   // caller even if we return it.  It's converted to just E_FAIL.
   // However, this is fixed on Win 10.
 
   bool dontReturnNoLayoutError = false;
 
-  const TSFStaticSink* kSink = TSFStaticSink::GetInstance();
   if (mComposition.IsComposing() && mComposition.mStart < acpEnd &&
       mContentForTSF.IsLayoutChangedAt(acpEnd)) {
     const Selection& selectionForTSF = SelectionForTSFRef();
     // The bug of Microsoft Office IME 2010 for Japanese is similar to
     // MS-IME for Win 8.1 and Win 10.  Newer version of MS Office IME is not
     // released yet.  So, we can hack it without prefs  because there must be
     // no developers who want to disable this hack for tests.
     const bool kIsMSOfficeJapaneseIME2010 =
-      kSink->IsMSOfficeJapaneseIME2010Active();
+      TSFStaticSink::IsMSOfficeJapaneseIME2010Active();
     // MS IME for Japanese doesn't support asynchronous handling at deciding
     // its suggest list window position.  The feature was implemented
     // starting from Windows 8.  And also we may meet same trouble in e10s
     // mode on Win7.  So, we should never return TS_E_NOLAYOUT to MS IME for
     // Japanese.
     if (kIsMSOfficeJapaneseIME2010 ||
         ((sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar ||
           sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret) &&
-         kSink->IsMSJapaneseIMEActive())) {
+         TSFStaticSink::IsMSJapaneseIMEActive())) {
       // Basically, MS-IME tries to retrieve whole composition string rect
       // at deciding suggest window immediately after unlocking the document.
       // However, in e10s mode, the content hasn't updated yet in most cases.
       // Therefore, if the first character at the retrieving range rect is
       // available, we should use it as the result.
       if ((kIsMSOfficeJapaneseIME2010 ||
            sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar) &&
           acpStart < acpEnd) {
@@ -4028,44 +4097,44 @@ TSFTextStore::GetTextExt(TsViewCookie vc
     // suggest window.  In such case, ATOK tries to query rect of whole
     // composition string.
     // XXX For testing with legacy ATOK, we should hack it even if current ATOK
     //     refers native caret rect on windows whose window class is one of
     //     Mozilla window classes and we stop creating native caret for ATOK
     //     because creating native caret causes ATOK refers caret position
     //     when GetTextExt() returns TS_E_NOLAYOUT.
     else if (sDoNotReturnNoLayoutErrorToATOKOfCompositionString &&
-             kSink->IsATOKActive() &&
-             (!kSink->IsATOKReferringNativeCaretActive() ||
+             TSFStaticSink::IsATOKActive() &&
+             (!TSFStaticSink::IsATOKReferringNativeCaretActive() ||
               !sCreateNativeCaretForLegacyATOK) &&
              mComposition.mStart == acpStart &&
              mComposition.EndOffset() == acpEnd) {
       dontReturnNoLayoutError = true;
     }
     // Free ChangJie 2010 and Easy Changjei 1.0.12.0 doesn't handle
     // ITfContextView::GetTextExt() properly.  Prehaps, it's due to the bug of
     // TSF.  We need to check if this is necessary on Windows 10 before
     // disabling this on Windows 10.
     else if ((sDoNotReturnNoLayoutErrorToFreeChangJie &&
-              kSink->IsFreeChangJieActive()) ||
+              TSFStaticSink::IsFreeChangJieActive()) ||
              (sDoNotReturnNoLayoutErrorToEasyChangjei &&
-              kSink->IsEasyChangjeiActive())) {
+              TSFStaticSink::IsEasyChangjeiActive())) {
       acpEnd = mComposition.mStart;
       acpStart = std::min(acpStart, acpEnd);
       dontReturnNoLayoutError = true;
     }
     // Some Chinese TIPs of Microsoft doesn't show candidate window in e10s
     // mode on Win8 or later.
     else if (IsWin8OrLater() &&
              ((sDoNotReturnNoLayoutErrorToMSTraditionalTIP &&
-               (kSink->IsMSChangJieActive() ||
-                kSink->IsMSQuickQuickActive())) ||
+               (TSFStaticSink::IsMSChangJieActive() ||
+                TSFStaticSink::IsMSQuickQuickActive())) ||
               (sDoNotReturnNoLayoutErrorToMSSimplifiedTIP &&
-                (kSink->IsMSPinyinActive() ||
-                 kSink->IsMSWubiActive())))) {
+                (TSFStaticSink::IsMSPinyinActive() ||
+                 TSFStaticSink::IsMSWubiActive())))) {
       acpEnd = mComposition.mStart;
       acpStart = std::min(acpStart, acpEnd);
       dontReturnNoLayoutError = true;
     }
 
     // If we hack the queried range for active TIP, that means we should not
     // return TS_E_NOLAYOUT even if hacked offset is still modified.  So, as
     // far as possible, we should adjust the offset.
@@ -4205,17 +4274,17 @@ TSFTextStore::GetTextExt(TsViewCookie vc
   // not equal if text rect was clipped
   *pfClipped = !::EqualRect(prc, &textRect);
 
   // ATOK 2011 - 2016 refers native caret position and size on windows whose
   // class name is one of Mozilla's windows for deciding candidate window
   // position.  Therefore, we need to create native caret only when ATOK 2011 -
   // 2016 is active.
   if (sCreateNativeCaretForLegacyATOK &&
-      kSink->IsATOKReferringNativeCaretActive() &&
+      TSFStaticSink::IsATOKReferringNativeCaretActive() &&
       mComposition.IsComposing() &&
       mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart &&
       mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) {
     CreateNativeCaret();
   }
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p   TSFTextStore::GetTextExt() succeeded: "
@@ -6002,37 +6071,20 @@ TSFTextStore::Initialize()
   bool enableTsf = Preferences::GetBool(kPrefNameEnableTSF, false);
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("  TSFTextStore::Initialize(), TSF is %s",
      enableTsf ? "enabled" : "disabled"));
   if (!enableTsf) {
     return;
   }
 
-  // XXX MSDN documents that ITfInputProcessorProfiles is available only on
-  //     desktop apps.  However, there is no known way to obtain
-  //     ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
-  //     instance.
-  RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
-  HRESULT hr =
-    ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
-                       CLSCTX_INPROC_SERVER,
-                       IID_ITfInputProcessorProfiles,
-                       getter_AddRefs(inputProcessorProfiles));
-  if (FAILED(hr) || !inputProcessorProfiles) {
-    MOZ_LOG(sTextStoreLog, LogLevel::Error,
-      ("  TSFTextStore::Initialize() FAILED to create input processor "
-       "profiles, hr=0x%08X", hr));
-    return;
-  }
-
   RefPtr<ITfThreadMgr> threadMgr;
-  hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr,
-                          CLSCTX_INPROC_SERVER, IID_ITfThreadMgr,
-                          getter_AddRefs(threadMgr));
+  HRESULT hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr,
+                                  CLSCTX_INPROC_SERVER, IID_ITfThreadMgr,
+                                  getter_AddRefs(threadMgr));
   if (FAILED(hr) || !threadMgr) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::Initialize() FAILED to "
        "create the thread manager, hr=0x%08X", hr));
     return;
   }
 
   RefPtr<ITfMessagePump> messagePump;
@@ -6057,38 +6109,16 @@ TSFTextStore::Initialize()
 
   hr = threadMgr->Activate(&sClientId);
   if (FAILED(hr)) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::Initialize() FAILED to activate, hr=0x%08X", hr));
     return;
   }
 
-  RefPtr<ITfDisplayAttributeMgr> displayAttributeMgr;
-  hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr,
-                          CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr,
-                          getter_AddRefs(displayAttributeMgr));
-  if (FAILED(hr) || !displayAttributeMgr) {
-    MOZ_LOG(sTextStoreLog, LogLevel::Error,
-      ("  TSFTextStore::Initialize() FAILED to create "
-       "a display attribute manager instance, hr=0x%08X", hr));
-    return;
-  }
-
-  RefPtr<ITfCategoryMgr> categoryMgr;
-  hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr,
-                          CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr,
-                          getter_AddRefs(categoryMgr));
-  if (FAILED(hr) || !categoryMgr) {
-    MOZ_LOG(sTextStoreLog, LogLevel::Error,
-      ("  TSFTextStore::Initialize() FAILED to create "
-       "a category manager instance, hr=0x%08X", hr));
-    return;
-  }
-
   RefPtr<ITfDocumentMgr> disabledDocumentMgr;
   hr = threadMgr->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr));
   if (FAILED(hr) || !disabledDocumentMgr) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::Initialize() FAILED to create "
        "a document manager for disabled mode, hr=0x%08X", hr));
     return;
   }
@@ -6103,34 +6133,19 @@ TSFTextStore::Initialize()
       ("  TSFTextStore::Initialize() FAILED to create "
        "a context for disabled mode, hr=0x%08X", hr));
     return;
   }
 
   MarkContextAsKeyboardDisabled(disabledContext);
   MarkContextAsEmpty(disabledContext);
 
-  MOZ_LOG(sTextStoreLog, LogLevel::Info,
-    ("  TSFTextStore::Initialize() is creating "
-     "a TSFStaticSink instance..."));
-  TSFStaticSink* staticSink = TSFStaticSink::GetInstance();
-  if (!staticSink->Init(threadMgr, inputProcessorProfiles)) {
-    TSFStaticSink::Shutdown();
-    MOZ_LOG(sTextStoreLog, LogLevel::Error,
-      ("  TSFTextStore::Initialize() FAILED to initialize TSFStaticSink "
-       "instance"));
-    return;
-  }
-
-  sInputProcessorProfiles = inputProcessorProfiles;
   sThreadMgr = threadMgr;
   sMessagePump = messagePump;
   sKeystrokeMgr = keystrokeMgr;
-  sDisplayAttrMgr = displayAttributeMgr;
-  sCategoryMgr = categoryMgr;
   sDisabledDocumentMgr = disabledDocumentMgr;
   sDisabledContext = disabledContext;
 
   sCreateNativeCaretForLegacyATOK =
     Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true);
   sDoNotReturnNoLayoutErrorToATOKOfCompositionString =
     Preferences::GetBool(
       "intl.tsf.hack.atok.do_not_return_no_layout_error_of_composition_string",
@@ -6161,35 +6176,117 @@ TSFTextStore::Initialize()
     Preferences::GetBool(
       "intl.tsf.hack.ms_simplified_chinese.query_insert_result", true);
   sHackQueryInsertForMSTraditionalTIP =
     Preferences::GetBool(
       "intl.tsf.hack.ms_traditional_chinese.query_insert_result", true);
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("  TSFTextStore::Initialize(), sThreadMgr=0x%p, "
-     "sClientId=0x%08X, sDisplayAttrMgr=0x%p, "
-     "sCategoryMgr=0x%p, sDisabledDocumentMgr=0x%p, sDisabledContext=%p, "
+     "sClientId=0x%08X, sDisabledDocumentMgr=0x%p, sDisabledContext=%p, "
      "sCreateNativeCaretForLegacyATOK=%s, "
      "sDoNotReturnNoLayoutErrorToATOKOfCompositionString=%s, "
      "sDoNotReturnNoLayoutErrorToFreeChangJie=%s, "
      "sDoNotReturnNoLayoutErrorToEasyChangjei=%s, "
      "sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar=%s, "
      "sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret=%s",
-     sThreadMgr.get(), sClientId, sDisplayAttrMgr.get(),
-     sCategoryMgr.get(), sDisabledDocumentMgr.get(), sDisabledContext.get(),
+     sThreadMgr.get(), sClientId,
+     sDisabledDocumentMgr.get(), sDisabledContext.get(),
      GetBoolName(sCreateNativeCaretForLegacyATOK),
      GetBoolName(sDoNotReturnNoLayoutErrorToATOKOfCompositionString),
      GetBoolName(sDoNotReturnNoLayoutErrorToFreeChangJie),
      GetBoolName(sDoNotReturnNoLayoutErrorToEasyChangjei),
      GetBoolName(sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar),
      GetBoolName(sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret)));
 }
 
 // static
+already_AddRefed<ITfThreadMgr>
+TSFTextStore::GetThreadMgr()
+{
+  RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
+  return threadMgr.forget();
+}
+
+// static
+already_AddRefed<ITfDisplayAttributeMgr>
+TSFTextStore::GetDisplayAttributeMgr()
+{
+  RefPtr<ITfDisplayAttributeMgr> displayAttributeMgr;
+  if (sDisplayAttrMgr) {
+    displayAttributeMgr = sDisplayAttrMgr;
+    return displayAttributeMgr.forget();
+  }
+
+  HRESULT hr =
+    ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr,
+                       CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr,
+                       getter_AddRefs(displayAttributeMgr));
+  if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!displayAttributeMgr)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("TSFTextStore::GetDisplayAttributeMgr() FAILED to create "
+       "a display attribute manager instance, hr=0x%08X", hr));
+    return nullptr;
+  }
+  sDisplayAttrMgr = displayAttributeMgr;
+  return displayAttributeMgr.forget();
+}
+
+// static
+already_AddRefed<ITfCategoryMgr>
+TSFTextStore::GetCategoryMgr()
+{
+  RefPtr<ITfCategoryMgr> categoryMgr;
+  if (sCategoryMgr) {
+    categoryMgr = sCategoryMgr;
+    return categoryMgr.forget();
+  }
+  HRESULT hr =
+    ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr,
+                       CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr,
+                       getter_AddRefs(categoryMgr));
+  if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!categoryMgr)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("TSFTextStore::GetCategoryMgr() FAILED to create "
+       "a category manager instance, hr=0x%08X", hr));
+    return nullptr;
+  }
+  sCategoryMgr = categoryMgr;
+  return categoryMgr.forget();
+}
+
+// static
+already_AddRefed<ITfInputProcessorProfiles>
+TSFTextStore::GetInputProcessorProfiles()
+{
+  RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
+  if (sInputProcessorProfiles) {
+    inputProcessorProfiles = sInputProcessorProfiles;
+    return inputProcessorProfiles.forget();
+  }
+  // XXX MSDN documents that ITfInputProcessorProfiles is available only on
+  //     desktop apps.  However, there is no known way to obtain
+  //     ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
+  //     instance.
+  HRESULT hr =
+    ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
+                       CLSCTX_INPROC_SERVER,
+                       IID_ITfInputProcessorProfiles,
+                       getter_AddRefs(inputProcessorProfiles));
+  if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!inputProcessorProfiles)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("TSFTextStore::GetInputProcessorProfiles() FAILED to create input "
+       "processor profiles, hr=0x%08X", hr));
+    return nullptr;
+  }
+  sInputProcessorProfiles = inputProcessorProfiles;
+  return inputProcessorProfiles.forget();
+}
+
+// static
 void
 TSFTextStore::Terminate()
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Info, ("TSFTextStore::Terminate()"));
 
   TSFStaticSink::Shutdown();
 
   sDisplayAttrMgr = nullptr;
@@ -6296,17 +6393,17 @@ TSFTextStore::IsIMM_IMEActive()
 {
   return TSFStaticSink::IsIMM_IMEActive();
 }
 
 // static
 bool
 TSFTextStore::IsMSJapaneseIMEActive()
 {
-  return TSFStaticSink::GetInstance()->IsMSJapaneseIMEActive();
+  return TSFStaticSink::IsMSJapaneseIMEActive();
 }
 
 /******************************************************************/
 /* TSFTextStore::Composition                                       */
 /******************************************************************/
 
 void
 TSFTextStore::Composition::Start(ITfCompositionView* aCompositionView,
@@ -6615,26 +6712,28 @@ TSFTextStore::MouseTracker::OnMouseButto
   return SUCCEEDED(hr) && eaten;
 }
 
 #ifdef DEBUG
 // static
 bool
 TSFTextStore::CurrentKeyboardLayoutHasIME()
 {
-  if (!sInputProcessorProfiles) {
+  RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
+    TSFTextStore::GetInputProcessorProfiles();
+  if (!inputProcessorProfiles) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED due to "
        "there is no input processor profiles instance"));
     return false;
   }
   RefPtr<ITfInputProcessorProfileMgr> profileMgr;
   HRESULT hr =
-    sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
-                                            getter_AddRefs(profileMgr));
+    inputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
+                                           getter_AddRefs(profileMgr));
   if (FAILED(hr) || !profileMgr) {
     // On Windows Vista or later, ImmIsIME() API always returns true.
     // If we failed to obtain the profile manager, we cannot know if current
     // keyboard layout has IME.
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
        "ITfInputProcessorProfileMgr"));
     return false;
--- a/widget/windows/TSFTextStore.h
+++ b/widget/windows/TSFTextStore.h
@@ -41,26 +41,28 @@ struct ITfThreadMgr;
 struct ITfDocumentMgr;
 struct ITfDisplayAttributeMgr;
 struct ITfCategoryMgr;
 class nsWindow;
 
 namespace mozilla {
 namespace widget {
 
+class TSFStaticSink;
 struct MSGResult;
 
 /*
  * Text Services Framework text store
  */
 
 class TSFTextStore final : public ITextStoreACP
                          , public ITfContextOwnerCompositionSink
                          , public ITfMouseTrackerACP
 {
+  friend class TSFStaticSink;
 private:
   typedef IMENotification::SelectionChangeDataBase SelectionChangeDataBase;
   typedef IMENotification::SelectionChangeData SelectionChangeData;
   typedef IMENotification::TextChangeDataBase TextChangeDataBase;
   typedef IMENotification::TextChangeData TextChangeData;
 
 public: /*IUnknown*/
   STDMETHODIMP          QueryInterface(REFIID, void**);
@@ -997,36 +999,41 @@ protected:
   bool                         mDestroyed;
   // While the instance is being destroyed, this is set to true for avoiding
   // recursive Destroy() calls.
   bool                         mBeingDestroyed;
 
 
   // TSF thread manager object for the current application
   static StaticRefPtr<ITfThreadMgr> sThreadMgr;
+  static already_AddRefed<ITfThreadMgr> GetThreadMgr();
   // sMessagePump is QI'ed from sThreadMgr
   static StaticRefPtr<ITfMessagePump> sMessagePump;
   // sKeystrokeMgr is QI'ed from sThreadMgr
   static StaticRefPtr<ITfKeystrokeMgr> sKeystrokeMgr;
   // TSF display attribute manager
   static StaticRefPtr<ITfDisplayAttributeMgr> sDisplayAttrMgr;
+  static already_AddRefed<ITfDisplayAttributeMgr> GetDisplayAttributeMgr();
   // TSF category manager
   static StaticRefPtr<ITfCategoryMgr> sCategoryMgr;
+  static already_AddRefed<ITfCategoryMgr> GetCategoryMgr();
 
   // Current text store which is managing a keyboard enabled editor (i.e.,
   // editable editor).  Currently only ONE TSFTextStore instance is ever used,
   // although Create is called when an editor is focused and Destroy called
   // when the focused editor is blurred.
   static StaticRefPtr<TSFTextStore> sEnabledTextStore;
 
   // For IME (keyboard) disabled state:
   static StaticRefPtr<ITfDocumentMgr> sDisabledDocumentMgr;
   static StaticRefPtr<ITfContext> sDisabledContext;
 
   static StaticRefPtr<ITfInputProcessorProfiles> sInputProcessorProfiles;
+  static already_AddRefed<ITfInputProcessorProfiles>
+           GetInputProcessorProfiles();
 
   // TSF client ID for the current application
   static DWORD sClientId;
 
   // Enables/Disables hack for specific TIP.
   static bool sCreateNativeCaretForLegacyATOK;
   static bool sDoNotReturnNoLayoutErrorToATOKOfCompositionString;
   static bool sDoNotReturnNoLayoutErrorToMSSimplifiedTIP;
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -174,17 +174,17 @@ IMEHandler::ProcessMessage(nsWindow* aWi
       return true;
     }
     // If we don't support IMM in TSF mode, we don't use IMMHandler.
     if (!sIsIMMEnabled) {
       return false;
     }
     // IME isn't implemented with IMM, IMMHandler shouldn't handle any
     // messages.
-    if (!TSFTextStore::IsIMM_IMEActive()) {
+    if (!IsIMMActive()) {
       return false;
     }
   }
 #endif // #ifdef NS_ENABLE_TSF
 
   return IMMHandler::ProcessMessage(aWindow, aMessage, aWParam, aLParam,
                                     aResult);
 }
@@ -416,17 +416,17 @@ IMEHandler::OnDestroyWindow(nsWindow* aW
 }
 
 #ifdef NS_ENABLE_TSF
 // static
 bool
 IMEHandler::NeedsToAssociateIMC()
 {
   if (sAssociateIMCOnlyWhenIMM_IMEActive) {
-    return TSFTextStore::IsIMM_IMEActive();
+    return IsIMMActive();
   }
 
   // Even if IMC should be associated with focused widget with non-IMM-IME,
   // we need to avoid crash bug of MS-IME for Japanese on Win10.  It crashes
   // while we're associating default IME to a window when it's active.
   static const bool sDoNotAssociateIMCWhenMSJapaneseIMEActiveOnWin10 =
     IsWin10OrLater() &&
     Preferences::GetBool(
@@ -551,16 +551,21 @@ IMEHandler::CurrentKeyboardLayoutHasIME(
   return IMMHandler::IsIMEAvailable();
 }
 #endif // #ifdef DEBUG
 
 // static
 void
 IMEHandler::OnKeyboardLayoutChanged()
 {
+  // Be aware, this method won't be called until TSFStaticSink starts to
+  // observe active TIP change.  If you need to be notified of this, you
+  // need to create TSFStaticSink::Observe() or something and call it
+  // TSFStaticSink::EnsureInitActiveTIPKeyboard() forcibly.
+
   if (!sIsIMMEnabled || !IsTSFAvailable()) {
     return;
   }
 
   // If there is no TSFTextStore which has focus, i.e., no editor has focus,
   // nothing to do here.
   nsWindowBase* windowBase = TSFTextStore::GetEnabledWindowBase();
   if (!windowBase) {
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1800,121 +1800,51 @@ uint32_t
 WinUtils::GetMaxTouchPoints()
 {
   if (IsTouchDeviceSupportPresent()) {
     return GetSystemMetrics(SM_MAXIMUMTOUCHES);
   }
   return 0;
 }
 
-#pragma pack(push, 1)
-typedef struct REPARSE_DATA_BUFFER {
-  ULONG  ReparseTag;
-  USHORT ReparseDataLength;
-  USHORT Reserved;
-  union {
-    struct {
-      USHORT SubstituteNameOffset;
-      USHORT SubstituteNameLength;
-      USHORT PrintNameOffset;
-      USHORT PrintNameLength;
-      ULONG  Flags;
-      WCHAR  PathBuffer[1];
-    } SymbolicLinkReparseBuffer;
-    struct {
-      USHORT SubstituteNameOffset;
-      USHORT SubstituteNameLength;
-      USHORT PrintNameOffset;
-      USHORT PrintNameLength;
-      WCHAR  PathBuffer[1];
-    } MountPointReparseBuffer;
-    struct {
-      UCHAR DataBuffer[1];
-    } GenericReparseBuffer;
-  };
-} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
-#pragma pack(pop)
-
 /* static */
 bool
-WinUtils::ResolveMovedUsersFolder(std::wstring& aPath)
+WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath)
 {
-  wchar_t* usersPath;
-  if (FAILED(SHGetKnownFolderPath(FOLDERID_UserProfiles, 0, nullptr,
-                                  &usersPath))) {
-    return false;
-  }
+  wchar_t path[MAX_PATH] = { 0 };
 
-  // Ensure usersPath gets freed properly.
-  UniquePtr<wchar_t, CoTaskMemFreePolicy> autoFreePath(usersPath);
+  nsAutoHandle handle(
+    ::CreateFileW(aPath.c_str(),
+                  0,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                  nullptr,
+                  OPEN_EXISTING,
+                  FILE_FLAG_BACKUP_SEMANTICS,
+                  nullptr));
 
-  // Is aPath in Users folder?
-  size_t usersLen = wcslen(usersPath);
-  if (_wcsnicmp(aPath.c_str(), usersPath, usersLen) != 0 ||
-      aPath[usersLen] != L'\\') {
-    return true;
-  }
-
-  DWORD attributes = ::GetFileAttributesW(usersPath);
-  if (attributes == INVALID_FILE_ATTRIBUTES) {
+  if (handle == INVALID_HANDLE_VALUE) {
     return false;
   }
 
-  // Junction points are implemented as reparse points, is the Users folder one?
-  if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
-    return true;
+  DWORD pathLen = GetFinalPathNameByHandleW(
+    handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+  if (pathLen == 0 || pathLen >= MAX_PATH) {
+    return false;
   }
+  aPath = path;
 
-  // Get the reparse point data.
-  nsAutoHandle usersHandle(
-    ::CreateFileW(usersPath, 0,
-                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                  nullptr, OPEN_EXISTING,
-                  FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
-                  nullptr));
-
-  char maxReparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0};
-  REPARSE_DATA_BUFFER* reparseBuf = (REPARSE_DATA_BUFFER*)maxReparseBuf;
-  DWORD bytesReturned = 0;
-  if (!::DeviceIoControl(usersHandle, FSCTL_GET_REPARSE_POINT, nullptr, 0,
-                         reparseBuf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
-                         &bytesReturned, nullptr)) {
-    return false;
+  // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
+  // but that confuses some APIs so strip it off. It will also put
+  // '\\?\UNC\' in front of network paths, we convert that to '\\'.
+  if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) {
+    aPath.erase(2, 6);
+  } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) {
+    aPath.erase(0, 4);
   }
 
-  // Check to see if the reparse point is a junction point.
-  if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
-    return true;
-  }
-
-  // The offset and length are in bytes. Length doesn't include null.
-  wchar_t* substituteName = reparseBuf->MountPointReparseBuffer.PathBuffer +
-    reparseBuf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
-  std::wstring::size_type substituteLen =
-    reparseBuf->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
-
-  // If the substitute path starts with the NT namespace then remove it.
-  if (wcsncmp(substituteName, kNTPrefix, kNTPrefixLen) == 0) {
-    substituteName += kNTPrefixLen;
-    substituteLen -= kNTPrefixLen;
-  }
-
-  // Check that what remains looks like a drive letter path.
-  if (substituteName[1] != L':' || substituteName[2] != L'\\') {
-    return false;
-  }
-
-  // The documentation for SHGetKnownFolderPath says that it doesn't return a
-  // trailing backslash. The REPARSE_DATA_BUFFER path doesn't seem to have one
-  // either, but the documentation doesn't mention it, so let's make sure.
-  if (substituteName[substituteLen - 1] == L'\\') {
-    --substituteLen;
-  }
-
-  aPath.replace(0, usersLen, substituteName, substituteLen);
   return true;
 }
 
 /* static */
 bool
 WinUtils::SanitizePath(const wchar_t* aInputPath, nsAString& aOutput)
 {
   aOutput.Truncate();
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -458,26 +458,26 @@ public:
   * The maximum number of simultaneous touch contacts supported by the device.
   * In the case of devices with multiple digitizers (e.g. multiple touch screens),
   * the value will be the maximum of the set of maximum supported contacts by
   * each individual digitizer.
   */
   static uint32_t GetMaxTouchPoints();
 
   /**
-   * Detect if path is within the Users folder and Users is actually a junction
-   * point to another folder.
-   * If this is detected it will change the path to the actual path.
+   * Fully resolves a path to its final path name. So if path contains
+   * junction points or symlinks to other folders, we'll resolve the path
+   * fully to the actual path that the links target.
    *
    * @param aPath path to be resolved.
    * @return true if successful, including if nothing needs to be changed.
    *         false if something failed or aPath does not exist, aPath will
    *               remain unchanged.
    */
-  static bool ResolveMovedUsersFolder(std::wstring& aPath);
+  static bool ResolveJunctionPointsAndSymLinks(std::wstring& aPath);
 
   static void Initialize();
 
   static bool ShouldHideScrollbars();
 
   /**
    * This function normalizes the input path, converts short filenames to long
    * filenames, and substitutes environment variables for system paths.