merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 02 Mar 2017 14:02:48 +0100
changeset 374582 66535e831760421b270662aa8d0773b0fde7c9f3
parent 374541 180a160ae22a4d63867cfd02608e7621e8e697d1 (current diff)
parent 374581 8c7dbec36fa77c5bb427c480f3620dd4a781260f (diff)
child 374583 e93fde9f9168721881ed5a321a20becc7321725f
child 374588 87176aa9560af31faf448464e28b7c53a459f9af
child 374646 5c8f4643a2e93f4860953b904890119b281ba65b
child 374822 8744594fc366cf463313d3636e62789a907b0877
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
merge mozilla-inbound to mozilla-central a=merge
layout/base/nsLayoutUtils.cpp
layout/generic/nsIFrame.h
mobile/android/app/mobile.js
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
netwerk/protocol/http/nsHttpChannel.cpp
tools/profiler/core/SyncProfile.cpp
tools/profiler/core/SyncProfile.h
--- a/accessible/base/SelectionManager.h
+++ b/accessible/base/SelectionManager.h
@@ -116,17 +116,17 @@ protected:
 
   /**
    * Process DOM selection change. Fire selection and caret move events.
    */
   void ProcessSelectionChanged(SelData* aSelData);
 
 private:
   // Currently focused control.
-  nsWeakFrame mCurrCtrlFrame;
+  WeakFrame mCurrCtrlFrame;
   int32_t mCaretOffset;
   HyperTextAccessible* mAccWithCaret;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -106,17 +106,17 @@ nsCoreUtils::DispatchClickEvent(nsITreeB
 
   int32_t tcX = 0;
   tcBoxObj->GetX(&tcX);
 
   int32_t tcY = 0;
   tcBoxObj->GetY(&tcY);
 
   // Dispatch mouse events.
-  nsWeakFrame tcFrame = tcContent->GetPrimaryFrame();
+  AutoWeakFrame tcFrame = tcContent->GetPrimaryFrame();
   nsIFrame* rootFrame = presShell->GetRootFrame();
 
   nsPoint offset;
   nsIWidget *rootWidget =
     rootFrame->GetView()->GetNearestWidget(&offset);
 
   RefPtr<nsPresContext> presContext = presShell->GetPresContext();
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1825,17 +1825,17 @@ Accessible::DispatchClickEvent(nsIConten
   nsCOMPtr<nsIPresShell> presShell = mDoc->PresShell();
 
   // Scroll into view.
   presShell->ScrollContentIntoView(aContent,
                                    nsIPresShell::ScrollAxis(),
                                    nsIPresShell::ScrollAxis(),
                                    nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
 
-  nsWeakFrame frame = aContent->GetPrimaryFrame();
+  AutoWeakFrame frame = aContent->GetPrimaryFrame();
   if (!frame)
     return;
 
   // Compute x and y coordinates.
   nsPoint point;
   nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget(point);
   if (!widget)
     return;
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -16,16 +16,17 @@
 #include "AccessibleWrap.h"
 #include "Compatibility.h"
 #include "nsWinUtils.h"
 #include "RootAccessible.h"
 #endif
 
 namespace mozilla {
 namespace a11y {
+uint64_t DocAccessibleParent::sMaxDocID = 0;
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData,
                                    const bool& aFromUser)
 {
   if (mShutdown)
     return IPC_OK();
 
@@ -428,30 +429,29 @@ DocAccessibleParent::AddChildDoc(DocAcce
   // here.
   if (outerDoc->ChildrenCount() > 1 ||
       (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
     return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!");
   }
 
   aChildDoc->SetParent(outerDoc);
   outerDoc->SetChildDoc(aChildDoc);
-  mChildDocs.AppendElement(aChildDoc->IProtocol::Id());
-  aChildDoc->mParentDoc = IProtocol::Id();
+  mChildDocs.AppendElement(aChildDoc->mActorID);
+  aChildDoc->mParentDoc = mActorID;
 
   if (aCreating) {
     ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvShutdown()
 {
-  MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(IProtocol::Id()));
   Destroy();
 
   auto mgr = static_cast<dom::TabParent*>(Manager());
   if (!mgr->IsDestroyed()) {
     if (!PDocAccessibleParent::Send__delete__(this)) {
       return IPC_FAIL_NO_REASON(mgr);
     }
   }
@@ -465,41 +465,70 @@ DocAccessibleParent::Destroy()
   // If we are already shutdown that is because our containing tab parent is
   // shutting down in which case we don't need to do anything.
   if (mShutdown) {
     return;
   }
 
   mShutdown = true;
 
+  MOZ_DIAGNOSTIC_ASSERT(LiveDocs().Contains(mActorID));
   uint32_t childDocCount = mChildDocs.Length();
   for (uint32_t i = 0; i < childDocCount; i++) {
     for (uint32_t j = i + 1; j < childDocCount; j++) {
       MOZ_DIAGNOSTIC_ASSERT(mChildDocs[i] != mChildDocs[j]);
     }
   }
 
-  for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
-    ChildDocAt(i)->Destroy();
+  // XXX This indirection through the hash map of live documents shouldn't be
+  // needed, but be paranoid for now.
+  int32_t actorID = mActorID;
+  for (uint32_t i = childDocCount - 1; i < childDocCount; i--) {
+    DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
+    MOZ_ASSERT(thisDoc);
+    if (!thisDoc) {
+      return;
+    }
+
+    thisDoc->ChildDocAt(i)->Destroy();
+  }
 
   for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
     MOZ_ASSERT(iter.Get()->mProxy != this);
     ProxyDestroyed(iter.Get()->mProxy);
     iter.Remove();
   }
 
+  DocAccessibleParent* thisDoc = LiveDocs().Get(actorID);
+  MOZ_ASSERT(thisDoc);
+  if (!thisDoc) {
+    return;
+  }
+
   // The code above should have already completely cleared these, but to be
   // extra safe make sure they are cleared here.
-  mAccessibles.Clear();
-  mChildDocs.Clear();
+  thisDoc->mAccessibles.Clear();
+  thisDoc->mChildDocs.Clear();
+
+  DocManager::NotifyOfRemoteDocShutdown(thisDoc);
+  thisDoc = LiveDocs().Get(actorID);
+  MOZ_ASSERT(thisDoc);
+  if (!thisDoc) {
+    return;
+  }
 
-  DocManager::NotifyOfRemoteDocShutdown(this);
-  ProxyDestroyed(this);
-  if (DocAccessibleParent* parentDoc = ParentDoc())
-    parentDoc->RemoveChildDoc(this);
+  ProxyDestroyed(thisDoc);
+  thisDoc = LiveDocs().Get(actorID);
+  MOZ_ASSERT(thisDoc);
+  if (!thisDoc) {
+    return;
+  }
+
+  if (DocAccessibleParent* parentDoc = thisDoc->ParentDoc())
+    parentDoc->RemoveChildDoc(thisDoc);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
 }
 
 DocAccessibleParent*
 DocAccessibleParent::ParentDoc() const
 {
   if (mParentDoc == kNoParentDoc) {
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -28,20 +28,26 @@ class DocAccessibleParent : public Proxy
 {
 public:
   DocAccessibleParent() :
     ProxyAccessible(this), mParentDoc(kNoParentDoc),
     mTopLevel(false), mShutdown(false)
 #if defined(XP_WIN)
                                       , mEmulatedWindowHandle(nullptr)
 #endif // defined(XP_WIN)
-  { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
+  {
+    MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
+    sMaxDocID++;
+    mActorID = sMaxDocID;
+    MOZ_ASSERT(!LiveDocs().Get(mActorID));
+    LiveDocs().Put(mActorID, this);
+  }
   ~DocAccessibleParent()
   {
-    LiveDocs().Remove(IProtocol::Id());
+    LiveDocs().Remove(mActorID);
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
     MOZ_ASSERT(mChildDocs.Length() == 0);
     MOZ_ASSERT(!ParentDoc());
   }
 
   void SetTopLevel() { mTopLevel = true; }
   bool IsTopLevel() const { return mTopLevel; }
 
@@ -54,21 +60,16 @@ public:
    */
   void MarkAsShutdown()
   {
     MOZ_ASSERT(mChildDocs.IsEmpty());
     MOZ_ASSERT(mAccessibles.Count() == 0);
     mShutdown = true;
   }
 
-  /**
-   * Add this document to the set of tracked documents.
-   */
-  void AddToMap() { LiveDocs().Put(IProtocol::Id(), this); }
-
   /*
    * Called when a message from a document in a child process notifies the main
    * process it is firing an event.
    */
   virtual mozilla::ipc::IPCResult RecvEvent(const uint64_t& aID, const uint32_t& aType)
     override;
 
   virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
@@ -113,17 +114,17 @@ public:
       Destroy();
   }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
   DocAccessibleParent* ParentDoc() const;
-  static const int32_t kNoParentDoc = INT32_MIN;
+  static const uint64_t kNoParentDoc = UINT64_MAX;
 
   /*
    * Called when a document in a content process notifies the main process of a
    * new child document.
    */
   ipc::IPCResult AddChildDoc(DocAccessibleParent* aChildDoc,
                              uint64_t aParentID, bool aCreating = true);
 
@@ -133,17 +134,17 @@ public:
    */
   void RemoveChildDoc(DocAccessibleParent* aChildDoc)
   {
     ProxyAccessible* parent = aChildDoc->Parent();
     MOZ_ASSERT(parent);
     if (parent) {
       aChildDoc->Parent()->ClearChildDoc(aChildDoc);
     }
-    DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->IProtocol::Id());
+    DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID);
     aChildDoc->mParentDoc = kNoParentDoc;
     MOZ_ASSERT(result);
     MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
   }
 
   void RemoveAccessible(ProxyAccessible* aAccessible)
   {
     MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
@@ -212,32 +213,34 @@ private:
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
                       uint32_t aIdxInParent);
   MOZ_MUST_USE bool CheckDocTree() const;
   xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
 
-  nsTArray<int32_t> mChildDocs;
-  int32_t mParentDoc;
+  nsTArray<uint64_t> mChildDocs;
+  uint64_t mParentDoc;
 
 #if defined(XP_WIN)
   // The handle associated with the emulated window that contains this document
   HWND mEmulatedWindowHandle;
 #endif
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
   nsTHashtable<ProxyEntry> mAccessibles;
+  uint64_t mActorID;
   bool mTopLevel;
   bool mShutdown;
 
+  static uint64_t sMaxDocID;
   static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*>&
     LiveDocs()
     {
       static nsDataHashtable<nsUint64HashKey, DocAccessibleParent*> sLiveDocs;
       return sLiveDocs;
     }
 };
 
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.7.312
+Current extension version is: 1.7.337
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -83,25 +83,26 @@ return /******/ (function(modules) { // 
 
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__w_pdfjs_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 
 /******/ 	// __webpack_public_path__
 /******/ 	__w_pdfjs_require__.p = "";
 
 /******/ 	// Load entry module and return exports
-/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 13);
+/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 14);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 /* WEBPACK VAR INJECTION */(function(global) {
+var compatibility = __w_pdfjs_require__(13);
 var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
 var FONT_IDENTITY_MATRIX = [
  0.001,
  0,
  0,
  0.001,
  0,
  0
@@ -1180,64 +1181,16 @@ function isNodeJS() {
 function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
   capability.resolve = resolve;
   capability.reject = reject;
  });
  return capability;
 }
-(function PromiseClosure() {
- if (globalScope.Promise) {
-  if (typeof globalScope.Promise.all !== 'function') {
-   globalScope.Promise.all = function (iterable) {
-    var count = 0, results = [], resolve, reject;
-    var promise = new globalScope.Promise(function (resolve_, reject_) {
-     resolve = resolve_;
-     reject = reject_;
-    });
-    iterable.forEach(function (p, i) {
-     count++;
-     p.then(function (result) {
-      results[i] = result;
-      count--;
-      if (count === 0) {
-       resolve(results);
-      }
-     }, reject);
-    });
-    if (count === 0) {
-     resolve(results);
-    }
-    return promise;
-   };
-  }
-  if (typeof globalScope.Promise.resolve !== 'function') {
-   globalScope.Promise.resolve = function (value) {
-    return new globalScope.Promise(function (resolve) {
-     resolve(value);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.reject !== 'function') {
-   globalScope.Promise.reject = function (reason) {
-    return new globalScope.Promise(function (resolve, reject) {
-     reject(reason);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.prototype.catch !== 'function') {
-   globalScope.Promise.prototype.catch = function (onReject) {
-    return globalScope.Promise.prototype.then(undefined, onReject);
-   };
-  }
-  return;
- }
- throw new Error('DOM Promise is not present');
-}());
 var StatTimer = function StatTimerClosure() {
  function rpad(str, pad, length) {
   while (str.length < length) {
    str += pad;
   }
   return str;
  }
  function StatTimer() {
@@ -1686,16 +1639,18 @@ function getDefaultSetting(id) {
  case 'disableWebGL':
   return globalSettings ? globalSettings.disableWebGL : true;
  case 'cMapUrl':
   return globalSettings ? globalSettings.cMapUrl : null;
  case 'cMapPacked':
   return globalSettings ? globalSettings.cMapPacked : false;
  case 'postMessageTransfers':
   return globalSettings ? globalSettings.postMessageTransfers : true;
+ case 'workerPort':
+  return globalSettings ? globalSettings.workerPort : null;
  case 'workerSrc':
   return globalSettings ? globalSettings.workerSrc : null;
  case 'disableWorker':
   return globalSettings ? globalSettings.disableWorker : false;
  case 'maxImageSize':
   return globalSettings ? globalSettings.maxImageSize : -1;
  case 'imageResourcesPath':
   return globalSettings ? globalSettings.imageResourcesPath : '';
@@ -2487,17 +2442,18 @@ function getDocument(src, pdfDataRangeTr
    continue;
   }
   params[key] = source[key];
  }
  params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
  params.disableNativeImageDecoder = params.disableNativeImageDecoder === true;
  var CMapReaderFactory = params.CMapReaderFactory || DOMCMapReaderFactory;
  if (!worker) {
-  worker = new PDFWorker();
+  var workerPort = getDefaultSetting('workerPort');
+  worker = workerPort ? new PDFWorker(null, workerPort) : new PDFWorker();
   task._worker = worker;
  }
  var docId = task.docId;
  worker.promise.then(function () {
   if (task.destroyed) {
    throw new Error('Loading aborted');
   }
   return _fetchDocument(worker, params, rangeTransport, docId).then(function (workerId) {
@@ -2997,35 +2953,46 @@ var PDFWorker = function PDFWorkerClosur
   terminate: function () {
    this._listeners = [];
   }
  };
  function createCDNWrapper(url) {
   var wrapper = 'importScripts(\'' + url + '\');';
   return URL.createObjectURL(new Blob([wrapper]));
  }
- function PDFWorker(name) {
+ function PDFWorker(name, port) {
   this.name = name;
   this.destroyed = false;
   this._readyCapability = createPromiseCapability();
   this._port = null;
   this._webWorker = null;
   this._messageHandler = null;
+  if (port) {
+   this._initializeFromPort(port);
+   return;
+  }
   this._initialize();
  }
  PDFWorker.prototype = {
   get promise() {
    return this._readyCapability.promise;
   },
   get port() {
    return this._port;
   },
   get messageHandler() {
    return this._messageHandler;
   },
+  _initializeFromPort: function PDFWorker_initializeFromPort(port) {
+   this._port = port;
+   this._messageHandler = new MessageHandler('main', 'worker', port);
+   this._messageHandler.on('ready', function () {
+   });
+   this._readyCapability.resolve();
+  },
   _initialize: function PDFWorker_initialize() {
    if (!isWorkerDisabled && !getDefaultSetting('disableWorker') && typeof Worker !== 'undefined') {
     var workerSrc = getWorkerSrc();
     try {
      var worker = new Worker(workerSrc);
      var messageHandler = new MessageHandler('main', 'worker', worker);
      var terminateEarly = function () {
       worker.removeEventListener('error', onWorkerError);
@@ -3695,18 +3662,18 @@ var _UnsupportedManager = function Unsup
   },
   notify: function (featureId) {
    for (var i = 0, ii = listeners.length; i < ii; i++) {
     listeners[i](featureId);
    }
   }
  };
 }();
-exports.version = '1.7.312';
-exports.build = 'cada411a';
+exports.version = '1.7.337';
+exports.build = '9163a6fb';
 exports.getDocument = getDocument;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
 
 /***/ }),
@@ -4713,18 +4680,18 @@ var deprecated = sharedUtil.deprecated;
 var warn = sharedUtil.warn;
 var LinkTarget = displayDOMUtils.LinkTarget;
 var DEFAULT_LINK_REL = displayDOMUtils.DEFAULT_LINK_REL;
 var isWorker = typeof window === 'undefined';
 if (!globalScope.PDFJS) {
  globalScope.PDFJS = {};
 }
 var PDFJS = globalScope.PDFJS;
-PDFJS.version = '1.7.312';
-PDFJS.build = 'cada411a';
+PDFJS.version = '1.7.337';
+PDFJS.build = '9163a6fb';
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
  sharedUtil.setVerbosityLevel(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
  get: function () {
   return sharedUtil.getVerbosityLevel();
@@ -4763,16 +4730,17 @@ PDFJS.PageViewport = sharedUtil.PageView
 PDFJS.createPromiseCapability = sharedUtil.createPromiseCapability;
 PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize;
 PDFJS.cMapUrl = PDFJS.cMapUrl === undefined ? null : PDFJS.cMapUrl;
 PDFJS.cMapPacked = PDFJS.cMapPacked === undefined ? false : PDFJS.cMapPacked;
 PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ? false : PDFJS.disableFontFace;
 PDFJS.imageResourcesPath = PDFJS.imageResourcesPath === undefined ? '' : PDFJS.imageResourcesPath;
 PDFJS.disableWorker = PDFJS.disableWorker === undefined ? false : PDFJS.disableWorker;
 PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc;
+PDFJS.workerPort = PDFJS.workerPort === undefined ? null : PDFJS.workerPort;
 PDFJS.disableRange = PDFJS.disableRange === undefined ? false : PDFJS.disableRange;
 PDFJS.disableStream = PDFJS.disableStream === undefined ? false : PDFJS.disableStream;
 PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ? false : PDFJS.disableAutoFetch;
 PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
 PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ? true : PDFJS.postMessageTransfers;
 PDFJS.disableCreateObjectURL = PDFJS.disableCreateObjectURL === undefined ? false : PDFJS.disableCreateObjectURL;
 PDFJS.disableWebGL = PDFJS.disableWebGL === undefined ? true : PDFJS.disableWebGL;
 PDFJS.externalLinkTarget = PDFJS.externalLinkTarget === undefined ? LinkTarget.NONE : PDFJS.externalLinkTarget;
@@ -7206,18 +7174,25 @@ exports.getShadingPatternFromIR = getSha
 exports.TilingPattern = TilingPattern;
 
 /***/ }),
 /* 13 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.312';
-var pdfjsBuild = 'cada411a';
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+var pdfjsVersion = '1.7.337';
+var pdfjsBuild = '9163a6fb';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(2);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(4);
 exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -83,25 +83,26 @@ return /******/ (function(modules) { // 
 
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__w_pdfjs_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 
 /******/ 	// __webpack_public_path__
 /******/ 	__w_pdfjs_require__.p = "";
 
 /******/ 	// Load entry module and return exports
-/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 35);
+/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 36);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 /* WEBPACK VAR INJECTION */(function(global) {
+var compatibility = __w_pdfjs_require__(35);
 var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
 var FONT_IDENTITY_MATRIX = [
  0.001,
  0,
  0,
  0.001,
  0,
  0
@@ -1180,64 +1181,16 @@ function isNodeJS() {
 function createPromiseCapability() {
  var capability = {};
  capability.promise = new Promise(function (resolve, reject) {
   capability.resolve = resolve;
   capability.reject = reject;
  });
  return capability;
 }
-(function PromiseClosure() {
- if (globalScope.Promise) {
-  if (typeof globalScope.Promise.all !== 'function') {
-   globalScope.Promise.all = function (iterable) {
-    var count = 0, results = [], resolve, reject;
-    var promise = new globalScope.Promise(function (resolve_, reject_) {
-     resolve = resolve_;
-     reject = reject_;
-    });
-    iterable.forEach(function (p, i) {
-     count++;
-     p.then(function (result) {
-      results[i] = result;
-      count--;
-      if (count === 0) {
-       resolve(results);
-      }
-     }, reject);
-    });
-    if (count === 0) {
-     resolve(results);
-    }
-    return promise;
-   };
-  }
-  if (typeof globalScope.Promise.resolve !== 'function') {
-   globalScope.Promise.resolve = function (value) {
-    return new globalScope.Promise(function (resolve) {
-     resolve(value);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.reject !== 'function') {
-   globalScope.Promise.reject = function (reason) {
-    return new globalScope.Promise(function (resolve, reject) {
-     reject(reason);
-    });
-   };
-  }
-  if (typeof globalScope.Promise.prototype.catch !== 'function') {
-   globalScope.Promise.prototype.catch = function (onReject) {
-    return globalScope.Promise.prototype.then(undefined, onReject);
-   };
-  }
-  return;
- }
- throw new Error('DOM Promise is not present');
-}());
 var StatTimer = function StatTimerClosure() {
  function rpad(str, pad, length) {
   while (str.length < length) {
    str += pad;
   }
   return str;
  }
  function StatTimer() {
@@ -30238,17 +30191,16 @@ var Catalog = function CatalogClosure() 
    }
    return this.pagePromises[pageIndex];
   },
   getPageDict: function Catalog_getPageDict(pageIndex) {
    var capability = createPromiseCapability();
    var nodesToVisit = [this.catDict.getRaw('Pages')];
    var currentPageIndex = 0;
    var xref = this.xref;
-   var checkAllKids = false;
    function next() {
     while (nodesToVisit.length) {
      var currentNode = nodesToVisit.pop();
      if (isRef(currentNode)) {
       xref.fetchAsync(currentNode).then(function (obj) {
        if (isDict(obj, 'Page') || isDict(obj) && !obj.has('Kids')) {
         if (pageIndex === currentPageIndex) {
          capability.resolve([
@@ -30263,33 +30215,24 @@ var Catalog = function CatalogClosure() 
        }
        nodesToVisit.push(obj);
        next();
       }, capability.reject);
       return;
      }
      assert(isDict(currentNode), 'page dictionary kid reference points to wrong type of object');
      var count = currentNode.get('Count');
-     if (count === 0) {
-      checkAllKids = true;
-     }
      if (currentPageIndex + count <= pageIndex) {
       currentPageIndex += count;
       continue;
      }
      var kids = currentNode.get('Kids');
      assert(isArray(kids), 'page dictionary kids object is not an array');
-     if (!checkAllKids && count === kids.length) {
-      nodesToVisit = [kids[pageIndex - currentPageIndex]];
-      currentPageIndex = pageIndex;
-      continue;
-     } else {
-      for (var last = kids.length - 1; last >= 0; last--) {
-       nodesToVisit.push(kids[last]);
-      }
+     for (var last = kids.length - 1; last >= 0; last--) {
+      nodesToVisit.push(kids[last]);
      }
     }
     capability.reject('Page index ' + pageIndex + ' not found.');
    }
    next();
    return capability.promise;
   },
   getPageIndex: function Catalog_getPageIndex(pageRef) {
@@ -30441,20 +30384,23 @@ var Catalog = function CatalogClosure() 
     } else if (isString(jsAction)) {
      js = jsAction;
     }
     if (js) {
      var URL_OPEN_METHODS = [
       'app.launchURL',
       'window.open'
      ];
-     var regex = new RegExp('^(?:' + URL_OPEN_METHODS.join('|') + ')' + '\\((?:\'|\")(\\S+)(?:\'|\")(?:,|\\))');
-     var jsUrl = regex.exec(stringToPDFString(js), 'i');
-     if (jsUrl && jsUrl[1]) {
-      url = jsUrl[1];
+     var regex = new RegExp('^\\s*(' + URL_OPEN_METHODS.join('|').split('.').join('\\.') + ')\\((?:\'|\")([^\'\"]*)(?:\'|\")(?:,\\s*(\\w+)\\)|\\))', 'i');
+     var jsUrl = regex.exec(stringToPDFString(js));
+     if (jsUrl && jsUrl[2]) {
+      url = jsUrl[2];
+      if (jsUrl[3] === 'true' && jsUrl[1] === 'app.launchURL') {
+       resultObj.newWindow = true;
+      }
       break;
      }
     }
    default:
     warn('Catalog_parseDestDictionary: Unrecognized link type "' + linkType + '".');
     break;
    }
   } else if (destDict.has('Dest')) {
@@ -30887,16 +30833,19 @@ var XRef = function XRefClosure() {
    }
    return this.fetch(obj, suppressEncryption);
   },
   fetch: function XRef_fetch(ref, suppressEncryption) {
    assert(isRef(ref), 'ref object is not a reference');
    var num = ref.num;
    if (num in this.cache) {
     var cacheEntry = this.cache[num];
+    if (isDict(cacheEntry) && !cacheEntry.objId) {
+     cacheEntry.objId = ref.toString();
+    }
     return cacheEntry;
    }
    var xrefEntry = this.getEntry(num);
    if (xrefEntry === null) {
     return this.cache[num] = null;
    }
    if (xrefEntry.uncompressed) {
     xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
@@ -33957,25 +33906,23 @@ var MissingPDFException = sharedUtil.Mis
 var UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
 var PasswordException = sharedUtil.PasswordException;
 var UnknownErrorException = sharedUtil.UnknownErrorException;
 var XRefParseException = sharedUtil.XRefParseException;
 var arrayByteLength = sharedUtil.arrayByteLength;
 var arraysToBytes = sharedUtil.arraysToBytes;
 var assert = sharedUtil.assert;
 var createPromiseCapability = sharedUtil.createPromiseCapability;
-var error = sharedUtil.error;
 var info = sharedUtil.info;
 var warn = sharedUtil.warn;
 var setVerbosityLevel = sharedUtil.setVerbosityLevel;
 var isNodeJS = sharedUtil.isNodeJS;
 var Ref = corePrimitives.Ref;
 var LocalPdfManager = corePdfManager.LocalPdfManager;
 var NetworkPdfManager = corePdfManager.NetworkPdfManager;
-var globalScope = sharedUtil.globalScope;
 var WorkerTask = function WorkerTaskClosure() {
  function WorkerTask(name) {
   this.name = name;
   this.terminated = false;
   this._capability = createPromiseCapability();
  }
  WorkerTask.prototype = {
   get finished() {
@@ -34960,31 +34907,16 @@ var Annotation = function AnnotationClos
     return evaluator.getOperatorList(self.appearance, task, resources, opList).then(function () {
      opList.addOp(OPS.endAnnotation, []);
      self.appearance.reset();
      return opList;
     });
    });
   }
  };
- Annotation.appendToOperatorList = function Annotation_appendToOperatorList(annotations, opList, partialEvaluator, task, intent, renderForms) {
-  var annotationPromises = [];
-  for (var i = 0, n = annotations.length; i < n; ++i) {
-   if (intent === 'display' && annotations[i].viewable || intent === 'print' && annotations[i].printable) {
-    annotationPromises.push(annotations[i].getOperatorList(partialEvaluator, task, renderForms));
-   }
-  }
-  return Promise.all(annotationPromises).then(function (operatorLists) {
-   opList.addOp(OPS.beginAnnotations, []);
-   for (var i = 0, n = operatorLists.length; i < n; ++i) {
-    opList.addOpList(operatorLists[i]);
-   }
-   opList.addOp(OPS.endAnnotations, []);
-  });
- };
  return Annotation;
 }();
 var AnnotationBorderStyle = function AnnotationBorderStyleClosure() {
  function AnnotationBorderStyle() {
   this.width = 1;
   this.style = AnnotationBorderStyleType.SOLID;
   this.dashArray = [3];
   this.horizontalCornerRadius = 0;
@@ -35193,17 +35125,17 @@ var ButtonWidgetAnnotation = function Bu
   }
  });
  return ButtonWidgetAnnotation;
 }();
 var ChoiceWidgetAnnotation = function ChoiceWidgetAnnotationClosure() {
  function ChoiceWidgetAnnotation(params) {
   WidgetAnnotation.call(this, params);
   this.data.options = [];
-  var options = params.dict.get('Opt');
+  var options = Util.getInheritableProperty(params.dict, 'Opt');
   if (isArray(options)) {
    var xref = params.xref;
    for (var i = 0, ii = options.length; i < ii; i++) {
     var option = xref.fetchIfRef(options[i]);
     var isOptionArray = isArray(option);
     this.data.options[i] = {
      exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option,
      displayValue: isOptionArray ? xref.fetchIfRef(option[1]) : option
@@ -37478,16 +37410,17 @@ exports.IdentityCMap = IdentityCMap;
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreStream = __w_pdfjs_require__(2);
 var coreObj = __w_pdfjs_require__(14);
 var coreParser = __w_pdfjs_require__(5);
 var coreCrypto = __w_pdfjs_require__(11);
 var coreEvaluator = __w_pdfjs_require__(12);
 var coreAnnotation = __w_pdfjs_require__(19);
+var OPS = sharedUtil.OPS;
 var MissingDataException = sharedUtil.MissingDataException;
 var Util = sharedUtil.Util;
 var assert = sharedUtil.assert;
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var isArray = sharedUtil.isArray;
 var isArrayBuffer = sharedUtil.isArrayBuffer;
 var isNum = sharedUtil.isNum;
@@ -37506,26 +37439,28 @@ var Stream = coreStream.Stream;
 var StreamsSequenceStream = coreStream.StreamsSequenceStream;
 var Catalog = coreObj.Catalog;
 var ObjectLoader = coreObj.ObjectLoader;
 var XRef = coreObj.XRef;
 var Linearization = coreParser.Linearization;
 var calculateMD5 = coreCrypto.calculateMD5;
 var OperatorList = coreEvaluator.OperatorList;
 var PartialEvaluator = coreEvaluator.PartialEvaluator;
-var Annotation = coreAnnotation.Annotation;
 var AnnotationFactory = coreAnnotation.AnnotationFactory;
 var Page = function PageClosure() {
  var DEFAULT_USER_UNIT = 1.0;
  var LETTER_SIZE_MEDIABOX = [
   0,
   0,
   612,
   792
  ];
+ function isAnnotationRenderable(annotation, intent) {
+  return intent === 'display' && annotation.viewable || intent === 'print' && annotation.printable;
+ }
  function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache, builtInCMapCache) {
   this.pdfManager = pdfManager;
   this.pageIndex = pageIndex;
   this.pageDict = pageDict;
   this.xref = xref;
   this.ref = ref;
   this.fontCache = fontCache;
   this.builtInCMapCache = builtInCMapCache;
@@ -37677,18 +37612,28 @@ var Page = function PageClosure() {
     annotationsPromise
    ]).then(function (datas) {
     var pageOpList = datas[0];
     var annotations = datas[1];
     if (annotations.length === 0) {
      pageOpList.flush(true);
      return pageOpList;
     }
-    var annotationsReadyPromise = Annotation.appendToOperatorList(annotations, pageOpList, partialEvaluator, task, intent, renderInteractiveForms);
-    return annotationsReadyPromise.then(function () {
+    var i, ii, opListPromises = [];
+    for (i = 0, ii = annotations.length; i < ii; i++) {
+     if (isAnnotationRenderable(annotations[i], intent)) {
+      opListPromises.push(annotations[i].getOperatorList(partialEvaluator, task, renderInteractiveForms));
+     }
+    }
+    return Promise.all(opListPromises).then(function (opLists) {
+     pageOpList.addOp(OPS.beginAnnotations, []);
+     for (i = 0, ii = opLists.length; i < ii; i++) {
+      pageOpList.addOpList(opLists[i]);
+     }
+     pageOpList.addOp(OPS.endAnnotations, []);
      pageOpList.flush(true);
      return pageOpList;
     });
    });
   },
   extractTextContent: function Page_extractTextContent(task, normalizeWhitespace, combineTextItems) {
    var handler = {
     on: function nullHandlerOn() {
@@ -37713,22 +37658,19 @@ var Page = function PageClosure() {
     var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.builtInCMapCache, self.evaluatorOptions);
     return partialEvaluator.getTextContent(contentStream, task, self.resources, null, normalizeWhitespace, combineTextItems);
    });
   },
   getAnnotationsData: function Page_getAnnotationsData(intent) {
    var annotations = this.annotations;
    var annotationsData = [];
    for (var i = 0, n = annotations.length; i < n; ++i) {
-    if (intent) {
-     if (!(intent === 'display' && annotations[i].viewable) && !(intent === 'print' && annotations[i].printable)) {
-      continue;
-     }
-    }
-    annotationsData.push(annotations[i].data);
+    if (!intent || isAnnotationRenderable(annotations[i], intent)) {
+     annotationsData.push(annotations[i].data);
+    }
    }
    return annotationsData;
   },
   get annotations() {
    var annotations = [];
    var annotationRefs = this.getInheritedPageProp('Annots') || [];
    var annotationFactory = new AnnotationFactory();
    for (var i = 0, n = annotationRefs.length; i < n; ++i) {
@@ -49171,17 +49113,24 @@ var Type1Parser = function Type1ParserCl
 exports.Type1Parser = Type1Parser;
 
 /***/ }),
 /* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.312';
-var pdfjsBuild = 'cada411a';
+
+/***/ }),
+/* 36 */
+/***/ (function(module, exports, __w_pdfjs_require__) {
+
+"use strict";
+
+var pdfjsVersion = '1.7.337';
+var pdfjsBuild = '9163a6fb';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -497,17 +497,25 @@ exports.localized = localized;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 {
- module.exports = window['pdfjs-dist/build/pdf'];
+ var pdfjsLib;
+ if (typeof __pdfjsdev_webpack__ === 'undefined') {
+  if (typeof require === 'function') {
+   pdfjsLib = require('../build/pdf.js');
+  } else {
+   pdfjsLib = window['pdfjs-dist/build/pdf'];
+  }
+ }
+ module.exports = pdfjsLib;
 }
 
 /***/ }),
 /* 2 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
--- a/browser/themes/shared/compacttheme.inc.css
+++ b/browser/themes/shared/compacttheme.inc.css
@@ -207,16 +207,25 @@ toolbar[brighttext] #downloads-indicator
 }
 
 window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
   overflow: -moz-hidden-unscrollable;
   clip-path: none;
   margin-inline-start: 0;
 }
 
+window:not([chromehidden~="toolbar"]) #urlbar-wrapper:-moz-locale-dir(rtl),
+window:not([chromehidden~="toolbar"]) #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) {
+  /* Resolves text blurring issue when hovering, see bug 1340206 */
+  transform: none;
+  /* For some reason, this property must be specified here, even though the same
+     value is set in the previous rule set. o_O */
+  margin-inline-start: 0;
+}
+
 #urlbar-zoom-button:-moz-lwtheme-brighttext:hover {
   background-color: rgba(255,255,255,.2);
 }
 
 #urlbar-zoom-button:-moz-lwtheme-brighttext:hover:active {
   background-color: rgba(255,255,255,.3);
 }
 
--- a/build/clang-plugin/RefCountedInsideLambdaChecker.cpp
+++ b/build/clang-plugin/RefCountedInsideLambdaChecker.cpp
@@ -23,25 +23,29 @@ void RefCountedInsideLambdaChecker::regi
       this);
   AstMatcher->addMatcher(
       classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType(
         recordType(hasDeclaration(cxxRecordDecl(
           isLambdaDecl()).bind("decl")))))),
       this);
 }
 
+void RefCountedInsideLambdaChecker::emitDiagnostics(SourceLocation Loc,
+                                                    StringRef Name,
+                                                    QualType Type) {
+  diag(Loc, "Refcounted variable '%0' of type %1 cannot be captured by a lambda",
+       DiagnosticIDs::Error) << Name << Type;
+  diag(Loc, "Please consider using a smart pointer",
+       DiagnosticIDs::Note);
+}
+
 void RefCountedInsideLambdaChecker::check(
     const MatchFinder::MatchResult &Result) {
   static DenseSet<const CXXRecordDecl*> CheckedDecls;
 
-  const char* Error =
-      "Refcounted variable %0 of type %1 cannot be captured by a lambda";
-  const char* Note =
-      "Please consider using a smart pointer";
-
   const CXXRecordDecl *Lambda = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
 
   if (const LambdaExpr *OuterLambda =
     Result.Nodes.getNodeAs<LambdaExpr>("lambdaExpr")) {
     const CXXMethodDecl *OpCall = OuterLambda->getCallOperator();
     QualType ReturnTy = OpCall->getReturnType();
     if (const CXXRecordDecl *Record = ReturnTy->getAsCXXRecordDecl()) {
       Lambda = Record;
@@ -53,23 +57,93 @@ void RefCountedInsideLambdaChecker::chec
   }
 
   // Don't report errors on the same declarations more than once.
   if (CheckedDecls.count(Lambda)) {
     return;
   }
   CheckedDecls.insert(Lambda);
 
-  for (const LambdaCapture Capture : Lambda->captures()) {
-    if (Capture.capturesVariable() && Capture.getCaptureKind() != LCK_ByRef) {
+  bool StrongRefToThisCaptured = false;
+
+  for (const LambdaCapture& Capture : Lambda->captures()) {
+    // Check if any of the captures are ByRef. If they are, we have nothing to
+    // report, as it's OK to capture raw pointers to refcounted objects so long as
+    // the Lambda doesn't escape the current scope, which is required by ByRef
+    // captures already.
+    if (Capture.getCaptureKind() == LCK_ByRef) {
+      return;
+    }
+
+    // Check if this capture is byvalue, and captures a strong reference to this.
+    // XXX: Do we want to make sure that this type which we are capturing is a "Smart Pointer" somehow?
+    if (!StrongRefToThisCaptured &&
+        Capture.capturesVariable() &&
+        Capture.getCaptureKind() == LCK_ByCopy) {
+      const VarDecl *Var = Capture.getCapturedVar();
+      if (Var->hasInit()) {
+        const Stmt *Init = Var->getInit();
+
+        // Ignore single argument constructors, and trivial nodes.
+        while (true) {
+          auto NewInit = IgnoreTrivials(Init);
+          if (auto ConstructExpr = dyn_cast<CXXConstructExpr>(NewInit)) {
+            if (ConstructExpr->getNumArgs() == 1) {
+              NewInit = ConstructExpr->getArg(0);
+            }
+          }
+          if (Init == NewInit) {
+            break;
+          }
+          Init = NewInit;
+        }
+
+        if (isa<CXXThisExpr>(Init)) {
+          StrongRefToThisCaptured = true;
+        }
+      }
+    }
+  }
+
+  // Now we can go through and produce errors for any captured variables or this pointers.
+  for (const LambdaCapture& Capture : Lambda->captures()) {
+    if (Capture.capturesVariable()) {
       QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType();
 
       if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
-        diag(Capture.getLocation(), Error,
-             DiagnosticIDs::Error) << Capture.getCapturedVar()
-                                   << Pointee;
-        diag(Capture.getLocation(), Note,
-             DiagnosticIDs::Note);
+        emitDiagnostics(Capture.getLocation(), Capture.getCapturedVar()->getName(), Pointee);
+        return;
+      }
+    }
+
+    // The situation with captures of `this` is more complex. All captures of
+    // `this` look the same-ish (they are LCK_This). We want to complain about
+    // captures of `this` where `this` is a refcounted type, and the capture is
+    // actually used in the body of the lambda (if the capture isn't used, then
+    // we don't care, because it's only being captured in order to give access
+    // to private methods).
+    //
+    // In addition, we don't complain about this, even if it is used, if it was
+    // captured implicitly when the LambdaCaptureDefault was LCD_ByRef, as that
+    // expresses the intent that the lambda won't leave the enclosing scope.
+    bool ImplicitByRefDefaultedCapture =
+      Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef;
+    if (Capture.capturesThis() &&
+        !ImplicitByRefDefaultedCapture &&
+        !StrongRefToThisCaptured) {
+      ThisVisitor V(*this);
+      bool NotAborted = V.TraverseDecl(const_cast<CXXMethodDecl *>(Lambda->getLambdaCallOperator()));
+      if (!NotAborted) {
         return;
       }
     }
   }
 }
+
+bool RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(CXXThisExpr *This) {
+  QualType Pointee = This->getType()->getPointeeType();
+  if (!Pointee.isNull() && isClassRefCounted(Pointee)) {
+    Checker.emitDiagnostics(This->getLocStart(), "this", Pointee);
+    return false;
+  }
+
+  return true;
+}
--- a/build/clang-plugin/RefCountedInsideLambdaChecker.h
+++ b/build/clang-plugin/RefCountedInsideLambdaChecker.h
@@ -9,11 +9,24 @@
 
 class RefCountedInsideLambdaChecker : public BaseCheck {
 public:
   RefCountedInsideLambdaChecker(StringRef CheckName,
                                 ContextType *Context = nullptr)
     : BaseCheck(CheckName, Context) {}
   void registerMatchers(MatchFinder* AstMatcher) override;
   void check(const MatchFinder::MatchResult &Result) override;
+
+  void emitDiagnostics(SourceLocation Loc, StringRef Name, QualType Type);
+
+private:
+  class ThisVisitor : public RecursiveASTVisitor<ThisVisitor> {
+  public:
+    explicit ThisVisitor(RefCountedInsideLambdaChecker& Checker)
+      : Checker(Checker) {}
+
+    bool VisitCXXThisExpr(CXXThisExpr *This);
+  private:
+    RefCountedInsideLambdaChecker& Checker;
+  };
 };
 
 #endif
--- a/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp
+++ b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp
@@ -1,24 +1,29 @@
 #include <functional>
 #define MOZ_STRONG_REF __attribute__((annotate("moz_strong_ref")))
+#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
 
 struct RefCountedBase {
   void AddRef();
   void Release();
 };
 
 template <class T>
 struct SmartPtr {
+  SmartPtr();
+  MOZ_IMPLICIT SmartPtr(T*);
   T* MOZ_STRONG_REF t;
   T* operator->() const;
 };
 
 struct R : RefCountedBase {
   void method();
+private:
+  void privateMethod();
 };
 
 void take(...);
 void foo() {
   R* ptr;
   SmartPtr<R> sp;
   take([&](R* argptr) {
     R* localptr;
@@ -542,8 +547,75 @@ void e() {
     return ([&sp](SmartPtr<R> argsp) {
       SmartPtr<R> localsp;
       take(sp);
       take(argsp);
       take(localsp);
     });
   };
 }
+
+void
+R::privateMethod() {
+  SmartPtr<R> self = this;
+  std::function<void()>([&]() {
+    self->method();
+  });
+  std::function<void()>([&]() {
+    self->privateMethod();
+  });
+  std::function<void()>([&]() {
+    this->method();
+  });
+  std::function<void()>([&]() {
+    this->privateMethod();
+  });
+  std::function<void()>([=]() {
+    self->method();
+  });
+  std::function<void()>([=]() {
+    self->privateMethod();
+  });
+  std::function<void()>([=]() {
+    this->method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([=]() {
+    this->privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([self]() {
+    self->method();
+  });
+  std::function<void()>([self]() {
+    self->privateMethod();
+  });
+  std::function<void()>([this]() {
+    this->method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([this]() {
+    this->privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([this]() {
+    method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([this]() {
+    privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([=]() {
+    method(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([=]() {
+    privateMethod(); // expected-error{{Refcounted variable 'this' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}}
+  });
+  std::function<void()>([&]() {
+    method();
+  });
+  std::function<void()>([&]() {
+    privateMethod();
+  });
+
+  // It should be OK to go through `this` if we have captured a reference to it.
+  std::function<void()>([this, self]() {
+    this->method();
+    this->privateMethod();
+    method();
+    privateMethod();
+  });
+}
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -853,17 +853,17 @@ Element::SetScrollLeft(int32_t aScrollLe
 bool
 Element::ScrollByNoFlush(int32_t aDx, int32_t aDy)
 {
   nsIScrollableFrame* sf = GetScrollFrame(nullptr, false);
   if (!sf) {
     return false;
   }
 
-  nsWeakFrame weakRef(sf->GetScrolledFrame());
+  AutoWeakFrame weakRef(sf->GetScrolledFrame());
 
   CSSIntPoint before = sf->GetScrollPositionCSSPixels();
   sf->ScrollToCSSPixelsApproximate(CSSIntPoint(before.x + aDx, before.y + aDy));
 
   // The frame was destroyed, can't keep on scrolling.
   if (!weakRef.IsAlive()) {
     return false;
   }
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2694,17 +2694,17 @@ nsFrameLoader::UpdateBaseWindowPositionA
   GetDocShell(getter_AddRefs(docShell));
   nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell));
 
   // resize the sub document
   if (baseWindow) {
     int32_t x = 0;
     int32_t y = 0;
 
-    nsWeakFrame weakFrame(aIFrame);
+    AutoWeakFrame weakFrame(aIFrame);
 
     baseWindow->GetPosition(&x, &y);
 
     if (!weakFrame.IsAlive()) {
       // GetPosition() killed us
       return;
     }
 
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -324,17 +324,17 @@ private:
 
   // After the frameloader has been removed from the DOM but before all of the
   // messages from the frame have been received, we keep a strong reference to
   // our <browser> element.
   RefPtr<mozilla::dom::Element> mOwnerContentStrong;
 
   // Stores the root frame of the subdocument while the subdocument is being
   // reframed. Used to restore the presentation after reframing.
-  nsWeakFrame mDetachedSubdocFrame;
+  WeakFrame mDetachedSubdocFrame;
   // Stores the containing document of the frame corresponding to this
   // frame loader. This is reference is kept valid while the subframe's
   // presentation is detached and stored in mDetachedSubdocFrame. This
   // enables us to detect whether the frame has moved documents during
   // a reframe, so that we know not to restore the presentation.
   nsCOMPtr<nsIDocument> mContainerDocWhileDetached;
 
   // An opener window which should be used when the docshell is created.
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -710,16 +710,16 @@ class nsObjectLoadingContent : public ns
     // videos.
     bool                        mRewrittenYoutubeEmbed : 1;
 
     // Cache the answer of PreferFallback() because ShouldPlay is called several
     // times during the load process.
     bool                        mPreferFallback : 1;
     bool                        mPreferFallbackKnown : 1;
 
-    nsWeakFrame                 mPrintFrame;
+    WeakFrame                   mPrintFrame;
 
     RefPtr<nsPluginInstanceOwner> mInstanceOwner;
     nsTArray<mozilla::dom::MozPluginParameter> mCachedAttributes;
     nsTArray<mozilla::dom::MozPluginParameter> mCachedParameters;
 };
 
 #endif
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -110,17 +110,17 @@ using namespace dom;
 
 static const LayoutDeviceIntPoint kInvalidRefPoint = LayoutDeviceIntPoint(-1,-1);
 
 static uint32_t gMouseOrKeyboardEventCounter = 0;
 static nsITimer* gUserInteractionTimer = nullptr;
 static nsITimerCallback* gUserInteractionTimerCallback = nullptr;
 
 static const double kCursorLoadingTimeout = 1000; // ms
-static nsWeakFrame gLastCursorSourceFrame;
+static AutoWeakFrame gLastCursorSourceFrame;
 static TimeStamp gLastCursorUpdateTime;
 
 static inline int32_t
 RoundDown(double aDouble)
 {
   return (aDouble > 0) ? static_cast<int32_t>(floor(aDouble)) :
                          static_cast<int32_t>(ceil(aDouble));
 }
@@ -278,17 +278,17 @@ NS_INTERFACE_MAP_END
 static uint32_t sESMInstanceCount = 0;
 static bool sPointerEventEnabled = false;
 
 uint64_t EventStateManager::sUserInputCounter = 0;
 int32_t EventStateManager::sUserInputEventDepth = 0;
 bool EventStateManager::sNormalLMouseEventInProcess = false;
 EventStateManager* EventStateManager::sActiveESM = nullptr;
 nsIDocument* EventStateManager::sMouseOverDocument = nullptr;
-nsWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
+AutoWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
 LayoutDeviceIntPoint EventStateManager::sPreLockPoint = LayoutDeviceIntPoint(0, 0);
 LayoutDeviceIntPoint EventStateManager::sLastRefPoint = kInvalidRefPoint;
 CSSIntPoint EventStateManager::sLastScreenPoint = CSSIntPoint(0, 0);
 LayoutDeviceIntPoint EventStateManager::sSynthCenteringPoint = kInvalidRefPoint;
 CSSIntPoint EventStateManager::sLastClientPoint = CSSIntPoint(0, 0);
 bool EventStateManager::sIsPointerLocked = false;
 // Reference to the pointer locked element.
 nsWeakPtr EventStateManager::sPointerLockedElement;
@@ -2232,17 +2232,17 @@ EventStateManager::DispatchLegacyMouseSc
   }
 
   // Send the legacy events in following order:
   // 1. Vertical scroll
   // 2. Vertical pixel scroll (even if #1 isn't consumed)
   // 3. Horizontal scroll (even if #1 and/or #2 are consumed)
   // 4. Horizontal pixel scroll (even if #3 isn't consumed)
 
-  nsWeakFrame targetFrame(aTargetFrame);
+  AutoWeakFrame targetFrame(aTargetFrame);
 
   MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault &&
              !aEvent->DefaultPrevented(),
              "If you make legacy events dispatched for default prevented wheel "
              "event, you need to initialize stateX and stateY");
   EventState stateX, stateY;
   if (scrollDeltaY) {
     SendLineScrollEvent(aTargetFrame, aEvent, stateY,
@@ -2544,17 +2544,17 @@ EventStateManager::DoScrollText(nsIScrol
                                 WidgetWheelEvent* aEvent)
 {
   MOZ_ASSERT(aScrollableFrame);
   MOZ_ASSERT(aEvent);
 
   nsIFrame* scrollFrame = do_QueryFrame(aScrollableFrame);
   MOZ_ASSERT(scrollFrame);
 
-  nsWeakFrame scrollFrameWeak(scrollFrame);
+  AutoWeakFrame scrollFrameWeak(scrollFrame);
   if (!WheelTransaction::WillHandleDefaultAction(aEvent, scrollFrameWeak)) {
     return;
   }
 
   // Default action's actual scroll amount should be computed from device
   // pixels.
   nsPresContext* pc = scrollFrame->PresContext();
   nsSize scrollAmount = GetScrollAmount(pc, aEvent, aScrollableFrame);
@@ -3925,17 +3925,17 @@ EventStateManager::DispatchMouseOrPointe
   if (!aTargetContent) {
     return nullptr;
   }
 
   nsAutoPtr<WidgetMouseEvent> dispatchEvent;
   CreateMouseOrPointerWidgetEvent(aMouseEvent, aMessage,
                                   aRelatedContent, dispatchEvent);
 
-  nsWeakFrame previousTarget = mCurrentTarget;
+  AutoWeakFrame previousTarget = mCurrentTarget;
   mCurrentTargetContent = aTargetContent;
 
   nsIFrame* targetFrame = nullptr;
 
   if (aMouseEvent->AsMouseEvent()) {
     PROFILER_LABEL("Input", "DispatchMouseEvent",
       js::ProfileEntry::Category::EVENTS);
   }
@@ -4445,18 +4445,19 @@ EventStateManager::GenerateDragDropEnter
             WidgetDragEvent remoteEvent(aDragEvent->IsTrusted(), eDragExit,
                                         aDragEvent->mWidget);
             remoteEvent.AssignDragEventData(*aDragEvent, true);
             nsEventStatus remoteStatus = nsEventStatus_eIgnore;
             HandleCrossProcessEvent(&remoteEvent, &remoteStatus);
           }
         }
 
+        AutoWeakFrame currentTraget = mCurrentTarget;
         FireDragEnterOrExit(aPresContext, aDragEvent, eDragEnter,
-                            lastContent, targetContent, mCurrentTarget);
+                            lastContent, targetContent, currentTraget);
 
         if (sLastDragOverFrame) {
           FireDragEnterOrExit(sLastDragOverFrame->PresContext(),
                               aDragEvent, eDragLeave,
                               targetContent, lastContent, sLastDragOverFrame);
         }
 
         sLastDragOverFrame = mCurrentTarget;
@@ -4497,17 +4498,17 @@ EventStateManager::GenerateDragDropEnter
 }
 
 void
 EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext,
                                        WidgetDragEvent* aDragEvent,
                                        EventMessage aMessage,
                                        nsIContent* aRelatedTarget,
                                        nsIContent* aTargetContent,
-                                       nsWeakFrame& aTargetFrame)
+                                       AutoWeakFrame& aTargetFrame)
 {
   MOZ_ASSERT(aMessage == eDragLeave || aMessage == eDragExit ||
              aMessage == eDragEnter);
   nsEventStatus status = nsEventStatus_eIgnore;
   WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->mWidget);
   event.AssignDragEventData(*aDragEvent, true);
   mCurrentTargetContent = aTargetContent;
 
@@ -4636,17 +4637,17 @@ EventStateManager::SetClickCount(WidgetM
 }
 
 nsresult
 EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
                                              nsEventStatus* aStatus,
                                              EventMessage aMessage,
                                              nsIPresShell* aPresShell,
                                              nsIContent* aMouseTarget,
-                                             nsWeakFrame aCurrentTarget,
+                                             AutoWeakFrame aCurrentTarget,
                                              bool aNoContentDispatch)
 {
   WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
                          aEvent->mWidget, WidgetMouseEvent::eReal);
 
   event.mRefPoint = aEvent->mRefPoint;
   event.mClickCount = aEvent->mClickCount;
   event.mModifiers = aEvent->mModifiers;
@@ -4694,17 +4695,17 @@ EventStateManager::CheckForAndDispatchCl
         mouseContent = mouseContent->GetParent();
       }
 
       if (!mouseContent && !mCurrentTarget) {
         return NS_OK;
       }
 
       // HandleEvent clears out mCurrentTarget which we might need again
-      nsWeakFrame currentTarget = mCurrentTarget;
+      AutoWeakFrame currentTarget = mCurrentTarget;
       ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
                                       presShell, mouseContent, currentTarget,
                                       notDispatchToContents);
 
       if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
           mouseContent && mouseContent->IsInComposedDoc()) {
         //fire double click
         ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -49,17 +49,17 @@ class OverOutElementsWrapper final : pub
   ~OverOutElementsWrapper();
 
 public:
   OverOutElementsWrapper();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(OverOutElementsWrapper)
 
-  nsWeakFrame mLastOverFrame;
+  WeakFrame mLastOverFrame;
 
   nsCOMPtr<nsIContent> mLastOverElement;
 
   // The last element on which we fired a over event, or null if
   // the last over event we fired has finished processing.
   nsCOMPtr<nsIContent> mFirstOverEventElement;
 
   // The last element on which we fired a out event, or null if
@@ -403,29 +403,29 @@ protected:
    * @param aTargetContent target to set for the event
    * @param aTargetFrame target frame for the event
    */
   void FireDragEnterOrExit(nsPresContext* aPresContext,
                            WidgetDragEvent* aDragEvent,
                            EventMessage aMessage,
                            nsIContent* aRelatedTarget,
                            nsIContent* aTargetContent,
-                           nsWeakFrame& aTargetFrame);
+                           AutoWeakFrame& aTargetFrame);
   /**
    * Update the initial drag session data transfer with any changes that occur
    * on cloned data transfer objects used for events.
    */
   void UpdateDragDataTransfer(WidgetDragEvent* dragEvent);
 
   static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
                                             nsEventStatus* aStatus,
                                             EventMessage aMessage,
                                             nsIPresShell* aPresShell,
                                             nsIContent* aMouseTarget,
-                                            nsWeakFrame aCurrentTarget,
+                                            AutoWeakFrame aCurrentTarget,
                                             bool aNoContentDispatch);
   nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
   nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
                                     nsEventStatus* aStatus);
   void EnsureDocument(nsPresContext* aPresContext);
   void FlushPendingEvents(nsPresContext* aPresContext);
 
   /**
@@ -925,19 +925,19 @@ private:
 
   // Stores the mRefPoint of the last synthetic mouse move we dispatched
   // to re-center the mouse when we were pointer locked. If this is (-1,-1) it
   // means we've not recently dispatched a centering event. We use this to
   // detect when we receive the synth event, so we can cancel and not send it
   // to content.
   static LayoutDeviceIntPoint sSynthCenteringPoint;
 
-  nsWeakFrame mCurrentTarget;
+  WeakFrame mCurrentTarget;
   nsCOMPtr<nsIContent> mCurrentTargetContent;
-  static nsWeakFrame sLastDragOverFrame;
+  static AutoWeakFrame sLastDragOverFrame;
 
   // Stores the mRefPoint (the offset from the widget's origin in device
   // pixels) of the last mouse event.
   static LayoutDeviceIntPoint sLastRefPoint;
 
   // member variables for the d&d gesture state machine
   LayoutDeviceIntPoint mGestureDownPoint; // screen coordinates
   // The content to use as target if we start a d&d (what we drag).
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -78,17 +78,17 @@ WheelHandlingUtils::CanScrollOn(nsIScrol
           CanScrollInRange(scrollRange.y, scrollPt.y,
                            scrollRange.YMost(), aDirectionY));
 }
 
 /******************************************************************/
 /* mozilla::WheelTransaction                                      */
 /******************************************************************/
 
-nsWeakFrame WheelTransaction::sTargetFrame(nullptr);
+AutoWeakFrame WheelTransaction::sTargetFrame(nullptr);
 uint32_t WheelTransaction::sTime = 0;
 uint32_t WheelTransaction::sMouseMoved = 0;
 nsITimer* WheelTransaction::sTimer = nullptr;
 int32_t WheelTransaction::sScrollSeriesCounter = 0;
 bool WheelTransaction::sOwnScrollbars = false;
 
 /* static */ bool
 WheelTransaction::OutOfTime(uint32_t aBaseTime, uint32_t aThreshold)
@@ -174,17 +174,17 @@ WheelTransaction::EndTransaction()
     sOwnScrollbars = false;
     ScrollbarsForWheel::OwnWheelTransaction(false);
     ScrollbarsForWheel::Inactivate();
   }
 }
 
 /* static */ bool
 WheelTransaction::WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
-                                          nsWeakFrame& aTargetWeakFrame)
+                                          AutoWeakFrame& aTargetWeakFrame)
 {
   nsIFrame* lastTargetFrame = GetTargetFrame();
   if (!lastTargetFrame) {
     BeginTransaction(aTargetWeakFrame.GetFrame(), aWheelEvent);
   } else if (lastTargetFrame != aTargetWeakFrame.GetFrame()) {
     EndTransaction();
     BeginTransaction(aTargetWeakFrame.GetFrame(), aWheelEvent);
   } else {
@@ -422,18 +422,18 @@ WheelTransaction::OverrideSystemScrollSp
 /******************************************************************/
 /* mozilla::ScrollbarsForWheel                                    */
 /******************************************************************/
 
 const DeltaValues ScrollbarsForWheel::directions[kNumberOfTargets] = {
   DeltaValues(-1, 0), DeltaValues(+1, 0), DeltaValues(0, -1), DeltaValues(0, +1)
 };
 
-nsWeakFrame ScrollbarsForWheel::sActiveOwner = nullptr;
-nsWeakFrame ScrollbarsForWheel::sActivatedScrollTargets[kNumberOfTargets] = {
+AutoWeakFrame ScrollbarsForWheel::sActiveOwner = nullptr;
+AutoWeakFrame ScrollbarsForWheel::sActivatedScrollTargets[kNumberOfTargets] = {
   nullptr, nullptr, nullptr, nullptr
 };
 
 bool ScrollbarsForWheel::sHadWheelStart = false;
 bool ScrollbarsForWheel::sOwnWheelTransaction = false;
 
 /* static */ void
 ScrollbarsForWheel::PrepareToScrollText(EventStateManager* aESM,
@@ -515,17 +515,17 @@ ScrollbarsForWheel::OwnWheelTransaction(
 /* static */ void
 ScrollbarsForWheel::TemporarilyActivateAllPossibleScrollTargets(
                       EventStateManager* aESM,
                       nsIFrame* aTargetFrame,
                       WidgetWheelEvent* aEvent)
 {
   for (size_t i = 0; i < kNumberOfTargets; i++) {
     const DeltaValues *dir = &directions[i];
-    nsWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
+    AutoWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
     MOZ_ASSERT(!*scrollTarget, "scroll target still temporarily activated!");
     nsIScrollableFrame* target = do_QueryFrame(
       aESM->ComputeScrollTarget(aTargetFrame, dir->deltaX, dir->deltaY, aEvent,
               EventStateManager::COMPUTE_DEFAULT_ACTION_TARGET));
     nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(target);
     if (scrollbarMediator) {
       nsIFrame* targetFrame = do_QueryFrame(target);
       *scrollTarget = targetFrame;
@@ -533,17 +533,17 @@ ScrollbarsForWheel::TemporarilyActivateA
     }
   }
 }
 
 /* static */ void
 ScrollbarsForWheel::DeactivateAllTemporarilyActivatedScrollTargets()
 {
   for (size_t i = 0; i < kNumberOfTargets; i++) {
-    nsWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
+    AutoWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
     if (*scrollTarget) {
       nsIScrollbarMediator* scrollbarMediator = do_QueryFrame(*scrollTarget);
       if (scrollbarMediator) {
         scrollbarMediator->ScrollbarActivityStopped();
       }
       *scrollTarget = nullptr;
     }
   }
--- a/dom/events/WheelHandlingHelper.h
+++ b/dom/events/WheelHandlingHelper.h
@@ -91,18 +91,18 @@ public:
   static void MayInactivate();
   static void Inactivate();
   static bool IsActive();
   static void OwnWheelTransaction(bool aOwn);
 
 protected:
   static const size_t kNumberOfTargets = 4;
   static const DeltaValues directions[kNumberOfTargets];
-  static nsWeakFrame sActiveOwner;
-  static nsWeakFrame sActivatedScrollTargets[kNumberOfTargets];
+  static AutoWeakFrame sActiveOwner;
+  static AutoWeakFrame sActivatedScrollTargets[kNumberOfTargets];
   static bool sHadWheelStart;
   static bool sOwnWheelTransaction;
 
 
   /**
    * These two methods are called upon eWheelOperationStart/eWheelOperationEnd
    * events to show/hide the right scrollbars.
    */
@@ -129,21 +129,21 @@ public:
   /**
    * WillHandleDefaultAction() is called before handling aWheelEvent on
    * aTargetFrame.
    *
    * @return    false if the caller cannot continue to handle the default
    *            action.  Otherwise, true.
    */ 
   static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
-                                      nsWeakFrame& aTargetWeakFrame);
+                                      AutoWeakFrame& aTargetWeakFrame);
   static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
                                       nsIFrame* aTargetFrame)
   {
-    nsWeakFrame targetWeakFrame(aTargetFrame);
+    AutoWeakFrame targetWeakFrame(aTargetFrame);
     return WillHandleDefaultAction(aWheelEvent, targetWeakFrame);
   }
   static void OnEvent(WidgetEvent* aEvent);
   static void Shutdown();
   static uint32_t GetTimeoutTime();
 
   static void OwnScrollbars(bool aOwn);
 
@@ -164,17 +164,17 @@ protected:
   static void SetTimeout();
   static uint32_t GetIgnoreMoveDelayTime();
   static int32_t GetAccelerationStart();
   static int32_t GetAccelerationFactor();
   static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent);
   static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
   static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
 
-  static nsWeakFrame sTargetFrame;
+  static AutoWeakFrame sTargetFrame;
   static uint32_t sTime; // in milliseconds
   static uint32_t sMouseMoved; // in milliseconds
   static nsITimer* sTimer;
   static int32_t sScrollSeriesCounter;
   static bool sOwnScrollbars;
 };
 
 } // namespace mozilla
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4114,17 +4114,17 @@ HTMLInputElement::PreHandleEvent(EventCh
     MOZ_ASSERT(aVisitor.mEvent->mMessage == eEditorInput);
     MOZ_ASSERT(numberControlFrame);
     MOZ_ASSERT(numberControlFrame->GetAnonTextControl() ==
                aVisitor.mEvent->mOriginalTarget);
     // Propogate the anon text control's new value to our HTMLInputElement:
     nsAutoString value;
     numberControlFrame->GetValueOfAnonTextControl(value);
     numberControlFrame->HandlingInputEvent(true);
-    nsWeakFrame weakNumberControlFrame(numberControlFrame);
+    AutoWeakFrame weakNumberControlFrame(numberControlFrame);
     rv = SetValueInternal(value,
                           nsTextEditorState::eSetValue_BySetUserInput |
                           nsTextEditorState::eSetValue_Notify);
     NS_ENSURE_SUCCESS(rv, rv);
     if (weakNumberControlFrame.IsAlive()) {
       numberControlFrame->HandlingInputEvent(false);
     }
   }
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -273,17 +273,17 @@ HTMLSelectElement::InsertOptionsIntoList
       mSelectedIndex += (insertIndex - aListIndex);
       SetSelectionChanged(true, aNotify);
     }
 
     // Get the frame stuff for notification. No need to flush here
     // since if there's no frame for the select yet the select will
     // get into the right state once it's created.
     nsISelectControlFrame* selectFrame = nullptr;
-    nsWeakFrame weakSelectFrame;
+    AutoWeakFrame weakSelectFrame;
     bool didGetFrame = false;
 
     // Actually select the options if the added options warrant it
     for (int32_t i = aListIndex; i < insertIndex; i++) {
       // Notify the frame that the option is added
       if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) {
         selectFrame = GetSelectFrame();
         weakSelectFrame = do_QueryFrame(selectFrame);
@@ -926,17 +926,17 @@ HTMLSelectElement::SetOptionsSelectedByI
 
   // These variables tell us whether any options were selected
   // or deselected.
   bool optionsSelected = false;
   bool optionsDeselected = false;
 
   nsISelectControlFrame* selectFrame = nullptr;
   bool didGetFrame = false;
-  nsWeakFrame weakSelectFrame;
+  AutoWeakFrame weakSelectFrame;
 
   if (aOptionsMask & IS_SELECTED) {
     // Setting selectedIndex to an out-of-bounds index means -1. (HTML5)
     if (aStartIndex < 0 || AssertedCast<uint32_t>(aStartIndex) >= numItems ||
         aEndIndex < 0 || AssertedCast<uint32_t>(aEndIndex) >= numItems) {
       aStartIndex = -1;
       aEndIndex = -1;
     }
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -116,17 +116,17 @@ public:
     }
 
     if (mTextEditorState) {
       mTextEditorState->FinishedRestoringSelection();
     }
     return NS_OK;
   }
 
-  // Let the text editor tell us we're no longer relevant - avoids use of nsWeakFrame
+  // Let the text editor tell us we're no longer relevant - avoids use of AutoWeakFrame
   void Revoke() {
     mFrame = nullptr;
     mTextEditorState = nullptr;
   }
 
 private:
   nsTextControlFrame* mFrame;
   nsTextEditorState* mTextEditorState;
@@ -785,17 +785,17 @@ NS_IMPL_ISUPPORTS(nsTextInputListener,
                   nsIDOMEventListener)
 
 // BEGIN nsIDOMSelectionListener
 
 NS_IMETHODIMP
 nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, int16_t aReason)
 {
   bool collapsed;
-  nsWeakFrame weakFrame = mFrame;
+  AutoWeakFrame weakFrame = mFrame;
 
   if (!aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed)))
     return NS_OK;
 
   // Fire the select event
   // The specs don't exactly say when we should fire the select event.
   // IE: Whenever you add/remove a character to/from the selection. Also
   //     each time for select all. Also if you get to the end of the text 
@@ -936,17 +936,17 @@ NS_IMETHODIMP
 nsTextInputListener::EditAction()
 {
   if (!mFrame) {
     // We've been disconnected from the nsTextEditorState object, nothing to do
     // here.
     return NS_OK;
   }
 
-  nsWeakFrame weakFrame = mFrame;
+  AutoWeakFrame weakFrame = mFrame;
 
   nsITextControlFrame* frameBase = do_QueryFrame(mFrame);
   nsTextControlFrame* frame = static_cast<nsTextControlFrame*> (frameBase);
   NS_ASSERTION(frame, "Where is our frame?");
   //
   // Update the undo / redo menus
   //
   nsCOMPtr<nsIEditor> editor;
@@ -2121,17 +2121,17 @@ nsTextEditorState::SetValue(const nsAStr
       NS_ASSERTION(mEditorInitialized || mInitializing,
                    "We should never try to use the editor if we're not initialized unless we're being initialized");
     }
 #endif
 
     nsAutoString currentValue;
     mBoundFrame->GetText(currentValue);
 
-    nsWeakFrame weakFrame(mBoundFrame);
+    AutoWeakFrame weakFrame(mBoundFrame);
 
     // this is necessary to avoid infinite recursion
     if (!currentValue.Equals(newValue))
     {
       ValueSetter valueSetter(mEditor);
 
       // \r is an illegal character in the dom, but people use them,
       // so convert windows and mac platform linebreaks to \n:
--- a/dom/html/test/browser_bug1108547.js
+++ b/dom/html/test/browser_bug1108547.js
@@ -100,11 +100,14 @@ function runPass(getterFile, finishedCal
   function onNewTabLoaded2(result) {
     // Now, ensure that the normal tab doesn't have access to the cookie set in private mode.
     is(result, "", "Shouldn't have access to the cookies");
 
     // Remove both of the tabs opened here.
     gBrowser.removeCurrentTab();
     gBrowser.removeCurrentTab();
 
+    privateWin = null;
+    testBrowser = null;
+
     finishedCallback();
   }
 }
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -918,17 +918,16 @@ mozilla::ipc::IPCResult
 TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                          PDocAccessibleParent* aParentDoc,
                                          const uint64_t& aParentID,
                                          const uint32_t& aMsaaID,
                                          const IAccessibleHolder& aDocCOMProxy)
 {
 #ifdef ACCESSIBILITY
   auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
-  doc->AddToMap();
 
   // If this tab is already shutting down just mark the new actor as shutdown
   // and ignore it.  When the tab actor is destroyed it will be too.
   if (mIsDestroyed) {
     doc->MarkAsShutdown();
     return IPC_OK();
   }
 
--- a/intl/lwbrk/nsJISx4051LineBreaker.cpp
+++ b/intl/lwbrk/nsJISx4051LineBreaker.cpp
@@ -7,16 +7,18 @@
 
 #include "nsJISx4051LineBreaker.h"
 
 #include "jisx4051class.h"
 #include "nsComplexBreaker.h"
 #include "nsTArray.h"
 #include "nsUnicodeProperties.h"
 
+using namespace mozilla::unicode;
+
 /* 
 
    Simplification of Pair Table in JIS X 4051
 
    1. The Origion Table - in 4.1.3
 
    In JIS x 4051. The pair table is defined as below
 
@@ -374,22 +376,23 @@ GETCLASSFROMTABLE(const uint32_t* t, uin
 
 static inline int
 IS_HALFWIDTH_IN_JISx4051_CLASS3(char16_t u)
 {
   return ((0xff66 <= (u)) && ((u) <= 0xff70));
 }
 
 static inline int
-IS_CJK_CHAR(char16_t u)
+IS_CJK_CHAR(char32_t u)
 {
   return ((0x1100 <= (u) && (u) <= 0x11ff) ||
           (0x2e80 <= (u) && (u) <= 0xd7ff) ||
           (0xf900 <= (u) && (u) <= 0xfaff) ||
-          (0xff00 <= (u) && (u) <= 0xffef) );
+          (0xff00 <= (u) && (u) <= 0xffef) ||
+          (0x20000 <= (u) && (u) <= 0x2fffd));
 }
 
 static inline bool
 IS_NONBREAKABLE_SPACE(char16_t u)
 {
   return u == 0x00A0 || u == 0x2007; // NO-BREAK SPACE, FIGURE SPACE
 }
 
@@ -588,75 +591,116 @@ nsJISx4051LineBreaker::nsJISx4051LineBre
 nsJISx4051LineBreaker::~nsJISx4051LineBreaker()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsJISx4051LineBreaker, nsILineBreaker)
 
 class ContextState {
 public:
-  ContextState(const char16_t* aText, uint32_t aLength) {
-    mUniText = aText;
-    mText = nullptr;
-    mLength = aLength;
+  ContextState(const char16_t* aText, uint32_t aLength)
+    : mUniText(aText)
+    , mText(nullptr)
+    , mLength(aLength)
+  {
+    Init();
+  }
+
+  ContextState(const uint8_t* aText, uint32_t aLength)
+    : mUniText(nullptr)
+    , mText(aText)
+    , mLength(aLength)
+  {
     Init();
   }
 
-  ContextState(const uint8_t* aText, uint32_t aLength) {
-    mUniText = nullptr;
-    mText = aText;
-    mLength = aLength;
-    Init();
+  uint32_t Length() const { return mLength; }
+  uint32_t Index() const { return mIndex; }
+
+  // This gets a single code unit of the text, without checking for surrogates
+  // (in the case of a 16-bit text buffer). That's OK if we're only checking for
+  // specific characters that are known to be BMP values.
+  char16_t GetCodeUnitAt(uint32_t aIndex) const {
+    MOZ_ASSERT(aIndex < mLength, "Out of range!");
+    return mUniText ? mUniText[aIndex] : char16_t(mText[aIndex]);
   }
 
-  uint32_t Length() { return mLength; }
-  uint32_t Index() { return mIndex; }
-
-  char16_t GetCharAt(uint32_t aIndex) {
-    NS_ASSERTION(aIndex < mLength, "Out of range!");
-    return mUniText ? mUniText[aIndex] : char16_t(mText[aIndex]);
+  // This gets a 32-bit Unicode character (codepoint), handling surrogate pairs
+  // as necessary. It must ONLY be called for 16-bit text, not 8-bit.
+  char32_t GetUnicodeCharAt(uint32_t aIndex) const {
+    MOZ_ASSERT(mUniText, "Only for 16-bit text!");
+    MOZ_ASSERT(aIndex < mLength, "Out of range!");
+    char32_t c = mUniText[aIndex];
+    if (NS_IS_HIGH_SURROGATE(c) && aIndex + 1 < mLength &&
+        NS_IS_LOW_SURROGATE(mUniText[aIndex + 1])) {
+      c = SURROGATE_TO_UCS4(c, mUniText[aIndex + 1]);
+    }
+    return c;
   }
 
   void AdvanceIndex() {
     ++mIndex;
   }
 
   void NotifyBreakBefore() { mLastBreakIndex = mIndex; }
 
 // A word of western language should not be broken. But even if the word has
 // only ASCII characters, non-natural context words should be broken, e.g.,
 // URL and file path. For protecting the natural words, we should use
 // conservative breaking rules at following conditions:
 //   1. at near the start of word
 //   2. at near the end of word
 //   3. at near the latest broken point
-// CONSERVATIVE_BREAK_RANGE define the 'near' in characters.
-#define CONSERVATIVE_BREAK_RANGE 6
+// CONSERVATIVE_RANGE_{LETTER,OTHER} define the 'near' in characters,
+// which varies depending whether we are looking at a letter or a non-letter
+// character: for non-letters, we use an extended "conservative" range.
 
-  bool UseConservativeBreaking(uint32_t aOffset = 0) {
+#define CONSERVATIVE_RANGE_LETTER 2
+#define CONSERVATIVE_RANGE_OTHER  6
+
+  bool UseConservativeBreaking(uint32_t aOffset = 0) const {
     if (mHasCJKChar)
       return false;
     uint32_t index = mIndex + aOffset;
-    bool result = (index < CONSERVATIVE_BREAK_RANGE ||
-                     mLength - index < CONSERVATIVE_BREAK_RANGE ||
-                     index - mLastBreakIndex < CONSERVATIVE_BREAK_RANGE);
+
+    // If the character at index is a letter (rather than various punctuation
+    // characters, etc) then we want a shorter "conservative" range
+    uint32_t conservativeRangeStart, conservativeRangeEnd;
+    if (index < mLength &&
+        nsIUGenCategory::kLetter ==
+          (mText ? GetGenCategory(mText[index])
+                 : GetGenCategory(GetUnicodeCharAt(index)))) {
+      // Primarily for hyphenated word prefixes/suffixes; we add 1 to Start
+      // to get more balanced behavior (if we break off a 2-letter prefix,
+      // that means the break will actually be three letters from start of
+      // word, to include the hyphen; whereas a 2-letter suffix will be
+      // broken only two letters from end of word).
+      conservativeRangeEnd = CONSERVATIVE_RANGE_LETTER;
+      conservativeRangeStart = CONSERVATIVE_RANGE_LETTER + 1;
+    } else {
+      conservativeRangeEnd = conservativeRangeStart = CONSERVATIVE_RANGE_OTHER;
+    }
+
+    bool result = (index < conservativeRangeStart ||
+                     mLength - index < conservativeRangeEnd ||
+                     index - mLastBreakIndex < conservativeRangeStart);
     if (result || !mHasNonbreakableSpace)
       return result;
 
     // This text has no-breakable space, we need to check whether the index
     // is near it.
 
-    // Note that index is always larger than CONSERVATIVE_BREAK_RANGE here.
-    for (uint32_t i = index; index - CONSERVATIVE_BREAK_RANGE < i; --i) {
-      if (IS_NONBREAKABLE_SPACE(GetCharAt(i - 1)))
+    // Note that index is always larger than conservativeRange here.
+    for (uint32_t i = index; index - conservativeRangeStart < i; --i) {
+      if (IS_NONBREAKABLE_SPACE(GetCodeUnitAt(i - 1)))
         return true;
     }
-    // Note that index is always less than mLength - CONSERVATIVE_BREAK_RANGE.
-    for (uint32_t i = index + 1; i < index + CONSERVATIVE_BREAK_RANGE; ++i) {
-      if (IS_NONBREAKABLE_SPACE(GetCharAt(i)))
+    // Note that index is always less than mLength - conservativeRange.
+    for (uint32_t i = index + 1; i < index + conservativeRangeEnd; ++i) {
+      if (IS_NONBREAKABLE_SPACE(GetCodeUnitAt(i)))
         return true;
     }
     return false;
   }
 
   bool HasPreviousEqualsSign() const {
     return mHasPreviousEqualsSign;
   }
@@ -685,49 +729,70 @@ public:
     mPreviousNonHyphenCharacter = ch;
   }
 
 private:
   void Init() {
     mIndex = 0;
     mLastBreakIndex = 0;
     mPreviousNonHyphenCharacter = U_NULL;
-    mHasCJKChar = 0;
-    mHasNonbreakableSpace = 0;
+    mHasCJKChar = false;
+    mHasNonbreakableSpace = false;
     mHasPreviousEqualsSign = false;
     mHasPreviousSlash = false;
     mHasPreviousBackslash = false;
 
-    for (uint32_t i = 0; i < mLength; ++i) {
-      char16_t u = GetCharAt(i);
-      if (!mHasNonbreakableSpace && IS_NONBREAKABLE_SPACE(u))
-        mHasNonbreakableSpace = 1;
-      else if (mUniText && !mHasCJKChar && IS_CJK_CHAR(u))
-        mHasCJKChar = 1;
+    if (mText) {
+      // 8-bit text: we only need to check for &nbsp;
+      for (uint32_t i = 0; i < mLength; ++i) {
+        if (IS_NONBREAKABLE_SPACE(mText[i])) {
+          mHasNonbreakableSpace = true;
+          break;
+        }
+      }
+    } else {
+      // 16-bit text: handle surrogates and check for CJK as well as &nbsp;
+      for (uint32_t i = 0; i < mLength; ++i) {
+        char32_t u = GetUnicodeCharAt(i);
+        if (!mHasNonbreakableSpace && IS_NONBREAKABLE_SPACE(u)) {
+          mHasNonbreakableSpace = true;
+          if (mHasCJKChar) {
+            break;
+          }
+        } else if (!mHasCJKChar && IS_CJK_CHAR(u)) {
+          mHasCJKChar = 1;
+          if (mHasNonbreakableSpace) {
+            break;
+          }
+        }
+        if (u > 0xFFFFu) {
+          ++i; // step over trailing low surrogate
+        }
+      }
     }
   }
 
-  const char16_t* mUniText;
-  const uint8_t* mText;
+  const char16_t* const mUniText;
+  const uint8_t* const mText;
 
   uint32_t mIndex;
-  uint32_t mLength;         // length of text
+  const uint32_t mLength;         // length of text
   uint32_t mLastBreakIndex;
-  uint32_t mPreviousNonHyphenCharacter; // The last character we have seen
+  char32_t mPreviousNonHyphenCharacter; // The last character we have seen
                                          // which is not U_HYPHEN
   bool mHasCJKChar; // if the text has CJK character, this is true.
   bool mHasNonbreakableSpace; // if the text has no-breakable space,
                                      // this is true.
   bool mHasPreviousEqualsSign; // True if we have seen a U_EQUAL
   bool mHasPreviousSlash;      // True if we have seen a U_SLASH
   bool mHasPreviousBackslash;  // True if we have seen a U_BACKSLASH
 };
 
 static int8_t
-ContextualAnalysis(char16_t prev, char16_t cur, char16_t next,
+ContextualAnalysis(char32_t prev, char32_t cur, char32_t next,
                    ContextState &aState)
 {
   // Don't return CLASS_OPEN/CLASS_CLOSE if aState.UseJISX4051 is FALSE.
 
   if (IS_HYPHEN(cur)) {
     // If next character is hyphen, we don't need to break between them.
     if (IS_HYPHEN(next))
       return CLASS_CHARACTER;
@@ -735,17 +800,17 @@ ContextualAnalysis(char16_t prev, char16
     // So, we should not break here.
     bool prevIsNum = IS_ASCII_DIGIT(prev);
     bool nextIsNum = IS_ASCII_DIGIT(next);
     if (prevIsNum && nextIsNum)
       return CLASS_NUMERIC;
     // If one side is numeric and the other is a character, or if both sides are
     // characters, the hyphen should be breakable.
     if (!aState.UseConservativeBreaking(1)) {
-      char16_t prevOfHyphen = aState.GetPreviousNonHyphenCharacter();
+      char32_t prevOfHyphen = aState.GetPreviousNonHyphenCharacter();
       if (prevOfHyphen && next) {
         int8_t prevClass = GetClass(prevOfHyphen);
         int8_t nextClass = GetClass(next);
         bool prevIsNumOrCharOrClose =
           prevIsNum ||
           (prevClass == CLASS_CHARACTER &&
             !NEED_CONTEXTUAL_ANALYSIS(prevOfHyphen)) ||
           prevClass == CLASS_CLOSE ||
@@ -782,20 +847,20 @@ ContextualAnalysis(char16_t prev, char16
       }
 
       if (shouldReturn)
         return CLASS_OPEN;
     } else if (cur == U_PERCENT) {
       // If this is a part of the param of URL, we should break before.
       if (!aState.UseConservativeBreaking()) {
         if (aState.Index() >= 3 &&
-            aState.GetCharAt(aState.Index() - 3) == U_PERCENT)
+            aState.GetCodeUnitAt(aState.Index() - 3) == U_PERCENT)
           return CLASS_OPEN;
         if (aState.Index() + 3 < aState.Length() &&
-            aState.GetCharAt(aState.Index() + 3) == U_PERCENT)
+            aState.GetCodeUnitAt(aState.Index() + 3) == U_PERCENT)
           return CLASS_OPEN;
       }
     } else if (cur == U_AMPERSAND || cur == U_SEMICOLON) {
       // If this may be a separator of params of URL, we should break after.
       if (!aState.UseConservativeBreaking(1) &&
           aState.HasPreviousEqualsSign())
         return CLASS_CLOSE;
     } else if (cur == U_OPEN_SINGLE_QUOTE ||
@@ -884,29 +949,39 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
                                          uint8_t aWordBreak,
                                          uint8_t* aBreakBefore)
 {
   uint32_t cur;
   int8_t lastClass = CLASS_NONE;
   ContextState state(aChars, aLength);
 
   for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) {
-    uint32_t ch = aChars[cur];
-    if (NS_IS_HIGH_SURROGATE(ch)) {
-      if (cur + 1 < aLength && NS_IS_LOW_SURROGATE(aChars[cur + 1])) {
-        ch = SURROGATE_TO_UCS4(ch, aChars[cur + 1]);
-      }
-    }
+    char32_t ch = state.GetUnicodeCharAt(cur);
+    uint32_t chLen = ch > 0xFFFFu ? 2 : 1;
     int8_t cl;
 
     if (NEED_CONTEXTUAL_ANALYSIS(ch)) {
-      cl = ContextualAnalysis(cur > 0 ? aChars[cur - 1] : U_NULL,
-                              ch,
-                              cur + 1 < aLength ? aChars[cur + 1] : U_NULL,
-                              state);
+      char32_t prev, next;
+      if (cur > 0) {
+        // not using state.GetUnicodeCharAt() here because we're looking back
+        // rather than forward for possible surrogates
+        prev = aChars[cur - 1];
+        if (NS_IS_LOW_SURROGATE(prev) && cur > 1 &&
+            NS_IS_HIGH_SURROGATE(aChars[cur - 2])) {
+          prev = SURROGATE_TO_UCS4(aChars[cur - 2], prev);
+        }
+      } else {
+        prev = 0;
+      }
+      if (cur + chLen < aLength) {
+        next = state.GetUnicodeCharAt(cur + chLen);
+      } else {
+        next = 0;
+      }
+      cl = ContextualAnalysis(prev, ch, next, state);
     } else {
       if (ch == U_EQUAL)
         state.NotifySeenEqualsSign();
       state.NotifyNonHyphenCharacter(ch);
       cl = GetClass(ch);
     }
 
     bool allowBreak = false;
@@ -920,20 +995,27 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
         allowBreak = true;
       }
     }
     aBreakBefore[cur] = allowBreak;
     if (allowBreak)
       state.NotifyBreakBefore();
     lastClass = cl;
     if (CLASS_COMPLEX == cl) {
-      uint32_t end = cur + 1;
+      uint32_t end = cur + chLen;
 
-      while (end < aLength && CLASS_COMPLEX == GetClass(aChars[end])) {
+      while (end < aLength) {
+        char32_t c = state.GetUnicodeCharAt(end);
+        if (CLASS_COMPLEX != GetClass(c)) {
+          break;
+        }
         ++end;
+        if (c > 0xFFFFU) { // it was a surrogate pair
+          ++end;
+        }
       }
 
       NS_GetComplexLineBreaks(aChars + cur, end - cur, aBreakBefore + cur);
 
       // We have to consider word-break value again for complex characters
       if (aWordBreak != nsILineBreaker::kWordBreak_Normal) {
         // Respect word-break property 
         for (uint32_t i = cur; i < end; i++)
@@ -942,17 +1024,17 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
 
       // restore breakability at chunk begin, which was always set to false
       // by the complex line breaker
       aBreakBefore[cur] = allowBreak;
 
       cur = end - 1;
     }
 
-    if (ch > 0xffff) {
+    if (chLen == 2) {
       // Supplementary-plane character: mark that we cannot break before the
       // trailing low surrogate, and advance past it.
       ++cur;
       aBreakBefore[cur] = false;
       state.AdvanceIndex();
     }
   }
 }
@@ -962,17 +1044,17 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
                                          uint8_t aWordBreak,
                                          uint8_t* aBreakBefore)
 {
   uint32_t cur;
   int8_t lastClass = CLASS_NONE;
   ContextState state(aChars, aLength);
 
   for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) {
-    char16_t ch = aChars[cur];
+    char32_t ch = aChars[cur];
     int8_t cl;
 
     if (NEED_CONTEXTUAL_ANALYSIS(ch)) {
       cl = ContextualAnalysis(cur > 0 ? aChars[cur - 1] : U_NULL,
                               ch,
                               cur + 1 < aLength ? aChars[cur + 1] : U_NULL,
                               state);
     } else {
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -583,17 +583,17 @@ AccessibleCaretManager::SelectWordOrShor
   }
 
   nsIFrame* rootFrame = mPresShell->GetRootFrame();
   if (!rootFrame) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Find the frame under point.
-  nsWeakFrame ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, aPoint,
+  AutoWeakFrame ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, aPoint,
     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
   if (!ptFrame.IsAlive()) {
     return NS_ERROR_FAILURE;
   }
 
   nsIFrame* focusableFrame = GetFocusableFrame(ptFrame);
 
 #ifdef DEBUG_FRAME_DUMP
@@ -1255,17 +1255,17 @@ AccessibleCaretManager::DragCaretInterna
 
   ClearMaintainedSelection();
 
   nsIFrame* anchorFrame = nullptr;
   selection->GetPrimaryFrameForAnchorNode(&anchorFrame);
 
   nsIFrame* scrollable =
     nsLayoutUtils::GetClosestFrameOfType(anchorFrame, nsGkAtoms::scrollFrame);
-  nsWeakFrame weakScrollable = scrollable;
+  AutoWeakFrame weakScrollable = scrollable;
   fs->HandleClick(offsets.content, offsets.StartOffset(), offsets.EndOffset(),
                   GetCaretMode() == CaretMode::Selection, false,
                   offsets.associate);
   if (!weakScrollable.IsAlive()) {
     return NS_OK;
   }
 
   // Scroll scrolled frame.
--- a/layout/base/GeometryUtils.cpp
+++ b/layout/base/GeometryUtils.cpp
@@ -258,17 +258,17 @@ void GetBoxQuads(nsINode* aNode,
                  CallerType aCallerType,
                  ErrorResult& aRv)
 {
   nsIFrame* frame = GetFrameForNode(aNode);
   if (!frame) {
     // No boxes to return
     return;
   }
-  nsWeakFrame weakFrame(frame);
+  AutoWeakFrame weakFrame(frame);
   nsIDocument* ownerDoc = aNode->OwnerDoc();
   nsIFrame* relativeToFrame =
     GetFirstNonAnonymousFrameForGeometryNode(aOptions.mRelativeTo, ownerDoc);
   // The first frame might be destroyed now if the above call lead to an
   // EnsureFrameForTextNode call.  We need to get the first frame again
   // when that happens and re-check it.
   if (!weakFrame.IsAlive()) {
     frame = GetFrameForNode(aNode);
@@ -296,17 +296,17 @@ void GetBoxQuads(nsINode* aNode,
 
 static void
 TransformPoints(nsINode* aTo, const GeometryNode& aFrom,
                 uint32_t aPointCount, CSSPoint* aPoints,
                 const ConvertCoordinateOptions& aOptions,
                 CallerType aCallerType, ErrorResult& aRv)
 {
   nsIFrame* fromFrame = GetFirstNonAnonymousFrameForGeometryNode(aFrom);
-  nsWeakFrame weakFrame(fromFrame);
+  AutoWeakFrame weakFrame(fromFrame);
   nsIFrame* toFrame = GetFirstNonAnonymousFrameForNode(aTo);
   // The first frame might be destroyed now if the above call lead to an
   // EnsureFrameForTextNode call.  We need to get the first frame again
   // when that happens.
   if (fromFrame && !weakFrame.IsAlive()) {
     fromFrame = GetFirstNonAnonymousFrameForGeometryNode(aFrom);
   }
   if (!fromFrame || !toFrame) {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -660,53 +660,82 @@ nsIPresShell::GetVerifyReflowEnable()
 
 void
 nsIPresShell::SetVerifyReflowEnable(bool aEnabled)
 {
   gVerifyReflowEnabled = aEnabled;
 }
 
 /* virtual */ void
-nsIPresShell::AddWeakFrameExternal(nsWeakFrame* aWeakFrame)
-{
-  AddWeakFrameInternal(aWeakFrame);
-}
-
-void
-nsIPresShell::AddWeakFrameInternal(nsWeakFrame* aWeakFrame)
+nsIPresShell::AddAutoWeakFrameExternal(AutoWeakFrame* aWeakFrame)
+{
+  AddAutoWeakFrameInternal(aWeakFrame);
+}
+
+void
+nsIPresShell::AddAutoWeakFrameInternal(AutoWeakFrame* aWeakFrame)
 {
   if (aWeakFrame->GetFrame()) {
     aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
   }
-  aWeakFrame->SetPreviousWeakFrame(mWeakFrames);
-  mWeakFrames = aWeakFrame;
+  aWeakFrame->SetPreviousWeakFrame(mAutoWeakFrames);
+  mAutoWeakFrames = aWeakFrame;
 }
 
 /* virtual */ void
-nsIPresShell::RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame)
-{
-  RemoveWeakFrameInternal(aWeakFrame);
-}
-
-void
-nsIPresShell::RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame)
-{
-  if (mWeakFrames == aWeakFrame) {
-    mWeakFrames = aWeakFrame->GetPreviousWeakFrame();
-    return;
-  }
-  nsWeakFrame* nextWeak = mWeakFrames;
+nsIPresShell::AddWeakFrameExternal(WeakFrame* aWeakFrame)
+{
+  AddWeakFrameInternal(aWeakFrame);
+}
+
+void
+nsIPresShell::AddWeakFrameInternal(WeakFrame* aWeakFrame)
+{
+  if (aWeakFrame->GetFrame()) {
+    aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
+  }
+  MOZ_ASSERT(!mWeakFrames.GetEntry(aWeakFrame));
+  mWeakFrames.PutEntry(aWeakFrame);
+}
+
+/* virtual */ void
+nsIPresShell::RemoveAutoWeakFrameExternal(AutoWeakFrame* aWeakFrame)
+{
+  RemoveAutoWeakFrameInternal(aWeakFrame);
+}
+
+void
+nsIPresShell::RemoveAutoWeakFrameInternal(AutoWeakFrame* aWeakFrame)
+{
+  if (mAutoWeakFrames == aWeakFrame) {
+    mAutoWeakFrames = aWeakFrame->GetPreviousWeakFrame();
+    return;
+  }
+  AutoWeakFrame* nextWeak = mAutoWeakFrames;
   while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
     nextWeak = nextWeak->GetPreviousWeakFrame();
   }
   if (nextWeak) {
     nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
   }
 }
 
+/* virtual */ void
+nsIPresShell::RemoveWeakFrameExternal(WeakFrame* aWeakFrame)
+{
+  RemoveWeakFrameInternal(aWeakFrame);
+}
+
+void
+nsIPresShell::RemoveWeakFrameInternal(WeakFrame* aWeakFrame)
+{
+  MOZ_ASSERT(mWeakFrames.GetEntry(aWeakFrame));
+  mWeakFrames.RemoveEntry(aWeakFrame);
+}
+
 already_AddRefed<nsFrameSelection>
 nsIPresShell::FrameSelection()
 {
   RefPtr<nsFrameSelection> ret = mSelection;
   return ret.forget();
 }
 
 //----------------------------------------------------------------------
@@ -746,17 +775,17 @@ nsIPresShell::nsIPresShell()
     , mFrameManager(nullptr)
 #ifdef ACCESSIBILITY
     , mDocAccessible(nullptr)
 #endif
 #ifdef DEBUG
     , mDrawEventTargetFrame(nullptr)
 #endif
     , mPaintCount(0)
-    , mWeakFrames(nullptr)
+    , mAutoWeakFrames(nullptr)
     , mCanvasBackgroundColor(NS_RGBA(0,0,0,0))
     , mSelectionFlags(0)
     , mRenderFlags(0)
     , mStylesHaveChanged(false)
     , mDidInitialize(false)
     , mIsDestroying(false)
     , mIsReflowing(false)
     , mPaintingSuppressed(false)
@@ -1361,20 +1390,27 @@ PresShell::Destroy()
     // now dead, we shouldn't be looking up any more properties in that table.
     // We want to do this before we call DetachShell() on the prescontext, so
     // property destructors can usefully call GetPresShell() on the
     // prescontext.
     mPresContext->PropertyTable()->DeleteAll();
   }
 
 
-  NS_WARNING_ASSERTION(!mWeakFrames,
+  NS_WARNING_ASSERTION(!mAutoWeakFrames && mWeakFrames.IsEmpty(),
                        "Weak frames alive after destroying FrameManager");
-  while (mWeakFrames) {
-    mWeakFrames->Clear(this);
+  while (mAutoWeakFrames) {
+    mAutoWeakFrames->Clear(this);
+  }
+  nsTArray<WeakFrame*> toRemove(mWeakFrames.Count());
+  for (auto iter = mWeakFrames.Iter(); !iter.Done(); iter.Next()) {
+    toRemove.AppendElement(iter.Get()->GetKey());
+  }
+  for (WeakFrame* weakFrame : toRemove) {
+    weakFrame->Clear(this);
   }
 
   // Let the style set do its cleanup.
   mStyleSet->Shutdown();
 
   if (mPresContext) {
     // We hold a reference to the pres context, and it holds a weak link back
     // to us. To avoid the pres context having a dangling reference, set its
@@ -2944,25 +2980,36 @@ nsIPresShell::SetForwardingContainer(con
   mForwardingContainer = aContainer;
 }
 
 void
 PresShell::ClearFrameRefs(nsIFrame* aFrame)
 {
   mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
 
-  nsWeakFrame* weakFrame = mWeakFrames;
+  AutoWeakFrame* weakFrame = mAutoWeakFrames;
   while (weakFrame) {
-    nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
+    AutoWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
     if (weakFrame->GetFrame() == aFrame) {
-      // This removes weakFrame from mWeakFrames.
+      // This removes weakFrame from mAutoWeakFrames.
       weakFrame->Clear(this);
     }
     weakFrame = prev;
   }
+
+  AutoTArray<WeakFrame*, 4> toRemove;
+  for (auto iter = mWeakFrames.Iter(); !iter.Done(); iter.Next()) {
+    WeakFrame* weakFrame = iter.Get()->GetKey();
+    if (weakFrame->GetFrame() == aFrame) {
+      toRemove.AppendElement(weakFrame);
+    }
+  }
+  for (WeakFrame* weakFrame : toRemove) {
+    weakFrame->Clear(this);
+  }
 }
 
 already_AddRefed<gfxContext>
 PresShell::CreateReferenceRenderingContext()
 {
   nsDeviceContext* devCtx = mPresContext->DeviceContext();
   RefPtr<gfxContext> rc;
   if (mPresContext->IsScreen()) {
@@ -7128,17 +7175,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
     type = SourceEventType::Key;
   }
   AutoSourceEvent taskTracerEvent(type);
 #endif
 
   NS_ASSERTION(aFrame, "aFrame should be not null");
 
   if (sPointerEventEnabled) {
-    nsWeakFrame weakFrame(aFrame);
+    AutoWeakFrame weakFrame(aFrame);
     nsCOMPtr<nsIContent> targetContent;
     DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents,
                                     aEventStatus,
                                     getter_AddRefs(targetContent));
     if (!weakFrame.IsAlive()) {
       if (targetContent) {
         aFrame = targetContent->GetPrimaryFrame();
         if (!aFrame) {
@@ -7290,17 +7337,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
 
   if (aEvent->IsUsingCoordinates()) {
     ReleasePointerCaptureCaller releasePointerCaptureCaller;
     if (mDocument) {
       if (aEvent->mClass == eTouchEventClass) {
         nsIDocument::UnlockPointer();
       }
 
-      nsWeakFrame weakFrame(frame);
+      AutoWeakFrame weakFrame(frame);
       {  // scope for scriptBlocker.
         nsAutoScriptBlocker scriptBlocker;
         FlushThrottledStyles(GetRootPresShell()->GetDocument(), nullptr);
       }
 
 
       if (!weakFrame.IsAlive()) {
         frame = GetNearestFrameContainingPresShell(this);
@@ -7511,17 +7558,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         frame = capturingFrame;
       }
     }
 
     if (aEvent->mClass == ePointerEventClass) {
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
         // Try to keep frame for following check, because
         // frame can be damaged during CheckPointerCaptureState.
-        nsWeakFrame frameKeeper(frame);
+        AutoWeakFrame frameKeeper(frame);
         // Handle pending pointer capture before any pointer events except
         // gotpointercapture / lostpointercapture.
         CheckPointerCaptureState(pointerEvent->pointerId,
                                  pointerEvent->inputSource,
                                  pointerEvent->mIsPrimary);
         // Prevent application crashes, in case damaged frame.
         if (!frameKeeper.IsAlive()) {
           frame = nullptr;
@@ -7661,17 +7708,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
             }
           }
         }
       }
     }
 
     // Before HandlePositionedEvent we should save mPointerEventTarget in some
     // cases
-    nsWeakFrame weakFrame;
+    AutoWeakFrame weakFrame;
     if (sPointerEventEnabled && aTargetContent &&
         ePointerEventClass == aEvent->mClass) {
       weakFrame = frame;
       shell->mPointerEventTarget = frame->GetContent();
       MOZ_ASSERT(!frame->GetContent() ||
                  shell->GetDocument() == frame->GetContent()->OwnerDoc());
     }
 
@@ -9522,17 +9569,17 @@ PresShell::Observe(nsISupports* aSubject
 #ifdef MOZ_XUL
   if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
     nsIFrame *rootFrame = mFrameConstructor->GetRootFrame();
     // Need to null-check because "chrome-flush-skin-caches" can happen
     // at interesting times during startup.
     if (rootFrame) {
       NS_ASSERTION(mViewManager, "View manager must exist");
 
-      nsWeakFrame weakRoot(rootFrame);
+      AutoWeakFrame weakRoot(rootFrame);
       // Have to make sure that the content notifications are flushed before we
       // start messing with the frame model; otherwise we can get content doubling.
       mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
 
       if (weakRoot.IsAlive()) {
         WalkFramesThroughPlaceholders(mPresContext, rootFrame,
                                       &ReResolveMenusAndTrees, nullptr);
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -73,17 +73,18 @@ class CSSStyleSheet;
 class nsFrameSelection;
 class nsFrameManager;
 class nsILayoutHistoryState;
 class nsIReflowCallback;
 class nsIDOMNode;
 class nsCSSFrameConstructor;
 class nsISelection;
 template<class E> class nsCOMArray;
-class nsWeakFrame;
+class AutoWeakFrame;
+class WeakFrame;
 class nsIScrollableFrame;
 class gfxContext;
 class nsIDOMEvent;
 class nsDisplayList;
 class nsDisplayListBuilder;
 class nsPIDOMWindowOuter;
 struct nsPoint;
 class nsINode;
@@ -1187,32 +1188,52 @@ public:
    * frames.
    */
   virtual already_AddRefed<mozilla::gfx::SourceSurface>
   RenderSelection(nsISelection* aSelection,
                   const mozilla::LayoutDeviceIntPoint aPoint,
                   mozilla::LayoutDeviceIntRect* aScreenRect,
                   uint32_t aFlags) = 0;
 
-  void AddWeakFrameInternal(nsWeakFrame* aWeakFrame);
-  virtual void AddWeakFrameExternal(nsWeakFrame* aWeakFrame);
+  void AddAutoWeakFrameInternal(AutoWeakFrame* aWeakFrame);
+  virtual void AddAutoWeakFrameExternal(AutoWeakFrame* aWeakFrame);
+  void AddWeakFrameInternal(WeakFrame* aWeakFrame);
+  virtual void AddWeakFrameExternal(WeakFrame* aWeakFrame);
 
-  void AddWeakFrame(nsWeakFrame* aWeakFrame)
+  void AddAutoWeakFrame(AutoWeakFrame* aWeakFrame)
+  {
+#ifdef MOZILLA_INTERNAL_API
+    AddAutoWeakFrameInternal(aWeakFrame);
+#else
+    AddAutoWeakFrameExternal(aWeakFrame);
+#endif
+  }
+  void AddWeakFrame(WeakFrame* aWeakFrame)
   {
 #ifdef MOZILLA_INTERNAL_API
     AddWeakFrameInternal(aWeakFrame);
 #else
     AddWeakFrameExternal(aWeakFrame);
 #endif
   }
 
-  void RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame);
-  virtual void RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame);
+  void RemoveAutoWeakFrameInternal(AutoWeakFrame* aWeakFrame);
+  virtual void RemoveAutoWeakFrameExternal(AutoWeakFrame* aWeakFrame);
+  void RemoveWeakFrameInternal(WeakFrame* aWeakFrame);
+  virtual void RemoveWeakFrameExternal(WeakFrame* aWeakFrame);
 
-  void RemoveWeakFrame(nsWeakFrame* aWeakFrame)
+  void RemoveAutoWeakFrame(AutoWeakFrame* aWeakFrame)
+  {
+#ifdef MOZILLA_INTERNAL_API
+    RemoveAutoWeakFrameInternal(aWeakFrame);
+#else
+    RemoveAutoWeakFrameExternal(aWeakFrame);
+#endif
+  }
+  void RemoveWeakFrame(WeakFrame* aWeakFrame)
   {
 #ifdef MOZILLA_INTERNAL_API
     RemoveWeakFrameInternal(aWeakFrame);
 #else
     RemoveWeakFrameExternal(aWeakFrame);
 #endif
   }
 
@@ -1810,18 +1831,21 @@ protected:
 #endif
 
 
   // Count of the number of times this presshell has been painted to a window.
   uint64_t                  mPaintCount;
 
   nsSize                    mScrollPositionClampingScrollPortSize;
 
-  // A list of weak frames. This is a pointer to the last item in the list.
-  nsWeakFrame*              mWeakFrames;
+  // A list of stack weak frames. This is a pointer to the last item in the list.
+  AutoWeakFrame*            mAutoWeakFrames;
+
+  // A hash table of heap allocated weak frames.
+  nsTHashtable<nsPtrHashKey<WeakFrame>> mWeakFrames;
 
   // Most recent canvas background color.
   nscolor                   mCanvasBackgroundColor;
 
   // Used to force allocation and rendering of proportionally more or
   // less pixels in both dimensions.
   mozilla::Maybe<float>     mResolution;
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -3263,19 +3263,20 @@ nsRootPresContext::EnsureEventualDidPain
   for (NotifyDidPaintTimer& t : mNotifyDidPaintTimers) {
     if (t.mTransactionId == aTransactionId) {
       return;
     }
   }
 
   nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
   if (timer) {
-    nsresult rv = timer->InitWithCallback(NewTimerCallback([=](){
+    RefPtr<nsRootPresContext> self = this;
+    nsresult rv = timer->InitWithCallback(NewTimerCallback([self, aTransactionId](){
       nsAutoScriptBlocker blockScripts;
-      this->NotifyDidPaintForSubtree(aTransactionId);
+      self->NotifyDidPaintForSubtree(aTransactionId);
     }), 100, nsITimer::TYPE_ONE_SHOT);
 
     if (NS_SUCCEEDED(rv)) {
       NotifyDidPaintTimer* t = mNotifyDidPaintTimers.AppendElement();
       t->mTransactionId = aTransactionId;
       t->mTimer = timer;
     }
   }
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -263,17 +263,17 @@ nsComboboxControlFrame::AccessibleType()
 {
   return a11y::eHTMLComboboxType;
 }
 #endif
 
 void
 nsComboboxControlFrame::SetFocus(bool aOn, bool aRepaint)
 {
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   if (aOn) {
     nsListControlFrame::ComboboxFocusSet();
     sFocused = this;
     if (mDelayedShowDropDown) {
       ShowDropDown(true); // might destroy us
       if (!weakFrame.IsAlive()) {
         return;
       }
@@ -347,17 +347,17 @@ nsComboboxControlFrame::ShowList(bool aS
   } else {
     nsIWidget* widget = view->GetWidget();
     if (widget) {
       // We must do this before ShowPopup in case it destroys us (bug 813442).
       widget->CaptureRollupEvents(this, false);
     }
   }
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   ShowPopup(aShowList);  // might destroy us
   if (!weakFrame.IsAlive()) {
     return false;
   }
 
   mDroppedDown = aShowList;
   nsIWidget* widget = view->GetWidget();
   if (mDroppedDown) {
@@ -408,17 +408,17 @@ public:
   {
     if (mFrame.IsAlive()) {
       static_cast<nsComboboxControlFrame*>(mFrame.GetFrame())->
         AbsolutelyPositionDropDown();
     }
     return NS_OK;
   }
 
-  nsWeakFrame mFrame;
+  WeakFrame mFrame;
 };
 
 void
 nsComboboxControlFrame::ReflowDropdown(nsPresContext*  aPresContext,
                                        const ReflowInput& aReflowInput)
 {
   // All we want out of it later on, really, is the block size of a row, so we
   // don't even need to cache mDropdownFrame's ascent or anything.  If we don't
@@ -522,17 +522,17 @@ public:
   NS_IMETHOD Run() override
   {
     if (mFrame.IsAlive()) {
       static_cast<nsComboboxControlFrame*>(mFrame.GetFrame())
         ->RollupFromList();
     }
     return NS_OK;
   }
-  nsWeakFrame mFrame;
+  WeakFrame mFrame;
 };
 
 class nsAsyncResize : public Runnable
 {
 public:
   explicit nsAsyncResize(nsComboboxControlFrame* aFrame) : mFrame(aFrame) {}
   NS_IMETHOD Run() override
   {
@@ -551,17 +551,17 @@ public:
           SetSuppressScrollbarUpdate(false);
         if (combo->mDelayedShowDropDown) {
           combo->ShowDropDown(true);
         }
       }
     }
     return NS_OK;
   }
-  nsWeakFrame mFrame;
+  WeakFrame mFrame;
 };
 
 void
 nsComboboxControlFrame::GetAvailableDropdownSpace(WritingMode aWM,
                                                   nscoord* aBefore,
                                                   nscoord* aAfter,
                                                   LogicalPoint* aTranslation)
 {
@@ -1020,17 +1020,17 @@ void
 nsComboboxControlFrame::HandleRedisplayTextEvent()
 {
   // First, make sure that the content model is up to date and we've
   // constructed the frames for all our content in the right places.
   // Otherwise they'll end up under the wrong insertion frame when we
   // ActuallyDisplayText, since that flushes out the content sink by
   // calling SetText on a DOM node with aNotify set to true.  See bug
   // 289730.
-  nsWeakFrame weakThis(this);
+  AutoWeakFrame weakThis(this);
   PresContext()->Document()->
     FlushPendingNotifications(FlushType::ContentAndNotify);
   if (!weakThis.IsAlive())
     return;
 
   // Redirect frame insertions during this method (see GetContentInsertionFrame())
   // so that any reframing that the frame constructor forces upon us is inserted
   // into the correct parent (mDisplayFrame). See bug 282607.
@@ -1089,17 +1089,17 @@ nsComboboxControlFrame::AddOption(int32_
   nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame);
   return lcf->AddOption(aIndex);
 }
 
 
 NS_IMETHODIMP
 nsComboboxControlFrame::RemoveOption(int32_t aIndex)
 {
-  nsWeakFrame weakThis(this);
+  AutoWeakFrame weakThis(this);
   if (mListControlFrame->GetNumberOfOptions() > 0) {
     if (aIndex < mDisplayedIndex) {
       --mDisplayedIndex;
     } else if (aIndex == mDisplayedIndex) {
       mDisplayedIndex = 0; // IE6 compat
       RedisplayText(mDisplayedIndex);
     }
   }
@@ -1455,17 +1455,17 @@ bool
 nsComboboxControlFrame::Rollup(uint32_t aCount, bool aFlush,
                                const nsIntPoint* pos, nsIContent** aLastRolledUp)
 {
   if (!mDroppedDown) {
     return false;
   }
 
   bool consume = !!COMBOBOX_ROLLUP_CONSUME_EVENT;
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   mListControlFrame->AboutToRollup(); // might destroy us
   if (!weakFrame.IsAlive()) {
     return consume;
   }
   ShowDropDown(false); // might destroy us
   if (weakFrame.IsAlive()) {
     mListControlFrame->CaptureMouseEvents(false);
   }
@@ -1620,17 +1620,17 @@ nsComboboxControlFrame::OnOptionSelected
     if (selectFrame) {
       selectFrame->OnOptionSelected(aIndex, aSelected);
     }
   } else {
     if (aSelected) {
       nsAutoScriptBlocker blocker;
       RedisplayText(aIndex);
     } else {
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       RedisplaySelectedText();
       if (weakFrame.IsAlive()) {
         FireValueChangeEvent(); // Fire after old option is unselected
       }
     }
   }
 
   return NS_OK;
--- a/layout/forms/nsDateTimeControlFrame.h
+++ b/layout/forms/nsDateTimeControlFrame.h
@@ -98,17 +98,17 @@ private:
         static_cast<nsDateTimeControlFrame*>(mFrame.GetFrame());
       NS_ENSURE_STATE(frame);
 
       frame->SyncDisabledState();
       return NS_OK;
     }
 
   private:
-    nsWeakFrame mFrame;
+    WeakFrame mFrame;
   };
 
   /**
    * Sync the disabled state of the anonymous children up with our content's.
    */
   void SyncDisabledState();
 
   // Anonymous child which is bound via XBL to an element that wraps the input
--- a/layout/forms/nsFileControlFrame.h
+++ b/layout/forms/nsFileControlFrame.h
@@ -106,17 +106,17 @@ protected:
       nsFileControlFrame* frame = static_cast<nsFileControlFrame*>(mFrame.GetFrame());
       NS_ENSURE_STATE(frame);
 
       frame->SyncDisabledState();
       return NS_OK;
     }
 
   private:
-    nsWeakFrame mFrame;
+    WeakFrame mFrame;
   };
 
   class DnDListener: public MouseListener {
   public:
     explicit DnDListener(nsFileControlFrame* aFrame)
       : MouseListener(aFrame)
     {}
 
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -667,17 +667,17 @@ nsListControlFrame::SingleSelection(int3
   bool wasChanged = false;
   // Get Current selection
   if (aDoToggle) {
     wasChanged = ToggleOptionSelectedFromFrame(aClickedIndex);
   } else {
     wasChanged = SetOptionsSelectedFromFrame(aClickedIndex, aClickedIndex,
                                 true, true);
   }
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   ScrollToIndex(aClickedIndex);
   if (!weakFrame.IsAlive()) {
     return wasChanged;
   }
 
 #ifdef ACCESSIBILITY
   bool isCurrentOptionChanged = mEndSelectionIndex != aClickedIndex;
 #endif
@@ -803,17 +803,17 @@ nsListControlFrame::PerformSelection(int
         endIndex   = aClickedIndex;
       } else {
         startIndex = aClickedIndex;
         endIndex   = mStartSelectionIndex;
       }
 
       // Clear only if control was not pressed
       wasChanged = ExtendedSelection(startIndex, endIndex, !aIsControl);
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       ScrollToIndex(aClickedIndex);
       if (!weakFrame.IsAlive()) {
         return wasChanged;
       }
 
       if (mStartSelectionIndex == kNothingSelected) {
         mStartSelectionIndex = aClickedIndex;
       }
@@ -1051,17 +1051,17 @@ nsListControlFrame::ResetList(bool aAllo
 
     // Scroll to the selected index
     int32_t indexToSelect = kNothingSelected;
 
     nsCOMPtr<nsIDOMHTMLSelectElement> selectElement(do_QueryInterface(mContent));
     NS_ASSERTION(selectElement, "No select element!");
     if (selectElement) {
       selectElement->GetSelectedIndex(&indexToSelect);
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       ScrollToIndex(indexToSelect);
       if (!weakFrame.IsAlive()) {
         return;
       }
     }
   }
 
   mStartSelectionIndex = kNothingSelected;
@@ -1324,17 +1324,17 @@ nsListControlFrame::ToggleOptionSelected
 
 
 // Dispatch event and such
 bool
 nsListControlFrame::UpdateSelection()
 {
   if (mIsAllFramesHere) {
     // if it's a combobox, display the new text
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     if (mComboboxFrame) {
       mComboboxFrame->RedisplaySelectedText();
 
       // When dropdown list is open, onchange event will be fired when Enter key
       // is hit or when dropdown list is dismissed.
       if (mComboboxFrame->IsDroppedDown()) {
         return weakFrame.IsAlive();
       }
@@ -1352,17 +1352,17 @@ nsListControlFrame::ComboboxFinish(int32
 {
   gLastKeyTime = 0;
 
   if (mComboboxFrame) {
     int32_t displayIndex = mComboboxFrame->GetIndexOfDisplayArea();
     // Make sure we can always reset to the displayed index
     mForceSelection = displayIndex == aIndex;
 
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     PerformSelection(aIndex, false, false);  // might destroy us
     if (!weakFrame.IsAlive() || !mComboboxFrame) {
       return;
     }
 
     if (displayIndex != aIndex) {
       mComboboxFrame->RedisplaySelectedText(); // might destroy us
     }
@@ -1405,17 +1405,17 @@ NS_IMETHODIMP
 nsListControlFrame::OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex)
 {
   if (mComboboxFrame) {
     // UpdateRecentIndex with NS_SKIP_NOTIFY_INDEX, so that we won't fire an onchange
     // event for this setting of selectedIndex.
     mComboboxFrame->UpdateRecentIndex(NS_SKIP_NOTIFY_INDEX);
   }
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   ScrollToIndex(aNewIndex);
   if (!weakFrame.IsAlive()) {
     return NS_OK;
   }
   mStartSelectionIndex = aNewIndex;
   mEndSelectionIndex = aNewIndex;
   InvalidateFocus();
 
@@ -1471,17 +1471,17 @@ nsListControlFrame::AboutToDropDown()
                        mLastDropdownBackstopColor);
     context = context->GetParent();
   }
   mLastDropdownBackstopColor =
     NS_ComposeColors(PresContext()->DefaultBackgroundColor(),
                      mLastDropdownBackstopColor);
 
   if (mIsAllContentHere && mIsAllFramesHere && mHasBeenInitialized) {
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     ScrollToIndex(GetSelectedIndex());
     if (!weakFrame.IsAlive()) {
       return;
     }
 #ifdef ACCESSIBILITY
     FireMenuItemActiveEvent(); // Inform assistive tech what got focus
 #endif
   }
@@ -1681,17 +1681,17 @@ nsListControlFrame::MouseUp(nsIDOMEvent*
       if (isDisabled) {
         aMouseEvent->PreventDefault();
         aMouseEvent->StopPropagation();
         CaptureMouseEvents(false);
         return NS_ERROR_FAILURE;
       }
 
       if (kNothingSelected != selectedIndex) {
-        nsWeakFrame weakFrame(this);
+        AutoWeakFrame weakFrame(this);
         ComboboxFinish(selectedIndex);
         if (!weakFrame.IsAlive()) {
           return NS_OK;
         }
 
         FireOnInputAndOnChange();
       }
 
@@ -1842,17 +1842,17 @@ nsListControlFrame::MouseDown(nsIDOMEven
     }
   }
 
   int32_t selectedIndex;
   if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, selectedIndex))) {
     // Handle Like List
     mButtonDown = true;
     CaptureMouseEvents(true);
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     bool change =
       HandleListSelection(aMouseEvent, selectedIndex); // might destroy us
     if (!weakFrame.IsAlive()) {
       return NS_OK;
     }
     mChangesSinceDragStart = change;
   } else {
     // NOTE: the combo box is responsible for dropping it down
@@ -1881,17 +1881,17 @@ nsListControlFrame::MouseDown(nsIDOMEven
       if (!IgnoreMouseEventForSelection(aMouseEvent)) {
         return NS_OK;
       }
 
       if (!nsComboboxControlFrame::ToolkitHasNativePopup())
       {
         bool isDroppedDown = mComboboxFrame->IsDroppedDown();
         nsIFrame* comboFrame = do_QueryFrame(mComboboxFrame);
-        nsWeakFrame weakFrame(comboFrame);
+        AutoWeakFrame weakFrame(comboFrame);
         mComboboxFrame->ShowDropDown(!isDroppedDown);
         if (!weakFrame.IsAlive())
           return NS_OK;
         if (isDroppedDown) {
           CaptureMouseEvents(false);
         }
       }
     }
@@ -1944,17 +1944,17 @@ nsListControlFrame::DragMove(nsIDOMEvent
       nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
       NS_ASSERTION(mouseEvent, "aMouseEvent is not an nsIDOMMouseEvent!");
       bool isControl;
 #ifdef XP_MACOSX
       mouseEvent->GetMetaKey(&isControl);
 #else
       mouseEvent->GetCtrlKey(&isControl);
 #endif
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       // Turn SHIFT on when you are dragging, unless control is on.
       bool wasChanged = PerformSelection(selectedIndex,
                                            !isControl, isControl);
       if (!weakFrame.IsAlive()) {
         return NS_OK;
       }
       mChangesSinceDragStart = mChangesSinceDragStart || wasChanged;
     }
@@ -2116,17 +2116,17 @@ nsListControlFrame::DropDownToggleKey(ns
   // dropdowns there.
   if (IsInDropDownMode() && !nsComboboxControlFrame::ToolkitHasNativePopup()) {
     aKeyEvent->PreventDefault();
     if (!mComboboxFrame->IsDroppedDown()) {
       if (!FireShowDropDownEvent(mContent, true, false)) {
         mComboboxFrame->ShowDropDown(true);
       }
     } else {
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       // mEndSelectionIndex is the last item that got selected.
       ComboboxFinish(mEndSelectionIndex);
       if (weakFrame.IsAlive()) {
         FireOnInputAndOnChange();
       }
     }
   }
 }
@@ -2231,17 +2231,17 @@ nsListControlFrame::KeyDown(nsIDOMEvent*
       break;
     case NS_VK_RETURN:
       if (IsInDropDownMode()) {
         if (mComboboxFrame->IsDroppedDown()) {
           // If the select element is a dropdown style, Enter key should be
           // consumed while the dropdown is open for security.
           aKeyEvent->PreventDefault();
 
-          nsWeakFrame weakFrame(this);
+          AutoWeakFrame weakFrame(this);
           ComboboxFinish(mEndSelectionIndex);
           if (!weakFrame.IsAlive()) {
             return NS_OK;
           }
         }
         FireOnInputAndOnChange();
         return NS_OK;
       }
@@ -2443,17 +2443,17 @@ nsListControlFrame::KeyPress(nsIDOMEvent
   }
 
   // now make sure there are options or we are wasting our time
   RefPtr<dom::HTMLOptionsCollection> options = GetOptions();
   NS_ENSURE_TRUE(options, NS_ERROR_FAILURE);
 
   uint32_t numOptions = options->Length();
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   for (uint32_t i = 0; i < numOptions; ++i) {
     uint32_t index = (i + startIndex) % numOptions;
     RefPtr<dom::HTMLOptionElement> optionElement =
       options->ItemAsOption(index);
     if (!optionElement || !optionElement->GetPrimaryFrame()) {
       continue;
     }
 
@@ -2502,17 +2502,17 @@ nsListControlFrame::PostHandleKeyEvent(i
     // the user acted on.
     if (!GetNonDisabledOptionFrom(0, &aNewIndex)) {
       return;
     }
   }
 
   // If you hold control, but not shift, no key will actually do anything
   // except space.
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   bool wasChanged = false;
   if (aIsControlOrMeta && !aIsShift && aCharCode != ' ') {
     mStartSelectionIndex = aNewIndex;
     mEndSelectionIndex = aNewIndex;
     InvalidateFocus();
     ScrollToIndex(aNewIndex);
     if (!weakFrame.IsAlive()) {
       return;
--- a/layout/forms/nsNumberControlFrame.h
+++ b/layout/forms/nsNumberControlFrame.h
@@ -187,17 +187,17 @@ private:
         static_cast<nsNumberControlFrame*>(mFrame.GetFrame());
       NS_ENSURE_STATE(frame);
 
       frame->SyncDisabledState();
       return NS_OK;
     }
 
   private:
-    nsWeakFrame mFrame;
+    WeakFrame mFrame;
   };
 
   /**
    * Sync the disabled state of the anonymous children up with our content's.
    */
   void SyncDisabledState();
 
   /**
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -245,17 +245,17 @@ nsTextControlFrame::EnsureEditorInitiali
   // when we actually need an editor.
 
   if (mEditorHasBeenInitialized)
     return NS_OK;
 
   nsIDocument* doc = mContent->GetComposedDoc();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
 
   // Flush out content on our document.  Have to do this, because script
   // blockers don't prevent the sink flushing out content and notifying in the
   // process, which can destroy frames.
   doc->FlushPendingNotifications(FlushType::ContentAndNotify);
   NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE);
 
   // Make sure that editor init doesn't do things that would kill us off
@@ -706,17 +706,17 @@ nsresult nsTextControlFrame::SetFormProp
       // Select all the text.
       //
       // XXX: This is lame, we can't call editor's SelectAll method
       //      because that triggers AutoCopies in unix builds.
       //      Instead, we have to call our own homegrown version
       //      of select all which merely builds a range that selects
       //      all of the content and adds that to the selection.
 
-      nsWeakFrame weakThis = this;
+      AutoWeakFrame weakThis = this;
       SelectAllOrCollapseToEndOfText(true);  // NOTE: can destroy the world
       if (!weakThis.IsAlive()) {
         return NS_OK;
       }
     }
     mIsProcessing = false;
   }
   return NS_OK;
@@ -1195,17 +1195,17 @@ nsTextControlFrame::SetInitialChildList(
 
 void
 nsTextControlFrame::SetValueChanged(bool aValueChanged)
 {
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
 
   if (mUsePlaceholder) {
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     txtCtrl->UpdatePlaceholderVisibility(true);
     if (!weakFrame.IsAlive()) {
       return;
     }
   }
 
   txtCtrl->SetValueChanged(aValueChanged);
 }
@@ -1251,17 +1251,17 @@ nsTextControlFrame::UpdateValueDisplay(b
     txtCtrl->GetTextEditorValue(value, true);
   }
 
   // Update the display of the placeholder value if needed.
   // We don't need to do this if we're about to initialize the
   // editor, since EnsureEditorInitialized takes care of this.
   if (mUsePlaceholder && !aBeforeEditorInit)
   {
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     txtCtrl->UpdatePlaceholderVisibility(aNotify);
     NS_ENSURE_STATE(weakFrame.IsAlive());
   }
 
   if (aBeforeEditorInit && value.IsEmpty()) {
     rootNode->RemoveChildAt(0, true);
     return NS_OK;
   }
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -235,17 +235,17 @@ protected:
 
   class EditorInitializer : public mozilla::Runnable {
   public:
     explicit EditorInitializer(nsTextControlFrame* aFrame) :
       mFrame(aFrame) {}
 
     NS_IMETHOD Run() override;
 
-    // avoids use of nsWeakFrame
+    // avoids use of AutoWeakFrame
     void Revoke() {
       mFrame = nullptr;
     }
 
   private:
     nsTextControlFrame* mFrame;
   };
 
--- a/layout/generic/ScrollbarActivity.cpp
+++ b/layout/generic/ScrollbarActivity.cpp
@@ -369,17 +369,17 @@ ScrollbarActivity::UpdateOpacity(TimeSta
   // Avoid division by zero if mScrollbarFadeDuration is zero, just jump
   // to the end of the fade animation
   double progress = mScrollbarFadeDuration
     ? ((aTime - mFadeBeginTime) / FadeDuration())
     : 1.0;
   double opacity = 1.0 - std::max(0.0, std::min(1.0, progress));
 
   // 'this' may be getting destroyed during SetOpacityOnElement calls.
-  nsWeakFrame weakFrame((do_QueryFrame(mScrollableFrame)));
+  AutoWeakFrame weakFrame((do_QueryFrame(mScrollableFrame)));
   SetOpacityOnElement(GetHorizontalScrollbar(), opacity);
   if (!weakFrame.IsAlive()) {
     return false;
   }
   SetOpacityOnElement(GetVerticalScrollbar(), opacity);
   if (!weakFrame.IsAlive()) {
     return false;
   }
@@ -403,17 +403,17 @@ ScrollbarActivity::SetIsFading(bool aNew
 {
   if (mIsFading == aNewFading)
     return true;
 
   mIsFading = aNewFading;
   if (!mIsFading) {
     mFadeBeginTime = TimeStamp();
     // 'this' may be getting destroyed during UnsetOpacityOnElement calls.
-    nsWeakFrame weakFrame((do_QueryFrame(mScrollableFrame)));
+    AutoWeakFrame weakFrame((do_QueryFrame(mScrollableFrame)));
     UnsetOpacityOnElement(GetHorizontalScrollbar());
     if (!weakFrame.IsAlive()) {
       return false;
     }
     UnsetOpacityOnElement(GetVerticalScrollbar());
     if (!weakFrame.IsAlive()) {
       return false;
     }
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -681,17 +681,17 @@ nsContainerFrame::SyncWindowProperties(n
     return;
 
   if (aFlags & SET_ASYNC) {
     aView->SetNeedsWindowPropertiesSync();
     return;
   }
 
   RefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
-  nsWeakFrame weak(rootFrame);
+  AutoWeakFrame weak(rootFrame);
 
   nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
   int32_t shadow = rootFrame->StyleUIReset()->mWindowShadow;
   nsCOMPtr<nsIWidget> viewWidget = aView->GetWidget();
   viewWidget->SetTransparencyMode(mode);
   windowWidget->SetWindowShadowStyle(shadow);
 
   if (!aRC)
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -401,24 +401,46 @@ nsIFrame::FindCloserFrameForSelection(
   }
 }
 
 void
 nsIFrame::ContentStatesChanged(mozilla::EventStates aStates)
 {
 }
 
+AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
+  : mPrev(nullptr), mFrame(nullptr)
+{
+  Init(aOther.GetFrame());
+}
+
 void
-nsWeakFrame::Init(nsIFrame* aFrame)
+AutoWeakFrame::Init(nsIFrame* aFrame)
 {
   Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
   mFrame = aFrame;
   if (mFrame) {
     nsIPresShell* shell = mFrame->PresContext()->GetPresShell();
-    NS_WARNING_ASSERTION(shell, "Null PresShell in nsWeakFrame!");
+    NS_WARNING_ASSERTION(shell, "Null PresShell in AutoWeakFrame!");
+    if (shell) {
+      shell->AddAutoWeakFrame(this);
+    } else {
+      mFrame = nullptr;
+    }
+  }
+}
+
+void
+WeakFrame::Init(nsIFrame* aFrame)
+{
+  Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
+  mFrame = aFrame;
+  if (mFrame) {
+    nsIPresShell* shell = mFrame->PresContext()->GetPresShell();
+    MOZ_ASSERT(shell, "Null PresShell in WeakFrame!");
     if (shell) {
       shell->AddWeakFrame(this);
     } else {
       mFrame = nullptr;
     }
   }
 }
 
@@ -3415,17 +3437,17 @@ nsFrame::HandlePress(nsPresContext* aPre
   bool control = mouseEvent->IsMeta();
 #else
   bool control = mouseEvent->IsControl();
 #endif
 
   RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
   if (mouseEvent->mClickCount > 1) {
     // These methods aren't const but can't actually delete anything,
-    // so no need for nsWeakFrame.
+    // so no need for AutoWeakFrame.
     fc->SetDragState(true);
     fc->SetMouseDoubleDown(true);
     return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control);
   }
 
   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
   ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
 
@@ -3746,17 +3768,17 @@ NS_IMETHODIMP nsFrame::HandleDrag(nsPres
   int32_t target;
   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
   nsresult result;
   result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
                                     getter_AddRefs(parentContent),
                                     &contentOffset, &target);      
 
-  nsWeakFrame weakThis = this;
+  AutoWeakFrame weakThis = this;
   if (NS_SUCCEEDED(result) && parentContent) {
     frameselection->HandleTableSelection(parentContent, contentOffset, target,
                                          mouseEvent);
   } else {
     nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent, this);
     frameselection->HandleDrag(this, pt);
   }
 
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -1251,17 +1251,17 @@ nsHTMLFramesetFrame::MouseDrag(nsPresCon
   // if the capture ended, reset the drag state
   if (nsIPresShell::GetCapturingContent() != GetContent()) {
     mDragger = nullptr;
     gDragInProgress = false;
     return;
   }
 
   int32_t change; // measured positive from left-to-right or top-to-bottom
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   if (mDragger->mVertical) {
     change = aPresContext->DevPixelsToAppUnits(
                              aEvent->mRefPoint.x - mFirstDragPoint.x);
     if (change > mNextNeighborOrigSize - mMinDrag) {
       change = mNextNeighborOrigSize - mMinDrag;
     } else if (change <= mMinDrag - mPrevNeighborOrigSize) {
       change = mMinDrag - mPrevNeighborOrigSize;
     }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -152,48 +152,48 @@ nsHTMLScrollFrame::DestroyFrom(nsIFrame*
   nsContainerFrame::DestroyFrom(aDestructRoot);
 }
 
 void
 nsHTMLScrollFrame::SetInitialChildList(ChildListID  aListID,
                                        nsFrameList& aChildList)
 {
   nsContainerFrame::SetInitialChildList(aListID, aChildList);
-  ReloadChildFrames();
+  mHelper.ReloadChildFrames();
 }
 
 
 void
 nsHTMLScrollFrame::AppendFrames(ChildListID  aListID,
                                 nsFrameList& aFrameList)
 {
   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   mFrames.AppendFrames(nullptr, aFrameList);
-  ReloadChildFrames();
+  mHelper.ReloadChildFrames();
 }
 
 void
 nsHTMLScrollFrame::InsertFrames(ChildListID aListID,
                                 nsIFrame* aPrevFrame,
                                 nsFrameList& aFrameList)
 {
   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
                "inserting after sibling frame with different parent");
   mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
-  ReloadChildFrames();
+  mHelper.ReloadChildFrames();
 }
 
 void
 nsHTMLScrollFrame::RemoveFrame(ChildListID aListID,
                                nsIFrame* aOldFrame)
 {
   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   mFrames.DestroyFrame(aOldFrame);
-  ReloadChildFrames();
+  mHelper.ReloadChildFrames();
 }
 
 nsSplittableType
 nsHTMLScrollFrame::GetSplittableType() const
 {
   return NS_FRAME_NOT_SPLITTABLE;
 }
 
@@ -2146,17 +2146,17 @@ ScrollFrameHelper::AsyncScrollCallback(S
 }
 
 void
 ScrollFrameHelper::CompleteAsyncScroll(const nsRect &aRange, nsIAtom* aOrigin)
 {
   // Apply desired destination range since this is the last step of scrolling.
   mAsyncSmoothMSDScroll = nullptr;
   mAsyncScroll = nullptr;
-  nsWeakFrame weakFrame(mOuter);
+  AutoWeakFrame weakFrame(mOuter);
   ScrollToImpl(mDestination, aRange, aOrigin);
   if (!weakFrame.IsAlive()) {
     return;
   }
   // We are done scrolling, set our destination to wherever we actually ended
   // up scrolling to.
   mDestination = GetScrollPosition();
 }
@@ -2899,17 +2899,17 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
     // Update the overflow for the outer so that we recompute scrollbars.
     mOuter->UpdateOverflow();
   }
 
   ScheduleSyntheticMouseMove();
 
   { // scope the AutoScrollbarRepaintSuppression
     AutoScrollbarRepaintSuppression repaintSuppression(this, !schedulePaint);
-    nsWeakFrame weakFrame(mOuter);
+    AutoWeakFrame weakFrame(mOuter);
     UpdateScrollbarPosition();
     if (!weakFrame.IsAlive()) {
       return;
     }
   }
 
   presContext->RecordInteractionTime(
     nsPresContext::InteractionType::eScrollInteraction,
@@ -4012,17 +4012,17 @@ ScrollFrameHelper::ScrollBy(nsIntPoint a
   CalcRangeForScrollBy(aDelta.x, newPos.x, negativeTolerance, positiveTolerance,
                        deltaMultiplier.width, &rangeLowerX, &rangeUpperX);
   CalcRangeForScrollBy(aDelta.y, newPos.y, negativeTolerance, positiveTolerance,
                        deltaMultiplier.height, &rangeLowerY, &rangeUpperY);
   nsRect range(rangeLowerX,
                rangeLowerY,
                rangeUpperX - rangeLowerX,
                rangeUpperY - rangeLowerY);
-  nsWeakFrame weakFrame(mOuter);
+  AutoWeakFrame weakFrame(mOuter);
   ScrollToWithOrigin(newPos, aMode, aOrigin, &range);
   if (!weakFrame.IsAlive()) {
     return;
   }
 
   if (aOverflow) {
     nsPoint clampAmount = newPos - mDestination;
     float appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
@@ -4219,17 +4219,17 @@ ScrollFrameHelper::ScrollToRestoredPosit
     // and scroll many times.
     if (mRestorePos != mLastPos /* GetLogicalScrollPosition() */) {
       nsPoint scrollToPos = mRestorePos;
       if (!IsPhysicalLTR()) {
         // convert from logical to physical scroll position
         scrollToPos.x = mScrollPort.x -
           (mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width);
       }
-      nsWeakFrame weakFrame(mOuter);
+      AutoWeakFrame weakFrame(mOuter);
       ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT,
                          nsGkAtoms::restore, nullptr);
       if (!weakFrame.IsAlive()) {
         return;
       }
       if (PageIsStillLoading() || NS_SUBTREE_DIRTY(mOuter)) {
         // If we're trying to do a history scroll restore, then we want to
         // keep trying this until we succeed, because the page can be loading
@@ -4596,17 +4596,17 @@ ScrollFrameHelper::Destroy()
  * Called when we want to update the scrollbar position, either because scrolling happened
  * or the user moved the scrollbar position and we need to undo that (e.g., when the user
  * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
  * to its initial position for the start of the smooth sequence).
  */
 void
 ScrollFrameHelper::UpdateScrollbarPosition()
 {
-  nsWeakFrame weakFrame(mOuter);
+  AutoWeakFrame weakFrame(mOuter);
   mFrameIsUpdatingScrollbar = true;
 
   nsPoint pt = GetScrollPosition();
   if (mVScrollbarBox) {
     SetCoordAttribute(mVScrollbarBox->GetContent(), nsGkAtoms::curpos,
                       pt.y - GetScrolledRect().y);
     if (!weakFrame.IsAlive()) {
       return;
@@ -4668,17 +4668,17 @@ void ScrollFrameHelper::CurPosAttributeC
   }
 
   bool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::smooth);
   if (isSmooth) {
     // Make sure an attribute-setting callback occurs even if the view
     // didn't actually move yet.  We need to make sure other listeners
     // see that the scroll position is not (yet) what they thought it
     // was.
-    nsWeakFrame weakFrame(mOuter);
+    AutoWeakFrame weakFrame(mOuter);
     UpdateScrollbarPosition();
     if (!weakFrame.IsAlive()) {
       return;
     }
   }
   ScrollToWithOrigin(dest,
                      isSmooth ? nsIScrollableFrame::SMOOTH : nsIScrollableFrame::INSTANT,
                      nsGkAtoms::scrollbars, &allowedRange);
@@ -5304,17 +5304,17 @@ ScrollFrameHelper::ReflowFinished()
     mVScrollbarBox ? mVScrollbarBox->GetContent() : nullptr;
   nsCOMPtr<nsIContent> hScroll =
     mHScrollbarBox ? mHScrollbarBox->GetContent() : nullptr;
 
   // Note, in some cases mOuter may get deleted while finishing reflow
   // for scrollbars. XXXmats is this still true now that we have a script
   // blocker in this scope? (if not, remove the weak frame checks below).
   if (vScroll || hScroll) {
-    nsWeakFrame weakFrame(mOuter);
+    AutoWeakFrame weakFrame(mOuter);
     nsPoint scrollPos = GetScrollPosition();
     nsSize lineScrollAmount = GetLineScrollAmount();
     if (vScroll) {
       const double kScrollMultiplier =
         Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
                             NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
       nscoord increment = lineScrollAmount.height * kScrollMultiplier;
       // We normally use (scrollArea.height - increment) for height
@@ -5668,17 +5668,17 @@ ScrollFrameHelper::SetCoordAttribute(nsI
   // only set the attribute if it changed.
 
   nsAutoString newValue;
   newValue.AppendInt(pixelSize);
 
   if (aContent->AttrValueIs(kNameSpaceID_None, aAtom, newValue, eCaseMatters))
     return;
 
-  nsWeakFrame weakFrame(mOuter);
+  AutoWeakFrame weakFrame(mOuter);
   nsCOMPtr<nsIContent> kungFuDeathGrip = aContent;
   aContent->SetAttr(kNameSpaceID_None, aAtom, newValue, true);
   MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
   if (!weakFrame.IsAlive()) {
     return;
   }
 
   if (mScrollbarActivity) {
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -1063,24 +1063,16 @@ protected:
   
   /**
    * Override this to return false if computed bsize/min-bsize/max-bsize
    * should NOT be propagated to child content.
    * nsListControlFrame uses this.
    */
   virtual bool ShouldPropagateComputedBSizeToScrolledContent() const { return true; }
 
-  void ReloadChildFrames()
-  {
-    mHelper.ReloadChildFrames();
-    if (mHelper.mScrolledFrame) {
-      mWritingMode = mHelper.mScrolledFrame->GetWritingMode();
-    }
-  }
-
 private:
   friend class mozilla::ScrollFrameHelper;
   ScrollFrameHelper mHelper;
 };
 
 /**
  * The scroll frame creates and manages the scrolling view
  *
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -3829,87 +3829,157 @@ public:
   virtual nsresult  DumpRegressionData(nsPresContext* aPresContext,
                                        FILE* out, int32_t aIndent) = 0;
 #endif
 };
 
 //----------------------------------------------------------------------
 
 /**
- * nsWeakFrame can be used to keep a reference to a nsIFrame in a safe way.
- * Whenever an nsIFrame object is deleted, the nsWeakFrames pointing
- * to it will be cleared.
+ * AutoWeakFrame can be used to keep a reference to a nsIFrame in a safe way.
+ * Whenever an nsIFrame object is deleted, the AutoWeakFrames pointing
+ * to it will be cleared.  AutoWeakFrame is for variables on the stack or
+ * in static storage only, there is also a WeakFrame below for heap uses.
  *
- * Create nsWeakFrame object when it is sure that nsIFrame object
+ * Create AutoWeakFrame object when it is sure that nsIFrame object
  * is alive and after some operations which may destroy the nsIFrame
  * (for example any DOM modifications) use IsAlive() or GetFrame() methods to
  * check whether it is safe to continue to use the nsIFrame object.
  *
  * @note The usage of this class should be kept to a minimum.
  */
-
-class nsWeakFrame {
+class WeakFrame;
+class MOZ_NONHEAP_CLASS AutoWeakFrame
+{
 public:
-  nsWeakFrame() : mPrev(nullptr), mFrame(nullptr) { }
-
-  nsWeakFrame(const nsWeakFrame& aOther) : mPrev(nullptr), mFrame(nullptr)
+  explicit AutoWeakFrame()
+    : mPrev(nullptr), mFrame(nullptr) {}
+
+  AutoWeakFrame(const AutoWeakFrame& aOther)
+    : mPrev(nullptr), mFrame(nullptr)
   {
     Init(aOther.GetFrame());
   }
 
-  MOZ_IMPLICIT nsWeakFrame(nsIFrame* aFrame) : mPrev(nullptr), mFrame(nullptr)
+  MOZ_IMPLICIT AutoWeakFrame(const WeakFrame& aOther);
+
+  MOZ_IMPLICIT AutoWeakFrame(nsIFrame* aFrame)
+    : mPrev(nullptr), mFrame(nullptr)
   {
     Init(aFrame);
   }
 
-  nsWeakFrame& operator=(nsWeakFrame& aOther) {
+  AutoWeakFrame& operator=(AutoWeakFrame& aOther) {
     Init(aOther.GetFrame());
     return *this;
   }
 
-  nsWeakFrame& operator=(nsIFrame* aFrame) {
+  AutoWeakFrame& operator=(nsIFrame* aFrame) {
     Init(aFrame);
     return *this;
   }
 
   nsIFrame* operator->()
   {
     return mFrame;
   }
 
   operator nsIFrame*()
   {
     return mFrame;
   }
 
   void Clear(nsIPresShell* aShell) {
     if (aShell) {
-      aShell->RemoveWeakFrame(this);
+      aShell->RemoveAutoWeakFrame(this);
     }
     mFrame = nullptr;
     mPrev = nullptr;
   }
 
   bool IsAlive() { return !!mFrame; }
 
   nsIFrame* GetFrame() const { return mFrame; }
 
-  nsWeakFrame* GetPreviousWeakFrame() { return mPrev; }
-
-  void SetPreviousWeakFrame(nsWeakFrame* aPrev) { mPrev = aPrev; }
-
-  ~nsWeakFrame()
+  AutoWeakFrame* GetPreviousWeakFrame() { return mPrev; }
+
+  void SetPreviousWeakFrame(AutoWeakFrame* aPrev) { mPrev = aPrev; }
+
+  ~AutoWeakFrame()
   {
     Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
   }
 private:
+  // Not available for the heap!
+  void* operator new(size_t) = delete;
+  void* operator new[](size_t) = delete;
+  void operator delete(void*) = delete;
+  void operator delete[](void*) = delete;
+
   void Init(nsIFrame* aFrame);
 
-  nsWeakFrame*  mPrev;
-  nsIFrame*     mFrame;
+  AutoWeakFrame*  mPrev;
+  nsIFrame*       mFrame;
+};
+
+/**
+ * @see AutoWeakFrame
+ */
+class MOZ_HEAP_CLASS WeakFrame
+{
+public:
+  WeakFrame() : mFrame(nullptr) {}
+
+  WeakFrame(const WeakFrame& aOther) : mFrame(nullptr)
+  {
+    Init(aOther.GetFrame());
+  }
+
+  MOZ_IMPLICIT WeakFrame(const AutoWeakFrame& aOther) : mFrame(nullptr)
+  {
+    Init(aOther.GetFrame());
+  }
+
+  MOZ_IMPLICIT WeakFrame(nsIFrame* aFrame) : mFrame(nullptr)
+  {
+    Init(aFrame);
+  }
+
+  ~WeakFrame()
+  {
+    Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
+  }
+
+  WeakFrame& operator=(WeakFrame& aOther) {
+    Init(aOther.GetFrame());
+    return *this;
+  }
+
+  WeakFrame& operator=(nsIFrame* aFrame) {
+    Init(aFrame);
+    return *this;
+  }
+
+  nsIFrame* operator->() { return mFrame; }
+  operator nsIFrame*() { return mFrame; }
+
+  void Clear(nsIPresShell* aShell) {
+    if (aShell) {
+      aShell->RemoveWeakFrame(this);
+    }
+    mFrame = nullptr;
+  }
+
+  bool IsAlive() { return !!mFrame; }
+  nsIFrame* GetFrame() const { return mFrame; }
+
+private:
+  void Init(nsIFrame* aFrame);
+
+  nsIFrame* mFrame;
 };
 
 inline bool
 nsFrameList::ContinueRemoveFrame(nsIFrame* aFrame)
 {
   MOZ_ASSERT(!aFrame->GetPrevSibling() || !aFrame->GetNextSibling(),
              "Forgot to call StartRemoveFrame?");
   if (aFrame == mLastChild) {
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -1847,17 +1847,17 @@ nsPluginFrame::EndSwapDocShells(nsISuppo
   nsRootPresContext* rootPC = objectFrame->PresContext()->GetRootPresContext();
   NS_ASSERTION(rootPC, "unable to register the plugin frame");
   nsIWidget* widget = objectFrame->mWidget;
   if (widget) {
     // Reparent the widget.
     nsIWidget* parent =
       rootPC->PresShell()->GetRootFrame()->GetNearestWidget();
     widget->SetParent(parent);
-    nsWeakFrame weakFrame(objectFrame);
+    AutoWeakFrame weakFrame(objectFrame);
     objectFrame->CallSetWindow();
     if (!weakFrame.IsAlive()) {
       return;
     }
   }
 
   if (objectFrame->mInstanceOwner) {
     objectFrame->RegisterPluginForGeometryUpdates();
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -299,17 +299,17 @@ public:
     mDelay = aDelay;
     return NS_OK;
   }
 
   NS_IMETHOD Notify(nsITimer *timer) override
   {
     if (mSelection && mPresContext)
     {
-      nsWeakFrame frame =
+      AutoWeakFrame frame =
         mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
       if (!frame)
         return NS_OK;
       mContent = nullptr;
 
       nsPoint pt = mPoint -
         frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
       mFrameSelection->HandleDrag(frame, pt);
@@ -4855,18 +4855,18 @@ Selection::DoAutoScroll(nsIFrame* aFrame
     (void)mAutoScrollTimer->Stop();
 
   nsPresContext* presContext = aFrame->PresContext();
   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
   nsRootPresContext* rootPC = presContext->GetRootPresContext();
   if (!rootPC)
     return NS_OK;
   nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
-  nsWeakFrame weakRootFrame(rootmostFrame);
-  nsWeakFrame weakFrame(aFrame);
+  AutoWeakFrame weakRootFrame(rootmostFrame);
+  AutoWeakFrame weakFrame(aFrame);
   // Get the point relative to the root most frame because the scroll we are
   // about to do will change the coordinates of aFrame.
   nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
 
   bool done = false;
   bool didScroll;
   while (true) {
     didScroll = shell->ScrollFrameRectIntoView(
--- a/layout/generic/nsSimplePageSequenceFrame.cpp
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -656,17 +656,17 @@ nsSimplePageSequenceFrame::PrePrintNextP
         if (!ctx) {
           continue;
         }
 
         // Initialize the context with the new DrawTarget.
         ctx->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget));
 
         // Start the rendering process.
-        nsWeakFrame weakFrame = this;
+        AutoWeakFrame weakFrame = this;
         canvas->DispatchPrintCallback(aCallback);
         NS_ENSURE_STATE(weakFrame.IsAlive());
       }
     }
   }
   uint32_t doneCounter = 0;
   for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
     HTMLCanvasElement* canvas = mCurrentCanvasList[i];
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -88,17 +88,17 @@ public:
   {
     PROFILER_LABEL("mozilla", "AsyncFrameInit::Run", js::ProfileEntry::Category::OTHER);
     if (mFrame.IsAlive()) {
       static_cast<nsSubDocumentFrame*>(mFrame.GetFrame())->ShowViewer();
     }
     return NS_OK;
   }
 private:
-  nsWeakFrame mFrame;
+  WeakFrame mFrame;
 };
 
 static void
 InsertViewsInReverseOrder(nsView* aSibling, nsView* aParent);
 
 static void
 EndSwapDocShellsForViews(nsView* aView);
 
@@ -176,17 +176,17 @@ nsSubDocumentFrame::ShowViewer()
   if (!PresContext()->IsDynamic()) {
     // We let the printing code take care of loading the document; just
     // create the inner view for it to use.
     (void) EnsureInnerView();
   } else {
     RefPtr<nsFrameLoader> frameloader = FrameLoader();
     if (frameloader) {
       CSSIntSize margin = GetMarginAttributes();
-      nsWeakFrame weakThis(this);
+      AutoWeakFrame weakThis(this);
       mCallingShow = true;
       const nsAttrValue* attrValue =
         GetContent()->AsElement()->GetParsedAttr(nsGkAtoms::scrolling);
       int32_t scrolling =
         nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue);
       bool didCreateDoc =
         frameloader->Show(margin.width, margin.height,
                           scrolling, scrolling, this);
@@ -810,17 +810,17 @@ nsSubDocumentFrame::Reflow(nsPresContext
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 }
 
 bool
 nsSubDocumentFrame::ReflowFinished()
 {
   if (mFrameLoader) {
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
 
     mFrameLoader->UpdatePositionAndSize(this);
 
     if (weakFrame.IsAlive()) {
       // Make sure that we can post a reflow callback in the future.
       mPostedReflowCallback = false;
     }
   } else {
@@ -1177,18 +1177,18 @@ EndSwapDocShellsForViews(nsView* aSiblin
     }
   }
 }
 
 void
 nsSubDocumentFrame::EndSwapDocShells(nsIFrame* aOther)
 {
   nsSubDocumentFrame* other = static_cast<nsSubDocumentFrame*>(aOther);
-  nsWeakFrame weakThis(this);
-  nsWeakFrame weakOther(aOther);
+  AutoWeakFrame weakThis(this);
+  AutoWeakFrame weakOther(aOther);
 
   if (mInnerView) {
     ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
   }
   if (other->mInnerView) {
     ::EndSwapDocShellsForViews(other->mInnerView->GetFirstChild());
   }
 
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -100,17 +100,17 @@ public:
 
   nsExpirationState mState;
 
   // Previous scale due to the CSS transform property.
   Maybe<gfxSize> mPreviousTransformScale;
 
   // The scroll frame during for which we most recently received a call to
   // NotifyAnimatedFromScrollHandler.
-  nsWeakFrame mAnimatingScrollHandlerFrame;
+  WeakFrame mAnimatingScrollHandlerFrame;
   // The set of activities that were triggered during
   // mAnimatingScrollHandlerFrame's scroll event handler.
   EnumSet<ActivityIndex> mScrollHandlerInducedActivity;
 
   // Number of restyle operations detected
   uint8_t mRestyleCounts[ACTIVITY_COUNT];
   bool mContentActive;
 };
@@ -127,17 +127,17 @@ public:
   ~LayerActivityTracker() {
     mDestroying = true;
     AgeAllGenerations();
   }
 
   virtual void NotifyExpired(LayerActivity* aObject);
 
 public:
-  nsWeakFrame mCurrentScrollHandlerFrame;
+  WeakFrame mCurrentScrollHandlerFrame;
 
 private:
   bool mDestroying;
 };
 
 static LayerActivityTracker* gLayerActivityTracker = nullptr;
 
 LayerActivity::~LayerActivity()
--- a/layout/printing/nsPrintEngine.h
+++ b/layout/printing/nsPrintEngine.h
@@ -258,17 +258,17 @@ protected:
   bool mProgressDialogIsShown;
 
   nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
   nsWeakPtr               mContainer;
   float                   mScreenDPI;
   
   mozilla::UniquePtr<nsPrintData> mPrt;
   nsPagePrintTimer*       mPagePrintTimer;
-  nsWeakFrame             mPageSeqFrame;
+  WeakFrame               mPageSeqFrame;
 
   // Print Preview
   mozilla::UniquePtr<nsPrintData> mPrtPreview;
   mozilla::UniquePtr<nsPrintData> mOldPrtPreview;
 
   nsCOMPtr<nsIDocument>   mDocument;
 
   FILE* mDebugFile;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/conservative-range-1-ref.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug 809020</title>
+<style>
+p { 
+  font-family: monospace;
+  margin: .5em 0;
+}
+.break {
+  color: green;
+}
+.nobreak {
+  color: red;
+}
+</style>
+</head>
+<body>
+The green examples should break at all their hyphens; the red ones should not.
+<p class="nobreak">
+T-shirt
+</p>
+<p class="nobreak">
+billy-o
+</p>
+<p class="break">
+tally-<br>ho
+</p>
+<p class="break">
+co-<br>author
+</p>
+<p class="break">
+hi-<br>de-<br>hi
+</p>
+<p class="break">
+far-<br>sighted
+</p>
+<p class="break">
+hocus-<br>pocus
+</p>
+<p class="break">
+Nanki-<br>Poo
+</p>
+<p class="break">
+Miami-<br>Dade
+</p>
+<p class="break">
+Wells-<br>next-<br>the-<br>Sea
+</p>
+<p class="break">
+Lee-<br>on-<br>the-<br>Solent
+</p>
+<p class="nobreak">
+\\/^-_-^\//
+</p>
+<p class="nobreak">
+(''')-.-(''')
+<p class="nobreak">
+:-"&gt;
+<p class="nobreak">
+&lt;{^-^}&gt;
+</p>
+<p class="nobreak">
+:-D
+</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/conservative-range-1.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug 809020</title>
+<style>
+p { 
+  font-family: monospace;
+  margin: .5em 0;
+  width:0;
+}
+.break {
+  color: green;
+}
+.nobreak {
+  color: red;
+}
+</style>
+</head>
+<body>
+The green examples should break at all their hyphens; the red ones should not.
+<p class="nobreak">
+T-shirt
+</p>
+<p class="nobreak">
+billy-o
+</p>
+<p class="break">
+tally-ho
+</p>
+<p class="break">
+co-author
+</p>
+<p class="break">
+hi-de-hi
+</p>
+<p class="break">
+far-sighted
+</p>
+<p class="break">
+hocus-pocus
+</p>
+<p class="break">
+Nanki-Poo
+</p>
+<p class="break">
+Miami-Dade
+</p>
+<p class="break">
+Wells-next-the-Sea
+</p>
+<p class="break">
+Lee-on-the-Solent
+</p>
+<p class="nobreak">
+\\/^-_-^\//
+</p>
+<p class="nobreak">
+(''')-.-(''')
+<p class="nobreak">
+:-"&gt;
+<p class="nobreak">
+&lt;{^-^}&gt;
+</p>
+<p class="nobreak">
+:-D
+</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/conservative-range-2-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug 809020</title>
+<style>
+p { 
+  font-family: monospace;
+  margin: 1em 0;
+}
+</style>
+</head>
+<body>
+
+Line breaks should always occur before a '/' character,
+and no fragment should be less than 6 chars long.
+
+<p>/a/a</p>
+<p>/a/a/a</p>
+<p>/a/a/a/a</p>
+<p>/a/a/a/a/a</p>
+<p>/a/a/a<br>/a/a/a</p>
+
+<p>/aa/aa<br>/aa/aa<br>/aa/aa/aa</p>
+<p>/aaa/aaa<br>/aaa/aaa/aaa</p>
+<p>/aaaa/aaaa<br>/aaaa/aaaa</p>
+<p>/aaaaa<br>/aaaaa<br>/aaaaa</p>
+<p>/aaaaaa<br>/aaaaaa<br>/aaaaaa</p>
+
+<p>/a/ab/abc<br>/abcd/abcde<br>/abcdef<br>/abcdefg</p>
+<p>/abcdefg<br>/abcdef<br>/abcde<br>/abcd/abc/ab/a</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/conservative-range-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug 809020</title>
+<style>
+p { 
+  font-family: monospace;
+  margin: 1em 0;
+  width:0;
+}
+</style>
+</head>
+<body>
+
+Line breaks should always occur before a '/' character,
+and no fragment should be less than 6 chars long.
+
+<p>/a/a</p>
+<p>/a/a/a</p>
+<p>/a/a/a/a</p>
+<p>/a/a/a/a/a</p>
+<p>/a/a/a/a/a/a</p>
+
+<p>/aa/aa/aa/aa/aa/aa/aa</p>
+<p>/aaa/aaa/aaa/aaa/aaa</p>
+<p>/aaaa/aaaa/aaaa/aaaa</p>
+<p>/aaaaa/aaaaa/aaaaa</p>
+<p>/aaaaaa/aaaaaa/aaaaaa</p>
+
+<p>/a/ab/abc/abcd/abcde/abcdef/abcdefg</p>
+<p>/abcdefg/abcdef/abcde/abcd/abc/ab/a</p>
+
+</body>
+</html>
--- a/layout/reftests/line-breaking/datetime-1-ref.html
+++ b/layout/reftests/line-breaking/datetime-1-ref.html
@@ -1,19 +1,19 @@
 <html>
 <head>
 <style type="text/css"> p { margin: 5px 1em; width: 0; white-space: nowrap; } </style>
 </head>
 <body>
 
 <p>2007-01-01</p>
-<p>2007-Jan-01</p>
+<p>2007-<br>Jan-01</p>
 <p>Jan-01-2007</p>
 <p>2007-01-01&nbsp;00:00:00</p>
-<p>2007-Jan-01&nbsp;00:00:00</p>
+<p>2007-<br>Jan-01&nbsp;00:00:00</p>
 <p>Jan-01-2007&nbsp;00:00:00</p>
 
 <p>2007/01/01</p>
 <p>2007/Jan/01</p>
 <p>Jan/01/2007</p>
 <p>2007/01/01&nbsp;00:00:00</p>
 <p>2007/Jan/01&nbsp;00:00:00</p>
 <p>Jan/01/2007&nbsp;00:00:00</p>
--- a/layout/reftests/line-breaking/hyphens-1-ref.html
+++ b/layout/reftests/line-breaking/hyphens-1-ref.html
@@ -1,41 +1,41 @@
 <html>
 <head>
 <style type="text/css"> p { margin: 5px 1em; width: 0; white-space: nowrap; } </style>
 </head>
 <body>
 
-<p>abcde-<br>abcdef</p>
-<p>abcd-abcdef</p>
-<p>abcde-abcde</p>
+<p>ab-<br>ab</p>
+<p>a-ab</p>
+<p>ab-a</p>
 <p>abcdef--<br>abcdef</p>
 <p>------abcdef<p>
 
 <!-- U+058A is ARMENIAN HYPHEN -->
-<p>abcde&#x058A;<br>abcdef</p>
-<p>abcd&#x058A;abcdef</p>
-<p>abcde&#x058A;abcde</p>
-<p>abcde&#x058A;&#x058A;<br>abcdef</p>
-<p>&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;abcdef</p>
+<p>ab&#x058A;<br>ab</p>
+<p>a&#x058A;ab</p>
+<p>ab&#x058A;a</p>
+<p>abcdef&#x058A;&#x058A;<br>abcdef</p>
+<p>&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;abcdef<p>
 
 <!-- U+2010 is HYPHEN -->
-<p>abcde&#x2010;<br>abcdef</p>
-<p>abcd&#x2010;abcdef</p>
-<p>abcde&#x2010;abcde</p>
-<p>abcde&#x2010;&#x2010;<br>abcdef</p>
-<p>&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;abcdef</p>
+<p>ab&#x2010;<br>ab</p>
+<p>a&#x2010;ab</p>
+<p>ab&#x2010;a</p>
+<p>abcdef&#x2010;&#x2010;<br>abcdef</p>
+<p>&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;abcdef<p>
 
 <!-- U+2012 is FIGURE DASH -->
-<p>abcde&#x2012;<br>abcdef</p>
-<p>abcd&#x2012;abcdef</p>
-<p>abcde&#x2012;abcde</p>
-<p>abcde&#x2012;&#x2012;<br>abcdef</p>
-<p>&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;abcdef</p>
+<p>ab&#x2012;<br>ab</p>
+<p>a&#x2012;ab</p>
+<p>ab&#x2012;a</p>
+<p>abcdef&#x2012;&#x2012;<br>abcdef</p>
+<p>&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;abcdef<p>
 
-<p>abcde&ndash;<br>abcdef</p>
-<p>abcd&ndash;abcdef</p>
-<p>abcde&ndash;abcde</p>
-<p>abcde&ndash;&ndash;<br>abcdef</p>
-<p>&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;abcdef</p>
+<p>ab&ndash;<br>ab</p>
+<p>a&ndash;ab</p>
+<p>ab&ndash;a</p>
+<p>abcdef&ndash;&ndash;<br>abcdef</p>
+<p>&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;abcdef<p>
 
 </body>
 </html>
--- a/layout/reftests/line-breaking/hyphens-1.html
+++ b/layout/reftests/line-breaking/hyphens-1.html
@@ -1,41 +1,41 @@
 <html>
 <head>
 <style type="text/css"> p { margin: 5px 1em; width: 0; } </style>
 </head>
 <body>
 
-<p>abcde-abcdef</p>
-<p>abcd-abcdef</p>
-<p>abcde-abcde</p>
+<p>ab-ab</p>
+<p>a-ab</p>
+<p>ab-a</p>
 <p>abcdef--abcdef</p>
 <p>------abcdef<p>
 
 <!-- U+058A is ARMENIAN HYPHEN -->
-<p>abcde&#x058A;abcdef</p>
-<p>abcd&#x058A;abcdef</p>
-<p>abcde&#x058A;abcde</p>
-<p>abcde&#x058A;&#x058A;abcdef</p>
-<p>&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;abcdef</p>
+<p>ab&#x058A;ab</p>
+<p>a&#x058A;ab</p>
+<p>ab&#x058A;a</p>
+<p>abcdef&#x058A;&#x058A;abcdef</p>
+<p>&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;&#x058A;abcdef<p>
 
 <!-- U+2010 is HYPHEN -->
-<p>abcde&#x2010;abcdef</p>
-<p>abcd&#x2010;abcdef</p>
-<p>abcde&#x2010;abcde</p>
-<p>abcde&#x2010;&#x2010;abcdef</p>
-<p>&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;abcdef</p>
+<p>ab&#x2010;ab</p>
+<p>a&#x2010;ab</p>
+<p>ab&#x2010;a</p>
+<p>abcdef&#x2010;&#x2010;abcdef</p>
+<p>&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;&#x2010;abcdef<p>
 
 <!-- U+2012 is FIGURE DASH -->
-<p>abcde&#x2012;abcdef</p>
-<p>abcd&#x2012;abcdef</p>
-<p>abcde&#x2012;abcde</p>
-<p>abcde&#x2012;&#x2012;abcdef</p>
-<p>&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;abcdef</p>
+<p>ab&#x2012;ab</p>
+<p>a&#x2012;ab</p>
+<p>ab&#x2012;a</p>
+<p>abcdef&#x2012;&#x2012;abcdef</p>
+<p>&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;&#x2012;abcdef<p>
 
-<p>abcde&ndash;abcdef</p>
-<p>abcd&ndash;abcdef</p>
-<p>abcde&ndash;abcde</p>
-<p>abcde&ndash;&ndash;abcdef</p>
-<p>&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;abcdef</p>
+<p>ab&ndash;ab</p>
+<p>a&ndash;ab</p>
+<p>ab&ndash;a</p>
+<p>abcdef&ndash;&ndash;abcdef</p>
+<p>&ndash;&ndash;&ndash;&ndash;&ndash;&ndash;abcdef<p>
 
 </body>
 </html>
--- a/layout/reftests/line-breaking/reftest.list
+++ b/layout/reftests/line-breaking/reftest.list
@@ -1,10 +1,12 @@
 == between-whitespaces.html between-whitespaces-ref.html
 == chemical-1.html chemical-1-ref.html
+== conservative-range-1.html conservative-range-1-ref.html
+== conservative-range-2.html conservative-range-2-ref.html
 == currency-1.html currency-1-ref.html
 == currency-2.html currency-2-ref.html
 == datetime-1.html datetime-1-ref.html
 == emoji-1.html emoji-1-ref.html
 == emoji-2.html emoji-2-ref.html
 == hyphens-1.html hyphens-1-ref.html
 == hyphens-2.html hyphens-2-ref.html
 # The following three tests may fail if rendering with Core Text (see bug 389074)
@@ -27,13 +29,17 @@ random-if(cocoaWidget) == ja-3.html ja-3
 == quotationmarks-1.html quotationmarks-1-ref.html
 # The following is currently disabled on Linux because of a rendering issue with missing-glyph
 # representations on the test boxes. See bug #450088 for discussion.
 skip-if(gtkWidget) == quotationmarks-cjk-1.html quotationmarks-cjk-1-ref.html
 == smileys-1.html smileys-1-ref.html
 == smileys-2.html smileys-2-ref.html
 == space-cluster-1.html space-cluster-1-ref.html
 == space-cluster-2.html space-cluster-2-ref.html
+== surrogates-1.html surrogates-1-ref.html
+== surrogates-2.html surrogates-2-ref.html
+== surrogates-3.html surrogates-3-ref.html
+== surrogates-4.html surrogates-4-ref.html
 == url-1.html url-1-ref.html
 == url-2.html url-2-ref.html
 == url-3.html url-3-ref.html
 == winpath-1.html winpath-1-ref.html
 == zwnbsp-1.html zwnbsp-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-1-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef<br>&#x2018;&#x6587;&#x2019;<br>abcdef</p>
+<p class="supp">abcdef<br>&#x2018;&#x2000B;&#x2019;<br>abcdef</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+p {
+  width:0;
+}
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef &#x2018;&#x6587;&#x2019; abcdef</p>
+<p class="supp">abcdef &#x2018;&#x2000B;&#x2019; abcdef</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-2-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef<br>&#x2018;&#x6587;&#x2019;abcdef</p>
+<p class="supp">abcdef<br>&#x2018;&#x2000B;&#x2019;abcdef</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+p {
+  width:0;
+}
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef&#x2018;&#x6587;&#x2019;abcdef</p>
+<p class="supp">abcdef&#x2018;&#x2000B;&#x2019;abcdef</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-3-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef<br>&#x2018;&#x6587;&#x2019;<br>abcdef</p>
+<p class="supp">&#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d;<br>&#x2018;&#x2000B;&#x2019;<br>&#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d;</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+p {
+  width:0;
+}
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef &#x2018;&#x6587;&#x2019; abcdef</p>
+<p class="supp">&#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d; &#x2018;&#x2000B;&#x2019; &#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d;</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-4-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef<br>&#x2018;&#x6587;&#x2019;abcdef</p>
+<p class="supp">&#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d;<br>&#x2018;&#x2000B;&#x2019;&#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d;</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/line-breaking/surrogates-4.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>Bug xxxx</title>
+<style>
+p {
+  width:0;
+}
+.bmp {
+  color: green;
+}
+.supp {
+  color: blue;
+}
+</style>
+</head>
+<body>
+Line-breaking in the green (BMP) and blue (supplementary-plane) examples should match.
+<p class="bmp">abcdef&#x2018;&#x6587;&#x2019;abcdef</p>
+<p class="supp">&#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d;&#x2018;&#x2000B;&#x2019;&#x10408;&#x10409;&#x1040a;&#x1040b;&#x1040c;&#x1040d;</p>
+</body>
+</html>
--- a/layout/reftests/line-breaking/url-3-ref.html
+++ b/layout/reftests/line-breaking/url-3-ref.html
@@ -1,14 +1,14 @@
 <html>
 <head>
 <style type="text/css"> p { margin: 5px 1em; width: 0; white-space: nowrap; } </style>
 </head>
 <body>
 
-<p>index.cgi?abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA<br>%9E&amp;abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA%9E</p>
-<p>index.cgi?abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA<br>%9E;abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA%9E</p>
+<p>index.cgi?abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA<br>%9E&amp;<br>abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA%9E</p>
+<p>index.cgi?abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA<br>%9E;<br>abcdef=<br>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA%9E</p>
 <p>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA%9E</p>
 <p>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA%9Eab</p>
 <p>%E6%97<br>%A5%E6<br>%9C%AC<br>%E8%AA<br>%9Eabc</p>
 
 </body>
 </html>
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -4856,17 +4856,17 @@ public:
       nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
       if (tableFrame->NeedToCalcBCBorders()) {
         tableFrame->CalcBCBorders();
       }
     }
     return NS_OK;
   }
 private:
-  nsWeakFrame mFrame;
+  WeakFrame mFrame;
 };
 
 bool
 nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
                              nsStyleContext* aNewStyleContext)
 {
   // Attention: the old style context is the one we're forgetting,
   // and hence possibly completely bogus for GetStyle* purposes.
--- a/layout/xul/nsListBoxBodyFrame.cpp
+++ b/layout/xul/nsListBoxBodyFrame.cpp
@@ -327,58 +327,58 @@ nsListBoxBodyFrame::GetXULPrefSize(nsBox
 
 void
 nsListBoxBodyFrame::ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
                                  nsIScrollbarMediator::ScrollSnapMode aSnap)
 {
   // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
   MOZ_ASSERT(aScrollbar != nullptr);
   aScrollbar->SetIncrementToPage(aDirection);
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   int32_t newPos = aScrollbar->MoveToNewPosition();
   if (!weakFrame.IsAlive()) {
     return;
   }
   UpdateIndex(newPos);
 }
 
 void
 nsListBoxBodyFrame::ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
                                   nsIScrollbarMediator::ScrollSnapMode aSnap)
 {
   // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
   MOZ_ASSERT(aScrollbar != nullptr);
   aScrollbar->SetIncrementToWhole(aDirection);
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   int32_t newPos = aScrollbar->MoveToNewPosition();
   if (!weakFrame.IsAlive()) {
     return;
   }
   UpdateIndex(newPos);
 }
 
 void
 nsListBoxBodyFrame::ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
                                  nsIScrollbarMediator::ScrollSnapMode aSnap)
 {
   // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
   MOZ_ASSERT(aScrollbar != nullptr);
   aScrollbar->SetIncrementToLine(aDirection);
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   int32_t newPos = aScrollbar->MoveToNewPosition();
   if (!weakFrame.IsAlive()) {
     return;
   }
   UpdateIndex(newPos);
 }
 
 void
 nsListBoxBodyFrame::RepeatButtonScroll(nsScrollbarFrame* aScrollbar)
 {
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   int32_t newPos = aScrollbar->MoveToNewPosition();
   if (!weakFrame.IsAlive()) {
     return;
   }
   UpdateIndex(newPos);
 }
 
 int32_t
@@ -785,17 +785,17 @@ nsListBoxBodyFrame::ScrollToIndex(int32_
   if (lastPageTopRow < 0)
     lastPageTopRow = 0;
 
   if (aRowIndex > lastPageTopRow)
     return NS_OK;
 
   mCurrentIndex = newIndex;
 
-  nsWeakFrame weak(this);
+  AutoWeakFrame weak(this);
 
   // Since we're going to flush anyway, we need to not do this off an event
   DoInternalPositionChangedSync(up, delta);
 
   if (!weak.IsAlive()) {
     return NS_OK;
   }
 
@@ -838,17 +838,17 @@ nsListBoxBodyFrame::InternalPositionChan
     }
   }
   return rv;
 }
 
 nsresult
 nsListBoxBodyFrame::DoInternalPositionChangedSync(bool aUp, int32_t aDelta)
 {
-  nsWeakFrame weak(this);
+  AutoWeakFrame weak(this);
   
   // Process all the pending position changes first
   nsTArray< RefPtr<nsPositionChangedEvent> > temp;
   temp.SwapElements(mPendingPositionChangeEvents);
   for (uint32_t i = 0; i < temp.Length(); ++i) {
     if (weak.IsAlive()) {
       temp[i]->Run();
     }
@@ -869,17 +869,17 @@ nsListBoxBodyFrame::DoInternalPositionCh
     return NS_OK;
 
   RefPtr<nsPresContext> presContext(PresContext());
   nsBoxLayoutState state(presContext);
 
   // begin timing how long it takes to scroll a row
   PRTime start = PR_Now();
 
-  nsWeakFrame weakThis(this);
+  AutoWeakFrame weakThis(this);
   mContent->GetComposedDoc()->FlushPendingNotifications(FlushType::Layout);
   if (!weakThis.IsAlive()) {
     return NS_OK;
   }
 
   {
     nsAutoScriptBlocker scriptBlocker;
 
@@ -965,17 +965,17 @@ nsListBoxBodyFrame::VerticalScroll(int32
   nsIScrollableFrame* scrollFrame
     = nsLayoutUtils::GetScrollableFrameFor(this);
   if (!scrollFrame) {
     return;
   }
 
   nsPoint scrollPosition = scrollFrame->GetScrollPosition();
  
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   scrollFrame->ScrollTo(nsPoint(scrollPosition.x, aPosition),
                         nsIScrollableFrame::INSTANT);
   if (!weakFrame.IsAlive()) {
     return;
   }
 
   mYPosition = aPosition;
 }
@@ -1125,25 +1125,25 @@ nsListBoxBodyFrame::GetFirstItemBox(int3
 {
   if (aCreated)
    *aCreated = false;
 
   // Clear ourselves out.
   mBottomFrame = mTopFrame;
 
   if (mTopFrame) {
-    return mTopFrame->IsXULBoxFrame() ? mTopFrame : nullptr;
+    return mTopFrame->IsXULBoxFrame() ? mTopFrame.GetFrame() : nullptr;
   }
 
   // top frame was cleared out
   mTopFrame = GetFirstFrame();
   mBottomFrame = mTopFrame;
 
   if (mTopFrame && mRowsToPrepend <= 0) {
-    return mTopFrame->IsXULBoxFrame() ? mTopFrame : nullptr;
+    return mTopFrame->IsXULBoxFrame() ? mTopFrame.GetFrame() : nullptr;
   }
 
   // At this point, we either have no frames at all, 
   // or the user has scrolled upwards, leaving frames
   // to be created at the top.  Let's determine which
   // content needs a new frame first.
 
   nsCOMPtr<nsIContent> startContent;
@@ -1182,17 +1182,17 @@ nsListBoxBodyFrame::GetFirstItemBox(int3
     fc->CreateListBoxContent(this, nullptr, startContent, &topFrame, isAppend);
     mTopFrame = topFrame;
     if (mTopFrame) {
       if (aCreated)
         *aCreated = true;
 
       mBottomFrame = mTopFrame;
 
-      return mTopFrame->IsXULBoxFrame() ? mTopFrame : nullptr;
+      return mTopFrame->IsXULBoxFrame() ? mTopFrame.GetFrame() : nullptr;
     } else
       return GetFirstItemBox(++aOffset, 0);
   }
 
   return nullptr;
 }
 
 //
@@ -1402,17 +1402,17 @@ nsListBoxBodyFrame::OnContentRemoved(nsP
       }
     
       // if the row being removed is off-screen and above the top frame, we need to
       // adjust our top index and tell the scrollbar to shift up one row.
       if (siblingIndex >= 0 && siblingIndex-1 < mCurrentIndex) {
         NS_PRECONDITION(mCurrentIndex > 0, "mCurrentIndex > 0");
         --mCurrentIndex;
         mYPosition = mCurrentIndex*mRowHeight;
-        nsWeakFrame weakChildFrame(aChildFrame);
+        AutoWeakFrame weakChildFrame(aChildFrame);
         VerticalScroll(mYPosition);
         if (!weakChildFrame.IsAlive()) {
           return;
         }
       }
     } else if (mCurrentIndex > 0) {
       // At this point, we know we have a scrollbar, and we need to know 
       // if we are scrolled to the last row.  In this case, the behavior
@@ -1430,17 +1430,17 @@ nsListBoxBodyFrame::OnContentRemoved(nsP
       if (lastChild) {
         nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
 
         if (lastChildFrame) {
           mTopFrame = nullptr;
           mRowsToPrepend = 1;
           --mCurrentIndex;
           mYPosition = mCurrentIndex*mRowHeight;
-          nsWeakFrame weakChildFrame(aChildFrame);
+          AutoWeakFrame weakChildFrame(aChildFrame);
           VerticalScroll(mYPosition);
           if (!weakChildFrame.IsAlive()) {
             return;
           }
         }
       }
     }
   }
--- a/layout/xul/nsListBoxBodyFrame.h
+++ b/layout/xul/nsListBoxBodyFrame.h
@@ -180,17 +180,17 @@ protected:
   void ComputeTotalRowCount();
   int32_t ToRowIndex(nscoord aPos) const;
   void RemoveChildFrame(nsBoxLayoutState &aState, nsIFrame *aChild);
 
   nsTArray< RefPtr<nsPositionChangedEvent> > mPendingPositionChangeEvents;
   nsCOMPtr<nsPIBoxObject> mBoxObject;
 
   // frame markers
-  nsWeakFrame mTopFrame;
+  WeakFrame mTopFrame;
   nsIFrame* mBottomFrame;
   nsIFrame* mLinkupFrame;
 
   nsListScrollSmoother* mScrollSmoother;
 
   int32_t mRowsToPrepend;
 
   // row height
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -276,17 +276,17 @@ public:
     nsMenuBarFrame* menubar = nullptr;
     if (mOldMenu && mNewMenu) {
       menubar = do_QueryFrame(mMenuBar->GetPrimaryFrame());
       if (menubar)
         menubar->SetStayActive(true);
     }
 
     if (mOldMenu) {
-      nsWeakFrame weakMenuBar(menubar);
+      AutoWeakFrame weakMenuBar(menubar);
       pm->HidePopup(mOldMenu, false, false, false, false);
       // clear the flag again
       if (mNewMenu && weakMenuBar.IsAlive())
         menubar->SetStayActive(false);
     }
 
     if (mNewMenu)
       pm->ShowMenu(mNewMenu, mSelectFirstItem, false);
--- a/layout/xul/nsMenuFrame.cpp
+++ b/layout/xul/nsMenuFrame.cpp
@@ -133,17 +133,17 @@ public:
     else if (mAttr == nsGkAtoms::key) {
       frame->BuildAcceleratorText(true);
     } else if (mAttr == nsGkAtoms::type || mAttr == nsGkAtoms::name) {
       frame->UpdateMenuType();
     }
     return NS_OK;
   }
 protected:
-  nsWeakFrame       mFrame;
+  WeakFrame         mFrame;
   nsCOMPtr<nsIAtom> mAttr;
 };
 
 //
 // NS_NewMenuFrame and NS_NewMenuItemFrame
 //
 // Wrappers for creating a new menu popup container
 //
@@ -374,17 +374,17 @@ nsMenuFrame::HandleEvent(nsPresContext* 
   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
   nsMenuParent* menuParent = GetMenuParent();
   if (menuParent && menuParent->IsMenuLocked()) {
     return NS_OK;
   }
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   if (*aEventStatus == nsEventStatus_eIgnore)
     *aEventStatus = nsEventStatus_eConsumeDoDefault;
 
   // If a menu just opened, ignore the mouseup event that might occur after a
   // the mousedown event that opened it. However, if a different mousedown
   // event occurs, just clear this flag.
   if (gMenuJustOpenedOrClosed) {
     if (aEvent->mMessage == eMouseDown) {
@@ -541,17 +541,17 @@ nsMenuFrame::ToggleMenuState()
     OpenMenu(false);
 }
 
 void
 nsMenuFrame::PopupOpened()
 {
   gMenuJustOpenedOrClosed = true;
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open,
                     NS_LITERAL_STRING("true"), true);
   if (!weakFrame.IsAlive())
     return;
 
   nsMenuParent* menuParent = GetMenuParent();
   if (menuParent) {
     menuParent->SetActive(true);
@@ -559,17 +559,17 @@ nsMenuFrame::PopupOpened()
     // the menubar is highlighted
     menuParent->SetCurrentMenuItem(this);
   }
 }
 
 void
 nsMenuFrame::PopupClosed(bool aDeselectMenu)
 {
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   nsContentUtils::AddScriptRunner(
     new nsUnsetAttrRunnable(mContent, nsGkAtoms::open));
   if (!weakFrame.IsAlive())
     return;
 
   // if the popup is for a menu on a menubar, inform menubar to deactivate
   nsMenuParent* menuParent = GetMenuParent();
   if (menuParent && menuParent->MenuClosed()) {
@@ -892,17 +892,17 @@ nsMenuFrame::Notify(nsITimer* aTimer)
     switch (mBlinkState++) {
       case 0:
         NS_ASSERTION(false, "Blink timer fired while not blinking");
         StopBlinking();
         break;
       case 1:
         {
           // Turn the highlight back on and wait for a while before closing the menu.
-          nsWeakFrame weakFrame(this);
+          AutoWeakFrame weakFrame(this);
           mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive,
                             NS_LITERAL_STRING("true"), true);
           if (weakFrame.IsAlive()) {
             aTimer->InitWithCallback(mTimerMediator, kBlinkDelay, nsITimer::TYPE_ONE_SHOT);
           }
         }
         break;
       default: {
@@ -937,17 +937,17 @@ nsMenuFrame::UpdateMenuType()
     case 0: mType = eMenuType_Checkbox; break;
     case 1:
       mType = eMenuType_Radio;
       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, mGroupName);
       break;
 
     default:
       if (mType != eMenuType_Normal) {
-        nsWeakFrame weakFrame(this);
+        AutoWeakFrame weakFrame(this);
         mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
                             true);
         ENSURE_TRUE(weakFrame.IsAlive());
       }
       mType = eMenuType_Normal;
       break;
   }
   UpdateMenuSpecialState();
@@ -1031,17 +1031,17 @@ nsMenuFrame::BuildAcceleratorText(bool a
       return;
   }
   // accelText is definitely empty here.
 
   // Now we're going to compute the accelerator text, so remember that we did.
   AddStateBits(NS_STATE_ACCELTEXT_IS_DERIVED);
 
   // If anything below fails, just leave the accelerator text blank.
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::acceltext, aNotify);
   ENSURE_TRUE(weakFrame.IsAlive());
 
   // See if we have a key node and use that instead.
   nsAutoString keyValue;
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyValue);
   if (keyValue.IsEmpty())
     return;
@@ -1215,17 +1215,17 @@ nsMenuFrame::StartBlinking(WidgetGUIEven
   CreateMenuCommandEvent(aEvent, aFlipChecked);
 
   if (!ShouldBlink()) {
     PassMenuCommandEventToPopupManager();
     return;
   }
 
   // Blink off.
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, true);
   if (!weakFrame.IsAlive())
     return;
 
   nsMenuParent* menuParent = GetMenuParent();
   if (menuParent) {
     // Make this menu ignore events from now on.
     menuParent->LockMenuUntilClosed(true);
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -445,17 +445,17 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayou
     }
   }
 
   // if the popup has just been opened, make sure the scrolled window is at 0,0
   // Don't scroll menulists as they will scroll to their selected item on their own.
   if (mIsOpenChanged && !IsMenuList()) {
     nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
     if (scrollframe) {
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
       if (!weakFrame.IsAlive()) {
         return;
       }
     }
   }
 
   // get the preferred, minimum and maximum size. If the menu is sized to the
@@ -887,17 +887,17 @@ nsMenuPopupFrame::ShowPopup(bool aIsCont
         EventStateManager::ClearGlobalActiveContent(activeESM);
       }
 
       nsIPresShell::SetCapturingContent(nullptr, 0);
     }
 
     nsMenuFrame* menuFrame = do_QueryFrame(GetParent());
     if (menuFrame) {
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       menuFrame->PopupOpened();
       if (!weakFrame.IsAlive())
         return;
     }
 
     // do we need an actual reflow here?
     // is SetPopupPosition all that is needed?
     PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
--- a/layout/xul/nsProgressMeterFrame.cpp
+++ b/layout/xul/nsProgressMeterFrame.cpp
@@ -26,17 +26,17 @@ class nsReflowFrameRunnable : public moz
 {
 public:
   nsReflowFrameRunnable(nsIFrame* aFrame,
                         nsIPresShell::IntrinsicDirty aIntrinsicDirty,
                         nsFrameState aBitToAdd);
 
   NS_DECL_NSIRUNNABLE
 
-  nsWeakFrame mWeakFrame;
+  WeakFrame mWeakFrame;
   nsIPresShell::IntrinsicDirty mIntrinsicDirty;
   nsFrameState mBitToAdd;
 };
 
 nsReflowFrameRunnable::nsReflowFrameRunnable(nsIFrame* aFrame,
                           nsIPresShell::IntrinsicDirty aIntrinsicDirty,
                           nsFrameState aBitToAdd)
   : mWeakFrame(aFrame),
@@ -95,17 +95,17 @@ public:
     return shouldFlush;
   }
 
   virtual void ReflowCallbackCanceled() override
   {
     delete this;
   }
 
-  nsWeakFrame mWeakFrame;
+  WeakFrame mWeakFrame;
 };
 
 NS_IMETHODIMP
 nsProgressMeterFrame::DoXULLayout(nsBoxLayoutState& aState)
 {
   if (mNeedsReflowCallback) {
     nsIReflowCallback* cb = new nsAsyncProgressMeterInit(this);
     if (cb) {
--- a/layout/xul/nsResizerFrame.cpp
+++ b/layout/xul/nsResizerFrame.cpp
@@ -55,17 +55,17 @@ nsResizerFrame::HandleEvent(nsPresContex
                             WidgetGUIEvent* aEvent,
                             nsEventStatus* aEventStatus)
 {
   NS_ENSURE_ARG_POINTER(aEventStatus);
   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   bool doDefault = true;
 
   switch (aEvent->mMessage) {
     case eTouchStart:
     case eMouseDown: {
       if (aEvent->mClass == eTouchEventClass ||
           (aEvent->mClass == eMouseEventClass &&
            aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) {
@@ -242,17 +242,17 @@ nsResizerFrame::HandleEvent(nsPresContex
         nsRect appUnitsRect = ToAppUnits(rect.ToUnknownRect(), aPresContext->AppUnitsPerDevPixel());
         if (appUnitsRect.width < mRect.width && mouseMove.x)
           appUnitsRect.width = mRect.width;
         if (appUnitsRect.height < mRect.height && mouseMove.y)
           appUnitsRect.height = mRect.height;
         nsIntRect cssRect = appUnitsRect.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel());
 
         LayoutDeviceIntRect oldRect;
-        nsWeakFrame weakFrame(menuPopupFrame);
+        AutoWeakFrame weakFrame(menuPopupFrame);
         if (menuPopupFrame) {
           nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
           if (widget)
             oldRect = widget->GetScreenBounds();
 
           // convert the new rectangle into outer window coordinates
           LayoutDeviceIntPoint clientOffset = widget->GetClientOffset();
           rect.x -= clientOffset.x;
--- a/layout/xul/nsScrollbarButtonFrame.cpp
+++ b/layout/xul/nsScrollbarButtonFrame.cpp
@@ -125,17 +125,17 @@ nsScrollbarButtonFrame::HandleButtonPres
     direction = 1;
   else if (index == 1)
     direction = -1;
   else
     return false;
 
   bool repeat = pressedButtonAction != 2;
   // set this attribute so we can style it later
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::active, NS_LITERAL_STRING("true"), true);
 
   nsIPresShell::SetCapturingContent(mContent, CAPTURE_IGNOREALLOWED);
 
   if (!weakFrame.IsAlive()) {
     return false;
   }
 
--- a/layout/xul/nsScrollbarFrame.cpp
+++ b/layout/xul/nsScrollbarFrame.cpp
@@ -263,17 +263,17 @@ nsScrollbarFrame::MoveToNewPosition()
   } else if (curpos > maxpos) {
     curpos = maxpos;
   }
 
   // set the current position of the slider.
   nsAutoString curposStr;
   curposStr.AppendInt(curpos);
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   if (mSmoothScroll) {
     content->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), false);
   }
   content->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curposStr, false);
   // notify the nsScrollbarFrame of the change
   AttributeChanged(kNameSpaceID_None, nsGkAtoms::curpos, nsIDOMMutationEvent::MODIFICATION);
   if (!weakFrame.IsAlive()) {
     return curpos;
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -604,17 +604,17 @@ nsSliderFrame::HandleEvent(nsPresContext
     }
     nsSize thumbSize = thumbFrame->GetSize();
     nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
 
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
         (uint32_t) ScrollInputMethod::MainThreadScrollbarTrackClick);
 
     // set it
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     // should aMaySnap be true here?
     SetCurrentThumbPosition(scrollbar, pos - thumbLength/2, false, false);
     NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
 
     DragThumb(true);
 
 #ifdef MOZ_WIDGET_GTK
     nsCOMPtr<nsIContent> thumb = thumbFrame->GetContent();
@@ -871,17 +871,17 @@ nsSliderFrame::SetCurrentPosition(nsICon
 }
 
 void
 nsSliderFrame::SetCurrentPositionInternal(nsIContent* aScrollbar, int32_t aNewPos,
                                           bool aIsSmooth)
 {
   nsCOMPtr<nsIContent> scrollbar = aScrollbar;
   nsIFrame* scrollbarBox = GetScrollbar();
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
 
   mUserChanged = true;
 
   nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox);
   if (scrollbarFrame) {
     // See if we have a mediator.
     nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator();
     if (mediator) {
--- a/layout/xul/nsSplitterFrame.cpp
+++ b/layout/xul/nsSplitterFrame.cpp
@@ -379,17 +379,17 @@ nsSplitterFrame::HandleEvent(nsPresConte
                              WidgetGUIEvent* aEvent,
                              nsEventStatus* aEventStatus)
 {
   NS_ENSURE_ARG_POINTER(aEventStatus);
   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   RefPtr<nsSplitterFrameInner> inner(mInner);
   switch (aEvent->mMessage) {
     case eMouseMove: 
       inner->MouseDrag(aPresContext, aEvent);
       break;
   
     case eMouseUp:
       if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
@@ -971,17 +971,17 @@ nsSplitterFrameInner::SetPreferredSize(n
 
   // set its preferred size.
   nsAutoString prefValue;
   prefValue.AppendInt(pref/aOnePixel);
   if (content->AttrValueIs(kNameSpaceID_None, attribute,
                            prefValue, eCaseMatters))
      return;
 
-  nsWeakFrame weakBox(aChildBox);
+  AutoWeakFrame weakBox(aChildBox);
   content->SetAttr(kNameSpaceID_None, attribute, prefValue, true);
   ENSURE_TRUE(weakBox.IsAlive());
   aState.PresShell()->FrameNeedsReflow(aChildBox, nsIPresShell::eStyleChange,
                                        NS_FRAME_IS_DIRTY);
 }
 
 
 void 
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -182,21 +182,21 @@ public:
         return shouldFlush;
     }
 
     virtual void ReflowCallbackCanceled() override
     {
         delete this;
     }
 
-    nsWeakFrame mWeakFrame;
+    WeakFrame mWeakFrame;
 };
 
 bool
-nsTextBoxFrame::UpdateAccesskey(nsWeakFrame& aWeakThis)
+nsTextBoxFrame::UpdateAccesskey(WeakFrame& aWeakThis)
 {
     nsAutoString accesskey;
     nsCOMPtr<nsIDOMXULLabelElement> labelElement = do_QueryInterface(mContent);
     NS_ENSURE_TRUE(aWeakThis.IsAlive(), false);
     if (labelElement) {
         // Accesskey may be stored on control.
         labelElement->GetAccessKey(accesskey);
         NS_ENSURE_TRUE(aWeakThis.IsAlive(), false);
--- a/layout/xul/nsTextBoxFrame.h
+++ b/layout/xul/nsTextBoxFrame.h
@@ -66,17 +66,17 @@ public:
 
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
 protected:
   friend class nsAsyncAccesskeyUpdate;
   friend class nsDisplayXULTextBox;
   // Should be called only by nsAsyncAccesskeyUpdate.
   // Returns true if accesskey was updated.
-  bool UpdateAccesskey(nsWeakFrame& aWeakThis);
+  bool UpdateAccesskey(WeakFrame& aWeakThis);
   void UpdateAccessTitle();
   void UpdateAccessIndex();
 
   // Recompute our title, ignoring the access key but taking into
   // account text-transform.
   void RecomputeTitle();
 
   // REVIEW: SORRY! Couldn't resist devirtualizing these
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -931,17 +931,17 @@ nsXULPopupManager::ShowPopupCallback(nsI
     // if the menu is on a menubar, use the menubar's listener instead
     nsMenuFrame* menuFrame = do_QueryFrame(aPopupFrame->GetParent());
     if (menuFrame) {
       item->SetOnMenuBar(menuFrame->IsOnMenuBar());
     }
   }
 
   // use a weak frame as the popup will set an open attribute if it is a menu
-  nsWeakFrame weakFrame(aPopupFrame);
+  AutoWeakFrame weakFrame(aPopupFrame);
   aPopupFrame->ShowPopup(aIsContextMenu);
   ENSURE_TRUE(weakFrame.IsAlive());
 
   // popups normally hide when an outside click occurs. Panels may use
   // the noautohide attribute to disable this behaviour. It is expected
   // that the application will hide these popups manually. The tooltip
   // listener will handle closing the tooltip also.
   if (aPopupFrame->IsNoAutoHide() || popupType == ePopupTypeTooltip) {
@@ -1182,17 +1182,17 @@ nsXULPopupManager::HidePopupCallback(nsI
         break;
       }
       item = item->GetParent();
     }
   }
 
   delete item;
 
-  nsWeakFrame weakFrame(aPopupFrame);
+  AutoWeakFrame weakFrame(aPopupFrame);
   aPopupFrame->HidePopup(aDeselectMenu, ePopupClosed);
   ENSURE_TRUE(weakFrame.IsAlive());
 
   // send the popuphidden event synchronously. This event has no default
   // behaviour.
   nsEventStatus status = nsEventStatus_eIgnore;
   WidgetMouseEvent event(true, eXULPopupHidden, nullptr,
                          WidgetMouseEvent::eReal);
@@ -1258,22 +1258,21 @@ nsXULPopupManager::HidePopupAfterDelay(n
   // is set to mTimerMenu, so it should be safe to keep a reference to it
   mTimerMenu = aPopup;
 }
 
 void
 nsXULPopupManager::HidePopupsInList(const nsTArray<nsMenuPopupFrame *> &aFrames)
 {
   // Create a weak frame list. This is done in a separate array with the
-  // right capacity predetermined, otherwise the array would get resized and
-  // move the weak frame pointers around.
-  nsTArray<nsWeakFrame> weakPopups(aFrames.Length());
+  // right capacity predetermined to avoid multiple allocations.
+  nsTArray<WeakFrame> weakPopups(aFrames.Length());
   uint32_t f;
   for (f = 0; f < aFrames.Length(); f++) {
-    nsWeakFrame* wframe = weakPopups.AppendElement();
+    WeakFrame* wframe = weakPopups.AppendElement();
     if (wframe)
       *wframe = aFrames[f];
   }
 
   for (f = 0; f < weakPopups.Length(); f++) {
     // check to ensure that the frame is still alive before hiding it.
     if (weakPopups[f].IsAlive()) {
       nsMenuPopupFrame* frame =
@@ -2897,17 +2896,17 @@ nsXULMenuCommandEvent::Run()
   // The order of the nsViewManager and nsIPresShell COM pointers is
   // important below.  We want the pres shell to get released before the
   // associated view manager on exit from this function.
   // See bug 54233.
   // XXXndeakin is this still needed?
 
   nsCOMPtr<nsIContent> popup;
   nsMenuFrame* menuFrame = do_QueryFrame(mMenu->GetPrimaryFrame());
-  nsWeakFrame weakFrame(menuFrame);
+  AutoWeakFrame weakFrame(menuFrame);
   if (menuFrame && mFlipChecked) {
     if (menuFrame->IsChecked()) {
       mMenu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, true);
     } else {
       mMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::checked,
                      NS_LITERAL_STRING("true"), true);
     }
   }
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -352,17 +352,17 @@ nsTreeBodyFrame::EnsureView()
       if (!mReflowCallbackPosted) {
         mReflowCallbackPosted = true;
         PresContext()->PresShell()->PostReflowCallback(this);
       }
       return;
     }
     nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject);
     if (box) {
-      nsWeakFrame weakFrame(this);
+      AutoWeakFrame weakFrame(this);
       nsCOMPtr<nsITreeView> treeView;
       mTreeBoxObject->GetView(getter_AddRefs(treeView));
       if (treeView && weakFrame.IsAlive()) {
         nsXPIDLString rowStr;
         box->GetProperty(u"topRow", getter_Copies(rowStr));
         nsAutoString rowStr2(rowStr);
         nsresult error;
         int32_t rowIndex = rowStr2.ToInteger(&error);
@@ -412,17 +412,17 @@ nsTreeBodyFrame::SetXULBounds(nsBoxLayou
   nsLeafBoxFrame::SetXULBounds(aBoxLayoutState, aRect, aRemoveOverflowArea);
 }
 
 
 bool
 nsTreeBodyFrame::ReflowFinished()
 {
   if (!mView) {
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     EnsureView();
     NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   }
   if (mView) {
     CalcInnerBox();
     ScrollParts parts = GetScrollParts();
     mHorzWidth = CalcHorzWidth(parts);
     if (!mHasFixedRowCount) {
@@ -464,17 +464,17 @@ nsTreeBodyFrame::ReflowCallbackCanceled(
 {
   mReflowCallbackPosted = false;
 }
 
 nsresult
 nsTreeBodyFrame::GetView(nsITreeView * *aView)
 {
   *aView = nullptr;
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   EnsureView();
   NS_ENSURE_STATE(weakFrame.IsAlive());
   NS_IF_ADDREF(*aView = mView);
   return NS_OK;
 }
 
 nsresult
 nsTreeBodyFrame::SetView(nsITreeView * aView)
@@ -517,17 +517,17 @@ nsTreeBodyFrame::SetView(nsITreeView * a
       sel->SetTree(mTreeBoxObject);
     }
     else {
       NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel));
       mView->SetSelection(sel);
     }
 
     // View, meet the tree.
-    nsWeakFrame weakFrame(this);
+    AutoWeakFrame weakFrame(this);
     mView->SetTree(mTreeBoxObject);
     NS_ENSURE_STATE(weakFrame.IsAlive());
     mView->GetRowCount(&mRowCount);
  
     if (!PresContext()->PresShell()->IsReflowLocked()) {
       // The scrollbar will need to be updated.
       FullScrollbarsUpdate(false);
     } else if (!mReflowCallbackPosted) {
@@ -847,17 +847,17 @@ nsTreeBodyFrame::ScrollParts nsTreeBodyF
   return result;
 }
 
 void
 nsTreeBodyFrame::UpdateScrollbars(const ScrollParts& aParts)
 {
   nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
 
   if (aParts.mVScrollbar) {
     nsAutoString curPos;
     curPos.AppendInt(mTopRowIndex*rowHeightAsPixels);
     aParts.mVScrollbarContent->
       SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true);
     // 'this' might be deleted here
   }
@@ -902,17 +902,17 @@ nsTreeBodyFrame::CheckOverflow(const Scr
         horizontalOverflowChanged = true;
       } else if (mHorizontalOverflow && bounds.width >= mHorzWidth) {
         mHorizontalOverflow = false;
         horizontalOverflowChanged = true;
       }
     }
   }
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
 
   RefPtr<nsPresContext> presContext = PresContext();
   nsCOMPtr<nsIPresShell> presShell = presContext->GetPresShell();
   nsCOMPtr<nsIContent> content = mContent;
 
   if (verticalOverflowChanged) {
     InternalScrollPortEvent event(true,
       mVerticalOverflow ? eScrollPortOverflow : eScrollPortUnderflow,
@@ -942,21 +942,21 @@ nsTreeBodyFrame::CheckOverflow(const Scr
   presShell->FlushPendingNotifications(FlushType::Layout);
   if (!weakFrame.IsAlive()) {
     return;
   }
   mCheckingOverflow = false;
 }
 
 void
-nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame)
+nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts, AutoWeakFrame& aWeakColumnsFrame)
 {
   if (mUpdateBatchNest || !mView)
     return;
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
 
   if (aParts.mVScrollbar) {
     // Do Vertical Scrollbar 
     nsAutoString maxposStr;
 
     nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
 
     int32_t size = rowHeightAsPixels * (mRowCount > mPageLength ? mRowCount - mPageLength : 0);
@@ -4360,17 +4360,17 @@ nsTreeBodyFrame::ScrollHorzInternal(cons
   if (aPosition > (mHorzWidth - bounds.width)) 
     aPosition = mHorzWidth - bounds.width;
 
   mHorzPosition = aPosition;
 
   Invalidate();
 
   // Update the column scroll view
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   aParts.mColumnsScrollFrame->ScrollTo(nsPoint(mHorzPosition, 0),
                                        nsIScrollableFrame::INSTANT);
   if (!weakFrame.IsAlive()) {
     return NS_ERROR_FAILURE;
   }
   // And fire off an event about it all
   PostScrollEvent();
   return NS_OK;
@@ -4412,17 +4412,17 @@ nsTreeBodyFrame::RepeatButtonScroll(nsSc
   int32_t direction = 0;
   if (increment < 0) {
     direction = -1;
   } else if (increment > 0) {
     direction = 1;
   }
   bool isHorizontal = aScrollbar->IsXULHorizontal();
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
   if (isHorizontal) {
     int32_t curpos = aScrollbar->MoveToNewPosition();
     if (weakFrame.IsAlive()) {
       ScrollHorzInternal(parts, curpos);
     }
   } else {
     ScrollToRowInternal(parts, mTopRowIndex+direction);
   }
@@ -4440,17 +4440,17 @@ nsTreeBodyFrame::ThumbMoved(nsScrollbarF
                             nscoord aOldPos,
                             nscoord aNewPos)
 {
   ScrollParts parts = GetScrollParts();
   
   if (aOldPos == aNewPos)
     return;
 
-  nsWeakFrame weakFrame(this);
+  AutoWeakFrame weakFrame(this);
 
   // Vertical Scrollbar 
   if (parts.mVScrollbar == aScrollbar) {
     nscoord rh = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
     nscoord newIndex = nsPresContext::AppUnitsToIntCSSPixels(aNewPos);
     nscoord newrow = newIndex/rh;
     ScrollInternal(parts, newrow);
   // Horizontal Scrollbar
@@ -4901,25 +4901,25 @@ public:
     if (mFrame.IsAlive()) {
       nsTreeBodyFrame* tree = static_cast<nsTreeBodyFrame*>(mFrame.GetFrame());
       nsTreeBodyFrame::ScrollParts parts = tree->GetScrollParts();
       tree->CheckOverflow(parts);
     }
     return NS_OK;
   }
 private:
-  nsWeakFrame mFrame;
+  WeakFrame mFrame;
 };
 
 bool
 nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation)
 {
   ScrollParts parts = GetScrollParts();
-  nsWeakFrame weakFrame(this);
-  nsWeakFrame weakColumnsFrame(parts.mColumnsFrame);
+  AutoWeakFrame weakFrame(this);
+  AutoWeakFrame weakColumnsFrame(parts.mColumnsFrame);
   UpdateScrollbars(parts);
   NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
   if (aNeedsFullInvalidation) {
     Invalidate();
   }
   InvalidateScrollbars(parts, weakColumnsFrame);
   NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
 
--- a/layout/xul/tree/nsTreeBodyFrame.h
+++ b/layout/xul/tree/nsTreeBodyFrame.h
@@ -369,17 +369,17 @@ protected:
   // scrollable frame (with associated content and scrollable view). These
   // are all volatile and should not be retained.
   ScrollParts GetScrollParts();
 
   // Update the curpos of the scrollbar.
   void UpdateScrollbars(const ScrollParts& aParts);
 
   // Update the maxpos of the scrollbar.
-  void InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame);
+  void InvalidateScrollbars(const ScrollParts& aParts, AutoWeakFrame& aWeakColumnsFrame);
 
   // Check overflow and generate events.
   void CheckOverflow(const ScrollParts& aParts);
 
   // Calls UpdateScrollbars, Invalidate aNeedsFullInvalidation if true,
   // InvalidateScrollbars and finally CheckOverflow.
   // returns true if the frame is still alive after the method call.
   bool FullScrollbarsUpdate(bool aNeedsFullInvalidation);
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -917,8 +917,10 @@ pref("dom.presentation.receiver.enabled"
 pref("dom.audiochannel.audioCompeting", true);
 pref("dom.audiochannel.mediaControl", true);
 
 // Space separated list of URLS that are allowed to send objects (instead of
 // only strings) through webchannels. This list is duplicated in browser/app/profile/firefox.js
 pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
 
 pref("media.openUnsupportedTypeWithExternalApp", true);
+
+pref("dom.keyboardevent.dispatch_during_composition", true);
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -57,16 +57,17 @@ import org.mozilla.gecko.home.HomePager.
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.home.HomeScreen;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.icons.Icons;
 import org.mozilla.gecko.javaaddons.JavaAddonManager;
 import org.mozilla.gecko.media.VideoPlayer;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuItem;
+import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
 import org.mozilla.gecko.delegates.BookmarkStateChangeDelegate;
@@ -1741,16 +1742,20 @@ public class BrowserApp extends GeckoApp
         mBrowserToolbar.refresh();
     }
 
     @Override // BundleEventListener
     public void handleMessage(final String event, final GeckoBundle message,
                               final EventCallback callback) {
         switch (event) {
             case "Gecko:Ready":
+                if (!GeckoLoader.neonCompatible()) {
+                    conditionallyNotifyEOL();
+                }
+
                 EventDispatcher.getInstance().registerUiThreadListener(this, "Gecko:DelayedStartup");
 
                 // Handle this message in GeckoApp, but also enable the Settings
                 // menuitem, which is specific to BrowserApp.
                 super.handleMessage(event, message, callback);
 
                 final Menu menu = mMenu;
                 ThreadUtils.postToUiThread(new Runnable() {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
@@ -88,64 +88,69 @@ final class GeckoEditable extends JNIObj
     private static final int IME_RANGE_LINE_DOUBLE = 4;
     private static final int IME_RANGE_LINE_WAVY = 5;
 
     private static final int IME_RANGE_UNDERLINE = 1;
     private static final int IME_RANGE_FORECOLOR = 2;
     private static final int IME_RANGE_BACKCOLOR = 4;
     private static final int IME_RANGE_LINECOLOR = 8;
 
-    @WrapForJNI(dispatchTo = "proxy")
+    @WrapForJNI(dispatchTo = "proxy") // Dispatch a UI-activity event through proxy.
     private native void onKeyEvent(int action, int keyCode, int scanCode, int metaState,
-                                   long time, int unicodeChar, int baseUnicodeChar,
-                                   int domPrintableKeyValue, int repeatCount, int flags,
-                                   boolean isSynthesizedImeKey, KeyEvent event);
+                                   int keyPressMetaState, long time, int domPrintableKeyValue,
+                                   int repeatCount, int flags, boolean isSynthesizedImeKey,
+                                   KeyEvent event);
 
     private void onKeyEvent(KeyEvent event, int action, int savedMetaState,
                             boolean isSynthesizedImeKey) {
         // Use a separate action argument so we can override the key's original action,
         // e.g. change ACTION_MULTIPLE to ACTION_DOWN. That way we don't have to allocate
         // a new key event just to change its action field.
         //
         // Normally we expect event.getMetaState() to reflect the current meta-state; however,
         // some software-generated key events may not have event.getMetaState() set, e.g. key
         // events from Swype. Therefore, it's necessary to combine the key's meta-states
         // with the meta-states that we keep separately in KeyListener
         final int metaState = event.getMetaState() | savedMetaState;
         final int unmodifiedMetaState = metaState &
                 ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK);
+
         final int unicodeChar = event.getUnicodeChar(metaState);
+        final int unmodifiedUnicodeChar = event.getUnicodeChar(unmodifiedMetaState);
         final int domPrintableKeyValue =
                 unicodeChar >= ' '               ? unicodeChar :
-                unmodifiedMetaState != metaState ? event.getUnicodeChar(unmodifiedMetaState) :
-                                                   0;
+                unmodifiedMetaState != metaState ? unmodifiedUnicodeChar : 0;
+
+        // If a modifier (e.g. meta key) caused a different character to be entered, we
+        // drop that modifier from the metastate for the generated keypress event.
+        final int keyPressMetaState = (unicodeChar >= ' ' &&
+                unicodeChar != unmodifiedUnicodeChar) ? unmodifiedMetaState : metaState;
+
         onKeyEvent(action, event.getKeyCode(), event.getScanCode(),
-                   metaState, event.getEventTime(), unicodeChar,
-                   // e.g. for Ctrl+A, Android returns 0 for unicodeChar,
-                   // but Gecko expects 'a', so we return that in baseUnicodeChar.
-                   event.getUnicodeChar(0), domPrintableKeyValue, event.getRepeatCount(),
-                   event.getFlags(), isSynthesizedImeKey, event);
+                   metaState, keyPressMetaState, event.getEventTime(),
+                   domPrintableKeyValue, event.getRepeatCount(), event.getFlags(),
+                   isSynthesizedImeKey, event);
     }
 
-    @WrapForJNI(dispatchTo = "proxy")
+    @WrapForJNI(dispatchTo = "gecko")
     private native void onImeSynchronize();
 
-    @WrapForJNI(dispatchTo = "proxy")
+    @WrapForJNI(dispatchTo = "proxy") // Dispatch a UI-activity event through proxy.
     private native void onImeReplaceText(int start, int end, String text);
 
-    @WrapForJNI(dispatchTo = "proxy")
+    @WrapForJNI(dispatchTo = "gecko")
     private native void onImeAddCompositionRange(int start, int end, int rangeType,
                                                  int rangeStyles, int rangeLineStyle,
                                                  boolean rangeBoldLine, int rangeForeColor,
                                                  int rangeBackColor, int rangeLineColor);
 
-    @WrapForJNI(dispatchTo = "proxy")
+    @WrapForJNI(dispatchTo = "proxy") // Dispatch a UI-activity event through proxy.
     private native void onImeUpdateComposition(int start, int end);
 
-    @WrapForJNI(dispatchTo = "proxy")
+    @WrapForJNI(dispatchTo = "gecko")
     private native void onImeRequestCursorUpdates(int requestMode);
 
     /**
      * Class that encapsulates asynchronous text editing. There are two copies of the
      * text, a current copy and a shadow copy. Both can be modified independently through
      * the current*** and shadow*** methods, respectively. The current copy can only be
      * modified on the Gecko side and reflects the authoritative version of the text. The
      * shadow copy can only be modified on the IC side and reflects what we think the
@@ -577,17 +582,17 @@ final class GeckoEditable extends JNIObj
                 Editable.class.getClassLoader(),
                 PROXY_INTERFACES, this);
 
         mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler();
 
         onViewChange(v);
     }
 
-    @WrapForJNI(dispatchTo = "proxy") @Override
+    @WrapForJNI(dispatchTo = "gecko") @Override
     protected native void disposeNative();
 
     @WrapForJNI(calledFrom = "gecko")
     private void onViewChange(final GeckoView v) {
         if (DEBUG) {
             // Called by nsWindow.
             ThreadUtils.assertOnGeckoThread();
             Log.d(LOGTAG, "onViewChange(" + v + ")");
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
@@ -547,9 +547,10 @@ public final class GeckoLoader {
     // These methods are implemented in mozglue/android/nsGeckoUtils.cpp
     private static native void putenv(String map);
 
     // These methods are implemented in mozglue/android/APKOpen.cpp
     public static native void nativeRun(String[] args, int crashFd, int ipcFd);
     private static native void loadGeckoLibsNative(String apkName);
     private static native void loadSQLiteLibsNative(String apkName);
     private static native void loadNSSLibsNative(String apkName);
+    public static native boolean neonCompatible();
 }
--- a/modules/fdlibm/README.mozilla
+++ b/modules/fdlibm/README.mozilla
@@ -6,12 +6,12 @@ Upstream code can be viewed at
 
 Each file is downloaded separately, as cloning whole repository takes so much
 resources.
 
 The in-tree copy is updated by running
   sh update.sh
 from within the modules/fdlibm directory.
 
-Current version: [commit f2287da07ac7a26ac08745cac66eec82ab9ba384].
+Current version: [commit f2287da07ac7a26ac08745cac66eec82ab9ba384 (2016-09-04T12:01:32Z)].
 
 patches 01-14 fixes files to be usable within mozilla-central tree.
 See https://bugzilla.mozilla.org/show_bug.cgi?id=933257
--- a/modules/fdlibm/update.sh
+++ b/modules/fdlibm/update.sh
@@ -6,30 +6,35 @@
 set -e
 
 API_BASE_URL=https://api.github.com/repos/freebsd/freebsd
 
 get_commit() {
     curl -s "${API_BASE_URL}/commits?path=lib/msun/src&per_page=1" \
         | python -c 'import json, sys; print(json.loads(sys.stdin.read())[0]["sha"])'
 }
+get_date() {
+    curl -s "${API_BASE_URL}/commits?path=lib/msun/src&per_page=1" \
+        | python -c 'import json, sys; print(json.loads(sys.stdin.read())[0]["commit"]["committer"]["date"])'
+}
 
 mv ./src/moz.build ./src_moz.build
 rm -rf src
 BEFORE_COMMIT=$(get_commit)
 sh ./import.sh
 mv ./src_moz.build ./src/moz.build
 COMMIT=$(get_commit)
+COMMITDATE=$(get_date)
 if [ ${BEFORE_COMMIT} != ${COMMIT} ]; then
     echo "Latest commit is changed during import.  Please run again."
     exit 1
 fi
 for FILE in $(ls patches/*.patch | sort); do
     patch -p3 < ${FILE}
 done
 hg add src
 
-perl -p -i -e "s/\[commit [0-9a-f]{40}\]/[commit ${COMMIT}]/" README.mozilla
+perl -p -i -e "s/\[commit [0-9a-f]{40} \(.{1,100}\)\]/[commit ${COMMIT} (${COMMITDATE})]/" README.mozilla
 
 echo "###"
 echo "### Updated fdlibm/src to ${COMMIT}."
 echo "### Remember to verify and commit the changes to source control!"
 echo "###"
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -28,16 +28,17 @@
 #include <sys/resource.h>
 #include <sys/prctl.h>
 #include "sqlite3.h"
 #include "SQLiteBridge.h"
 #include "NSSBridge.h"
 #include "ElfLoader.h"
 #include "application.ini.h"
 
+#include "mozilla/arm.h"
 #include "mozilla/Bootstrap.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "XREChildData.h"
 
 /* Android headers don't define RUSAGE_THREAD */
 #ifndef RUSAGE_THREAD
 #define RUSAGE_THREAD 1
@@ -468,8 +469,13 @@ ChildProcessInit(int argc, char* argv[])
   }
 
   gBootstrap->XRE_SetProcessType(argv[--argc]);
 
   XREChildData childData;
   return NS_FAILED(gBootstrap->XRE_InitChildProcess(argc, argv, &childData));
 }
 
+extern "C" APKOPEN_EXPORT jboolean MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_neonCompatible(JNIEnv *jenv, jclass jc)
+{
+  return mozilla::supports_neon();
+}
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -440,17 +440,17 @@ private:
         if (NS_FAILED(rv)) {
           if (mVisitEntries) {
             // both onStorageInfo and onCompleted are expected
             NS_DispatchToMainThread(this);
           }
           return NS_DispatchToMainThread(this);
         }
 
-        mSize = size << 10;
+        mSize = static_cast<uint64_t>(size) << 10;
 
         // Invoke onCacheStorageInfo with valid information.
         NS_DispatchToMainThread(this);
 
         if (!mVisitEntries) {
           return NS_OK; // done
         }
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -3703,19 +3703,21 @@ nsHttpChannel::OpenCacheEntry(bool isHtt
         }
 
         mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
         mCacheQueueSizeWhenOpen = CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
 
         if (!mCacheOpenDelay) {
             rv = cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, this);
         } else {
-            mCacheOpenRunnable = NS_NewRunnableFunction([openURI, extension, cacheEntryOpenFlags, cacheStorage, this] () -> void {
-                cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, this);
-            });
+            // We pass `this` explicitly as a parameter due to the raw pointer
+            // to refcounted object in lambda analysis.
+            mCacheOpenFunc = [openURI, extension, cacheEntryOpenFlags, cacheStorage] (nsHttpChannel* self) -> void {
+                cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, self);
+            };
 
             mCacheOpenTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
             // calls nsHttpChannel::Notify after `mCacheOpenDelay` milliseconds
             mCacheOpenTimer->InitWithCallback(this, mCacheOpenDelay, nsITimer::TYPE_ONE_SHOT);
 
         }
         NS_ENSURE_SUCCESS(rv, rv);
     }
@@ -8669,31 +8671,32 @@ NS_IMETHODIMP
 nsHttpChannel::Test_triggerDelayedOpenCacheEntry()
 {
     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
     nsresult rv;
     if (!mCacheOpenDelay) {
         // No delay was set.
         return NS_ERROR_NOT_AVAILABLE;
     }
-    if (!mCacheOpenRunnable) {
+    if (!mCacheOpenFunc) {
         // There should be a runnable.
         return NS_ERROR_FAILURE;
     }
     if (mCacheOpenTimer) {
         rv = mCacheOpenTimer->Cancel();
         if (NS_FAILED(rv)) {
             return rv;
         }
         mCacheOpenTimer = nullptr;
     }
-    nsCOMPtr<nsIRunnable> runnable;
-    mCacheOpenRunnable.swap(runnable);
     mCacheOpenDelay = 0;
-    runnable->Run();
+    // Avoid re-entrancy issues by nulling our mCacheOpenFunc before calling it.
+    std::function<void(nsHttpChannel*)> cacheOpenFunc = nullptr;
+    std::swap(cacheOpenFunc, mCacheOpenFunc);
+    cacheOpenFunc(this);
 
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::TriggerNetwork(int32_t aTimeout)
 {
     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -616,17 +616,17 @@ private:
     RefPtr<ADivertableParentChannel> mParentChannel;
 
     // True if the channel is reading from cache.
     Atomic<bool> mIsReadingFromCache;
 
     // These next members are only used in unit tests to delay the call to
     // cache->AsyncOpenURI in order to race the cache with the network.
     nsCOMPtr<nsITimer> mCacheOpenTimer;
-    nsCOMPtr<nsIRunnable> mCacheOpenRunnable;
+    std::function<void(nsHttpChannel*)> mCacheOpenFunc;
     uint32_t mCacheOpenDelay = 0;
 
     // We need to remember which is the source of the response we are using.
     enum {
         RESPONSE_PENDING,           // response is pending
         RESPONSE_FROM_CACHE,        // response coming from cache. no network.
         RESPONSE_FROM_NETWORK,      // response coming from the network
     } mFirstResponseSource = RESPONSE_PENDING;
--- a/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
+++ b/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
@@ -22,17 +22,17 @@ nsHttpChunkedDecoder::HandleChunkedConte
                                            uint32_t count,
                                            uint32_t *contentRead,
                                            uint32_t *contentRemaining)
 {
     LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count));
 
     *contentRead = 0;
 
-    // from RFC2617 section 3.6.1, the chunked transfer coding is defined as:
+    // from RFC2616 section 3.6.1, the chunked transfer coding is defined as:
     //
     //   Chunked-Body    = *chunk
     //                     last-chunk
     //                     trailer
     //                     CRLF
     //   chunk           = chunk-size [ chunk-extension ] CRLF
     //                     chunk-data CRLF
     //   chunk-size      = 1*HEX
--- a/taskcluster/taskgraph/transforms/beetmover.py
+++ b/taskcluster/taskgraph/transforms/beetmover.py
@@ -44,16 +44,17 @@ from voluptuous import Schema, Any, Requ
     "balrog_props.json",
 ]
 _DESKTOP_UPSTREAM_ARTIFACTS_SIGNED_L10N = [
     "target.complete.mar",
 ]
 _MOBILE_UPSTREAM_ARTIFACTS_UNSIGNED_EN_US = [
     "en-US/target.common.tests.zip",
     "en-US/target.cppunittest.tests.zip",
+    "en-US/target.crashreporter-symbols.zip",
     "en-US/target.json",
     "en-US/target.mochitest.tests.zip",
     "en-US/target.mozinfo.json",
     "en-US/target.reftest.tests.zip",
     "en-US/target.talos.tests.zip",
     "en-US/target.test_packages.json",
     "en-US/target.txt",
     "en-US/target.web-platform.tests.zip",
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -11,17 +11,17 @@ complexities of worker implementations, 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import json
 import time
 
 from taskgraph.util.treeherder import split_symbol
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.schema import validate_schema
-from taskgraph.util.scriptworker import get_release_build_number
+from taskgraph.util.scriptworker import get_release_config
 from voluptuous import Schema, Any, Required, Optional, Extra
 
 from .gecko_v2_whitelist import JOB_NAME_WHITELIST, JOB_NAME_WHITELIST_ERROR
 
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
@@ -565,27 +565,27 @@ def build_scriptworker_signing_payload(c
         'maxRunTime': worker['max-run-time'],
         'upstreamArtifacts':  worker['upstream-artifacts']
     }
 
 
 @payload_builder('beetmover')
 def build_beetmover_payload(config, task, task_def):
     worker = task['worker']
-    build_number = get_release_build_number(config)
+    release_config = get_release_config(config)
 
     task_def['payload'] = {
         'maxRunTime': worker['max-run-time'],
         'upload_date': config.params['build_date'],
         'upstreamArtifacts':  worker['upstream-artifacts']
     }
     if worker.get('locale'):
         task_def['payload']['locale'] = worker['locale']
-    if build_number:
-        task_def['payload']['build_number'] = build_number
+    if release_config:
+        task_def['payload'].update(release_config)
 
 
 @payload_builder('balrog')
 def build_balrog_payload(config, task, task_def):
     worker = task['worker']
 
     task_def['payload'] = {
         'upstreamArtifacts':  worker['upstream-artifacts']
--- a/taskcluster/taskgraph/util/scriptworker.py
+++ b/taskcluster/taskgraph/util/scriptworker.py
@@ -13,16 +13,20 @@ In the future, we may adjust scopes by o
 scopes for `push-to-candidates` rather than `push-to-releases`, even if both
 happen on mozilla-beta and mozilla-release.
 """
 from __future__ import absolute_import, print_function, unicode_literals
 import functools
 import os
 
 
+# constants {{{1
+GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..'))
+VERSION_PATH = os.path.join(GECKO, "browser", "config", "version_display.txt")
+
 """Map signing scope aliases to sets of projects.
 
 Currently m-c and m-a use nightly signing; m-b and m-r use release signing.
 We will need to add esr support at some point. Eventually we want to add
 nuance so certain m-b and m-r tasks use dep or nightly signing, and we only
 release sign when we have a signed-off set of candidate builds.  This current
 approach works for now, though.
 
@@ -228,29 +232,35 @@ get_beetmover_action_scope = functools.p
 
 get_balrog_server_scope = functools.partial(
     get_scope_from_project,
     BALROG_SCOPE_ALIAS_TO_PROJECT,
     BALROG_SERVER_SCOPES
 )
 
 
-# build_number {{{1
-def get_release_build_number(config):
-    """Get the build number for a release task.
+# release_config {{{1
+def get_release_config(config):
+    """Get the build number and version for a release task.
 
     Currently only applies to beetmover tasks.
 
     Args:
         config (dict): the task config that defines the target task method.
 
     Raises:
         ValueError: if a release graph doesn't define a valid
             `os.environ['BUILD_NUMBER']`
 
     Returns:
-        int: the build number, if applicable.
+        dict: containing both `build_number` and `version`.  This can be used to
+            update `task.payload`.
     """
+    release_config = {}
     if config.params['target_tasks_method'] in BEETMOVER_RELEASE_TARGET_TASKS:
         build_number = str(os.environ.get("BUILD_NUMBER", ""))
         if not build_number.isdigit():
             raise ValueError("Release graphs must specify `BUILD_NUMBER` in the environment!")
-        return int(build_number)
+        release_config['build_number'] = int(build_number)
+        with open(VERSION_PATH, "r") as fh:
+            version = fh.readline().rstrip()
+        release_config['version'] = version
+    return release_config
--- a/toolkit/components/url-classifier/tests/mochitest/chrome.ini
+++ b/toolkit/components/url-classifier/tests/mochitest/chrome.ini
@@ -21,9 +21,8 @@ tags = trackingprotection
 [test_trackingprotection_bug1157081.html]
 tags = trackingprotection
 [test_trackingprotection_whitelist.html]
 tags = trackingprotection
 [test_safebrowsing_bug1272239.html]
 [test_donottrack.html]
 [test_classifier_changetablepref.html]
 [test_reporturl.html]
-skip-if = true # Bug 1341514
--- a/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
@@ -89,16 +89,17 @@ function testOnWindow(aTestData, aCallba
       ok(notification, "Notification box should be displayed");
 
       let buttons = notification.getElementsByTagName("button");
       let button = buttons[1];
       if (aTestData.provider != "google" && aTestData.provider != "google4") {
         is(button, undefined, "Report button should not be showed");
         win.close();
         resolve();
+        return;
       }
 
       button.click();
 
       let newTabBrowser = win.gBrowser.selectedTab.linkedBrowser;
       yield BrowserTestUtils.browserLoaded(newTabBrowser);
 
       aCallback(newTabBrowser);
@@ -153,21 +154,23 @@ var testDatas = [
   { topUrl: "http://itisaphishingsite.org/phishing.html",
     testUrl: "itisaphishingsite.org/phishing.html",
     list: "mochi1-phish-simple",
     provider: "google",
     blockCreater : createBlockedPage,
     expectedReportUri: "http://itisaphishingsite.org/phishing.html"
   },
 
-  // Non-google provider, no report button is showed
+  // Non-google provider, no report button is showed.
+  // Test provider needs a valid update URL (mozilla for example) otherwise
+  // the updates inserting the test data will fail.
   { topUrl: "http://fakeitisaphishingsite.org/phishing.html",
     testUrl: "fakeitisaphishingsite.org/phishing.html",
     list: "fake-phish-simple",
-    provider: "fake",
+    provider: "mozilla",
     blockCreater : createBlockedPage
   },
 
   // Iframe case:
   // A top level page at
   // http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-iframe
   // contains an iframe to http://phishing.example.com/test.html (blocked).
 
--- a/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
@@ -41,34 +41,10 @@ function run_test() {
              Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
              scope.TelemetryController.testSetup();
            },
            function(mdump, extra) {
              do_check_eq(extra.TestKey, "TestValue");
              do_check_eq(extra["\u2665"], "\u{1F4A9}");
              do_check_eq(extra.Notes, "JunkMoreJunk");
              do_check_true("TelemetrySessionId" in extra);
-             Assert.ok(
-              "TelemetryServerURL" in extra,
-              "The TelemetryServerURL field is omitted when telemetry is off"
-             );
            });
-
-  do_crash(function() {
-    // Enable telemetry
-    let prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                          .getService(Components.interfaces.nsIPrefBranch);
-
-    // Turn on telemetry and specify a telemetry server
-    prefs.setCharPref("toolkit.telemetry.server", "http://a.telemetry.server");
-    prefs.setBoolPref("toolkit.telemetry.enabled", true);
-
-    // TelemetrySession setup will trigger the session annotation
-    let scope = {};
-    Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
-    scope.TelemetryController.testSetup();
-  }, function(mdump, extra) {
-    Assert.ok("TelemetryServerURL" in extra,
-              "The TelemetryServerURL field is present in the extra file");
-    Assert.equal(extra.TelemetryServerURL, "http://a.telemetry.server",
-                  "The TelemetryServerURL field is properly set");
-  });
 }
--- a/tools/profiler/core/ProfileBuffer.h
+++ b/tools/profiler/core/ProfileBuffer.h
@@ -7,24 +7,22 @@
 #define MOZ_PROFILE_BUFFER_H
 
 #include "ProfileBufferEntry.h"
 #include "platform.h"
 #include "ProfileJSONWriter.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/RefCounted.h"
 
-class ProfileBuffer : public mozilla::RefCounted<ProfileBuffer>
+class ProfileBuffer final
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ProfileBuffer)
-
   explicit ProfileBuffer(int aEntrySize);
 
-  virtual ~ProfileBuffer();
+  ~ProfileBuffer();
 
   void addTag(const ProfileBufferEntry& aTag);
   void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            JSContext* cx, UniqueStacks& aUniqueStacks);
   void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            UniqueStacks& aUniqueStacks);
   void DuplicateLastSample(int aThreadId, const mozilla::TimeStamp& aStartTime);
 
--- a/tools/profiler/core/ProfilerBacktrace.cpp
+++ b/tools/profiler/core/ProfilerBacktrace.cpp
@@ -2,30 +2,34 @@
 /* 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 "ProfilerBacktrace.h"
 
 #include "ProfileJSONWriter.h"
-#include "SyncProfile.h"
+#include "ThreadInfo.h"
 
-ProfilerBacktrace::ProfilerBacktrace(SyncProfile* aProfile)
-  : mProfile(aProfile)
+ProfilerBacktrace::ProfilerBacktrace(ProfileBuffer* aBuffer,
+                                     ThreadInfo* aThreadInfo)
+  : mBuffer(aBuffer)
+  , mThreadInfo(aThreadInfo)
 {
   MOZ_COUNT_CTOR(ProfilerBacktrace);
-  MOZ_ASSERT(aProfile);
+  MOZ_ASSERT(aThreadInfo && aThreadInfo->HasProfile());
 }
 
 ProfilerBacktrace::~ProfilerBacktrace()
 {
   MOZ_COUNT_DTOR(ProfilerBacktrace);
-  delete mProfile;
+  delete mBuffer;
+  delete mThreadInfo;
 }
 
 void
 ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
                               UniqueStacks& aUniqueStacks)
 {
-  mozilla::MutexAutoLock lock(mProfile->GetMutex());
-  mProfile->StreamJSON(aWriter, aUniqueStacks);
+  mozilla::MutexAutoLock lock(mThreadInfo->GetMutex());
+  mThreadInfo->StreamSamplesAndMarkers(mBuffer, aWriter, /* aSinceTime */ 0,
+                                       aUniqueStacks);
 }
--- a/tools/profiler/core/ProfilerMarkers.cpp
+++ b/tools/profiler/core/ProfilerMarkers.cpp
@@ -1,17 +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 "GeckoProfiler.h"
 #include "ProfilerBacktrace.h"
 #include "ProfilerMarkers.h"
-#include "SyncProfile.h"
 #include "gfxASurface.h"
 #include "Layers.h"
 #include "mozilla/Sprintf.h"
 
 ProfilerMarkerPayload::ProfilerMarkerPayload(UniqueProfilerBacktrace aStack)
   : mStack(mozilla::Move(aStack))
 {}
 
deleted file mode 100644
--- a/tools/profiler/core/SyncProfile.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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 "SyncProfile.h"
-
-SyncProfile::SyncProfile(int aThreadId, PseudoStack* aStack)
-  : ThreadInfo("SyncProfile", aThreadId, /* isMainThread = */ false, aStack,
-               /* stackTop = */ nullptr)
-{
-  MOZ_COUNT_CTOR(SyncProfile);
-  SetProfile(new ProfileBuffer(GET_BACKTRACE_DEFAULT_ENTRIES));
-}
-
-SyncProfile::~SyncProfile()
-{
-  MOZ_COUNT_DTOR(SyncProfile);
-}
-
-// SyncProfiles' stacks are deduplicated in the context of the containing
-// profile in which the backtrace is as a marker payload.
-void
-SyncProfile::StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
-{
-  ThreadInfo::StreamSamplesAndMarkers(aWriter, /* aSinceTime = */ 0, aUniqueStacks);
-}
deleted file mode 100644
--- a/tools/profiler/core/SyncProfile.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; 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/. */
-
-#ifndef __SYNCPROFILE_H
-#define __SYNCPROFILE_H
-
-#include "ThreadInfo.h"
-
-class SyncProfile : public ThreadInfo
-{
-public:
-  SyncProfile(int aThreadId, PseudoStack* aStack);
-  ~SyncProfile();
-
-  // SyncProfiles' stacks are deduplicated in the context of the containing
-  // profile in which the backtrace is as a marker payload.
-  void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
-
-private:
-  friend class ProfilerBacktrace;
-};
-
-#endif // __SYNCPROFILE_H
-
--- a/tools/profiler/core/ThreadInfo.cpp
+++ b/tools/profiler/core/ThreadInfo.cpp
@@ -17,16 +17,17 @@ ThreadInfo::ThreadInfo(const char* aName
                        void* aStackTop)
   : mName(strdup(aName))
   , mThreadId(aThreadId)
   , mIsMainThread(aIsMainThread)
   , mPseudoStack(aPseudoStack)
   , mPlatformData(AllocPlatformData(aThreadId))
   , mStackTop(aStackTop)
   , mPendingDelete(false)
+  , mHasProfile(false)
   , mMutex(MakeUnique<mozilla::Mutex>("ThreadInfo::mMutex"))
 {
   MOZ_COUNT_CTOR(ThreadInfo);
   mThread = NS_GetCurrentThread();
 
   // We don't have to guess on mac
 #if defined(GP_OS_darwin)
   pthread_t self = pthread_self();
@@ -59,37 +60,27 @@ ThreadInfo::CanInvokeJS() const
   }
   bool result;
   mozilla::DebugOnly<nsresult> rv = mThread->GetCanInvokeJS(&result);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   return result;
 }
 
 void
-ThreadInfo::addTag(const ProfileBufferEntry& aTag)
-{
-  mBuffer->addTag(aTag);
-}
-
-void
-ThreadInfo::addStoredMarker(ProfilerMarker* aStoredMarker) {
-  mBuffer->addStoredMarker(aStoredMarker);
-}
-
-void
-ThreadInfo::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
+ThreadInfo::StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
+                       double aSinceTime)
 {
   // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
   if (!mUniqueStacks.isSome()) {
     mUniqueStacks.emplace(mPseudoStack->mContext);
   }
 
   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   {
-    StreamSamplesAndMarkers(aWriter, aSinceTime, *mUniqueStacks);
+    StreamSamplesAndMarkers(aBuffer, aWriter, aSinceTime, *mUniqueStacks);
 
     aWriter.StartObjectProperty("stackTable");
     {
       {
         JSONSchemaWriter schema(aWriter);
         schema.WriteField("prefix");
         schema.WriteField("frame");
       }
@@ -128,17 +119,18 @@ ThreadInfo::StreamJSON(SpliceableJSONWri
     aWriter.EndArray();
   }
   aWriter.End();
 
   mUniqueStacks.reset();
 }
 
 void
-ThreadInfo::StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter,
+ThreadInfo::StreamSamplesAndMarkers(ProfileBuffer* aBuffer,
+                                    SpliceableJSONWriter& aWriter,
                                     double aSinceTime,
                                     UniqueStacks& aUniqueStacks)
 {
   aWriter.StringProperty("processType",
                          XRE_ChildProcessTypeToString(XRE_GetProcessType()));
 
   aWriter.StringProperty("name", Name());
   aWriter.IntProperty("tid", static_cast<int>(mThreadId));
@@ -160,17 +152,17 @@ ThreadInfo::StreamSamplesAndMarkers(Spli
       if (mSavedStreamedSamples) {
         // We would only have saved streamed samples during shutdown
         // streaming, which cares about dumping the entire buffer, and thus
         // should have passed in 0 for aSinceTime.
         MOZ_ASSERT(aSinceTime == 0);
         aWriter.Splice(mSavedStreamedSamples.get());
         mSavedStreamedSamples.reset();
       }
-      mBuffer->StreamSamplesToJSON(aWriter, mThreadId, aSinceTime,
+      aBuffer->StreamSamplesToJSON(aWriter, mThreadId, aSinceTime,
                                    mPseudoStack->mContext, aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 
   aWriter.StartObjectProperty("markers");
   {
@@ -183,25 +175,26 @@ ThreadInfo::StreamSamplesAndMarkers(Spli
 
     aWriter.StartArrayProperty("data");
     {
       if (mSavedStreamedMarkers) {
         MOZ_ASSERT(aSinceTime == 0);
         aWriter.Splice(mSavedStreamedMarkers.get());
         mSavedStreamedMarkers.reset();
       }
-      mBuffer->StreamMarkersToJSON(aWriter, mThreadId, aSinceTime, aUniqueStacks);
+      aBuffer->StreamMarkersToJSON(aWriter, mThreadId, aSinceTime,
+                                   aUniqueStacks);
     }
     aWriter.EndArray();
   }
   aWriter.EndObject();
 }
 
 void
-ThreadInfo::FlushSamplesAndMarkers()
+ThreadInfo::FlushSamplesAndMarkers(ProfileBuffer* aBuffer)
 {
   // This function is used to serialize the current buffer just before
   // JSContext destruction.
   MOZ_ASSERT(mPseudoStack->mContext);
 
   // Unlike StreamJSObject, do not surround the samples in brackets by calling
   // aWriter.{Start,End}BareList. The result string will be a comma-separated
   // list of JSON object literals that will prepended by StreamJSObject into
@@ -210,48 +203,49 @@ ThreadInfo::FlushSamplesAndMarkers()
   // Note that the UniqueStacks instance is persisted so that the frame-index
   // mapping is stable across JS shutdown.
   mUniqueStacks.emplace(mPseudoStack->mContext);
 
   {
     SpliceableChunkedJSONWriter b;
     b.StartBareList();
     {
-      mBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
+      aBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
                                    mPseudoStack->mContext, *mUniqueStacks);
     }
     b.EndBareList();
     mSavedStreamedSamples = b.WriteFunc()->CopyData();
   }
 
   {
     SpliceableChunkedJSONWriter b;
     b.StartBareList();
     {
-      mBuffer->StreamMarkersToJSON(b, mThreadId, /* aSinceTime = */ 0, *mUniqueStacks);
+      aBuffer->StreamMarkersToJSON(b, mThreadId, /* aSinceTime = */ 0, *mUniqueStacks);
     }
     b.EndBareList();
     mSavedStreamedMarkers = b.WriteFunc()->CopyData();
   }
 
   // Reset the buffer. Attempting to symbolicate JS samples after mContext has
   // gone away will crash.
-  mBuffer->reset();
+  aBuffer->reset();
 }
 
 mozilla::Mutex&
 ThreadInfo::GetMutex()
 {
   return *mMutex.get();
 }
 
 void
-ThreadInfo::DuplicateLastSample(const TimeStamp& aStartTime)
+ThreadInfo::DuplicateLastSample(ProfileBuffer* aBuffer,
+                                const TimeStamp& aStartTime)
 {
-  mBuffer->DuplicateLastSample(mThreadId, aStartTime);
+  aBuffer->DuplicateLastSample(mThreadId, aStartTime);
 }
 
 size_t
 ThreadInfo::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
 
   n += aMallocSizeOf(mName.get());
--- a/tools/profiler/core/ThreadInfo.h
+++ b/tools/profiler/core/ThreadInfo.h
@@ -19,17 +19,17 @@ class ThreadInfo {
   virtual ~ThreadInfo();
 
   const char* Name() const { return mName.get(); }
   int ThreadId() const { return mThreadId; }
 
   bool IsMainThread() const { return mIsMainThread; }
   PseudoStack* Stack() const { return mPseudoStack; }
 
-  void SetProfile(ProfileBuffer* aBuffer) { mBuffer = aBuffer; }
+  void SetHasProfile() { mHasProfile = true; }
 
   PlatformData* GetPlatformData() const { return mPlatformData.get(); }
   void* StackTop() const { return mStackTop; }
 
   virtual void SetPendingDelete();
   bool IsPendingDelete() const { return mPendingDelete; }
 
   bool CanInvokeJS() const;
@@ -46,57 +46,52 @@ class ThreadInfo {
 
   // May be null for the main thread if the profiler was started during startup.
   nsCOMPtr<nsIThread> mThread;
 
   bool mPendingDelete;
 
   //
   // The following code is only used for threads that are being profiled, i.e.
-  // for which SetProfile() has been called.
+  // for which SetHasProfile() has been called.
   //
 
 public:
-  bool hasProfile() { return !!mBuffer; }
-
-  void addTag(const ProfileBufferEntry& aTag);
+  bool HasProfile() { return mHasProfile; }
 
-  // Track a marker which has been inserted into the thread profile.
-  // This marker can safely be deleted once the generation has
-  // expired.
-  void addStoredMarker(ProfilerMarker* aStoredMarker);
   mozilla::Mutex& GetMutex();
-  void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime = 0);
+  void StreamJSON(ProfileBuffer* aBuffer, SpliceableJSONWriter& aWriter,
+                  double aSinceTime = 0);
 
   // Call this method when the JS entries inside the buffer are about to
   // become invalid, i.e., just before JS shutdown.
-  void FlushSamplesAndMarkers();
+  void FlushSamplesAndMarkers(ProfileBuffer* aBuffer);
 
-  void DuplicateLastSample(const mozilla::TimeStamp& aStartTime);
+  void DuplicateLastSample(ProfileBuffer* aBuffer,
+                           const mozilla::TimeStamp& aStartTime);
 
   ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; }
 
   void UpdateThreadResponsiveness() {
     mRespInfo.Update(mIsMainThread, mThread);
   }
 
-  uint32_t bufferGeneration() const { return mBuffer->mGeneration; }
-
-protected:
-  void StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
+  void StreamSamplesAndMarkers(ProfileBuffer* aBuffer,
+                               SpliceableJSONWriter& aWriter,
+                               double aSinceTime,
                                UniqueStacks& aUniqueStacks);
 
 private:
   FRIEND_TEST(ThreadProfile, InsertOneTag);
   FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
   FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
   FRIEND_TEST(ThreadProfile, InsertTagsWrap);
   FRIEND_TEST(ThreadProfile, MemoryMeasure);
 
-  RefPtr<ProfileBuffer> mBuffer;
+  bool mHasProfile;
 
   // JS frames in the buffer may require a live JSRuntime to stream (e.g.,
   // stringifying JIT frames). In the case of JSRuntime destruction,
   // FlushSamplesAndMarkers should be called to save them. These are spliced
   // into the final stream.
   mozilla::UniquePtr<char[]> mSavedStreamedSamples;
   mozilla::UniquePtr<char[]> mSavedStreamedMarkers;
   mozilla::Maybe<UniqueStacks> mUniqueStacks;
--- a/tools/profiler/core/platform-linux-android.cpp
+++ b/tools/profiler/core/platform-linux-android.cpp
@@ -357,22 +357,22 @@ SigprofSender(void* aArg)
     if (!gIsPaused) {
       StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
       bool isFirstProfiledThread = true;
       for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
         ThreadInfo* info = (*gRegisteredThreads)[i];
 
         // This will be null if we're not interested in profiling this thread.
-        if (!info->hasProfile() || info->IsPendingDelete()) {
+        if (!info->HasProfile() || info->IsPendingDelete()) {
           continue;
         }
 
         if (info->Stack()->CanDuplicateLastSampleDueToSleep()) {
-          info->DuplicateLastSample(gStartTime);
+          info->DuplicateLastSample(gBuffer, gStartTime);
           continue;
         }
 
         info->UpdateThreadResponsiveness();
 
         int threadId = info->ThreadId();
         MOZ_ASSERT(threadId != my_tid);
 
@@ -415,17 +415,17 @@ SigprofSender(void* aArg)
         // Extract the current pc and sp.
         SetSampleContext(&sample,
                          gSigHandlerCoordinator->mUContext.uc_mcontext);
         sample.threadInfo = info;
         sample.timestamp = mozilla::TimeStamp::Now();
         sample.rssMemory = rssMemory;
         sample.ussMemory = ussMemory;
 
-        Tick(&sample);
+        Tick(gBuffer, &sample);
 
         // Send message 3 to the samplee, which tells it to resume.
         r = sem_post(&gSigHandlerCoordinator->mMessage3);
         MOZ_ASSERT(r == 0);
 
         // Wait for message 4 from the samplee, which tells us that it has
         // finished with |gSigHandlerCoordinator|.
         while (true) {
--- a/tools/profiler/core/platform-macos.cpp
+++ b/tools/profiler/core/platform-macos.cpp
@@ -154,22 +154,22 @@ public:
       if (!gIsPaused) {
         StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
           ThreadInfo* info = (*gRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
-          if (!info->hasProfile() || info->IsPendingDelete()) {
+          if (!info->HasProfile() || info->IsPendingDelete()) {
             continue;
           }
 
           if (info->Stack()->CanDuplicateLastSampleDueToSleep()) {
-            info->DuplicateLastSample(gStartTime);
+            info->DuplicateLastSample(gBuffer, gStartTime);
             continue;
           }
 
           info->UpdateThreadResponsiveness();
 
           SampleContext(info, isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
@@ -235,17 +235,17 @@ public:
       sample.pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
       sample.sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
       sample.fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
       sample.timestamp = mozilla::TimeStamp::Now();
       sample.threadInfo = aThreadInfo;
 
 #undef REGISTER_FIELD
 
-      Tick(&sample);
+      Tick(gBuffer, &sample);
     }
     thread_resume(profiled_thread);
   }
 
 private:
   pthread_t mThread;
 
   int mIntervalMicro;
--- a/tools/profiler/core/platform-win32.cpp
+++ b/tools/profiler/core/platform-win32.cpp
@@ -163,22 +163,22 @@ class SamplerThread
       if (!gIsPaused) {
         mozilla::StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
           ThreadInfo* info = (*gRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
-          if (!info->hasProfile() || info->IsPendingDelete()) {
+          if (!info->HasProfile() || info->IsPendingDelete()) {
             continue;
           }
 
           if (info->Stack()->CanDuplicateLastSampleDueToSleep()) {
-            info->DuplicateLastSample(gStartTime);
+            info->DuplicateLastSample(gBuffer, gStartTime);
             continue;
           }
 
           info->UpdateThreadResponsiveness();
 
           SampleContext(info, isFirstProfiledThread);
           isFirstProfiledThread = false;
         }
@@ -244,17 +244,17 @@ class SamplerThread
 #else
     sample.pc = reinterpret_cast<Address>(context.Eip);
     sample.sp = reinterpret_cast<Address>(context.Esp);
     sample.fp = reinterpret_cast<Address>(context.Ebp);
 #endif
 
     sample.context = &context;
 
-    Tick(&sample);
+    Tick(gBuffer, &sample);
 
     ResumeThread(profiled_thread);
   }
 
 private:
   HANDLE mThread;
   Thread::tid_t mThreadId;
 
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -102,19 +102,18 @@ MOZ_THREAD_LOCAL(PseudoStack *) tlsPseud
 static Sampler* gSampler;
 
 static std::vector<ThreadInfo*>* gRegisteredThreads = nullptr;
 static mozilla::StaticMutex gRegisteredThreadsMutex;
 
 // All accesses to gGatherer are on the main thread, so no locking is needed.
 static StaticRefPtr<mozilla::ProfileGatherer> gGatherer;
 
-// XXX: gBuffer is used on multiple threads -- including via copies in
-// ThreadInfo::mBuffer -- without any apparent synchronization(!)
-static StaticRefPtr<ProfileBuffer> gBuffer;
+// Always used in conjunction with gRegisteredThreads.
+static ProfileBuffer* gBuffer;
 
 // gThreadNameFilters is accessed from multiple threads. All accesses to it
 // must be guarded by gThreadNameFiltersMutex.
 static Vector<std::string> gThreadNameFilters;
 static mozilla::StaticMutex gThreadNameFiltersMutex;
 
 // All accesses to gFeatures are on the main thread, so no locking is needed.
 static Vector<std::string> gFeatures;
@@ -225,38 +224,38 @@ public:
   bool isSamplingCurrentThread;
   ThreadInfo* threadInfo;
   mozilla::TimeStamp timestamp;
   int64_t rssMemory;
   int64_t ussMemory;
 };
 
 static void
-AddDynamicCodeLocationTag(ThreadInfo& aInfo, const char* aStr)
+AddDynamicCodeLocationTag(ProfileBuffer* aBuffer, const char* aStr)
 {
-  aInfo.addTag(ProfileBufferEntry::CodeLocation(""));
+  aBuffer->addTag(ProfileBufferEntry::CodeLocation(""));
 
   size_t strLen = strlen(aStr) + 1;   // +1 for the null terminator
   for (size_t j = 0; j < strLen; ) {
     // Store as many characters in the void* as the platform allows.
     char text[sizeof(void*)];
     size_t len = sizeof(void*) / sizeof(char);
     if (j+len >= strLen) {
       len = strLen - j;
     }
     memcpy(text, &aStr[j], len);
     j += sizeof(void*) / sizeof(char);
 
     // Cast to *((void**) to pass the text data to a void*.
-    aInfo.addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
+    aBuffer->addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
   }
 }
 
 static void
-AddPseudoEntry(volatile js::ProfileEntry& entry, ThreadInfo& aInfo,
+AddPseudoEntry(ProfileBuffer* aBuffer, volatile js::ProfileEntry& entry,
                PseudoStack* stack, void* lastpc)
 {
   // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations and
   // should not be recorded in the profile.
   if (entry.hasFlag(js::ProfileEntry::BEGIN_PSEUDO_JS)) {
     return;
   }
 
@@ -264,17 +263,17 @@ AddPseudoEntry(volatile js::ProfileEntry
 
   // First entry has kind CodeLocation. Check for magic pointer bit 1 to
   // indicate copy.
   const char* sampleLabel = entry.label();
 
   if (entry.isCopyLabel()) {
     // Store the string using 1 or more EmbeddedString tags.
     // That will happen to the preceding tag.
-    AddDynamicCodeLocationTag(aInfo, sampleLabel);
+    AddDynamicCodeLocationTag(aBuffer, sampleLabel);
     if (entry.isJs()) {
       JSScript* script = entry.script();
       if (script) {
         if (!entry.pc()) {
           // The JIT only allows the top-most entry to have a nullptr pc.
           MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
 
           // If stack-walking was disabled, then that's just unfortunate.
@@ -288,35 +287,35 @@ AddPseudoEntry(volatile js::ProfileEntry
         } else {
           lineno = JS_PCToLineNumber(script, entry.pc());
         }
       }
     } else {
       lineno = entry.line();
     }
   } else {
-    aInfo.addTag(ProfileBufferEntry::CodeLocation(sampleLabel));
+    aBuffer->addTag(ProfileBufferEntry::CodeLocation(sampleLabel));
 
     // XXX: Bug 1010578. Don't assume a CPP entry and try to get the line for
     // js entries as well.
     if (entry.isCpp()) {
       lineno = entry.line();
     }
   }
 
   if (lineno != -1) {
-    aInfo.addTag(ProfileBufferEntry::LineNumber(lineno));
+    aBuffer->addTag(ProfileBufferEntry::LineNumber(lineno));
   }
 
   uint32_t category = entry.category();
   MOZ_ASSERT(!(category & js::ProfileEntry::IS_CPP_ENTRY));
   MOZ_ASSERT(!(category & js::ProfileEntry::FRAME_LABEL_COPY));
 
   if (category) {
-    aInfo.addTag(ProfileBufferEntry::Category((int)category));
+    aBuffer->addTag(ProfileBufferEntry::Category((int)category));
   }
 }
 
 struct NativeStack
 {
   void** pc_array;
   void** sp_array;
   size_t size;
@@ -336,35 +335,35 @@ struct AutoWalkJSStack
   ~AutoWalkJSStack() {
     if (walkAllowed) {
       WALKING_JS_STACK = false;
     }
   }
 };
 
 static void
-MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
+MergeStacksIntoProfile(ProfileBuffer* aBuffer, TickSample* aSample,
                        NativeStack& aNativeStack)
 {
-  PseudoStack* pseudoStack = aInfo.Stack();
+  PseudoStack* pseudoStack = aSample->threadInfo->Stack();
   volatile js::ProfileEntry* pseudoFrames = pseudoStack->mStack;
   uint32_t pseudoCount = pseudoStack->stackSize();
 
   // Make a copy of the JS stack into a JSFrame array. This is necessary since,
   // like the native stack, the JS stack is iterated youngest-to-oldest and we
   // need to iterate oldest-to-youngest when adding entries to aInfo.
 
   // Synchronous sampling reports an invalid buffer generation to
   // ProfilingFrameIterator to avoid incorrectly resetting the generation of
   // sampled JIT entries inside the JS engine. See note below concerning 'J'
   // entries.
   uint32_t startBufferGen;
   startBufferGen = aSample->isSamplingCurrentThread
                  ? UINT32_MAX
-                 : aInfo.bufferGeneration();
+                 : aBuffer->mGeneration;
   uint32_t jsCount = 0;
   JS::ProfilingFrameIterator::Frame jsFrames[1000];
 
   // Only walk jit stack if profiling frame iterator is turned on.
   if (pseudoStack->mContext &&
       JS::IsProfilingEnabledForContext(pseudoStack->mContext)) {
     AutoWalkJSStack autoWalkJSStack;
     const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);
@@ -394,17 +393,17 @@ MergeStacksIntoProfile(ThreadInfo& aInfo
             jsFrames[jsCount++] = frame.value();
           }
         }
       }
     }
   }
 
   // Start the sample with a root entry.
-  aInfo.addTag(ProfileBufferEntry::Sample("(root)"));
+  aBuffer->addTag(ProfileBufferEntry::Sample("(root)"));
 
   // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
   // native arrays are ordered youngest-to-oldest. We must add frames to aInfo
   // oldest-to-youngest. Thus, iterate over the pseudo-stack forwards and JS
   // and native arrays backwards. Note: this means the terminating condition
   // jsIndex and nativeIndex is being < 0.
   uint32_t pseudoIndex = 0;
   int32_t jsIndex = jsCount - 1;
@@ -467,17 +466,17 @@ MergeStacksIntoProfile(ThreadInfo& aInfo
                                jsStackAddr != nativeStackAddr);
     MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                    nativeStackAddr != jsStackAddr);
 
     // Check to see if pseudoStack frame is top-most.
     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
       MOZ_ASSERT(pseudoIndex < pseudoCount);
       volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex];
-      AddPseudoEntry(pseudoFrame, aInfo, pseudoStack, nullptr);
+      AddPseudoEntry(aBuffer, pseudoFrame, pseudoStack, nullptr);
       pseudoIndex++;
       continue;
     }
 
     // Check to see if JS jit stack frame is top-most
     if (jsStackAddr > nativeStackAddr) {
       MOZ_ASSERT(jsIndex >= 0);
       const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
@@ -492,49 +491,49 @@ MergeStacksIntoProfile(ThreadInfo& aInfo
       // amount of time, such as in nsRefreshDriver. Problematically, the
       // stored backtrace may be alive across a GC during which the profiler
       // itself is disabled. In that case, the JS engine is free to discard its
       // JIT code. This means that if we inserted such OptInfoAddr entries into
       // the buffer, nsRefreshDriver would now be holding on to a backtrace
       // with stale JIT code return addresses.
       if (aSample->isSamplingCurrentThread ||
           jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
-        AddDynamicCodeLocationTag(aInfo, jsFrame.label);
+        AddDynamicCodeLocationTag(aBuffer, jsFrame.label);
       } else {
         MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
                    jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
-        aInfo.addTag(
+        aBuffer->addTag(
           ProfileBufferEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
       }
 
       jsIndex--;
       continue;
     }
 
     // If we reach here, there must be a native stack entry and it must be the
     // greatest entry.
     if (nativeStackAddr) {
       MOZ_ASSERT(nativeIndex >= 0);
       void* addr = (void*)aNativeStack.pc_array[nativeIndex];
-      aInfo.addTag(ProfileBufferEntry::NativeLeafAddr(addr));
+      aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr(addr));
     }
     if (nativeIndex >= 0) {
       nativeIndex--;
     }
   }
 
   // Update the JS context with the current profile sample buffer generation.
   //
   // Do not do this for synchronous sampling, which create their own
   // ProfileBuffers.
   if (!aSample->isSamplingCurrentThread && pseudoStack->mContext) {
-    MOZ_ASSERT(aInfo.bufferGeneration() >= startBufferGen);
-    uint32_t lapCount = aInfo.bufferGeneration() - startBufferGen;
+    MOZ_ASSERT(aBuffer->mGeneration >= startBufferGen);
+    uint32_t lapCount = aBuffer->mGeneration - startBufferGen;
     JS::UpdateJSContextProfilerSampleBufferGen(pseudoStack->mContext,
-                                               aInfo.bufferGeneration(),
+                                               aBuffer->mGeneration,
                                                lapCount);
   }
 }
 
 #if defined(GP_OS_windows)
 static uintptr_t GetThreadHandle(PlatformData* aData);
 #endif
 
@@ -545,17 +544,17 @@ StackWalkCallback(uint32_t aFrameNumber,
   NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
   MOZ_ASSERT(nativeStack->count < nativeStack->size);
   nativeStack->sp_array[nativeStack->count] = aSP;
   nativeStack->pc_array[nativeStack->count] = aPC;
   nativeStack->count++;
 }
 
 static void
-DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+DoNativeBacktrace(ProfileBuffer* aBuffer, TickSample* aSample)
 {
   void* pc_array[1000];
   void* sp_array[1000];
   NativeStack nativeStack = {
     pc_array,
     sp_array,
     mozilla::ArrayLength(pc_array),
     0
@@ -580,23 +579,23 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
   // Win64 always omits frame pointers so for it we use the slower
   // MozStackWalk().
   uintptr_t thread = GetThreadHandle(aSample->threadInfo->GetPlatformData());
   MOZ_ASSERT(thread);
   MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &nativeStack,
                thread, /* platformData */ nullptr);
 #endif
 
-  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+  MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 }
 #endif
 
 #ifdef USE_EHABI_STACKWALK
 static void
-DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+DoNativeBacktrace(Profile* aBuffer, TickSample* aSample)
 {
   void* pc_array[1000];
   void* sp_array[1000];
   NativeStack nativeStack = {
     pc_array,
     sp_array,
     mozilla::ArrayLength(pc_array),
     0
@@ -657,17 +656,17 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
                                       nativeStack.size - nativeStack.count);
 
   MergeStacksIntoProfile(aInfo, aSample, nativeStack);
 }
 #endif
 
 #ifdef USE_LUL_STACKWALK
 static void
-DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+DoNativeBacktrace(ProfileBuffer* aBuffer, TickSample* aSample)
 {
   const mcontext_t* mc =
     &reinterpret_cast<ucontext_t*>(aSample->context)->uc_mcontext;
 
   lul::UnwindRegs startRegs;
   memset(&startRegs, 0, sizeof(startRegs));
 
 #if defined(GP_PLAT_amd64_linux)
@@ -704,18 +703,18 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
     uintptr_t rEDZONE_SIZE = 0;
     uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
 #elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
     uintptr_t rEDZONE_SIZE = 0;
     uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
 #else
 #   error "Unknown plat"
 #endif
-    uintptr_t end   = reinterpret_cast<uintptr_t>(aInfo.StackTop());
-    uintptr_t ws    = sizeof(void*);
+    uintptr_t end = reinterpret_cast<uintptr_t>(aSample->threadInfo->StackTop());
+    uintptr_t ws  = sizeof(void*);
     start &= ~(ws-1);
     end   &= ~(ws-1);
     uintptr_t nToCopy = 0;
     if (start < end) {
       nToCopy = end - start;
       if (nToCopy > lul::N_STACK_BYTES)
         nToCopy = lul::N_STACK_BYTES;
     }
@@ -749,97 +748,95 @@ DoNativeBacktrace(ThreadInfo& aInfo, Tic
     reinterpret_cast<void**>(framePCs),
     reinterpret_cast<void**>(frameSPs),
     mozilla::ArrayLength(framePCs),
     0
   };
 
   nativeStack.count = framesUsed;
 
-  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+  MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 
   // Update stats in the LUL stats object.  Unfortunately this requires
   // three global memory operations.
   gLUL->mStats.mContext += 1;
   gLUL->mStats.mCFI     += framesUsed - 1 - scannedFramesAcquired;
   gLUL->mStats.mScanned += scannedFramesAcquired;
 }
 #endif
 
 static void
-DoSampleStackTrace(ThreadInfo& aInfo, TickSample* aSample,
+DoSampleStackTrace(ProfileBuffer* aBuffer, TickSample* aSample,
                    bool aAddLeafAddresses)
 {
   NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
-  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+  MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 
   if (aSample && aAddLeafAddresses) {
-    aInfo.addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample->pc));
+    aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample->pc));
   }
 }
 
 // This function is called for each sampling period with the current program
 // counter. It is called within a signal and so must be re-entrant.
 static void
-Tick(TickSample* aSample)
+Tick(ProfileBuffer* aBuffer, TickSample* aSample)
 {
-  ThreadInfo& currThreadInfo = *aSample->threadInfo;
-
-  currThreadInfo.addTag(
-    ProfileBufferEntry::ThreadId(currThreadInfo.ThreadId()));
+  ThreadInfo& threadInfo = *aSample->threadInfo;
+
+  aBuffer->addTag(ProfileBufferEntry::ThreadId(threadInfo.ThreadId()));
 
   mozilla::TimeDuration delta = aSample->timestamp - gStartTime;
-  currThreadInfo.addTag(ProfileBufferEntry::Time(delta.ToMilliseconds()));
-
-  PseudoStack* stack = currThreadInfo.Stack();
+  aBuffer->addTag(ProfileBufferEntry::Time(delta.ToMilliseconds()));
+
+  PseudoStack* stack = threadInfo.Stack();
 
 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
     defined(USE_LUL_STACKWALK)
   if (gUseStackWalk) {
-    DoNativeBacktrace(currThreadInfo, aSample);
+    DoNativeBacktrace(aBuffer, aSample);
   } else {
-    DoSampleStackTrace(currThreadInfo, aSample, gAddLeafAddresses);
+    DoSampleStackTrace(aBuffer, aSample, gAddLeafAddresses);
   }
 #else
-  DoSampleStackTrace(currThreadInfo, aSample, gAddLeafAddresses);
+  DoSampleStackTrace(aBuffer, aSample, gAddLeafAddresses);
 #endif
 
   // Don't process the PeudoStack's markers if we're synchronously sampling the
   // current thread.
   if (!aSample->isSamplingCurrentThread) {
     ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
     while (pendingMarkersList && pendingMarkersList->peek()) {
       ProfilerMarker* marker = pendingMarkersList->popHead();
-      currThreadInfo.addStoredMarker(marker);
-      currThreadInfo.addTag(ProfileBufferEntry::Marker(marker));
+      aBuffer->addStoredMarker(marker);
+      aBuffer->addTag(ProfileBufferEntry::Marker(marker));
     }
   }
 
-  if (currThreadInfo.GetThreadResponsiveness()->HasData()) {
+  if (threadInfo.GetThreadResponsiveness()->HasData()) {
     mozilla::TimeDuration delta =
-      currThreadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(
+      threadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(
         aSample->timestamp);
-    currThreadInfo.addTag(
-      ProfileBufferEntry::Responsiveness(delta.ToMilliseconds()));
+    aBuffer->addTag(ProfileBufferEntry::Responsiveness(delta.ToMilliseconds()));
   }
 
   // rssMemory is equal to 0 when we are not recording.
   if (aSample->rssMemory != 0) {
-    currThreadInfo.addTag(ProfileBufferEntry::ResidentMemory(
+    aBuffer->addTag(ProfileBufferEntry::ResidentMemory(
       static_cast<double>(aSample->rssMemory)));
   }
 
   // ussMemory is equal to 0 when we are not recording.
   if (aSample->ussMemory != 0) {
-    currThreadInfo.addTag(ProfileBufferEntry::UnsharedMemory(
+    aBuffer->addTag(ProfileBufferEntry::UnsharedMemory(
       static_cast<double>(aSample->ussMemory)));
   }
 
   if (gLastFrameNumber != gFrameNumber) {
-    currThreadInfo.addTag(ProfileBufferEntry::FrameNumber(gFrameNumber));
+    aBuffer->addTag(ProfileBufferEntry::FrameNumber(gFrameNumber));
     gLastFrameNumber = gFrameNumber;
   }
 }
 
 // END tick/unwinding code
 ////////////////////////////////////////////////////////////////////////
 
 ////////////////////////////////////////////////////////////////////////
@@ -1123,26 +1120,26 @@ StreamJSON(SpliceableJSONWriter& aWriter
       gIsPaused = true;
 
       {
         StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
         for (size_t i = 0; i < gRegisteredThreads->size(); i++) {
           // Thread not being profiled, skip it
           ThreadInfo* info = gRegisteredThreads->at(i);
-          if (!info->hasProfile()) {
+          if (!info->HasProfile()) {
             continue;
           }
 
           // Note that we intentionally include thread profiles which
           // have been marked for pending delete.
 
           MutexAutoLock lock(info->GetMutex());
 
-          info->StreamJSON(aWriter, aSinceTime);
+          info->StreamJSON(gBuffer, aWriter, aSinceTime);
         }
       }
 
       if (CanNotifyObservers()) {
         // Send a event asking any subprocesses (plugins) to
         // give us their information
         SubprocessClosure closure(&aWriter);
         nsCOMPtr<nsIObserverService> os =
@@ -1484,17 +1481,17 @@ ThreadSelected(const char* aThreadName)
   return false;
 }
 
 static void
 MaybeSetProfile(ThreadInfo* aInfo)
 {
   if ((aInfo->IsMainThread() || gProfileThreads) &&
       ThreadSelected(aInfo->Name())) {
-    aInfo->SetProfile(gBuffer);
+    aInfo->SetHasProfile();
   }
 }
 
 static void
 RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack,
                       bool aIsMainThread, void* stackTop)
 {
   StaticMutexAutoLock lock(gRegisteredThreadsMutex);
@@ -1988,17 +1985,17 @@ profiler_start(int aEntries, double aInt
   gIsPaused = false;
   PlatformStart(gInterval);
 
   if (gProfileJS || privacyMode) {
     mozilla::StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
     for (uint32_t i = 0; i < gRegisteredThreads->size(); i++) {
       ThreadInfo* info = (*gRegisteredThreads)[i];
-      if (info->IsPendingDelete() || !info->hasProfile()) {
+      if (info->IsPendingDelete() || !info->HasProfile()) {
         continue;
       }
       info->Stack()->reinitializeOnResume();
       if (gProfileJS) {
         info->Stack()->enableJSSampling();
       }
       if (privacyMode) {
         info->Stack()->mPrivacyMode = true;
@@ -2114,17 +2111,20 @@ profiler_stop()
 #ifdef MOZ_TASK_TRACER
   if (gTaskTracer) {
     mozilla::tasktracer::StopLogging();
   }
 #endif
 
   delete gSampler;
   gSampler = nullptr;
+
+  delete gBuffer;
   gBuffer = nullptr;
+
   gEntries = 0;
   gInterval = 0;
 
   // Cancel any in-flight async profile gatherering requests.
   gGatherer->Cancel();
   gGatherer = nullptr;
 
   if (disableJS) {
@@ -2435,38 +2435,42 @@ profiler_get_backtrace()
 
   PseudoStack* stack = tlsPseudoStack.get();
   if (!stack) {
     MOZ_ASSERT(stack);
     return nullptr;
   }
   Thread::tid_t tid = Thread::GetCurrentId();
 
-  SyncProfile* profile = new SyncProfile(tid, stack);
+  ProfileBuffer* buffer = new ProfileBuffer(GET_BACKTRACE_DEFAULT_ENTRIES);
+  ThreadInfo* threadInfo =
+    new ThreadInfo("SyncProfile", tid, NS_IsMainThread(), stack,
+                   /* stackTop */ nullptr);
+  threadInfo->SetHasProfile();
 
   TickSample sample;
-  sample.threadInfo = profile;
+  sample.threadInfo = threadInfo;
 
 #if defined(HAVE_NATIVE_UNWIND)
 #if defined(GP_OS_windows) || defined(GP_OS_linux) || defined(GP_OS_android)
   tickcontext_t context;
   sample.PopulateContext(&context);
 #elif defined(GP_OS_darwin)
   sample.PopulateContext(nullptr);
 #else
 # error "unknown platform"
 #endif
 #endif
 
   sample.isSamplingCurrentThread = true;
   sample.timestamp = mozilla::TimeStamp::Now();
 
-  Tick(&sample);
-
-  return UniqueProfilerBacktrace(new ProfilerBacktrace(profile));
+  Tick(buffer, &sample);
+
+  return UniqueProfilerBacktrace(new ProfilerBacktrace(buffer, threadInfo));
 }
 
 void
 ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace)
 {
   delete aBacktrace;
 }
 
@@ -2579,27 +2583,27 @@ void PseudoStack::flushSamplerOnJSShutdo
   gIsPaused = true;
 
   {
     StaticMutexAutoLock lock(gRegisteredThreadsMutex);
 
     for (size_t i = 0; i < gRegisteredThreads->size(); i++) {
       // Thread not being profiled, skip it.
       ThreadInfo* info = gRegisteredThreads->at(i);
-      if (!info->hasProfile() || info->IsPendingDelete()) {
+      if (!info->HasProfile() || info->IsPendingDelete()) {
         continue;
       }
 
       // Thread not profiling the context that's going away, skip it.
       if (info->Stack()->mContext != mContext) {
         continue;
       }
 
       MutexAutoLock lock(info->GetMutex());
-      info->FlushSamplesAndMarkers();
+      info->FlushSamplesAndMarkers(gBuffer);
     }
   }
 
   gIsPaused = false;
 }
 
 // We #include these files directly because it means those files can use
 // declarations from this file trivially.
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -22,17 +22,16 @@ if CONFIG['MOZ_GECKO_PROFILER']:
     UNIFIED_SOURCES += [
         'core/platform.cpp',
         'core/ProfileBuffer.cpp',
         'core/ProfileBufferEntry.cpp',
         'core/ProfileJSONWriter.cpp',
         'core/ProfilerBacktrace.cpp',
         'core/ProfilerMarkers.cpp',
         'core/StackTop.cpp',
-        'core/SyncProfile.cpp',
         'core/ThreadInfo.cpp',
         'gecko/nsProfiler.cpp',
         'gecko/nsProfilerFactory.cpp',
         'gecko/nsProfilerStartParams.cpp',
         'gecko/ProfilerIOInterposeObserver.cpp',
         'gecko/ThreadResponsiveness.cpp',
     ]
     if CONFIG['OS_TARGET'] == 'Darwin':
--- a/tools/profiler/public/ProfilerBacktrace.h
+++ b/tools/profiler/public/ProfilerBacktrace.h
@@ -2,35 +2,37 @@
 /* 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/. */
 
 #ifndef __PROFILER_BACKTRACE_H
 #define __PROFILER_BACKTRACE_H
 
-class SyncProfile;
+class ProfileBuffer;
 class SpliceableJSONWriter;
+class ThreadInfo;
 class UniqueStacks;
 
 class ProfilerBacktrace
 {
 public:
-  explicit ProfilerBacktrace(SyncProfile* aProfile);
+  explicit ProfilerBacktrace(ProfileBuffer* aBuffer, ThreadInfo* aThreadInfo);
   ~ProfilerBacktrace();
 
   // ProfilerBacktraces' stacks are deduplicated in the context of the
   // profile that contains the backtrace as a marker payload.
   //
   // That is, markers that contain backtraces should not need their own stack,
   // frame, and string tables. They should instead reuse their parent
   // profile's tables.
   void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
 
 private:
   ProfilerBacktrace(const ProfilerBacktrace&);
   ProfilerBacktrace& operator=(const ProfilerBacktrace&);
 
-  SyncProfile*  mProfile;
+  ProfileBuffer* mBuffer;
+  ThreadInfo* mThreadInfo;
 };
 
 #endif // __PROFILER_BACKTRACE_H
 
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
@@ -8,39 +8,38 @@
 #include "ProfileBufferEntry.h"
 #include "ThreadInfo.h"
 
 // Make sure we can initialize our thread profile
 TEST(ThreadProfile, Initialization) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
-  info.SetProfile(pb);
+  info.SetHasProfile();
 }
 
 // Make sure we can record one tag and read it
 TEST(ThreadProfile, InsertOneTag) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
+  ProfileBuffer* pb = new ProfileBuffer(10);
   pb->addTag(ProfileBufferEntry::Time(123.1));
   ASSERT_TRUE(pb->mEntries != nullptr);
   ASSERT_TRUE(pb->mEntries[pb->mReadPos].kind() ==
               ProfileBufferEntry::Kind::Time);
   ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1);
 }
 
 // See if we can insert some tags
 TEST(ThreadProfile, InsertTagsNoWrap) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(100);
+  ProfileBuffer* pb = new ProfileBuffer(100);
   int test_size = 50;
   for (int i = 0; i < test_size; i++) {
     pb->addTag(ProfileBufferEntry::Time(i));
   }
   ASSERT_TRUE(pb->mEntries != nullptr);
   int readPos = pb->mReadPos;
   while (readPos != pb->mWritePos) {
     ASSERT_TRUE(pb->mEntries[readPos].kind() ==
@@ -53,17 +52,17 @@ TEST(ThreadProfile, InsertTagsNoWrap) {
 // See if wrapping works as it should in the basic case
 TEST(ThreadProfile, InsertTagsWrap) {
   PseudoStack* stack = new PseudoStack();
   Thread::tid_t tid = 1000;
   // we can fit only 24 tags in this buffer because of the empty slot
   int tags = 24;
   int buffer_size = tags + 1;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
-  RefPtr<ProfileBuffer> pb = new ProfileBuffer(buffer_size);
+  ProfileBuffer* pb = new ProfileBuffer(buffer_size);
   int test_size = 43;
   for (int i = 0; i < test_size; i++) {
     pb->addTag(ProfileBufferEntry::Time(i));
   }
   ASSERT_TRUE(pb->mEntries != nullptr);
   int readPos = pb->mReadPos;
   int ctr = 0;
   while (readPos != pb->mWritePos) {
new file mode 100644
--- /dev/null
+++ b/widget/android/GeckoEditableSupport.cpp
@@ -0,0 +1,1183 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * vim: set sw=4 ts=4 expandtab:
+ * 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 "GeckoEditableSupport.h"
+
+#include "AndroidRect.h"
+#include "KeyEvent.h"
+#include "android_npapi.h"
+#include "nsIContent.h"
+#include "nsISelection.h"
+
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/TextComposition.h"
+#include "mozilla/TextEventDispatcherListener.h"
+#include "mozilla/TextEvents.h"
+
+#include <android/log.h>
+
+#ifdef DEBUG_ANDROID_IME
+#define ALOGIME(args...) __android_log_print(ANDROID_LOG_INFO, \
+                                             "GeckoEditableSupport" , ## args)
+#else
+#define ALOGIME(args...) do { } while (0)
+#endif
+
+// Sync with GeckoEditableClient class
+static const int IME_MONITOR_CURSOR_ONE_SHOT = 1;
+static const int IME_MONITOR_CURSOR_START_MONITOR = 2;
+static const int IME_MONITOR_CURSOR_END_MONITOR = 3;
+
+template<> const char
+nsWindow::NativePtr<mozilla::widget::GeckoEditableSupport>::sName[] =
+        "GeckoEditableSupport";
+
+static uint32_t
+ConvertAndroidKeyCodeToDOMKeyCode(int32_t androidKeyCode)
+{
+    // Special-case alphanumeric keycodes because they are most common.
+    if (androidKeyCode >= AKEYCODE_A &&
+        androidKeyCode <= AKEYCODE_Z) {
+        return androidKeyCode - AKEYCODE_A + NS_VK_A;
+    }
+
+    if (androidKeyCode >= AKEYCODE_0 &&
+        androidKeyCode <= AKEYCODE_9) {
+        return androidKeyCode - AKEYCODE_0 + NS_VK_0;
+    }
+
+    switch (androidKeyCode) {
+        // KEYCODE_UNKNOWN (0) ... KEYCODE_HOME (3)
+        case AKEYCODE_BACK:               return NS_VK_ESCAPE;
+        // KEYCODE_CALL (5) ... KEYCODE_POUND (18)
+        case AKEYCODE_DPAD_UP:            return NS_VK_UP;
+        case AKEYCODE_DPAD_DOWN:          return NS_VK_DOWN;
+        case AKEYCODE_DPAD_LEFT:          return NS_VK_LEFT;
+        case AKEYCODE_DPAD_RIGHT:         return NS_VK_RIGHT;
+        case AKEYCODE_DPAD_CENTER:        return NS_VK_RETURN;
+        case AKEYCODE_VOLUME_UP:          return NS_VK_VOLUME_UP;
+        case AKEYCODE_VOLUME_DOWN:        return NS_VK_VOLUME_DOWN;
+        // KEYCODE_VOLUME_POWER (26) ... KEYCODE_Z (54)
+        case AKEYCODE_COMMA:              return NS_VK_COMMA;
+        case AKEYCODE_PERIOD:             return NS_VK_PERIOD;
+        case AKEYCODE_ALT_LEFT:           return NS_VK_ALT;
+        case AKEYCODE_ALT_RIGHT:          return NS_VK_ALT;
+        case AKEYCODE_SHIFT_LEFT:         return NS_VK_SHIFT;
+        case AKEYCODE_SHIFT_RIGHT:        return NS_VK_SHIFT;
+        case AKEYCODE_TAB:                return NS_VK_TAB;
+        case AKEYCODE_SPACE:              return NS_VK_SPACE;
+        // KEYCODE_SYM (63) ... KEYCODE_ENVELOPE (65)
+        case AKEYCODE_ENTER:              return NS_VK_RETURN;
+        case AKEYCODE_DEL:                return NS_VK_BACK; // Backspace
+        case AKEYCODE_GRAVE:              return NS_VK_BACK_QUOTE;
+        // KEYCODE_MINUS (69)
+        case AKEYCODE_EQUALS:             return NS_VK_EQUALS;
+        case AKEYCODE_LEFT_BRACKET:       return NS_VK_OPEN_BRACKET;
+        case AKEYCODE_RIGHT_BRACKET:      return NS_VK_CLOSE_BRACKET;
+        case AKEYCODE_BACKSLASH:          return NS_VK_BACK_SLASH;
+        case AKEYCODE_SEMICOLON:          return NS_VK_SEMICOLON;
+        // KEYCODE_APOSTROPHE (75)
+        case AKEYCODE_SLASH:              return NS_VK_SLASH;
+        // KEYCODE_AT (77) ... KEYCODE_MEDIA_FAST_FORWARD (90)
+        case AKEYCODE_MUTE:               return NS_VK_VOLUME_MUTE;
+        case AKEYCODE_PAGE_UP:            return NS_VK_PAGE_UP;
+        case AKEYCODE_PAGE_DOWN:          return NS_VK_PAGE_DOWN;
+        // KEYCODE_PICTSYMBOLS (94) ... KEYCODE_BUTTON_MODE (110)
+        case AKEYCODE_ESCAPE:             return NS_VK_ESCAPE;
+        case AKEYCODE_FORWARD_DEL:        return NS_VK_DELETE;
+        case AKEYCODE_CTRL_LEFT:          return NS_VK_CONTROL;
+        case AKEYCODE_CTRL_RIGHT:         return NS_VK_CONTROL;
+        case AKEYCODE_CAPS_LOCK:          return NS_VK_CAPS_LOCK;
+        case AKEYCODE_SCROLL_LOCK:        return NS_VK_SCROLL_LOCK;
+        // KEYCODE_META_LEFT (117) ... KEYCODE_FUNCTION (119)
+        case AKEYCODE_SYSRQ:              return NS_VK_PRINTSCREEN;
+        case AKEYCODE_BREAK:              return NS_VK_PAUSE;
+        case AKEYCODE_MOVE_HOME:          return NS_VK_HOME;
+        case AKEYCODE_MOVE_END:           return NS_VK_END;
+        case AKEYCODE_INSERT:             return NS_VK_INSERT;
+        // KEYCODE_FORWARD (125) ... KEYCODE_MEDIA_RECORD (130)
+        case AKEYCODE_F1:                 return NS_VK_F1;
+        case AKEYCODE_F2:                 return NS_VK_F2;
+        case AKEYCODE_F3:                 return NS_VK_F3;
+        case AKEYCODE_F4:                 return NS_VK_F4;
+        case AKEYCODE_F5:                 return NS_VK_F5;
+        case AKEYCODE_F6:                 return NS_VK_F6;
+        case AKEYCODE_F7:                 return NS_VK_F7;
+        case AKEYCODE_F8:                 return NS_VK_F8;
+        case AKEYCODE_F9:                 return NS_VK_F9;
+        case AKEYCODE_F10:                return NS_VK_F10;
+        case AKEYCODE_F11:                return NS_VK_F11;
+        case AKEYCODE_F12:                return NS_VK_F12;
+        case AKEYCODE_NUM_LOCK:           return NS_VK_NUM_LOCK;
+        case AKEYCODE_NUMPAD_0:           return NS_VK_NUMPAD0;
+        case AKEYCODE_NUMPAD_1:           return NS_VK_NUMPAD1;
+        case AKEYCODE_NUMPAD_2:           return NS_VK_NUMPAD2;
+        case AKEYCODE_NUMPAD_3:           return NS_VK_NUMPAD3;
+        case AKEYCODE_NUMPAD_4:           return NS_VK_NUMPAD4;
+        case AKEYCODE_NUMPAD_5:           return NS_VK_NUMPAD5;
+        case AKEYCODE_NUMPAD_6:           return NS_VK_NUMPAD6;
+        case AKEYCODE_NUMPAD_7:           return NS_VK_NUMPAD7;
+        case AKEYCODE_NUMPAD_8:           return NS_VK_NUMPAD8;
+        case AKEYCODE_NUMPAD_9:           return NS_VK_NUMPAD9;
+        case AKEYCODE_NUMPAD_DIVIDE:      return NS_VK_DIVIDE;
+        case AKEYCODE_NUMPAD_MULTIPLY:    return NS_VK_MULTIPLY;
+        case AKEYCODE_NUMPAD_SUBTRACT:    return NS_VK_SUBTRACT;
+        case AKEYCODE_NUMPAD_ADD:         return NS_VK_ADD;
+        case AKEYCODE_NUMPAD_DOT:         return NS_VK_DECIMAL;
+        case AKEYCODE_NUMPAD_COMMA:       return NS_VK_SEPARATOR;
+        case AKEYCODE_NUMPAD_ENTER:       return NS_VK_RETURN;
+        case AKEYCODE_NUMPAD_EQUALS:      return NS_VK_EQUALS;
+        // KEYCODE_NUMPAD_LEFT_PAREN (162) ... KEYCODE_CALCULATOR (210)
+
+        // Needs to confirm the behavior.  If the key switches the open state
+        // of Japanese IME (or switches input character between Hiragana and
+        // Roman numeric characters), then, it might be better to use
+        // NS_VK_KANJI which is used for Alt+Zenkaku/Hankaku key on Windows.
+        case AKEYCODE_ZENKAKU_HANKAKU:    return 0;
+        case AKEYCODE_EISU:               return NS_VK_EISU;
+        case AKEYCODE_MUHENKAN:           return NS_VK_NONCONVERT;
+        case AKEYCODE_HENKAN:             return NS_VK_CONVERT;
+        case AKEYCODE_KATAKANA_HIRAGANA:  return 0;
+        case AKEYCODE_YEN:                return NS_VK_BACK_SLASH; // Same as other platforms.
+        case AKEYCODE_RO:                 return NS_VK_BACK_SLASH; // Same as other platforms.
+        case AKEYCODE_KANA:               return NS_VK_KANA;
+        case AKEYCODE_ASSIST:             return NS_VK_HELP;
+
+        // the A key is the action key for gamepad devices.
+        case AKEYCODE_BUTTON_A:          return NS_VK_RETURN;
+
+        default:
+            ALOG("ConvertAndroidKeyCodeToDOMKeyCode: "
+                 "No DOM keycode for Android keycode %d", int(androidKeyCode));
+        return 0;
+    }
+}
+
+static KeyNameIndex
+ConvertAndroidKeyCodeToKeyNameIndex(int32_t keyCode, int32_t action,
+                                    int32_t domPrintableKeyValue)
+{
+    // Special-case alphanumeric keycodes because they are most common.
+    if (keyCode >= AKEYCODE_A && keyCode <= AKEYCODE_Z) {
+        return KEY_NAME_INDEX_USE_STRING;
+    }
+
+    if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) {
+        return KEY_NAME_INDEX_USE_STRING;
+    }
+
+    switch (keyCode) {
+
+#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
+        case aNativeKey: return aKeyNameIndex;
+
+#include "NativeKeyToDOMKeyName.h"
+
+#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
+
+        // KEYCODE_0 (7) ... KEYCODE_9 (16)
+        case AKEYCODE_STAR:               // '*' key
+        case AKEYCODE_POUND:              // '#' key
+
+        // KEYCODE_A (29) ... KEYCODE_Z (54)
+
+        case AKEYCODE_COMMA:              // ',' key
+        case AKEYCODE_PERIOD:             // '.' key
+        case AKEYCODE_SPACE:
+        case AKEYCODE_GRAVE:              // '`' key
+        case AKEYCODE_MINUS:              // '-' key
+        case AKEYCODE_EQUALS:             // '=' key
+        case AKEYCODE_LEFT_BRACKET:       // '[' key
+        case AKEYCODE_RIGHT_BRACKET:      // ']' key
+        case AKEYCODE_BACKSLASH:          // '\' key
+        case AKEYCODE_SEMICOLON:          // ';' key
+        case AKEYCODE_APOSTROPHE:         // ''' key
+        case AKEYCODE_SLASH:              // '/' key
+        case AKEYCODE_AT:                 // '@' key
+        case AKEYCODE_PLUS:               // '+' key
+
+        case AKEYCODE_NUMPAD_0:
+        case AKEYCODE_NUMPAD_1:
+        case AKEYCODE_NUMPAD_2:
+        case AKEYCODE_NUMPAD_3:
+        case AKEYCODE_NUMPAD_4:
+        case AKEYCODE_NUMPAD_5:
+        case AKEYCODE_NUMPAD_6:
+        case AKEYCODE_NUMPAD_7:
+        case AKEYCODE_NUMPAD_8:
+        case AKEYCODE_NUMPAD_9:
+        case AKEYCODE_NUMPAD_DIVIDE:
+        case AKEYCODE_NUMPAD_MULTIPLY:
+        case AKEYCODE_NUMPAD_SUBTRACT:
+        case AKEYCODE_NUMPAD_ADD:
+        case AKEYCODE_NUMPAD_DOT:
+        case AKEYCODE_NUMPAD_COMMA:
+        case AKEYCODE_NUMPAD_EQUALS:
+        case AKEYCODE_NUMPAD_LEFT_PAREN:
+        case AKEYCODE_NUMPAD_RIGHT_PAREN:
+
+        case AKEYCODE_YEN:                // yen sign key
+        case AKEYCODE_RO:                 // Japanese Ro key
+            return KEY_NAME_INDEX_USE_STRING;
+
+        case AKEYCODE_NUM:                // XXX Not sure
+        case AKEYCODE_PICTSYMBOLS:
+
+        case AKEYCODE_BUTTON_A:
+        case AKEYCODE_BUTTON_B:
+        case AKEYCODE_BUTTON_C:
+        case AKEYCODE_BUTTON_X:
+        case AKEYCODE_BUTTON_Y:
+        case AKEYCODE_BUTTON_Z:
+        case AKEYCODE_BUTTON_L1:
+        case AKEYCODE_BUTTON_R1:
+        case AKEYCODE_BUTTON_L2:
+        case AKEYCODE_BUTTON_R2:
+        case AKEYCODE_BUTTON_THUMBL:
+        case AKEYCODE_BUTTON_THUMBR:
+        case AKEYCODE_BUTTON_START:
+        case AKEYCODE_BUTTON_SELECT:
+        case AKEYCODE_BUTTON_MODE:
+
+        case AKEYCODE_MEDIA_CLOSE:
+
+        case AKEYCODE_BUTTON_1:
+        case AKEYCODE_BUTTON_2:
+        case AKEYCODE_BUTTON_3:
+        case AKEYCODE_BUTTON_4:
+        case AKEYCODE_BUTTON_5:
+        case AKEYCODE_BUTTON_6:
+        case AKEYCODE_BUTTON_7:
+        case AKEYCODE_BUTTON_8:
+        case AKEYCODE_BUTTON_9:
+        case AKEYCODE_BUTTON_10:
+        case AKEYCODE_BUTTON_11:
+        case AKEYCODE_BUTTON_12:
+        case AKEYCODE_BUTTON_13:
+        case AKEYCODE_BUTTON_14:
+        case AKEYCODE_BUTTON_15:
+        case AKEYCODE_BUTTON_16:
+            return KEY_NAME_INDEX_Unidentified;
+
+        case AKEYCODE_UNKNOWN:
+            MOZ_ASSERT(
+                action != AKEY_EVENT_ACTION_MULTIPLE,
+                "Don't call this when action is AKEY_EVENT_ACTION_MULTIPLE!");
+            // It's actually an unknown key if the action isn't ACTION_MULTIPLE.
+            // However, it might cause text input.  So, let's check the value.
+            return domPrintableKeyValue ?
+                KEY_NAME_INDEX_USE_STRING : KEY_NAME_INDEX_Unidentified;
+
+        default:
+            ALOG("ConvertAndroidKeyCodeToKeyNameIndex: "
+                 "No DOM key name index for Android keycode %d", keyCode);
+            return KEY_NAME_INDEX_Unidentified;
+    }
+}
+
+static CodeNameIndex
+ConvertAndroidScanCodeToCodeNameIndex(int32_t scanCode)
+{
+    switch (scanCode) {
+
+#define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
+        case aNativeKey: return aCodeNameIndex;
+
+#include "NativeKeyToDOMCodeName.h"
+
+#undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
+
+        default:
+          return CODE_NAME_INDEX_UNKNOWN;
+    }
+}
+
+static void
+InitKeyEvent(WidgetKeyboardEvent& aEvent, int32_t aAction, int32_t aKeyCode,
+             int32_t aScanCode, int32_t aMetaState, int64_t aTime,
+             int32_t aDomPrintableKeyValue, int32_t aRepeatCount,
+             int32_t aFlags)
+{
+    const uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(aKeyCode);
+
+    aEvent.mModifiers = nsWindow::GetModifiers(aMetaState);
+    aEvent.mKeyCode = domKeyCode;
+
+    if (aEvent.mMessage != eKeyPress) {
+        ANPEvent pluginEvent;
+        pluginEvent.inSize = sizeof(pluginEvent);
+        pluginEvent.eventType = kKey_ANPEventType;
+        pluginEvent.data.key.action = (aEvent.mMessage == eKeyDown) ?
+                kDown_ANPKeyAction : kUp_ANPKeyAction;
+        pluginEvent.data.key.nativeCode = aKeyCode;
+        pluginEvent.data.key.virtualCode = domKeyCode;
+        pluginEvent.data.key.unichar = aDomPrintableKeyValue;
+        pluginEvent.data.key.modifiers =
+                (aMetaState & sdk::KeyEvent::META_SHIFT_MASK
+                        ? kShift_ANPKeyModifier : 0) |
+                (aMetaState & sdk::KeyEvent::META_ALT_MASK
+                        ? kAlt_ANPKeyModifier : 0);
+        pluginEvent.data.key.repeatCount = aRepeatCount;
+        aEvent.mPluginEvent.Copy(pluginEvent);
+    }
+
+    aEvent.mIsRepeat =
+        (aEvent.mMessage == eKeyDown || aEvent.mMessage == eKeyPress) &&
+        ((aFlags & sdk::KeyEvent::FLAG_LONG_PRESS) || aRepeatCount);
+
+    aEvent.mKeyNameIndex = ConvertAndroidKeyCodeToKeyNameIndex(
+            aKeyCode, aAction, aDomPrintableKeyValue);
+    aEvent.mCodeNameIndex = ConvertAndroidScanCodeToCodeNameIndex(aScanCode);
+
+    if (aEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
+            aDomPrintableKeyValue) {
+        aEvent.mKeyValue = char16_t(aDomPrintableKeyValue);
+    }
+
+    aEvent.mLocation =
+        WidgetKeyboardEvent::ComputeLocationFromCodeValue(aEvent.mCodeNameIndex);
+    aEvent.mTime = aTime;
+    aEvent.mTimeStamp = nsWindow::GetEventTimeStamp(aTime);
+}
+
+static nscolor
+ConvertAndroidColor(uint32_t aArgb)
+{
+    return NS_RGBA((aArgb & 0x00ff0000) >> 16,
+                   (aArgb & 0x0000ff00) >> 8,
+                   (aArgb & 0x000000ff),
+                   (aArgb & 0xff000000) >> 24);
+}
+
+static jni::ObjectArray::LocalRef
+ConvertRectArrayToJavaRectFArray(const nsTArray<LayoutDeviceIntRect>& aRects,
+                                 const LayoutDeviceIntPoint& aOffset,
+                                 const CSSToLayoutDeviceScale aScale)
+{
+    const size_t length = aRects.Length();
+    auto rects = jni::ObjectArray::New<sdk::RectF>(length);
+
+    for (size_t i = 0; i < length; i++) {
+        LayoutDeviceIntRect tmp = aRects[i] + aOffset;
+
+        sdk::RectF::LocalRef rect(rects.Env());
+        sdk::RectF::New(tmp.x / aScale.scale, tmp.y / aScale.scale,
+                        (tmp.x + tmp.width) / aScale.scale,
+                        (tmp.y + tmp.height) / aScale.scale,
+                        &rect);
+        rects->SetElement(i, rect);
+    }
+    return rects;
+}
+
+namespace mozilla {
+namespace widget {
+
+NS_IMPL_ISUPPORTS(GeckoEditableSupport,
+                  TextEventDispatcherListener,
+                  nsISupportsWeakReference)
+
+RefPtr<TextComposition>
+GeckoEditableSupport::GetComposition() const
+{
+    nsCOMPtr<nsIWidget> widget = GetWidget();
+    return widget ? IMEStateManager::GetTextCompositionFor(widget) : nullptr;
+}
+
+void
+GeckoEditableSupport::RemoveComposition(RemoveCompositionFlag aFlag)
+{
+    if (!mDispatcher || !mDispatcher->IsComposing()) {
+        return;
+    }
+
+    nsEventStatus status = nsEventStatus_eIgnore;
+
+    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+    mDispatcher->CommitComposition(
+            status, aFlag == CANCEL_IME_COMPOSITION ? &EmptyString() : nullptr);
+}
+
+void
+GeckoEditableSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
+        int32_t aScanCode, int32_t aMetaState, int32_t aKeyPressMetaState,
+        int64_t aTime, int32_t aDomPrintableKeyValue, int32_t aRepeatCount,
+        int32_t aFlags, bool aIsSynthesizedImeKey,
+        jni::Object::Param aOriginalEvent)
+{
+    nsCOMPtr<nsIWidget> widget = GetWidget();
+    RefPtr<TextEventDispatcher> dispatcher =
+            mDispatcher ? mDispatcher.get() :
+            widget      ? widget->GetTextEventDispatcher() : nullptr;
+    NS_ENSURE_TRUE_VOID(dispatcher && widget);
+
+    if (!aIsSynthesizedImeKey && mWindow) {
+        mWindow->UserActivity();
+    }
+
+    EventMessage msg;
+    if (aAction == sdk::KeyEvent::ACTION_DOWN) {
+        msg = eKeyDown;
+    } else if (aAction == sdk::KeyEvent::ACTION_UP) {
+        msg = eKeyUp;
+    } else if (aAction == sdk::KeyEvent::ACTION_MULTIPLE) {
+        // Keys with multiple action are handled in Java,
+        // and we should never see one here
+        MOZ_CRASH("Cannot handle key with multiple action");
+    } else {
+        NS_WARNING("Unknown key action event");
+        return;
+    }
+
+    nsEventStatus status = nsEventStatus_eIgnore;
+    WidgetKeyboardEvent event(true, msg, widget);
+    InitKeyEvent(event, aAction, aKeyCode, aScanCode, aMetaState, aTime,
+                 aDomPrintableKeyValue, aRepeatCount, aFlags);
+
+    if (aIsSynthesizedImeKey) {
+        // Keys synthesized by Java IME code are saved in the mIMEKeyEvents
+        // array until the next IME_REPLACE_TEXT event, at which point
+        // these keys are dispatched in sequence.
+        mIMEKeyEvents.AppendElement(UniquePtr<WidgetEvent>(event.Duplicate()));
+    } else {
+        RemoveComposition();
+        NS_ENSURE_SUCCESS_VOID(dispatcher->BeginNativeInputTransaction());
+        dispatcher->DispatchKeyboardEvent(msg, event, status);
+        if (widget->Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
+            // Skip default processing.
+            return;
+        }
+        mEditable->OnDefaultKeyEvent(aOriginalEvent);
+    }
+
+    // Only send keypress after keydown.
+    if (msg != eKeyDown) {
+        return;
+    }
+
+    WidgetKeyboardEvent pressEvent(true, eKeyPress, widget);
+    InitKeyEvent(pressEvent, aAction, aKeyCode, aScanCode, aKeyPressMetaState,
+                 aTime, aDomPrintableKeyValue, aRepeatCount, aFlags);
+
+    if (aIsSynthesizedImeKey) {
+        mIMEKeyEvents.AppendElement(
+                UniquePtr<WidgetEvent>(pressEvent.Duplicate()));
+    } else {
+        dispatcher->MaybeDispatchKeypressEvents(pressEvent, status);
+    }
+}
+
+/*
+ * Send dummy key events for pages that are unaware of input events,
+ * to provide web compatibility for pages that depend on key events.
+ * Our dummy key events have 0 as the keycode.
+ */
+void
+GeckoEditableSupport::SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg)
+{
+    nsEventStatus status = nsEventStatus_eIgnore;
+    MOZ_ASSERT(mDispatcher);
+
+    WidgetKeyboardEvent event(true, msg, aWidget);
+    event.mTime = PR_Now() / 1000;
+    MOZ_ASSERT(event.mKeyCode == 0);
+    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+    mDispatcher->DispatchKeyboardEvent(msg, event, status);
+}
+
+void
+GeckoEditableSupport::AddIMETextChange(const IMETextChange& aChange)
+{
+    mIMETextChanges.AppendElement(aChange);
+
+    // We may not be in the middle of flushing,
+    // in which case this flag is meaningless.
+    mIMETextChangedDuringFlush = true;
+
+    // Now that we added a new range we need to go back and
+    // update all the ranges before that.
+    // Ranges that have offsets which follow this new range
+    // need to be updated to reflect new offsets
+    const int32_t delta = aChange.mNewEnd - aChange.mOldEnd;
+    for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
+        IMETextChange& previousChange = mIMETextChanges[i];
+        if (previousChange.mStart > aChange.mOldEnd) {
+            previousChange.mStart += delta;
+            previousChange.mOldEnd += delta;
+            previousChange.mNewEnd += delta;
+        }
+    }
+
+    // Now go through all ranges to merge any ranges that are connected
+    // srcIndex is the index of the range to merge from
+    // dstIndex is the index of the range to potentially merge into
+    int32_t srcIndex = mIMETextChanges.Length() - 1;
+    int32_t dstIndex = srcIndex;
+
+    while (--dstIndex >= 0) {
+        IMETextChange& src = mIMETextChanges[srcIndex];
+        IMETextChange& dst = mIMETextChanges[dstIndex];
+        // When merging a more recent change into an older
+        // change, we need to compare recent change's (start, oldEnd)
+        // range to the older change's (start, newEnd)
+        if (src.mOldEnd < dst.mStart || dst.mNewEnd < src.mStart) {
+            // No overlap between ranges
+            continue;
+        }
+        // When merging two ranges, there are generally four posibilities:
+        // [----(----]----), (----[----]----),
+        // [----(----)----], (----[----)----]
+        // where [----] is the first range and (----) is the second range
+        // As seen above, the start of the merged range is always the lesser
+        // of the two start offsets. OldEnd and NewEnd then need to be
+        // adjusted separately depending on the case. In any case, the change
+        // in text length of the merged range should be the sum of text length
+        // changes of the two original ranges, i.e.,
+        // newNewEnd - newOldEnd == newEnd1 - oldEnd1 + newEnd2 - oldEnd2
+        dst.mStart = std::min(dst.mStart, src.mStart);
+        if (src.mOldEnd < dst.mNewEnd) {
+            // New range overlaps or is within previous range; merge
+            dst.mNewEnd += src.mNewEnd - src.mOldEnd;
+        } else { // src.mOldEnd >= dst.mNewEnd
+            // New range overlaps previous range; merge
+            dst.mOldEnd += src.mOldEnd - dst.mNewEnd;
+            dst.mNewEnd = src.mNewEnd;
+        }
+        // src merged to dst; delete src.
+        mIMETextChanges.RemoveElementAt(srcIndex);
+        // Any ranges that we skip over between src and dst are not mergeable
+        // so we can safely continue the merge starting at dst
+        srcIndex = dstIndex;
+    }
+}
+
+void
+GeckoEditableSupport::PostFlushIMEChanges()
+{
+    if (!mIMETextChanges.IsEmpty() || mIMESelectionChanged) {
+        // Already posted
+        return;
+    }
+
+    RefPtr<GeckoEditableSupport> self(this);
+
+    nsAppShell::PostEvent([this, self] {
+        nsCOMPtr<nsIWidget> widget = GetWidget();
+        if (widget && !widget->Destroyed()) {
+            FlushIMEChanges();
+        }
+    });
+}
+
+void
+GeckoEditableSupport::FlushIMEChanges(FlushChangesFlag aFlags)
+{
+    // Only send change notifications if we are *not* masking events,
+    // i.e. if we have a focused editor,
+    NS_ENSURE_TRUE_VOID(!mIMEMaskEventsCount);
+
+    nsCOMPtr<nsIWidget> widget = GetWidget();
+    NS_ENSURE_TRUE_VOID(widget);
+
+    struct TextRecord
+    {
+        nsString text;
+        int32_t start;
+        int32_t oldEnd;
+        int32_t newEnd;
+    };
+    AutoTArray<TextRecord, 4> textTransaction;
+    textTransaction.SetCapacity(mIMETextChanges.Length());
+
+    nsEventStatus status = nsEventStatus_eIgnore;
+    mIMETextChangedDuringFlush = false;
+
+    auto shouldAbort = [=] (bool aForce) -> bool {
+        if (!aForce && !mIMETextChangedDuringFlush) {
+            return false;
+        }
+        // A query event could have triggered more text changes to come in, as
+        // indicated by our flag. If that happens, try flushing IME changes
+        // again.
+        if (aFlags == FLUSH_FLAG_NONE) {
+            FlushIMEChanges(FLUSH_FLAG_RETRY);
+        } else {
+            // Don't retry if already retrying, to avoid infinite loops.
+            __android_log_print(ANDROID_LOG_WARN, "GeckoEditableSupport",
+                    "Already retrying IME flush");
+        }
+        return true;
+    };
+
+    for (const IMETextChange &change : mIMETextChanges) {
+        if (change.mStart == change.mOldEnd &&
+                change.mStart == change.mNewEnd) {
+            continue;
+        }
+
+        WidgetQueryContentEvent event(true, eQueryTextContent, widget);
+
+        if (change.mNewEnd != change.mStart) {
+            event.InitForQueryTextContent(change.mStart,
+                                          change.mNewEnd - change.mStart);
+            widget->DispatchEvent(&event, status);
+
+            if (shouldAbort(NS_WARN_IF(!event.mSucceeded))) {
+                return;
+            }
+        }
+
+        textTransaction.AppendElement(
+                TextRecord{event.mReply.mString, change.mStart,
+                           change.mOldEnd, change.mNewEnd});
+    }
+
+    int32_t selStart = -1;
+    int32_t selEnd = -1;
+
+    if (mIMESelectionChanged) {
+        WidgetQueryContentEvent event(true, eQuerySelectedText, widget);
+        widget->DispatchEvent(&event, status);
+
+        if (shouldAbort(NS_WARN_IF(!event.mSucceeded))) {
+            return;
+        }
+
+        selStart = int32_t(event.GetSelectionStart());
+        selEnd = int32_t(event.GetSelectionEnd());
+    }
+
+    JNIEnv* const env = jni::GetGeckoThreadEnv();
+    auto flushOnException = [=] () -> bool {
+        if (!env->ExceptionCheck()) {
+            return false;
+        }
+        if (aFlags != FLUSH_FLAG_RECOVER) {
+            // First time seeing an exception; try flushing text.
+            env->ExceptionClear();
+            __android_log_print(ANDROID_LOG_WARN, "GeckoEditableSupport",
+                    "Recovering from IME exception");
+            FlushIMEText(FLUSH_FLAG_RECOVER);
+        } else {
+            // Give up because we've already tried.
+            MOZ_CATCH_JNI_EXCEPTION(env);
+        }
+        return true;
+    };
+
+    // Commit the text change and selection change transaction.
+    mIMETextChanges.Clear();
+
+    for (const TextRecord& record : textTransaction) {
+        mEditable->OnTextChange(record.text, record.start,
+                                record.oldEnd, record.newEnd);
+        if (flushOnException()) {
+            return;
+        }
+    }
+
+    if (mIMESelectionChanged) {
+        mIMESelectionChanged = false;
+        mEditable->OnSelectionChange(selStart, selEnd);
+        flushOnException();
+    }
+}
+
+void
+GeckoEditableSupport::FlushIMEText(FlushChangesFlag aFlags)
+{
+    // Notify Java of the newly focused content
+    mIMETextChanges.Clear();
+    mIMESelectionChanged = true;
+
+    // Use 'INT32_MAX / 2' here because subsequent text changes might combine
+    // with this text change, and overflow might occur if we just use
+    // INT32_MAX.
+    IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
+    notification.mTextChangeData.mStartOffset = 0;
+    notification.mTextChangeData.mRemovedEndOffset = INT32_MAX / 2;
+    notification.mTextChangeData.mAddedEndOffset = INT32_MAX / 2;
+    NotifyIME(mDispatcher, notification);
+
+    FlushIMEChanges(aFlags);
+}
+
+void
+GeckoEditableSupport::UpdateCompositionRects()
+{
+    nsCOMPtr<nsIWidget> widget = GetWidget();
+    RefPtr<TextComposition> composition(GetComposition());
+    NS_ENSURE_TRUE_VOID(mDispatcher && widget);
+
+    if (!composition) {
+        return;
+    }
+
+    nsEventStatus status = nsEventStatus_eIgnore;
+    uint32_t offset = composition->NativeOffsetOfStartComposition();
+    WidgetQueryContentEvent textRects(true, eQueryTextRectArray, widget);
+    textRects.InitForQueryTextRectArray(offset, composition->String().Length());
+    widget->DispatchEvent(&textRects, status);
+
+    auto rects = ConvertRectArrayToJavaRectFArray(
+            textRects.mReply.mRectArray,
+            widget->WidgetToScreenOffset(),
+            widget->GetDefaultScale());
+
+    mEditable->UpdateCompositionRects(rects);
+}
+
+void
+GeckoEditableSupport::OnImeSynchronize()
+{
+    if (!mIMEMaskEventsCount) {
+        FlushIMEChanges();
+    }
+    mEditable->NotifyIME(GeckoEditableListener::NOTIFY_IME_REPLY_EVENT);
+}
+
+void
+GeckoEditableSupport::OnImeReplaceText(int32_t aStart, int32_t aEnd,
+                                       jni::String::Param aText)
+{
+    AutoIMESynchronize as(this);
+
+    if (mIMEMaskEventsCount > 0) {
+        // Not focused; still reply to events, but don't do anything else.
+        return;
+    }
+
+    if (mWindow) {
+        mWindow->UserActivity();
+    }
+
+    /*
+        Replace text in Gecko thread from aStart to aEnd with the string text.
+    */
+    nsCOMPtr<nsIWidget> widget = GetWidget();
+    NS_ENSURE_TRUE_VOID(mDispatcher && widget);
+    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+
+    RefPtr<TextComposition> composition(GetComposition());
+    MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
+
+    nsString string(aText->ToString());
+    const bool composing = !mIMERanges->IsEmpty();
+    nsEventStatus status = nsEventStatus_eIgnore;
+
+    if (!mIMEKeyEvents.IsEmpty() || !mDispatcher->IsComposing() ||
+        uint32_t(aStart) != composition->NativeOffsetOfStartComposition() ||
+        uint32_t(aEnd) != composition->NativeOffsetOfStartComposition() +
+                          composition->String().Length()) {
+        // Only start a new composition if we have key events,
+        // if we don't have an existing composition, or
+        // the replaced text does not match our composition.
+        RemoveComposition();
+
+        {
+            // Use text selection to set target position(s) for
+            // insert, or replace, of text.
+            WidgetSelectionEvent event(true, eSetSelection, widget);
+            event.mOffset = uint32_t(aStart);
+            event.mLength = uint32_t(aEnd - aStart);
+            event.mExpandToClusterBoundary = false;
+            event.mReason = nsISelectionListener::IME_REASON;
+            widget->DispatchEvent(&event, status);
+        }
+
+        if (!mIMEKeyEvents.IsEmpty()) {
+            for (uint32_t i = 0; i < mIMEKeyEvents.Length(); i++) {
+                const auto event = mIMEKeyEvents[i]->AsKeyboardEvent();
+                // widget for duplicated events is initially nullptr.
+                event->mWidget = widget;
+
+                if (event->mMessage == eKeyPress) {
+                    mDispatcher->MaybeDispatchKeypressEvents(*event, status);
+                } else {
+                    mDispatcher->DispatchKeyboardEvent(
+                            event->mMessage, *event, status);
+                }
+                if (widget->Destroyed()) {
+                    break;
+                }
+            }
+            mIMEKeyEvents.Clear();
+            return;
+        }
+
+        if (aStart != aEnd) {
+            // Perform a deletion first.
+            WidgetContentCommandEvent event(
+                    true, eContentCommandDelete, widget);
+            event.mTime = PR_Now() / 1000;
+            widget->DispatchEvent(&event, status);
+            if (widget->Destroyed()) {
+                return;
+            }
+        }
+    } else if (composition->String().Equals(string)) {
+        /* If the new text is the same as the existing composition text,
+         * the NS_COMPOSITION_CHANGE event does not generate a text
+         * change notification. However, the Java side still expects
+         * one, so we manually generate a notification. */
+        IMETextChange dummyChange;
+        dummyChange.mStart = aStart;
+        dummyChange.mOldEnd = dummyChange.mNewEnd = aEnd;
+        AddIMETextChange(dummyChange);
+    }
+
+    if (mInputContext.mMayBeIMEUnaware) {
+        SendIMEDummyKeyEvent(widget, eKeyDown);
+        if (widget->Destroyed()) {
+            return;
+        }
+    }
+
+    if (composing) {
+        mDispatcher->SetPendingComposition(string, mIMERanges);
+        mDispatcher->FlushPendingComposition(status);
+        // Ensure IME ranges are empty.
+        mIMERanges->Clear();
+    } else if (!string.IsEmpty() || mDispatcher->IsComposing()) {
+        mDispatcher->CommitComposition(status, &string);
+    }
+    if (widget->Destroyed()) {
+        return;
+    }
+
+    if (mInputContext.mMayBeIMEUnaware) {
+        SendIMEDummyKeyEvent(widget, eKeyUp);
+        // Widget may be destroyed after dispatching the above event.
+    }
+}
+
+void
+GeckoEditableSupport::OnImeAddCompositionRange(
+        int32_t aStart, int32_t aEnd, int32_t aRangeType, int32_t aRangeStyle,
+        int32_t aRangeLineStyle, bool aRangeBoldLine, int32_t aRangeForeColor,
+        int32_t aRangeBackColor, int32_t aRangeLineColor)
+{
+    if (mIMEMaskEventsCount > 0) {
+        // Not focused.
+        return;
+    }
+
+    TextRange range;
+    range.mStartOffset = aStart;
+    range.mEndOffset = aEnd;
+    range.mRangeType = ToTextRangeType(aRangeType);
+    range.mRangeStyle.mDefinedStyles = aRangeStyle;
+    range.mRangeStyle.mLineStyle = aRangeLineStyle;
+    range.mRangeStyle.mIsBoldLine = aRangeBoldLine;
+    range.mRangeStyle.mForegroundColor =
+            ConvertAndroidColor(uint32_t(aRangeForeColor));
+    range.mRangeStyle.mBackgroundColor =
+            ConvertAndroidColor(uint32_t(aRangeBackColor));
+    range.mRangeStyle.mUnderlineColor =
+            ConvertAndroidColor(uint32_t(aRangeLineColor));
+    mIMERanges->AppendElement(range);
+}
+
+void
+GeckoEditableSupport::OnImeUpdateComposition(int32_t aStart, int32_t aEnd)
+{
+    if (mIMEMaskEventsCount > 0) {
+        // Not focused.
+        return;
+    }
+
+    nsCOMPtr<nsIWidget> widget = GetWidget();
+    nsEventStatus status = nsEventStatus_eIgnore;
+    NS_ENSURE_TRUE_VOID(mDispatcher && widget);
+
+    // A composition with no ranges means we want to set the selection.
+    if (mIMERanges->IsEmpty()) {
+        MOZ_ASSERT(aStart >= 0 && aEnd >= 0);
+        RemoveComposition();
+
+        WidgetSelectionEvent selEvent(true, eSetSelection, widget);
+        selEvent.mOffset = std::min(aStart, aEnd);
+        selEvent.mLength = std::max(aStart, aEnd) - selEvent.mOffset;
+        selEvent.mReversed = aStart > aEnd;
+        selEvent.mExpandToClusterBoundary = false;
+        widget->DispatchEvent(&selEvent, status);
+        return;
+    }
+
+    /**
+     * Update the composition from aStart to aEnd using information from added
+     * ranges. This is only used for visual indication and does not affect the
+     * text content.  Only the offsets are specified and not the text content
+     * to eliminate the possibility of this event altering the text content
+     * unintentionally.
+     */
+    nsString string;
+    RefPtr<TextComposition> composition(GetComposition());
+    MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
+
+    if (!mDispatcher->IsComposing() ||
+        uint32_t(aStart) != composition->NativeOffsetOfStartComposition() ||
+        uint32_t(aEnd) != composition->NativeOffsetOfStartComposition() +
+                          composition->String().Length()) {
+        // Only start new composition if we don't have an existing one,
+        // or if the existing composition doesn't match the new one.
+        RemoveComposition();
+
+        {
+            WidgetSelectionEvent event(true, eSetSelection, widget);
+            event.mOffset = uint32_t(aStart);
+            event.mLength = uint32_t(aEnd - aStart);
+            event.mExpandToClusterBoundary = false;
+            event.mReason = nsISelectionListener::IME_REASON;
+            widget->DispatchEvent(&event, status);
+        }
+
+        {
+            WidgetQueryContentEvent event(true, eQuerySelectedText, widget);
+            widget->DispatchEvent(&event, status);
+            MOZ_ASSERT(event.mSucceeded);
+            string = event.mReply.mString;
+        }
+    } else {
+        // If the new composition matches the existing composition,
+        // reuse the old composition.
+        string = composition->String();
+    }
+
+#ifdef DEBUG_ANDROID_IME
+    const NS_ConvertUTF16toUTF8 data(event.mData);
+    const char* text = data.get();
+    ALOGIME("IME: IME_SET_TEXT: text=\"%s\", length=%u, range=%u",
+            text, event.mData.Length(), event.mRanges->Length());
+#endif // DEBUG_ANDROID_IME
+
+    NS_ENSURE_SUCCESS_VOID(mDispatcher->BeginNativeInputTransaction());
+    mDispatcher->SetPendingComposition(string, mIMERanges);
+    mDispatcher->FlushPendingComposition(status);
+    mIMERanges->Clear();
+}
+
+void
+GeckoEditableSupport::OnImeRequestCursorUpdates(int aRequestMode)
+{
+    if (aRequestMode == IME_MONITOR_CURSOR_ONE_SHOT) {
+        UpdateCompositionRects();
+        return;
+    }
+
+    mIMEMonitorCursor = (aRequestMode == IME_MONITOR_CURSOR_START_MONITOR);
+}
+
+void
+GeckoEditableSupport::AsyncNotifyIME(int32_t aNotification)
+{
+    RefPtr<GeckoEditableSupport> self(this);
+
+    nsAppShell::PostEvent([this, self, aNotification] {
+        if (!mIMEMaskEventsCount) {
+            mEditable->NotifyIME(aNotification);
+        }
+    });
+}
+
+nsresult
+GeckoEditableSupport::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
+                                const IMENotification& aNotification)
+{
+    MOZ_ASSERT(mEditable);
+
+    switch (aNotification.mMessage) {
+        case REQUEST_TO_COMMIT_COMPOSITION: {
+            ALOGIME("IME: REQUEST_TO_COMMIT_COMPOSITION");
+
+            RemoveComposition(COMMIT_IME_COMPOSITION);
+            AsyncNotifyIME(GeckoEditableListener::
+                           NOTIFY_IME_TO_COMMIT_COMPOSITION);
+            break;
+        }
+
+        case REQUEST_TO_CANCEL_COMPOSITION: {
+            ALOGIME("IME: REQUEST_TO_CANCEL_COMPOSITION");
+
+            RemoveComposition(CANCEL_IME_COMPOSITION);
+            AsyncNotifyIME(GeckoEditableListener::
+                           NOTIFY_IME_TO_CANCEL_COMPOSITION);
+            break;
+        }
+
+        case NOTIFY_IME_OF_FOCUS: {
+            ALOGIME("IME: NOTIFY_IME_OF_FOCUS");
+
+            RefPtr<GeckoEditableSupport> self(this);
+            RefPtr<TextEventDispatcher> dispatcher = aTextEventDispatcher;
+
+            // Post an event because we have to flush the text before sending a
+            // focus event, and we may not be able to flush text during the
+            // NotifyIME call.
+            nsAppShell::PostEvent([this, self, dispatcher] {
+                nsCOMPtr<nsIWidget> widget = dispatcher->GetWidget();
+
+                --mIMEMaskEventsCount;
+                if (mIMEMaskEventsCount || !widget || widget->Destroyed()) {
+                    return;
+                }
+
+                mDispatcher = dispatcher;
+                FlushIMEText();
+
+                // IME will call requestCursorUpdates after getting context.
+                // So reset cursor update mode before getting context.
+                mIMEMonitorCursor = false;
+
+                MOZ_ASSERT(mEditable);
+                mEditable->NotifyIME(
+                        GeckoEditableListener::NOTIFY_IME_OF_FOCUS);
+            });
+            break;
+        }
+
+        case NOTIFY_IME_OF_BLUR: {
+            ALOGIME("IME: NOTIFY_IME_OF_BLUR");
+
+            if (!mIMEMaskEventsCount) {
+                mEditable->NotifyIME(GeckoEditableListener::NOTIFY_IME_OF_BLUR);
+                mDispatcher = nullptr;
+            }
+
+            // Mask events because we lost focus. Unmask on the next focus.
+            mIMEMaskEventsCount++;
+            break;
+        }
+
+        case NOTIFY_IME_OF_SELECTION_CHANGE: {
+            ALOGIME("IME: NOTIFY_IME_OF_SELECTION_CHANGE");
+
+            PostFlushIMEChanges();
+            mIMESelectionChanged = true;
+            break;
+        }
+
+        case NOTIFY_IME_OF_TEXT_CHANGE: {
+            ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d",
+                    aNotification.mTextChangeData.mStartOffset,
+                    aNotification.mTextChangeData.mRemovedEndOffset,
+                    aNotification.mTextChangeData.mAddedEndOffset);
+
+            /* Make sure Java's selection is up-to-date */
+            PostFlushIMEChanges();
+            mIMESelectionChanged = true;
+            AddIMETextChange(IMETextChange(aNotification));
+            break;
+        }
+
+        case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
+            ALOGIME("IME: NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED");
+
+            // Hardware keyboard support requires each string rect.
+            if (mIMEMonitorCursor) {
+                UpdateCompositionRects();
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+    return NS_OK;
+}
+
+void
+GeckoEditableSupport::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
+{
+}
+
+void
+GeckoEditableSupport::WillDispatchKeyboardEvent(
+        TextEventDispatcher* aTextEventDispatcher,
+        WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
+        void* aData)
+{
+}
+
+nsIMEUpdatePreference
+GeckoEditableSupport::GetIMEUpdatePreference()
+{
+    // While a plugin has focus, Listener doesn't need any notifications.
+    if (GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
+      return nsIMEUpdatePreference();
+    }
+    return nsIMEUpdatePreference(nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
+}
+
+void
+GeckoEditableSupport::SetInputContext(const InputContext& aContext,
+                                      const InputContextAction& aAction)
+{
+    MOZ_ASSERT(mEditable);
+
+    ALOGIME("IME: SetInputContext: s=0x%X, 0x%X, action=0x%X, 0x%X",
+            aContext.mIMEState.mEnabled, aContext.mIMEState.mOpen,
+            aAction.mCause, aAction.mFocusChange);
+
+    // Ensure that opening the virtual keyboard is allowed for this specific
+    // InputContext depending on the content.ime.strict.policy pref
+    if (aContext.mIMEState.mEnabled != IMEState::DISABLED &&
+        aContext.mIMEState.mEnabled != IMEState::PLUGIN &&
+        Preferences::GetBool("content.ime.strict_policy", false) &&
+        !aAction.ContentGotFocusByTrustedCause() &&
+        !aAction.UserMightRequestOpenVKB()) {
+        return;
+    }
+
+    IMEState::Enabled enabled = aContext.mIMEState.mEnabled;
+
+    // Only show the virtual keyboard for plugins if mOpen is set appropriately.
+    // This avoids showing it whenever a plugin is focused. Bug 747492
+    if (aContext.mIMEState.mEnabled == IMEState::PLUGIN &&
+        aContext.mIMEState.mOpen != IMEState::OPEN) {
+        enabled = IMEState::DISABLED;
+    }
+
+    mInputContext = aContext;
+    mInputContext.mIMEState.mEnabled = enabled;
+
+    if (enabled == IMEState::ENABLED && aAction.UserMightRequestOpenVKB()) {
+        // Don't reset keyboard when we should simply open the vkb
+        mEditable->NotifyIME(GeckoEditableListener::NOTIFY_IME_OPEN_VKB);
+        return;
+    }
+
+    if (mIMEUpdatingContext) {
+        return;
+    }
+    mIMEUpdatingContext = true;
+
+    RefPtr<GeckoEditableSupport> self(this);
+
+    nsAppShell::PostEvent([this, self] {
+        nsCOMPtr<nsIWidget> widget = GetWidget();
+
+        mIMEUpdatingContext = false;
+        if (!widget || widget->Destroyed()) {
+            return;
+        }
+        mEditable->NotifyIMEContext(mInputContext.mIMEState.mEnabled,
+                                    mInputContext.mHTMLInputType,
+                                    mInputContext.mHTMLInputInputmode,
+                                    mInputContext.mActionHint);
+    });
+}
+
+InputContext
+GeckoEditableSupport::GetInputContext()
+{
+    InputContext context = mInputContext;
+    context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
+    return context;
+}
+
+} // namespace widget
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/android/GeckoEditableSupport.h
@@ -0,0 +1,217 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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 mozilla_widget_GeckoEditableSupport_h
+#define mozilla_widget_GeckoEditableSupport_h
+
+#include "GeneratedJNIWrappers.h"
+#include "nsAppShell.h"
+#include "nsIWidget.h"
+#include "nsTArray.h"
+
+#include "mozilla/TextEventDispatcher.h"
+#include "mozilla/TextEventDispatcherListener.h"
+#include "mozilla/UniquePtr.h"
+
+class nsWindow;
+
+namespace mozilla {
+
+class TextComposition;
+
+namespace widget {
+
+class GeckoEditableSupport final
+    : public TextEventDispatcherListener
+    , public java::GeckoEditable::Natives<GeckoEditableSupport>
+{
+    /*
+        Rules for managing IME between Gecko and Java:
+
+        * Gecko controls the text content, and Java shadows the Gecko text
+           through text updates
+        * Gecko and Java maintain separate selections, and synchronize when
+           needed through selection updates and set-selection events
+        * Java controls the composition, and Gecko shadows the Java
+           composition through update composition events
+    */
+
+    using EditableBase = java::GeckoEditable::Natives<GeckoEditableSupport>;
+
+    // RAII helper class that automatically sends an event reply through
+    // OnImeSynchronize, as required by events like OnImeReplaceText.
+    class AutoIMESynchronize
+    {
+        GeckoEditableSupport* const mGES;
+    public:
+        AutoIMESynchronize(GeckoEditableSupport* ges) : mGES(ges) {}
+        ~AutoIMESynchronize() { mGES->OnImeSynchronize(); }
+    };
+
+    struct IMETextChange final {
+        int32_t mStart, mOldEnd, mNewEnd;
+
+        IMETextChange() :
+            mStart(-1), mOldEnd(-1), mNewEnd(-1) {}
+
+        IMETextChange(const IMENotification& aIMENotification)
+            : mStart(aIMENotification.mTextChangeData.mStartOffset)
+            , mOldEnd(aIMENotification.mTextChangeData.mRemovedEndOffset)
+            , mNewEnd(aIMENotification.mTextChangeData.mAddedEndOffset)
+        {
+            MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
+                       "IMETextChange initialized with wrong notification");
+            MOZ_ASSERT(aIMENotification.mTextChangeData.IsValid(),
+                       "The text change notification isn't initialized");
+            MOZ_ASSERT(aIMENotification.mTextChangeData.IsInInt32Range(),
+                       "The text change notification is out of range");
+        }
+
+        bool IsEmpty() const { return mStart < 0; }
+    };
+
+    enum FlushChangesFlag
+    {
+        // Not retrying.
+        FLUSH_FLAG_NONE,
+        // Retrying due to IME text changes during flush.
+        FLUSH_FLAG_RETRY,
+        // Retrying due to IME sync exceptions during flush.
+        FLUSH_FLAG_RECOVER
+    };
+
+    enum RemoveCompositionFlag
+    {
+        CANCEL_IME_COMPOSITION,
+        COMMIT_IME_COMPOSITION
+    };
+
+    nsWindow::WindowPtr<GeckoEditableSupport> mWindow; // Parent only
+    RefPtr<TextEventDispatcher> mDispatcher;
+    java::GeckoEditable::GlobalRef mEditable;
+    InputContext mInputContext;
+    AutoTArray<UniquePtr<mozilla::WidgetEvent>, 4> mIMEKeyEvents;
+    AutoTArray<IMETextChange, 4> mIMETextChanges;
+    RefPtr<TextRangeArray> mIMERanges;
+    int32_t mIMEMaskEventsCount; // Mask events when > 0.
+    bool mIMEUpdatingContext;
+    bool mIMESelectionChanged;
+    bool mIMETextChangedDuringFlush;
+    bool mIMEMonitorCursor;
+
+    nsIWidget* GetWidget() const
+    {
+        return mDispatcher ? mDispatcher->GetWidget() : mWindow;
+    }
+
+    virtual ~GeckoEditableSupport() {}
+
+    RefPtr<TextComposition> GetComposition() const;
+    void RemoveComposition(
+            RemoveCompositionFlag aFlag = COMMIT_IME_COMPOSITION);
+    void SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg);
+    void AddIMETextChange(const IMETextChange& aChange);
+    void PostFlushIMEChanges();
+    void FlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
+    void FlushIMEText(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
+    void AsyncNotifyIME(int32_t aNotification);
+    void UpdateCompositionRects();
+
+public:
+    template<typename Functor>
+    static void OnNativeCall(Functor&& aCall)
+    {
+        struct IMEEvent : nsAppShell::LambdaEvent<Functor>
+        {
+            using nsAppShell::LambdaEvent<Functor>::LambdaEvent;
+
+            nsAppShell::Event::Type ActivityType() const override
+            {
+                return nsAppShell::Event::Type::kUIActivity;
+            }
+        };
+        nsAppShell::PostEvent(mozilla::MakeUnique<IMEEvent>(
+                mozilla::Move(aCall)));
+    }
+
+    GeckoEditableSupport(nsWindow::NativePtr<GeckoEditableSupport>* aPtr,
+                         nsWindow* aWindow,
+                         java::GeckoEditable::Param aEditable)
+        : mWindow(aPtr, aWindow)
+        , mEditable(aEditable)
+        , mIMERanges(new TextRangeArray())
+        , mIMEMaskEventsCount(1) // Mask IME events since there's no focus yet
+        , mIMEUpdatingContext(false)
+        , mIMESelectionChanged(false)
+        , mIMETextChangedDuringFlush(false)
+        , mIMEMonitorCursor(false)
+    {}
+
+    NS_DECL_ISUPPORTS
+
+    // TextEventDispatcherListener methods
+    NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
+                         const IMENotification& aNotification) override;
+
+    NS_IMETHOD_(void) OnRemovedFrom(
+            TextEventDispatcher* aTextEventDispatcher) override;
+
+    NS_IMETHOD_(void) WillDispatchKeyboardEvent(
+            TextEventDispatcher* aTextEventDispatcher,
+            WidgetKeyboardEvent& aKeyboardEvent,
+            uint32_t aIndexOfKeypress,
+            void* aData) override;
+
+    nsIMEUpdatePreference GetIMEUpdatePreference();
+
+    void SetInputContext(const InputContext& aContext,
+                         const InputContextAction& aAction);
+
+    InputContext GetInputContext();
+
+    // GeckoEditable methods
+    using EditableBase::AttachNative;
+    using EditableBase::DisposeNative;
+
+    void OnDetach() {
+        mEditable->OnViewChange(nullptr);
+    }
+
+    void OnViewChange(java::GeckoView::Param aView) {
+        mEditable->OnViewChange(aView);
+    }
+
+    // Handle an Android KeyEvent.
+    void OnKeyEvent(int32_t aAction, int32_t aKeyCode, int32_t aScanCode,
+                    int32_t aMetaState, int32_t aKeyPressMetaState,
+                    int64_t aTime, int32_t aDomPrintableKeyValue,
+                    int32_t aRepeatCount, int32_t aFlags,
+                    bool aIsSynthesizedImeKey,
+                    jni::Object::Param originalEvent);
+
+    // Synchronize Gecko thread with the InputConnection thread.
+    void OnImeSynchronize();
+
+    // Replace a range of text with new text.
+    void OnImeReplaceText(int32_t aStart, int32_t aEnd,
+                          jni::String::Param aText);
+
+    // Add styling for a range within the active composition.
+    void OnImeAddCompositionRange(int32_t aStart, int32_t aEnd,
+            int32_t aRangeType, int32_t aRangeStyle, int32_t aRangeLineStyle,
+            bool aRangeBoldLine, int32_t aRangeForeColor,
+            int32_t aRangeBackColor, int32_t aRangeLineColor);
+
+    // Update styling for the active composition using previous-added ranges.
+    void OnImeUpdateComposition(int32_t aStart, int32_t aEnd);
+
+    // Set cursor mode whether IME requests
+    void OnImeRequestCursorUpdates(int aRequestMode);
+};
+
+} // namespace widget
+} // namespace mozill
+
+#endif // mozilla_widget_GeckoEditableSupport_h
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2107,17 +2107,17 @@ public:
         static constexpr char signature[] =
                 "()V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
+                mozilla::jni::DispatchTarget::GECKO;
     };
 
     struct NotifyIME_t {
         typedef GeckoEditable Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int32_t> Args;
@@ -2196,17 +2196,17 @@ public:
         static constexpr char signature[] =
                 "(IIIIIZIII)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
+                mozilla::jni::DispatchTarget::GECKO;
     };
 
     struct OnImeReplaceText_t {
         typedef GeckoEditable Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int32_t,
@@ -2234,34 +2234,34 @@ public:
         static constexpr char signature[] =
                 "(I)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
+                mozilla::jni::DispatchTarget::GECKO;
     };
 
     struct OnImeSynchronize_t {
         typedef GeckoEditable Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "onImeSynchronize";
         static constexpr char signature[] =
                 "()V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::PROXY;
+                mozilla::jni::DispatchTarget::GECKO;
     };
 
     struct OnImeUpdateComposition_t {
         typedef GeckoEditable Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int32_t,
@@ -2282,27 +2282,26 @@ public:
         typedef GeckoEditable Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int32_t,
                 int32_t,
                 int32_t,
                 int32_t,
+                int32_t,
                 int64_t,
                 int32_t,
                 int32_t,
                 int32_t,
-                int32_t,
-                int32_t,
                 bool,
                 mozilla::jni::Object::Param> Args;
         static constexpr char name[] = "onKeyEvent";
         static constexpr char signature[] =
-                "(IIIIJIIIIIZLandroid/view/KeyEvent;)V";
+                "(IIIIIJIIIZLandroid/view/KeyEvent;)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::PROXY;
     };
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -35,16 +35,17 @@ UNIFIED_SOURCES += [
     'AndroidCompositorWidget.cpp',
     'AndroidContentController.cpp',
     'AndroidJavaWrappers.cpp',
     'AndroidJNI.cpp',
     'AndroidJNIWrapper.cpp',
     'AndroidUiThread.cpp',
     'ANRReporter.cpp',
     'EventDispatcher.cpp',
+    'GeckoEditableSupport.cpp',
     'GeneratedJNIWrappers.cpp',
     'GfxInfo.cpp',
     'nsAndroidProtocolHandler.cpp',
     'nsAppShell.cpp',
     'nsClipboard.cpp',
     'nsDeviceContextAndroid.cpp',
     'nsIdleServiceAndroid.cpp',
     'nsLookAndFeel.cpp',
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -5,21 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <android/log.h>
 #include <android/native_window.h>
 #include <android/native_window_jni.h>
 #include <math.h>
 #include <unistd.h>
 
-#include "mozilla/IMEStateManager.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
-#include "mozilla/TextComposition.h"
-#include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/WeakPtr.h"
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Preferences.h"
@@ -28,19 +25,19 @@
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 using mozilla::Unused;
 
 #include "nsWindow.h"
 
 #include "nsIBaseWindow.h"
+#include "nsIBrowserDOMWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIObserverService.h"
-#include "nsISelection.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIWidgetListener.h"
 #include "nsIWindowWatcher.h"
 #include "nsIXULWindow.h"
 
 #include "nsAppShell.h"
 #include "nsFocusManager.h"
 #include "nsIdleService.h"
@@ -72,16 +69,17 @@ using mozilla::Unused;
 #include "nsTArray.h"
 
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
 #include "AndroidUiThread.h"
 #include "android_npapi.h"
 #include "FennecJNINatives.h"
 #include "GeneratedJNINatives.h"
+#include "GeckoEditableSupport.h"
 #include "KeyEvent.h"
 #include "MotionEvent.h"
 
 #include "imgIEncoder.h"
 
 #include "nsString.h"
 #include "GeckoProfiler.h" // For PROFILER_LABEL
 #include "nsIXULRuntime.h"
@@ -97,48 +95,27 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, n
 
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/CompositorSession.h"
 #include "mozilla/layers/LayerTransactionParent.h"
 #include "mozilla/layers/UiCompositorControllerChild.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 
-static TimeStamp
-GetEventTimeStamp(int64_t aEventTime)
-{
-    // Android's event time is SystemClock.uptimeMillis that is counted in ms
-    // since OS was booted.
-    // (https://developer.android.com/reference/android/os/SystemClock.html)
-    // and this SystemClock.uptimeMillis uses SYSTEM_TIME_MONOTONIC.
-    // Our posix implemententaion of TimeStamp::Now uses SYSTEM_TIME_MONOTONIC
-    //  too. Due to same implementation, we can use this via FromSystemTime.
-    int64_t tick =
-        BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime);
-    return TimeStamp::FromSystemTime(tick);
-}
-
 // All the toplevel windows that have been created; these are in
 // stacking order, so the window at gTopLevelWindows[0] is the topmost
 // one.
 static nsTArray<nsWindow*> gTopLevelWindows;
 
 static bool sFailedToCreateGLContext = false;
 
 // Multitouch swipe thresholds in inches
 static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
 static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
 
-// Sync with GeckoEditableView class
-static const int IME_MONITOR_CURSOR_ONE_SHOT = 1;
-static const int IME_MONITOR_CURSOR_START_MONITOR = 2;
-static const int IME_MONITOR_CURSOR_END_MONITOR = 3;
-
-static Modifiers GetModifiers(int32_t metaState);
-
 template<typename Lambda, bool IsStatic, typename InstanceType, class Impl>
 class nsWindow::WindowEvent : public nsAppShell::LambdaEvent<Lambda>
 {
     typedef nsAppShell::Event Event;
     typedef nsAppShell::LambdaEvent<Lambda> Base;
 
     bool IsStaleCall()
     {
@@ -188,29 +165,48 @@ public:
     }
 
     Event::Type ActivityType() const override
     {
         return mEventType;
     }
 };
 
+namespace {
+    template<class Instance, class Impl> typename EnableIf<
+        jni::detail::NativePtrPicker<Impl>::value ==
+        jni::detail::REFPTR, void>::Type
+    CallAttachNative(Instance aInstance, Impl* aImpl)
+    {
+        Impl::AttachNative(aInstance, RefPtr<Impl>(aImpl).get());
+    }
+
+    template<class Instance, class Impl> typename EnableIf<
+        jni::detail::NativePtrPicker<Impl>::value ==
+        jni::detail::OWNING, void>::Type
+    CallAttachNative(Instance aInstance, Impl* aImpl)
+    {
+        Impl::AttachNative(aInstance, UniquePtr<Impl>(aImpl));
+    }
+} // namespace
+
 template<class Impl>
 template<class Instance, typename... Args> void
 nsWindow::NativePtr<Impl>::Attach(Instance aInstance, nsWindow* aWindow,
                                   Args&&... aArgs)
 {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(!mPtr && !mImpl);
 
-    auto impl = mozilla::MakeUnique<Impl>(
+    Impl* const impl = new Impl(
             this, aWindow, mozilla::Forward<Args>(aArgs)...);
-    mImpl = impl.get();
+    mImpl = impl;
 
-    Impl::AttachNative(aInstance, mozilla::Move(impl));
+    // CallAttachNative transfers ownership of impl.
+    CallAttachNative<Instance, Impl>(aInstance, impl);
 }
 
 template<class Impl> void
 nsWindow::NativePtr<Impl>::Detach()
 {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mPtr && mImpl);
 
@@ -236,125 +232,54 @@ public:
         : MutexAutoLock(aPtr.mImplLock)
         , mImpl(aPtr.mImpl)
     {}
 
     operator Impl*() const { return mImpl; }
     Impl* operator->() const { return mImpl; }
 };
 
-template<class Impl>
-class nsWindow::WindowPtr final
-{
-    friend NativePtr<Impl>;
-
-    NativePtr<Impl>* mPtr;
-    nsWindow* mWindow;
-    Mutex mWindowLock;
-
-public:
-    class Locked final : private MutexAutoLock
-    {
-        nsWindow* const mWindow;
-
-    public:
-        Locked(WindowPtr<Impl>& aPtr)
-            : MutexAutoLock(aPtr.mWindowLock)
-            , mWindow(aPtr.mWindow)
-        {}
-
-        operator nsWindow*() const { return mWindow; }
-        nsWindow* operator->() const { return mWindow; }
-    };
-
-    WindowPtr(NativePtr<Impl>* aPtr, nsWindow* aWindow)
-        : mPtr(aPtr)
-        , mWindow(aWindow)
-        , mWindowLock(NativePtr<Impl>::sName)
-    {
-        MOZ_ASSERT(NS_IsMainThread());
-        mPtr->mPtr = this;
-    }
-
-    ~WindowPtr()
-    {
-        MOZ_ASSERT(NS_IsMainThread());
-        if (!mPtr) {
-            return;
-        }
-        mPtr->mPtr = nullptr;
-        mPtr->mImpl = nullptr;
-    }
-
-    operator nsWindow*() const
-    {
-        MOZ_ASSERT(NS_IsMainThread());
-        return mWindow;
-    }
-
-    nsWindow* operator->() const { return operator nsWindow*(); }
-};
-
 
 class nsWindow::GeckoViewSupport final
     : public GeckoView::Window::Natives<GeckoViewSupport>
-    , public GeckoEditable::Natives<GeckoViewSupport>
     , public SupportsWeakPtr<GeckoViewSupport>
 {
     nsWindow& window;
 
 public:
     typedef GeckoView::Window::Natives<GeckoViewSupport> Base;
-    typedef GeckoEditable::Natives<GeckoViewSupport> EditableBase;
     typedef SupportsWeakPtr<GeckoViewSupport> SupportsWeakPtr;
 
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GeckoViewSupport);
 
     template<typename Functor>
     static void OnNativeCall(Functor&& aCall)
     {
         if (aCall.IsTarget(&Open) && NS_IsMainThread()) {
             // Gecko state probably just switched to PROFILE_READY, and the
             // event loop is not running yet. Skip the event loop here so we
             // can get a head start on opening our window.
             return aCall();
         }
 
-        const nsAppShell::Event::Type eventType =
-                aCall.IsTarget(&GeckoViewSupport::OnKeyEvent) ||
-                aCall.IsTarget(&GeckoViewSupport::OnImeReplaceText) ||
-                aCall.IsTarget(&GeckoViewSupport::OnImeUpdateComposition) ?
-                nsAppShell::Event::Type::kUIActivity :
-                nsAppShell::Event::Type::kGeneralActivity;
-
         nsAppShell::PostEvent(mozilla::MakeUnique<WindowEvent<Functor>>(
-                mozilla::Move(aCall), eventType));
+                mozilla::Move(aCall)));
     }
 
     GeckoViewSupport(nsWindow* aWindow,
                      const GeckoView::Window::LocalRef& aInstance,
                      GeckoView::Param aView)
         : window(*aWindow)
-        , mEditable(GeckoEditable::New(aView))
-        , mIMERanges(new TextRangeArray())
-        , mIMEMaskEventsCount(1) // Mask IME events since there's no focus yet
-        , mIMEUpdatingContext(false)
-        , mIMESelectionChanged(false)
-        , mIMETextChangedDuringFlush(false)
-        , mIMEMonitorCursor(false)
     {
         Base::AttachNative(aInstance, static_cast<SupportsWeakPtr*>(this));
-        EditableBase::AttachNative(
-                mEditable, static_cast<SupportsWeakPtr*>(this));
     }
 
     ~GeckoViewSupport();
 
     using Base::DisposeNative;
-    using EditableBase::DisposeNative;
 
     /**
      * GeckoView methods
      */
 private:
     nsCOMPtr<nsPIDOMWindowOuter> mDOMWindow;
 
 public:
@@ -371,123 +296,16 @@ public:
     void Close();
 
     // Reattach this nsWindow to a new GeckoView.
     void Reattach(const GeckoView::Window::LocalRef& inst,
                   GeckoView::Param aView, jni::Object::Param aCompositor,
                   jni::Object::Param aDispatcher);
 
     void LoadUri(jni::String::Param aUri, int32_t aFlags);
-
-    /**
-     * GeckoEditable methods
-     */
-private:
-    /*
-        Rules for managing IME between Gecko and Java:
-
-        * Gecko controls the text content, and Java shadows the Gecko text
-           through text updates
-        * Gecko and Java maintain separate selections, and synchronize when
-           needed through selection updates and set-selection events
-        * Java controls the composition, and Gecko shadows the Java
-           composition through update composition events
-    */
-
-    struct IMETextChange final {
-        int32_t mStart, mOldEnd, mNewEnd;
-
-        IMETextChange() :
-            mStart(-1), mOldEnd(-1), mNewEnd(-1) {}
-
-        IMETextChange(const IMENotification& aIMENotification)
-            : mStart(aIMENotification.mTextChangeData.mStartOffset)
-            , mOldEnd(aIMENotification.mTextChangeData.mRemovedEndOffset)
-            , mNewEnd(aIMENotification.mTextChangeData.mAddedEndOffset)
-        {
-            MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
-                       "IMETextChange initialized with wrong notification");
-            MOZ_ASSERT(aIMENotification.mTextChangeData.IsValid(),
-                       "The text change notification isn't initialized");
-            MOZ_ASSERT(aIMENotification.mTextChangeData.IsInInt32Range(),
-                       "The text change notification is out of range");
-        }
-
-        bool IsEmpty() const { return mStart < 0; }
-    };
-
-    // GeckoEditable instance used by this nsWindow;
-    java::GeckoEditable::GlobalRef mEditable;
-    AutoTArray<mozilla::UniquePtr<mozilla::WidgetEvent>, 8> mIMEKeyEvents;
-    AutoTArray<IMETextChange, 4> mIMETextChanges;
-    InputContext mInputContext;
-    RefPtr<mozilla::TextRangeArray> mIMERanges;
-    int32_t mIMEMaskEventsCount; // Mask events when > 0.
-    bool mIMEUpdatingContext;
-    bool mIMESelectionChanged;
-    bool mIMETextChangedDuringFlush;
-    bool mIMEMonitorCursor;
-
-    void SendIMEDummyKeyEvents();
-    void AddIMETextChange(const IMETextChange& aChange);
-
-    enum FlushChangesFlag {
-        // Not retrying.
-        FLUSH_FLAG_NONE,
-        // Retrying due to IME text changes during flush.
-        FLUSH_FLAG_RETRY,
-        // Retrying due to IME sync exceptions during flush.
-        FLUSH_FLAG_RECOVER
-    };
-    void PostFlushIMEChanges();
-    void FlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
-    void FlushIMEText(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
-    void AsyncNotifyIME(int32_t aNotification);
-    void UpdateCompositionRects();
-
-public:
-    bool NotifyIME(const IMENotification& aIMENotification);
-    void SetInputContext(const InputContext& aContext,
-                         const InputContextAction& aAction);
-    InputContext GetInputContext();
-
-    // RAII helper class that automatically sends an event reply through
-    // OnImeSynchronize, as required by events like OnImeReplaceText.
-    class AutoIMESynchronize {
-        GeckoViewSupport* const mGVS;
-    public:
-        AutoIMESynchronize(GeckoViewSupport* gvs) : mGVS(gvs) {}
-        ~AutoIMESynchronize() { mGVS->OnImeSynchronize(); }
-    };
-
-    // Handle an Android KeyEvent.
-    void OnKeyEvent(int32_t aAction, int32_t aKeyCode, int32_t aScanCode,
-                    int32_t aMetaState, int64_t aTime, int32_t aUnicodeChar,
-                    int32_t aBaseUnicodeChar, int32_t aDomPrintableKeyValue,
-                    int32_t aRepeatCount, int32_t aFlags,
-                    bool aIsSynthesizedImeKey, jni::Object::Param originalEvent);
-
-    // Synchronize Gecko thread with the InputConnection thread.
-    void OnImeSynchronize();
-
-    // Replace a range of text with new text.
-    void OnImeReplaceText(int32_t aStart, int32_t aEnd,
-                          jni::String::Param aText);
-
-    // Add styling for a range within the active composition.
-    void OnImeAddCompositionRange(int32_t aStart, int32_t aEnd,
-            int32_t aRangeType, int32_t aRangeStyle, int32_t aRangeLineStyle,
-            bool aRangeBoldLine, int32_t aRangeForeColor,
-            int32_t aRangeBackColor, int32_t aRangeLineColor);
-
-    // Update styling for the active composition using previous-added ranges.
-    void OnImeUpdateComposition(int32_t aStart, int32_t aEnd);
-
-    // Set cursor mode whether IME requests
-    void OnImeRequestCursorUpdates(int aRequestMode);
 };
 
 /**
  * NativePanZoomController handles its native calls on the UI thread, so make
  * it separate from GeckoViewSupport.
  */
 class nsWindow::NPZCSupport final
     : public NativePanZoomController::Natives<NPZCSupport>
@@ -1365,18 +1183,18 @@ ANativeWindow* nsWindow::PMPMSupport::sW
 EGLSurface nsWindow::PMPMSupport::sSurface;
 
 
 nsWindow::GeckoViewSupport::~GeckoViewSupport()
 {
     // Disassociate our GeckoEditable instance with our native object.
     // OnDestroy will call disposeNative after any pending native calls have
     // been made.
-    MOZ_ASSERT(mEditable);
-    mEditable->OnViewChange(nullptr);
+    MOZ_ASSERT(window.mEditableSupport);
+    window.mEditableSupport.Detach();
 
     if (window.mNPZCSupport) {
         window.mNPZCSupport.Detach();
     }
 
     if (window.mLayerViewSupport) {
         window.mLayerViewSupport.Detach();
     }
@@ -1426,21 +1244,25 @@ nsWindow::GeckoViewSupport::Open(const j
             nsPIDOMWindowOuter::From(domWindow);
     nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(pdomWindow);
     MOZ_ASSERT(widget);
 
     const auto window = static_cast<nsWindow*>(widget.get());
     window->SetScreenId(aScreenId);
 
     // Attach a new GeckoView support object to the new window.
-    window->mGeckoViewSupport  = mozilla::MakeUnique<GeckoViewSupport>(
+    window->mGeckoViewSupport = mozilla::MakeUnique<GeckoViewSupport>(
             window, GeckoView::Window::LocalRef(aCls.Env(), aWindow), aView);
 
     window->mGeckoViewSupport->mDOMWindow = pdomWindow;
 
+    // Attach a new GeckoEditable support object to the new window.
+    auto editable = GeckoEditable::New(aView);
+    window->mEditableSupport.Attach(editable, window, editable);
+
     // Attach the Compositor to the new window.
     auto compositor = LayerView::Compositor::LocalRef(
             aCls.Env(), LayerView::Compositor::Ref::From(aCompositor));
     window->mLayerViewSupport.Attach(compositor, window, compositor);
 
     // Attach again using the new window.
     androidView->mEventDispatcher->Attach(
             java::EventDispatcher::Ref::From(aDispatcher), pdomWindow);
@@ -1474,17 +1296,18 @@ nsWindow::GeckoViewSupport::Close()
 
 void
 nsWindow::GeckoViewSupport::Reattach(const GeckoView::Window::LocalRef& inst,
                                      GeckoView::Param aView,
                                      jni::Object::Param aCompositor,
                                      jni::Object::Param aDispatcher)
 {
     // Associate our previous GeckoEditable with the new GeckoView.
-    mEditable->OnViewChange(aView);
+    MOZ_ASSERT(window.mEditableSupport);
+    window.mEditableSupport->OnViewChange(aView);
 
     // mNPZCSupport might have already been detached through the Java side calling
     // NativePanZoomController.destroy().
     if (window.mNPZCSupport) {
         window.mNPZCSupport.Detach();
     }
 
     MOZ_ASSERT(window.mLayerViewSupport);
@@ -1533,17 +1356,16 @@ nsWindow::GeckoViewSupport::LoadUri(jni:
         NS_WARNING("Failed to open URI");
     }
 }
 
 void
 nsWindow::InitNatives()
 {
     nsWindow::GeckoViewSupport::Base::Init();
-    nsWindow::GeckoViewSupport::EditableBase::Init();
     nsWindow::LayerViewSupport::Init();
     nsWindow::NPZCSupport::Init();
     if (jni::IsFennec()) {
         nsWindow::PMPMSupport::Init();
     }
 }
 
 nsWindow*
@@ -2206,1309 +2028,116 @@ nsWindow::DispatchHitTest(const WidgetTo
         DispatchEvent(&hittest, status);
 
         if (mAPZEventState && hittest.hitCluster) {
             mAPZEventState->ProcessClusterHit();
         }
     }
 }
 
-static unsigned int ConvertAndroidKeyCodeToDOMKeyCode(int androidKeyCode)
-{
-    // Special-case alphanumeric keycodes because they are most common.
-    if (androidKeyCode >= AKEYCODE_A &&
-        androidKeyCode <= AKEYCODE_Z) {
-        return androidKeyCode - AKEYCODE_A + NS_VK_A;
-    }
-
-    if (androidKeyCode >= AKEYCODE_0 &&
-        androidKeyCode <= AKEYCODE_9) {
-        return androidKeyCode - AKEYCODE_0 + NS_VK_0;
-    }
-
-    switch (androidKeyCode) {
-        // KEYCODE_UNKNOWN (0) ... KEYCODE_HOME (3)
-        case AKEYCODE_BACK:               return NS_VK_ESCAPE;
-        // KEYCODE_CALL (5) ... KEYCODE_POUND (18)
-        case AKEYCODE_DPAD_UP:            return NS_VK_UP;
-        case AKEYCODE_DPAD_DOWN:          return NS_VK_DOWN;
-        case AKEYCODE_DPAD_LEFT:          return NS_VK_LEFT;
-        case AKEYCODE_DPAD_RIGHT:         return NS_VK_RIGHT;
-        case AKEYCODE_DPAD_CENTER:        return NS_VK_RETURN;
-        case AKEYCODE_VOLUME_UP:          return NS_VK_VOLUME_UP;
-        case AKEYCODE_VOLUME_DOWN:        return NS_VK_VOLUME_DOWN;
-        // KEYCODE_VOLUME_POWER (26) ... KEYCODE_Z (54)
-        case AKEYCODE_COMMA:              return NS_VK_COMMA;
-        case AKEYCODE_PERIOD:             return NS_VK_PERIOD;
-        case AKEYCODE_ALT_LEFT:           return NS_VK_ALT;
-        case AKEYCODE_ALT_RIGHT:          return NS_VK_ALT;
-        case AKEYCODE_SHIFT_LEFT:         return NS_VK_SHIFT;
-        case AKEYCODE_SHIFT_RIGHT:        return NS_VK_SHIFT;
-        case AKEYCODE_TAB:                return NS_VK_TAB;
-        case AKEYCODE_SPACE:              return NS_VK_SPACE;
-        // KEYCODE_SYM (63) ... KEYCODE_ENVELOPE (65)
-        case AKEYCODE_ENTER:              return NS_VK_RETURN;
-        case AKEYCODE_DEL:                return NS_VK_BACK; // Backspace
-        case AKEYCODE_GRAVE:              return NS_VK_BACK_QUOTE;
-        // KEYCODE_MINUS (69)
-        case AKEYCODE_EQUALS:             return NS_VK_EQUALS;
-        case AKEYCODE_LEFT_BRACKET:       return NS_VK_OPEN_BRACKET;
-        case AKEYCODE_RIGHT_BRACKET:      return NS_VK_CLOSE_BRACKET;
-        case AKEYCODE_BACKSLASH:          return NS_VK_BACK_SLASH;
-        case AKEYCODE_SEMICOLON:          return NS_VK_SEMICOLON;
-        // KEYCODE_APOSTROPHE (75)
-        case AKEYCODE_SLASH:              return NS_VK_SLASH;
-        // KEYCODE_AT (77) ... KEYCODE_MEDIA_FAST_FORWARD (90)
-        case AKEYCODE_MUTE:               return NS_VK_VOLUME_MUTE;
-        case AKEYCODE_PAGE_UP:            return NS_VK_PAGE_UP;
-        case AKEYCODE_PAGE_DOWN:          return NS_VK_PAGE_DOWN;
-        // KEYCODE_PICTSYMBOLS (94) ... KEYCODE_BUTTON_MODE (110)
-        case AKEYCODE_ESCAPE:             return NS_VK_ESCAPE;
-        case AKEYCODE_FORWARD_DEL:        return NS_VK_DELETE;
-        case AKEYCODE_CTRL_LEFT:          return NS_VK_CONTROL;
-        case AKEYCODE_CTRL_RIGHT:         return NS_VK_CONTROL;
-        case AKEYCODE_CAPS_LOCK:          return NS_VK_CAPS_LOCK;
-        case AKEYCODE_SCROLL_LOCK:        return NS_VK_SCROLL_LOCK;
-        // KEYCODE_META_LEFT (117) ... KEYCODE_FUNCTION (119)
-        case AKEYCODE_SYSRQ:              return NS_VK_PRINTSCREEN;
-        case AKEYCODE_BREAK:              return NS_VK_PAUSE;
-        case AKEYCODE_MOVE_HOME:          return NS_VK_HOME;
-        case AKEYCODE_MOVE_END:           return NS_VK_END;
-        case AKEYCODE_INSERT:             return NS_VK_INSERT;
-        // KEYCODE_FORWARD (125) ... KEYCODE_MEDIA_RECORD (130)
-        case AKEYCODE_F1:                 return NS_VK_F1;
-        case AKEYCODE_F2:                 return NS_VK_F2;
-        case AKEYCODE_F3:                 return NS_VK_F3;
-        case AKEYCODE_F4:                 return NS_VK_F4;
-        case AKEYCODE_F5:                 return NS_VK_F5;
-        case AKEYCODE_F6:                 return NS_VK_F6;
-        case AKEYCODE_F7:                 return NS_VK_F7;
-        case AKEYCODE_F8:                 return NS_VK_F8;
-        case AKEYCODE_F9:                 return NS_VK_F9;
-        case AKEYCODE_F10:                return NS_VK_F10;
-        case AKEYCODE_F11:                return NS_VK_F11;
-        case AKEYCODE_F12:                return NS_VK_F12;
-        case AKEYCODE_NUM_LOCK:           return NS_VK_NUM_LOCK;
-        case AKEYCODE_NUMPAD_0:           return NS_VK_NUMPAD0;
-        case AKEYCODE_NUMPAD_1:           return NS_VK_NUMPAD1;
-        case AKEYCODE_NUMPAD_2:           return NS_VK_NUMPAD2;
-        case AKEYCODE_NUMPAD_3:           return NS_VK_NUMPAD3;
-        case AKEYCODE_NUMPAD_4:           return NS_VK_NUMPAD4;
-        case AKEYCODE_NUMPAD_5:           return NS_VK_NUMPAD5;
-        case AKEYCODE_NUMPAD_6:           return NS_VK_NUMPAD6;
-        case AKEYCODE_NUMPAD_7:           return NS_VK_NUMPAD7;
-        case AKEYCODE_NUMPAD_8:           return NS_VK_NUMPAD8;
-        case AKEYCODE_NUMPAD_9:           return NS_VK_NUMPAD9;
-        case AKEYCODE_NUMPAD_DIVIDE:      return NS_VK_DIVIDE;
-        case AKEYCODE_NUMPAD_MULTIPLY:    return NS_VK_MULTIPLY;
-        case AKEYCODE_NUMPAD_SUBTRACT:    return NS_VK_SUBTRACT;
-        case AKEYCODE_NUMPAD_ADD:         return NS_VK_ADD;
-        case AKEYCODE_NUMPAD_DOT:         return NS_VK_DECIMAL;
-        case AKEYCODE_NUMPAD_COMMA:       return NS_VK_SEPARATOR;
-        case AKEYCODE_NUMPAD_ENTER:       return NS_VK_RETURN;
-        case AKEYCODE_NUMPAD_EQUALS:      return NS_VK_EQUALS;
-        // KEYCODE_NUMPAD_LEFT_PAREN (162) ... KEYCODE_CALCULATOR (210)
-
-        // Needs to confirm the behavior.  If the key switches the open state
-        // of Japanese IME (or switches input character between Hiragana and
-        // Roman numeric characters), then, it might be better to use
-        // NS_VK_KANJI which is used for Alt+Zenkaku/Hankaku key on Windows.
-        case AKEYCODE_ZENKAKU_HANKAKU:    return 0;
-        case AKEYCODE_EISU:               return NS_VK_EISU;
-        case AKEYCODE_MUHENKAN:           return NS_VK_NONCONVERT;
-        case AKEYCODE_HENKAN:             return NS_VK_CONVERT;
-        case AKEYCODE_KATAKANA_HIRAGANA:  return 0;
-        case AKEYCODE_YEN:                return NS_VK_BACK_SLASH; // Same as other platforms.
-        case AKEYCODE_RO:                 return NS_VK_BACK_SLASH; // Same as other platforms.
-        case AKEYCODE_KANA:               return NS_VK_KANA;
-        case AKEYCODE_ASSIST:             return NS_VK_HELP;
-
-        // the A key is the action key for gamepad devices.
-        case AKEYCODE_BUTTON_A:          return NS_VK_RETURN;
-
-        default:
-            ALOG("ConvertAndroidKeyCodeToDOMKeyCode: "
-                 "No DOM keycode for Android keycode %d", androidKeyCode);
-        return 0;
-    }
-}
-
-static KeyNameIndex
-ConvertAndroidKeyCodeToKeyNameIndex(int keyCode, int action,
-                                    int domPrintableKeyValue)
-{
-    // Special-case alphanumeric keycodes because they are most common.
-    if (keyCode >= AKEYCODE_A && keyCode <= AKEYCODE_Z) {
-        return KEY_NAME_INDEX_USE_STRING;
-    }
-
-    if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) {
-        return KEY_NAME_INDEX_USE_STRING;
-    }
-
-    switch (keyCode) {
-
-#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
-        case aNativeKey: return aKeyNameIndex;
-
-#include "NativeKeyToDOMKeyName.h"
-
-#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
-
-        // KEYCODE_0 (7) ... KEYCODE_9 (16)
-        case AKEYCODE_STAR:               // '*' key
-        case AKEYCODE_POUND:              // '#' key
-
-        // KEYCODE_A (29) ... KEYCODE_Z (54)
-
-        case AKEYCODE_COMMA:              // ',' key
-        case AKEYCODE_PERIOD:             // '.' key
-        case AKEYCODE_SPACE:
-        case AKEYCODE_GRAVE:              // '`' key
-        case AKEYCODE_MINUS:              // '-' key
-        case AKEYCODE_EQUALS:             // '=' key
-        case AKEYCODE_LEFT_BRACKET:       // '[' key
-        case AKEYCODE_RIGHT_BRACKET:      // ']' key
-        case AKEYCODE_BACKSLASH:          // '\' key
-        case AKEYCODE_SEMICOLON:          // ';' key
-        case AKEYCODE_APOSTROPHE:         // ''' key
-        case AKEYCODE_SLASH:              // '/' key
-        case AKEYCODE_AT:                 // '@' key
-        case AKEYCODE_PLUS:               // '+' key
-
-        case AKEYCODE_NUMPAD_0:
-        case AKEYCODE_NUMPAD_1:
-        case AKEYCODE_NUMPAD_2:
-        case AKEYCODE_NUMPAD_3:
-        case AKEYCODE_NUMPAD_4:
-        case AKEYCODE_NUMPAD_5:
-        case AKEYCODE_NUMPAD_6:
-        case AKEYCODE_NUMPAD_7:
-        case AKEYCODE_NUMPAD_8:
-        case AKEYCODE_NUMPAD_9:
-        case AKEYCODE_NUMPAD_DIVIDE:
-        case AKEYCODE_NUMPAD_MULTIPLY:
-        case AKEYCODE_NUMPAD_SUBTRACT:
-        case AKEYCODE_NUMPAD_ADD:
-        case AKEYCODE_NUMPAD_DOT:
-        case AKEYCODE_NUMPAD_COMMA:
-        case AKEYCODE_NUMPAD_EQUALS:
-        case AKEYCODE_NUMPAD_LEFT_PAREN:
-        case AKEYCODE_NUMPAD_RIGHT_PAREN:
-
-        case AKEYCODE_YEN:                // yen sign key
-        case AKEYCODE_RO:                 // Japanese Ro key
-            return KEY_NAME_INDEX_USE_STRING;
-
-        case AKEYCODE_NUM:                // XXX Not sure
-        case AKEYCODE_PICTSYMBOLS:
-
-        case AKEYCODE_BUTTON_A:
-        case AKEYCODE_BUTTON_B:
-        case AKEYCODE_BUTTON_C:
-        case AKEYCODE_BUTTON_X:
-        case AKEYCODE_BUTTON_Y:
-        case AKEYCODE_BUTTON_Z:
-        case AKEYCODE_BUTTON_L1:
-        case AKEYCODE_BUTTON_R1:
-        case AKEYCODE_BUTTON_L2:
-        case AKEYCODE_BUTTON_R2:
-        case AKEYCODE_BUTTON_THUMBL:
-        case AKEYCODE_BUTTON_THUMBR:
-        case AKEYCODE_BUTTON_START:
-        case AKEYCODE_BUTTON_SELECT:
-        case AKEYCODE_BUTTON_MODE:
-
-        case AKEYCODE_MEDIA_CLOSE:
-
-        case AKEYCODE_BUTTON_1:
-        case AKEYCODE_BUTTON_2:
-        case AKEYCODE_BUTTON_3:
-        case AKEYCODE_BUTTON_4:
-        case AKEYCODE_BUTTON_5:
-        case AKEYCODE_BUTTON_6:
-        case AKEYCODE_BUTTON_7:
-        case AKEYCODE_BUTTON_8:
-        case AKEYCODE_BUTTON_9:
-        case AKEYCODE_BUTTON_10:
-        case AKEYCODE_BUTTON_11:
-        case AKEYCODE_BUTTON_12:
-        case AKEYCODE_BUTTON_13:
-        case AKEYCODE_BUTTON_14:
-        case AKEYCODE_BUTTON_15:
-        case AKEYCODE_BUTTON_16:
-            return KEY_NAME_INDEX_Unidentified;
-
-        case AKEYCODE_UNKNOWN:
-            MOZ_ASSERT(
-                action != AKEY_EVENT_ACTION_MULTIPLE,
-                "Don't call this when action is AKEY_EVENT_ACTION_MULTIPLE!");
-            // It's actually an unknown key if the action isn't ACTION_MULTIPLE.
-            // However, it might cause text input.  So, let's check the value.
-            return domPrintableKeyValue ?
-                KEY_NAME_INDEX_USE_STRING : KEY_NAME_INDEX_Unidentified;
-
-        default:
-            ALOG("ConvertAndroidKeyCodeToKeyNameIndex: "
-                 "No DOM key name index for Android keycode %d", keyCode);
-            return KEY_NAME_INDEX_Unidentified;
-    }
-}
-
-static CodeNameIndex
-ConvertAndroidScanCodeToCodeNameIndex(int scanCode)
-{
-    switch (scanCode) {
-
-#define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
-        case aNativeKey: return aCodeNameIndex;
-
-#include "NativeKeyToDOMCodeName.h"
-
-#undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
-
-        default:
-          return CODE_NAME_INDEX_UNKNOWN;
-    }
-}
-
-static bool
-IsModifierKey(int32_t keyCode)
-{
-    using mozilla::java::sdk::KeyEvent;
-    return keyCode == KeyEvent::KEYCODE_ALT_LEFT ||
-           keyCode == KeyEvent::KEYCODE_ALT_RIGHT ||
-           keyCode == KeyEvent::KEYCODE_SHIFT_LEFT ||
-           keyCode == KeyEvent::KEYCODE_SHIFT_RIGHT ||
-           keyCode == KeyEvent::KEYCODE_CTRL_LEFT ||
-           keyCode == KeyEvent::KEYCODE_CTRL_RIGHT ||
-           keyCode == KeyEvent::KEYCODE_META_LEFT ||
-           keyCode == KeyEvent::KEYCODE_META_RIGHT;
-}
-
-static Modifiers
-GetModifiers(int32_t metaState)
+mozilla::Modifiers
+nsWindow::GetModifiers(int32_t metaState)
 {
     using mozilla::java::sdk::KeyEvent;
     return (metaState & KeyEvent::META_ALT_MASK ? MODIFIER_ALT : 0)
         | (metaState & KeyEvent::META_SHIFT_MASK ? MODIFIER_SHIFT : 0)
         | (metaState & KeyEvent::META_CTRL_MASK ? MODIFIER_CONTROL : 0)
         | (metaState & KeyEvent::META_META_MASK ? MODIFIER_META : 0)
         | (metaState & KeyEvent::META_FUNCTION_ON ? MODIFIER_FN : 0)
         | (metaState & KeyEvent::META_CAPS_LOCK_ON ? MODIFIER_CAPSLOCK : 0)
         | (metaState & KeyEvent::META_NUM_LOCK_ON ? MODIFIER_NUMLOCK : 0)
         | (metaState & KeyEvent::META_SCROLL_LOCK_ON ? MODIFIER_SCROLLLOCK : 0);
 }
 
-static void
-InitKeyEvent(WidgetKeyboardEvent& event,
-             int32_t action, int32_t keyCode, int32_t scanCode,
-             int32_t metaState, int64_t time, int32_t unicodeChar,
-             int32_t baseUnicodeChar, int32_t domPrintableKeyValue,
-             int32_t repeatCount, int32_t flags)
-{
-    const uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(keyCode);
-    const int32_t charCode = unicodeChar ? unicodeChar : baseUnicodeChar;
-
-    event.mModifiers = GetModifiers(metaState);
-
-    if (event.mMessage == eKeyPress) {
-        // Android gives us \n, so filter out some control characters.
-        event.mIsChar = (charCode >= ' ');
-        event.mCharCode = event.mIsChar ? charCode : 0;
-        event.mKeyCode = event.mIsChar ? 0 : domKeyCode;
-        event.mPluginEvent.Clear();
-
-        // For keypress, if the unicode char already has modifiers applied, we
-        // don't specify extra modifiers. If UnicodeChar() != BaseUnicodeChar()
-        // it means UnicodeChar() already has modifiers applied.
-        // Note that on Android 4.x, Alt modifier isn't set when the key input
-        // causes text input even while right Alt key is pressed.  However,
-        // this is necessary for Android 2.3 compatibility.
-        if (unicodeChar && unicodeChar != baseUnicodeChar) {
-            event.mModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL
-                                               | MODIFIER_META);
-        }
-
-    } else {
-        event.mIsChar = false;
-        event.mCharCode = 0;
-        event.mKeyCode = domKeyCode;
-
-        ANPEvent pluginEvent;
-        pluginEvent.inSize = sizeof(pluginEvent);
-        pluginEvent.eventType = kKey_ANPEventType;
-        pluginEvent.data.key.action = event.mMessage == eKeyDown
-                ? kDown_ANPKeyAction : kUp_ANPKeyAction;
-        pluginEvent.data.key.nativeCode = keyCode;
-        pluginEvent.data.key.virtualCode = domKeyCode;
-        pluginEvent.data.key.unichar = charCode;
-        pluginEvent.data.key.modifiers =
-                (metaState & sdk::KeyEvent::META_SHIFT_MASK
-                        ? kShift_ANPKeyModifier : 0) |
-                (metaState & sdk::KeyEvent::META_ALT_MASK
-                        ? kAlt_ANPKeyModifier : 0);
-        pluginEvent.data.key.repeatCount = repeatCount;
-        event.mPluginEvent.Copy(pluginEvent);
-    }
-
-    event.mIsRepeat =
-        (event.mMessage == eKeyDown || event.mMessage == eKeyPress) &&
-        ((flags & sdk::KeyEvent::FLAG_LONG_PRESS) || repeatCount);
-
-    event.mKeyNameIndex = ConvertAndroidKeyCodeToKeyNameIndex(
-            keyCode, action, domPrintableKeyValue);
-    event.mCodeNameIndex = ConvertAndroidScanCodeToCodeNameIndex(scanCode);
-
-    if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
-            domPrintableKeyValue) {
-        event.mKeyValue = char16_t(domPrintableKeyValue);
-    }
-
-    event.mLocation =
-        WidgetKeyboardEvent::ComputeLocationFromCodeValue(event.mCodeNameIndex);
-    event.mTime = time;
-    event.mTimeStamp = GetEventTimeStamp(time);
-}
-
-void
-nsWindow::GeckoViewSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
-        int32_t aScanCode, int32_t aMetaState, int64_t aTime,
-        int32_t aUnicodeChar, int32_t aBaseUnicodeChar,
-        int32_t aDomPrintableKeyValue, int32_t aRepeatCount, int32_t aFlags,
-        bool aIsSynthesizedImeKey, jni::Object::Param originalEvent)
-{
-    RefPtr<nsWindow> kungFuDeathGrip(&window);
-    if (!aIsSynthesizedImeKey) {
-        window.UserActivity();
-        window.RemoveIMEComposition();
-    }
-
-    EventMessage msg;
-    if (aAction == sdk::KeyEvent::ACTION_DOWN) {
-        msg = eKeyDown;
-    } else if (aAction == sdk::KeyEvent::ACTION_UP) {
-        msg = eKeyUp;
-    } else if (aAction == sdk::KeyEvent::ACTION_MULTIPLE) {
-        // Keys with multiple action are handled in Java,
-        // and we should never see one here
-        MOZ_CRASH("Cannot handle key with multiple action");
-    } else {
-        ALOG("Unknown key action event!");
-        return;
-    }
-
-    nsEventStatus status = nsEventStatus_eIgnore;
-    WidgetKeyboardEvent event(true, msg, &window);
-    window.InitEvent(event, nullptr);
-    InitKeyEvent(event, aAction, aKeyCode, aScanCode, aMetaState, aTime,
-                 aUnicodeChar, aBaseUnicodeChar, aDomPrintableKeyValue,
-                 aRepeatCount, aFlags);
-
-    if (aIsSynthesizedImeKey) {
-        // Keys synthesized by Java IME code are saved in the mIMEKeyEvents
-        // array until the next IME_REPLACE_TEXT event, at which point
-        // these keys are dispatched in sequence.
-        mIMEKeyEvents.AppendElement(
-                mozilla::UniquePtr<WidgetEvent>(event.Duplicate()));
-
-    } else {
-        window.DispatchEvent(&event, status);
-
-        if (window.Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
-            // Skip default processing.
-            return;
-        }
-
-        mEditable->OnDefaultKeyEvent(originalEvent);
-    }
-
-    if (msg != eKeyDown || IsModifierKey(aKeyCode)) {
-        // Skip sending key press event.
-        return;
-    }
-
-    WidgetKeyboardEvent pressEvent(true, eKeyPress, &window);
-    window.InitEvent(pressEvent, nullptr);
-    InitKeyEvent(pressEvent, aAction, aKeyCode, aScanCode, aMetaState, aTime,
-                 aUnicodeChar, aBaseUnicodeChar, aDomPrintableKeyValue,
-                 aRepeatCount, aFlags);
-
-    if (aIsSynthesizedImeKey) {
-        mIMEKeyEvents.AppendElement(
-                mozilla::UniquePtr<WidgetEvent>(pressEvent.Duplicate()));
-    } else {
-        window.DispatchEvent(&pressEvent, status);
-    }
-}
-
-#ifdef DEBUG_ANDROID_IME
-#define ALOGIME(args...) ALOG(args)
-#else
-#define ALOGIME(args...) ((void)0)
-#endif
-
-static nscolor
-ConvertAndroidColor(uint32_t aArgb)
-{
-    return NS_RGBA((aArgb & 0x00ff0000) >> 16,
-                   (aArgb & 0x0000ff00) >> 8,
-                   (aArgb & 0x000000ff),
-                   (aArgb & 0xff000000) >> 24);
-}
-
-/*
- * Get the current composition object, if any.
- */
-RefPtr<mozilla::TextComposition>
-nsWindow::GetIMEComposition()
-{
-    MOZ_ASSERT(this == FindTopLevel());
-    return mozilla::IMEStateManager::GetTextCompositionFor(this);
-}
-
-/*
-    Remove the composition but leave the text content as-is
-*/
-void
-nsWindow::RemoveIMEComposition(RemoveIMECompositionFlag aFlag)
-{
-    // Remove composition on Gecko side
-    const RefPtr<mozilla::TextComposition> composition(GetIMEComposition());
-    if (!composition) {
-        return;
-    }
-
-    RefPtr<nsWindow> kungFuDeathGrip(this);
-
-    WidgetCompositionEvent compositionCommitEvent(
-            true, eCompositionCommit, this);
-    if (aFlag == COMMIT_IME_COMPOSITION) {
-        compositionCommitEvent.mMessage = eCompositionCommitAsIs;
-    }
-    InitEvent(compositionCommitEvent, nullptr);
-    DispatchEvent(&compositionCommitEvent);
-}
-
-/*
- * Send dummy key events for pages that are unaware of input events,
- * to provide web compatibility for pages that depend on key events.
- * Our dummy key events have 0 as the keycode.
- */
-void
-nsWindow::GeckoViewSupport::SendIMEDummyKeyEvents()
-{
-    WidgetKeyboardEvent downEvent(true, eKeyDown, &window);
-    window.InitEvent(downEvent, nullptr);
-    MOZ_ASSERT(downEvent.mKeyCode == 0);
-    window.DispatchEvent(&downEvent);
-
-    WidgetKeyboardEvent upEvent(true, eKeyUp, &window);
-    window.InitEvent(upEvent, nullptr);
-    MOZ_ASSERT(upEvent.mKeyCode == 0);
-    window.DispatchEvent(&upEvent);
-}
-
-void
-nsWindow::GeckoViewSupport::AddIMETextChange(const IMETextChange& aChange)
-{
-    mIMETextChanges.AppendElement(aChange);
-
-    // We may not be in the middle of flushing,
-    // in which case this flag is meaningless.
-    mIMETextChangedDuringFlush = true;
-
-    // Now that we added a new range we need to go back and
-    // update all the ranges before that.
-    // Ranges that have offsets which follow this new range
-    // need to be updated to reflect new offsets
-    const int32_t delta = aChange.mNewEnd - aChange.mOldEnd;
-    for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
-        IMETextChange& previousChange = mIMETextChanges[i];
-        if (previousChange.mStart > aChange.mOldEnd) {
-            previousChange.mStart += delta;
-            previousChange.mOldEnd += delta;
-            previousChange.mNewEnd += delta;
-        }
-    }
-
-    // Now go through all ranges to merge any ranges that are connected
-    // srcIndex is the index of the range to merge from
-    // dstIndex is the index of the range to potentially merge into
-    int32_t srcIndex = mIMETextChanges.Length() - 1;
-    int32_t dstIndex = srcIndex;
-
-    while (--dstIndex >= 0) {
-        IMETextChange& src = mIMETextChanges[srcIndex];
-        IMETextChange& dst = mIMETextChanges[dstIndex];
-        // When merging a more recent change into an older
-        // change, we need to compare recent change's (start, oldEnd)
-        // range to the older change's (start, newEnd)
-        if (src.mOldEnd < dst.mStart || dst.mNewEnd < src.mStart) {
-            // No overlap between ranges
-            continue;
-        }
-        // When merging two ranges, there are generally four posibilities:
-        // [----(----]----), (----[----]----),
-        // [----(----)----], (----[----)----]
-        // where [----] is the first range and (----) is the second range
-        // As seen above, the start of the merged range is always the lesser
-        // of the two start offsets. OldEnd and NewEnd then need to be
-        // adjusted separately depending on the case. In any case, the change
-        // in text length of the merged range should be the sum of text length
-        // changes of the two original ranges, i.e.,
-        // newNewEnd - newOldEnd == newEnd1 - oldEnd1 + newEnd2 - oldEnd2
-        dst.mStart = std::min(dst.mStart, src.mStart);
-        if (src.mOldEnd < dst.mNewEnd) {
-            // New range overlaps or is within previous range; merge
-            dst.mNewEnd += src.mNewEnd - src.mOldEnd;
-        } else { // src.mOldEnd >= dst.mNewEnd
-            // New range overlaps previous range; merge
-            dst.mOldEnd += src.mOldEnd - dst.mNewEnd;
-            dst.mNewEnd = src.mNewEnd;
-        }
-        // src merged to dst; delete src.
-        mIMETextChanges.RemoveElementAt(srcIndex);
-        // Any ranges that we skip over between src and dst are not mergeable
-        // so we can safely continue the merge starting at dst
-        srcIndex = dstIndex;
-    }
-}
-
-void
-nsWindow::GeckoViewSupport::PostFlushIMEChanges()
-{
-    if (!mIMETextChanges.IsEmpty() || mIMESelectionChanged) {
-        // Already posted
-        return;
-    }
-
-    // Keep a strong reference to the window to keep 'this' alive.
-    RefPtr<nsWindow> window(&this->window);
-
-    nsAppShell::PostEvent([this, window] {
-        if (!window->Destroyed()) {
-            FlushIMEChanges();
-        }
-    });
-}
-
-void
-nsWindow::GeckoViewSupport::FlushIMEChanges(FlushChangesFlag aFlags)
-{
-    // Only send change notifications if we are *not* masking events,
-    // i.e. if we have a focused editor,
-    NS_ENSURE_TRUE_VOID(!mIMEMaskEventsCount);
-
-    nsCOMPtr<nsISelection> imeSelection;
-    nsCOMPtr<nsIContent> imeRoot;
-
-    // If we are receiving notifications, we must have selection/root content.
-    nsresult rv = IMEStateManager::GetFocusSelectionAndRoot(
-            getter_AddRefs(imeSelection), getter_AddRefs(imeRoot));
-
-    // With e10s enabled, GetFocusSelectionAndRoot will fail because the IME
-    // content observer is out of process.
-    const bool e10sEnabled = rv == NS_ERROR_NOT_AVAILABLE;
-    if (!e10sEnabled) {
-        MOZ_ALWAYS_SUCCEEDS(rv);
-    }
-
-    // Make sure we still have a valid selection/root. We can potentially get
-    // a stale selection/root if the editor becomes hidden, for example.
-    NS_ENSURE_TRUE_VOID(e10sEnabled || imeRoot->IsInComposedDoc());
-
-    RefPtr<nsWindow> kungFuDeathGrip(&window);
-    window.UserActivity();
-
-    struct TextRecord {
-        nsString text;
-        int32_t start;
-        int32_t oldEnd;
-        int32_t newEnd;
-    };
-    AutoTArray<TextRecord, 4> textTransaction;
-    if (mIMETextChanges.Length() > textTransaction.Capacity()) {
-        textTransaction.SetCapacity(mIMETextChanges.Length());
-    }
-
-    mIMETextChangedDuringFlush = false;
-
-    auto shouldAbort = [=] () -> bool {
-        if (!mIMETextChangedDuringFlush) {
-            return false;
-        }
-        // A query event could have triggered more text changes to come in, as
-        // indicated by our flag. If that happens, try flushing IME changes
-        // again.
-        if (aFlags == FLUSH_FLAG_NONE) {
-            FlushIMEChanges(FLUSH_FLAG_RETRY);
-        } else {
-            // Don't retry if already retrying, to avoid infinite loops.
-            __android_log_print(ANDROID_LOG_WARN, "GeckoViewSupport",
-                    "Already retrying IME flush");
-        }
-        return true;
-    };
-
-    for (const IMETextChange &change : mIMETextChanges) {
-        if (change.mStart == change.mOldEnd &&
-                change.mStart == change.mNewEnd) {
-            continue;
-        }
-
-        WidgetQueryContentEvent event(true, eQueryTextContent, &window);
-
-        if (change.mNewEnd != change.mStart) {
-            window.InitEvent(event, nullptr);
-            event.InitForQueryTextContent(change.mStart,
-                                          change.mNewEnd - change.mStart);
-            window.DispatchEvent(&event);
-            NS_ENSURE_TRUE_VOID(event.mSucceeded);
-            NS_ENSURE_TRUE_VOID(e10sEnabled || event.mReply.mContentsRoot == imeRoot.get());
-        }
-
-        if (shouldAbort()) {
-            return;
-        }
-
-        textTransaction.AppendElement(
-                TextRecord{event.mReply.mString, change.mStart,
-                           change.mOldEnd, change.mNewEnd});
-    }
-
-    int32_t selStart = -1;
-    int32_t selEnd = -1;
-
-    if (mIMESelectionChanged) {
-        WidgetQueryContentEvent event(true, eQuerySelectedText, &window);
-        window.InitEvent(event, nullptr);
-        window.DispatchEvent(&event);
-
-        NS_ENSURE_TRUE_VOID(event.mSucceeded);
-        NS_ENSURE_TRUE_VOID(e10sEnabled || event.mReply.mContentsRoot == imeRoot.get());
-
-        if (shouldAbort()) {
-            return;
-        }
-
-        selStart = int32_t(event.GetSelectionStart());
-        selEnd = int32_t(event.GetSelectionEnd());
-    }
-
-    JNIEnv* const env = jni::GetGeckoThreadEnv();
-    auto flushOnException = [=] () -> bool {
-        if (!env->ExceptionCheck()) {
-            return false;
-        }
-        if (aFlags != FLUSH_FLAG_RECOVER) {
-            // First time seeing an exception; try flushing text.
-            env->ExceptionClear();
-            __android_log_print(ANDROID_LOG_WARN, "GeckoViewSupport",
-                    "Recovering from IME exception");
-            FlushIMEText(FLUSH_FLAG_RECOVER);
-        } else {
-            // Give up because we've already tried.
-            MOZ_CATCH_JNI_EXCEPTION(env);
-        }
-        return true;
-    };
-
-    // Commit the text change and selection change transaction.
-    mIMETextChanges.Clear();
-
-    for (const TextRecord& record : textTransaction) {
-        mEditable->OnTextChange(record.text, record.start,
-                                record.oldEnd, record.newEnd);
-        if (flushOnException()) {
-            return;
-        }
-    }
-
-    if (mIMESelectionChanged) {
-        mIMESelectionChanged = false;
-        mEditable->OnSelectionChange(selStart, selEnd);
-        flushOnException();
-    }
-}
-
-void
-nsWindow::GeckoViewSupport::FlushIMEText(FlushChangesFlag aFlags)
-{
-    // Notify Java of the newly focused content
-    mIMETextChanges.Clear();
-    mIMESelectionChanged = true;
-
-    // Use 'INT32_MAX / 2' here because subsequent text changes might combine
-    // with this text change, and overflow might occur if we just use
-    // INT32_MAX.
-    IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
-    notification.mTextChangeData.mStartOffset = 0;
-    notification.mTextChangeData.mRemovedEndOffset = INT32_MAX / 2;
-    notification.mTextChangeData.mAddedEndOffset = INT32_MAX / 2;
-    NotifyIME(notification);
-
-    FlushIMEChanges(aFlags);
<