Merge inbound to mozilla-central r=merge a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Sun, 12 Nov 2017 00:05:11 +0200
changeset 696849 f2eacbcd8f3d65d25bac4d9f49d0f692f109af3c
parent 696842 58b6b371c2f85614ab52dc2d6235e12af020dc1f (current diff)
parent 696848 e2dc5d006fc76e2e013d0d07a9707c20c2b3227c (diff)
child 696850 aabfc14671b55983e1c3053989a4c3b7c5691aaa
push id88807
push userbmo:ato@sny.no
push dateSun, 12 Nov 2017 01:46:20 +0000
reviewersmerge, merge
milestone58.0a1
Merge inbound to mozilla-central r=merge a=merge
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,11 +1,11 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Taken from upstream commit: be179268c9b89390c13bdc9c4cca6000f6f583e5
+Taken from upstream commit: 09d6d4f93135367b2639d78ad884434f73ab449c
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @15.6.2
 - react-dom @15.6.2
 - webpack @3.8.1
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -3734,10 +3734,8 @@ html[dir="rtl"] .dropdown {
 
 .search-bar .result-list {
   border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .theme-dark .result-list {
   background-color: var(--theme-body-background);
 }
-
-/*# sourceMappingURL=debugger.css.map*/
\ No newline at end of file
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -17607,17 +17607,17 @@ exports.default = _Svg2.default;
 
 const {
   originalToGeneratedId,
   generatedToOriginalId,
   isGeneratedId,
   isOriginalId
 } = __webpack_require__(1389);
 
-const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1363);
+const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1390);
 
 const dispatcher = new WorkerDispatcher();
 
 const getOriginalURLs = dispatcher.task("getOriginalURLs");
 const getGeneratedLocation = dispatcher.task("getGeneratedLocation");
 const getOriginalLocation = dispatcher.task("getOriginalLocation");
 const getLocationScopes = dispatcher.task("getLocationScopes");
 const getOriginalSourceText = dispatcher.task("getOriginalSourceText");
@@ -18272,19 +18272,19 @@ WorkerDispatcher.prototype = {
         this.worker.postMessage({ id, method, args });
 
         const listener = ({ data: result }) => {
           if (result.id !== id) {
             return;
           }
 
           if (!this.worker) {
-            reject("Oops, The worker has shutdown!");
             return;
           }
+
           this.worker.removeEventListener("message", listener);
           if (result.error) {
             reject(result.error);
           } else {
             resolve(result.response);
           }
         };
 
@@ -20805,19 +20805,191 @@ module.exports = {
   generatedToOriginalId,
   isOriginalId,
   isGeneratedId,
   getContentType,
   contentMapForTesting: contentMap
 };
 
 /***/ }),
-/* 1390 */,
-/* 1391 */,
-/* 1392 */,
+/* 1390 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+const networkRequest = __webpack_require__(1391);
+const workerUtils = __webpack_require__(1392);
+
+module.exports = {
+  networkRequest,
+  workerUtils
+};
+
+/***/ }),
+/* 1391 */
+/***/ (function(module, exports) {
+
+/* 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/. */
+
+function networkRequest(url, opts) {
+  return fetch(url, {
+    cache: opts.loadFromCache ? "default" : "no-cache"
+  }).then(res => {
+    if (res.status >= 200 && res.status < 300) {
+      return res.text().then(text => ({ content: text }));
+    }
+    return Promise.reject(`request failed with status ${res.status}`);
+  });
+}
+
+module.exports = networkRequest;
+
+/***/ }),
+/* 1392 */
+/***/ (function(module, exports) {
+
+function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
+
+function WorkerDispatcher() {
+  this.msgId = 1;
+  this.worker = null;
+} /* 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/. */
+
+WorkerDispatcher.prototype = {
+  start(url) {
+    this.worker = new Worker(url);
+    this.worker.onerror = () => {
+      console.error(`Error in worker ${url}`);
+    };
+  },
+
+  stop() {
+    if (!this.worker) {
+      return;
+    }
+
+    this.worker.terminate();
+    this.worker = null;
+  },
+
+  task(method) {
+    return (...args) => {
+      return new Promise((resolve, reject) => {
+        const id = this.msgId++;
+        this.worker.postMessage({ id, method, args });
+
+        const listener = ({ data: result }) => {
+          if (result.id !== id) {
+            return;
+          }
+
+          if (!this.worker) {
+            reject("Oops, The worker has shutdown!");
+            return;
+          }
+          this.worker.removeEventListener("message", listener);
+          if (result.error) {
+            reject(result.error);
+          } else {
+            resolve(result.response);
+          }
+        };
+
+        this.worker.addEventListener("message", listener);
+      });
+    };
+  }
+};
+
+function workerHandler(publicInterface) {
+  return function (msg) {
+    const { id, method, args } = msg.data;
+    try {
+      const response = publicInterface[method].apply(undefined, args);
+      if (response instanceof Promise) {
+        response.then(val => self.postMessage({ id, response: val }),
+        // Error can't be sent via postMessage, so be sure to
+        // convert to string.
+        err => self.postMessage({ id, error: err.toString() }));
+      } else {
+        self.postMessage({ id, response });
+      }
+    } catch (error) {
+      // Error can't be sent via postMessage, so be sure to convert to
+      // string.
+      self.postMessage({ id, error: error.toString() });
+    }
+  };
+}
+
+function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
+  let streamingWorker = (() => {
+    var _ref = _asyncToGenerator(function* (id, tasks) {
+      let isWorking = true;
+
+      const intervalId = setTimeout(function () {
+        isWorking = false;
+      }, timeout);
+
+      const results = [];
+      while (tasks.length !== 0 && isWorking) {
+        const { callback, context, args } = tasks.shift();
+        const result = yield callback.call(context, args);
+        results.push(result);
+      }
+      worker.postMessage({ id, status: "pending", data: results });
+      clearInterval(intervalId);
+
+      if (tasks.length !== 0) {
+        yield streamingWorker(id, tasks);
+      }
+    });
+
+    return function streamingWorker(_x, _x2) {
+      return _ref.apply(this, arguments);
+    };
+  })();
+
+  return (() => {
+    var _ref2 = _asyncToGenerator(function* (msg) {
+      const { id, method, args } = msg.data;
+      const workerMethod = publicInterface[method];
+      if (!workerMethod) {
+        console.error(`Could not find ${method} defined in worker.`);
+      }
+      worker.postMessage({ id, status: "start" });
+
+      try {
+        const tasks = workerMethod(args);
+        yield streamingWorker(id, tasks);
+        worker.postMessage({ id, status: "done" });
+      } catch (error) {
+        worker.postMessage({ id, status: "error", error });
+      }
+    });
+
+    return function (_x3) {
+      return _ref2.apply(this, arguments);
+    };
+  })();
+}
+
+module.exports = {
+  WorkerDispatcher,
+  workerHandler,
+  streamingWorkerHandler
+};
+
+/***/ }),
 /* 1393 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
@@ -21815,16 +21987,19 @@ function setSymbols(sourceId) {
 
     const symbols = await (0, _parser.getSymbols)(source);
 
     dispatch({
       type: "SET_SYMBOLS",
       source,
       symbols
     });
+
+    dispatch(setEmptyLines(source.id));
+    dispatch(setSourceMetaData(source.id));
   };
 }
 
 function setEmptyLines(sourceId) {
   return async ({ dispatch, getState }) => {
     const sourceRecord = (0, _selectors.getSource)(getState(), sourceId);
     if (!sourceRecord) {
       return;
@@ -24777,19 +24952,17 @@ function loadSourceText(source) {
     });
 
     const newSource = (0, _selectors.getSource)(getState(), source.id).toJS();
     if (newSource.isWasm) {
       return;
     }
 
     await (0, _parser.setSource)(newSource);
-    await dispatch((0, _ast.setSymbols)(source.id));
-    await dispatch((0, _ast.setEmptyLines)(source.id));
-    await dispatch((0, _ast.setSourceMetaData)(source.id));
+    dispatch((0, _ast.setSymbols)(source.id));
   };
 }
 
 /***/ }),
 /* 1436 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -45762,10 +45935,9 @@ function timing(store) {
     mark(`${action.type}_end`);
     measure(`${action.type}`, `${action.type}_start`, `${action.type}_end`);
     return result;
   };
 }
 
 /***/ })
 /******/ ]);
-});
-//# sourceMappingURL=debugger.js.map
\ No newline at end of file
+});
\ No newline at end of file
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -35427,19 +35427,19 @@ WorkerDispatcher.prototype = {
         this.worker.postMessage({ id, method, args });
 
         const listener = ({ data: result }) => {
           if (result.id !== id) {
             return;
           }
 
           if (!this.worker) {
-            reject("Oops, The worker has shutdown!");
             return;
           }
+
           this.worker.removeEventListener("message", listener);
           if (result.error) {
             reject(result.error);
           } else {
             resolve(result.response);
           }
         };
 
@@ -41819,10 +41819,9 @@ function extendsComponent(classes) {
     }
   });
 
   return result;
 }
 
 /***/ })
 /******/ ]);
-});
-//# sourceMappingURL=parser-worker.js.map
\ No newline at end of file
+});
\ No newline at end of file
--- a/devtools/client/debugger/new/pretty-print-worker.js
+++ b/devtools/client/debugger/new/pretty-print-worker.js
@@ -158,19 +158,19 @@ WorkerDispatcher.prototype = {
         this.worker.postMessage({ id, method, args });
 
         const listener = ({ data: result }) => {
           if (result.id !== id) {
             return;
           }
 
           if (!this.worker) {
-            reject("Oops, The worker has shutdown!");
             return;
           }
+
           this.worker.removeEventListener("message", listener);
           if (result.error) {
             reject(result.error);
           } else {
             resolve(result.response);
           }
         };
 
@@ -7586,10 +7586,9 @@ SourceNode.prototype.toStringWithSourceM
 };
 
 exports.SourceNode = SourceNode;
 
 
 /***/ })
 
 /******/ });
-});
-//# sourceMappingURL=pretty-print-worker.js.map
\ No newline at end of file
+});
\ No newline at end of file
--- a/devtools/client/debugger/new/search-worker.js
+++ b/devtools/client/debugger/new/search-worker.js
@@ -741,17 +741,17 @@ exports.isLoaded = isLoaded;
 
 const {
   originalToGeneratedId,
   generatedToOriginalId,
   isGeneratedId,
   isOriginalId
 } = __webpack_require__(1389);
 
-const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1363);
+const { workerUtils: { WorkerDispatcher } } = __webpack_require__(1390);
 
 const dispatcher = new WorkerDispatcher();
 
 const getOriginalURLs = dispatcher.task("getOriginalURLs");
 const getGeneratedLocation = dispatcher.task("getGeneratedLocation");
 const getOriginalLocation = dispatcher.task("getOriginalLocation");
 const getLocationScopes = dispatcher.task("getLocationScopes");
 const getOriginalSourceText = dispatcher.task("getOriginalSourceText");
@@ -940,19 +940,19 @@ WorkerDispatcher.prototype = {
         this.worker.postMessage({ id, method, args });
 
         const listener = ({ data: result }) => {
           if (result.id !== id) {
             return;
           }
 
           if (!this.worker) {
-            reject("Oops, The worker has shutdown!");
             return;
           }
+
           this.worker.removeEventListener("message", listener);
           if (result.error) {
             reject(result.error);
           } else {
             resolve(result.response);
           }
         };
 
@@ -1119,16 +1119,194 @@ module.exports = {
   isOriginalId,
   isGeneratedId,
   getContentType,
   contentMapForTesting: contentMap
 };
 
 /***/ }),
 
+/***/ 1390:
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+const networkRequest = __webpack_require__(1391);
+const workerUtils = __webpack_require__(1392);
+
+module.exports = {
+  networkRequest,
+  workerUtils
+};
+
+/***/ }),
+
+/***/ 1391:
+/***/ (function(module, exports) {
+
+/* 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/. */
+
+function networkRequest(url, opts) {
+  return fetch(url, {
+    cache: opts.loadFromCache ? "default" : "no-cache"
+  }).then(res => {
+    if (res.status >= 200 && res.status < 300) {
+      return res.text().then(text => ({ content: text }));
+    }
+    return Promise.reject(`request failed with status ${res.status}`);
+  });
+}
+
+module.exports = networkRequest;
+
+/***/ }),
+
+/***/ 1392:
+/***/ (function(module, exports) {
+
+function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
+
+function WorkerDispatcher() {
+  this.msgId = 1;
+  this.worker = null;
+} /* 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/. */
+
+WorkerDispatcher.prototype = {
+  start(url) {
+    this.worker = new Worker(url);
+    this.worker.onerror = () => {
+      console.error(`Error in worker ${url}`);
+    };
+  },
+
+  stop() {
+    if (!this.worker) {
+      return;
+    }
+
+    this.worker.terminate();
+    this.worker = null;
+  },
+
+  task(method) {
+    return (...args) => {
+      return new Promise((resolve, reject) => {
+        const id = this.msgId++;
+        this.worker.postMessage({ id, method, args });
+
+        const listener = ({ data: result }) => {
+          if (result.id !== id) {
+            return;
+          }
+
+          if (!this.worker) {
+            reject("Oops, The worker has shutdown!");
+            return;
+          }
+          this.worker.removeEventListener("message", listener);
+          if (result.error) {
+            reject(result.error);
+          } else {
+            resolve(result.response);
+          }
+        };
+
+        this.worker.addEventListener("message", listener);
+      });
+    };
+  }
+};
+
+function workerHandler(publicInterface) {
+  return function (msg) {
+    const { id, method, args } = msg.data;
+    try {
+      const response = publicInterface[method].apply(undefined, args);
+      if (response instanceof Promise) {
+        response.then(val => self.postMessage({ id, response: val }),
+        // Error can't be sent via postMessage, so be sure to
+        // convert to string.
+        err => self.postMessage({ id, error: err.toString() }));
+      } else {
+        self.postMessage({ id, response });
+      }
+    } catch (error) {
+      // Error can't be sent via postMessage, so be sure to convert to
+      // string.
+      self.postMessage({ id, error: error.toString() });
+    }
+  };
+}
+
+function streamingWorkerHandler(publicInterface, { timeout = 100 } = {}, worker = self) {
+  let streamingWorker = (() => {
+    var _ref = _asyncToGenerator(function* (id, tasks) {
+      let isWorking = true;
+
+      const intervalId = setTimeout(function () {
+        isWorking = false;
+      }, timeout);
+
+      const results = [];
+      while (tasks.length !== 0 && isWorking) {
+        const { callback, context, args } = tasks.shift();
+        const result = yield callback.call(context, args);
+        results.push(result);
+      }
+      worker.postMessage({ id, status: "pending", data: results });
+      clearInterval(intervalId);
+
+      if (tasks.length !== 0) {
+        yield streamingWorker(id, tasks);
+      }
+    });
+
+    return function streamingWorker(_x, _x2) {
+      return _ref.apply(this, arguments);
+    };
+  })();
+
+  return (() => {
+    var _ref2 = _asyncToGenerator(function* (msg) {
+      const { id, method, args } = msg.data;
+      const workerMethod = publicInterface[method];
+      if (!workerMethod) {
+        console.error(`Could not find ${method} defined in worker.`);
+      }
+      worker.postMessage({ id, status: "start" });
+
+      try {
+        const tasks = workerMethod(args);
+        yield streamingWorker(id, tasks);
+        worker.postMessage({ id, status: "done" });
+      } catch (error) {
+        worker.postMessage({ id, status: "error", error });
+      }
+    });
+
+    return function (_x3) {
+      return _ref2.apply(this, arguments);
+    };
+  })();
+}
+
+module.exports = {
+  WorkerDispatcher,
+  workerHandler,
+  streamingWorkerHandler
+};
+
+/***/ }),
+
 /***/ 1393:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
@@ -3288,10 +3466,9 @@ module.exports = freeGlobal;
 
 }(this));
 
 /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(793)(module), __webpack_require__(792)))
 
 /***/ })
 
 /******/ });
-});
-//# sourceMappingURL=search-worker.js.map
\ No newline at end of file
+});
\ No newline at end of file
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -96,16 +96,29 @@ void* get_proc_address_from_glcontext(vo
   mozilla::gl::GLContext* glcontext = reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr);
   if (!glcontext) {
     return nullptr;
   }
   PRFuncPtr p = glcontext->LookupSymbol(procname);
   return reinterpret_cast<void*>(p);
 }
 
+void
+gecko_profiler_register_thread(const char* name)
+{
+  char stackTop;
+  profiler_register_thread(name, &stackTop);
+}
+
+void
+gecko_profiler_unregister_thread()
+{
+  profiler_unregister_thread();
+}
+
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 class MOZ_STACK_CLASS AutoWebRenderBridgeParentAsyncMessageSender
 {
 public:
--- a/gfx/thebes/gfxCoreTextShaper.cpp
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -239,16 +239,18 @@ gfxCoreTextShaper::ShapeText(DrawTarget 
                             // without requiring a separate allocation
 
 nsresult
 gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedText *aShapedText,
                                     uint32_t       aOffset,
                                     uint32_t       aLength,
                                     CTRunRef       aCTRun)
 {
+    typedef gfxShapedText::CompressedGlyph CompressedGlyph;
+
     int32_t direction = aShapedText->IsRightToLeft() ? -1 : 1;
 
     int32_t numGlyphs = ::CTRunGetGlyphCount(aCTRun);
     if (numGlyphs == 0) {
         return NS_OK;
     }
 
     int32_t wordLength = aLength;
@@ -314,18 +316,17 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
         ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
         glyphToChar = glyphToCharArray.get();
     }
 
     double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0),
                                                   nullptr, nullptr, nullptr);
 
     AutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
-    gfxShapedText::CompressedGlyph *charGlyphs =
-        aShapedText->GetCharacterGlyphs() + aOffset;
+    CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
 
     // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
     // to a source text character; we also need the charindex-to-glyphindex mapping to
     // find the glyph for a given char. Note that some chars may not map to any glyph
     // (ligature continuations), and some may map to several glyphs (eg Indic split vowels).
     // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we
     // record the last glyph index for cases where the char maps to several glyphs,
     // so that our clumping will include all the glyph fragments for the character.
@@ -535,28 +536,28 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxS
                 if (glyphStart < numGlyphs-1) {
                     toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
                 } else {
                     toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
                 }
                 advance = int32_t(toNextGlyph * appUnitsPerDevUnit);
             }
 
-            gfxTextRun::CompressedGlyph textRunGlyph;
-            textRunGlyph.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
-                                    true, detailedGlyphs.Length());
-            aShapedText->SetGlyphs(aOffset + baseCharIndex, textRunGlyph,
+            bool isClusterStart = charGlyphs[baseCharIndex].IsClusterStart();
+            aShapedText->SetGlyphs(aOffset + baseCharIndex,
+                                   CompressedGlyph::MakeComplex(isClusterStart, true,
+                                                                detailedGlyphs.Length()),
                                    detailedGlyphs.Elements());
 
             detailedGlyphs.Clear();
         }
 
         // the rest of the chars in the group are ligature continuations, no associated glyphs
         while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
-            gfxShapedText::CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex];
+            CompressedGlyph &shapedTextGlyph = charGlyphs[baseCharIndex];
             NS_ASSERTION(!shapedTextGlyph.IsSimpleGlyph(), "overwriting a simple glyph");
             shapedTextGlyph.SetComplex(inOrder && shapedTextGlyph.IsClusterStart(), false, 0);
         }
 
         glyphStart = glyphEnd;
         charStart = charEnd;
     }
 
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -61,23 +61,24 @@ gfxFT2Font::ShapeText(DrawTarget     *aD
 
     return true;
 }
 
 void
 gfxFT2Font::AddRange(const char16_t *aText, uint32_t aOffset,
                      uint32_t aLength, gfxShapedText *aShapedText)
 {
+    typedef gfxShapedText::CompressedGlyph CompressedGlyph;
+
     const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
     // we'll pass this in/figure it out dynamically, but at this point there can be only one face.
     gfxFT2LockedFace faceLock(this);
     FT_Face face = faceLock.get();
 
-    gfxShapedText::CompressedGlyph *charGlyphs =
-        aShapedText->GetCharacterGlyphs();
+    CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs();
 
     const gfxFT2Font::CachedGlyphData *cgd = nullptr, *cgdNext = nullptr;
 
     FT_UInt spaceGlyph = GetSpaceGlyph();
 
     for (uint32_t i = 0; i < aLength; i++, aOffset++) {
         char16_t ch = aText[i];
 
@@ -134,31 +135,33 @@ gfxFT2Font::AddRange(const char16_t *aTe
 
             // convert 26.6 fixed point to app units
             // round rather than truncate to nearest pixel
             // because these advances are often scaled
             advance = ((advance * appUnitsPerDevUnit + 32) >> 6);
         }
 
         if (advance >= 0 &&
-            gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
-            gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid)) {
+            CompressedGlyph::IsSimpleAdvance(advance) &&
+            CompressedGlyph::IsSimpleGlyphID(gid)) {
             charGlyphs[aOffset].SetSimpleGlyph(advance, gid);
         } else if (gid == 0) {
             // gid = 0 only happens when the glyph is missing from the font
             aShapedText->SetMissingGlyph(aOffset, ch, this);
         } else {
             gfxTextRun::DetailedGlyph details;
             details.mGlyphID = gid;
             NS_ASSERTION(details.mGlyphID == gid,
                          "Seriously weird glyph ID detected!");
             details.mAdvance = advance;
-            gfxShapedText::CompressedGlyph g;
-            g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1);
-            aShapedText->SetGlyphs(aOffset, g, &details);
+            bool isClusterStart = charGlyphs[aOffset].IsClusterStart();
+            aShapedText->SetGlyphs(aOffset,
+                                   CompressedGlyph::MakeComplex(isClusterStart,
+                                                                true, 1),
+                                   &details);
         }
     }
 }
 
 gfxFT2Font::gfxFT2Font(const RefPtr<UnscaledFontFreeType>& aUnscaledFont,
                        cairo_scaled_font_t *aCairoFont,
                        FT2FontEntry *aFontEntry,
                        const gfxFontStyle *aFontStyle,
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -566,20 +566,20 @@ gfxFontShaper::MergeFontFeatures(
     }
 }
 
 void
 gfxShapedText::SetupClusterBoundaries(uint32_t        aOffset,
                                       const char16_t *aString,
                                       uint32_t        aLength)
 {
-    CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
-
-    gfxTextRun::CompressedGlyph extendCluster;
-    extendCluster.SetComplex(false, true, 0);
+    CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
+
+    CompressedGlyph extendCluster =
+        CompressedGlyph::MakeComplex(false, true, 0);
 
     ClusterIterator iter(aString, aLength);
 
     // the ClusterIterator won't be able to tell us if the string
     // _begins_ with a cluster-extender, so we handle that here
     if (aLength) {
         uint32_t ch = *aString;
         if (aLength > 1 && NS_IS_HIGH_SURROGATE(ch) &&
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -758,18 +758,16 @@ public:
      * A character can be "missing", indicating that the system is unable
      * to render the character.
      * 
      * All characters in a ligature group conceptually share all the glyphs
      * associated with the characters in a group.
      */
     class CompressedGlyph {
     public:
-        CompressedGlyph() { mValue = 0; }
-
         enum {
             // Indicates that a cluster and ligature group starts at this
             // character; this character has a single glyph with a reasonable
             // advance and zero offsets. A "reasonable" advance
             // is one that fits in the available bits (currently 12) (specified
             // in appunits).
             FLAG_IS_SIMPLE_GLYPH  = 0x80000000U,
 
@@ -889,35 +887,62 @@ public:
             NS_ASSERTION(aCanBreakBefore <= 2,
                          "Bogus break-before value!");
             uint32_t breakMask = (uint32_t(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT);
             uint32_t toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE);
             mValue ^= toggle;
             return toggle;
         }
 
-        CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
+        // Create a CompressedGlyph value representing a simple glyph with
+        // no extra flags (line-break or is_space) set.
+        static CompressedGlyph
+        MakeSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
             NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
             NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
+            CompressedGlyph g;
+            g.mValue = FLAG_IS_SIMPLE_GLYPH |
+                       (aAdvanceAppUnits << ADVANCE_SHIFT) |
+                       aGlyph;
+            return g;
+        }
+
+        // Assign a simple glyph value to an existing CompressedGlyph record,
+        // preserving line-break/is-space flags if present.
+        CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits,
+                                        uint32_t aGlyph) {
             NS_ASSERTION(!CharTypeFlags(), "Char type flags lost");
             mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
-                FLAG_IS_SIMPLE_GLYPH |
-                (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
+                MakeSimpleGlyph(aAdvanceAppUnits, aGlyph).mValue;
             return *this;
         }
+
+        // Create a CompressedGlyph value representing a complex glyph record,
+        // without any line-break or char-type flags.
+        static CompressedGlyph
+        MakeComplex(bool aClusterStart, bool aLigatureStart,
+                    uint32_t aGlyphCount) {
+            CompressedGlyph g;
+            g.mValue = FLAG_NOT_MISSING |
+                       (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
+                       (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
+                       (aGlyphCount << GLYPH_COUNT_SHIFT);
+            return g;
+        }
+
+        // Assign a complex glyph value to an existing CompressedGlyph record,
+        // preserving line-break/char-type flags if present.
         CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
-                uint32_t aGlyphCount) {
+                                    uint32_t aGlyphCount) {
             mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
-                FLAG_NOT_MISSING |
                 CharTypeFlags() |
-                (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
-                (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
-                (aGlyphCount << GLYPH_COUNT_SHIFT);
+                MakeComplex(aClusterStart, aLigatureStart, aGlyphCount).mValue;
             return *this;
         }
+
         /**
          * Missing glyphs are treated as ligature group starts; don't mess with
          * the cluster-start flag (see bugs 618870 and 619286).
          */
         CompressedGlyph& SetMissing(uint32_t aGlyphCount) {
             mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START |
                                 FLAG_CHAR_IS_SPACE)) |
                 CharTypeFlags() |
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -211,16 +211,18 @@ struct Cluster {
 nsresult
 gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedText   *aShapedText,
                                         uint32_t         aOffset,
                                         uint32_t         aLength,
                                         const char16_t *aText,
                                         gr_segment      *aSegment,
                                         RoundingFlags    aRounding)
 {
+    typedef gfxShapedText::CompressedGlyph CompressedGlyph;
+
     int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit();
     bool rtl = aShapedText->IsRightToLeft();
 
     uint32_t glyphCount = gr_seg_n_slots(aSegment);
 
     // identify clusters; graphite may have reordered/expanded/ligated glyphs.
     AutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
     AutoTArray<uint16_t,SMALL_GLYPH_RUN> gids;
@@ -284,18 +286,17 @@ gfxGraphiteShaper::SetGlyphsFromSegment(
             after++;
         }
         // extend cluster if necessary to reach the glyph's "after" index
         if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
             clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
         }
     }
 
-    gfxShapedText::CompressedGlyph *charGlyphs =
-        aShapedText->GetCharacterGlyphs() + aOffset;
+    CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
 
     bool roundX = bool(aRounding & RoundingFlags::kRoundX);
     bool roundY = bool(aRounding & RoundingFlags::kRoundY);
 
     // now put glyphs into the textrun, one cluster at a time
     for (uint32_t i = 0; i <= cIndex; ++i) {
         const Cluster& c = clusters[i];
 
@@ -321,18 +322,18 @@ gfxGraphiteShaper::SetGlyphsFromSegment(
         if (c.nGlyphs == 1 && c.nChars == 1 &&
             aShapedText->FilterIfIgnorable(aOffset + offs, aText[offs])) {
             continue;
         }
 
         uint32_t appAdvance = roundX ? NSToIntRound(adv) * dev2appUnits
                                      : NSToIntRound(adv * dev2appUnits);
         if (c.nGlyphs == 1 &&
-            gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
-            gfxShapedText::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
+            CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
+            CompressedGlyph::IsSimpleAdvance(appAdvance) &&
             charGlyphs[offs].IsClusterStart() &&
             yLocs[c.baseGlyph] == 0)
         {
             charGlyphs[offs].SetSimpleGlyph(appAdvance, gids[c.baseGlyph]);
         } else {
             // not a one-to-one mapping with simple metrics: use DetailedGlyph
             AutoTArray<gfxShapedText::DetailedGlyph,8> details;
             float clusterLoc;
@@ -347,25 +348,27 @@ gfxGraphiteShaper::SetGlyphsFromSegment(
                 } else {
                     float dx = rtl ? (xLocs[j] - clusterLoc) :
                                      (xLocs[j] - clusterLoc - adv);
                     d->mOffset.x = roundX ? NSToIntRound(dx) * dev2appUnits
                                           : dx * dev2appUnits;
                     d->mAdvance = 0;
                 }
             }
-            gfxShapedText::CompressedGlyph g;
-            g.SetComplex(charGlyphs[offs].IsClusterStart(),
-                         true, details.Length());
-            aShapedText->SetGlyphs(aOffset + offs, g, details.Elements());
+            bool isClusterStart = charGlyphs[offs].IsClusterStart();
+            aShapedText->SetGlyphs(aOffset + offs,
+                                   CompressedGlyph::MakeComplex(isClusterStart,
+                                                                true,
+                                                                details.Length()),
+                                   details.Elements());
         }
 
         for (uint32_t j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
             NS_ASSERTION(j < aLength, "unexpected offset");
-            gfxShapedText::CompressedGlyph &g = charGlyphs[j];
+            CompressedGlyph &g = charGlyphs[j];
             NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
             g.SetComplex(g.IsClusterStart(), false, 0);
         }
     }
 
     return NS_OK;
 }
 
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -1524,16 +1524,18 @@ gfxHarfBuzzShaper::ShapeText(DrawTarget 
 nsresult
 gfxHarfBuzzShaper::SetGlyphsFromRun(gfxShapedText  *aShapedText,
                                     uint32_t        aOffset,
                                     uint32_t        aLength,
                                     const char16_t *aText,
                                     bool            aVertical,
                                     RoundingFlags   aRounding)
 {
+    typedef gfxShapedText::CompressedGlyph CompressedGlyph;
+
     uint32_t numGlyphs;
     const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
     if (numGlyphs == 0) {
         return NS_OK;
     }
 
     AutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
 
@@ -1564,18 +1566,17 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxS
         roundI = bool(aRounding & RoundingFlags::kRoundY);
         roundB = bool(aRounding & RoundingFlags::kRoundX);
     } else {
         roundI = bool(aRounding & RoundingFlags::kRoundX);
         roundB = bool(aRounding & RoundingFlags::kRoundY);
     }
 
     int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
-    gfxShapedText::CompressedGlyph *charGlyphs =
-        aShapedText->GetCharacterGlyphs() + aOffset;
+    CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset;
 
     // factor to convert 16.16 fixed-point pixels to app units
     // (only used if not rounding)
     double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
 
     // Residual from rounding of previous advance, for use in rounding the
     // subsequent offset or advance appropriately.  16.16 fixed-point
     //
@@ -1713,18 +1714,18 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxS
             residual = width - FloatToFixed(intWidth);
             advance = appUnitsPerDevUnit * intWidth + iOffset;
         } else {
             iOffset = floor(hb2appUnits * i_offset + 0.5);
             advance = floor(hb2appUnits * i_advance + 0.5);
         }
         // Check if it's a simple one-to-one mapping
         if (glyphsInClump == 1 &&
-            gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
-            gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
+            CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
+            CompressedGlyph::IsSimpleAdvance(advance) &&
             charGlyphs[baseCharIndex].IsClusterStart() &&
             iOffset == 0 && b_offset == 0 &&
             b_advance == 0 && bPos == 0)
         {
             charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
                                                      ginfo[glyphStart].codepoint);
         } else {
             // Collect all glyphs in a list to be assigned to the first char;
@@ -1784,30 +1785,31 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxS
                     residual = i_advance - FloatToFixed(intAdvance);
                     advance = appUnitsPerDevUnit * intAdvance;
                 } else {
                     iOffset = floor(hb2appUnits * i_offset + 0.5);
                     advance = floor(hb2appUnits * i_advance + 0.5);
                 }
             }
 
-            gfxShapedText::CompressedGlyph g;
-            g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
-                         true, detailedGlyphs.Length());
+            bool isClusterStart = charGlyphs[baseCharIndex].IsClusterStart();
             aShapedText->SetGlyphs(aOffset + baseCharIndex,
-                                   g, detailedGlyphs.Elements());
+                                   CompressedGlyph::MakeComplex(isClusterStart,
+                                                                true,
+                                                                detailedGlyphs.Length()),
+                                   detailedGlyphs.Elements());
 
             detailedGlyphs.Clear();
         }
 
         // the rest of the chars in the group are ligature continuations,
         // no associated glyphs
         while (++baseCharIndex != endCharIndex &&
                baseCharIndex < int32_t(wordLength)) {
-            gfxShapedText::CompressedGlyph &g = charGlyphs[baseCharIndex];
+            CompressedGlyph &g = charGlyphs[baseCharIndex];
             NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
             g.SetComplex(g.IsClusterStart(), false, 0);
         }
 
         glyphStart = glyphEnd;
         charStart = charEnd;
     }
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1653,18 +1653,18 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFon
         NS_lroundf(aFont->GetMetrics(fontOrientation).spaceWidth *
                    mAppUnitsPerDevUnit);
     if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
         return false;
     }
 
     AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
                 aOrientation);
-    CompressedGlyph g;
-    g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
+    CompressedGlyph g =
+        CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
     if (aSpaceChar == ' ') {
         g.SetIsSpace();
     }
     GetCharacterGlyphs()[aCharIndex] = g;
     return true;
 }
 
 void
@@ -2751,18 +2751,18 @@ gfxFontGroup::InitScriptRun(DrawTarget* 
                         if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
                             aTextRun->GetCharacterGlyphs()[aOffset + index].
                                 SetSimpleGlyph(advance,
                                                mainFont->GetSpaceGlyph());
                         } else {
                             gfxTextRun::DetailedGlyph detailedGlyph;
                             detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
                             detailedGlyph.mAdvance = advance;
-                            gfxShapedText::CompressedGlyph g;
-                            g.SetComplex(true, true, 1);
+                            CompressedGlyph g =
+                                CompressedGlyph::MakeComplex(true, true, 1);
                             aTextRun->SetGlyphs(aOffset + index,
                                                 g, &detailedGlyph);
                         }
                         continue;
                     }
                 }
 
                 if (IsInvalidChar(ch)) {
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -837,16 +837,17 @@ private:
     // shaping state for handling variant fallback features
     // such as subscript/superscript variant glyphs
     ShapingState      mShapingState;
 };
 
 class gfxFontGroup final : public gfxTextRunFactory {
 public:
     typedef mozilla::unicode::Script Script;
+    typedef gfxShapedText::CompressedGlyph CompressedGlyph;
 
     static void Shutdown(); // platform must call this to release the languageAtomService
 
     gfxFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
                  const gfxFontStyle* aStyle,
                  gfxTextPerfMetrics* aTextPerf,
                  gfxUserFontSet* aUserFontSet,
                  gfxFloat aDevToCssSize);
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -2,17 +2,17 @@ use std::ffi::{CStr, CString};
 use std::{mem, slice};
 use std::path::PathBuf;
 use std::ptr;
 use std::sync::Arc;
 use std::os::raw::{c_void, c_char, c_float};
 use gleam::gl;
 
 use webrender_api::*;
-use webrender::{ReadPixelsFormat, Renderer, RendererOptions};
+use webrender::{ReadPixelsFormat, Renderer, RendererOptions, ThreadListener};
 use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::DebugFlags;
 use webrender::{ApiRecordingReceiver, BinaryRecorder};
 use thread_profiler::register_thread_with_profiler;
 use moz2d_renderer::Moz2dImageRenderer;
 use app_units::Au;
 use rayon;
 use euclid::SideOffsets2D;
@@ -582,24 +582,58 @@ pub unsafe extern "C" fn wr_rendered_epo
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub unsafe extern "C" fn wr_rendered_epochs_delete(pipeline_epochs: *mut WrRenderedEpochs) {
     Box::from_raw(pipeline_epochs);
 }
 
+extern "C" {
+    fn gecko_profiler_register_thread(name: *const ::std::os::raw::c_char);
+    fn gecko_profiler_unregister_thread();
+}
+
+struct GeckoProfilerThreadListener {}
+
+impl GeckoProfilerThreadListener {
+    pub fn new() -> GeckoProfilerThreadListener {
+        GeckoProfilerThreadListener{}
+    }
+}
+
+impl ThreadListener for GeckoProfilerThreadListener {
+    fn thread_started(&self, thread_name: &str) {
+        let name = CString::new(thread_name).unwrap();
+        unsafe {
+            // gecko_profiler_register_thread copies the passed name here.
+            gecko_profiler_register_thread(name.as_ptr());
+        }
+    }
+
+    fn thread_stopped(&self, _: &str) {
+        unsafe {
+            gecko_profiler_unregister_thread();
+        }
+    }
+}
+
 pub struct WrThreadPool(Arc<rayon::ThreadPool>);
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_thread_pool_new() -> *mut WrThreadPool {
     let worker_config = rayon::Configuration::new()
-        .thread_name(|idx|{ format!("WebRender:Worker#{}", idx) })
+        .thread_name(|idx|{ format!("WRWorker#{}", idx) })
         .start_handler(|idx| {
-            register_thread_with_profiler(format!("WebRender:Worker#{}", idx));
+            let name = format!("WRWorker#{}", idx);
+            register_thread_with_profiler(name.clone());
+            gecko_profiler_register_thread(CString::new(name).unwrap().as_ptr());
+        })
+        .exit_handler(|_idx| {
+            gecko_profiler_unregister_thread();
         });
 
     let workers = Arc::new(rayon::ThreadPool::new(worker_config).unwrap());
 
     Box::into_raw(Box::new(WrThreadPool(workers)))
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
@@ -645,28 +679,30 @@ pub extern "C" fn wr_window_new(window_i
     };
 
     let opts = RendererOptions {
         enable_aa: true,
         enable_subpixel_aa: true,
         recorder: recorder,
         blob_image_renderer: Some(Box::new(Moz2dImageRenderer::new(workers.clone()))),
         workers: Some(workers.clone()),
+        thread_listener: Some(Box::new(GeckoProfilerThreadListener::new())),
         enable_render_on_scroll: false,
         resource_override_path: unsafe {
             let override_charptr = gfx_wr_resource_path_override();
             if override_charptr.is_null() {
                 None
             } else {
                 match CStr::from_ptr(override_charptr).to_str() {
                     Ok(override_str) => Some(PathBuf::from(override_str)),
                     _ => None
                 }
             }
         },
+        renderer_id: Some(window_id.0),
         ..Default::default()
     };
 
     let notifier = Box::new(CppNotifier {
         window_id: window_id,
     });
     let (renderer, sender) = match Renderer::new(gl, notifier, opts) {
         Ok((renderer, sender)) => (renderer, sender),
--- a/gfx/webrender_bindings/webrender_ffi.h
+++ b/gfx/webrender_bindings/webrender_ffi.h
@@ -21,16 +21,18 @@ bool is_in_main_thread();
 bool is_in_render_thread();
 bool is_glcontext_egl(void* glcontext_ptr);
 bool gfx_use_wrench();
 const char* gfx_wr_resource_path_override();
 void gfx_critical_note(const char* msg);
 void gfx_critical_error(const char* msg);
 void gecko_printf_stderr_output(const char* msg);
 void* get_proc_address_from_glcontext(void* glcontext_ptr, const char* procname);
+void gecko_profiler_register_thread(const char* threadname);
+void gecko_profiler_unregister_thread();
 
 } // extern "C"
 
 // Some useful defines to stub out webrender binding functions for when we
 // build gecko without webrender. We try to tell the compiler these functions
 // are unreachable in that case, but VC++ emits a warning if it finds any
 // unreachable functions invoked from destructors. That warning gets turned into
 // an error and causes the build to fail. So for wr_* functions called by
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -938,16 +938,20 @@ extern void AddFontData(WrFontKey aKey,
 extern void AddNativeFontHandle(WrFontKey aKey,
                                 void *aHandle,
                                 uint32_t aIndex);
 
 extern void DeleteFontData(WrFontKey aKey);
 
 extern void gecko_printf_stderr_output(const char *aMsg);
 
+extern void gecko_profiler_register_thread(const char *aName);
+
+extern void gecko_profiler_unregister_thread();
+
 extern void gfx_critical_error(const char *aMsg);
 
 extern void gfx_critical_note(const char *aMsg);
 
 extern bool gfx_use_wrench();
 
 extern const char *gfx_wr_resource_path_override();
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3668,17 +3668,18 @@ CreateArrayPrototype(JSContext* cx, JSPr
 
     return arrayProto;
 }
 
 static bool
 array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
 {
     // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
-    RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
+    RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr,
+                                                                      SingletonObject));
     if (!unscopables)
         return false;
 
     RootedValue value(cx, BooleanValue(true));
     if (!DefineDataProperty(cx, unscopables, cx->names().copyWithin, value) ||
         !DefineDataProperty(cx, unscopables, cx->names().entries, value) ||
         !DefineDataProperty(cx, unscopables, cx->names().fill, value) ||
         !DefineDataProperty(cx, unscopables, cx->names().find, value) ||
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -176,31 +176,33 @@ void counters_dump(int) {
 void counters_reset(int) {
     /* Do nothing */
 }
 #endif
 
 static void
 InstallCoverageSignalHandlers()
 {
+#ifndef XP_WIN
     fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n", getpid());
 
     struct sigaction dump_sa;
     dump_sa.sa_handler = counters_dump;
     dump_sa.sa_flags = SA_RESTART;
     sigemptyset(&dump_sa.sa_mask);
     mozilla::DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr);
     MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler");
 
     struct sigaction reset_sa;
     reset_sa.sa_handler = counters_reset;
     reset_sa.sa_flags = SA_RESTART;
     sigemptyset(&reset_sa.sa_mask);
     mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
     MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
+#endif
 }
 #endif
 
 class OffThreadState {
     enum State {
         IDLE,           /* ready to work; no token, no source */
         COMPILING,      /* working; no token, have source */
         DONE            /* compilation done: have token and source */
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -583,19 +583,19 @@ nsOpenTypeTable::MakeTextRun(DrawTarget*
                               // We don't care about CSS writing mode here;
                               // math runs are assumed to be horizontal.
   gfxTextRun::DetailedGlyph detailedGlyph;
   detailedGlyph.mGlyphID = aGlyph.glyphID;
   detailedGlyph.mAdvance =
     NSToCoordRound(aAppUnitsPerDevPixel *
                    aFontGroup->GetFirstValidFont()->
                    GetGlyphHAdvance(aDrawTarget, aGlyph.glyphID));
-  gfxShapedText::CompressedGlyph g;
-  g.SetComplex(true, true, 1);
-  textRun->SetGlyphs(0, g, &detailedGlyph);
+  textRun->SetGlyphs(0,
+                     gfxShapedText::CompressedGlyph::MakeComplex(true, true, 1),
+                     &detailedGlyph);
 
   return textRun.forget();
 }
 
 // -----------------------------------------------------------------------------
 // This is the list of all the applicable glyph tables.
 // We will maintain a single global instance that will only reveal those
 // glyph tables that are associated to fonts currently installed on the
new file mode 100644
--- /dev/null
+++ b/testing/config/tooltool-manifests/win32/ccov.manifest
@@ -0,0 +1,9 @@
+[
+  {
+    "size": 271486,
+    "digest": "6956eb1cab16dff465861cd1966e3115f31b533334089553b8b94897bc138c6dec279bf7c60b34fe3fc67c8cb0d41e30f55982adaad07320090dab053e018dec",
+    "algorithm": "sha512",
+    "filename": "grcov-win-i686.tar.bz2",
+    "unpack": false
+  }
+]
--- a/testing/mozharness/mozharness/mozilla/testing/codecoverage.py
+++ b/testing/mozharness/mozharness/mozilla/testing/codecoverage.py
@@ -1,17 +1,20 @@
 #!/usr/bin/env 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/.
 
 import os
 import shutil
+import sys
+import tarfile
 import tempfile
 
+import mozinfo
 from mozharness.base.script import (
     PreScriptAction,
     PostScriptAction,
 )
 from mozharness.mozilla.tooltool import TooltoolMixin
 
 code_coverage_config_options = [
     [["--code-coverage"],
@@ -45,17 +48,17 @@ class CodeCoverageMixin(object):
 
     @property
     def code_coverage_enabled(self):
         try:
             if self.config.get('code_coverage'):
                 return True
 
             # XXX workaround because bug 1110465 is hard
-            return self.buildbot_config['properties']['stage_platform'] in ('linux64-ccov',)
+            return 'ccov' in self.buildbot_config['properties']['stage_platform']
         except (AttributeError, KeyError, TypeError):
             return False
 
     @property
     def ccov_upload_disabled(self):
         try:
             if self.config.get('disable_ccov_upload'):
                 return True
@@ -65,17 +68,17 @@ class CodeCoverageMixin(object):
 
     @property
     def jsd_code_coverage_enabled(self):
         try:
             if self.config.get('jsd_code_coverage'):
                 return True
 
             # XXX workaround because bug 1110465 is hard
-            return self.buildbot_config['properties']['stage_platform'] in ('linux64-jsdcov',)
+            return 'jsdcov' in self.buildbot_config['properties']['stage_platform']
         except (AttributeError, KeyError, TypeError):
             return False
 
     @PreScriptAction('run-tests')
     def _set_gcov_prefix(self, action):
         if not self.code_coverage_enabled:
             return
         # Set the GCOV directory.
@@ -89,25 +92,34 @@ class CodeCoverageMixin(object):
         # Install grcov on the test machine
         # Get the path to the build machines gcno files.
         self.url_to_gcno = self.query_build_dir_url('target.code-coverage-gcno.zip')
         dirs = self.query_abs_dirs()
 
         # Create the grcov directory, get the tooltool manifest, and finally
         # download and unpack the grcov binary.
         self.grcov_dir = tempfile.mkdtemp()
+
+        if mozinfo.os == 'linux':
+            platform = 'linux64'
+            tar_file = 'grcov-linux-standalone-x86_64.tar.bz2'
+        elif mozinfo.os == 'win':
+            platform = 'win32'
+            tar_file = 'grcov-win-i686.tar.bz2'
+
         manifest = os.path.join(dirs.get('abs_test_install_dir', os.path.join(dirs['abs_work_dir'], 'tests')), \
-            'config/tooltool-manifests/linux64/ccov.manifest')
+            'config/tooltool-manifests/%s/ccov.manifest' % platform)
 
         tooltool_path = self._fetch_tooltool_py()
-        cmd = [tooltool_path, '--url', 'https://tooltool.mozilla-releng.net/', 'fetch', \
+        cmd = [sys.executable, tooltool_path, '--url', 'https://tooltool.mozilla-releng.net/', 'fetch', \
             '-m', manifest, '-o', '-c', '/builds/worker/tooltool-cache']
         self.run_command(cmd, cwd=self.grcov_dir)
-        self.run_command(['tar', '-jxvf', os.path.join(self.grcov_dir, 'grcov-linux-standalone-x86_64.tar.bz2'), \
-            '-C', self.grcov_dir], cwd=self.grcov_dir)
+
+        with tarfile.open(os.path.join(self.grcov_dir, tar_file)) as tar:
+            tar.extractall(self.grcov_dir)
 
     @PostScriptAction('run-tests')
     def _package_coverage_data(self, action, success=None):
         if self.jsd_code_coverage_enabled:
             # Setup the command for compression
             dirs = self.query_abs_dirs()
             jsdcov_dir = dirs['abs_blob_upload_dir']
             zipFile = os.path.join(jsdcov_dir, "jsdcov_artifacts.zip")
@@ -121,69 +133,79 @@ class CodeCoverageMixin(object):
                 if filename.startswith("jscov") and filename.endswith(".json"):
                     os.remove(os.path.join(jsdcov_dir, filename))
 
             self.info("Completed compression of JSDCov artifacts!")
             self.info("Path to JSDCov compressed artifacts: " + zipFile)
 
         if not self.code_coverage_enabled:
             return
+
         del os.environ['GCOV_PREFIX']
         del os.environ['JS_CODE_COVERAGE_OUTPUT_DIR']
 
         if not self.ccov_upload_disabled:
             # TODO This is fragile, find rel_topsrcdir properly somehow
             # We need to find the path relative to the gecko topsrcdir. Use
             # some known gecko directories as a test.
             canary_dirs = ['browser', 'docshell', 'dom', 'js', 'layout', 'toolkit', 'xpcom', 'xpfe']
             rel_topsrcdir = None
             for root, dirs, files in os.walk(self.gcov_dir):
                 # need to use 'any' in case no gcda data was generated in that subdir.
                 if any(d in dirs for d in canary_dirs):
                     rel_topsrcdir = root
                     break
-            else:
+
+            if rel_topsrcdir is None:
                 # Unable to upload code coverage files. Since this is the whole
                 # point of code coverage, making this fatal.
-                self.fatal("Could not find relative topsrcdir in code coverage "
-                           "data!")
+                self.fatal("Could not find relative topsrcdir in code coverage data!")
+
+            dirs = self.query_abs_dirs()
 
             # Package GCOV coverage data.
-            dirs = self.query_abs_dirs()
-            file_path_gcda = os.path.join(
-                dirs['abs_blob_upload_dir'], 'code-coverage-gcda.zip')
-            command = ['zip', '-r', file_path_gcda, '.']
-            self.run_command(command, cwd=rel_topsrcdir)
+            file_path_gcda = os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-gcda.zip')
+            self.run_command(['zip', '-r', file_path_gcda, '.'], cwd=rel_topsrcdir)
 
             # Package JSVM coverage data.
-            dirs = self.query_abs_dirs()
-            file_path_jsvm = os.path.join(
-                dirs['abs_blob_upload_dir'], 'code-coverage-jsvm.zip')
-            command = ['zip', '-r', file_path_jsvm, '.']
-            self.run_command(command, cwd=self.jsvm_dir)
+            file_path_jsvm = os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-jsvm.zip')
+            self.run_command(['zip', '-r', file_path_jsvm, '.'], cwd=self.jsvm_dir)
 
             # GRCOV post-processing
             # Download the gcno fom the build machine.
             self.download_file(self.url_to_gcno, file_name=None, parent_dir=self.grcov_dir)
 
+            if mozinfo.os == 'linux':
+                prefix = '/builds/worker/workspace/build/src/'
+            elif mozinfo.os == 'win':
+                prefix = 'z:/build/build/src/'
+
             # Run grcov on the zipped .gcno and .gcda files.
             grcov_command = [
                 os.path.join(self.grcov_dir, 'grcov'),
                 '-t', 'lcov',
-                '-p', '/builds/worker/workspace/build/src/',
+                '-p', prefix,
                 '--ignore-dir', 'gcc',
                 os.path.join(self.grcov_dir, 'target.code-coverage-gcno.zip'), file_path_gcda
             ]
 
             # 'grcov_output' will be a tuple, the first variable is the path to the lcov output,
             # the other is the path to the standard error output.
-            grcov_output = self.get_output_from_command(grcov_command, cwd=self.grcov_dir, \
-                silent=True, tmpfile_base_path=os.path.join(self.grcov_dir, 'grcov_lcov_output'), \
-                save_tmpfiles=True, return_type='files')
+            grcov_output = self.get_output_from_command(
+                grcov_command,
+                cwd=self.grcov_dir,
+                silent=True,
+                tmpfile_base_path=os.path.join(self.grcov_dir, 'grcov_lcov_output'),
+                save_tmpfiles=True,
+                return_type='files'
+            )
             new_output_name = grcov_output[0] + '.info'
             os.rename(grcov_output[0], new_output_name)
 
             # Zip the grcov output and upload it.
-            command = ['zip', os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-grcov.zip'), new_output_name]
-            self.run_command(command, cwd=self.grcov_dir)
+            self.run_command(
+                ['zip', os.path.join(dirs['abs_blob_upload_dir'], 'code-coverage-grcov.zip'), new_output_name],
+                cwd=self.grcov_dir
+            )
+
         shutil.rmtree(self.gcov_dir)
         shutil.rmtree(self.jsvm_dir)
         shutil.rmtree(self.grcov_dir)
--- a/tools/code-coverage/CodeCoverageHandler.cpp
+++ b/tools/code-coverage/CodeCoverageHandler.cpp
@@ -1,16 +1,18 @@
 /* -*- 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 <stdio.h>
+#ifndef XP_WIN
 #include <signal.h>
-#include <stdio.h>
 #include <unistd.h>
+#endif
 #include "mozilla/CodeCoverageHandler.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/DebugOnly.h"
 #include "nsAppRunner.h"
 
 using namespace mozilla;
 
 // The __gcov_dump function writes the coverage counters to gcda files.
@@ -57,31 +59,33 @@ void CodeCoverageHandler::ResetCounters(
 
   printf_stderr("[CodeCoverage] Requested reset.\n");
   counters_reset();
   printf_stderr("[CodeCoverage] Reset completed.\n");
 }
 
 void CodeCoverageHandler::SetSignalHandlers()
 {
+#ifndef XP_WIN
   printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid());
 
   struct sigaction dump_sa;
   dump_sa.sa_handler = CodeCoverageHandler::DumpCounters;
   dump_sa.sa_flags = SA_RESTART;
   sigemptyset(&dump_sa.sa_mask);
   DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr);
   MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler");
 
   struct sigaction reset_sa;
   reset_sa.sa_handler = CodeCoverageHandler::ResetCounters;
   reset_sa.sa_flags = SA_RESTART;
   sigemptyset(&reset_sa.sa_mask);
   DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
   MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
+#endif
 }
 
 CodeCoverageHandler::CodeCoverageHandler()
   : mGcovLock("GcovLock")
 {
   SetSignalHandlers();
 }