merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 02 Mar 2017 14:02:48 +0100
changeset 491917 66535e831760421b270662aa8d0773b0fde7c9f3
parent 491908 180a160ae22a4d63867cfd02608e7621e8e697d1 (current diff)
parent 491916 8c7dbec36fa77c5bb427c480f3620dd4a781260f (diff)
child 491918 f39e2503f9afd83d50a3557e5e76412ad7bf7dc7
child 491928 63007c25517ce2ebe6b5ea9f86d443335894cead
child 491934 8e5f3ea51c81366b88e5ff170df630a5b7464077
child 491939 87176aa9560af31faf448464e28b7c53a459f9af
child 491943 8549f49de3789c30df623222c5d7b2b8dfefd87b
child 491985 5c8f4643a2e93f4860953b904890119b281ba65b
child 491992 474087509de5de6cbd9adf86e73a07791398a84e
child 492007 c942063daa8ec7e15491b8feb829633ed68ef85e
child 492008 e93fde9f9168721881ed5a321a20becc7321725f
child 492035 e01b100a8b584d94848b73c8c5b2145fb40dc471
child 492054 25431abaf2b1ed8f54db49f201d6a822a7016d8e
child 492062 41ed1623de7e9d863709d5ddc0643fea4b801164
child 492063 d78bc99408e0d0987f4e526626b3d78efaf290e7
child 492065 a1f1c37d4123398a276940cc29cb091eb12bedd3
child 492129 8744594fc366cf463313d3636e62789a907b0877
push id47457
push userjichen@mozilla.com
push dateThu, 02 Mar 2017 13:21:35 +0000
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);