Merge inbound to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Mon, 22 Apr 2019 12:41:08 +0300
changeset 470331 b783cd5203ea589bb7505852e5108ed142d2d37a
parent 470330 43c7c3f10a71a5bede2282db7dcd80d674cf237d (diff)
parent 470315 f8541439e10186980ecea712820310b08144ec96 (current diff)
child 470332 ab1da7fa2ad0aa1c9d6d0a9e2f63e6492cd1b712
child 470341 355f26707b354faec37e740b99950e70ab3221fc
child 470348 34059c6188e54415a97125be22b06040dbcc2f93
push id112863
push usershindli@mozilla.com
push dateMon, 22 Apr 2019 09:53:25 +0000
treeherdermozilla-inbound@ab1da7fa2ad0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
b783cd5203ea / 68.0a1 / 20190422094240 / files
nightly linux64
b783cd5203ea / 68.0a1 / 20190422094240 / files
nightly mac
b783cd5203ea / 68.0a1 / 20190422094240 / files
nightly win32
b783cd5203ea / 68.0a1 / 20190422094240 / files
nightly win64
b783cd5203ea / 68.0a1 / 20190422094240 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,10 +1,8 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
 [[package]]
 name = "Inflector"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -353,16 +351,17 @@ dependencies = [
  "byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "bookmark_sync"
 version = "0.1.0"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "dogear 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "moz_task 0.1.0",
  "nserror 0.1.0",
  "nsstring 0.1.0",
  "storage 0.1.0",
  "storage_variant 0.1.0",
@@ -1243,16 +1242,17 @@ dependencies = [
  "prefs_parser 0.0.1",
  "profiler_helper 0.1.0",
  "rsdparsa_capi 0.1.0",
  "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "storage 0.1.0",
  "u2fhid 0.2.3",
  "webrender_bindings 0.1.0",
  "xpcom 0.1.0",
+ "xulstore 0.1.0",
 ]
 
 [[package]]
 name = "gkrust_utils"
 version = "0.1.0"
 dependencies = [
  "nsstring 0.1.0",
  "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3428,16 +3428,35 @@ name = "xpcom_macros"
 version = "0.1.0"
 dependencies = [
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "xulstore"
+version = "0.1.0"
+dependencies = [
+ "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lmdb-rkv 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "moz_task 0.1.0",
+ "nserror 0.1.0",
+ "nsstring 0.1.0",
+ "rkv 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xpcom 0.1.0",
+]
+
+[[package]]
 name = "yaml-rust"
 version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -118,19 +118,19 @@ void nsCoreUtils::DispatchMouseEvent(Eve
                                      nsIFrame *aFrame, PresShell *aPresShell,
                                      nsIWidget *aRootWidget) {
   WidgetMouseEvent event(true, aMessage, aRootWidget, WidgetMouseEvent::eReal,
                          WidgetMouseEvent::eNormal);
 
   event.mRefPoint = LayoutDeviceIntPoint(aX, aY);
 
   event.mClickCount = 1;
-  event.button = WidgetMouseEvent::eLeftButton;
+  event.mButton = MouseButton::eLeft;
   event.mTime = PR_IntervalNow();
-  event.inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
+  event.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status);
 }
 
 void nsCoreUtils::DispatchTouchEvent(EventMessage aMessage, int32_t aX,
                                      int32_t aY, nsIContent *aContent,
                                      nsIFrame *aFrame, PresShell *aPresShell,
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2198,27 +2198,27 @@ nsresult Element::DispatchClickEvent(nsP
   event.mRefPoint = aSourceEvent->mRefPoint;
   uint32_t clickCount = 1;
   float pressure = 0;
   uint32_t pointerId = 0;  // Use the default value here.
   uint16_t inputSource = 0;
   WidgetMouseEvent* sourceMouseEvent = aSourceEvent->AsMouseEvent();
   if (sourceMouseEvent) {
     clickCount = sourceMouseEvent->mClickCount;
-    pressure = sourceMouseEvent->pressure;
+    pressure = sourceMouseEvent->mPressure;
     pointerId = sourceMouseEvent->pointerId;
-    inputSource = sourceMouseEvent->inputSource;
+    inputSource = sourceMouseEvent->mInputSource;
   } else if (aSourceEvent->mClass == eKeyboardEventClass) {
     event.mFlags.mIsPositionless = true;
     inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
   }
-  event.pressure = pressure;
+  event.mPressure = pressure;
   event.mClickCount = clickCount;
   event.pointerId = pointerId;
-  event.inputSource = inputSource;
+  event.mInputSource = inputSource;
   event.mModifiers = aSourceEvent->mModifiers;
   if (aExtraEventFlags) {
     // Be careful not to overwrite existing flags!
     event.mFlags.Union(*aExtraEventFlags);
   }
 
   return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
 }
@@ -3092,18 +3092,17 @@ nsresult Element::PostHandleEventForLink
   if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
     return NS_OK;
   }
 
   nsresult rv = NS_OK;
 
   switch (aVisitor.mEvent->mMessage) {
     case eMouseDown: {
-      if (aVisitor.mEvent->AsMouseEvent()->button ==
-          WidgetMouseEvent::eLeftButton) {
+      if (aVisitor.mEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
         // don't make the link grab the focus if there is no link handler
         nsILinkHandler* handler = aVisitor.mPresContext->GetLinkHandler();
         Document* document = GetComposedDoc();
         if (handler && document) {
           nsIFocusManager* fm = nsFocusManager::GetFocusManager();
           if (fm) {
             aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
             RefPtr<Element> kungFuDeathGrip(this);
--- a/dom/base/PopupBlocker.cpp
+++ b/dom/base/PopupBlocker.cpp
@@ -288,18 +288,17 @@ PopupBlocker::PopupControlState PopupBlo
             break;
           default:
             break;
         }
       }
       break;
     case eMouseEventClass:
       if (aEvent->IsTrusted()) {
-        // eLeftButton
-        if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+        if (aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
           abuse = PopupBlocker::openBlocked;
           switch (aEvent->mMessage) {
             case eMouseUp:
               if (PopupAllowedForEvent("mouseup")) {
                 abuse = PopupBlocker::openControlled;
               }
               break;
             case eMouseDown:
@@ -346,17 +345,17 @@ PopupBlocker::PopupControlState PopupBlo
             break;
           default:
             break;
         }
       }
       break;
     case ePointerEventClass:
       if (aEvent->IsTrusted() &&
-          aEvent->AsPointerEvent()->button == WidgetMouseEvent::eLeftButton) {
+          aEvent->AsPointerEvent()->mButton == MouseButton::eLeft) {
         switch (aEvent->mMessage) {
           case ePointerUp:
             if (PopupAllowedForEvent("pointerup")) {
               abuse = PopupBlocker::openControlled;
             }
             break;
           case ePointerDown:
             if (PopupAllowedForEvent("pointerdown")) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7871,27 +7871,27 @@ nsIWidget* nsContentUtils::GetWidget(nsI
     if (frame) return frame->GetView()->GetNearestWidget(aOffset);
   }
   return nullptr;
 }
 
 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
   switch (aButton) {
     case -1:
-      return WidgetMouseEvent::eNoButtonFlag;
-    case WidgetMouseEvent::eLeftButton:
-      return WidgetMouseEvent::eLeftButtonFlag;
-    case WidgetMouseEvent::eMiddleButton:
-      return WidgetMouseEvent::eMiddleButtonFlag;
-    case WidgetMouseEvent::eRightButton:
-      return WidgetMouseEvent::eRightButtonFlag;
+      return MouseButtonsFlag::eNoButtons;
+    case MouseButton::eLeft:
+      return MouseButtonsFlag::eLeftFlag;
+    case MouseButton::eMiddle:
+      return MouseButtonsFlag::eMiddleFlag;
+    case MouseButton::eRight:
+      return MouseButtonsFlag::eRightFlag;
     case 4:
-      return WidgetMouseEvent::e4thButtonFlag;
+      return MouseButtonsFlag::e4thFlag;
     case 5:
-      return WidgetMouseEvent::e5thButtonFlag;
+      return MouseButtonsFlag::e5thFlag;
     default:
       NS_ERROR("Button not known.");
       return 0;
   }
 }
 
 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
     const CSSPoint& aPoint, const nsPoint& aOffset,
@@ -7964,22 +7964,22 @@ nsresult nsContentUtils::SendMouseEvent(
   WidgetMouseEvent event(true, msg, widget,
                          aIsWidgetEventSynthesized
                              ? WidgetMouseEvent::eSynthesized
                              : WidgetMouseEvent::eReal,
                          contextMenuKey ? WidgetMouseEvent::eContextMenuKey
                                         : WidgetMouseEvent::eNormal);
   event.pointerId = aIdentifier;
   event.mModifiers = GetWidgetModifiers(aModifiers);
-  event.button = aButton;
-  event.buttons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
-                      ? aButtons
-                      : msg == eMouseUp ? 0 : GetButtonsFlagForButton(aButton);
-  event.pressure = aPressure;
-  event.inputSource = aInputSourceArg;
+  event.mButton = aButton;
+  event.mButtons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED
+                       ? aButtons
+                       : msg == eMouseUp ? 0 : GetButtonsFlagForButton(aButton);
+  event.mPressure = aPressure;
+  event.mInputSource = aInputSourceArg;
   event.mClickCount = aClickCount;
   event.mTime = PR_IntervalNow();
   event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
   event.mExitFrom = exitFrom;
 
   nsPresContext* presContext = aPresShell->GetPresContext();
   if (!presContext) return NS_ERROR_FAILURE;
 
--- a/dom/events/DragEvent.cpp
+++ b/dom/events/DragEvent.cpp
@@ -19,17 +19,17 @@ DragEvent::DragEvent(EventTarget* aOwner
           aOwner, aPresContext,
           aEvent ? aEvent : new WidgetDragEvent(false, eVoidEvent, nullptr)) {
   if (aEvent) {
     mEventIsInternal = false;
   } else {
     mEventIsInternal = true;
     mEvent->mTime = PR_Now();
     mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
-    mEvent->AsMouseEvent()->inputSource =
+    mEvent->AsMouseEvent()->mInputSource =
         MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   }
 }
 
 void DragEvent::InitDragEvent(const nsAString& aType, bool aCanBubble,
                               bool aCancelable, nsGlobalWindowInner* aView,
                               int32_t aDetail, int32_t aScreenX,
                               int32_t aScreenY, int32_t aClientX,
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -514,48 +514,48 @@ nsresult EventStateManager::PreHandleEve
         return NS_ERROR_DOM_INVALID_STATE_ERR;
       }
       break;
     case eMouseTouchDrag:
       mInTouchDrag = true;
       BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
       break;
     case eMouseDown: {
-      switch (mouseEvent->button) {
-        case WidgetMouseEvent::eLeftButton:
+      switch (mouseEvent->mButton) {
+        case MouseButton::eLeft:
           BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
           mLClickCount = mouseEvent->mClickCount;
           SetClickCount(mouseEvent, aStatus);
           sNormalLMouseEventInProcess = true;
           break;
-        case WidgetMouseEvent::eMiddleButton:
+        case MouseButton::eMiddle:
           mMClickCount = mouseEvent->mClickCount;
           SetClickCount(mouseEvent, aStatus);
           break;
-        case WidgetMouseEvent::eRightButton:
+        case MouseButton::eRight:
           mRClickCount = mouseEvent->mClickCount;
           SetClickCount(mouseEvent, aStatus);
           break;
       }
       NotifyTargetUserActivation(aEvent, aTargetContent);
       break;
     }
     case eMouseUp: {
-      switch (mouseEvent->button) {
-        case WidgetMouseEvent::eLeftButton:
+      switch (mouseEvent->mButton) {
+        case MouseButton::eLeft:
           if (Prefs::ClickHoldContextMenu()) {
             KillClickHoldTimer();
           }
           mInTouchDrag = false;
           StopTrackingDragGesture();
           sNormalLMouseEventInProcess = false;
           // then fall through...
           MOZ_FALLTHROUGH;
-        case WidgetMouseEvent::eRightButton:
-        case WidgetMouseEvent::eMiddleButton:
+        case MouseButton::eRight:
+        case MouseButton::eMiddle:
           RefPtr<EventStateManager> esm =
               ESMFromContentOrThis(aOverrideClickTarget);
           esm->SetClickCount(mouseEvent, aStatus, aOverrideClickTarget);
           break;
       }
       break;
     }
     case eMouseEnterIntoWidget:
@@ -603,17 +603,17 @@ nsresult EventStateManager::PreHandleEve
         aEvent->mMessage = eVoidEvent;
         break;
       }
       MOZ_FALLTHROUGH;
     case eMouseMove:
     case ePointerDown:
       if (aEvent->mMessage == ePointerDown) {
         PointerEventHandler::ImplicitlyCapturePointer(aTargetFrame, aEvent);
-        if (mouseEvent->inputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
+        if (mouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
           NotifyTargetUserActivation(aEvent, aTargetContent);
         }
       }
       MOZ_FALLTHROUGH;
     case ePointerMove: {
       // on the Mac, GenerateDragGesture() may not return until the drag
       // has completed and so |aTargetFrame| may have been deleted (moving
       // a bookmark, for example).  If this is the case, however, we know
@@ -1608,17 +1608,17 @@ void EventStateManager::BeginTrackingDra
                                     getter_AddRefs(mGestureDownContent));
 
     mGestureDownFrameOwner = inDownFrame->GetContent();
     if (!mGestureDownFrameOwner) {
       mGestureDownFrameOwner = mGestureDownContent;
     }
   }
   mGestureModifiers = inDownEvent->mModifiers;
-  mGestureDownButtons = inDownEvent->buttons;
+  mGestureDownButtons = inDownEvent->mButtons;
 
   if (inDownEvent->mMessage != eMouseTouchDrag &&
       Prefs::ClickHoldContextMenu()) {
     // fire off a timer to track click-hold
     CreateClickHoldTimer(aPresContext, inDownFrame, inDownEvent);
   }
 }
 
@@ -1656,17 +1656,17 @@ void EventStateManager::FillInEventFromG
                "Incorrect widget in event");
 
   // Set the coordinates in the new event to the coordinates of
   // the old event, adjusted for the fact that the widget might be
   // different
   aEvent->mRefPoint =
       mGestureDownPoint - aEvent->mWidget->WidgetToScreenOffset();
   aEvent->mModifiers = mGestureModifiers;
-  aEvent->buttons = mGestureDownButtons;
+  aEvent->mButtons = mGestureDownButtons;
 }
 
 void EventStateManager::MaybeFirePointerCancel(WidgetInputEvent* aEvent) {
   RefPtr<PresShell> presShell = mPresContext->GetPresShell();
   AutoWeakFrame targetFrame = mCurrentTarget;
 
   if (!PointerEventHandler::IsPointerEventEnabled() || !presShell ||
       !targetFrame) {
@@ -1808,19 +1808,19 @@ void EventStateManager::GenerateDragGest
       nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetNearestWidget();
 
       // get the widget from the target frame
       WidgetDragEvent startEvent(aEvent->IsTrusted(), eDragStart, widget);
       FillInEventFromGestureDown(&startEvent);
 
       startEvent.mDataTransfer = dataTransfer;
       if (aEvent->AsMouseEvent()) {
-        startEvent.inputSource = aEvent->AsMouseEvent()->inputSource;
+        startEvent.mInputSource = aEvent->AsMouseEvent()->mInputSource;
       } else if (aEvent->AsTouchEvent()) {
-        startEvent.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+        startEvent.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
       } else {
         MOZ_ASSERT(false);
       }
 
       // Dispatch to the DOM. By setting mCurrentTarget we are faking
       // out the ESM and telling it that the current target frame is
       // actually where the mouseDown occurred, otherwise it will use
       // the frame the mouse is currently over which may or may not be
@@ -2298,20 +2298,20 @@ void EventStateManager::SendLineScrollEv
   WidgetMouseScrollEvent event(aEvent->IsTrusted(),
                                eLegacyMouseLineOrPageScroll, aEvent->mWidget);
   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.mRefPoint = aEvent->mRefPoint;
   event.mTime = aEvent->mTime;
   event.mTimeStamp = aEvent->mTimeStamp;
   event.mModifiers = aEvent->mModifiers;
-  event.buttons = aEvent->buttons;
+  event.mButtons = aEvent->mButtons;
   event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.mDelta = aDelta;
-  event.inputSource = aEvent->inputSource;
+  event.mInputSource = aEvent->mInputSource;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(), &event,
                             nullptr, &status);
   aState.mDefaultPrevented =
       event.DefaultPrevented() || status == nsEventStatus_eConsumeNoDefault;
   aState.mDefaultPreventedByContent = event.DefaultPreventedByContent();
 }
@@ -2334,20 +2334,20 @@ void EventStateManager::SendPixelScrollE
   WidgetMouseScrollEvent event(aEvent->IsTrusted(), eLegacyMousePixelScroll,
                                aEvent->mWidget);
   event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   event.mRefPoint = aEvent->mRefPoint;
   event.mTime = aEvent->mTime;
   event.mTimeStamp = aEvent->mTimeStamp;
   event.mModifiers = aEvent->mModifiers;
-  event.buttons = aEvent->buttons;
+  event.mButtons = aEvent->mButtons;
   event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   event.mDelta = aPixelDelta;
-  event.inputSource = aEvent->inputSource;
+  event.mInputSource = aEvent->mInputSource;
 
   nsEventStatus status = nsEventStatus_eIgnore;
   EventDispatcher::Dispatch(targetContent, aTargetFrame->PresContext(), &event,
                             nullptr, &status);
   aState.mDefaultPrevented =
       event.DefaultPrevented() || status == nsEventStatus_eConsumeNoDefault;
   aState.mDefaultPreventedByContent = event.DefaultPreventedByContent();
 }
@@ -3015,17 +3015,17 @@ nsresult EventStateManager::PostHandleEv
 
   // Keep the prescontext alive, we might need it after event dispatch
   RefPtr<nsPresContext> presContext = aPresContext;
   nsresult ret = NS_OK;
 
   switch (aEvent->mMessage) {
     case eMouseDown: {
       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
-      if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
+      if (mouseEvent->mButton == MouseButton::eLeft &&
           !sNormalLMouseEventInProcess) {
         // We got a mouseup event while a mousedown event was being processed.
         // Make sure that the capturing content is cleared.
         nsIPresShell::SetCapturingContent(nullptr, 0);
         break;
       }
 
       // For remote content, capture the event in the parent process at the
@@ -3152,17 +3152,17 @@ nsresult EventStateManager::PostHandleEv
           // focus to be shifted from the caret position instead of the root.
           if (newFocus) {
             // use the mouse flag and the noscroll flag so that the content
             // doesn't unexpectedly scroll when clicking an element that is
             // only half visible
             uint32_t flags =
                 nsIFocusManager::FLAG_BYMOUSE | nsIFocusManager::FLAG_NOSCROLL;
             // If this was a touch-generated event, pass that information:
-            if (mouseEvent->inputSource ==
+            if (mouseEvent->mInputSource ==
                 MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
               flags |= nsIFocusManager::FLAG_BYTOUCH;
             }
             fm->SetFocus(newFocus->AsElement(), flags);
           } else if (!suppressBlur) {
             // clear the focus within the frame and then set it as the
             // focused frame
             EnsureDocument(mPresContext);
@@ -3172,17 +3172,17 @@ nsresult EventStateManager::PostHandleEv
 #endif
                 fm->ClearFocus(mDocument->GetWindow());
               fm->SetFocusedWindow(mDocument->GetWindow());
             }
           }
         }
 
         // The rest is left button-specific.
-        if (mouseEvent->button != WidgetMouseEvent::eLeftButton) {
+        if (mouseEvent->mButton != MouseButton::eLeft) {
           break;
         }
 
         // The nearest enclosing element goes into the :active state.  If we're
         // not an element (so we're text or something) we need to obtain
         // our parent element and put it into :active instead.
         if (activeContent && !activeContent->IsElement()) {
           if (nsIContent* par = activeContent->GetFlattenedTreeParent()) {
@@ -3224,17 +3224,17 @@ nsresult EventStateManager::PostHandleEv
     case ePointerUp: {
       WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
       MOZ_ASSERT(pointerEvent);
       // Implicitly releasing capture for given pointer. ePointerLostCapture
       // should be send after ePointerUp or ePointerCancel.
       PointerEventHandler::ImplicitlyReleasePointerCapture(pointerEvent);
 
       if (pointerEvent->mMessage == ePointerCancel ||
-          pointerEvent->inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
+          pointerEvent->mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
         // After pointercancel, pointer becomes invalid so we can remove
         // relevant helper from table. Regarding pointerup with non-hoverable
         // device, the pointer also becomes invalid. Hoverable (mouse/pen)
         // pointers are valid all the time (not only between down/up).
         GenerateMouseEnterExit(pointerEvent);
         mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
       }
       break;
@@ -4078,32 +4078,32 @@ static void CreateMouseOrPointerWidgetEv
     AUTO_PROFILER_LABEL("CreateMouseOrPointerWidgetEvent", OTHER);
 
     nsAutoPtr<WidgetPointerEvent> newPointerEvent;
     newPointerEvent = new WidgetPointerEvent(aMouseEvent->IsTrusted(), aMessage,
                                              aMouseEvent->mWidget);
     newPointerEvent->mIsPrimary = sourcePointer->mIsPrimary;
     newPointerEvent->mWidth = sourcePointer->mWidth;
     newPointerEvent->mHeight = sourcePointer->mHeight;
-    newPointerEvent->inputSource = sourcePointer->inputSource;
+    newPointerEvent->mInputSource = sourcePointer->mInputSource;
     newPointerEvent->mRelatedTarget = aRelatedContent;
     aNewEvent = newPointerEvent.forget();
   } else {
     aNewEvent =
         new WidgetMouseEvent(aMouseEvent->IsTrusted(), aMessage,
                              aMouseEvent->mWidget, WidgetMouseEvent::eReal);
     aNewEvent->mRelatedTarget = aRelatedContent;
   }
   aNewEvent->mRefPoint = aMouseEvent->mRefPoint;
   aNewEvent->mModifiers = aMouseEvent->mModifiers;
-  aNewEvent->button = aMouseEvent->button;
-  aNewEvent->buttons = aMouseEvent->buttons;
-  aNewEvent->pressure = aMouseEvent->pressure;
+  aNewEvent->mButton = aMouseEvent->mButton;
+  aNewEvent->mButtons = aMouseEvent->mButtons;
+  aNewEvent->mPressure = aMouseEvent->mPressure;
   aNewEvent->mPluginEvent = aMouseEvent->mPluginEvent;
-  aNewEvent->inputSource = aMouseEvent->inputSource;
+  aNewEvent->mInputSource = aMouseEvent->mInputSource;
   aNewEvent->pointerId = aMouseEvent->pointerId;
 }
 
 nsIFrame* EventStateManager::DispatchMouseOrPointerEvent(
     WidgetMouseEvent* aMouseEvent, EventMessage aMessage,
     nsIContent* aTargetContent, nsIContent* aRelatedContent) {
   // http://dvcs.w3.org/hg/webevents/raw-file/default/mouse-lock.html#methods
   // "[When the mouse is locked on an element...e]vents that require the concept
@@ -4763,52 +4763,52 @@ nsresult EventStateManager::SetClickCoun
   }
   if (mouseContent && mouseContent->IsText()) {
     nsINode* parent = mouseContent->GetFlattenedTreeParentNode();
     if (parent && parent->IsContent()) {
       mouseContent = parent->AsContent();
     }
   }
 
-  switch (aEvent->button) {
-    case WidgetMouseEvent::eLeftButton:
+  switch (aEvent->mButton) {
+    case MouseButton::eLeft:
       if (aEvent->mMessage == eMouseDown) {
         mLastLeftMouseDownContent = mouseContent;
       } else if (aEvent->mMessage == eMouseUp) {
         aEvent->mClickTarget =
             nsContentUtils::GetCommonAncestorUnderInteractiveContent(
                 mouseContent, mLastLeftMouseDownContent);
         if (aEvent->mClickTarget) {
           aEvent->mClickCount = mLClickCount;
           mLClickCount = 0;
         } else {
           aEvent->mClickCount = 0;
         }
         mLastLeftMouseDownContent = nullptr;
       }
       break;
 
-    case WidgetMouseEvent::eMiddleButton:
+    case MouseButton::eMiddle:
       if (aEvent->mMessage == eMouseDown) {
         mLastMiddleMouseDownContent = mouseContent;
       } else if (aEvent->mMessage == eMouseUp) {
         aEvent->mClickTarget =
             nsContentUtils::GetCommonAncestorUnderInteractiveContent(
                 mouseContent, mLastMiddleMouseDownContent);
         if (aEvent->mClickTarget) {
           aEvent->mClickCount = mMClickCount;
           mMClickCount = 0;
         } else {
           aEvent->mClickCount = 0;
         }
         mLastMiddleMouseDownContent = nullptr;
       }
       break;
 
-    case WidgetMouseEvent::eRightButton:
+    case MouseButton::eRight:
       if (aEvent->mMessage == eMouseDown) {
         mLastRightMouseDownContent = mouseContent;
       } else if (aEvent->mMessage == eMouseUp) {
         aEvent->mClickTarget =
             nsContentUtils::GetCommonAncestorUnderInteractiveContent(
                 mouseContent, mLastRightMouseDownContent);
         if (aEvent->mClickTarget) {
           aEvent->mClickCount = mRClickCount;
@@ -4855,25 +4855,25 @@ nsresult EventStateManager::InitAndDispa
   MOZ_ASSERT(aMouseUpContent || aCurrentTarget || aOverrideClickTarget);
 
   WidgetMouseEvent event(aMouseUpEvent->IsTrusted(), aMessage,
                          aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
 
   event.mRefPoint = aMouseUpEvent->mRefPoint;
   event.mClickCount = aMouseUpEvent->mClickCount;
   event.mModifiers = aMouseUpEvent->mModifiers;
-  event.buttons = aMouseUpEvent->buttons;
+  event.mButtons = aMouseUpEvent->mButtons;
   event.mTime = aMouseUpEvent->mTime;
   event.mTimeStamp = aMouseUpEvent->mTimeStamp;
   event.mFlags.mOnlyChromeDispatch =
       aNoContentDispatch && !aMouseUpEvent->mUseLegacyNonPrimaryDispatch;
   event.mFlags.mNoContentDispatch = aNoContentDispatch;
-  event.button = aMouseUpEvent->button;
+  event.mButton = aMouseUpEvent->mButton;
   event.pointerId = aMouseUpEvent->pointerId;
-  event.inputSource = aMouseUpEvent->inputSource;
+  event.mInputSource = aMouseUpEvent->mInputSource;
   nsIContent* target = aMouseUpContent;
   nsIFrame* targetFrame = aCurrentTarget;
   if (aOverrideClickTarget) {
     target = aOverrideClickTarget;
     targetFrame = aOverrideClickTarget->GetPrimaryFrame();
   }
 
   if (!target->IsInComposedDoc()) {
@@ -4941,17 +4941,17 @@ nsresult EventStateManager::PostHandleMo
   // conform to DOM events.  If we need to keep compatibility with Chromium,
   // we should change it later.
   if (status == nsEventStatus_eConsumeNoDefault) {
     *aStatus = nsEventStatus_eConsumeNoDefault;
     return NS_OK;
   }
 
   // Handle middle click paste if it's enabled and the mouse button is middle.
-  if (aMouseUpEvent->button != WidgetMouseEventBase::eMiddleButton ||
+  if (aMouseUpEvent->mButton != MouseButton::eMiddle ||
       !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
     return NS_OK;
   }
   DebugOnly<nsresult> rvIgnored =
       HandleMiddleClickPaste(presShell, aMouseUpEvent, &status, nullptr);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                        "Failed to paste for a middle click");
 
@@ -4974,18 +4974,18 @@ nsresult EventStateManager::DispatchClic
     nsIContent* aOverrideClickTarget) {
   MOZ_ASSERT(aPresShell);
   MOZ_ASSERT(aMouseUpEvent);
   MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
   MOZ_ASSERT(aStatus);
   MOZ_ASSERT(aClickTarget || aOverrideClickTarget);
 
   bool notDispatchToContents =
-      (aMouseUpEvent->button == WidgetMouseEvent::eMiddleButton ||
-       aMouseUpEvent->button == WidgetMouseEvent::eRightButton);
+      (aMouseUpEvent->mButton == MouseButton::eMiddle ||
+       aMouseUpEvent->mButton == MouseButton::eRight);
 
   bool fireAuxClick = notDispatchToContents;
 
   AutoWeakFrame currentTarget = aClickTarget->GetPrimaryFrame();
   nsresult rv = InitAndDispatchClickEvent(
       aMouseUpEvent, aStatus, eMouseClick, aPresShell, aClickTarget,
       currentTarget, notDispatchToContents, aOverrideClickTarget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -5016,17 +5016,17 @@ nsresult EventStateManager::DispatchClic
 }
 
 nsresult EventStateManager::HandleMiddleClickPaste(
     nsIPresShell* aPresShell, WidgetMouseEvent* aMouseEvent,
     nsEventStatus* aStatus, TextEditor* aTextEditor) {
   MOZ_ASSERT(aPresShell);
   MOZ_ASSERT(aMouseEvent);
   MOZ_ASSERT((aMouseEvent->mMessage == eMouseAuxClick &&
-              aMouseEvent->button == WidgetMouseEventBase::eMiddleButton) ||
+              aMouseEvent->mButton == MouseButton::eMiddle) ||
              EventCausesClickEvents(*aMouseEvent));
   MOZ_ASSERT(aStatus);
   MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault);
 
   // Even if we're called twice or more for a mouse operation, we should
   // handle only once.  Although mMultipleActionsPrevented may be set to
   // true by different event handler in the future, we can use it for now.
   if (aMouseEvent->mFlags.mMultipleActionsPrevented) {
@@ -5469,17 +5469,17 @@ void EventStateManager::ContentRemoved(D
   for (auto iter = mPointersEnterLeaveHelper.Iter(); !iter.Done();
        iter.Next()) {
     ResetLastOverForContent(iter.Key(), iter.Data(), aContent);
   }
 }
 
 bool EventStateManager::EventStatusOK(WidgetGUIEvent* aEvent) {
   return !(aEvent->mMessage == eMouseDown &&
-           aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
+           aEvent->AsMouseEvent()->mButton == MouseButton::eLeft &&
            !sNormalLMouseEventInProcess);
 }
 
 //-------------------------------------------
 // Access Key Registration
 //-------------------------------------------
 void EventStateManager::RegisterAccessKey(Element* aElement, uint32_t aKey) {
   if (aElement && mAccessKeys.IndexOf(aElement) == -1)
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -830,18 +830,18 @@ bool IMEContentObserver::OnMouseButtonEv
 
   IMENotification notification(NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
   notification.mMouseButtonEventData.mEventMessage = aMouseEvent->mMessage;
   notification.mMouseButtonEventData.mOffset = charAtPt.mReply.mOffset;
   notification.mMouseButtonEventData.mCursorPos.Set(
       charAtPt.mRefPoint.ToUnknownPoint());
   notification.mMouseButtonEventData.mCharRect.Set(
       charAtPt.mReply.mRect.ToUnknownRect());
-  notification.mMouseButtonEventData.mButton = aMouseEvent->button;
-  notification.mMouseButtonEventData.mButtons = aMouseEvent->buttons;
+  notification.mMouseButtonEventData.mButton = aMouseEvent->mButton;
+  notification.mMouseButtonEventData.mButtons = aMouseEvent->mButtons;
   notification.mMouseButtonEventData.mModifiers = aMouseEvent->mModifiers;
 
   nsresult rv = IMEStateManager::NotifyIME(notification, mWidget);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -724,18 +724,18 @@ bool IMEStateManager::OnMouseButtonEvent
 
   bool consumed =
       sActiveIMEContentObserver->OnMouseButtonEvent(aPresContext, aMouseEvent);
 
   if (MOZ_LOG_TEST(sISMLog, LogLevel::Info)) {
     nsAutoString eventType;
     MOZ_LOG(sISMLog, LogLevel::Info,
             ("  OnMouseButtonEventInEditor(), "
-             "mouse event (mMessage=%s, button=%d) is %s",
-             ToChar(aMouseEvent->mMessage), aMouseEvent->button,
+             "mouse event (mMessage=%s, mButton=%d) is %s",
+             ToChar(aMouseEvent->mMessage), aMouseEvent->mButton,
              consumed ? "consumed" : "not consumed"));
   }
 
   return consumed;
 }
 
 // static
 void IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
@@ -768,32 +768,32 @@ void IMEStateManager::OnClickInEditor(ns
 
   if (!aMouseEvent->IsTrusted()) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
             ("  OnClickInEditor(), "
              "the mouse event isn't a trusted event"));
     return;  // ignore untrusted event.
   }
 
-  if (aMouseEvent->button) {
+  if (aMouseEvent->mButton) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
             ("  OnClickInEditor(), "
              "the mouse event isn't a left mouse button event"));
     return;  // not a left click event.
   }
 
   if (aMouseEvent->mClickCount != 1) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
             ("  OnClickInEditor(), "
              "the mouse event isn't a single click event"));
     return;  // should notify only first click event.
   }
 
   InputContextAction::Cause cause =
-      aMouseEvent->inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH
+      aMouseEvent->mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH
           ? InputContextAction::CAUSE_TOUCH
           : InputContextAction::CAUSE_MOUSE;
 
   InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED);
   IMEState newState = GetNewIMEState(aPresContext, aContent);
   SetIMEState(newState, aPresContext, aContent, widget, action, sOrigin);
 }
 
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -25,17 +25,17 @@ MouseEvent::MouseEvent(EventTarget* aOwn
 
   WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
   if (aEvent) {
     mEventIsInternal = false;
   } else {
     mEventIsInternal = true;
     mEvent->mTime = PR_Now();
     mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
-    mouseEvent->inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
+    mouseEvent->mInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   }
 
   if (mouseEvent) {
     MOZ_ASSERT(mouseEvent->mReason != WidgetMouseEvent::eSynthesized,
                "Don't dispatch DOM events from synthesized mouse events");
     mDetail = mouseEvent->mClickCount;
   }
 }
@@ -55,17 +55,17 @@ void MouseEvent::InitMouseEvent(const ns
     case eMouseEventClass:
     case eMouseScrollEventClass:
     case eWheelEventClass:
     case eDragEventClass:
     case ePointerEventClass:
     case eSimpleGestureEventClass: {
       WidgetMouseEventBase* mouseEventBase = mEvent->AsMouseEventBase();
       mouseEventBase->mRelatedTarget = aRelatedTarget;
-      mouseEventBase->button = aButton;
+      mouseEventBase->mButton = aButton;
       mouseEventBase->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey,
                                          aMetaKey);
       mClientPoint.x = aClientX;
       mClientPoint.y = aClientY;
       mouseEventBase->mRefPoint.x = aScreenX;
       mouseEventBase->mRefPoint.y = aScreenY;
 
       WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
@@ -108,17 +108,17 @@ void MouseEvent::InitMouseEvent(const ns
     default:
       MOZ_CRASH("There is no space to store the modifiers");
   }
 }
 
 void MouseEvent::InitializeExtraMouseEventDictionaryMembers(
     const MouseEventInit& aParam) {
   InitModifiers(aParam);
-  mEvent->AsMouseEventBase()->buttons = aParam.mButtons;
+  mEvent->AsMouseEventBase()->mButtons = aParam.mButtons;
   mMovementPoint.x = aParam.mMovementX;
   mMovementPoint.y = aParam.mMovementY;
 }
 
 already_AddRefed<MouseEvent> MouseEvent::Constructor(
     const GlobalObject& aGlobal, const nsAString& aType,
     const MouseEventInit& aParam, ErrorResult& aRv) {
   nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
@@ -146,44 +146,44 @@ void MouseEvent::InitNSMouseEvent(const 
   NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
 
   MouseEvent::InitMouseEvent(aType, aCanBubble, aCancelable, aView, aDetail,
                              aScreenX, aScreenY, aClientX, aClientY, aCtrlKey,
                              aAltKey, aShiftKey, aMetaKey, aButton,
                              aRelatedTarget);
 
   WidgetMouseEventBase* mouseEventBase = mEvent->AsMouseEventBase();
-  mouseEventBase->pressure = aPressure;
-  mouseEventBase->inputSource = aInputSource;
+  mouseEventBase->mPressure = aPressure;
+  mouseEventBase->mInputSource = aInputSource;
 }
 
 int16_t MouseEvent::Button() {
   switch (mEvent->mClass) {
     case eMouseEventClass:
     case eMouseScrollEventClass:
     case eWheelEventClass:
     case eDragEventClass:
     case ePointerEventClass:
     case eSimpleGestureEventClass:
-      return mEvent->AsMouseEventBase()->button;
+      return mEvent->AsMouseEventBase()->mButton;
     default:
-      NS_WARNING("Tried to get mouse button for non-mouse event!");
-      return WidgetMouseEvent::eLeftButton;
+      NS_WARNING("Tried to get mouse mButton for non-mouse event!");
+      return MouseButton::eLeft;
   }
 }
 
 uint16_t MouseEvent::Buttons() {
   switch (mEvent->mClass) {
     case eMouseEventClass:
     case eMouseScrollEventClass:
     case eWheelEventClass:
     case eDragEventClass:
     case ePointerEventClass:
     case eSimpleGestureEventClass:
-      return mEvent->AsMouseEventBase()->buttons;
+      return mEvent->AsMouseEventBase()->mButtons;
     default:
       MOZ_CRASH("Tried to get mouse buttons for non-mouse event!");
   }
 }
 
 already_AddRefed<EventTarget> MouseEvent::GetRelatedTarget() {
   nsCOMPtr<EventTarget> relatedTarget;
   switch (mEvent->mClass) {
@@ -201,17 +201,17 @@ already_AddRefed<EventTarget> MouseEvent
 
   return EnsureWebAccessibleRelatedTarget(relatedTarget);
 }
 
 void MouseEvent::GetRegion(nsAString& aRegion) {
   SetDOMStringToNull(aRegion);
   WidgetMouseEventBase* mouseEventBase = mEvent->AsMouseEventBase();
   if (mouseEventBase) {
-    aRegion = mouseEventBase->region;
+    aRegion = mouseEventBase->mRegion;
   }
 }
 
 int32_t MouseEvent::ScreenX(CallerType aCallerType) {
   if (mEvent->mFlags.mIsPositionless) {
     return 0;
   }
 
@@ -284,25 +284,25 @@ bool MouseEvent::AltKey() { return mEven
 
 bool MouseEvent::CtrlKey() { return mEvent->AsInputEvent()->IsControl(); }
 
 bool MouseEvent::ShiftKey() { return mEvent->AsInputEvent()->IsShift(); }
 
 bool MouseEvent::MetaKey() { return mEvent->AsInputEvent()->IsMeta(); }
 
 float MouseEvent::MozPressure() const {
-  return mEvent->AsMouseEventBase()->pressure;
+  return mEvent->AsMouseEventBase()->mPressure;
 }
 
 bool MouseEvent::HitCluster() const {
-  return mEvent->AsMouseEventBase()->hitCluster;
+  return mEvent->AsMouseEventBase()->mHitCluster;
 }
 
 uint16_t MouseEvent::MozInputSource() const {
-  return mEvent->AsMouseEventBase()->inputSource;
+  return mEvent->AsMouseEventBase()->mInputSource;
 }
 
 }  // namespace dom
 }  // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
--- a/dom/events/MouseScrollEvent.cpp
+++ b/dom/events/MouseScrollEvent.cpp
@@ -20,17 +20,17 @@ MouseScrollEvent::MouseScrollEvent(Event
                      ? aEvent
                      : new WidgetMouseScrollEvent(false, eVoidEvent, nullptr)) {
   if (aEvent) {
     mEventIsInternal = false;
   } else {
     mEventIsInternal = true;
     mEvent->mTime = PR_Now();
     mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
-    static_cast<WidgetMouseEventBase*>(mEvent)->inputSource =
+    static_cast<WidgetMouseEventBase*>(mEvent)->mInputSource =
         MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   }
 
   mDetail = mEvent->AsMouseScrollEvent()->mDelta;
 }
 
 void MouseScrollEvent::InitMouseScrollEvent(
     const nsAString& aType, bool aCanBubble, bool aCancelable,
--- a/dom/events/PointerEvent.cpp
+++ b/dom/events/PointerEvent.cpp
@@ -26,17 +26,17 @@ PointerEvent::PointerEvent(EventTarget* 
 
   WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
   if (aEvent) {
     mEventIsInternal = false;
   } else {
     mEventIsInternal = true;
     mEvent->mTime = PR_Now();
     mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
-    mouseEvent->inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
+    mouseEvent->mInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   }
   // 5.2 Pointer Event types, for all pointer events, |detail| attribute SHOULD
   // be 0.
   mDetail = 0;
 }
 
 JSObject* PointerEvent::WrapObjectInternal(JSContext* aCx,
                                            JS::Handle<JSObject*> aGivenProto) {
@@ -87,24 +87,24 @@ already_AddRefed<PointerEvent> PointerEv
                     aParam.mClientX, aParam.mClientY, false, false, false,
                     false, aParam.mButton, aParam.mRelatedTarget);
   e->InitializeExtraMouseEventDictionaryMembers(aParam);
 
   WidgetPointerEvent* widgetEvent = e->mEvent->AsPointerEvent();
   widgetEvent->pointerId = aParam.mPointerId;
   widgetEvent->mWidth = aParam.mWidth;
   widgetEvent->mHeight = aParam.mHeight;
-  widgetEvent->pressure = aParam.mPressure;
+  widgetEvent->mPressure = aParam.mPressure;
   widgetEvent->tangentialPressure = aParam.mTangentialPressure;
   widgetEvent->tiltX = aParam.mTiltX;
   widgetEvent->tiltY = aParam.mTiltY;
   widgetEvent->twist = aParam.mTwist;
-  widgetEvent->inputSource = ConvertStringToPointerType(aParam.mPointerType);
+  widgetEvent->mInputSource = ConvertStringToPointerType(aParam.mPointerType);
   widgetEvent->mIsPrimary = aParam.mIsPrimary;
-  widgetEvent->buttons = aParam.mButtons;
+  widgetEvent->mButtons = aParam.mButtons;
 
   if (!aParam.mCoalescedEvents.IsEmpty()) {
     e->mCoalescedEvents.AppendElements(aParam.mCoalescedEvents);
   }
   e->SetTrusted(trusted);
   e->SetComposed(aParam.mComposed);
   return e.forget();
 }
@@ -135,17 +135,17 @@ NS_IMPL_RELEASE_INHERITED(PointerEvent, 
 
 void PointerEvent::GetPointerType(nsAString& aPointerType,
                                   CallerType aCallerType) {
   if (ShouldResistFingerprinting(aCallerType)) {
     aPointerType.AssignLiteral("mouse");
     return;
   }
 
-  ConvertPointerTypeToString(mEvent->AsPointerEvent()->inputSource,
+  ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource,
                              aPointerType);
 }
 
 int32_t PointerEvent::PointerId(CallerType aCallerType) {
   return ShouldResistFingerprinting(aCallerType)
              ? PointerEventHandler::GetSpoofedPointerIdForRFP()
              : mEvent->AsPointerEvent()->pointerId;
 }
@@ -160,26 +160,26 @@ int32_t PointerEvent::Height(CallerType 
   return ShouldResistFingerprinting(aCallerType)
              ? 1
              : mEvent->AsPointerEvent()->mHeight;
 }
 
 float PointerEvent::Pressure(CallerType aCallerType) {
   if (mEvent->mMessage == ePointerUp ||
       !ShouldResistFingerprinting(aCallerType)) {
-    return mEvent->AsPointerEvent()->pressure;
+    return mEvent->AsPointerEvent()->mPressure;
   }
 
   // According to [1], we should use 0.5 when it is in active buttons state and
   // 0 otherwise for devices that don't support pressure. And a pointerup event
   // always reports 0, so we don't need to spoof that.
   //
   // [1] https://www.w3.org/TR/pointerevents/#dom-pointerevent-pressure
   float spoofedPressure = 0.0;
-  if (mEvent->AsPointerEvent()->buttons) {
+  if (mEvent->AsPointerEvent()->mButtons) {
     spoofedPressure = 0.5;
   }
 
   return spoofedPressure;
 }
 
 float PointerEvent::TangentialPressure(CallerType aCallerType) {
   return ShouldResistFingerprinting(aCallerType)
@@ -256,17 +256,17 @@ bool PointerEvent::ShouldResistFingerpri
   //   2. This event is a mouse pointer event.
   //   3. The caller type is system.
   //   4. The pref privcy.resistFingerprinting' is false, we fast return here
   //      since we don't need to do any QI of following codes.
   //  We don't need to check for the system group since pointer events won't be
   //  dispatched to the system group.
   if (!mEvent->IsTrusted() || aCallerType == CallerType::System ||
       !nsContentUtils::ShouldResistFingerprinting() ||
-      mEvent->AsPointerEvent()->inputSource ==
+      mEvent->AsPointerEvent()->mInputSource ==
           MouseEvent_Binding::MOZ_SOURCE_MOUSE) {
     return false;
   }
 
   nsCOMPtr<Document> doc = GetDocument();
 
   return doc && !nsContentUtils::IsChromeDoc(doc);
 }
--- a/dom/events/PointerEventHandler.cpp
+++ b/dom/events/PointerEventHandler.cpp
@@ -87,42 +87,45 @@ bool PointerEventHandler::IsPointerEvent
 void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent) {
   if (!IsPointerEventEnabled() || !aEvent) {
     return;
   }
   switch (aEvent->mMessage) {
     case eMouseEnterIntoWidget:
       // In this case we have to know information about available mouse pointers
       sActivePointersIds->Put(
-          aEvent->pointerId, new PointerInfo(false, aEvent->inputSource, true));
+          aEvent->pointerId,
+          new PointerInfo(false, aEvent->mInputSource, true));
 
-      MaybeCacheSpoofedPointerID(aEvent->inputSource, aEvent->pointerId);
+      MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
       break;
     case ePointerDown:
       // In this case we switch pointer to active state
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
-        sActivePointersIds->Put(pointerEvent->pointerId,
-                                new PointerInfo(true, pointerEvent->inputSource,
-                                                pointerEvent->mIsPrimary));
-        MaybeCacheSpoofedPointerID(pointerEvent->inputSource,
+        sActivePointersIds->Put(
+            pointerEvent->pointerId,
+            new PointerInfo(true, pointerEvent->mInputSource,
+                            pointerEvent->mIsPrimary));
+        MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
                                    pointerEvent->pointerId);
       }
       break;
     case ePointerCancel:
       // pointercancel means a pointer is unlikely to continue to produce
       // pointer events. In that case, we should turn off active state or remove
       // the pointer from active pointers.
     case ePointerUp:
       // In this case we remove information about pointer or turn off active
       // state
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
-        if (pointerEvent->inputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
+        if (pointerEvent->mInputSource !=
+            MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
           sActivePointersIds->Put(
               pointerEvent->pointerId,
-              new PointerInfo(false, pointerEvent->inputSource,
+              new PointerInfo(false, pointerEvent->mInputSource,
                               pointerEvent->mIsPrimary));
         } else {
           sActivePointersIds->Remove(pointerEvent->pointerId);
         }
       }
       break;
     case eMouseExitFromWidget:
       // In this case we have to remove information about disappeared mouse
@@ -306,17 +309,17 @@ void PointerEventHandler::ImplicitlyCapt
   MOZ_ASSERT(aEvent->mMessage == ePointerDown);
   if (!aFrame || !IsPointerEventEnabled() ||
       !IsPointerEventImplicitCaptureForTouchEnabled()) {
     return;
   }
   WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
   NS_WARNING_ASSERTION(pointerEvent,
                        "Call ImplicitlyCapturePointer with non-pointer event");
-  if (pointerEvent->inputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
+  if (pointerEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
     // We only implicitly capture the pointer for touch device.
     return;
   }
   nsCOMPtr<nsIContent> target;
   aFrame->GetContentForEvent(aEvent, getter_AddRefs(target));
   while (target && !target->IsElement()) {
     target = target->GetParent();
   }
@@ -435,58 +438,58 @@ void PointerEventHandler::PostHandlePoin
 
 /* static */
 void PointerEventHandler::InitPointerEventFromMouse(
     WidgetPointerEvent* aPointerEvent, WidgetMouseEvent* aMouseEvent,
     EventMessage aMessage) {
   MOZ_ASSERT(aPointerEvent);
   MOZ_ASSERT(aMouseEvent);
   aPointerEvent->pointerId = aMouseEvent->pointerId;
-  aPointerEvent->inputSource = aMouseEvent->inputSource;
+  aPointerEvent->mInputSource = aMouseEvent->mInputSource;
   aPointerEvent->mMessage = aMessage;
-  aPointerEvent->button = aMouseEvent->mMessage == eMouseMove
-                              ? WidgetMouseEvent::eNoButton
-                              : aMouseEvent->button;
+  aPointerEvent->mButton = aMouseEvent->mMessage == eMouseMove
+                               ? MouseButton::eNotPressed
+                               : aMouseEvent->mButton;
 
-  aPointerEvent->buttons = aMouseEvent->buttons;
-  aPointerEvent->pressure =
-      aPointerEvent->buttons
-          ? aMouseEvent->pressure ? aMouseEvent->pressure : 0.5f
+  aPointerEvent->mButtons = aMouseEvent->mButtons;
+  aPointerEvent->mPressure =
+      aPointerEvent->mButtons
+          ? aMouseEvent->mPressure ? aMouseEvent->mPressure : 0.5f
           : 0.0f;
 }
 
 /* static */
 void PointerEventHandler::InitPointerEventFromTouch(
     WidgetPointerEvent* aPointerEvent, WidgetTouchEvent* aTouchEvent,
     mozilla::dom::Touch* aTouch, bool aIsPrimary) {
   MOZ_ASSERT(aPointerEvent);
   MOZ_ASSERT(aTouchEvent);
 
   int16_t button = aTouchEvent->mMessage == eTouchMove
-                       ? WidgetMouseEvent::eNoButton
-                       : WidgetMouseEvent::eLeftButton;
+                       ? MouseButton::eNotPressed
+                       : MouseButton::eLeft;
 
   int16_t buttons = aTouchEvent->mMessage == eTouchEnd
-                        ? WidgetMouseEvent::eNoButtonFlag
-                        : WidgetMouseEvent::eLeftButtonFlag;
+                        ? MouseButtonsFlag::eNoButtons
+                        : MouseButtonsFlag::eLeftFlag;
 
   aPointerEvent->mIsPrimary = aIsPrimary;
   aPointerEvent->pointerId = aTouch->Identifier();
   aPointerEvent->mRefPoint = aTouch->mRefPoint;
   aPointerEvent->mModifiers = aTouchEvent->mModifiers;
   aPointerEvent->mWidth = aTouch->RadiusX(CallerType::System);
   aPointerEvent->mHeight = aTouch->RadiusY(CallerType::System);
   aPointerEvent->tiltX = aTouch->tiltX;
   aPointerEvent->tiltY = aTouch->tiltY;
   aPointerEvent->mTime = aTouchEvent->mTime;
   aPointerEvent->mTimeStamp = aTouchEvent->mTimeStamp;
   aPointerEvent->mFlags = aTouchEvent->mFlags;
-  aPointerEvent->button = button;
-  aPointerEvent->buttons = buttons;
-  aPointerEvent->inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+  aPointerEvent->mButton = button;
+  aPointerEvent->mButtons = buttons;
+  aPointerEvent->mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
 }
 
 /* static */
 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
     PresShell* aShell, nsIFrame* aFrame, nsIContent* aContent,
     WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aStatus,
     nsIContent** aTargetContent) {
   MOZ_ASSERT(IsPointerEventEnabled());
@@ -498,28 +501,28 @@ void PointerEventHandler::DispatchPointe
     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
     // 1. If it is not mouse then it is likely will come as touch event
     // 2. We don't synthesize pointer events for those events that are not
     //    dispatched to DOM.
     if (!mouseEvent->convertToPointer ||
         !aEvent->IsAllowedToDispatchDOMEvent()) {
       return;
     }
-    int16_t button = mouseEvent->button;
+    int16_t button = mouseEvent->mButton;
     switch (mouseEvent->mMessage) {
       case eMouseMove:
-        button = WidgetMouseEvent::eNoButton;
+        button = MouseButton::eNotPressed;
         pointerMessage = ePointerMove;
         break;
       case eMouseUp:
-        pointerMessage = mouseEvent->buttons ? ePointerMove : ePointerUp;
+        pointerMessage = mouseEvent->mButtons ? ePointerMove : ePointerUp;
         break;
       case eMouseDown:
         pointerMessage =
-            mouseEvent->buttons &
+            mouseEvent->mButtons &
                     ~nsContentUtils::GetButtonsFlagForButton(button)
                 ? ePointerMove
                 : ePointerDown;
         break;
       default:
         return;
     }
 
@@ -634,17 +637,17 @@ void PointerEventHandler::DispatchGotOrL
 
   if (!aIsGotCapture && !aCaptureTarget->IsInComposedDoc()) {
     // If the capturing element was removed from the DOM tree, fire
     // ePointerLostCapture at the document.
     PointerEventInit init;
     init.mPointerId = aPointerEvent->pointerId;
     init.mBubbles = true;
     init.mComposed = true;
-    ConvertPointerTypeToString(aPointerEvent->inputSource, init.mPointerType);
+    ConvertPointerTypeToString(aPointerEvent->mInputSource, init.mPointerType);
     init.mIsPrimary = aPointerEvent->mIsPrimary;
     RefPtr<PointerEvent> event;
     event = PointerEvent::Constructor(
         aCaptureTarget, NS_LITERAL_STRING("lostpointercapture"), init);
     targetDoc->DispatchEvent(*event);
     return;
   }
   nsEventStatus status = nsEventStatus_eIgnore;
--- a/dom/events/SimpleGestureEvent.cpp
+++ b/dom/events/SimpleGestureEvent.cpp
@@ -23,17 +23,17 @@ SimpleGestureEvent::SimpleGestureEvent(E
                "event type mismatch");
 
   if (aEvent) {
     mEventIsInternal = false;
   } else {
     mEventIsInternal = true;
     mEvent->mTime = PR_Now();
     mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
-    static_cast<WidgetMouseEventBase*>(mEvent)->inputSource =
+    static_cast<WidgetMouseEventBase*>(mEvent)->mInputSource =
         MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   }
 }
 
 uint32_t SimpleGestureEvent::AllowedDirections() const {
   return mEvent->AsSimpleGestureEvent()->mAllowedDirections;
 }
 
--- a/dom/events/WheelEvent.cpp
+++ b/dom/events/WheelEvent.cpp
@@ -27,17 +27,17 @@ WheelEvent::WheelEvent(EventTarget* aOwn
     // it might be changed by changing zoom or something.
     if (aWheelEvent->mDeltaMode == WheelEvent_Binding::DOM_DELTA_PIXEL) {
       mAppUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
     }
   } else {
     mEventIsInternal = true;
     mEvent->mTime = PR_Now();
     mEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
-    mEvent->AsWheelEvent()->inputSource =
+    mEvent->AsWheelEvent()->mInputSource =
         MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   }
 }
 
 void WheelEvent::InitWheelEvent(
     const nsAString& aType, bool aCanBubble, bool aCancelable,
     nsGlobalWindowInner* aView, int32_t aDetail, int32_t aScreenX,
     int32_t aScreenY, int32_t aClientX, int32_t aClientY, uint16_t aButton,
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -554,17 +554,17 @@ void HTMLCanvasElement::GetEventTargetPa
       }
       nsPoint ptInRoot =
           nsLayoutUtils::GetEventCoordinatesRelativeTo(evt, frame);
       nsRect paddingRect = frame->GetContentRectRelativeToSelf();
       Point hitpoint;
       hitpoint.x = (ptInRoot.x - paddingRect.x) / AppUnitsPerCSSPixel();
       hitpoint.y = (ptInRoot.y - paddingRect.y) / AppUnitsPerCSSPixel();
 
-      evt->region = mCurrentContext->GetHitRegion(hitpoint);
+      evt->mRegion = mCurrentContext->GetHitRegion(hitpoint);
       aVisitor.mCanHandle = true;
     }
   }
   nsGenericHTMLElement::GetEventTargetParent(aVisitor);
 }
 
 nsChangeHint HTMLCanvasElement::GetAttributeChangeHint(const nsAtom* aAttribute,
                                                        int32_t aModType) const {
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4007,29 +4007,29 @@ nsresult HTMLInputElement::PostHandleEve
         } break;  // eKeyPress || eKeyUp
 
         case eMouseDown:
         case eMouseUp:
         case eMouseDoubleClick: {
           // cancel all of these events for buttons
           // XXXsmaug Why?
           WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
-          if (mouseEvent->button == WidgetMouseEvent::eMiddleButton ||
-              mouseEvent->button == WidgetMouseEvent::eRightButton) {
+          if (mouseEvent->mButton == MouseButton::eMiddle ||
+              mouseEvent->mButton == MouseButton::eRight) {
             if (mType == NS_FORM_INPUT_BUTTON || mType == NS_FORM_INPUT_RESET ||
                 mType == NS_FORM_INPUT_SUBMIT) {
               if (aVisitor.mDOMEvent) {
                 aVisitor.mDOMEvent->StopPropagation();
               } else {
                 rv = NS_ERROR_FAILURE;
               }
             }
           }
           if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted()) {
-            if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
+            if (mouseEvent->mButton == MouseButton::eLeft &&
                 !IgnoreInputEventWithModifier(mouseEvent, false)) {
               nsNumberControlFrame* numberControlFrame =
                   do_QueryFrame(GetPrimaryFrame());
               if (numberControlFrame) {
                 if (aVisitor.mEvent->mMessage == eMouseDown && IsMutable()) {
                   switch (numberControlFrame->GetSpinButtonForPointerEvent(
                       aVisitor.mEvent->AsMouseEvent())) {
                     case nsNumberControlFrame::eSpinButtonUp:
@@ -4186,18 +4186,18 @@ void HTMLInputElement::PostHandleEventFo
       if (nsIPresShell::GetCapturingContent()) {
         break;  // don't start drag if someone else is already capturing
       }
       WidgetInputEvent* inputEvent = aVisitor.mEvent->AsInputEvent();
       if (IgnoreInputEventWithModifier(inputEvent, true)) {
         break;  // ignore
       }
       if (aVisitor.mEvent->mMessage == eMouseDown) {
-        if (aVisitor.mEvent->AsMouseEvent()->buttons ==
-            WidgetMouseEvent::eLeftButtonFlag) {
+        if (aVisitor.mEvent->AsMouseEvent()->mButtons ==
+            MouseButtonsFlag::eLeftFlag) {
           StartRangeThumbDrag(inputEvent);
         } else if (mIsDraggingRange) {
           CancelRangeThumbDrag();
         }
       } else {
         if (aVisitor.mEvent->AsTouchEvent()->mTouches.Length() == 1) {
           StartRangeThumbDrag(inputEvent);
         } else if (mIsDraggingRange) {
--- a/dom/html/HTMLLabelElement.cpp
+++ b/dom/html/HTMLLabelElement.cpp
@@ -84,17 +84,17 @@ nsresult HTMLLabelElement::PostHandleEve
 
   // Strong ref because event dispatch is going to happen.
   RefPtr<Element> content = GetLabeledElement();
 
   if (content) {
     mHandlingEvent = true;
     switch (aVisitor.mEvent->mMessage) {
       case eMouseDown:
-        if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
+        if (mouseEvent->mButton == MouseButton::eLeft) {
           // We reset the mouse-down point on every event because there is
           // no guarantee we will reach the eMouseClick code below.
           LayoutDeviceIntPoint* curPoint =
               new LayoutDeviceIntPoint(mouseEvent->mRefPoint);
           SetProperty(nsGkAtoms::labelMouseDownPtProperty,
                       static_cast<void*>(curPoint),
                       nsINode::DeleteProperty<LayoutDeviceIntPoint>);
         }
@@ -133,19 +133,19 @@ nsresult HTMLLabelElement::PostHandleEve
               // Also, within HTMLInputElement::PostHandleEvent, inputs will
               // be selected only when focused via a key or when the navigation
               // flag is used and we want to select the text on label clicks as
               // well.
               // If the label has been clicked by the user, we also want to
               // pass FLAG_BYMOUSE so that we get correct focus ring behavior,
               // but we don't want to pass FLAG_BYMOUSE if this click event was
               // caused by the user pressing an accesskey.
-              bool byMouse = (mouseEvent->inputSource !=
+              bool byMouse = (mouseEvent->mInputSource !=
                               MouseEvent_Binding::MOZ_SOURCE_KEYBOARD);
-              bool byTouch = (mouseEvent->inputSource ==
+              bool byTouch = (mouseEvent->mInputSource ==
                               MouseEvent_Binding::MOZ_SOURCE_TOUCH);
               fm->SetFocus(content,
                            nsIFocusManager::FLAG_BYMOVEFOCUS |
                                nsIFocusManager::FLAG_BYELEMENTFOCUS |
                                (byMouse ? nsIFocusManager::FLAG_BYMOUSE : 0) |
                                (byTouch ? nsIFocusManager::FLAG_BYTOUCH : 0));
             }
           }
@@ -187,17 +187,17 @@ bool HTMLLabelElement::PerformAccesskey(
     nsPresContext* presContext = GetPresContext(eForUncomposedDoc);
     if (!presContext) {
       return false;
     }
 
     // Click on it if the users prefs indicate to do so.
     WidgetMouseEvent event(aIsTrustedEvent, eMouseClick, nullptr,
                            WidgetMouseEvent::eReal);
-    event.inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
+    event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
 
     nsAutoPopupStatePusher popupStatePusher(
         aIsTrustedEvent ? PopupBlocker::openAllowed : PopupBlocker::openAbused);
 
     EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
                               &event);
   }
 
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -2234,17 +2234,17 @@ void nsGenericHTMLElement::Click(CallerT
   }
 
   SetHandlingClick();
 
   // Mark this event trusted if Click() is called from system code.
   WidgetMouseEvent event(aCallerType == CallerType::System, eMouseClick,
                          nullptr, WidgetMouseEvent::eReal);
   event.mFlags.mIsPositionless = true;
-  event.inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
+  event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
 
   EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context, &event);
 
   ClearHandlingClick();
 }
 
 bool nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
                                            int32_t* aTabIndex) {
@@ -2350,17 +2350,17 @@ bool nsGenericHTMLElement::PerformAccess
   return focused;
 }
 
 nsresult nsGenericHTMLElement::DispatchSimulatedClick(
     nsGenericHTMLElement* aElement, bool aIsTrusted,
     nsPresContext* aPresContext) {
   WidgetMouseEvent event(aIsTrusted, eMouseClick, nullptr,
                          WidgetMouseEvent::eReal);
-  event.inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
+  event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
   event.mFlags.mIsPositionless = true;
   return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event);
 }
 
 already_AddRefed<TextEditor> nsGenericHTMLElement::GetAssociatedEditor() {
   // If contenteditable is ever implemented, it might need to do something
   // different here?
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1972,22 +1972,22 @@ interface nsIDOMWindowUtils : nsISupport
    */
   void resetPrefersReducedMotionOverrideForTest();
 
   // These consts are only for testing purposes.
   const long DEFAULT_MOUSE_POINTER_ID = 0;
   const long DEFAULT_PEN_POINTER_ID   = 1;
   const long DEFAULT_TOUCH_POINTER_ID = 2;
 
-  // Match WidgetMouseEventBase::buttonType.
+  // Match mozilla::MouseButton.
   const long MOUSE_BUTTON_LEFT_BUTTON   = 0;
   const long MOUSE_BUTTON_MIDDLE_BUTTON = 1;
   const long MOUSE_BUTTON_RIGHT_BUTTON  = 2;
 
-  // Match WidgetMouseEventBase::buttonsFlag.
+  // Match mozilla::MouseButtonsFlag.
   const long MOUSE_BUTTONS_NO_BUTTON = 0x00;
   const long MOUSE_BUTTONS_LEFT_BUTTON = 0x01;
   const long MOUSE_BUTTONS_RIGHT_BUTTON = 0x02;
   const long MOUSE_BUTTONS_MIDDLE_BUTTON = 0x04;
   // Typically, "back" button being left side of 5-button
   // mice, see "buttons" attribute document of DOM3 Events.
   const long MOUSE_BUTTONS_4TH_BUTTON = 0x08;
   // Typically, "forward" button being right side of 5-button
--- a/dom/ipc/CoalescedMouseData.cpp
+++ b/dom/ipc/CoalescedMouseData.cpp
@@ -21,22 +21,22 @@ void CoalescedMouseData::Coalesce(const 
     mGuid = aGuid;
     mInputBlockId = aInputBlockId;
     MOZ_ASSERT(!mCoalescedInputEvent->mCoalescedWidgetEvents);
   } else {
     MOZ_ASSERT(mGuid == aGuid);
     MOZ_ASSERT(mInputBlockId == aInputBlockId);
     MOZ_ASSERT(mCoalescedInputEvent->mModifiers == aEvent.mModifiers);
     MOZ_ASSERT(mCoalescedInputEvent->mReason == aEvent.mReason);
-    MOZ_ASSERT(mCoalescedInputEvent->inputSource == aEvent.inputSource);
-    MOZ_ASSERT(mCoalescedInputEvent->button == aEvent.button);
-    MOZ_ASSERT(mCoalescedInputEvent->buttons == aEvent.buttons);
+    MOZ_ASSERT(mCoalescedInputEvent->mInputSource == aEvent.mInputSource);
+    MOZ_ASSERT(mCoalescedInputEvent->mButton == aEvent.mButton);
+    MOZ_ASSERT(mCoalescedInputEvent->mButtons == aEvent.mButtons);
     mCoalescedInputEvent->mTimeStamp = aEvent.mTimeStamp;
     mCoalescedInputEvent->mRefPoint = aEvent.mRefPoint;
-    mCoalescedInputEvent->pressure = aEvent.pressure;
+    mCoalescedInputEvent->mPressure = aEvent.mPressure;
     mCoalescedInputEvent->AssignPointerHelperData(aEvent);
   }
 
   if (aEvent.mMessage == eMouseMove &&
       PointerEventHandler::IsPointerEventEnabled()) {
     // PointerEvent::getCoalescedEvents is only applied to pointermove events.
     if (!mCoalescedInputEvent->mCoalescedWidgetEvents) {
       mCoalescedInputEvent->mCoalescedWidgetEvents =
@@ -54,20 +54,20 @@ void CoalescedMouseData::Coalesce(const 
 }
 
 bool CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent,
                                      const ScrollableLayerGuid& aGuid,
                                      const uint64_t& aInputBlockId) {
   MOZ_ASSERT(aEvent.mMessage == eMouseMove);
   return !mCoalescedInputEvent ||
          (mCoalescedInputEvent->mModifiers == aEvent.mModifiers &&
-          mCoalescedInputEvent->inputSource == aEvent.inputSource &&
+          mCoalescedInputEvent->mInputSource == aEvent.mInputSource &&
           mCoalescedInputEvent->pointerId == aEvent.pointerId &&
-          mCoalescedInputEvent->button == aEvent.button &&
-          mCoalescedInputEvent->buttons == aEvent.buttons && mGuid == aGuid &&
+          mCoalescedInputEvent->mButton == aEvent.mButton &&
+          mCoalescedInputEvent->mButtons == aEvent.mButtons && mGuid == aGuid &&
           mInputBlockId == aInputBlockId);
 }
 
 void CoalescedMouseMoveFlusher::WillRefresh(mozilla::TimeStamp aTime) {
   MOZ_ASSERT(mRefreshDriver);
   mTabChild->FlushAllCoalescedMouseData();
   mTabChild->ProcessPendingCoalescedMouseDataAndDispatchEvents();
 }
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1468,17 +1468,17 @@ nsresult nsPluginInstanceOwner::ProcessM
     if (fm) {
       nsCOMPtr<Element> elem = do_QueryReferent(mContent);
       fm->SetFocus(elem, 0);
     }
   }
 
   WidgetMouseEvent* mouseEvent = aMouseEvent->WidgetEventPtr()->AsMouseEvent();
   if (mouseEvent && mouseEvent->mClass == eMouseEventClass) {
-    mLastMouseDownButtonType = mouseEvent->button;
+    mLastMouseDownButtonType = mouseEvent->mButton;
     nsEventStatus rv = ProcessEvent(*mouseEvent);
     if (nsEventStatus_eConsumeNoDefault == rv) {
       aMouseEvent->PreventDefault();  // consume event
       return NS_OK;
     }
   }
 
   return NS_OK;
@@ -1859,28 +1859,28 @@ static NPCocoaEvent TranslateToNPCocoaEv
     cocoaEvent.data.mouse.pluginY = double(ptPx.y);
   }
 
   switch (anEvent->mMessage) {
     case eMouseDown:
     case eMouseUp: {
       WidgetMouseEvent* mouseEvent = anEvent->AsMouseEvent();
       if (mouseEvent) {
-        switch (mouseEvent->button) {
-          case WidgetMouseEvent::eLeftButton:
+        switch (mouseEvent->mButton) {
+          case MouseButton::eLeft:
             cocoaEvent.data.mouse.buttonNumber = 0;
             break;
-          case WidgetMouseEvent::eRightButton:
+          case MouseButton::eRight:
             cocoaEvent.data.mouse.buttonNumber = 1;
             break;
-          case WidgetMouseEvent::eMiddleButton:
+          case MouseButton::eMiddle:
             cocoaEvent.data.mouse.buttonNumber = 2;
             break;
           default:
-            NS_WARNING("Mouse button we don't know about?");
+            NS_WARNING("Mouse mButton we don't know about?");
         }
         cocoaEvent.data.mouse.clickCount = mouseEvent->mClickCount;
       } else {
         NS_WARNING("eMouseUp/DOWN is not a WidgetMouseEvent?");
       }
       break;
     }
     case eLegacyMouseLineOrPageScroll: {
@@ -2028,17 +2028,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
         mInstance->HandleEvent(&textEvent, nullptr);
       }
     }
   }
 
   bool handled = (response == kNPEventHandled || response == kNPEventStartIME);
   bool leftMouseButtonDown =
       (anEvent.mMessage == eMouseDown) &&
-      (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton);
+      (anEvent.AsMouseEvent()->mButton == MouseButton::eLeft);
   if (handled && !(leftMouseButtonDown && !mContentFocused)) {
     rv = nsEventStatus_eConsumeNoDefault;
   }
 #endif
 
 #ifdef XP_WIN
   // this code supports windowless plugins
   const NPEvent* pPluginEvent =
@@ -2060,27 +2060,27 @@ nsEventStatus nsPluginInstanceOwner::Pro
         }
         case eMouseDown: {
           static const int downMsgs[] = {WM_LBUTTONDOWN, WM_MBUTTONDOWN,
                                          WM_RBUTTONDOWN};
           static const int dblClickMsgs[] = {WM_LBUTTONDBLCLK, WM_MBUTTONDBLCLK,
                                              WM_RBUTTONDBLCLK};
           const WidgetMouseEvent* mouseEvent = anEvent.AsMouseEvent();
           if (mouseEvent->mClickCount == 2) {
-            pluginEvent.event = dblClickMsgs[mouseEvent->button];
+            pluginEvent.event = dblClickMsgs[mouseEvent->mButton];
           } else {
-            pluginEvent.event = downMsgs[mouseEvent->button];
+            pluginEvent.event = downMsgs[mouseEvent->mButton];
           }
           break;
         }
         case eMouseUp: {
           static const int upMsgs[] = {WM_LBUTTONUP, WM_MBUTTONUP,
                                        WM_RBUTTONUP};
           const WidgetMouseEvent* mouseEvent = anEvent.AsMouseEvent();
-          pluginEvent.event = upMsgs[mouseEvent->button];
+          pluginEvent.event = upMsgs[mouseEvent->mButton];
           break;
         }
         // For plugins which don't support high-resolution scroll, we should
         // generate legacy resolution wheel messages.  I.e., the delta value
         // should be WHEEL_DELTA * n.
         case eWheel: {
           const WidgetWheelEvent* wheelEvent = anEvent.AsWheelEvent();
           int32_t delta = 0;
@@ -2313,24 +2313,24 @@ nsEventStatus nsPluginInstanceOwner::Pro
               anEvent.mMessage == eMouseDown ? ButtonPress : ButtonRelease;
           event.root = root;
           event.time = anEvent.mTime;
           event.x = pluginPoint.x;
           event.y = pluginPoint.y;
           event.x_root = rootPoint.x;
           event.y_root = rootPoint.y;
           event.state = XInputEventState(mouseEvent);
-          switch (mouseEvent.button) {
-            case WidgetMouseEvent::eMiddleButton:
+          switch (mouseEvent.mButton) {
+            case MouseButton::eMiddle:
               event.button = 2;
               break;
-            case WidgetMouseEvent::eRightButton:
+            case MouseButton::eRight:
               event.button = 3;
               break;
-            default:  // WidgetMouseEvent::eLeftButton;
+            default:  // MouseButton::eLeft;
               event.button = 1;
               break;
           }
           // information lost:
           event.subwindow = X11None;
           event.same_screen = X11True;
         } break;
         default:
--- a/dom/xul/XULPersist.cpp
+++ b/dom/xul/XULPersist.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 #include "XULPersist.h"
-
-#include "nsIXULStore.h"
+#include "mozilla/XULStore.h"
 
 namespace mozilla {
 namespace dom {
 
 static bool ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute) {
   if (aElement->IsXULElement(nsGkAtoms::window)) {
     // This is not an element of the top document, its owner is
     // not an nsXULWindow. Persist it.
@@ -75,110 +74,95 @@ void XULPersist::Persist(Element* aEleme
   if (!mDocument) {
     return;
   }
   // For non-chrome documents, persistance is simply broken
   if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) {
     return;
   }
 
-  if (!mLocalStore) {
-    mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
-    if (NS_WARN_IF(!mLocalStore)) {
-      return;
-    }
-  }
-
   nsAutoString id;
 
   aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
   nsAtomString attrstr(aAttribute);
 
   nsAutoString valuestr;
   aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
 
   nsAutoCString utf8uri;
   nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
   NS_ConvertUTF8toUTF16 uri(utf8uri);
 
   bool hasAttr;
-  rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
+  rv = XULStore::HasValue(uri, id, attrstr, hasAttr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   if (hasAttr && valuestr.IsEmpty()) {
-    mLocalStore->RemoveValue(uri, id, attrstr);
+    rv = XULStore::RemoveValue(uri, id, attrstr);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value removed");
     return;
   }
 
   // Persisting attributes to top level windows is handled by nsXULWindow.
   if (aElement->IsXULElement(nsGkAtoms::window)) {
     if (nsCOMPtr<nsIXULWindow> win =
             mDocument->GetXULWindowIfToplevelChrome()) {
       return;
     }
   }
 
-  mLocalStore->SetValue(uri, id, attrstr, valuestr);
+  rv = XULStore::SetValue(uri, id, attrstr, valuestr);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value set");
 }
 
 nsresult XULPersist::ApplyPersistentAttributes() {
   if (!mDocument) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   // For non-chrome documents, persistance is simply broken
   if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // Add all of the 'persisted' attributes into the content
   // model.
-  if (!mLocalStore) {
-    mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
-    if (NS_WARN_IF(!mLocalStore)) {
-      return NS_ERROR_NOT_INITIALIZED;
-    }
-  }
-
   ApplyPersistentAttributesInternal();
 
   return NS_OK;
 }
 
 nsresult XULPersist::ApplyPersistentAttributesInternal() {
   nsCOMArray<Element> elements;
 
   nsAutoCString utf8uri;
   nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   NS_ConvertUTF8toUTF16 uri(utf8uri);
 
   // Get a list of element IDs for which persisted values are available
-  nsCOMPtr<nsIStringEnumerator> ids;
-  rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
+  UniquePtr<XULStoreIterator> ids;
+  rv = XULStore::GetIDs(uri, ids);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  while (1) {
-    bool hasmore = false;
-    ids->HasMore(&hasmore);
-    if (!hasmore) {
-      break;
+  while (ids->HasMore()) {
+    nsAutoString id;
+    rv = ids->GetNext(&id);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
 
-    nsAutoString id;
-    ids->GetNext(id);
-
     // We want to hold strong refs to the elements while applying
     // persistent attributes, just in case.
     const nsTArray<Element*>* allElements = mDocument->GetAllElementsForId(id);
     if (!allElements) {
       continue;
     }
     elements.Clear();
     elements.SetCapacity(allElements->Length());
@@ -200,34 +184,31 @@ nsresult XULPersist::ApplyPersistentAttr
   nsAutoCString utf8uri;
   nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   NS_ConvertUTF8toUTF16 uri(utf8uri);
 
   // Get a list of attributes for which persisted values are available
-  nsCOMPtr<nsIStringEnumerator> attrs;
-  rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
+  UniquePtr<XULStoreIterator> attrs;
+  rv = XULStore::GetAttrs(uri, aID, attrs);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  while (1) {
-    bool hasmore = PR_FALSE;
-    attrs->HasMore(&hasmore);
-    if (!hasmore) {
-      break;
+  while (attrs->HasMore()) {
+    nsAutoString attrstr;
+    rv = attrs->GetNext(&attrstr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
 
-    nsAutoString attrstr;
-    attrs->GetNext(attrstr);
-
     nsAutoString value;
-    rv = mLocalStore->GetValue(uri, aID, attrstr, value);
+    rv = XULStore::GetValue(uri, aID, attrstr, value);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     RefPtr<nsAtom> attr = NS_Atomize(attrstr);
     if (NS_WARN_IF(!attr)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
--- a/dom/xul/XULPersist.h
+++ b/dom/xul/XULPersist.h
@@ -2,18 +2,16 @@
 /* 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 mozilla_dom_XULPersist_h
 #define mozilla_dom_XULPersist_h
 
-class nsIXULStore;
-
 namespace mozilla {
 namespace dom {
 
 class XULPersist final : public nsStubDocumentObserver {
  public:
   NS_DECL_ISUPPORTS
 
   explicit XULPersist(Document* aDocument);
@@ -28,17 +26,16 @@ class XULPersist final : public nsStubDo
 
  private:
   ~XULPersist();
   nsresult ApplyPersistentAttributes();
   nsresult ApplyPersistentAttributesInternal();
   nsresult ApplyPersistentAttributesToElements(const nsAString& aID,
                                                nsCOMArray<Element>& aElements);
 
-  nsCOMPtr<nsIXULStore> mLocalStore;
   // A weak pointer to our document. Nulled out by DropDocumentReference.
   Document* MOZ_NON_OWNING_REF mDocument;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_XULPersist_h
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1152,17 +1152,17 @@ void nsXULElement::ClickWithInputSource(
       // strong ref to PresContext so events don't destroy it
 
       WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown, nullptr,
                                  WidgetMouseEvent::eReal);
       WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp, nullptr,
                                WidgetMouseEvent::eReal);
       WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr,
                                   WidgetMouseEvent::eReal);
-      eventDown.inputSource = eventUp.inputSource = eventClick.inputSource =
+      eventDown.mInputSource = eventUp.mInputSource = eventClick.mInputSource =
           aInputSource;
 
       // send mouse down
       nsEventStatus status = nsEventStatus_eIgnore;
       EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context,
                                 &eventDown, nullptr, &status);
 
       // send mouse up
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -398,17 +398,17 @@ EditorEventListener::HandleEvent(Event* 
       }
       RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
       return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseUp(mouseEvent);
     }
     // click
     case eMouseClick: {
       WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
       // Don't handle non-primary click events
-      if (widgetMouseEvent->button != WidgetMouseEventBase::eLeftButton) {
+      if (widgetMouseEvent->mButton != MouseButton::eLeft) {
         return NS_OK;
       }
       MOZ_FALLTHROUGH;
     }
     // auxclick
     case eMouseAuxClick: {
       WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
       if (NS_WARN_IF(!widgetMouseEvent)) {
@@ -637,17 +637,17 @@ nsresult EditorEventListener::MouseClick
   //     any non-primary button click event handlers in our UI still keep
   //     listening to "click" events.  Additionally, "auxclick" event is
   //     fired after "click" events and even if we do this in the system event
   //     group, middle click opens new tab before us.  Therefore, we need to
   //     handle middle click at capturing phase of the default group even
   //     though this makes web apps cannot prevent middle click paste with
   //     calling preventDefault() of "click" nor "auxclick".
 
-  if (aMouseClickEvent->button != WidgetMouseEventBase::eMiddleButton ||
+  if (aMouseClickEvent->mButton != MouseButton::eMiddle ||
       !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
     return NS_OK;
   }
 
   RefPtr<PresShell> presShell = GetPresShell();
   if (NS_WARN_IF(!presShell)) {
     return NS_OK;
   }
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1852,17 +1852,17 @@ nsEventStatus APZCTreeManager::ProcessTo
   MOZ_ASSERT(mApzcForInputBlock);
   MOZ_ASSERT(aTouchInput.mTouches.Length() == 1);
 
   // Synthesize a mouse event based on the touch event, so that we can
   // reuse code in InputQueue and APZC for handling scrollbar mouse-drags.
   MouseInput mouseInput{MultiTouchTypeToMouseType(aTouchInput.mType),
                         MouseInput::LEFT_BUTTON,
                         dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH,
-                        WidgetMouseEvent::eLeftButtonFlag,
+                        MouseButtonsFlag::eLeftFlag,
                         aTouchInput.mTouches[0].mScreenPoint,
                         aTouchInput.mTime,
                         aTouchInput.mTimeStamp,
                         aTouchInput.modifiers};
   mouseInput.mHandledByAPZ = true;
 
   // The value of |targetConfirmed| passed to InputQueue::ReceiveInputEvent()
   // only matters for the first event, which creates the drag block. For
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -551,18 +551,18 @@ nsEventStatus APZCCallbackHelper::Dispat
     Modifiers aModifiers, int32_t aClickCount, nsIWidget* aWidget) {
   MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown || aMsg == eMouseUp ||
              aMsg == eMouseLongTap);
 
   WidgetMouseEvent event(true, aMsg, aWidget, WidgetMouseEvent::eReal,
                          WidgetMouseEvent::eNormal);
   event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
   event.mTime = aTime;
-  event.button = WidgetMouseEvent::eLeftButton;
-  event.inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+  event.mButton = MouseButton::eLeft;
+  event.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH;
   if (aMsg == eMouseLongTap) {
     event.mFlags.mOnlyChromeDispatch = true;
   }
   event.mIgnoreRootScrollFrame = true;
   if (aMsg != eMouseMove) {
     event.mClickCount = aClickCount;
   }
   event.mModifiers = aModifiers;
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -915,21 +915,32 @@ bool WebRenderBridgeParent::SetDisplayLi
   if (aValidTransaction) {
     if (IsRootWebRenderBridgeParent()) {
       if (aRenderRoot != wr::RenderRoot::Default) {
         MutexAutoLock lock(mRenderRootRectMutex);
         mRenderRootRects[aRenderRoot] = ViewAs<ScreenPixel>(
             aRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
       }
       LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
-      LayoutDeviceIntRect rect = RoundedToInt(aRect);
-      rect.SetWidth(
-          std::max(0, std::min(widgetSize.width - rect.X(), rect.Width())));
-      rect.SetHeight(
-          std::max(0, std::min(widgetSize.height - rect.Y(), rect.Height())));
+      LayoutDeviceIntRect rect;
+      if (gfxPrefs::WebRenderSplitRenderRoots()) {
+        rect = RoundedToInt(aRect);
+        rect.SetWidth(
+            std::max(0, std::min(widgetSize.width - rect.X(), rect.Width())));
+        rect.SetHeight(
+            std::max(0, std::min(widgetSize.height - rect.Y(), rect.Height())));
+      } else {
+        // XXX: If we can't have multiple documents, just use the
+        // pre-document- splitting behavior of directly applying the client
+        // size. This is a speculative and temporary attempt to address bug
+        // 1538540, as an incorrect rect supplied to SetDocumentView can cause
+        // us to not build a frame and potentially render with stale texture
+        // cache items.
+        rect = LayoutDeviceIntRect(LayoutDeviceIntPoint(), widgetSize);
+      }
       aTxn.SetDocumentView(rect, widgetSize);
     }
     gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
     aTxn.SetDisplayList(clearColor, aWrEpoch,
                         wr::ToLayoutSize(RoundedToInt(aRect).Size()),
                         mPipelineId, aContentSize, aDLDesc, dlData);
 
     if (aObserveLayersUpdate) {
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -423,31 +423,31 @@ nsEventStatus AccessibleCaretEventHub::H
 
   return status;
 }
 
 nsEventStatus AccessibleCaretEventHub::HandleMouseEvent(
     WidgetMouseEvent* aEvent) {
   nsEventStatus rv = nsEventStatus_eIgnore;
 
-  if (aEvent->button != WidgetMouseEvent::eLeftButton) {
+  if (aEvent->mButton != MouseButton::eLeft) {
     return rv;
   }
 
   int32_t id =
       (mActiveTouchId == kInvalidTouchId ? kDefaultTouchId : mActiveTouchId);
   nsPoint point = GetMouseEventPosition(aEvent);
 
   if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp ||
       aEvent->mMessage == eMouseClick ||
       aEvent->mMessage == eMouseDoubleClick ||
       aEvent->mMessage == eMouseLongTap) {
     // Don't reset the source on mouse movement since that can
     // happen anytime, even randomly during a touch sequence.
-    mManager->SetLastInputSource(aEvent->inputSource);
+    mManager->SetLastInputSource(aEvent->mInputSource);
   }
 
   switch (aEvent->mMessage) {
     case eMouseDown:
       AC_LOGV("Before eMouseDown, state: %s", mState->Name());
       rv = mState->OnPress(this, point, id, eMouseEventClass);
       AC_LOGV("After eMouseDown, state: %s, consume: %d", mState->Name(), rv);
       break;
--- a/layout/base/PositionedEventTargeting.cpp
+++ b/layout/base/PositionedEventTargeting.cpp
@@ -562,31 +562,31 @@ nsIFrame* FindFrameTargetedByInputEvent(
     PET_LOG("Retargeting disabled\n");
     return target;
   }
   nsIContent* clickableAncestor = nullptr;
   if (target) {
     clickableAncestor = GetClickableAncestor(target, nsGkAtoms::body);
     if (clickableAncestor) {
       if (!IsElementClickableAndReadable(target, aEvent, prefs)) {
-        aEvent->AsMouseEventBase()->hitCluster = true;
+        aEvent->AsMouseEventBase()->mHitCluster = true;
       }
       PET_LOG("Target %p is clickable\n", target);
       // If the target that was directly hit has a clickable ancestor, that
       // means it too is clickable. And since it is the same as or a descendant
       // of clickableAncestor, it should become the root for the GetClosest
       // search.
       clickableAncestor = target->GetContent();
     }
   }
 
   // Do not modify targeting for actual mouse hardware; only for mouse
   // events generated by touch-screen hardware.
   if (aEvent->mClass == eMouseEventClass && prefs->mTouchOnly &&
-      aEvent->AsMouseEvent()->inputSource !=
+      aEvent->AsMouseEvent()->mInputSource !=
           MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
     PET_LOG("Mouse input event is not from a touch source\n");
     return target;
   }
 
   // If the exact target is non-null, only consider candidate targets in the
   // same document as the exact target. Otherwise, if an ancestor document has
   // a mouse event handler for example, targets that are !GetClickableAncestor
@@ -611,17 +611,17 @@ nsIFrame* FindFrameTargetedByInputEvent(
   nsIFrame* closestClickable = GetClosest(
       aRootFrame, aPointRelativeToRootFrame, targetRect, prefs,
       restrictToDescendants, clickableAncestor, candidates, &elementsInCluster);
   if (closestClickable) {
     if ((prefs->mTouchClusterDetectionEnabled && elementsInCluster > 1) ||
         (!IsElementClickableAndReadable(closestClickable, aEvent, prefs))) {
       if (aEvent->mClass == eMouseEventClass) {
         WidgetMouseEventBase* mouseEventBase = aEvent->AsMouseEventBase();
-        mouseEventBase->hitCluster = true;
+        mouseEventBase->mHitCluster = true;
       }
     }
     target = closestClickable;
   }
   PET_LOG("Final target is %p\n", target);
 
   // Uncomment this to dump the frame tree to help with debugging.
   // Note that dumping the frame tree at the top of the function may flood
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1979,17 +1979,17 @@ RestyleManager::AnimationsWithDestroyedF
   mRestyleManager->mAnimationsWithDestroyedFrame = this;
 }
 
 void RestyleManager::AnimationsWithDestroyedFrame ::
     StopAnimationsForElementsWithoutFrames() {
   StopAnimationsWithoutFrame(mContents, PseudoStyleType::NotPseudo);
   StopAnimationsWithoutFrame(mBeforeContents, PseudoStyleType::before);
   StopAnimationsWithoutFrame(mAfterContents, PseudoStyleType::after);
-  StopAnimationsWithoutFrame(mAfterContents, PseudoStyleType::marker);
+  StopAnimationsWithoutFrame(mMarkerContents, PseudoStyleType::marker);
 }
 
 void RestyleManager::AnimationsWithDestroyedFrame ::StopAnimationsWithoutFrame(
     nsTArray<RefPtr<nsIContent>>& aArray, PseudoStyleType aPseudoType) {
   nsAnimationManager* animationManager =
       mRestyleManager->PresContext()->AnimationManager();
   nsTransitionManager* transitionManager =
       mRestyleManager->PresContext()->TransitionManager();
--- a/layout/base/gtest/TestAccessibleCaretEventHub.cpp
+++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
@@ -102,17 +102,17 @@ class AccessibleCaretEventHubTester : pu
     mHub.get()->Release();
   }
 
   static UniquePtr<WidgetEvent> CreateMouseEvent(EventMessage aMessage,
                                                  nscoord aX, nscoord aY) {
     auto event = MakeUnique<WidgetMouseEvent>(true, aMessage, nullptr,
                                               WidgetMouseEvent::eReal);
 
-    event->button = WidgetMouseEvent::eLeftButton;
+    event->mButton = MouseButton::eLeft;
     event->mRefPoint = LayoutDeviceIntPoint(aX, aY);
 
     return std::move(event);
   }
 
   static UniquePtr<WidgetEvent> CreateMousePressEvent(nscoord aX, nscoord aY) {
     return CreateMouseEvent(eMouseDown, aX, aY);
   }
--- a/layout/forms/nsImageControlFrame.cpp
+++ b/layout/forms/nsImageControlFrame.cpp
@@ -133,17 +133,17 @@ nsresult nsImageControlFrame::HandleEven
 
   if (IsContentDisabled()) {
     return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   }
 
   *aEventStatus = nsEventStatus_eIgnore;
 
   if (aEvent->mMessage == eMouseUp &&
-      aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+      aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
     // Store click point for HTMLInputElement::SubmitNamesValues
     // Do this on MouseUp because the specs don't say and that's what IE does
     nsIntPoint* lastClickPoint = static_cast<nsIntPoint*>(
         mContent->GetProperty(nsGkAtoms::imageClickedPoint));
     if (lastClickPoint) {
       // normally lastClickedPoint is not null, as it's allocated in Init()
       nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
       TranslateEventCoords(pt, *lastClickPoint);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4020,17 +4020,17 @@ nsresult nsFrame::HandleEvent(nsPresCont
                               nsEventStatus* aEventStatus) {
   if (aEvent->mMessage == eMouseMove) {
     // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
     //     the implementation becomes simpler.
     return HandleDrag(aPresContext, aEvent, aEventStatus);
   }
 
   if ((aEvent->mClass == eMouseEventClass &&
-       aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
+       aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) ||
       aEvent->mClass == eTouchEventClass) {
     if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
       HandlePress(aPresContext, aEvent, aEventStatus);
     } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
       HandleRelease(aPresContext, aEvent, aEventStatus);
     }
   }
   return NS_OK;
@@ -4057,17 +4057,17 @@ nsresult nsFrame::GetDataForTableSelecti
   //  continue selecting with mouse drag or end on mouse up,
   //  or when using shift key to extend block of cells
   //  (Mouse down does normal selection unless Ctrl/Cmd is pressed)
   bool doTableSelection =
       displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
       selectingTableCells &&
       (aMouseEvent->mMessage == eMouseMove ||
        (aMouseEvent->mMessage == eMouseUp &&
-        aMouseEvent->button == WidgetMouseEvent::eLeftButton) ||
+        aMouseEvent->mButton == MouseButton::eLeft) ||
        aMouseEvent->IsShift());
 
   if (!doTableSelection) {
     // In Browser, special 'table selection' key must be pressed for table
     // selection or when just Shift is pressed and we're already in table/cell
     // selection mode
 #ifdef XP_MACOSX
     doTableSelection = aMouseEvent->IsMeta() ||
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -613,17 +613,17 @@ nsresult nsHTMLFramesetFrame::HandleEven
   NS_ENSURE_ARG_POINTER(aEventStatus);
   if (mDragger) {
     // the nsFramesetBorderFrame has captured NS_MOUSE_DOWN
     switch (aEvent->mMessage) {
       case eMouseMove:
         MouseDrag(aPresContext, aEvent);
         break;
       case eMouseUp:
-        if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+        if (aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
           EndMouseDrag(aPresContext);
         }
         break;
       default:
         break;
     }
     *aEventStatus = nsEventStatus_eConsumeNoDefault;
   } else {
@@ -1448,17 +1448,17 @@ nsresult nsHTMLFramesetBorderFrame::Hand
   *aEventStatus = nsEventStatus_eIgnore;
 
   // XXX Mouse setting logic removed.  The remaining logic should also move.
   if (!mCanResize) {
     return NS_OK;
   }
 
   if (aEvent->mMessage == eMouseDown &&
-      aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+      aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
     nsHTMLFramesetFrame* parentFrame = do_QueryFrame(GetParent());
     if (parentFrame) {
       parentFrame->StartMouseDrag(aPresContext, this, aEvent);
       *aEventStatus = nsEventStatus_eConsumeNoDefault;
     }
   }
   return NS_OK;
 }
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -2230,17 +2230,17 @@ nsresult nsImageFrame::GetContentForEven
 
 // XXX what should clicks on transparent pixels do?
 nsresult nsImageFrame::HandleEvent(nsPresContext* aPresContext,
                                    WidgetGUIEvent* aEvent,
                                    nsEventStatus* aEventStatus) {
   NS_ENSURE_ARG_POINTER(aEventStatus);
 
   if ((aEvent->mMessage == eMouseClick &&
-       aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
+       aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) ||
       aEvent->mMessage == eMouseMove) {
     nsImageMap* map = GetImageMap();
     bool isServerMap = IsServerImageMap();
     if (map || isServerMap) {
       nsIntPoint p;
       TranslateEventCoords(
           nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
       bool inside = false;
--- a/layout/xul/nsButtonBoxFrame.cpp
+++ b/layout/xul/nsButtonBoxFrame.cpp
@@ -200,11 +200,11 @@ void nsButtonBoxFrame::MouseClicked(Widg
 
   // Execute the oncommand event handler.
   WidgetInputEvent* inputEvent = aEvent->AsInputEvent();
   WidgetMouseEventBase* mouseEvent = aEvent->AsMouseEventBase();
   nsContentUtils::DispatchXULCommand(
       mContent, aEvent->IsTrusted(), nullptr, presShell,
       inputEvent->IsControl(), inputEvent->IsAlt(), inputEvent->IsShift(),
       inputEvent->IsMeta(),
-      mouseEvent ? mouseEvent->inputSource
+      mouseEvent ? mouseEvent->mInputSource
                  : MouseEvent_Binding::MOZ_SOURCE_UNKNOWN);
 }
--- a/layout/xul/nsMenuFrame.cpp
+++ b/layout/xul/nsMenuFrame.cpp
@@ -381,34 +381,34 @@ nsresult nsMenuFrame::HandleEvent(nsPres
     // On other platforms, toggle menulist on unmodified F4 or Alt arrow
     if ((keyCode == NS_VK_F4 && !keyEvent->IsAlt()) ||
         ((keyCode == NS_VK_UP || keyCode == NS_VK_DOWN) && keyEvent->IsAlt())) {
       *aEventStatus = nsEventStatus_eConsumeNoDefault;
       ToggleMenuState();
     }
 #endif
   } else if (aEvent->mMessage == eMouseDown &&
-             aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
+             aEvent->AsMouseEvent()->mButton == MouseButton::eLeft &&
              !IsDisabled() && IsMenu()) {
     // The menu item was selected. Bring up the menu.
     // We have children.
     // Don't prevent the default action here, since that will also cancel
     // potential drag starts.
     if (!menuParent || menuParent->IsMenuBar()) {
       ToggleMenuState();
     } else {
       if (!IsOpen()) {
         menuParent->ChangeMenuItem(this, false, false);
         OpenMenu(false);
       }
     }
   } else if (
 #ifndef NSCONTEXTMENUISMOUSEUP
       (aEvent->mMessage == eMouseUp &&
-       aEvent->AsMouseEvent()->button == WidgetMouseEvent::eRightButton) &&
+       aEvent->AsMouseEvent()->mButton == MouseButton::eRight) &&
 #else
       aEvent->mMessage == eContextMenu &&
 #endif
       onmenu && !IsMenu() && !IsDisabled()) {
     // if this menu is a context menu it accepts right-clicks...fire away!
     // Make sure we cancel default processing of the context menu event so
     // that it doesn't bubble and get seen again by the popuplistener and show
     // another context menu.
@@ -418,17 +418,17 @@ nsresult nsMenuFrame::HandleEvent(nsPres
     // on others we get it on a mouse down. For the ones where we get it on a
     // mouse down, we must continue listening for the right button up event to
     // dismiss the menu.
     if (menuParent->IsContextMenu()) {
       *aEventStatus = nsEventStatus_eConsumeNoDefault;
       Execute(aEvent);
     }
   } else if (aEvent->mMessage == eMouseUp &&
-             aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
+             aEvent->AsMouseEvent()->mButton == MouseButton::eLeft &&
              !IsMenu() && !IsDisabled()) {
     // Execute the execute event handler.
     *aEventStatus = nsEventStatus_eConsumeNoDefault;
     Execute(aEvent);
   } else if (aEvent->mMessage == eMouseOut) {
     // Kill our timer if one is active.
     if (mOpenTimer) {
       mOpenTimer->Cancel();
--- a/layout/xul/nsResizerFrame.cpp
+++ b/layout/xul/nsResizerFrame.cpp
@@ -59,17 +59,17 @@ nsresult nsResizerFrame::HandleEvent(nsP
   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)) {
+           aEvent->AsMouseEvent()->mButton == MouseButton::eLeft)) {
         nsCOMPtr<nsIBaseWindow> window;
         mozilla::PresShell* presShell = aPresContext->GetPresShell();
         nsIContent* contentToResize =
             GetContentToResize(presShell, getter_AddRefs(window));
         if (contentToResize) {
           nsIFrame* frameToResize = contentToResize->GetPrimaryFrame();
           if (!frameToResize) break;
 
@@ -116,17 +116,17 @@ nsresult nsResizerFrame::HandleEvent(nsP
         nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
       }
     } break;
 
     case eTouchEnd:
     case eMouseUp: {
       if (aEvent->mClass == eTouchEventClass ||
           (aEvent->mClass == eMouseEventClass &&
-           aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton)) {
+           aEvent->AsMouseEvent()->mButton == MouseButton::eLeft)) {
         // we're done tracking.
         mTrackingMouseMove = false;
 
         nsIPresShell::SetCapturingContent(nullptr, 0);
 
         doDefault = false;
       }
     } break;
@@ -286,17 +286,17 @@ nsresult nsResizerFrame::HandleEvent(nsP
     case eMouseClick: {
       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
       if (mouseEvent->IsLeftClickEvent()) {
         MouseClicked(mouseEvent);
       }
       break;
     }
     case eMouseDoubleClick:
-      if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+      if (aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
         nsCOMPtr<nsIBaseWindow> window;
         mozilla::PresShell* presShell = aPresContext->GetPresShell();
         nsIContent* contentToResize =
             GetContentToResize(presShell, getter_AddRefs(window));
         if (contentToResize) {
           nsMenuPopupFrame* menuPopupFrame =
               do_QueryFrame(contentToResize->GetPrimaryFrame());
           if (menuPopupFrame)
@@ -524,10 +524,10 @@ nsResizerFrame::Direction nsResizerFrame
 
   return directions[index];
 }
 
 void nsResizerFrame::MouseClicked(WidgetMouseEvent* aEvent) {
   // Execute the oncommand event handler.
   nsContentUtils::DispatchXULCommand(
       mContent, false, nullptr, nullptr, aEvent->IsControl(), aEvent->IsAlt(),
-      aEvent->IsShift(), aEvent->IsMeta(), aEvent->inputSource);
+      aEvent->IsShift(), aEvent->IsMeta(), aEvent->mInputSource);
 }
--- a/layout/xul/nsScrollbarButtonFrame.cpp
+++ b/layout/xul/nsScrollbarButtonFrame.cpp
@@ -81,22 +81,22 @@ nsresult nsScrollbarButtonFrame::HandleE
   return nsButtonBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 }
 
 bool nsScrollbarButtonFrame::HandleButtonPress(nsPresContext* aPresContext,
                                                WidgetGUIEvent* aEvent,
                                                nsEventStatus* aEventStatus) {
   // Get the desired action for the scrollbar button.
   LookAndFeel::IntID tmpAction;
-  uint16_t button = aEvent->AsMouseEvent()->button;
-  if (button == WidgetMouseEvent::eLeftButton) {
+  uint16_t button = aEvent->AsMouseEvent()->mButton;
+  if (button == MouseButton::eLeft) {
     tmpAction = LookAndFeel::eIntID_ScrollButtonLeftMouseButtonAction;
-  } else if (button == WidgetMouseEvent::eMiddleButton) {
+  } else if (button == MouseButton::eMiddle) {
     tmpAction = LookAndFeel::eIntID_ScrollButtonMiddleMouseButtonAction;
-  } else if (button == WidgetMouseEvent::eRightButton) {
+  } else if (button == MouseButton::eRight) {
     tmpAction = LookAndFeel::eIntID_ScrollButtonRightMouseButtonAction;
   } else {
     return false;
   }
 
   // Get the button action metric from the pres. shell.
   int32_t pressedButtonAction;
   if (NS_FAILED(LookAndFeel::GetInt(tmpAction, &pressedButtonAction))) {
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -611,17 +611,17 @@ nsresult nsSliderFrame::HandleEvent(nsPr
       mThumbStart = thumbFrame->GetPosition().x;
     else
       mThumbStart = thumbFrame->GetPosition().y;
 
     mDragStart = pos - mThumbStart;
   }
 #ifdef MOZ_WIDGET_GTK
   else if (ShouldScrollForEvent(aEvent) && aEvent->mClass == eMouseEventClass &&
-           aEvent->AsMouseEvent()->button == WidgetMouseEvent::eRightButton) {
+           aEvent->AsMouseEvent()->mButton == MouseButton::eRight) {
     // HandlePress and HandleRelease are usually called via
     // nsFrame::HandleEvent, but only for the left mouse button.
     if (aEvent->mMessage == eMouseDown) {
       HandlePress(aPresContext, aEvent, aEventStatus);
     } else if (aEvent->mMessage == eMouseUp) {
       HandleRelease(aPresContext, aEvent, aEventStatus);
     }
 
@@ -1178,25 +1178,25 @@ void nsSliderFrame::RemoveListener() {
 
 bool nsSliderFrame::ShouldScrollForEvent(WidgetGUIEvent* aEvent) {
   switch (aEvent->mMessage) {
     case eTouchStart:
     case eTouchEnd:
       return true;
     case eMouseDown:
     case eMouseUp: {
-      uint16_t button = aEvent->AsMouseEvent()->button;
+      uint16_t button = aEvent->AsMouseEvent()->mButton;
 #ifdef MOZ_WIDGET_GTK
-      return (button == WidgetMouseEvent::eLeftButton) ||
-             (button == WidgetMouseEvent::eRightButton && GetScrollToClick()) ||
-             (button == WidgetMouseEvent::eMiddleButton && gMiddlePref &&
+      return (button == MouseButton::eLeft) ||
+             (button == MouseButton::eRight && GetScrollToClick()) ||
+             (button == MouseButton::eMiddle && gMiddlePref &&
               !GetScrollToClick());
 #else
-      return (button == WidgetMouseEvent::eLeftButton) ||
-             (button == WidgetMouseEvent::eMiddleButton && gMiddlePref);
+      return (button == MouseButton::eLeft) ||
+             (button == MouseButton::eMiddle && gMiddlePref);
 #endif
     }
     default:
       return false;
   }
 }
 
 bool nsSliderFrame::ShouldScrollToClickForEvent(WidgetGUIEvent* aEvent) {
@@ -1216,27 +1216,27 @@ bool nsSliderFrame::ShouldScrollToClickF
   }
 #endif
 
   if (aEvent->mMessage == eTouchStart) {
     return GetScrollToClick();
   }
 
   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
-  if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
+  if (mouseEvent->mButton == MouseButton::eLeft) {
 #ifdef XP_MACOSX
     bool invertPref = mouseEvent->IsAlt();
 #else
     bool invertPref = mouseEvent->IsShift();
 #endif
     return GetScrollToClick() != invertPref;
   }
 
 #ifdef MOZ_WIDGET_GTK
-  if (mouseEvent->button == WidgetMouseEvent::eRightButton) {
+  if (mouseEvent->mButton == MouseButton::eRight) {
     return !GetScrollToClick();
   }
 #endif
 
   return true;
 }
 
 bool nsSliderFrame::IsEventOverThumb(WidgetGUIEvent* aEvent) {
--- a/layout/xul/nsSplitterFrame.cpp
+++ b/layout/xul/nsSplitterFrame.cpp
@@ -329,17 +329,17 @@ nsresult nsSplitterFrame::HandleEvent(ns
   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) {
+      if (aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
         inner->MouseUp(aPresContext, aEvent);
       }
       break;
 
     default:
       break;
   }
 
--- a/layout/xul/nsTitleBarFrame.cpp
+++ b/layout/xul/nsTitleBarFrame.cpp
@@ -59,17 +59,17 @@ nsresult nsTitleBarFrame::HandleEvent(ns
   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
 
   bool doDefault = true;
 
   switch (aEvent->mMessage) {
     case eMouseDown: {
-      if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+      if (aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
         // titlebar has no effect in non-chrome shells
         nsCOMPtr<nsIDocShellTreeItem> dsti = aPresContext->GetDocShell();
         if (dsti) {
           if (dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
             // we're tracking.
             mTrackingMouseMove = true;
 
             // start capture.
@@ -83,17 +83,17 @@ nsresult nsTitleBarFrame::HandleEvent(ns
 
         *aEventStatus = nsEventStatus_eConsumeNoDefault;
         doDefault = false;
       }
     } break;
 
     case eMouseUp: {
       if (mTrackingMouseMove &&
-          aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
+          aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) {
         // we're done tracking.
         mTrackingMouseMove = false;
 
         // end capture
         nsIPresShell::SetCapturingContent(nullptr, 0);
 
         *aEventStatus = nsEventStatus_eConsumeNoDefault;
         doDefault = false;
@@ -153,10 +153,10 @@ nsresult nsTitleBarFrame::HandleEvent(ns
   else
     return NS_OK;
 }
 
 void nsTitleBarFrame::MouseClicked(WidgetMouseEvent* aEvent) {
   // Execute the oncommand event handler.
   nsContentUtils::DispatchXULCommand(
       mContent, false, nullptr, nullptr, aEvent->IsControl(), aEvent->IsAlt(),
-      aEvent->IsShift(), aEvent->IsMeta(), aEvent->inputSource);
+      aEvent->IsShift(), aEvent->IsMeta(), aEvent->mInputSource);
 }
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -1316,17 +1316,17 @@ void nsXULPopupManager::FirePopupShowing
   } else {
     event.mWidget = nullptr;
   }
 
   if (aTriggerEvent) {
     WidgetMouseEventBase* mouseEvent =
         aTriggerEvent->WidgetEventPtr()->AsMouseEventBase();
     if (mouseEvent) {
-      event.inputSource = mouseEvent->inputSource;
+      event.mInputSource = mouseEvent->mInputSource;
     }
   }
 
   event.mRefPoint = mCachedMousePoint;
   event.mModifiers = mCachedModifiers;
   EventDispatcher::Dispatch(popup, presContext, &event, nullptr, &status);
 
   mCachedMousePoint = LayoutDeviceIntPoint(0, 0);
--- a/testing/talos/talos/xtalos/xperf_whitelist.json
+++ b/testing/talos/talos/xtalos/xperf_whitelist.json
@@ -561,21 +561,21 @@
     "maxbytes": 512
   },
   "{profile}\\user.js": {
     "mincount": 4,
     "maxcount": 4,
     "minbytes": 6000,
     "maxbytes": 6000
   },
-  "{profile}\\xulstore.json": {
-    "mincount": 0,
-    "maxcount": 0,
-    "minbytes": 0,
-    "maxbytes": 702
-  },
   "{talos}\\talos\\tests\\{tp5n_files}": {
     "mincount": 0,
     "maxcount": 2,
     "minbytes": 0,
     "maxbytes": 16384
+  },
+  "{profile}\\xulstore\\data.mdb": {
+    "mincount": 0,
+    "maxcount": 4,
+    "minbytes": 0,
+    "maxbytes": 608
   }
 }
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-transitions/hidden-container-001.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[hidden-container-001.html]
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1472172
--- a/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/Document-getAnimations.tentative.html
@@ -13,22 +13,16 @@
   to { top: 100px }
 }
 @keyframes animBottom {
   to { bottom: 100px }
 }
 @keyframes animRight {
   to { right: 100px }
 }
-::before {
-  content: ''
-}
-::after {
-  content: ''
-}
 </style>
 <div id="log"></div>
 <script>
 'use strict';
 
 test(t => {
   assert_equals(document.getAnimations().length, 0,
     'getAnimations returns an empty sequence for a document'
@@ -246,39 +240,82 @@ test(t => {
   anim.cancel();
   anim.play();
   assert_equals(document.getAnimations().length, 1,
                 'CSS animations canceled and restarted by the API are ' +
                 'returned');
 }, 'CSS Animations canceled and restarted via the API are returned');
 
 test(t => {
-  addStyle(t, { '#parent::after': 'animation: animLeft 10s;',
-                '#parent::before': 'animation: animRight 10s;' });
-  // create two divs with these arrangement:
+  // Create two divs with the following arrangement:
+  //
   //       parent
+  //    (::marker,)
   //     ::before,
   //     ::after
   //        |
   //       child
-  const parent = addDiv(t, { 'id': 'parent' });
+
+  addStyle(t, {
+    '#parent::after': "content: ''; animation: animLeft 100s;",
+    '#parent::before': "content: ''; animation: animRight 100s;",
+  });
+
+  const supportsMarkerPseudos = CSS.supports('selector(::marker)');
+  if (supportsMarkerPseudos) {
+    addStyle(t, {
+      '#parent': 'display: list-item;',
+      '#parent::marker': "content: ''; animation: animLeft 100s;",
+    });
+  }
+
+  const parent = addDiv(t, { id: 'parent' });
   const child = addDiv(t);
   parent.appendChild(child);
   for (const div of [parent, child]) {
-    div.setAttribute('style', 'animation: animBottom 10s');
+    div.setAttribute('style', 'animation: animBottom 100s');
+  }
+
+  const expectedAnimations = [
+    [parent, undefined],
+    [parent, '::marker'],
+    [parent, '::before'],
+    [parent, '::after'],
+    [child, undefined],
+  ];
+  if (!supportsMarkerPseudos) {
+    expectedAnimations.splice(1, 1);
   }
 
-  const anims = document.getAnimations();
-  assert_equals(anims.length, 4,
-                'CSS animations on both pseudo-elements and elements ' +
-                'are returned');
-  assert_equals(anims[0].effect.target, parent,
-                'The animation targeting the parent element comes first');
-  assert_equals(anims[1].effect.target.type, '::before',
-                'The animation targeting the ::before element comes second');
-  assert_equals(anims[2].effect.target.type, '::after',
-                'The animation targeting the ::after element comes third');
-  assert_equals(anims[3].effect.target, child,
-                'The animation targeting the child element comes last');
-}, 'CSS Animations targetting (pseudo-)elements should have correct order ' +
-   'after sorting');
+  const animations = document.getAnimations();
+  assert_equals(
+    animations.length,
+    expectedAnimations.length,
+    'CSS animations on both pseudo-elements and elements are returned'
+  );
+
+  for (const [index, expected] of expectedAnimations.entries()) {
+    const [element, pseudo] = expected;
+    const actual = animations[index];
+
+    if (pseudo) {
+      assert_equals(
+        actual.effect.target.element,
+        element,
+        `Animation #${index + 1} has expected target`
+      );
+      assert_equals(
+        actual.effect.target.type,
+        pseudo,
+        `Animation #${index + 1} has expected pseudo type`
+      );
+    } else {
+      assert_equals(
+        actual.effect.target,
+        element,
+        `Animation #${index + 1} has expected target`
+      );
+    }
+  }
+}, 'CSS Animations targetting (pseudo-)elements should have correct order '
+   + 'after sorting');
 
 </script>
--- a/testing/web-platform/tests/css/css-animations/event-order.tentative.html
+++ b/testing/web-platform/tests/css/css-animations/event-order.tentative.html
@@ -1,62 +1,97 @@
 <!doctype html>
 <meta charset=utf-8>
 <title>Tests for CSS animation event order</title>
 <link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="support/testcommon.js"></script>
 <style>
-  @keyframes anim {
-    from { margin-left: 0px; }
-    to { margin-left: 100px; }
-  }
+@keyframes anim {
+  from { margin-left: 0px; }
+  to { margin-left: 100px; }
+}
+@keyframes color-anim {
+  from { color: red; }
+  to { color: green; }
+}
 </style>
 <div id="log"></div>
 <script type='text/javascript'>
 'use strict';
 
 /**
  * Asserts that the set of actual and received events match.
  * @param actualEvents   An array of the received AnimationEvent objects.
  * @param expectedEvents A series of array objects representing the expected
  *        events, each having the form:
- *          [ event type, target element, elapsed time ]
+ *          [ event type, target element, [pseudo type], elapsed time ]
  */
 const checkEvents = (actualEvents, ...expectedEvents) => {
-  assert_equals(actualEvents.length, expectedEvents.length,
-                `Number of actual events (${actualEvents.length}: \
-${actualEvents.map(event => event.type).join(', ')}) should match expected \
-events (${expectedEvents.map(event => event.type).join(', ')})`);
+  const actualTypeSummary = actualEvents.map(event => event.type).join(', ');
+  const expectedTypeSummary = expectedEvents.map(event => event[0]).join(', ');
+
+  assert_equals(
+    actualEvents.length,
+    expectedEvents.length,
+    `Number of events received (${actualEvents.length}) \
+should match expected number (${expectedEvents.length}) \
+(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})`
+  );
+
+  for (const [index, actualEvent] of actualEvents.entries()) {
+    const expectedEvent = expectedEvents[index];
+    const [type, target] = expectedEvent;
+    const pseudoElement = expectedEvent.length === 4 ? expectedEvent[2] : '';
+    const elapsedTime = expectedEvent[expectedEvent.length - 1];
 
-  actualEvents.forEach((actualEvent, i) => {
-    assert_equals(expectedEvents[i][0], actualEvent.type,
-                  'Event type should match');
-    assert_equals(expectedEvents[i][1], actualEvent.target,
-                  'Event target should match');
-    assert_equals(expectedEvents[i][2], actualEvent.elapsedTime,
-                  'Event\'s elapsed time should match');
-  });
+    assert_equals(
+      actualEvent.type,
+      type,
+      `Event #${index + 1} types should match \
+(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})`
+    );
+    assert_equals(
+      actualEvent.target,
+      target,
+      `Event #${index + 1} targets should match`
+    );
+    assert_equals(
+      actualEvent.pseudoElement,
+      pseudoElement,
+      `Event #${index + 1} pseudoElements should match`
+    );
+    assert_equals(
+      actualEvent.elapsedTime,
+      elapsedTime,
+      `Event #${index + 1} elapsedTimes should match`
+    );
+  }
 };
 
 const setupAnimation = (t, animationStyle, receiveEvents) => {
-  const div = addDiv(t, { style: "animation: " + animationStyle });
+  const div = addDiv(t, { style: 'animation: ' + animationStyle });
 
   for (const name of ['start', 'iteration', 'end']) {
     div['onanimation' + name] = evt => {
-    receiveEvents.push({ type:        evt.type,
-                         target:      evt.target,
-                         elapsedTime: evt.elapsedTime });
+      receiveEvents.push({
+        type: evt.type,
+        target: evt.target,
+        pseudoElement: evt.pseudoElement,
+        elapsedTime: evt.elapsedTime,
+      });
     };
   }
 
-  const watcher = new EventWatcher(t, div, [ 'animationstart',
-                                             'animationiteration',
-                                             'animationend' ]);
+  const watcher = new EventWatcher(t, div, [
+    'animationstart',
+    'animationiteration',
+    'animationend',
+  ]);
 
   const animation = div.getAnimations()[0];
 
   return [animation, watcher, div];
 };
 
 promise_test(async t => {
   let events = [];
@@ -87,17 +122,74 @@ promise_test(async t => {
   animation1.finish();
   animation2.finish();
 
   await Promise.all([ watcher1.wait_for('animationend'),
                       watcher2.wait_for('animationend') ]);
 
   checkEvents(events, ['animationend', div1, 200],
                       ['animationend', div2, 200]);
-}, 'Test same events are ordered by elements.');
+}, 'Same events are ordered by elements');
+
+promise_test(async t => {
+  // Setup a hierarchy as follows:
+  //
+  //              parent
+  //                |
+  //  (::marker, ::before, ::after)
+  //                |
+  //              child
+  const parentDiv = addDiv(t, { style: 'animation: anim 100s' });
+
+  parentDiv.id = 'parent-div';
+  addStyle(t, {
+    '#parent-div::after': "content: ''; animation: anim 100s",
+    '#parent-div::before': "content: ''; animation: anim 100s",
+  });
+
+  if (CSS.supports('selector(::marker)')) {
+    parentDiv.style.display = 'list-item';
+    addStyle(t, {
+      '#parent-div::marker': "content: ''; animation: color-anim 100s",
+    });
+  }
+
+  const childDiv = addDiv(t, { style: 'animation: anim 100s' });
+  parentDiv.append(childDiv);
+
+  // Setup event handlers
+  let events = [];
+  for (const name of ['start', 'iteration', 'end', 'cancel']) {
+    parentDiv['onanimation' + name] = evt => {
+      events.push({
+        type: evt.type,
+        target: evt.target,
+        pseudoElement: evt.pseudoElement,
+        elapsedTime: evt.elapsedTime,
+      });
+    };
+  }
+
+  // Wait a couple of frames for the events to be dispatched
+  await waitForFrame();
+  await waitForFrame();
+
+  const expectedEvents = [
+    ['animationstart', parentDiv, 0],
+    ['animationstart', parentDiv, '::marker', 0],
+    ['animationstart', parentDiv, '::before', 0],
+    ['animationstart', parentDiv, '::after', 0],
+    ['animationstart', childDiv, 0],
+  ];
+  if (!CSS.supports('selector(::marker)')) {
+    expectedEvents.splice(1, 1);
+  }
+
+  checkEvents(events, ...expectedEvents);
+}, 'Same events on pseudo-elements follow the prescribed order');
 
 promise_test(async t => {
   let events = [];
   const [animation1, watcher1, div1] =
     setupAnimation(t, 'anim 200s 400s', events);
   const [animation2, watcher2, div2] =
     setupAnimation(t, 'anim 300s 2', events);
 
@@ -108,17 +200,17 @@ promise_test(async t => {
 
   events.length = 0;  // Clear received event array
 
   await Promise.all([ watcher1.wait_for('animationstart'),
                       watcher2.wait_for('animationiteration') ]);
 
   checkEvents(events, ['animationiteration', div2, 300],
                       ['animationstart',     div1, 0]);
-}, 'Test start and iteration events are ordered by time.');
+}, 'Start and iteration events are ordered by time');
 
 promise_test(async t => {
   let events = [];
   const [animation1, watcher1, div1] =
     setupAnimation(t, 'anim 150s', events);
   const [animation2, watcher2, div2] =
     setupAnimation(t, 'anim 100s 2', events);
 
@@ -130,17 +222,17 @@ promise_test(async t => {
 
   events.length = 0;  // Clear received event array
 
   await Promise.all([ watcher1.wait_for('animationend'),
                       watcher2.wait_for('animationiteration') ]);
 
   checkEvents(events, ['animationiteration', div2, 100],
                       ['animationend',       div1, 150]);
-}, 'Test iteration and end events are ordered by time.');
+}, 'Iteration and end events are ordered by time');
 
 promise_test(async t => {
   let events = [];
   const [animation1, watcher1, div1] =
     setupAnimation(t, 'anim 100s 100s', events);
   const [animation2, watcher2, div2] =
     setupAnimation(t, 'anim 100s 2', events);
 
@@ -151,11 +243,11 @@ promise_test(async t => {
                                           'animationend' ]),
                       watcher2.wait_for([ 'animationstart',
                                           'animationend' ]) ]);
 
   checkEvents(events, ['animationstart', div2, 0],
                       ['animationstart', div1, 0],
                       ['animationend',   div1, 100],
                       ['animationend',   div2, 200]);
-}, 'Test start and end events are sorted correctly when fired simultaneously');
+}, 'Start and end events are sorted correctly when fired simultaneously');
 
 </script>
--- a/testing/web-platform/tests/css/css-transitions/Document-getAnimations.tentative.html
+++ b/testing/web-platform/tests/css/css-transitions/Document-getAnimations.tentative.html
@@ -31,57 +31,98 @@ test(t => {
 
   // Remove both
   div.style.transitionProperty = 'none';
   assert_equals(document.getAnimations().length, 0,
                 'getAnimations returns no running CSS Transitions');
 }, 'getAnimations for CSS Transitions');
 
 test(t => {
-  addStyle(t, { '.init::after': 'content: ""; width: 0px; ' +
-                                'transition: all 100s;',
-                '.init::before': 'content: ""; width: 0px; ' +
-                                 'transition: all 10s;',
-                '.change::after': 'width: 100px;',
-                '.change::before': 'width: 100px;' });
-  // create two divs with these arrangement:
+  // Create two divs with the following arrangement:
+  //
   //       parent
+  //    (::marker,)
   //     ::before,
   //     ::after
   //        |
   //       child
-  const parent = addDiv(t);
+
+  addStyle(t, {
+    '.init::after': 'content: ""; width: 0px; transition: all 100s;',
+    '.init::before': 'content: ""; width: 0px; transition: all 100s;',
+    '.change::after': 'width: 100px;',
+    '.change::before': 'width: 100px;',
+  });
+
+  const supportsMarkerPseudos = CSS.supports('selector(::marker)');
+  if (supportsMarkerPseudos) {
+    addStyle(t, {
+      '.init::marker': 'content: ""; color: red; transition: all 100s;',
+      '.change::marker': 'color: green;',
+    });
+  }
+
+  const parent = addDiv(t, { 'style': 'display: list-item' });
   const child = addDiv(t);
   parent.appendChild(child);
 
   parent.style.left = '0px';
-  parent.style.transition = 'left 10s';
+  parent.style.transition = 'left 100s';
   parent.classList.add('init');
   child.style.left = '0px';
-  child.style.transition = 'left 10s';
+  child.style.transition = 'left 100s';
   getComputedStyle(parent).left;
 
   parent.style.left = '100px';
   parent.classList.add('change');
   child.style.left = '100px';
 
-  const anims = document.getAnimations();
-  assert_equals(anims.length, 4,
-                'CSS transition on both pseudo-elements and elements ' +
-                'are returned');
-  assert_equals(anims[0].effect.target, parent,
-                'The animation targeting the parent element comes first');
-  assert_equals(anims[1].effect.target.type, '::before',
-                'The animation targeting the ::before element comes second');
-  assert_equals(anims[2].effect.target.type, '::after',
-                'The animation targeting the ::after element comes third');
-  assert_equals(anims[3].effect.target, child,
-                'The animation targeting the child element comes last');
-}, 'CSS Transitions targetting (pseudo-)elements should have correct order ' +
-   'after sorting');
+  const expectedTransitions = [
+    [parent, undefined],
+    [parent, '::marker'],
+    [parent, '::before'],
+    [parent, '::after'],
+    [child, undefined],
+  ];
+  if (!supportsMarkerPseudos) {
+    expectedTransitions.splice(1, 1);
+  }
+
+  const transitions = document.getAnimations();
+  assert_equals(
+    transitions.length,
+    expectedTransitions.length,
+    'CSS transition on both pseudo-elements and elements are returned'
+  );
+
+  for (const [index, expected] of expectedTransitions.entries()) {
+    const [element, pseudo] = expected;
+    const actual = transitions[index];
+
+    if (pseudo) {
+      assert_equals(
+        actual.effect.target.element,
+        element,
+        `Transition #${index + 1} has expected target`
+      );
+      assert_equals(
+        actual.effect.target.type,
+        pseudo,
+        `Transition #${index + 1} has expected pseudo type`
+      );
+    } else {
+      assert_equals(
+        actual.effect.target,
+        element,
+        `Transition #${index + 1} has expected target`
+      );
+    }
+  }
+}, 'CSS Transitions targetting (pseudo-)elements should have correct order '
+   + 'after sorting');
 
 promise_test(async t => {
   const div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
   getComputedStyle(div).left;
 
   div.style.left = '100px';
   const animations = div.getAnimations();
   assert_equals(animations.length, 1, 'Got transition');
deleted file mode 100644
--- a/testing/web-platform/tests/css/css-transitions/hidden-container-001.html
+++ /dev/null
@@ -1,125 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <head>
-        <meta charset="utf-8">
-        <title>CSS Transitions Test: Not Transitioning within hidden element</title>
-        <meta name="assert" content="Test checks that transitions are NOT run within hidden elements">
-        <link rel="help" title="2. Transitions" href="http://www.w3.org/TR/css3-transitions/#transitions">
-        <link rel="author" title="Rodney Rehm" href="http://rodneyrehm.de/en/">
-        <meta name="flags" content="dom">
-
-        <script src="/resources/testharness.js" type="text/javascript"></script>
-        <script src="/resources/testharnessreport.js" type="text/javascript"></script>
-
-        <script src="./support/vendorPrefix.js" type="text/javascript"></script>
-        <script src="./support/helper.js" type="text/javascript"></script>
-        <script src="./support/runParallelAsyncHarness.js" type="text/javascript"></script>
-        <script src="./support/generalParallelTest.js" type="text/javascript"></script>
-        <script src="./support/properties.js" type="text/javascript"></script>
-
-        <style type="text/css">
-            #offscreen {
-                display: none;
-            }
-        </style>
-    </head>
-    <body>
-        <!-- required by testharnessreport.js -->
-        <div id="log"></div>
-        <!-- elements used for testing -->
-        <div id="fixture" class="fixture">
-            <div class="container">
-                <div class="transition">Text sample</div>
-            </div>
-        </div>
-        <div id="offscreen"></div>
-
-        <!--
-            SEE ./support/README.md for an abstract explanation of the test procedure
-            http://test.csswg.org/source/contributors/rodneyrehm/submitted/css3-transitions/README.md
-        -->
-
-        <script>
-
-            // this test takes its time, give it a minute to run
-            var timeout = 60000;
-            setup({timeout: timeout});
-
-            var tests = [
-                {
-                    name: "transition within display:none",
-                    property: 'background-color',
-                    flags: {},
-                    from: {'background-color': 'red'},
-                    to: {'background-color': 'green'}
-                }
-            ];
-
-            // general transition-duration
-            var duration = '0.5s';
-
-            runParallelAsyncHarness({
-                // array of test data
-                tests: tests,
-                // the number of tests to run in parallel
-                testsPerSlice: 1,
-                // milliseconds to wait before calling teardown and ending test
-                duration: parseFloat(duration) * 1000,
-                // prepare individual test
-                setup: function(data, options) {
-                    var styles = {
-                        '.fixture': {},
-
-                        '.container': data.parentStyle,
-                        '.container.to': {},
-                        '.container.how': {},
-
-                        '.transition': data.from,
-                        '.transition.to' : data.to,
-                        '.transition.how' : {transition: 'all ' + duration + ' linear 0s'}
-                    };
-
-                    generalParallelTest.setup(data, options);
-                    generalParallelTest.addStyles(data, options, styles);
-                },
-                // cleanup after individual test
-                teardown: generalParallelTest.teardown,
-                // invoked prior to running a slice of tests
-                sliceStart: generalParallelTest.sliceStart,
-                // invoked after running a slice of tests
-                sliceDone: generalParallelTest.sliceDone,
-                // test cases, make them as granular as possible
-                cases: {
-                    // test property values while transitioning
-                    // values.start kicks off a transition
-                    'values': {
-                        // run actual test, assertions can be used here!
-                        start: function(test, data, options) {
-                            // identify initial and target values
-                            generalParallelTest.getStyle(data);
-                            // make sure values differ, if they don't, the property could most likely not be parsed
-                            assert_not_equals(data.transition.from, data.transition.to, "initial and target values may not match");
-                            // kick off the transition
-                            generalParallelTest.startTransition(data);
-                        },
-                        done: function(test, data, options) {
-                            // make sure there were no intermediate values
-                            assert_equals(data.transition.values.length, 2, "no intermediate values");
-                        }
-                    },
-                    // test TransitionEnd events
-                    'events': {
-                        done: function(test, data, options) {
-                            // make sure there were no events on parent
-                            test.step(generalParallelTest.assertExpectedEventsFunc(data, 'container', ""));
-                            // make sure we got the event for the tested property only
-                            test.step(generalParallelTest.assertExpectedEventsFunc(data, 'transition', ""));
-                        }
-                    }
-                },
-                // called once all tests are done
-                done: generalParallelTest.done
-            });
-        </script>
-    </body>
-</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-transitions/non-rendered-element-001.html
@@ -0,0 +1,127 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>CSS Transitions Test: Transitions do not run for an element that is not being rendered</title>
+<link rel="help" title="Starting transitions"
+  href="https://drafts.csswg.org/css-transitions/#starting">
+
+<script src="/resources/testharness.js" type="text/javascript"></script>
+<script src="/resources/testharnessreport.js" type="text/javascript"></script>
+<script src="./support/helper.js" type="text/javascript"></script>
+
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+promise_test(async t => {
+  // Create element that is not rendered
+  const div = addDiv(t, {
+    style:
+      'transition: background-color 100s;' +
+      'background-color: red;' +
+      'display: none',
+  });
+
+  // Attach event listeners
+  div.addEventListener(
+    'transitionrun',
+    t.step_func(() => {
+      assert_unreached('transitionrun event should not be fired');
+    })
+  );
+
+  // Resolve before-change style
+  getComputedStyle(div).backgroundColor;
+
+  // Set up and resolve after-change style
+  div.style.backgroundColor = 'green';
+  getComputedStyle(div).backgroundColor;
+
+  // There should be no events received for the triggered transition.
+  await waitForFrame();
+  await waitForFrame();
+}, 'Transitions do not run on an element not being rendered');
+
+test(t => {
+  // Create element that is not rendered
+  const div = addDiv(t, {
+    style:
+      'transition: background-color 100s;' +
+      'background-color: red;' +
+      'display: none',
+  });
+
+  // Resolve before-change style
+  getComputedStyle(div).backgroundColor;
+
+  // Make it rendered
+  div.style.display = 'block';
+
+  // Set up and resolve after-change style
+  div.style.backgroundColor = 'green';
+  getComputedStyle(div).backgroundColor;
+
+  // We should have jumped immediately to the after-change style rather than
+  // transitioning to it.
+  assert_equals(
+    getComputedStyle(div).backgroundColor,
+    'rgb(0, 128, 0)',
+    'No transition should run'
+  );
+}, 'Transitions do not run for an element newly rendered');
+
+promise_test(async t => {
+  // Create element
+  const div = addDiv(t, {
+    style: 'transition: background-color 100s; background-color: red',
+  });
+
+  // Attach event listeners
+  div.addEventListener('transitionrun', t.step_func(() => {
+    assert_unreached('transitionrun event should not be fired');
+  }));
+
+  // Resolve before-change style
+  getComputedStyle(div).backgroundColor;
+
+  // Set up after-change style
+  div.style.backgroundColor = 'green';
+
+  // But make the element non-rendered
+  div.style.display = 'none';
+
+  // There should be no events received for the triggered transition.
+  await waitForFrame();
+  await waitForFrame();
+}, 'Transitions do not run for an element newly made not rendered');
+
+promise_test(async t => {
+  // Create element
+  const div = addDiv(t, {
+    style: 'transition: background-color 100s; background-color: red',
+  });
+
+  // Attach event listeners
+  const eventWatcher = new EventWatcher(t, div, [
+    'transitionrun',
+    'transitioncancel',
+  ]);
+
+  // Trigger transition
+  getComputedStyle(div).backgroundColor;
+  div.style.backgroundColor = 'green';
+  getComputedStyle(div).backgroundColor;
+
+  await eventWatcher.wait_for('transitionrun');
+
+  // Make the element no longer rendered
+  div.style.display = 'none';
+
+  await eventWatcher.wait_for('transitioncancel');
+}, 'Transitions are canceled when an element is no longer rendered');
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-transitions/non-rendered-element-002.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<title>CSS Transitions Test: Transitions do not run for a pseudo-element that is not being rendered</title>
+<link rel="help" title="Starting transitions"
+  href="https://drafts.csswg.org/css-transitions/#starting">
+
+<script src="/resources/testharness.js" type="text/javascript"></script>
+<script src="/resources/testharnessreport.js" type="text/javascript"></script>
+<script src="./support/helper.js" type="text/javascript"></script>
+
+</head>
+<body>
+<div id="log"></div>
+
+<script>
+
+promise_test(async t => {
+  for (const pseudoType of ['before', 'after']) {
+    const style = addStyle(t, {
+      [`.init::${pseudoType}`]: 'content: ""; width: 0px; transition: width 100s;',
+      [`.change::${pseudoType}`]: 'width: 100px;',
+      [`.cancel::${pseudoType}`]: 'content: none',
+    });
+
+    // Create element (and pseudo-element) and attach event listeners
+    const div = addDiv(t);
+    div.classList.add('init');
+
+    const eventWatcher = new EventWatcher(t, div, [
+      'transitionrun',
+      'transitioncancel',
+    ]);
+
+    // Trigger transition
+    getComputedStyle(div).width;
+    div.classList.add('change');
+    getComputedStyle(div).width;
+
+    await eventWatcher.wait_for('transitionrun');
+
+    // Make the pseudo-element no longer rendered
+    div.classList.add('cancel');
+
+    await eventWatcher.wait_for('transitioncancel');
+
+    div.remove();
+    style.remove();
+  }
+}, 'Transitions on ::before/::after pseudo-elements are canceled when the'
+   + ' content property is cleared');
+
+promise_test(async t => {
+  if (!CSS.supports('selector(::marker)')) {
+    return;
+  }
+
+  addStyle(t, {
+    '.init::marker': 'content: ""; color: red; transition: color 100s;',
+    '.change::marker': 'color: green',
+  });
+
+  // Create element (and pseudo-element) and attach event listeners
+  const div = addDiv(t, { 'style': 'display: list-item' });
+  div.classList.add('init');
+
+  const eventWatcher = new EventWatcher(t, div, [
+    'transitionrun',
+    'transitioncancel',
+  ]);
+
+  // Trigger transition
+  getComputedStyle(div).color;
+  div.classList.add('change');
+  getComputedStyle(div).color;
+
+  await eventWatcher.wait_for('transitionrun');
+
+  // Make the parent element no longer display: list-item so that the pseudo
+  // element no longer renders
+  div.style.display = 'block';
+
+  await eventWatcher.wait_for('transitioncancel');
+}, 'Transitions on ::marker pseudo-elements are canceled when the'
+   + ' parent display type is no longer list-item');
+</script>
+
+</body>
+</html>
--- a/testing/web-platform/tests/css/css-transitions/support/helper.js
+++ b/testing/web-platform/tests/css/css-transitions/support/helper.js
@@ -226,16 +226,17 @@ root.addStyle = (t, rules) => {
     }
   }
 
   if (t && typeof t.add_cleanup === 'function') {
     t.add_cleanup(() => {
       extraStyle.remove();
     });
   }
+  return extraStyle;
 };
 
 /**
  * Promise wrapper for requestAnimationFrame.
  */
 root.waitForFrame = () => {
   return new Promise(resolve => {
     window.requestAnimationFrame(resolve);
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -75,17 +75,17 @@ DIRS += [
     'utils',
     'url-classifier',
     'urlformatter',
     'viewconfig',
     'viewsource',
     'windowcreator',
     'windowwatcher',
     'workerloader',
-    'xulstore'
+    'xulstore',
 ]
 
 if CONFIG['MOZ_BUILD_APP'] != 'mobile/android':
     DIRS += ['narrate'];
 
     if CONFIG['NS_PRINTING']:
         DIRS += ['printing']
 
--- a/toolkit/components/places/bookmark_sync/Cargo.toml
+++ b/toolkit/components/places/bookmark_sync/Cargo.toml
@@ -4,16 +4,17 @@ version = "0.1.0"
 authors = ["Lina Cambridge <lina@yakshaving.ninja>"]
 edition = "2018"
 
 [dependencies]
 atomic_refcell = "0.1"
 dogear = "0.2.3"
 libc = "0.2"
 log = "0.4"
+cstr = "0.1"
 moz_task = { path = "../../../../xpcom/rust/moz_task" }
 nserror = { path = "../../../../xpcom/rust/nserror" }
 nsstring = { path = "../../../../xpcom/rust/nsstring" }
 storage = { path = "../../../../storage/rust" }
 storage_variant = { path = "../../../../storage/variant" }
 xpcom = { path = "../../../../xpcom/rust/xpcom" }
 
 [dependencies.thin-vec]
--- a/toolkit/components/places/bookmark_sync/src/lib.rs
+++ b/toolkit/components/places/bookmark_sync/src/lib.rs
@@ -1,15 +1,17 @@
 /* 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/. */
 
 #![allow(non_snake_case)]
 
 #[macro_use]
+extern crate cstr;
+#[macro_use]
 extern crate xpcom;
 
 mod driver;
 mod error;
 mod merger;
 mod store;
 
 use xpcom::{interfaces::mozISyncedBookmarksMerger, RefPtr};
--- a/toolkit/components/places/bookmark_sync/src/merger.rs
+++ b/toolkit/components/places/bookmark_sync/src/merger.rs
@@ -149,29 +149,29 @@ impl MergeTask {
                 mozISyncedBookmarksMirrorLogger::LEVEL_WARN => LevelFilter::Warn,
                 mozISyncedBookmarksMirrorLogger::LEVEL_DEBUG => LevelFilter::Debug,
                 mozISyncedBookmarksMirrorLogger::LEVEL_TRACE => LevelFilter::Trace,
                 _ => LevelFilter::Off,
             })
             .unwrap_or(LevelFilter::Off);
         let logger = match logger {
             Some(logger) => Some(ThreadPtrHolder::new(
-                c_str!("mozISyncedBookmarksMirrorLogger"),
+                cstr!("mozISyncedBookmarksMirrorLogger"),
                 logger,
             )?),
             None => None,
         };
         Ok(MergeTask {
             db: db.clone(),
             max_log_level,
             logger,
             local_time_millis: local_time_seconds * 1000,
             remote_time_millis: remote_time_seconds * 1000,
             weak_uploads,
-            callback: ThreadPtrHolder::new(c_str!("mozISyncedBookmarksMirrorCallback"), callback)?,
+            callback: ThreadPtrHolder::new(cstr!("mozISyncedBookmarksMirrorCallback"), callback)?,
             result: AtomicRefCell::default(),
         })
     }
 }
 
 impl Task for MergeTask {
     fn run(&self) {
         let mut db = self.db.clone();
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "xulstore"
+version = "0.1.0"
+authors = ["nobody@mozilla.org"]
+license = "MPL-2.0"
+
+[dependencies]
+crossbeam-utils = "0.6.3"
+cstr = "0.1"
+lazy_static = "1.0"
+libc = "0.2"
+lmdb-rkv = "0.11.2"
+log = "0.4"
+moz_task = { path = "../../../xpcom/rust/moz_task" }
+nsstring = { path = "../../../xpcom/rust/nsstring" }
+nserror = { path = "../../../xpcom/rust/nserror" }
+rkv = "0.9.3"
+serde_json = "1"
+xpcom = { path = "../../../xpcom/rust/xpcom" }
+
+# Get rid of failure's dependency on backtrace. Eventually
+# backtrace will move into Rust core, but we don't need it here.
+[dependencies.failure]
+version = "0.1"
+default_features = false
+features = ["derive"]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/XULStore.cpp
@@ -0,0 +1,107 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/XULStore.h"
+#include "nsCOMPtr.h"
+#include "nsIXULStore.h"
+
+namespace mozilla {
+
+// The XULStore API is implemented in Rust and exposed to C++ via a set of
+// C functions with the "xulstore_" prefix.  We declare them in this anonymous
+// namespace to prevent C++ code outside this file from accessing them,
+// as they are an internal implementation detail, and C++ code should use
+// the mozilla::XULStore::* functions and mozilla::XULStoreIterator class
+// declared in XULStore.h.
+namespace {
+extern "C" {
+void xulstore_new_service(nsIXULStore** result);
+nsresult xulstore_set_value(const nsAString* doc, const nsAString* id,
+                            const nsAString* attr, const nsAString* value);
+nsresult xulstore_has_value(const nsAString* doc, const nsAString* id,
+                            const nsAString* attr, bool* has_value);
+nsresult xulstore_get_value(const nsAString* doc, const nsAString* id,
+                            const nsAString* attr, nsAString* value);
+nsresult xulstore_remove_value(const nsAString* doc, const nsAString* id,
+                               const nsAString* attr);
+XULStoreIterator* xulstore_get_ids(const nsAString* doc, nsresult* result);
+XULStoreIterator* xulstore_get_attrs(const nsAString* doc, const nsAString* id,
+                                     nsresult* result);
+bool xulstore_iter_has_more(const XULStoreIterator*);
+nsresult xulstore_iter_get_next(XULStoreIterator*, nsAString* value);
+void xulstore_iter_free(XULStoreIterator* iterator);
+}
+
+// A static reference to the nsIXULStore singleton that JS uses to access
+// the store.  Retrieved via mozilla::XULStore::GetService().
+static StaticRefPtr<nsIXULStore> sXULStore;
+}  // namespace
+
+bool XULStoreIterator::HasMore() const { return xulstore_iter_has_more(this); }
+
+nsresult XULStoreIterator::GetNext(nsAString* item) {
+  return xulstore_iter_get_next(this, item);
+}
+
+void DefaultDelete<XULStoreIterator>::operator()(XULStoreIterator* ptr) const {
+  xulstore_iter_free(ptr);
+}
+
+namespace XULStore {
+already_AddRefed<nsIXULStore> GetService() {
+  nsCOMPtr<nsIXULStore> xulStore;
+
+  if (sXULStore) {
+    xulStore = sXULStore;
+  } else {
+    xulstore_new_service(getter_AddRefs(xulStore));
+    sXULStore = xulStore;
+    mozilla::ClearOnShutdown(&sXULStore);
+  }
+
+  return xulStore.forget();
+}
+
+nsresult SetValue(const nsAString& doc, const nsAString& id,
+                  const nsAString& attr, const nsAString& value) {
+  return xulstore_set_value(&doc, &id, &attr, &value);
+}
+nsresult HasValue(const nsAString& doc, const nsAString& id,
+                  const nsAString& attr, bool& has_value) {
+  return xulstore_has_value(&doc, &id, &attr, &has_value);
+}
+nsresult GetValue(const nsAString& doc, const nsAString& id,
+                  const nsAString& attr, nsAString& value) {
+  return xulstore_get_value(&doc, &id, &attr, &value);
+}
+nsresult RemoveValue(const nsAString& doc, const nsAString& id,
+                     const nsAString& attr) {
+  return xulstore_remove_value(&doc, &id, &attr);
+}
+nsresult GetIDs(const nsAString& doc, UniquePtr<XULStoreIterator>& iter) {
+  // We assign the value of the iter here in C++ via a return value
+  // rather than in the Rust function via an out parameter in order
+  // to ensure that any old value is deleted, since the UniquePtr's
+  // assignment operator won't delete the old value if the assignment
+  // happens in Rust.
+  nsresult result;
+  iter.reset(xulstore_get_ids(&doc, &result));
+  return result;
+}
+nsresult GetAttrs(const nsAString& doc, const nsAString& id,
+                  UniquePtr<XULStoreIterator>& iter) {
+  // We assign the value of the iter here in C++ via a return value
+  // rather than in the Rust function via an out parameter in order
+  // to ensure that any old value is deleted, since the UniquePtr's
+  // assignment operator won't delete the old value if the assignment
+  // happens in Rust.
+  nsresult result;
+  iter.reset(xulstore_get_attrs(&doc, &id, &result));
+  return result;
+}
+
+};  // namespace XULStore
+};  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/XULStore.h
@@ -0,0 +1,55 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file declares the XULStore API for C++ via the mozilla::XULStore
+ * namespace and the mozilla::XULStoreIterator class.  It also declares
+ * the mozilla::XULStore::GetService() function that the component manager
+ * uses to instantiate and retrieve the nsIXULStore singleton.
+ */
+
+#ifndef mozilla_XULStore_h
+#define mozilla_XULStore_h
+
+#include "nsIXULStore.h"
+
+namespace mozilla {
+class XULStoreIterator final {
+ public:
+  bool HasMore() const;
+  nsresult GetNext(nsAString* item);
+
+ private:
+  XULStoreIterator() = delete;
+  XULStoreIterator(const XULStoreIterator&) = delete;
+  XULStoreIterator& operator=(const XULStoreIterator&) = delete;
+  ~XULStoreIterator() = delete;
+};
+
+template <>
+class DefaultDelete<XULStoreIterator> {
+ public:
+  void operator()(XULStoreIterator* ptr) const;
+};
+
+namespace XULStore {
+// Instantiates and retrieves the nsIXULStore singleton that JS uses to access
+// the store.  C++ code should use the mozilla::XULStore::* functions instead.
+already_AddRefed<nsIXULStore> GetService();
+
+nsresult SetValue(const nsAString& doc, const nsAString& id,
+                  const nsAString& attr, const nsAString& value);
+nsresult HasValue(const nsAString& doc, const nsAString& id,
+                  const nsAString& attr, bool& has_value);
+nsresult GetValue(const nsAString& doc, const nsAString& id,
+                  const nsAString& attr, nsAString& value);
+nsresult RemoveValue(const nsAString& doc, const nsAString& id,
+                     const nsAString& attr);
+nsresult GetIDs(const nsAString& doc, UniquePtr<XULStoreIterator>& iter);
+nsresult GetAttrs(const nsAString& doc, const nsAString& id,
+                  UniquePtr<XULStoreIterator>& iter);
+};  // namespace XULStore
+};  // namespace mozilla
+
+#endif  // mozilla_XULStore_h
--- a/toolkit/components/xulstore/XULStore.jsm
+++ b/toolkit/components/xulstore/XULStore.jsm
@@ -1,308 +1,95 @@
 /* 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/. */
 
-// Enables logging and shorter save intervals.
+"use strict";
+
+// This JS module wraps the nsIXULStore XPCOM service with useful abstractions.
+// In particular, it wraps the enumerators returned by getIDsEnumerator()
+// and getAttributeEnumerator() in JS objects that implement the iterable
+// protocol.  It also implements the persist() method.  JS consumers should use
+// this module rather than accessing nsIXULStore directly.
+
+const EXPORTED_SYMBOLS = ["XULStore"];
+
+const xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
+
+// Enables logging.
 const debugMode = false;
 
-// Delay when a change is made to when the file is saved.
-// 30 seconds normally, or 3 seconds for testing
-const WRITE_DELAY_MS = (debugMode ? 3 : 30) * 1000;
-
-const XULSTORE_CID = Components.ID("{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}");
-const STOREDB_FILENAME = "xulstore.json";
-
-const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
-
-function XULStore() {
-  if (!Services.appinfo.inSafeMode)
-    this.load();
+// Internal function for logging debug messages to the Error Console window
+function log(message) {
+  if (!debugMode)
+    return;
+  console.log("XULStore: " + message);
 }
 
-XULStore.prototype = {
-  classID: XULSTORE_CID,
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsIXULStore,
-                                          Ci.nsISupportsWeakReference]),
-  _xpcom_factory: XPCOMUtils.generateSingletonFactory(XULStore),
-
-  /* ---------- private members ---------- */
-
-  /*
-   * The format of _data is _data[docuri][elementid][attribute]. For example:
-   *  {
-   *      "chrome://blah/foo.xul" : {
-   *                                    "main-window" : { aaa : 1, bbb : "c" },
-   *                                    "barColumn"   : { ddd : 9, eee : "f" },
-   *                                },
-   *
-   *      "chrome://foopy/b.xul" :  { ... },
-   *      ...
-   *  }
-   */
-  _data: {},
-  _storeFile: null,
-  _needsSaving: false,
-  _saveAllowed: true,
-  _writeTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
-
-  load() {
-    Services.obs.addObserver(this, "profile-before-change", true);
-
-    try {
-      this._storeFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
-    } catch (ex) {
-      try {
-        this._storeFile = Services.dirsvc.get("ProfDS", Ci.nsIFile);
-      } catch (ex) {
-        throw new Error("Can't find profile directory.");
-      }
-    }
-    this._storeFile.append(STOREDB_FILENAME);
-
-    this.readFile();
-  },
-
-  observe(subject, topic, data) {
-    this.writeFile();
-    if (topic == "profile-before-change") {
-      this._saveAllowed = false;
-    }
-  },
+const XULStore = {
+  setValue: xulStore.setValue,
+  hasValue: xulStore.hasValue,
+  getValue: xulStore.getValue,
+  removeValue: xulStore.removeValue,
+  removeDocument: xulStore.removeDocument,
 
-  /*
-   * Internal function for logging debug messages to the Error Console window
+  /**
+   * Sets a value for a specified node's attribute, except in
+   * the case below (following the original XULDocument::persist):
+   * If the value is empty and if calling `hasValue` with the node's
+   * document and ID and `attr` would return true, then the
+   * value instead gets removed from the store (see Bug 1476680).
+   *
+   * @param node - DOM node
+   * @param attr - attribute to store
    */
-  log(message) {
-    if (!debugMode)
-      return;
-    console.log("XULStore: " + message);
-  },
-
-  readFile() {
-    try {
-      this._data = JSON.parse(Cu.readUTF8File(this._storeFile));
-    } catch (e) {
-      this.log("Error reading JSON: " + e);
-      // This exception could mean that the file didn't exist.
-      // We'll just ignore the error and start with a blank slate.
-    }
-  },
-
-  async writeFile() {
-    if (!this._needsSaving)
-      return;
-
-    this._needsSaving = false;
-
-    this.log("Writing to xulstore.json");
-
-    try {
-      let data = JSON.stringify(this._data);
-      let encoder = new TextEncoder();
-
-      data = encoder.encode(data);
-      await OS.File.writeAtomic(this._storeFile.path, data,
-                              { tmpPath: this._storeFile.path + ".tmp" });
-    } catch (e) {
-      this.log("Failed to write xulstore.json: " + e);
-      throw e;
-    }
-  },
-
-  markAsChanged() {
-    if (this._needsSaving || !this._storeFile)
-      return;
-
-    // Don't write the file more than once every 30 seconds.
-    this._needsSaving = true;
-    this._writeTimer.init(this, WRITE_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  /* ---------- interface implementation ---------- */
-
   persist(node, attr) {
     if (!node.id) {
       throw new Error("Node without ID passed into persist()");
     }
 
     const uri = node.ownerDocument.documentURI;
     const value = node.getAttribute(attr);
 
     if (node.localName == "window") {
-      this.log("Persisting attributes to windows is handled by nsXULWindow.");
+      log("Persisting attributes to windows is handled by nsXULWindow.");
       return;
     }
 
     // See Bug 1476680 - we could drop the `hasValue` check so that
     // any time there's an empty attribute it gets removed from the
     // store. Since this is copying behavior from document.persist,
     // callers would need to be updated with that change.
-    if (!value && this.hasValue(uri, node.id, attr)) {
-      this.removeValue(uri, node.id, attr);
+    if (!value && xulStore.hasValue(uri, node.id, attr)) {
+      xulStore.removeValue(uri, node.id, attr);
     } else {
-      this.setValue(uri, node.id, attr, value);
-    }
-  },
-
-  setValue(docURI, id, attr, value) {
-    this.log("Saving " + attr + "=" + value + " for id=" + id + ", doc=" + docURI);
-
-    if (!this._saveAllowed) {
-      Services.console.logStringMessage("XULStore: Changes after profile-before-change are ignored!");
-      return;
-    }
-
-    // bug 319846 -- don't save really long attributes or values.
-    if (id.length > 512 || attr.length > 512) {
-      throw Components.Exception("id or attribute name too long", Cr.NS_ERROR_ILLEGAL_VALUE);
-    }
-
-    if (value.length > 4096) {
-      Services.console.logStringMessage("XULStore: Warning, truncating long attribute value");
-      value = value.substr(0, 4096);
-    }
-
-    let obj = this._data;
-    if (!(docURI in obj)) {
-      obj[docURI] = {};
-    }
-    obj = obj[docURI];
-    if (!(id in obj)) {
-      obj[id] = {};
-    }
-    obj = obj[id];
-
-    // Don't set the value if it is already set to avoid saving the file.
-    if (attr in obj && obj[attr] == value)
-      return;
-
-    obj[attr] = value; // IE, this._data[docURI][id][attr] = value;
-
-    this.markAsChanged();
-  },
-
-  hasValue(docURI, id, attr) {
-    this.log("has store value for id=" + id + ", attr=" + attr + ", doc=" + docURI);
-
-    let ids = this._data[docURI];
-    if (ids) {
-      let attrs = ids[id];
-      if (attrs) {
-        return attr in attrs;
-      }
-    }
-
-    return false;
-  },
-
-  getValue(docURI, id, attr) {
-    this.log("get store value for id=" + id + ", attr=" + attr + ", doc=" + docURI);
-
-    let ids = this._data[docURI];
-    if (ids) {
-      let attrs = ids[id];
-      if (attrs) {
-        return attrs[attr] || "";
-      }
-    }
-
-    return "";
-  },
-
-  removeValue(docURI, id, attr) {
-    this.log("remove store value for id=" + id + ", attr=" + attr + ", doc=" + docURI);
-
-    if (!this._saveAllowed) {
-      Services.console.logStringMessage("XULStore: Changes after profile-before-change are ignored!");
-      return;
-    }
-
-    let ids = this._data[docURI];
-    if (ids) {
-      let attrs = ids[id];
-      if (attrs && attr in attrs) {
-        delete attrs[attr];
-
-        if (Object.getOwnPropertyNames(attrs).length == 0) {
-          delete ids[id];
-
-          if (Object.getOwnPropertyNames(ids).length == 0) {
-            delete this._data[docURI];
-          }
-        }
-
-        this.markAsChanged();
-      }
-    }
-  },
-
-  removeDocument(docURI) {
-    this.log("remove store values for doc=" + docURI);
-
-    if (!this._saveAllowed) {
-      Services.console.logStringMessage("XULStore: Changes after profile-before-change are ignored!");
-      return;
-    }
-
-    if (this._data[docURI]) {
-      delete this._data[docURI];
-      this.markAsChanged();
+      xulStore.setValue(uri, node.id, attr, value);
     }
   },
 
   getIDsEnumerator(docURI) {
-    this.log("Getting ID enumerator for doc=" + docURI);
-
-    if (!(docURI in this._data))
-      return new nsStringEnumerator([]);
-
-    let result = [];
-    let ids = this._data[docURI];
-    if (ids) {
-      for (let id in this._data[docURI]) {
-        result.push(id);
-      }
-    }
-
-    return new nsStringEnumerator(result);
+    return new XULStoreEnumerator(xulStore.getIDsEnumerator(docURI));
   },
 
   getAttributeEnumerator(docURI, id) {
-    this.log("Getting attribute enumerator for id=" + id + ", doc=" + docURI);
-
-    if (!(docURI in this._data) || !(id in this._data[docURI]))
-      return new nsStringEnumerator([]);
-
-    let attrs = [];
-    for (let attr in this._data[docURI][id]) {
-      attrs.push(attr);
-    }
-
-    return new nsStringEnumerator(attrs);
+    return new XULStoreEnumerator(xulStore.getAttributeEnumerator(docURI, id));
   },
 };
 
-function nsStringEnumerator(items) {
-  this._items = items;
-}
+class XULStoreEnumerator {
+  constructor(enumerator) {
+    this.enumerator = enumerator;
+  }
 
-nsStringEnumerator.prototype = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIStringEnumerator]),
-  _nextIndex: 0,
-  [Symbol.iterator]() {
-    return this._items.values();
-  },
   hasMore() {
-    return this._nextIndex < this._items.length;
-  },
+    return this.enumerator.hasMore();
+  }
+
   getNext() {
-    if (!this.hasMore())
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-    return this._items[this._nextIndex++];
-  },
-};
+    return this.enumerator.getNext();
+  }
 
-var EXPORTED_SYMBOLS = ["XULStore"];
+  * [Symbol.iterator]() {
+    while (this.enumerator.hasMore()) {
+      yield (this.enumerator.getNext());
+    }
+  }
+}
--- a/toolkit/components/xulstore/components.conf
+++ b/toolkit/components/xulstore/components.conf
@@ -1,14 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Classes = [
     {
-        'cid': '{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}',
+        'cid': '{be70bf11-0c28-4a02-a38c-0148538d42cf}',
         'contract_ids': ['@mozilla.org/xul/xulstore;1'],
-        'jsm': 'resource://gre/modules/XULStore.jsm',
-        'constructor': 'XULStore',
+        'type': 'nsIXULStore',
+        'headers': ['mozilla/XULStore.h'],
+        'singleton': True,
+        'constructor': 'mozilla::XULStore::GetService',
     },
 ]
--- a/toolkit/components/xulstore/moz.build
+++ b/toolkit/components/xulstore/moz.build
@@ -9,17 +9,31 @@ with Files('**'):
 
 MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
 
 XPIDL_SOURCES += [
     'nsIXULStore.idl',
 ]
 
-XPIDL_MODULE = 'toolkit_xulstore'
+TEST_DIRS += [
+    'tests/gtest',
+]
+
+EXPORTS.mozilla += [
+    'XULStore.h',
+]
 
 EXTRA_JS_MODULES += [
     'XULStore.jsm',
 ]
 
+XPIDL_MODULE = 'xulstore'
+
 XPCOM_MANIFESTS += [
     'components.conf',
 ]
+
+UNIFIED_SOURCES += [
+    'XULStore.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/toolkit/components/xulstore/nsIXULStore.idl
+++ b/toolkit/components/xulstore/nsIXULStore.idl
@@ -1,40 +1,29 @@
 /* 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 "nsISupports.idl"
 
 interface nsIStringEnumerator;
-webidl Node;
 
 /**
  * The XUL store is used to store information related to a XUL document/application.
  * Typically it is used to store the persisted state for the document, such as
  * window location, toolbars that are open and nodes that are open and closed in a tree.
  *
- * The data is serialized to [profile directory]/xulstore.json
+ * XULStore.jsm wraps this API in useful abstractions for JS consumers.
+ * XULStore.h provides a more idiomatic API for C++ consumers.
+ * You should use those APIs unless you have good reasons to use this one.
  */
 [scriptable, uuid(987c4b35-c426-4dd7-ad49-3c9fa4c65d20)]
 interface nsIXULStore: nsISupports
 {
   /**
-   * Sets a value for a specified node's attribute, except in
-   * the case below (following the original XULDocument::persist):
-   * If the value is empty and if calling `hasValue` with the node's
-   * document and ID and `attr` would return true, then the
-   * value instead gets removed from the store (see Bug 1476680).
-   *
-   * @param node - DOM node
-   * @param attr - attribute to store
-   */
-  void persist(in Node aNode, in AString attr);
-
-  /**
    * Sets a value in the store.
    *
    * @param doc - document URI
    * @param id - identifier of the node
    * @param attr - attribute to store
    * @param value - value of the attribute
    */
   void setValue(in AString doc, in AString id, in AString attr, in AString value);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/src/error.rs
@@ -0,0 +1,112 @@
+/* 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/. */
+
+use nserror::{
+    nsresult, NS_ERROR_FAILURE, NS_ERROR_ILLEGAL_VALUE, NS_ERROR_NOT_AVAILABLE, NS_ERROR_UNEXPECTED,
+};
+use rkv::StoreError as RkvStoreError;
+use serde_json::Error as SerdeJsonError;
+use std::{io::Error as IoError, str::Utf8Error, string::FromUtf16Error, sync::PoisonError};
+
+pub(crate) type XULStoreResult<T> = Result<T, XULStoreError>;
+
+#[derive(Debug, Fail)]
+pub(crate) enum XULStoreError {
+    #[fail(display = "error converting bytes: {:?}", _0)]
+    ConvertBytes(Utf8Error),
+
+    #[fail(display = "error converting string: {:?}", _0)]
+    ConvertString(FromUtf16Error),
+
+    #[fail(display = "I/O error: {:?}", _0)]
+    IoError(IoError),
+
+    #[fail(display = "iteration is finished")]
+    IterationFinished,
+
+    #[fail(display = "JSON error: {}", _0)]
+    JsonError(SerdeJsonError),
+
+    #[fail(display = "error result {}", _0)]
+    NsResult(nsresult),
+
+    #[fail(display = "poison error getting read/write lock")]
+    PoisonError,
+
+    #[fail(display = "store error: {:?}", _0)]
+    RkvStoreError(RkvStoreError),
+
+    #[fail(display = "id or attribute name too long")]
+    IdAttrNameTooLong,
+
+    #[fail(display = "unavailable")]
+    Unavailable,
+
+    #[fail(display = "unexpected key: {:?}", _0)]
+    UnexpectedKey(String),
+
+    #[fail(display = "unexpected value")]
+    UnexpectedValue,
+}
+
+impl From<XULStoreError> for nsresult {
+    fn from(err: XULStoreError) -> nsresult {
+        match err {
+            XULStoreError::ConvertBytes(_) => NS_ERROR_FAILURE,
+            XULStoreError::ConvertString(_) => NS_ERROR_FAILURE,
+            XULStoreError::IoError(_) => NS_ERROR_FAILURE,
+            XULStoreError::IterationFinished => NS_ERROR_FAILURE,
+            XULStoreError::JsonError(_) => NS_ERROR_FAILURE,
+            XULStoreError::NsResult(result) => result,
+            XULStoreError::PoisonError => NS_ERROR_UNEXPECTED,
+            XULStoreError::RkvStoreError(_) => NS_ERROR_FAILURE,
+            XULStoreError::IdAttrNameTooLong => NS_ERROR_ILLEGAL_VALUE,
+            XULStoreError::Unavailable => NS_ERROR_NOT_AVAILABLE,
+            XULStoreError::UnexpectedKey(_) => NS_ERROR_UNEXPECTED,
+            XULStoreError::UnexpectedValue => NS_ERROR_UNEXPECTED,
+        }
+    }
+}
+
+impl From<FromUtf16Error> for XULStoreError {
+    fn from(err: FromUtf16Error) -> XULStoreError {
+        XULStoreError::ConvertString(err)
+    }
+}
+
+impl From<nsresult> for XULStoreError {
+    fn from(result: nsresult) -> XULStoreError {
+        XULStoreError::NsResult(result)
+    }
+}
+
+impl<T> From<PoisonError<T>> for XULStoreError {
+    fn from(_: PoisonError<T>) -> XULStoreError {
+        XULStoreError::PoisonError
+    }
+}
+
+impl From<RkvStoreError> for XULStoreError {
+    fn from(err: RkvStoreError) -> XULStoreError {
+        XULStoreError::RkvStoreError(err)
+    }
+}
+
+impl From<Utf8Error> for XULStoreError {
+    fn from(err: Utf8Error) -> XULStoreError {
+        XULStoreError::ConvertBytes(err)
+    }
+}
+
+impl From<IoError> for XULStoreError {
+    fn from(err: IoError) -> XULStoreError {
+        XULStoreError::IoError(err)
+    }
+}
+
+impl From<SerdeJsonError> for XULStoreError {
+    fn from(err: SerdeJsonError) -> XULStoreError {
+        XULStoreError::JsonError(err)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/src/ffi.rs
@@ -0,0 +1,339 @@
+/* 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/. */
+
+use crate as XULStore;
+use crate::{iter::XULStoreIterator, persist::clear_on_shutdown, statics::update_profile_dir};
+use libc::c_char;
+use nserror::{nsresult, NS_ERROR_NOT_IMPLEMENTED, NS_OK};
+use nsstring::{nsAString, nsString};
+use std::cell::RefCell;
+use std::ptr;
+use xpcom::{
+    interfaces::{nsIJSEnumerator, nsIStringEnumerator, nsISupports, nsIXULStore},
+    RefPtr,
+};
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_new_service(result: *mut *const nsIXULStore) {
+    let xul_store_service = XULStoreService::new();
+    RefPtr::new(xul_store_service.coerce::<nsIXULStore>()).forget(&mut *result);
+}
+
+#[derive(xpcom)]
+#[xpimplements(nsIXULStore)]
+#[refcnt = "atomic"]
+pub struct InitXULStoreService {}
+
+impl XULStoreService {
+    fn new() -> RefPtr<XULStoreService> {
+        XULStoreService::allocate(InitXULStoreService {})
+    }
+
+    xpcom_method!(
+        set_value => SetValue(
+            doc: *const nsAString,
+            id: *const nsAString,
+            attr: *const nsAString,
+            value: *const nsAString
+        )
+    );
+
+    fn set_value(
+        &self,
+        doc: &nsAString,
+        id: &nsAString,
+        attr: &nsAString,
+        value: &nsAString,
+    ) -> Result<(), nsresult> {
+        XULStore::set_value(doc, id, attr, value).map_err(|err| err.into())
+    }
+
+    xpcom_method!(
+        has_value => HasValue(
+            doc: *const nsAString,
+            id: *const nsAString,
+            attr: *const nsAString
+        ) -> bool
+    );
+
+    fn has_value(
+        &self,
+        doc: &nsAString,
+        id: &nsAString,
+        attr: &nsAString,
+    ) -> Result<bool, nsresult> {
+        XULStore::has_value(doc, id, attr).map_err(|err| err.into())
+    }
+
+    xpcom_method!(
+        get_value => GetValue(
+            doc: *const nsAString,
+            id: *const nsAString,
+            attr: *const nsAString
+        ) -> nsAString
+    );
+
+    fn get_value(
+        &self,
+        doc: &nsAString,
+        id: &nsAString,
+        attr: &nsAString,
+    ) -> Result<nsString, nsresult> {
+        match XULStore::get_value(doc, id, attr) {
+            Ok(val) => Ok(nsString::from(&val)),
+            Err(err) => Err(err.into()),
+        }
+    }
+
+    xpcom_method!(
+        remove_value => RemoveValue(
+            doc: *const nsAString,
+            id: *const nsAString,
+            attr: *const nsAString
+        )
+    );
+
+    fn remove_value(
+        &self,
+        doc: &nsAString,
+        id: &nsAString,
+        attr: &nsAString,
+    ) -> Result<(), nsresult> {
+        XULStore::remove_value(doc, id, attr).map_err(|err| err.into())
+    }
+
+    xpcom_method!(
+        remove_document => RemoveDocument(doc: *const nsAString)
+    );
+
+    fn remove_document(&self, doc: &nsAString) -> Result<(), nsresult> {
+        XULStore::remove_document(doc).map_err(|err| err.into())
+    }
+
+    xpcom_method!(
+        get_ids_enumerator => GetIDsEnumerator(
+            doc: *const nsAString
+        ) -> * const nsIStringEnumerator
+    );
+
+    fn get_ids_enumerator(&self, doc: &nsAString) -> Result<RefPtr<nsIStringEnumerator>, nsresult> {
+        match XULStore::get_ids(doc) {
+            Ok(val) => {
+                let enumerator = StringEnumerator::new(val);
+                Ok(RefPtr::new(enumerator.coerce::<nsIStringEnumerator>()))
+            }
+            Err(err) => Err(err.into()),
+        }
+    }
+
+    xpcom_method!(
+        get_attribute_enumerator => GetAttributeEnumerator(
+            doc: *const nsAString,
+            id: *const nsAString
+        ) -> * const nsIStringEnumerator
+    );
+
+    fn get_attribute_enumerator(
+        &self,
+        doc: &nsAString,
+        id: &nsAString,
+    ) -> Result<RefPtr<nsIStringEnumerator>, nsresult> {
+        match XULStore::get_attrs(doc, id) {
+            Ok(val) => {
+                let enumerator = StringEnumerator::new(val);
+                Ok(RefPtr::new(enumerator.coerce::<nsIStringEnumerator>()))
+            }
+            Err(err) => Err(err.into()),
+        }
+    }
+}
+
+#[derive(xpcom)]
+#[xpimplements(nsIStringEnumerator)]
+#[refcnt = "nonatomic"]
+pub(crate) struct InitStringEnumerator {
+    iter: RefCell<XULStoreIterator>,
+}
+impl StringEnumerator {
+    pub(crate) fn new(iter: XULStoreIterator) -> RefPtr<StringEnumerator> {
+        StringEnumerator::allocate(InitStringEnumerator {
+            iter: RefCell::new(iter),
+        })
+    }
+
+    xpcom_method!(string_iterator => StringIterator() -> *const nsIJSEnumerator);
+
+    fn string_iterator(&self) -> Result<RefPtr<nsIJSEnumerator>, nsresult> {
+        Err(NS_ERROR_NOT_IMPLEMENTED)
+    }
+
+    xpcom_method!(has_more => HasMore() -> bool);
+
+    fn has_more(&self) -> Result<bool, nsresult> {
+        let iter = self.iter.borrow();
+        Ok(iter.has_more())
+    }
+
+    xpcom_method!(get_next => GetNext() -> nsAString);
+
+    fn get_next(&self) -> Result<nsString, nsresult> {
+        let mut iter = self.iter.borrow_mut();
+        match iter.get_next() {
+            Ok(value) => Ok(nsString::from(&value)),
+            Err(err) => Err(err.into()),
+        }
+    }
+}
+
+#[derive(xpcom)]
+#[xpimplements(nsIObserver)]
+#[refcnt = "nonatomic"]
+pub(crate) struct InitProfileChangeObserver {}
+impl ProfileChangeObserver {
+    #[allow(non_snake_case)]
+    unsafe fn Observe(
+        &self,
+        _subject: *const nsISupports,
+        _topic: *const c_char,
+        _data: *const i16,
+    ) -> nsresult {
+        update_profile_dir();
+        NS_OK
+    }
+
+    pub(crate) fn new() -> RefPtr<ProfileChangeObserver> {
+        ProfileChangeObserver::allocate(InitProfileChangeObserver {})
+    }
+}
+
+#[derive(xpcom)]
+#[xpimplements(nsIObserver)]
+#[refcnt = "nonatomic"]
+pub(crate) struct InitXpcomShutdownObserver {}
+impl XpcomShutdownObserver {
+    #[allow(non_snake_case)]
+    unsafe fn Observe(
+        &self,
+        _subject: *const nsISupports,
+        _topic: *const c_char,
+        _data: *const i16,
+    ) -> nsresult {
+        clear_on_shutdown();
+        NS_OK
+    }
+
+    pub(crate) fn new() -> RefPtr<XpcomShutdownObserver> {
+        XpcomShutdownObserver::allocate(InitXpcomShutdownObserver {})
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_set_value(
+    doc: &nsAString,
+    id: &nsAString,
+    attr: &nsAString,
+    value: &nsAString,
+) -> nsresult {
+    XULStore::set_value(doc, id, attr, value).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_has_value(
+    doc: &nsAString,
+    id: &nsAString,
+    attr: &nsAString,
+    has_value: *mut bool,
+) -> nsresult {
+    match XULStore::has_value(doc, id, attr) {
+        Ok(val) => {
+            *has_value = val;
+            NS_OK
+        }
+        Err(err) => err.into(),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_get_value(
+    doc: &nsAString,
+    id: &nsAString,
+    attr: &nsAString,
+    value: *mut nsAString,
+) -> nsresult {
+    match XULStore::get_value(doc, id, attr) {
+        Ok(val) => {
+            (*value).assign(&nsString::from(&val));
+            NS_OK
+        }
+        Err(err) => err.into(),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_remove_value(
+    doc: &nsAString,
+    id: &nsAString,
+    attr: &nsAString,
+) -> nsresult {
+    XULStore::remove_value(doc, id, attr).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_get_ids(
+    doc: &nsAString,
+    result: *mut nsresult,
+) -> *mut XULStoreIterator {
+    match XULStore::get_ids(doc) {
+        Ok(iter) => {
+            *result = NS_OK;
+            Box::into_raw(Box::new(iter))
+        }
+        Err(err) => {
+            *result = err.into();
+            ptr::null_mut()
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_get_attrs(
+    doc: &nsAString,
+    id: &nsAString,
+    result: *mut nsresult,
+) -> *mut XULStoreIterator {
+    match XULStore::get_attrs(doc, id) {
+        Ok(iter) => {
+            *result = NS_OK;
+            Box::into_raw(Box::new(iter))
+        }
+        Err(err) => {
+            *result = err.into();
+            ptr::null_mut()
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_iter_has_more(iter: &XULStoreIterator) -> bool {
+    iter.has_more()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_iter_get_next(
+    iter: &mut XULStoreIterator,
+    value: *mut nsAString,
+) -> nsresult {
+    match iter.get_next() {
+        Ok(val) => {
+            (*value).assign(&nsString::from(&val));
+            NS_OK
+        }
+        Err(err) => err.into(),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn xulstore_iter_free(iter: *mut XULStoreIterator) {
+    drop(Box::from_raw(iter));
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/src/iter.rs
@@ -0,0 +1,24 @@
+/* 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/. */
+
+use crate::error::{XULStoreError, XULStoreResult};
+use std::vec::IntoIter;
+
+pub struct XULStoreIterator {
+    values: IntoIter<String>,
+}
+
+impl XULStoreIterator {
+    pub(crate) fn new(values: IntoIter<String>) -> Self {
+        Self { values }
+    }
+
+    pub(crate) fn has_more(&self) -> bool {
+        !self.values.as_slice().is_empty()
+    }
+
+    pub(crate) fn get_next(&mut self) -> XULStoreResult<String> {
+        Ok(self.values.next().ok_or(XULStoreError::IterationFinished)?)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/src/lib.rs
@@ -0,0 +1,221 @@
+/* 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/. */
+
+extern crate crossbeam_utils;
+#[macro_use]
+extern crate cstr;
+#[macro_use]
+extern crate failure;
+#[macro_use]
+extern crate lazy_static;
+extern crate libc;
+extern crate lmdb;
+#[macro_use]
+extern crate log;
+extern crate moz_task;
+extern crate nserror;
+extern crate nsstring;
+extern crate rkv;
+extern crate serde_json;
+#[macro_use]
+extern crate xpcom;
+
+mod error;
+mod ffi;
+mod iter;
+mod persist;
+mod statics;
+
+use crate::{
+    error::{XULStoreError, XULStoreResult},
+    iter::XULStoreIterator,
+    persist::persist,
+    statics::DATA_CACHE,
+};
+use nsstring::nsAString;
+use std::collections::btree_map::Entry;
+use std::fmt::Display;
+
+const SEPARATOR: char = '\u{0009}';
+
+pub(crate) fn make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String {
+    format!("{}{}{}{}{}", doc, SEPARATOR, id, SEPARATOR, attr)
+}
+
+pub(crate) fn set_value(
+    doc: &nsAString,
+    id: &nsAString,
+    attr: &nsAString,
+    value: &nsAString,
+) -> XULStoreResult<()> {
+    debug!("XULStore set value: {} {} {} {}", doc, id, attr, value);
+
+    // bug 319846 -- don't save really long attributes or values.
+    if id.len() > 512 || attr.len() > 512 {
+        return Err(XULStoreError::IdAttrNameTooLong);
+    }
+
+    let value = if value.len() > 4096 {
+        warn!("XULStore: truncating long attribute value");
+        String::from_utf16(&value[0..4096])?
+    } else {
+        String::from_utf16(value)?
+    };
+
+    let mut cache_guard = DATA_CACHE.lock()?;
+    let data = match cache_guard.as_mut() {
+        Some(data) => data,
+        None => return Ok(()),
+    };
+    data.entry(doc.to_string())
+        .or_default()
+        .entry(id.to_string())
+        .or_default()
+        .insert(attr.to_string(), value.clone());
+
+    persist(make_key(doc, id, attr), Some(value))?;
+
+    Ok(())
+}
+
+pub(crate) fn has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool> {
+    debug!("XULStore has value: {} {} {}", doc, id, attr);
+
+    let cache_guard = DATA_CACHE.lock()?;
+    let data = match cache_guard.as_ref() {
+        Some(data) => data,
+        None => return Ok(false),
+    };
+
+    match data.get(&doc.to_string()) {
+        Some(ids) => match ids.get(&id.to_string()) {
+            Some(attrs) => Ok(attrs.contains_key(&attr.to_string())),
+            None => Ok(false),
+        },
+        None => Ok(false),
+    }
+}
+
+pub(crate) fn get_value(
+    doc: &nsAString,
+    id: &nsAString,
+    attr: &nsAString,
+) -> XULStoreResult<String> {
+    debug!("XULStore get value {} {} {}", doc, id, attr);
+
+    let cache_guard = DATA_CACHE.lock()?;
+    let data = match cache_guard.as_ref() {
+        Some(data) => data,
+        None => return Ok(String::new()),
+    };
+
+    match data.get(&doc.to_string()) {
+        Some(ids) => match ids.get(&id.to_string()) {
+            Some(attrs) => match attrs.get(&attr.to_string()) {
+                Some(value) => Ok(value.clone()),
+                None => Ok(String::new()),
+            },
+            None => Ok(String::new()),
+        },
+        None => Ok(String::new()),
+    }
+}
+
+pub(crate) fn remove_value(
+    doc: &nsAString,
+    id: &nsAString,
+    attr: &nsAString,
+) -> XULStoreResult<()> {
+    debug!("XULStore remove value {} {} {}", doc, id, attr);
+
+    let mut cache_guard = DATA_CACHE.lock()?;
+    let data = match cache_guard.as_mut() {
+        Some(data) => data,
+        None => return Ok(()),
+    };
+
+    let mut ids_empty = false;
+    if let Some(ids) = data.get_mut(&doc.to_string()) {
+        let mut attrs_empty = false;
+        if let Some(attrs) = ids.get_mut(&id.to_string()) {
+            attrs.remove(&attr.to_string());
+            if attrs.is_empty() {
+                attrs_empty = true;
+            }
+        }
+        if attrs_empty {
+            ids.remove(&id.to_string());
+            if ids.is_empty() {
+                ids_empty = true;
+            }
+        }
+    };
+    if ids_empty {
+        data.remove(&doc.to_string());
+    }
+
+    persist(make_key(doc, id, attr), None)?;
+
+    Ok(())
+}
+
+pub(crate) fn remove_document(doc: &nsAString) -> XULStoreResult<()> {
+    debug!("XULStore remove document {}", doc);
+
+    let mut cache_guard = DATA_CACHE.lock()?;
+    let data = match cache_guard.as_mut() {
+        Some(data) => data,
+        None => return Ok(()),
+    };
+
+    if let Entry::Occupied(entry) = data.entry(doc.to_string()) {
+        for (id, attrs) in entry.get() {
+            for attr in attrs.keys() {
+                persist(make_key(entry.key(), id, attr), None)?;
+            }
+        }
+        entry.remove_entry();
+    }
+
+    Ok(())
+}
+
+pub(crate) fn get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator> {
+    debug!("XULStore get IDs for {}", doc);
+
+    let cache_guard = DATA_CACHE.lock()?;
+    let data = match cache_guard.as_ref() {
+        Some(data) => data,
+        None => return Ok(XULStoreIterator::new(vec![].into_iter())),
+    };
+
+    match data.get(&doc.to_string()) {
+        Some(ids) => {
+            let mut ids: Vec<String> = ids.keys().cloned().collect();
+            Ok(XULStoreIterator::new(ids.into_iter()))
+        }
+        None => Ok(XULStoreIterator::new(vec![].into_iter())),
+    }
+}
+
+pub(crate) fn get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator> {
+    debug!("XULStore get attrs for doc, ID: {} {}", doc, id);
+
+    let cache_guard = DATA_CACHE.lock()?;
+    let data = match cache_guard.as_ref() {
+        Some(data) => data,
+        None => return Ok(XULStoreIterator::new(vec![].into_iter())),
+    };
+
+    match data.get(&doc.to_string()) {
+        Some(ids) => match ids.get(&id.to_string()) {
+            Some(attrs) => {
+                let mut attrs: Vec<String> = attrs.keys().cloned().collect();
+                Ok(XULStoreIterator::new(attrs.into_iter()))
+            }
+            None => Ok(XULStoreIterator::new(vec![].into_iter())),
+        },
+        None => Ok(XULStoreIterator::new(vec![].into_iter())),
+    }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/src/persist.rs
@@ -0,0 +1,188 @@
+/* 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/. */
+
+use crate::{
+    error::{XULStoreError, XULStoreResult},
+    ffi::XpcomShutdownObserver,
+    statics::get_database,
+};
+use crossbeam_utils::atomic::AtomicCell;
+use lmdb::Error as LmdbError;
+use moz_task::{create_thread, Task, TaskRunnable};
+use nserror::nsresult;
+use rkv::{StoreError as RkvStoreError, Value};
+use std::{collections::HashMap, sync::Mutex, thread::sleep, time::Duration};
+use xpcom::{interfaces::nsIThread, RefPtr, ThreadBoundRefPtr};
+
+/// The XULStore API is synchronous for both C++ and JS consumers and accessed
+/// on the main thread, so we persist its data to disk on a background thread
+/// to avoid janking the UI.
+///
+/// We also re-open the database each time we write to it in order to conserve
+/// heap memory, since holding a database connection open would consume at least
+/// 3MB of heap memory in perpetuity.
+///
+/// Since re-opening the database repeatedly to write individual changes can be
+/// expensive when there are many of them in quick succession, we batch changes
+/// and write them in batches.
+
+lazy_static! {
+    /// A map of key/value pairs to persist.  Values are Options so we can
+    /// use the same structure for both puts and deletes, with a `None` value
+    /// identifying a key that should be deleted from the database.
+    ///
+    /// This is a map rather than a sequence in order to merge consecutive
+    /// changes to the same key, i.e. when a consumer sets *foo* to `bar`
+    /// and then sets it again to `baz` before we persist the first change.
+    ///
+    /// In that case, there's no point in setting *foo* to `bar` before we set
+    /// it to `baz`, and the map ensures we only ever persist the latest value
+    /// for any given key.
+    static ref CHANGES: Mutex<Option<HashMap<String, Option<String>>>> = { Mutex::new(None) };
+
+    /// A Mutex that prevents two PersistTasks from running at the same time,
+    /// since each task opens the database, and we need to ensure there is only
+    /// one open database handle for the database at any given time.
+    static ref PERSIST: Mutex<()> = { Mutex::new(()) };
+
+    static ref THREAD: Mutex<Option<ThreadBoundRefPtr<nsIThread>>> = {
+        let thread: RefPtr<nsIThread> = match create_thread("XULStore") {
+            Ok(thread) => thread,
+            Err(err) => {
+                error!("error creating XULStore thread: {}", err);
+                return Mutex::new(None);
+            }
+        };
+
+        // Observe XPCOM shutdown so we can clear the thread and thus not
+        // "leak" it (from the perspective of the leak checker).
+        observe_xpcom_shutdown();
+
+        Mutex::new(Some(ThreadBoundRefPtr::new(thread)))
+    };
+}
+
+fn observe_xpcom_shutdown() {
+    (|| -> XULStoreResult<()> {
+        let obs_svc = xpcom::services::get_ObserverService().ok_or(XULStoreError::Unavailable)?;
+        let observer = XpcomShutdownObserver::new();
+        unsafe {
+            obs_svc
+                .AddObserver(observer.coerce(), cstr!("xpcom-shutdown").as_ptr(), false)
+                .to_result()?
+        };
+        Ok(())
+    })()
+    .unwrap_or_else(|err| error!("error observing XPCOM shutdown: {}", err));
+}
+
+pub(crate) fn clear_on_shutdown() {
+    (|| -> XULStoreResult<()> {
+        THREAD.lock()?.take();
+        Ok(())
+    })()
+    .unwrap_or_else(|err| error!("error clearing thread: {}", err));
+}
+
+pub(crate) fn persist(key: String, value: Option<String>) -> XULStoreResult<()> {
+    let mut changes = CHANGES.lock()?;
+
+    if changes.is_none() {
+        *changes = Some(HashMap::new());
+
+        // If *changes* was `None`, then this is the first change since
+        // the last time we persisted, so dispatch a new PersistTask.
+        let task = Box::new(PersistTask::new());
+        let thread_guard = THREAD.lock()?;
+        let thread = thread_guard
+            .as_ref()
+            .ok_or(XULStoreError::Unavailable)?
+            .get_ref()
+            .ok_or(XULStoreError::Unavailable)?;
+        TaskRunnable::new("XULStore::Persist", task)?.dispatch(thread)?;
+    }
+
+    // Now insert the key/value pair into the map.  The unwrap() call here
+    // should never panic, since the code above sets `writes` to a Some(HashMap)
+    // if it's None.
+    changes.as_mut().unwrap().insert(key, value);
+
+    Ok(())
+}
+
+pub struct PersistTask {
+    result: AtomicCell<Option<Result<(), XULStoreError>>>,
+}
+
+impl PersistTask {
+    pub fn new() -> PersistTask {
+        PersistTask {
+            result: AtomicCell::default(),
+        }
+    }
+}
+
+impl Task for PersistTask {
+    fn run(&self) {
+        self.result.store(Some(|| -> Result<(), XULStoreError> {
+            // Avoid persisting too often.  We might want to adjust this value
+            // in the future to trade durability for performance.
+            sleep(Duration::from_millis(200));
+
+            // Prevent another PersistTask from running until this one finishes.
+            // We do this before getting the database to ensure that there is
+            // only ever one open database handle at a given time.
+            let _lock = PERSIST.lock()?;
+
+            let db = get_database()?;
+            let mut writer = db.env.write()?;
+
+            // Get the map of key/value pairs from the mutex, replacing it
+            // with None.  To avoid janking the main thread (if it decides
+            // to makes more changes while we're persisting to disk), we only
+            // lock the map long enough to move it out of the Mutex.
+            let writes = CHANGES.lock()?.take();
+
+            // The Option should be a Some(HashMap) (otherwise the task
+            // shouldn't have been scheduled in the first place).  If it's None,
+            // unexpectedly, then we return an error early.
+            let writes = writes.ok_or(XULStoreError::Unavailable)?;
+
+            for (key, value) in writes.iter() {
+                match value {
+                    Some(val) => db.store.put(&mut writer, &key, &Value::Str(val))?,
+                    None => {
+                        match db.store.delete(&mut writer, &key) {
+                            Ok(_) => (),
+
+                            // The XULStore API doesn't care if a consumer tries
+                            // to remove a value that doesn't exist in the store,
+                            // so we ignore the error (although in this case the key
+                            // should exist, since it was in the cache!).
+                            Err(RkvStoreError::LmdbError(LmdbError::NotFound)) => {
+                                warn!("tried to remove key that isn't in the store");
+                            }
+
+                            Err(err) => return Err(err.into()),
+                        }
+                    }
+                }
+            }
+
+            writer.commit()?;
+
+            Ok(())
+        }()));
+    }
+
+    fn done(&self) -> Result<(), nsresult> {
+        match self.result.swap(None) {
+            Some(Ok(())) => (),
+            Some(Err(err)) => error!("removeDocument error: {}", err),
+            None => error!("removeDocument error: unexpected result"),
+        };
+
+        Ok(())
+    }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/src/statics.rs
@@ -0,0 +1,249 @@
+/* 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/. */
+
+use crate::{
+    error::{XULStoreError, XULStoreResult},
+    ffi::ProfileChangeObserver,
+    make_key, SEPARATOR,
+};
+use moz_task::is_main_thread;
+use nsstring::nsString;
+use rkv::{Rkv, SingleStore, StoreOptions, Value};
+use std::{
+    collections::BTreeMap,
+    fs::{create_dir_all, remove_file, File},
+    path::PathBuf,
+    str,
+    sync::Mutex,
+};
+use xpcom::{interfaces::nsIFile, XpCom};
+
+type XULStoreCache = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
+
+pub struct Database {
+    pub env: Rkv,
+    pub store: SingleStore,
+}
+
+impl Database {
+    fn new(env: Rkv, store: SingleStore) -> Database {
+        Database { env, store }
+    }
+}
+
+lazy_static! {
+    static ref PROFILE_DIR: Mutex<Option<PathBuf>> = {
+        observe_profile_change();
+        Mutex::new(get_profile_dir().ok())
+    };
+    pub(crate) static ref DATA_CACHE: Mutex<Option<XULStoreCache>> =
+        { Mutex::new(cache_data().ok()) };
+}
+
+pub(crate) fn get_database() -> XULStoreResult<Database> {
+    let xulstore_dir = get_xulstore_dir()?;
+    let env = Rkv::new(xulstore_dir.as_path())?;
+    let store = env.open_single("db", StoreOptions::create())?;
+
+    Ok(Database::new(env, store))
+}
+
+pub(crate) fn update_profile_dir() {
+    // Failure to update the dir isn't fatal (although it means that we won't
+    // persist XULStore data for this session), so we don't return a result.
+    // But we use a closure returning a result to enable use of the ? operator.
+    (|| -> XULStoreResult<()> {
+        {
+            let mut profile_dir_guard = PROFILE_DIR.lock()?;
+            *profile_dir_guard = get_profile_dir().ok();
+        }
+
+        let mut cache_guard = DATA_CACHE.lock()?;
+        *cache_guard = cache_data().ok();
+
+        Ok(())
+    })()
+    .unwrap_or_else(|err| error!("error updating profile dir: {}", err));
+}
+
+fn get_profile_dir() -> XULStoreResult<PathBuf> {
+    // We can't use getter_addrefs() here because get_DirectoryService()
+    // returns its nsIProperties interface, and its Get() method returns
+    // a directory via its nsQIResult out param, which gets translated to
+    // a `*mut *mut libc::c_void` in Rust, whereas getter_addrefs() expects
+    // a closure with a `*mut *const T` parameter.
+
+    let dir_svc = xpcom::services::get_DirectoryService().ok_or(XULStoreError::Unavailable)?;
+    let mut profile_dir = xpcom::GetterAddrefs::<nsIFile>::new();
+    unsafe {
+        dir_svc
+            .Get(
+                cstr!("ProfD").as_ptr(),
+                &nsIFile::IID,
+                profile_dir.void_ptr(),
+            )
+            .to_result()
+            .or_else(|_| {
+                dir_svc
+                    .Get(
+                        cstr!("ProfDS").as_ptr(),
+                        &nsIFile::IID,
+                        profile_dir.void_ptr(),
+                    )
+                    .to_result()
+            })?;
+    }
+    let profile_dir = profile_dir.refptr().ok_or(XULStoreError::Unavailable)?;
+
+    let mut profile_path = nsString::new();
+    unsafe {
+        profile_dir.GetPath(&mut *profile_path).to_result()?;
+    }
+
+    let path = String::from_utf16(&profile_path[..])?;
+    Ok(PathBuf::from(&path))
+}
+
+fn get_xulstore_dir() -> XULStoreResult<PathBuf> {
+    let mut xulstore_dir = PROFILE_DIR
+        .lock()?
+        .clone()
+        .ok_or(XULStoreError::Unavailable)?;
+    xulstore_dir.push("xulstore");
+
+    create_dir_all(xulstore_dir.clone())?;
+
+    Ok(xulstore_dir)
+}
+
+fn observe_profile_change() {
+    assert!(is_main_thread());
+
+    // Failure to observe the change isn't fatal (although it means we won't
+    // persist XULStore data for this session), so we don't return a result.
+    // But we use a closure returning a result to enable use of the ? operator.
+    (|| -> XULStoreResult<()> {
+        // Observe profile changes so we can update this directory accordingly.
+        let obs_svc = xpcom::services::get_ObserverService().ok_or(XULStoreError::Unavailable)?;
+        let observer = ProfileChangeObserver::new();
+        unsafe {
+            obs_svc
+                .AddObserver(
+                    observer.coerce(),
+                    cstr!("profile-after-change").as_ptr(),
+                    false,
+                )
+                .to_result()?
+        };
+        Ok(())
+    })()
+    .unwrap_or_else(|err| error!("error observing profile change: {}", err));
+}
+
+fn in_safe_mode() -> XULStoreResult<bool> {
+    let app_info_svc = xpcom::services::get_AppInfoService().ok_or(XULStoreError::Unavailable)?;
+    let mut in_safe_mode = false;
+    unsafe {
+        app_info_svc.GetInSafeMode(&mut in_safe_mode).to_result()?;
+    }
+    Ok(in_safe_mode)
+}
+
+fn cache_data() -> XULStoreResult<XULStoreCache> {
+    let db = get_database()?;
+    maybe_migrate_data(&db.env, db.store);
+
+    let mut all = XULStoreCache::default();
+    if in_safe_mode()? {
+        return Ok(all);
+    }
+
+    let reader = db.env.read()?;
+    let iterator = db.store.iter_start(&reader)?;
+
+    for result in iterator {
+        let (key, value): (&str, String) = match result {
+            Ok((key, value)) => {
+                assert!(value.is_some(), "iterated key has value");
+                match (str::from_utf8(&key), unwrap_value(&value)) {
+                    (Ok(key), Ok(value)) => (key, value),
+                    (Err(err), _) => return Err(err.into()),
+                    (_, Err(err)) => return Err(err),
+                }
+            }
+            Err(err) => return Err(err.into()),
+        };
+
+        let parts = key.split(SEPARATOR).collect::<Vec<&str>>();
+        if parts.len() != 3 {
+            return Err(XULStoreError::UnexpectedKey(key.to_string()));
+        }
+        let (doc, id, attr) = (
+            parts[0].to_string(),
+            parts[1].to_string(),
+            parts[2].to_string(),
+        );
+
+        all.entry(doc)
+            .or_default()
+            .entry(id)
+            .or_default()
+            .entry(attr)
+            .or_insert(value);
+    }
+
+    Ok(all)
+}
+
+fn maybe_migrate_data(env: &Rkv, store: SingleStore) {
+    // Failure to migrate data isn't fatal, so we don't return a result.
+    // But we use a closure returning a result to enable use of the ? operator.
+    (|| -> XULStoreResult<()> {
+        let mut old_datastore = PROFILE_DIR
+            .lock()?
+            .clone()
+            .ok_or(XULStoreError::Unavailable)?;
+        old_datastore.push("xulstore.json");
+        if !old_datastore.exists() {
+            debug!("old datastore doesn't exist: {:?}", old_datastore);
+            return Ok(());
+        }
+
+        let file = File::open(old_datastore.clone())?;
+        let json: XULStoreCache = serde_json::from_reader(file)?;
+
+        let mut writer = env.write()?;
+
+        for (doc, ids) in json {
+            for (id, attrs) in ids {
+                for (attr, value) in attrs {
+                    let key = make_key(&doc, &id, &attr);
+                    store.put(&mut writer, &key, &Value::Str(&value))?;
+                }
+            }
+        }
+
+        writer.commit()?;
+
+        remove_file(old_datastore)?;
+
+        Ok(())
+    })()
+    .unwrap_or_else(|err| error!("error migrating data: {}", err));
+}
+
+fn unwrap_value(value: &Option<Value>) -> XULStoreResult<String> {
+    match value {
+        Some(Value::Str(val)) => Ok(val.to_string()),
+
+        // Per the XULStore API, return an empty string if the value
+        // isn't found.
+        None => Ok(String::new()),
+
+        // This should never happen, but it could happen in theory
+        // if someone writes a different kind of value into the store
+        // using a more general API (kvstore, rkv, LMDB).
+        Some(_) => Err(XULStoreError::UnexpectedValue),
+    }
+}
--- a/toolkit/components/xulstore/tests/chrome/window_persistence.xul
+++ b/toolkit/components/xulstore/tests/chrome/window_persistence.xul
@@ -8,17 +8,17 @@
         persist="screenX screenY width height">
 
 <button id="button1" label="Button1" persist="value"/>
 <button id="button2" label="Button2" value="Normal" persist="value"/>
 
 <script>
 <![CDATA[
 
-let XULStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
+const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
 let URI = "chrome://mochitests/content/chrome/toolkit/components/xulstore/tests/chrome/window_persistence.xul";
 
 function opened()
 {
   runTest();
 }
 
 function runTest()
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/gtest/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "xulstore-gtest"
+version = "0.1.0"
+authors = ["nobody@mozilla.org"]
+
+[lib]
+path = "test.rs"
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/gtest/TestXULStore.cpp
@@ -0,0 +1,141 @@
+#include <stdint.h>
+#include "gtest/gtest.h"
+#include "mozilla/XULStore.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+using mozilla::XULStoreIterator;
+using mozilla::XULStore::GetAttrs;
+using mozilla::XULStore::GetIDs;
+using mozilla::XULStore::GetValue;
+using mozilla::XULStore::HasValue;
+using mozilla::XULStore::RemoveValue;
+using mozilla::XULStore::SetValue;
+
+TEST(XULStore, SetGetValue)
+{
+  nsAutoString doc(NS_LITERAL_STRING("SetGetValue"));
+  nsAutoString id(NS_LITERAL_STRING("foo"));
+  nsAutoString attr(NS_LITERAL_STRING("bar"));
+  nsAutoString value;
+
+  EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
+  EXPECT_TRUE(value.EqualsASCII(""));
+
+  {
+    nsAutoString value(NS_LITERAL_STRING("baz"));
+    EXPECT_EQ(SetValue(doc, id, attr, value), NS_OK);
+  }
+
+  EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
+  EXPECT_TRUE(value.EqualsASCII("baz"));
+}
+
+TEST(XULStore, HasValue)
+{
+  nsAutoString doc(NS_LITERAL_STRING("HasValue"));
+  nsAutoString id(NS_LITERAL_STRING("foo"));
+  nsAutoString attr(NS_LITERAL_STRING("bar"));
+  bool hasValue = true;
+  EXPECT_EQ(HasValue(doc, id, attr, hasValue), NS_OK);
+  EXPECT_FALSE(hasValue);
+  nsAutoString value(NS_LITERAL_STRING("baz"));
+  EXPECT_EQ(SetValue(doc, id, attr, value), NS_OK);
+  EXPECT_EQ(HasValue(doc, id, attr, hasValue), NS_OK);
+  EXPECT_TRUE(hasValue);
+}
+
+TEST(XULStore, RemoveValue)
+{
+  nsAutoString doc(NS_LITERAL_STRING("RemoveValue"));
+  nsAutoString id(NS_LITERAL_STRING("foo"));
+  nsAutoString attr(NS_LITERAL_STRING("bar"));
+  nsAutoString value(NS_LITERAL_STRING("baz"));
+  EXPECT_EQ(SetValue(doc, id, attr, value), NS_OK);
+  EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
+  EXPECT_TRUE(value.EqualsASCII("baz"));
+  EXPECT_EQ(RemoveValue(doc, id, attr), NS_OK);
+  EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
+  EXPECT_TRUE(value.EqualsASCII(""));
+}
+
+TEST(XULStore, GetIDsIterator)
+{
+  nsAutoString doc(NS_LITERAL_STRING("idIterDoc"));
+  nsAutoString id1(NS_LITERAL_STRING("id1"));
+  nsAutoString id2(NS_LITERAL_STRING("id2"));
+  nsAutoString id3(NS_LITERAL_STRING("id3"));
+  nsAutoString attr(NS_LITERAL_STRING("attr"));
+  nsAutoString value(NS_LITERAL_STRING("value"));
+  nsAutoString id;
+
+  // Confirm that the store doesn't have any IDs yet.
+  mozilla::UniquePtr<XULStoreIterator> iter;
+  EXPECT_EQ(GetIDs(doc, iter), NS_OK);
+  EXPECT_FALSE(iter->HasMore());
+  // EXPECT_EQ(iter->GetNext(&id), NS_ERROR_FAILURE);
+
+  // Insert with IDs in non-alphanumeric order to confirm
+  // that store will order them when iterating them.
+  EXPECT_EQ(SetValue(doc, id3, attr, value), NS_OK);
+  EXPECT_EQ(SetValue(doc, id1, attr, value), NS_OK);
+  EXPECT_EQ(SetValue(doc, id2, attr, value), NS_OK);
+
+  // Insert different ID for another doc to confirm that store
+  // won't return it when iterating IDs for our doc.
+  nsAutoString otherDoc(NS_LITERAL_STRING("otherDoc"));
+  nsAutoString otherID(NS_LITERAL_STRING("otherID"));
+  EXPECT_EQ(SetValue(otherDoc, otherID, attr, value), NS_OK);
+
+  EXPECT_EQ(GetIDs(doc, iter), NS_OK);
+  EXPECT_TRUE(iter->HasMore());
+  EXPECT_EQ(iter->GetNext(&id), NS_OK);
+  EXPECT_TRUE(id.EqualsASCII("id1"));
+  EXPECT_TRUE(iter->HasMore());
+  EXPECT_EQ(iter->GetNext(&id), NS_OK);
+  EXPECT_TRUE(id.EqualsASCII("id2"));
+  EXPECT_TRUE(iter->HasMore());
+  EXPECT_EQ(iter->GetNext(&id), NS_OK);
+  EXPECT_TRUE(id.EqualsASCII("id3"));
+  EXPECT_FALSE(iter->HasMore());
+}
+
+TEST(XULStore, GetAttributeIterator)
+{
+  nsAutoString doc(NS_LITERAL_STRING("attrIterDoc"));
+  nsAutoString id(NS_LITERAL_STRING("id"));
+  nsAutoString attr1(NS_LITERAL_STRING("attr1"));
+  nsAutoString attr2(NS_LITERAL_STRING("attr2"));
+  nsAutoString attr3(NS_LITERAL_STRING("attr3"));
+  nsAutoString value(NS_LITERAL_STRING("value"));
+  nsAutoString attr;
+
+  mozilla::UniquePtr<XULStoreIterator> iter;
+  EXPECT_EQ(GetAttrs(doc, id, iter), NS_OK);
+  EXPECT_FALSE(iter->HasMore());
+  // EXPECT_EQ(iter->GetNext(&attr), NS_ERROR_FAILURE);
+
+  // Insert with attributes in non-alphanumeric order to confirm
+  // that store will order them when iterating them.
+  EXPECT_EQ(SetValue(doc, id, attr3, value), NS_OK);
+  EXPECT_EQ(SetValue(doc, id, attr1, value), NS_OK);
+  EXPECT_EQ(SetValue(doc, id, attr2, value), NS_OK);
+
+  // Insert different attribute for another ID to confirm that store
+  // won't return it when iterating attributes for our ID.
+  nsAutoString otherID(NS_LITERAL_STRING("otherID"));
+  nsAutoString otherAttr(NS_LITERAL_STRING("otherAttr"));
+  EXPECT_EQ(SetValue(doc, otherID, otherAttr, value), NS_OK);
+
+  EXPECT_EQ(GetAttrs(doc, id, iter), NS_OK);
+  EXPECT_TRUE(iter->HasMore());
+  EXPECT_EQ(iter->GetNext(&attr), NS_OK);
+  EXPECT_TRUE(attr.EqualsASCII("attr1"));
+  EXPECT_TRUE(iter->HasMore());
+  EXPECT_EQ(iter->GetNext(&attr), NS_OK);
+  EXPECT_TRUE(attr.EqualsASCII("attr2"));
+  EXPECT_TRUE(iter->HasMore());
+  EXPECT_EQ(iter->GetNext(&attr), NS_OK);
+  EXPECT_TRUE(attr.EqualsASCII("attr3"));
+  EXPECT_FALSE(iter->HasMore());
+}
copy from toolkit/components/xulstore/components.conf
copy to toolkit/components/xulstore/tests/gtest/moz.build
--- a/toolkit/components/xulstore/components.conf
+++ b/toolkit/components/xulstore/tests/gtest/moz.build
@@ -1,14 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-Classes = [
-    {
-        'cid': '{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}',
-        'contract_ids': ['@mozilla.org/xul/xulstore;1'],
-        'jsm': 'resource://gre/modules/XULStore.jsm',
-        'constructor': 'XULStore',
-    },
+UNIFIED_SOURCES += [
+    'TestXULStore.cpp',
 ]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
+    CXXFLAGS += ['-Wno-error=shadow']
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/xpcshell/test_XULStore_migration.js
@@ -0,0 +1,71 @@
+"use strict";
+
+const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+
+function run_test() {
+  do_get_profile();
+  run_next_test();
+}
+
+add_task(async function test_create_old_datastore() {
+  const path = OS.Path.join(OS.Constants.Path.profileDir, "xulstore.json");
+
+  const xulstoreJSON = {
+    doc1: {
+      id1: {
+        attr1: "value1",
+      },
+    },
+    doc2: {
+      id1: {
+        attr2: "value2",
+      },
+      id2: {
+        attr1: "value1",
+        attr2: "value2",
+        attr3: "value3",
+      },
+      id3: {},
+    },
+    doc3: {},
+  };
+
+  await OS.File.writeAtomic(path, JSON.stringify(xulstoreJSON));
+});
+
+add_task(async function test_get_values() {
+  // We wait until now to import XULStore.jsm to ensure we've created
+  // the old datastore, as importing that module will initiate the attempt
+  // to migrate the old datastore to the new one.
+  const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
+
+  Assert.equal(await XULStore.getValue("doc1", "id1", "attr1"), "value1");
+  Assert.equal(await XULStore.getValue("doc1", "id1", "attr2"), "");
+  Assert.equal(await XULStore.getValue("doc1", "id1", "attr3"), "");
+  Assert.equal(await XULStore.getValue("doc1", "id2", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc1", "id2", "attr2"), "");
+  Assert.equal(await XULStore.getValue("doc1", "id2", "attr3"), "");
+  Assert.equal(await XULStore.getValue("doc1", "id3", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc1", "id3", "attr2"), "");
+  Assert.equal(await XULStore.getValue("doc1", "id3", "attr3"), "");
+
+  Assert.equal(await XULStore.getValue("doc2", "id1", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc2", "id1", "attr2"), "value2");
+  Assert.equal(await XULStore.getValue("doc2", "id1", "attr3"), "");
+  Assert.equal(await XULStore.getValue("doc2", "id2", "attr1"), "value1");
+  Assert.equal(await XULStore.getValue("doc2", "id2", "attr2"), "value2");
+  Assert.equal(await XULStore.getValue("doc2", "id2", "attr3"), "value3");
+  Assert.equal(await XULStore.getValue("doc2", "id3", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc2", "id3", "attr2"), "");
+  Assert.equal(await XULStore.getValue("doc2", "id3", "attr3"), "");
+
+  Assert.equal(await XULStore.getValue("doc3", "id1", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id1", "attr2"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id1", "attr3"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id2", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id2", "attr2"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id2", "attr3"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id3", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id3", "attr2"), "");
+  Assert.equal(await XULStore.getValue("doc3", "id3", "attr3"), "");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/xpcshell/test_XULStore_migration_fail_invalid_data.js
@@ -0,0 +1,43 @@
+"use strict";
+
+const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+
+function run_test() {
+  do_get_profile();
+  run_next_test();
+}
+
+add_task(async function test_create_old_datastore() {
+  const path = OS.Path.join(OS.Constants.Path.profileDir, "xulstore.json");
+
+  // Valid JSON, but invalid data: attr1's value is a number, not a string.
+  const xulstoreJSON = {
+    doc1: {
+      id1: {
+        attr1: 1,
+      },
+    },
+    doc2: {
+      id2: {
+        attr2: "value2",
+      },
+    },
+  };
+
+  await OS.File.writeAtomic(path, JSON.stringify(xulstoreJSON));
+});
+
+add_task(async function test_get_values() {
+  // We wait until now to import XULStore.jsm to ensure we've created
+  // the old store, as importing that module will initiate the attempt
+  // to migrate the old store to the new one.
+  const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
+
+  // XULStore should *not* have migrated the values from the old store,
+  // so it should return empty strings when we try to retrieve them.
+  // That's true for both values, even though one of them is valid,
+  // because the migrator uses a typed parser that requires the entire
+  // JSON file to conform to the XULStore format.
+  Assert.equal(await XULStore.getValue("doc1", "id1", "attr1"), "");
+  Assert.equal(await XULStore.getValue("doc2", "id2", "attr2"), "");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/xpcshell/test_XULStore_migration_fail_invalid_json.js
@@ -0,0 +1,28 @@
+"use strict";
+
+const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+
+function run_test() {
+  do_get_profile();
+  run_next_test();
+}
+
+add_task(async function test_create_old_datastore() {
+  const path = OS.Path.join(OS.Constants.Path.profileDir, "xulstore.json");
+
+  // Invalid JSON: it's missing the final closing brace.
+  const xulstoreJSON = '{ doc: { id: { attr: "value" } }';
+
+  await OS.File.writeAtomic(path, xulstoreJSON);
+});
+
+add_task(async function test_get_value() {
+  // We wait until now to import XULStore.jsm to ensure we've created
+  // the old store, as importing that module will initiate the attempt
+  // to migrate the old store to the new one.
+  const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
+
+  // XULStore should *not* have migrated the value from the old store,
+  // so it should return an empty string when we try to retrieve it.
+  Assert.equal(await XULStore.getValue("doc", "id", "attr"), "");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/xpcshell/test_XULStore_migration_profile_change.js
@@ -0,0 +1,43 @@
+"use strict";
+
+const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+add_task(async function test_get_values() {
+  // Import XULStore.jsm before getting the profile to ensure that the new store
+  // is initialized, as the purpose of this test is to confirm that the old
+  // store data gets migrated if the profile change happens post-initialization.
+  const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
+
+  // We haven't migrated any data yet (nor even changed to a profile), so there
+  // shouldn't be a value in the store.
+  Assert.equal(XULStore.getValue("doc1", "id1", "attr1"), "");
+
+  // Register an observer before the XULStore service registers its observer,
+  // so we can observe the profile-after-change notification first and create
+  // an old store for it to migrate.  We need to write synchronously to avoid
+  // racing XULStore, so we use FileUtils instead of OS.File.
+  Services.obs.addObserver({
+    observe() {
+      const file = FileUtils.getFile("ProfD", ["xulstore.json"]);
+      const xulstoreJSON = JSON.stringify({
+        doc1: {
+          id1: {
+            attr1: "value1",
+          },
+        },
+      });
+      let stream = FileUtils.openAtomicFileOutputStream(file);
+      stream.write(xulstoreJSON, xulstoreJSON.length);
+      FileUtils.closeAtomicFileOutputStream(stream);
+    },
+  }, "profile-after-change");
+
+  // This creates a profile and changes to it, triggering first our
+  // profile-after-change observer above and then XULStore's equivalent.
+  do_get_profile(true);
+
+  // XULStore should now have migrated the value from the old store.
+  Assert.equal(XULStore.getValue("doc1", "id1", "attr1"), "value1");
+});
--- a/toolkit/components/xulstore/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/xulstore/tests/xpcshell/xpcshell.ini
@@ -1,4 +1,8 @@
 [DEFAULT]
 skip-if = toolkit == 'android'
 
 [test_XULStore.js]
+[test_XULStore_migration.js]
+[test_XULStore_migration_fail_invalid_json.js]
+[test_XULStore_migration_fail_invalid_data.js]
+[test_XULStore_migration_profile_change.js]
--- a/toolkit/library/rust/shared/Cargo.toml
+++ b/toolkit/library/rust/shared/Cargo.toml
@@ -22,16 +22,17 @@ cubeb-pulse = { path = "../../../../medi
 cubeb-sys = { version = "0.5.0", optional = true, features=["gecko-in-tree"] }
 encoding_c = "0.9.0"
 encoding_glue = { path = "../../../../intl/encoding_glue" }
 audioipc-client = { path = "../../../../media/audioipc/client", optional = true }
 audioipc-server = { path = "../../../../media/audioipc/server", optional = true }
 u2fhid = { path = "../../../../dom/webauthn/u2f-hid-rs" }
 gkrust_utils = { path = "../../../../xpcom/rust/gkrust_utils" }
 rsdparsa_capi = { path = "../../../../media/webrtc/signaling/src/sdp/rsdparsa_capi" }
+xulstore = { path = "../../../components/xulstore" }
 # We have these to enforce common feature sets for said crates.
 log = {version = "0.4", features = ["release_max_level_info"]}
 env_logger = {version = "0.5", default-features = false} # disable `regex` to reduce code size
 cose-c = { version = "0.1.5" }
 jsrust_shared = { path = "../../../../js/src/rust/shared", optional = true }
 arrayvec = "0.4"
 cert_storage = { path = "../../../../security/manager/ssl/cert_storage" }
 bitsdownload = { path = "../../../components/bitsdownload", optional = true }
--- a/toolkit/library/rust/shared/lib.rs
+++ b/toolkit/library/rust/shared/lib.rs
@@ -29,16 +29,17 @@ extern crate audioipc_client;
 extern crate audioipc_server;
 extern crate env_logger;
 extern crate u2fhid;
 extern crate gkrust_utils;
 extern crate log;
 extern crate cert_storage;
 extern crate cosec;
 extern crate rsdparsa_capi;
+extern crate xulstore;
 #[cfg(feature = "spidermonkey_rust")]
 extern crate jsrust_shared;
 #[cfg(feature = "bitsdownload")]
 extern crate bitsdownload;
 extern crate storage;
 #[cfg(feature = "moz_places")]
 extern crate bookmark_sync;
 
--- a/toolkit/modules/Services.jsm
+++ b/toolkit/modules/Services.jsm
@@ -46,16 +46,21 @@ if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyGetter(Services, "crashmanager", () => {
     let ns = {};
     ChromeUtils.import("resource://gre/modules/CrashManager.jsm", ns);
 
     return ns.CrashManager.Singleton;
   });
 }
 
+XPCOMUtils.defineLazyGetter(Services, "xulStore", () => {
+  const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
+  return XULStore;
+});
+
 XPCOMUtils.defineLazyGetter(Services, "io", () => {
   return Cc["@mozilla.org/network/io-service;1"]
            .getService(Ci.nsIIOService)
            .QueryInterface(Ci.nsISpeculativeConnect);
 });
 
 var initTable = {
   appShell: ["@mozilla.org/appshell/appShellService;1", "nsIAppShellService"],
@@ -94,17 +99,16 @@ var initTable = {
   clipboard: ["@mozilla.org/widget/clipboard;1", "nsIClipboard"],
   DOMRequest: ["@mozilla.org/dom/dom-request-service;1", "nsIDOMRequestService"],
   focus: ["@mozilla.org/focus-manager;1", "nsIFocusManager"],
   uriFixup: ["@mozilla.org/docshell/urifixup;1", "nsIURIFixup"],
   blocklist: ["@mozilla.org/extensions/blocklist;1"],
   netUtils: ["@mozilla.org/network/util;1", "nsINetUtil"],
   loadContextInfo: ["@mozilla.org/load-context-info-factory;1", "nsILoadContextInfoFactory"],
   qms: ["@mozilla.org/dom/quota-manager-service;1", "nsIQuotaManagerService"],
-  xulStore: ["@mozilla.org/xul/xulstore;1", "nsIXULStore"],
 };
 
 if (AppConstants.platform == "android") {
   initTable.androidBridge = ["@mozilla.org/android/bridge;1", "nsIAndroidBridge"];
 }
 if (AppConstants.MOZ_TOOLKIT_SEARCH) {
   initTable.search = ["@mozilla.org/browser/search-service;1", "nsISearchService"];
 }
--- a/widget/EventForwards.h
+++ b/widget/EventForwards.h
@@ -257,11 +257,26 @@ struct TextRangeStyle;
 struct TextRange;
 
 class EditCommands;
 class TextRangeArray;
 
 // FontRange.h
 struct FontRange;
 
+enum MouseButton { eNotPressed = -1, eLeft = 0, eMiddle = 1, eRight = 2 };
+
+enum MouseButtonsFlag {
+  eNoButtons = 0x00,
+  eLeftFlag = 0x01,
+  eRightFlag = 0x02,
+  eMiddleFlag = 0x04,
+  // typicall, "back" button being left side of 5-button
+  // mice, see "buttons" attribute document of DOM3 Events.
+  e4thFlag = 0x08,
+  // typicall, "forward" button being right side of 5-button
+  // mice, see "buttons" attribute document of DOM3 Events.
+  e5thFlag = 0x10
+};
+
 }  // namespace mozilla
 
 #endif  // mozilla_EventForwards_h__
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -217,18 +217,18 @@ WidgetMouseEvent MultiTouchInput::ToWidg
   WidgetMouseEvent event(true, mouseEventMessage, aWidget,
                          WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
 
   const SingleTouchData& firstTouch = mTouches[0];
   event.mRefPoint.x = firstTouch.mScreenPoint.x;
   event.mRefPoint.y = firstTouch.mScreenPoint.y;
 
   event.mTime = mTime;
-  event.button = WidgetMouseEvent::eLeftButton;
-  event.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+  event.mButton = MouseButton::eLeft;
+  event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
   event.mModifiers = modifiers;
   event.mFlags.mHandledByAPZ = mHandledByAPZ;
   event.mFocusSequenceNumber = mFocusSequenceNumber;
 
   if (mouseEventMessage != eMouseMove) {
     event.mClickCount = 1;
   }
 
@@ -277,32 +277,32 @@ MouseInput::MouseInput(MouseType aType, 
       mOrigin(aPoint),
       mHandledByAPZ(false) {}
 
 MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent)
     : InputData(MOUSE_INPUT, aMouseEvent.mTime, aMouseEvent.mTimeStamp,
                 aMouseEvent.mModifiers),
       mType(MOUSE_NONE),
       mButtonType(NONE),
-      mInputSource(aMouseEvent.inputSource),
-      mButtons(aMouseEvent.buttons),
+      mInputSource(aMouseEvent.mInputSource),
+      mButtons(aMouseEvent.mButtons),
       mHandledByAPZ(aMouseEvent.mFlags.mHandledByAPZ) {
   MOZ_ASSERT(NS_IsMainThread(),
              "Can only copy from WidgetTouchEvent on main thread");
 
   mButtonType = NONE;
 
-  switch (aMouseEvent.button) {
-    case WidgetMouseEventBase::eLeftButton:
+  switch (aMouseEvent.mButton) {
+    case MouseButton::eLeft:
       mButtonType = MouseInput::LEFT_BUTTON;
       break;
-    case WidgetMouseEventBase::eMiddleButton:
+    case MouseButton::eMiddle:
       mButtonType = MouseInput::MIDDLE_BUTTON;
       break;
-    case WidgetMouseEventBase::eRightButton:
+    case MouseButton::eRight:
       mButtonType = MouseInput::RIGHT_BUTTON;
       break;
   }
 
   switch (aMouseEvent.mMessage) {
     case eMouseMove:
       mType = MOUSE_MOVE;
       break;
@@ -393,39 +393,39 @@ WidgetMouseEvent MouseInput::ToWidgetMou
                          WidgetMouseEvent::eNormal);
 
   if (msg == eVoidEvent) {
     return event;
   }
 
   switch (mButtonType) {
     case MouseInput::LEFT_BUTTON:
-      event.button = WidgetMouseEventBase::eLeftButton;
+      event.mButton = MouseButton::eLeft;
       break;
     case MouseInput::MIDDLE_BUTTON:
-      event.button = WidgetMouseEventBase::eMiddleButton;
+      event.mButton = MouseButton::eMiddle;
       break;
     case MouseInput::RIGHT_BUTTON:
-      event.button = WidgetMouseEventBase::eRightButton;
+      event.mButton = MouseButton::eRight;
       break;
     case MouseInput::NONE:
     default:
       break;
   }
 
-  event.buttons = mButtons;
+  event.mButtons = mButtons;
   event.mModifiers = modifiers;
   event.mTime = mTime;
   event.mTimeStamp = mTimeStamp;
   event.mFlags.mHandledByAPZ = mHandledByAPZ;
   event.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
       mOrigin,
       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
   event.mClickCount = clickCount;
-  event.inputSource = mInputSource;
+  event.mInputSource = mInputSource;
   event.mIgnoreRootScrollFrame = true;
   event.mFocusSequenceNumber = mFocusSequenceNumber;
 
   return event;
 }
 
 PanGestureInput::PanGestureInput()
     : InputData(PANGESTURE_INPUT),
@@ -471,17 +471,17 @@ bool PanGestureInput::IsMomentum() const
 WidgetWheelEvent PanGestureInput::ToWidgetWheelEvent(nsIWidget* aWidget) const {
   WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
   wheelEvent.mModifiers = this->modifiers;
   wheelEvent.mTime = mTime;
   wheelEvent.mTimeStamp = mTimeStamp;
   wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
       mPanStartPoint,
       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
-  wheelEvent.buttons = 0;
+  wheelEvent.mButtons = 0;
   wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
   wheelEvent.mMayHaveMomentum = true;  // pan inputs may have momentum
   wheelEvent.mIsMomentum = IsMomentum();
   wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
   wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
   wheelEvent.mDeltaX = mPanDisplacement.x;
   wheelEvent.mDeltaY = mPanDisplacement.y;
   wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
@@ -685,17 +685,17 @@ WidgetWheelEvent ScrollWheelInput::ToWid
     nsIWidget* aWidget) const {
   WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
   wheelEvent.mModifiers = this->modifiers;
   wheelEvent.mTime = mTime;
   wheelEvent.mTimeStamp = mTimeStamp;
   wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
       mOrigin,
       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
-  wheelEvent.buttons = 0;
+  wheelEvent.mButtons = 0;
   wheelEvent.mDeltaMode = DeltaModeForDeltaType(mDeltaType);
   wheelEvent.mMayHaveMomentum = mMayHaveMomentum;
   wheelEvent.mIsMomentum = mIsMomentum;
   wheelEvent.mDeltaX = mDeltaX;
   wheelEvent.mDeltaY = mDeltaY;
   wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
   wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
   wheelEvent.mAllowToOverrideSystemScrollSpeed =
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -90,104 +90,94 @@ class WidgetPointerHelper {
 
 class WidgetMouseEventBase : public WidgetInputEvent {
  private:
   friend class dom::PBrowserParent;
   friend class dom::PBrowserChild;
 
  protected:
   WidgetMouseEventBase()
-      : button(0),
-        buttons(0),
-        pressure(0),
-        hitCluster(false)
-        // Including MouseEventBinding.h here leads to an include loop, so
-        // we have to hardcode MouseEvent_Binding::MOZ_SOURCE_MOUSE.
-        ,
-        inputSource(/* MouseEvent_Binding::MOZ_SOURCE_MOUSE = */ 1) {}
+      : mPressure(0),
+        mButton(0),
+        mButtons(0),
+        mInputSource(/* MouseEvent_Binding::MOZ_SOURCE_MOUSE = */ 1),
+        mHitCluster(false) {}
+  // Including MouseEventBinding.h here leads to an include loop, so
+  // we have to hardcode MouseEvent_Binding::MOZ_SOURCE_MOUSE.
 
   WidgetMouseEventBase(bool aIsTrusted, EventMessage aMessage,
                        nsIWidget* aWidget, EventClassID aEventClassID)
       : WidgetInputEvent(aIsTrusted, aMessage, aWidget, aEventClassID),
-        button(0),
-        buttons(0),
-        pressure(0),
-        hitCluster(false)
-        // Including MouseEventBinding.h here leads to an include loop, so
-        // we have to hardcode MouseEvent_Binding::MOZ_SOURCE_MOUSE.
-        ,
-        inputSource(/* MouseEvent_Binding::MOZ_SOURCE_MOUSE = */ 1) {}
+        mPressure(0),
+        mButton(0),
+        mButtons(0),
+        mInputSource(/* MouseEvent_Binding::MOZ_SOURCE_MOUSE = */ 1),
+        mHitCluster(false) {}
+  // Including MouseEventBinding.h here leads to an include loop, so
+  // we have to hardcode MouseEvent_Binding::MOZ_SOURCE_MOUSE.
 
  public:
   virtual WidgetMouseEventBase* AsMouseEventBase() override { return this; }
 
   virtual WidgetEvent* Duplicate() const override {
     MOZ_CRASH("WidgetMouseEventBase must not be most-subclass");
   }
 
-  enum buttonType {
-    eNoButton = -1,
-    eLeftButton = 0,
-    eMiddleButton = 1,
-    eRightButton = 2
-  };
+  // ID of the canvas HitRegion
+  nsString mRegion;
+
+  // Finger or touch pressure of event. It ranges between 0.0 and 1.0.
+  float mPressure;
+
   // Pressed button ID of mousedown or mouseup event.
   // This is set only when pressing a button causes the event.
-  int16_t button;
-
-  enum buttonsFlag {
-    eNoButtonFlag = 0x00,
-    eLeftButtonFlag = 0x01,
-    eRightButtonFlag = 0x02,
-    eMiddleButtonFlag = 0x04,
-    // typicall, "back" button being left side of 5-button
-    // mice, see "buttons" attribute document of DOM3 Events.
-    e4thButtonFlag = 0x08,
-    // typicall, "forward" button being right side of 5-button
-    // mice, see "buttons" attribute document of DOM3 Events.
-    e5thButtonFlag = 0x10
-  };
+  int16_t mButton;
 
   // Flags of all pressed buttons at the event fired.
-  // This is set at any mouse event, don't be confused with |button|.
-  int16_t buttons;
-
-  // Finger or touch pressure of event. It ranges between 0.0 and 1.0.
-  float pressure;
-  // Touch near a cluster of links (true)
-  bool hitCluster;
+  // This is set at any mouse event, don't be confused with |mButton|.
+  int16_t mButtons;
 
   // Possible values a in MouseEvent
-  uint16_t inputSource;
+  uint16_t mInputSource;
 
-  // ID of the canvas HitRegion
-  nsString region;
+  // Touch near a cluster of links (true)
+  bool mHitCluster;
 
-  bool IsLeftButtonPressed() const { return !!(buttons & eLeftButtonFlag); }
-  bool IsRightButtonPressed() const { return !!(buttons & eRightButtonFlag); }
-  bool IsMiddleButtonPressed() const { return !!(buttons & eMiddleButtonFlag); }
-  bool Is4thButtonPressed() const { return !!(buttons & e4thButtonFlag); }
-  bool Is5thButtonPressed() const { return !!(buttons & e5thButtonFlag); }
+  bool IsLeftButtonPressed() const {
+    return !!(mButtons & MouseButtonsFlag::eLeftFlag);
+  }
+  bool IsRightButtonPressed() const {
+    return !!(mButtons & MouseButtonsFlag::eRightFlag);
+  }
+  bool IsMiddleButtonPressed() const {
+    return !!(mButtons & MouseButtonsFlag::eMiddleFlag);
+  }
+  bool Is4thButtonPressed() const {
+    return !!(mButtons & MouseButtonsFlag::e4thFlag);
+  }
+  bool Is5thButtonPressed() const {
+    return !!(mButtons & MouseButtonsFlag::e5thFlag);
+  }
 
   void AssignMouseEventBaseData(const WidgetMouseEventBase& aEvent,
                                 bool aCopyTargets) {
     AssignInputEventData(aEvent, aCopyTargets);
 
-    button = aEvent.button;
-    buttons = aEvent.buttons;
-    pressure = aEvent.pressure;
-    hitCluster = aEvent.hitCluster;
-    inputSource = aEvent.inputSource;
+    mButton = aEvent.mButton;
+    mButtons = aEvent.mButtons;
+    mPressure = aEvent.mPressure;
+    mHitCluster = aEvent.mHitCluster;
+    mInputSource = aEvent.mInputSource;
   }
 
   /**
    * Returns true if left click event.
    */
   bool IsLeftClickEvent() const {
-    return mMessage == eMouseClick && button == eLeftButton;
+    return mMessage == eMouseClick && mButton == MouseButton::eLeft;
   }
 };
 
 /******************************************************************************
  * mozilla::WidgetMouseEvent
  ******************************************************************************/
 
 class WidgetMouseEvent : public WidgetMouseEventBase,
@@ -234,26 +224,27 @@ class WidgetMouseEvent : public WidgetMo
       : WidgetMouseEventBase(aIsTrusted, aMessage, aWidget, eMouseEventClass),
         mReason(aReason),
         mContextMenuTrigger(aContextMenuTrigger),
         mExitFrom(eChild),
         mIgnoreRootScrollFrame(false),
         mClickCount(0),
         mUseLegacyNonPrimaryDispatch(false) {
     if (aMessage == eContextMenu) {
-      button = (mContextMenuTrigger == eNormal) ? eRightButton : eLeftButton;
+      mButton = (mContextMenuTrigger == eNormal) ? MouseButton::eRight
+                                                 : MouseButton::eLeft;
     }
   }
 
 #ifdef DEBUG
   virtual ~WidgetMouseEvent() {
     NS_WARNING_ASSERTION(
         mMessage != eContextMenu ||
-            button ==
-                ((mContextMenuTrigger == eNormal) ? eRightButton : eLeftButton),
+            mButton == ((mContextMenuTrigger == eNormal) ? MouseButton::eRight
+                                                         : MouseButton::eLeft),
         "Wrong button set to eContextMenu event?");
   }
 #endif
 
   virtual WidgetEvent* Duplicate() const override {
     MOZ_ASSERT(mClass == eMouseEventClass,
                "Duplicate() must be overridden by sub class");
     // Not copying widget, it is a weak reference.
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -558,29 +558,29 @@ class nsWindow::NPZCSupport final
 
     return result;
   }
 
   static int16_t ConvertButtons(int buttons) {
     int16_t result = 0;
 
     if (buttons & java::sdk::MotionEvent::BUTTON_PRIMARY) {
-      result |= WidgetMouseEventBase::eLeftButtonFlag;
+      result |= MouseButtonsFlag::eLeftFlag;
     }
     if (buttons & java::sdk::MotionEvent::BUTTON_SECONDARY) {
-      result |= WidgetMouseEventBase::eRightButtonFlag;
+      result |= MouseButtonsFlag::eRightFlag;
     }
     if (buttons & java::sdk::MotionEvent::BUTTON_TERTIARY) {
-      result |= WidgetMouseEventBase::eMiddleButtonFlag;
+      result |= MouseButtonsFlag::eMiddleFlag;
     }
     if (buttons & java::sdk::MotionEvent::BUTTON_BACK) {
-      result |= WidgetMouseEventBase::e4thButtonFlag;
+      result |= MouseButtonsFlag::e4thFlag;
     }
     if (buttons & java::sdk::MotionEvent::BUTTON_FORWARD) {
-      result |= WidgetMouseEventBase::e5thButtonFlag;
+      result |= MouseButtonsFlag::e5thFlag;
     }
 
     return result;
   }
 
  public:
   bool HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
                         float aX, float aY, int buttons) {
@@ -1965,21 +1965,21 @@ void nsWindow::DispatchHitTest(const Wid
     // Since touch events don't get retargeted by PositionedEventTargeting.cpp
     // code on Fennec, we dispatch a dummy mouse event that *does* get
     // retargeted. The Fennec browser.js code can use this to activate the
     // highlight element in case the this touchstart is the start of a tap.
     WidgetMouseEvent hittest(true, eMouseHitTest, this,
                              WidgetMouseEvent::eReal);
     hittest.mRefPoint = aEvent.mTouches[0]->mRefPoint;
     hittest.mIgnoreRootScrollFrame = true;
-    hittest.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+    hittest.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
     nsEventStatus status;
     DispatchEvent(&hittest, status);
 
-    if (mAPZEventState && hittest.hitCluster) {
+    if (mAPZEventState && hittest.mHitCluster) {
       mAPZEventState->ProcessClusterHit();
     }
   }
 }
 
 mozilla::Modifiers nsWindow::GetModifiers(int32_t metaState) {
   using mozilla::java::sdk::KeyEvent;
   return (metaState & KeyEvent::META_ALT_MASK ? MODIFIER_ALT : 0) |
--- a/widget/cocoa/SwipeTracker.mm
+++ b/widget/cocoa/SwipeTracker.mm
@@ -178,17 +178,17 @@ void SwipeTracker::UnregisterFromRefresh
     EventMessage aMsg, nsIWidget* aWidget, const LayoutDeviceIntPoint& aPosition,
     const TimeStamp& aTimeStamp) {
   // XXX Why isn't this initialized with nsCocoaUtils::InitInputEvent()?
   WidgetSimpleGestureEvent geckoEvent(true, aMsg, aWidget);
   geckoEvent.mModifiers = 0;
   // XXX How about geckoEvent.mTime?
   geckoEvent.mTimeStamp = aTimeStamp;
   geckoEvent.mRefPoint = aPosition;
-  geckoEvent.buttons = 0;
+  geckoEvent.mButtons = 0;
   return geckoEvent;
 }
 
 bool SwipeTracker::SendSwipeEvent(EventMessage aMsg, uint32_t aDirection, double aDelta,
                                   const TimeStamp& aTimeStamp) {
   WidgetSimpleGestureEvent geckoEvent =
       CreateSwipeGestureEvent(aMsg, &mWidget, mEventPosition, aTimeStamp);
   geckoEvent.mDirection = aDirection;
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -4060,19 +4060,19 @@ NSEvent* gLastDragMouseDownEvent = nil;
   if (mBlockedLastMouseDown && clickCount > 1) {
     // Don't send a double click if the first click of the double click was
     // blocked.
     clickCount--;
   }
   geckoEvent.mClickCount = clickCount;
 
   if (modifierFlags & NSControlKeyMask)
-    geckoEvent.button = WidgetMouseEvent::eRightButton;
+    geckoEvent.mButton = MouseButton::eRight;
   else
-    geckoEvent.button = WidgetMouseEvent::eLeftButton;
+    geckoEvent.mButton = MouseButton::eLeft;
 
   mGeckoChild->DispatchInputEvent(&geckoEvent);
   mBlockedLastMouseDown = NO;
 
   // XXX maybe call markedTextSelectionChanged:client: here?
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
@@ -4085,19 +4085,19 @@ NSEvent* gLastDragMouseDownEvent = nil;
     return;
   }
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   if ([theEvent modifierFlags] & NSControlKeyMask)
-    geckoEvent.button = WidgetMouseEvent::eRightButton;
+    geckoEvent.mButton = MouseButton::eRight;
   else
-    geckoEvent.button = WidgetMouseEvent::eLeftButton;
+    geckoEvent.mButton = MouseButton::eLeft;
 
   // Remember the event's position before calling DispatchInputEvent, because
   // that call can mutate it and convert it into a different coordinate space.
   LayoutDeviceIntPoint pos = geckoEvent.mRefPoint;
 
   // This might destroy our widget (and null out mGeckoChild).
   bool defaultPrevented =
       (mGeckoChild->DispatchInputEvent(&geckoEvent) == nsEventStatus_eConsumeNoDefault);
@@ -4187,17 +4187,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   if (!mGeckoChild) return;
   if (mTextInputHandler->OnHandleEvent(theEvent)) {
     return;
   }
 
   // The right mouse went down, fire off a right mouse down event to gecko
   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.button = WidgetMouseEvent::eRightButton;
+  geckoEvent.mButton = MouseButton::eRight;
   geckoEvent.mClickCount = [theEvent clickCount];
 
   mGeckoChild->DispatchInputEvent(&geckoEvent);
   if (!mGeckoChild) return;
 
   if (!nsBaseWidget::ShowContextMenuAfterMouseUp()) {
     // Let the superclass do the context menu stuff.
     [super rightMouseDown:theEvent];
@@ -4211,17 +4211,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
   if (!mGeckoChild) return;
   if (mTextInputHandler->OnHandleEvent(theEvent)) {
     return;
   }
 
   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.button = WidgetMouseEvent::eRightButton;
+  geckoEvent.mButton = MouseButton::eRight;
   geckoEvent.mClickCount = [theEvent clickCount];
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
   mGeckoChild->DispatchInputEvent(&geckoEvent);
   if (!mGeckoChild) return;
 
   if (nsBaseWidget::ShowContextMenuAfterMouseUp()) {
     // Let the superclass do the context menu stuff, but pretend it's rightMouseDown.
@@ -4244,17 +4244,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
 - (void)rightMouseDragged:(NSEvent*)theEvent {
   if (!mGeckoChild) return;
   if (mTextInputHandler->OnHandleEvent(theEvent)) {
     return;
   }
 
   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.button = WidgetMouseEvent::eRightButton;
+  geckoEvent.mButton = MouseButton::eRight;
 
   // send event into Gecko by going directly to the
   // the widget.
   mGeckoChild->DispatchInputEvent(&geckoEvent);
 }
 
 - (void)otherMouseDown:(NSEvent*)theEvent {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@@ -4267,47 +4267,47 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
   if (!mGeckoChild) return;
   if (mTextInputHandler->OnHandleEvent(theEvent)) {
     return;
   }
 
   WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.button = WidgetMouseEvent::eMiddleButton;
+  geckoEvent.mButton = MouseButton::eMiddle;
   geckoEvent.mClickCount = [theEvent clickCount];
 
   mGeckoChild->DispatchInputEvent(&geckoEvent);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)otherMouseUp:(NSEvent*)theEvent {
   if (!mGeckoChild) return;
   if (mTextInputHandler->OnHandleEvent(theEvent)) {
     return;
   }
 
   WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.button = WidgetMouseEvent::eMiddleButton;
+  geckoEvent.mButton = MouseButton::eMiddle;
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
   mGeckoChild->DispatchInputEvent(&geckoEvent);
 }
 
 - (void)otherMouseDragged:(NSEvent*)theEvent {
   if (!mGeckoChild) return;
   if (mTextInputHandler->OnHandleEvent(theEvent)) {
     return;
   }
 
   WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.button = WidgetMouseEvent::eMiddleButton;
+  geckoEvent.mButton = MouseButton::eMiddle;
 
   // send event into Gecko by going directly to the
   // the widget.
   mGeckoChild->DispatchInputEvent(&geckoEvent);
 }
 
 - (void)sendWheelStartOrStop:(EventMessage)msg forEvent:(NSEvent*)theEvent {
   WidgetWheelEvent wheelEvent(true, msg, mGeckoChild);
@@ -4542,17 +4542,17 @@ static gfx::IntPoint GetIntegerDeltaForE
   // get that event sent first if this is a left mouse click.
   if ([theEvent type] == NSLeftMouseDown) {
     [self mouseDown:theEvent];
     if (!mGeckoChild) return nil;
   }
 
   WidgetMouseEvent geckoEvent(true, eContextMenu, mGeckoChild, WidgetMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
-  geckoEvent.button = WidgetMouseEvent::eRightButton;
+  geckoEvent.mButton = MouseButton::eRight;
   mGeckoChild->DispatchInputEvent(&geckoEvent);
   if (!mGeckoChild) return nil;
 
   [self maybeInitContextMenuTracking];
 
   // We never return an actual NSMenu* for the context menu. Gecko might have
   // responded to the eContextMenu event by putting up a fake context menu.
   return nil;
@@ -4582,33 +4582,33 @@ static gfx::IntPoint GetIntegerDeltaForE
   nsCocoaUtils::InitInputEvent(*outGeckoEvent, aMouseEvent);
 
   // convert point to view coordinate system
   NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(aMouseEvent, [self window]);
 
   outGeckoEvent->mRefPoint = [self convertWindowCoordinates:locationInWindow];
 
   WidgetMouseEventBase* mouseEvent = outGeckoEvent->AsMouseEventBase();
-  mouseEvent->buttons = 0;
+  mouseEvent->mButtons = 0;
   NSUInteger mouseButtons = [NSEvent pressedMouseButtons];
 
   if (mouseButtons & 0x01) {
-    mouseEvent->buttons |= WidgetMouseEvent::eLeftButtonFlag;
+    mouseEvent->mButtons |= MouseButtonsFlag::eLeftFlag;
   }
   if (mouseButtons & 0x02) {
-    mouseEvent->buttons |= WidgetMouseEvent::eRightButtonFlag;
+    mouseEvent->mButtons |= MouseButtonsFlag::eRightFlag;
   }
   if (mouseButtons & 0x04) {
-    mouseEvent->buttons |= WidgetMouseEvent::eMiddleButtonFlag;
+    mouseEvent->mButtons |= MouseButtonsFlag::eMiddleFlag;
   }
   if (mouseButtons & 0x08) {
-    mouseEvent->buttons |= WidgetMouseEvent::e4thButtonFlag;
+    mouseEvent->mButtons |= MouseButtonsFlag::e4thFlag;
   }
   if (mouseButtons & 0x10) {
-    mouseEvent->buttons |= WidgetMouseEvent::e5thButtonFlag;
+    mouseEvent->mButtons |= MouseButtonsFlag::e5thFlag;
   }
 
   switch ([aMouseEvent type]) {
     case NSLeftMouseDown:
     case NSLeftMouseUp:
     case NSLeftMouseDragged:
     case NSRightMouseDown:
     case NSRightMouseUp:
@@ -4632,20 +4632,20 @@ static gfx::IntPoint GetIntegerDeltaForE
 
 - (void)convertCocoaTabletPointerEvent:(NSEvent*)aPointerEvent
                           toGeckoEvent:(WidgetMouseEvent*)aOutGeckoEvent {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN
   if (!aOutGeckoEvent || !sIsTabletPointerActivated) {
     return;
   }
   if ([aPointerEvent type] != NSMouseMoved) {
-    aOutGeckoEvent->pressure = [aPointerEvent pressure];
-    MOZ_ASSERT(aOutGeckoEvent->pressure >= 0.0 && aOutGeckoEvent->pressure <= 1.0);
-  }
-  aOutGeckoEvent->inputSource = dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
+    aOutGeckoEvent->mPressure = [aPointerEvent pressure];
+    MOZ_ASSERT(aOutGeckoEvent->mPressure >= 0.0 && aOutGeckoEvent->mPressure <= 1.0);
+  }
+  aOutGeckoEvent->mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_PEN;
   aOutGeckoEvent->tiltX = lround([aPointerEvent tilt].x * 90);
   aOutGeckoEvent->tiltY = lround([aPointerEvent tilt].y * 90);
   aOutGeckoEvent->tangentialPressure = [aPointerEvent tangentialPressure];
   // Make sure the twist value is in the range of 0-359.
   int32_t twist = fmod([aPointerEvent rotation], 360);
   aOutGeckoEvent->twist = twist >= 0 ? twist : twist + 360;
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
--- a/widget/gtk/nsGtkKeyUtils.cpp
+++ b/widget/gtk/nsGtkKeyUtils.cpp
@@ -1045,39 +1045,38 @@ void KeymapWrapper::InitInputEvent(Widge
     case eDragEventClass:
     case eSimpleGestureEventClass:
       break;
     default:
       return;
   }
 
   WidgetMouseEventBase& mouseEvent = *aInputEvent.AsMouseEventBase();
-  mouseEvent.buttons = 0;
+  mouseEvent.mButtons = 0;
   if (aModifierState & GDK_BUTTON1_MASK) {
-    mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::eLeftFlag;
   }
   if (aModifierState & GDK_BUTTON3_MASK) {
-    mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::eRightFlag;
   }
   if (aModifierState & GDK_BUTTON2_MASK) {
-    mouseEvent.buttons |= WidgetMouseEvent::eMiddleButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::eMiddleFlag;
   }
 
   if (doLog) {
-    MOZ_LOG(
-        gKeymapWrapperLog, LogLevel::Debug,
-        ("%p InitInputEvent, aInputEvent has buttons, "
-         "aInputEvent.buttons=0x%04X (Left: %s, Right: %s, Middle: %s, "
-         "4th (BACK): %s, 5th (FORWARD): %s)",
-         keymapWrapper, mouseEvent.buttons,
-         GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eLeftButtonFlag),
-         GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eRightButtonFlag),
-         GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eMiddleButtonFlag),
-         GetBoolName(mouseEvent.buttons & WidgetMouseEvent::e4thButtonFlag),
-         GetBoolName(mouseEvent.buttons & WidgetMouseEvent::e5thButtonFlag)));
+    MOZ_LOG(gKeymapWrapperLog, LogLevel::Debug,
+            ("%p InitInputEvent, aInputEvent has mButtons, "
+             "aInputEvent.mButtons=0x%04X (Left: %s, Right: %s, Middle: %s, "
+             "4th (BACK): %s, 5th (FORWARD): %s)",
+             keymapWrapper, mouseEvent.mButtons,
+             GetBoolName(mouseEvent.mButtons & MouseButtonsFlag::eLeftFlag),
+             GetBoolName(mouseEvent.mButtons & MouseButtonsFlag::eRightFlag),
+             GetBoolName(mouseEvent.mButtons & MouseButtonsFlag::eMiddleFlag),
+             GetBoolName(mouseEvent.mButtons & MouseButtonsFlag::e4thFlag),
+             GetBoolName(mouseEvent.mButtons & MouseButtonsFlag::e5thFlag)));
   }
 }
 
 /* static */
 uint32_t KeymapWrapper::ComputeDOMKeyCode(const GdkEventKey* aGdkKeyEvent) {
   // If the keyval indicates it's a modifier key, we should use unshifted
   // key's modifier keyval.
   guint keyval = aGdkKeyEvent->keyval;
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -2400,17 +2400,17 @@ void nsWindow::OnMotionNotifyEvent(GdkEv
 
   WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
 
   gdouble pressure = 0;
   gdk_event_get_axis((GdkEvent *)aEvent, GDK_AXIS_PRESSURE, &pressure);
   // Sometime gdk generate 0 pressure value between normal values
   // We have to ignore that and use last valid value
   if (pressure) mLastMotionPressure = pressure;
-  event.pressure = mLastMotionPressure;
+  event.mPressure = mLastMotionPressure;
 
   guint modifierState;
   if (synthEvent) {
 #ifdef MOZ_X11
     event.mRefPoint.x = nscoord(xevent.xmotion.x);
     event.mRefPoint.y = nscoord(xevent.xmotion.y);
 
     modifierState = xevent.xmotion.state;
@@ -2453,37 +2453,37 @@ void nsWindow::DispatchMissedButtonRelea
   // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
   // GDK ignores releases.
   for (guint buttonMask = GDK_BUTTON1_MASK; buttonMask <= GDK_BUTTON3_MASK;
        buttonMask <<= 1) {
     if (released & buttonMask) {
       int16_t buttonType;
       switch (buttonMask) {
         case GDK_BUTTON1_MASK:
-          buttonType = WidgetMouseEvent::eLeftButton;
+          buttonType = MouseButton::eLeft;
           break;
         case GDK_BUTTON2_MASK:
-          buttonType = WidgetMouseEvent::eMiddleButton;
+          buttonType = MouseButton::eMiddle;
           break;
         default:
           NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,
                        "Unexpected button mask");
-          buttonType = WidgetMouseEvent::eRightButton;
+          buttonType = MouseButton::eRight;
       }
 
       LOG(("Synthesized button %u release on %p\n", guint(buttonType + 1),
            (void *)this));
 
       // Dispatch a synthesized button up event to tell Gecko about the
       // change in state.  This event is marked as synthesized so that
       // it is not dispatched as a DOM event, because we don't know the
       // position, widget, modifiers, or time/order.
       WidgetMouseEvent synthEvent(true, eMouseUp, this,
                                   WidgetMouseEvent::eSynthesized);
-      synthEvent.button = buttonType;
+      synthEvent.mButton = buttonType;
       DispatchInputEvent(&synthEvent);
     }
   }
 }
 
 void nsWindow::InitButtonEvent(WidgetMouseEvent &aEvent,
                                GdkEventButton *aGdkEvent) {
   aEvent.mRefPoint = GetRefPoint(this, aGdkEvent);
@@ -2528,22 +2528,21 @@ void nsWindow::InitButtonEvent(WidgetMou
 }
 
 static guint ButtonMaskFromGDKButton(guint button) {
   return GDK_BUTTON1_MASK << (button - 1);
 }
 
 void nsWindow::DispatchContextMenuEventFromMouseEvent(uint16_t domButton,
                                                       GdkEventButton *aEvent) {
-  if (domButton == WidgetMouseEvent::eRightButton &&
-      MOZ_LIKELY(!mIsDestroyed)) {
+  if (domButton == MouseButton::eRight && MOZ_LIKELY(!mIsDestroyed)) {
     WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
                                       WidgetMouseEvent::eReal);
     InitButtonEvent(contextMenuEvent, aEvent);
-    contextMenuEvent.pressure = mLastMotionPressure;
+    contextMenuEvent.mPressure = mLastMotionPressure;
     DispatchInputEvent(&contextMenuEvent);
   }
 }
 
 void nsWindow::OnButtonPressEvent(GdkEventButton *aEvent) {
   LOG(("Button %u press on %p\n", aEvent->button, (void *)this));
 
   // If you double click in GDK, it will actually generate a second
@@ -2569,23 +2568,23 @@ void nsWindow::OnButtonPressEvent(GdkEve
 
   gdouble pressure = 0;
   gdk_event_get_axis((GdkEvent *)aEvent, GDK_AXIS_PRESSURE, &pressure);
   mLastMotionPressure = pressure;
 
   uint16_t domButton;
   switch (aEvent->button) {
     case 1:
-      domButton = WidgetMouseEvent::eLeftButton;
+      domButton = MouseButton::eLeft;
       break;
     case 2:
-      domButton = WidgetMouseEvent::eMiddleButton;
+      domButton = MouseButton::eMiddle;
       break;
     case 3:
-      domButton = WidgetMouseEvent::eRightButton;
+      domButton = MouseButton::eRight;
       break;
     // These are mapped to horizontal scroll
     case 6:
     case 7:
       NS_WARNING("We're not supporting legacy horizontal scroll event");
       return;
     // Map buttons 8-9 to back/forward
     case 8:
@@ -2596,26 +2595,26 @@ void nsWindow::OnButtonPressEvent(GdkEve
       return;
     default:
       return;
   }
 
   gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
 
   WidgetMouseEvent event(true, eMouseDown, this, WidgetMouseEvent::eReal);
-  event.button = domButton;
+  event.mButton = domButton;
   InitButtonEvent(event, aEvent);
-  event.pressure = mLastMotionPressure;
+  event.mPressure = mLastMotionPressure;
 
   nsEventStatus eventStatus = DispatchInputEvent(&event);
 
   LayoutDeviceIntPoint refPoint =
       GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
   if (mDraggableRegion.Contains(refPoint.x, refPoint.y) &&
-      domButton == WidgetMouseEvent::eLeftButton &&
+      domButton == MouseButton::eLeft &&
       eventStatus != nsEventStatus_eConsumeNoDefault) {
     mWindowShouldStartDragging = true;
   }
 
   // right menu click on linux should also pop up a context menu
   if (!nsBaseWidget::ShowContextMenuAfterMouseUp()) {
     DispatchContextMenuEventFromMouseEvent(domButton, aEvent);
   }
@@ -2626,48 +2625,48 @@ void nsWindow::OnButtonReleaseEvent(GdkE
 
   if (mWindowShouldStartDragging) {
     mWindowShouldStartDragging = false;
   }
 
   uint16_t domButton;
   switch (aEvent->button) {
     case 1:
-      domButton = WidgetMouseEvent::eLeftButton;
+      domButton = MouseButton::eLeft;
       break;
     case 2:
-      domButton = WidgetMouseEvent::eMiddleButton;
+      domButton = MouseButton::eMiddle;
       break;
     case 3:
-      domButton = WidgetMouseEvent::eRightButton;
+      domButton = MouseButton::eRight;
       break;
     default:
       return;
   }
 
   gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
 
   WidgetMouseEvent event(true, eMouseUp, this, WidgetMouseEvent::eReal);
-  event.button = domButton;
+  event.mButton = domButton;
   InitButtonEvent(event, aEvent);
   gdouble pressure = 0;
   gdk_event_get_axis((GdkEvent *)aEvent, GDK_AXIS_PRESSURE, &pressure);
-  event.pressure = pressure ? pressure : mLastMotionPressure;
+  event.mPressure = pressure ? pressure : mLastMotionPressure;
 
   // The mRefPoint is manipulated in DispatchInputEvent, we're saving it
   // to use it for the doubleclick position check.
   LayoutDeviceIntPoint pos = event.mRefPoint;
 
   nsEventStatus eventStatus = DispatchInputEvent(&event);
 
   bool defaultPrevented = (eventStatus == nsEventStatus_eConsumeNoDefault);
   // Check if mouse position in titlebar and doubleclick happened to
   // trigger restore/maximize.
   if (!defaultPrevented && mDrawInTitlebar &&
-      event.button == WidgetMouseEvent::eLeftButton && event.mClickCount == 2 &&
+      event.mButton == MouseButton::eLeft && event.mClickCount == 2 &&
       mDraggableRegion.Contains(pos.x, pos.y)) {
     if (mSizeState == nsSizeMode_Maximized) {
       SetSizeMode(nsSizeMode_Normal);
     } else {
       SetSizeMode(nsSizeMode_Maximized);
     }
   }
   mLastMotionPressure = pressure;
@@ -5934,17 +5933,17 @@ already_AddRefed<DrawTarget> nsWindow::S
 void nsWindow::EndRemoteDrawingInRegion(DrawTarget *aDrawTarget,
                                         LayoutDeviceIntRegion &aInvalidRegion) {
   mSurfaceProvider.EndRemoteDrawingInRegion(aDrawTarget, aInvalidRegion);
 }
 
 // Code shared begin BeginMoveDrag and BeginResizeDrag
 bool nsWindow::GetDragInfo(WidgetMouseEvent *aMouseEvent, GdkWindow **aWindow,
                            gint *aButton, gint *aRootX, gint *aRootY) {
-  if (aMouseEvent->button != WidgetMouseEvent::eLeftButton) {
+  if (aMouseEvent->mButton != MouseButton::eLeft) {
     // we can only begin a move drag with the left mouse button
     return false;
   }
   *aButton = 1;
 
   // get the gdk window for this widget
   GdkWindow *gdk_window = mGdkWindow;
   if (!gdk_window) {
--- a/widget/headless/HeadlessWidget.cpp
+++ b/widget/headless/HeadlessWidget.cpp
@@ -434,17 +434,17 @@ nsresult HeadlessWidget::SynthesizeNativ
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported synthesized mouse event");
       return NS_ERROR_UNEXPECTED;
   }
   WidgetMouseEvent event(true, msg, this, WidgetMouseEvent::eReal);
   event.mRefPoint = aPoint - WidgetToScreenOffset();
   if (msg == eMouseDown || msg == eMouseUp) {
-    event.button = WidgetMouseEvent::eLeftButton;
+    event.mButton = MouseButton::eLeft;
   }
   if (msg == eMouseDown) {
     event.mClickCount = 1;
   }
   event.AssignEventTime(WidgetEventTime());
   DispatchInputEvent(&event);
   return NS_OK;
 }
--- a/widget/nsBaseDragService.cpp
+++ b/widget/nsBaseDragService.cpp
@@ -474,17 +474,17 @@ void nsBaseDragService::DiscardInternalT
 NS_IMETHODIMP
 nsBaseDragService::FireDragEventAtSource(EventMessage aEventMessage,
                                          uint32_t aKeyModifiers) {
   if (mSourceNode && mSourceDocument && !mSuppressLevel) {
     RefPtr<PresShell> presShell = mSourceDocument->GetPresShell();
     if (presShell) {
       nsEventStatus status = nsEventStatus_eIgnore;
       WidgetDragEvent event(true, aEventMessage, nullptr);
-      event.inputSource = mInputSource;
+      event.mInputSource = mInputSource;
       if (aEventMessage == eDragEnd) {
         event.mRefPoint = mEndDragPoint;
         event.mUserCancelled = mUserCancelled;
       }
       event.mModifiers = aKeyModifiers;
       // Send the drag event to APZ, which needs to know about them to be
       // able to accurately detect the end of a drag gesture.
       if (nsPresContext* presContext = presShell->GetPresContext()) {
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -126,32 +126,32 @@ struct ParamTraits<mozilla::WidgetInputE
 };
 
 template <>
 struct ParamTraits<mozilla::WidgetMouseEventBase> {
   typedef mozilla::WidgetMouseEventBase paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, static_cast<const mozilla::WidgetInputEvent&>(aParam));
-    WriteParam(aMsg, aParam.button);
-    WriteParam(aMsg, aParam.buttons);
-    WriteParam(aMsg, aParam.pressure);
-    WriteParam(aMsg, aParam.hitCluster);
-    WriteParam(aMsg, aParam.inputSource);
+    WriteParam(aMsg, aParam.mButton);
+    WriteParam(aMsg, aParam.mButtons);
+    WriteParam(aMsg, aParam.mPressure);
+    WriteParam(aMsg, aParam.mHitCluster);
+    WriteParam(aMsg, aParam.mInputSource);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
     return ReadParam(aMsg, aIter,
                      static_cast<mozilla::WidgetInputEvent*>(aResult)) &&
-           ReadParam(aMsg, aIter, &aResult->button) &&
-           ReadParam(aMsg, aIter, &aResult->buttons) &&
-           ReadParam(aMsg, aIter, &aResult->pressure) &&
-           ReadParam(aMsg, aIter, &aResult->hitCluster) &&
-           ReadParam(aMsg, aIter, &aResult->inputSource);
+           ReadParam(aMsg, aIter, &aResult->mButton) &&
+           ReadParam(aMsg, aIter, &aResult->mButtons) &&
+           ReadParam(aMsg, aIter, &aResult->mPressure) &&
+           ReadParam(aMsg, aIter, &aResult->mHitCluster) &&
+           ReadParam(aMsg, aIter, &aResult->mInputSource);
   }
 };
 
 template <>
 struct ParamTraits<mozilla::WidgetWheelEvent> {
   typedef mozilla::WidgetWheelEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
--- a/widget/uikit/nsWindow.mm
+++ b/widget/uikit/nsWindow.mm
@@ -130,17 +130,17 @@ class nsAutoRetainUIKitObject {
 
 - (void)sendMouseEvent:(EventMessage)aType
                  point:(LayoutDeviceIntPoint)aPoint
                 widget:(nsWindow*)aWindow {
   WidgetMouseEvent event(true, aType, aWindow, WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
 
   event.mRefPoint = aPoint;
   event.mClickCount = 1;
-  event.button = WidgetMouseEvent::eLeftButton;
+  event.button = MouseButton::eLeft;
   event.mTime = PR_IntervalNow();
   event.inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
 
   nsEventStatus status;
   aWindow->DispatchEvent(&event, status);
 }
 
 - (void)handleTap:(UITapGestureRecognizer*)sender {
--- a/widget/windows/IMMHandler.cpp
+++ b/widget/windows/IMMHandler.cpp
@@ -2466,23 +2466,23 @@ nsresult IMMHandler::OnMouseButtonEvent(
       compositionStart + gIMMHandler->mCompositionString.Length();
   if (aIMENotification.mMouseButtonEventData.mOffset < compositionStart ||
       aIMENotification.mMouseButtonEventData.mOffset >= compositionEnd) {
     return NS_OK;
   }
 
   BYTE button;
   switch (aIMENotification.mMouseButtonEventData.mButton) {
-    case WidgetMouseEventBase::eLeftButton:
+    case MouseButton::eLeft:
       button = IMEMOUSE_LDOWN;
       break;
-    case WidgetMouseEventBase::eMiddleButton:
+    case MouseButton::eMiddle:
       button = IMEMOUSE_MDOWN;
       break;
-    case WidgetMouseEventBase::eRightButton:
+    case MouseButton::eRight:
       button = IMEMOUSE_RDOWN;
       break;
     default:
       return NS_OK;
   }
 
   // calcurate positioning and offset
   // char :            JCH1|JCH2|JCH3
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -911,31 +911,31 @@ void ModifierKeyState::InitInputEvent(Wi
 void ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const {
   NS_ASSERTION(aMouseEvent.mClass == eMouseEventClass ||
                    aMouseEvent.mClass == eWheelEventClass ||
                    aMouseEvent.mClass == eDragEventClass ||
                    aMouseEvent.mClass == eSimpleGestureEventClass,
                "called with non-mouse event");
 
   WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
-  mouseEvent.buttons = 0;
+  mouseEvent.mButtons = 0;
   if (::GetKeyState(VK_LBUTTON) < 0) {
-    mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::eLeftFlag;
   }
   if (::GetKeyState(VK_RBUTTON) < 0) {
-    mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::eRightFlag;
   }
   if (::GetKeyState(VK_MBUTTON) < 0) {
-    mouseEvent.buttons |= WidgetMouseEvent::eMiddleButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::eMiddleFlag;
   }
   if (::GetKeyState(VK_XBUTTON1) < 0) {
-    mouseEvent.buttons |= WidgetMouseEvent::e4thButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::e4thFlag;
   }
   if (::GetKeyState(VK_XBUTTON2) < 0) {
-    mouseEvent.buttons |= WidgetMouseEvent::e5thButtonFlag;
+    mouseEvent.mButtons |= MouseButtonsFlag::e5thFlag;
   }
 }
 
 bool ModifierKeyState::IsShift() const {
   return (mModifiers & MODIFIER_SHIFT) != 0;
 }
 
 bool ModifierKeyState::IsControl() const {
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -526,53 +526,53 @@ static nsCString GetDisplayAttrStr(const
   str += GetColorName(aDispAttr.crLine);
   str += " }, bAttr: ";
   str += GetClauseAttrName(aDispAttr.bAttr);
   return str;
 }
 
 static const char* GetMouseButtonName(int16_t aButton) {
   switch (aButton) {
-    case WidgetMouseEventBase::eLeftButton:
+    case MouseButton::eLeft:
       return "LeftButton";
-    case WidgetMouseEventBase::eMiddleButton:
+    case MouseButton::eMiddle:
       return "MiddleButton";
-    case WidgetMouseEventBase::eRightButton:
+    case MouseButton::eRight:
       return "RightButton";
     default:
       return "UnknownButton";
   }
 }
 
 #define ADD_SEPARATOR_IF_NECESSARY(aStr) \
   if (!aStr.IsEmpty()) {                 \
     aStr.AppendLiteral(", ");            \
   }
 
 static nsCString GetMouseButtonsName(int16_t aButtons) {
   if (!aButtons) {
     return NS_LITERAL_CSTRING("no buttons");
   }
   nsCString names;
-  if (aButtons & WidgetMouseEventBase::eLeftButtonFlag) {
+  if (aButtons & MouseButtonsFlag::eLeftFlag) {
     names = "LeftButton";
   }
-  if (aButtons & WidgetMouseEventBase::eRightButtonFlag) {
+  if (aButtons & MouseButtonsFlag::eRightFlag) {
     ADD_SEPARATOR_IF_NECESSARY(names);
     names += "RightButton";
   }
-  if (aButtons & WidgetMouseEventBase::eMiddleButtonFlag) {
+  if (aButtons & MouseButtonsFlag::eMiddleFlag) {
     ADD_SEPARATOR_IF_NECESSARY(names);
     names += "MiddleButton";
   }
-  if (aButtons & WidgetMouseEventBase::e4thButtonFlag) {
+  if (aButtons & MouseButtonsFlag::e4thFlag) {
     ADD_SEPARATOR_IF_NECESSARY(names);
     names += "4thButton";
   }
-  if (aButtons & WidgetMouseEventBase::e5thButtonFlag) {
+  if (aButtons & MouseButtonsFlag::e5thFlag) {
     ADD_SEPARATOR_IF_NECESSARY(names);
     names += "5thButton";
   }
   return names;
 }
 
 static nsCString GetModifiersName(Modifiers aModifiers) {
   if (aModifiers == MODIFIER_NONE) {
@@ -6435,23 +6435,23 @@ nsresult TSFTextStore::OnMouseButtonEven
     quadrant = (quadrant + 2) % 4;
   }
   ULONG edge = quadrant < 2 ? offset + 1 : offset;
   DWORD buttonStatus = 0;
   bool isMouseUp =
       aIMENotification.mMouseButtonEventData.mEventMessage == eMouseUp;
   if (!isMouseUp) {
     switch (aIMENotification.mMouseButtonEventData.mButton) {
-      case WidgetMouseEventBase::eLeftButton:
+      case MouseButton::eLeft:
         buttonStatus = MK_LBUTTON;
         break;
-      case WidgetMouseEventBase::eMiddleButton:
+      case MouseButton::eMiddle:
         buttonStatus = MK_MBUTTON;
         break;
-      case WidgetMouseEventBase::eRightButton:
+      case MouseButton::eRight:
         buttonStatus = MK_RBUTTON;
         break;
     }
   }
   if (aIMENotification.mMouseButtonEventData.mModifiers & MODIFIER_CONTROL) {
     buttonStatus |= MK_CONTROL;
   }
   if (aIMENotification.mMouseButtonEventData.mModifiers & MODIFIER_SHIFT) {
--- a/widget/windows/nsNativeDragTarget.cpp
+++ b/widget/windows/nsNativeDragTarget.cpp
@@ -159,17 +159,17 @@ void nsNativeDragTarget::DispatchDragDro
     event.mRefPoint = LayoutDeviceIntPoint(cpos.x, cpos.y);
   } else {
     event.mRefPoint = LayoutDeviceIntPoint(0, 0);
   }
 
   ModifierKeyState modifierKeyState;
   modifierKeyState.InitInputEvent(event);
 
-  event.inputSource =
+  event.mInputSource =
       static_cast<nsBaseDragService*>(mDragService)->GetInputSource();
 
   mWidget->DispatchEvent(&event, status);
 }
 
 void nsNativeDragTarget::ProcessDrag(EventMessage aEventMessage,
                                      DWORD grfKeyState, POINTL ptl,
                                      DWORD* pdwEffect) {
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1921,17 +1921,17 @@ nsresult nsWindow::BeginResizeDrag(Widge
                                    int32_t aVertical) {
   NS_ENSURE_ARG_POINTER(aEvent);
 
   if (aEvent->mClass != eMouseEventClass) {
     // you can only begin a resize drag with a mouse event
     return NS_ERROR_INVALID_ARG;
   }
 
-  if (aEvent->AsMouseEvent()->button != WidgetMouseEvent::eLeftButton) {
+  if (aEvent->AsMouseEvent()->mButton != MouseButton::eLeft) {
     // you can only begin a resize drag with the left mouse button
     return NS_ERROR_INVALID_ARG;
   }
 
   // work out what sizemode we're talking about
   WPARAM syscommand;
   if (aVertical < 0) {
     if (aHorizontal < 0) {
@@ -4086,17 +4086,17 @@ bool nsWindow::TouchEventShouldStartDrag
 
   // In chrome UI, allow touchdownstartsdrag attributes
   // to cause any touchdown event to trigger a drag.
   if (aEventMessage == eMouseDown) {
     WidgetMouseEvent hittest(true, eMouseHitTest, this,
                              WidgetMouseEvent::eReal);
     hittest.mRefPoint = aEventPoint;
     hittest.mIgnoreRootScrollFrame = true;
-    hittest.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+    hittest.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
     DispatchInputEvent(&hittest);
 
     EventTarget* target = hittest.GetDOMEventTarget();
     if (target) {
       nsCOMPtr<nsIContent> node = do_QueryInterface(target);
 
       // Check if the element or any parent element has the
       // attribute we're looking for.
@@ -4241,61 +4241,61 @@ bool nsWindow::DispatchMouseEvent(EventM
   // XXX Should we allow to block web page to prevent its default with
   //     Ctrl+Shift+F10 or Alt+Shift+F10 instead?
   if (aEventMessage == eContextMenu && aIsContextMenuKey && event.IsShift() &&
       NativeKey::LastKeyOrCharMSG().message == WM_SYSKEYDOWN &&
       NativeKey::LastKeyOrCharMSG().wParam == VK_F10) {
     event.mModifiers &= ~MODIFIER_SHIFT;
   }
 
-  event.button = aButton;
-  event.inputSource = aInputSource;
+  event.mButton = aButton;
+  event.mInputSource = aInputSource;
   if (aPointerInfo) {
     // Mouse events from Windows WM_POINTER*. Fill more information in
     // WidgetMouseEvent.
     event.AssignPointerHelperData(*aPointerInfo);
-    event.pressure = aPointerInfo->mPressure;
-    event.buttons = aPointerInfo->mButtons;
+    event.mPressure = aPointerInfo->mPressure;
+    event.mButtons = aPointerInfo->mButtons;
   } else {
     // If we get here the mouse events must be from non-touch sources, so
     // convert it to pointer events as well
     event.convertToPointer = true;
     event.pointerId = pointerId;
   }
 
   bool insideMovementThreshold =
       (DeprecatedAbs(sLastMousePoint.x - eventPoint.x) <
        (short)::GetSystemMetrics(SM_CXDOUBLECLK)) &&
       (DeprecatedAbs(sLastMousePoint.y - eventPoint.y) <
        (short)::GetSystemMetrics(SM_CYDOUBLECLK));
 
   BYTE eventButton;
   switch (aButton) {
-    case WidgetMouseEvent::eLeftButton:
+    case MouseButton::eLeft:
       eventButton = VK_LBUTTON;
       break;
-    case WidgetMouseEvent::eMiddleButton:
+    case MouseButton::eMiddle:
       eventButton = VK_MBUTTON;
       break;
-    case WidgetMouseEvent::eRightButton:
+    case MouseButton::eRight:
       eventButton = VK_RBUTTON;
       break;
     default:
       eventButton = 0;
       break;
   }
 
   // Doubleclicks are used to set the click count, then changed to mousedowns
   // We're going to time double-clicks from mouse *up* to next mouse *down*
   LONG curMsgTime = ::GetMessageTime();
 
   switch (aEventMessage) {
     case eMouseDoubleClick:
       event.mMessage = eMouseDown;
-      event.button = aButton;
+      event.mButton = aButton;
       sLastClickCount = 2;
       sLastMouseDownTime = curMsgTime;
       break;
     case eMouseUp:
       // remember when this happened for the next mouse down
       sLastMousePoint.x = eventPoint.x;
       sLastMousePoint.y = eventPoint.y;
       sLastMouseButton = eventButton;
@@ -4331,53 +4331,53 @@ bool nsWindow::DispatchMouseEvent(EventM
           ("Msg Time: %d Click Count: %d\n", curMsgTime, event.mClickCount));
 #endif
 
   NPEvent pluginEvent;
 
   switch (aEventMessage) {
     case eMouseDown:
       switch (aButton) {
-        case WidgetMouseEvent::eLeftButton:
+        case MouseButton::eLeft:
           pluginEvent.event = WM_LBUTTONDOWN;
           break;
-        case WidgetMouseEvent::eMiddleButton:
+        case MouseButton::eMiddle:
           pluginEvent.event = WM_MBUTTONDOWN;
           break;
-        case WidgetMouseEvent::eRightButton:
+        case MouseButton::eRight:
           pluginEvent.event = WM_RBUTTONDOWN;
           break;
         default:
           break;
       }
       break;
     case eMouseUp:
       switch (aButton) {
-        case WidgetMouseEvent::eLeftButton:
+        case MouseButton::eLeft:
           pluginEvent.event = WM_LBUTTONUP;
           break;
-        case WidgetMouseEvent::eMiddleButton:
+        case MouseButton::eMiddle:
           pluginEvent.event = WM_MBUTTONUP;
           break;
-        case WidgetMouseEvent::eRightButton:
+        case MouseButton::eRight:
           pluginEvent.event = WM_RBUTTONUP;
           break;
         default:
           break;
       }
       break;
     case eMouseDoubleClick:
       switch (aButton) {
-        case WidgetMouseEvent::eLeftButton:
+        case MouseButton::eLeft:
           pluginEvent.event = WM_LBUTTONDBLCLK;
           break;
-        case WidgetMouseEvent::eMiddleButton:
+        case MouseButton::eMiddle:
           pluginEvent.event = WM_MBUTTONDBLCLK;
           break;
-        case WidgetMouseEvent::eRightButton:
+        case MouseButton::eRight:
           pluginEvent.event = WM_RBUTTONDBLCLK;
           break;
         default:
           break;
       }
       break;
     case eMouseMove:
       pluginEvent.event = WM_MOUSEMOVE;
@@ -4400,26 +4400,26 @@ bool nsWindow::DispatchMouseEvent(EventM
     if (aEventMessage == eMouseMove) {
       LayoutDeviceIntRect rect = GetBounds();
       rect.MoveTo(0, 0);
 
       if (rect.Contains(event.mRefPoint)) {
         if (sCurrentWindow == nullptr || sCurrentWindow != this) {
           if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
-            sCurrentWindow->DispatchMouseEvent(
-                eMouseExitFromWidget, wParam, pos, false,
-                WidgetMouseEvent::eLeftButton, aInputSource, aPointerInfo);
+            sCurrentWindow->DispatchMouseEvent(eMouseExitFromWidget, wParam,
+                                               pos, false, MouseButton::eLeft,
+                                               aInputSource, aPointerInfo);
           }
           sCurrentWindow = this;
           if (!mInDtor) {
             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
-            sCurrentWindow->DispatchMouseEvent(
-                eMouseEnterIntoWidget, wParam, pos, false,
-                WidgetMouseEvent::eLeftButton, aInputSource, aPointerInfo);
+            sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget, wParam,
+                                               pos, false, MouseButton::eLeft,
+                                               aInputSource, aPointerInfo);
           }
         }
       }
     } else if (aEventMessage == eMouseExitFromWidget) {
       if (sCurrentWindow == this) {
         sCurrentWindow = nullptr;
       }
     }
@@ -5331,20 +5331,20 @@ bool nsWindow::ProcessMessage(UINT msg, 
       POINT mp;
       mp.x = GET_X_LPARAM(lParamScreen);
       mp.y = GET_Y_LPARAM(lParamScreen);
       bool userMovedMouse = false;
       if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) {
         userMovedMouse = true;
       }
 
-      result = DispatchMouseEvent(
-          eMouseMove, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
-          MOUSE_INPUT_SOURCE(),
-          mPointerEvents.GetCachedPointerInfo(msg, wParam));
+      result =
+          DispatchMouseEvent(eMouseMove, wParam, lParam, false,
+                             MouseButton::eLeft, MOUSE_INPUT_SOURCE(),
+                             mPointerEvents.GetCachedPointerInfo(msg, wParam));
       if (userMovedMouse) {
         DispatchPendingEvents();
       }
     } break;
 
     case WM_NCMOUSEMOVE: {
       LPARAM lParamClient = lParamToClient(lParam);
       if (WithinDraggableRegion(GET_X_LPARAM(lParamClient),
@@ -5355,28 +5355,28 @@ bool nsWindow::ProcessMessage(UINT msg, 
       } else if (mMousePresent && !sIsInMouseCapture) {
         // If we receive a mouse move event on non-client chrome, make sure and
         // send an eMouseExitFromWidget event as well.
         SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
       }
     } break;
 
     case WM_LBUTTONDOWN: {
-      result = DispatchMouseEvent(
-          eMouseDown, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
-          MOUSE_INPUT_SOURCE(),
-          mPointerEvents.GetCachedPointerInfo(msg, wParam));
+      result =
+          DispatchMouseEvent(eMouseDown, wParam, lParam, false,
+                             MouseButton::eLeft, MOUSE_INPUT_SOURCE(),
+                             mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
     } break;
 
     case WM_LBUTTONUP: {
-      result = DispatchMouseEvent(
-          eMouseUp, wParam, lParam, false, WidgetMouseEvent::eLeftButton,
-          MOUSE_INPUT_SOURCE(),
-          mPointerEvents.GetCachedPointerInfo(msg, wParam));
+      result =
+          DispatchMouseEvent(eMouseUp, wParam, lParam, false,
+                             MouseButton::eLeft, MOUSE_INPUT_SOURCE(),
+                             mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
     } break;
 
     case WM_MOUSELEAVE: {
       if (!mMousePresent) break;
       if (mMouseInDraggableArea) break;
       mMousePresent = false;
 
@@ -5392,28 +5392,28 @@ bool nsWindow::ProcessMessage(UINT msg, 
       // wParam.
       WPARAM mouseState = (GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) |
                           (GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) |
                           (GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0);
       // Synthesize an event position because we don't get one from
       // WM_MOUSELEAVE.
       LPARAM pos = lParamToClient(::GetMessagePos());
       DispatchMouseEvent(eMouseExitFromWidget, mouseState, pos, false,
-                         WidgetMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
+                         MouseButton::eLeft, MOUSE_INPUT_SOURCE());
     } break;
 
     case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER: {
       LPARAM pos = lParamToClient(::GetMessagePos());
       MOZ_ASSERT(InkCollector::sInkCollector);
       uint16_t pointerId = InkCollector::sInkCollector->GetPointerId();
       if (pointerId != 0) {
         WinPointerInfo pointerInfo;
         pointerInfo.pointerId = pointerId;
         DispatchMouseEvent(eMouseExitFromWidget, wParam, pos, false,
-                           WidgetMouseEvent::eLeftButton,
+                           MouseButton::eLeft,
                            MouseEvent_Binding::MOZ_SOURCE_PEN, &pointerInfo);
         InkCollector::sInkCollector->ClearTarget();
         InkCollector::sInkCollector->ClearPointerId();
       }
     } break;
 
     case WM_CONTEXTMENU: {
       // If the context menu is brought up by a touch long-press, then
@@ -5432,21 +5432,20 @@ bool nsWindow::ProcessMessage(UINT msg, 
       bool contextMenukey = false;
       if (lParam == -1) {
         contextMenukey = true;
         pos = lParamToClient(GetMessagePos());
       } else {
         pos = lParamToClient(lParam);
       }
 
-      result =
-          DispatchMouseEvent(eContextMenu, wParam, pos, contextMenukey,
-                             contextMenukey ? WidgetMouseEvent::eLeftButton
-                                            : WidgetMouseEvent::eRightButton,
-                             MOUSE_INPUT_SOURCE());
+      result = DispatchMouseEvent(
+          eContextMenu, wParam, pos, contextMenukey,
+          contextMenukey ? MouseButton::eLeft : MouseButton::eRight,
+          MOUSE_INPUT_SOURCE());
       if (lParam != -1 && !result && mCustomNonClient &&
           mDraggableRegion.Contains(GET_X_LPARAM(pos), GET_Y_LPARAM(pos))) {
         // Blank area hit, throw up the system menu.
         DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, GET_X_LPARAM(lParam),
                           GET_Y_LPARAM(lParam));
         result = true;
       }
     } break;
@@ -5458,104 +5457,95 @@ bool nsWindow::ProcessMessage(UINT msg, 
       result = OnPointerEvents(msg, wParam, lParam);
       if (result) {
         DispatchPendingEvents();
       }
       break;
 
     case WM_LBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
-                                  WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eLeft, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_MBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, wParam, lParam, false,
-                                  WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_MBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, wParam, lParam, false,
-                                  WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_MBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
-                                  WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCMBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false,
-                                  WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCMBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
-                                  WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCMBUTTONDBLCLK:
-      result = DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
-                                  false, WidgetMouseEvent::eMiddleButton,
-                                  MOUSE_INPUT_SOURCE());
+      result =
+          DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
+                             false, MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONDOWN:
-      result = DispatchMouseEvent(
-          eMouseDown, wParam, lParam, false, WidgetMouseEvent::eRightButton,
-          MOUSE_INPUT_SOURCE(),
-          mPointerEvents.GetCachedPointerInfo(msg, wParam));
+      result =
+          DispatchMouseEvent(eMouseDown, wParam, lParam, false,
+                             MouseButton::eRight, MOUSE_INPUT_SOURCE(),
+                             mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONUP:
-      result = DispatchMouseEvent(
-          eMouseUp, wParam, lParam, false, WidgetMouseEvent::eRightButton,
-          MOUSE_INPUT_SOURCE(),
-          mPointerEvents.GetCachedPointerInfo(msg, wParam));
+      result =
+          DispatchMouseEvent(eMouseUp, wParam, lParam, false,
+                             MouseButton::eRight, MOUSE_INPUT_SOURCE(),
+                             mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
-                                  WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eRight, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCRBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false,
-                                  WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eRight, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCRBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
-                                  WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eRight, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_NCRBUTTONDBLCLK:
-      result = DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
-                                  false, WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE());
+      result =
+          DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
+                             false, MouseButton::eRight, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     // Windows doesn't provide to customize the behavior of 4th nor 5th button
     // of mouse.  If 5-button mouse works with standard mouse deriver of
     // Windows, users cannot disable 4th button (browser back) nor 5th button
     // (browser forward).  We should allow to do it with our prefs since we can
     // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
@@ -5676,20 +5666,19 @@ bool nsWindow::ProcessMessage(UINT msg, 
       if (mWidgetListener) {
         mWidgetListener->UIResolutionChanged();
       }
       break;
     }
 
     case WM_NCLBUTTONDBLCLK:
       DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam), false,
-                         WidgetMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
+                         MouseButton::eLeft, MOUSE_INPUT_SOURCE());
       result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
-                                  WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MouseButton::eLeft, MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_APPCOMMAND: {
       MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
       result = HandleAppCommandMsg(nativeMsg, aRetValue);
       break;
     }
@@ -6758,20 +6747,20 @@ bool nsWindow::OnGesture(WPARAM wParam, 
 
     nsEventStatus status;
 
     WidgetWheelEvent wheelEvent(true, eWheel, this);
 
     ModifierKeyState modifierKeyState;
     modifierKeyState.InitInputEvent(wheelEvent);
 
-    wheelEvent.button = 0;
+    wheelEvent.mButton = 0;
     wheelEvent.mTime = ::GetMessageTime();
     wheelEvent.mTimeStamp = GetMessageTimeStamp(wheelEvent.mTime);
-    wheelEvent.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+    wheelEvent.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
 
     bool endFeedback = true;
 
     if (mGesture.PanDeltaToPixelScroll(wheelEvent)) {
       DispatchEvent(&wheelEvent, status);
     }
 
     if (mDisplayPanFeedback) {
@@ -6793,20 +6782,20 @@ bool nsWindow::OnGesture(WPARAM wParam, 
   WidgetSimpleGestureEvent event(true, eVoidEvent, this);
   if (!mGesture.ProcessGestureMessage(mWnd, wParam, lParam, event)) {
     return false;  // fall through to DefWndProc
   }
 
   // Polish up and send off the new event
   ModifierKeyState modifierKeyState;
   modifierKeyState.InitInputEvent(event);
-  event.button = 0;
+  event.mButton = 0;
   event.mTime = ::GetMessageTime();
   event.mTimeStamp = GetMessageTimeStamp(event.mTime);
-  event.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
+  event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
 
   nsEventStatus status;
   DispatchEvent(&event, status);
   if (status == nsEventStatus_eIgnore) {
     return false;  // Ignored, fall through
   }
 
   // Only close this if we process and return true.
@@ -8101,40 +8090,37 @@ bool nsWindow::OnPointerEvents(UINT msg,
   // sLastClickCount. To prevent that, we keep the last pen down position
   // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
   // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
   // eMouseMove for WM_POINTERUPDATE.
   static POINT sLastPointerDownPoint = {0};
 
   // We don't support chorded buttons for pen. Keep the button at
   // WM_POINTERDOWN.
-  static WidgetMouseEvent::buttonType sLastPenDownButton =
-      WidgetMouseEvent::eLeftButton;
+  static mozilla::MouseButton sLastPenDownButton = MouseButton::eLeft;
   static bool sPointerDown = false;
 
   EventMessage message;
-  WidgetMouseEvent::buttonType button = WidgetMouseEvent::eLeftButton;
+  mozilla::MouseButton button = MouseButton::eLeft;
   switch (msg) {
     case WM_POINTERDOWN: {
       LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
                                       GET_Y_LPARAM(aLParam));
       sLastPointerDownPoint.x = eventPoint.x;
       sLastPointerDownPoint.y = eventPoint.y;
       message = eMouseDown;
-      button = IS_POINTER_SECONDBUTTON_WPARAM(aWParam)
-                   ? WidgetMouseEvent::eRightButton
-                   : WidgetMouseEvent::eLeftButton;
+      button = IS_POINTER_SECONDBUTTON_WPARAM(aWParam) ? MouseButton::eRight
+                                                       : MouseButton::eLeft;
       sLastPenDownButton = button;
       sPointerDown = true;
     } break;
     case WM_POINTERUP:
       message = eMouseUp;
       MOZ_ASSERT(sPointerDown, "receive WM_POINTERUP w/o WM_POINTERDOWN");
-      button =
-          sPointerDown ? sLastPenDownButton : WidgetMouseEvent::eLeftButton;
+      button = sPointerDown ? sLastPenDownButton : MouseButton::eLeft;
       sPointerDown = false;
       break;
     case WM_POINTERUPDATE:
       message = eMouseMove;
       if (sPointerDown) {
         LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
                                         GET_Y_LPARAM(aLParam));
         int32_t movementX = sLastPointerDownPoint.x > eventPoint.x
@@ -8163,20 +8149,20 @@ bool nsWindow::OnPointerEvents(UINT msg,
   }
   uint32_t pointerId = mPointerEvents.GetPointerId(aWParam);
   POINTER_PEN_INFO penInfo;
   mPointerEvents.GetPointerPenInfo(pointerId, &penInfo);
 
   // Windows defines the pen pressure is normalized to a range between 0 and
   // 1024. Convert it to float.
   float pressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
-  int16_t buttons = sPointerDown ? button == WidgetMouseEvent::eLeftButton
-                                       ? WidgetMouseEvent::eLeftButtonFlag
-                                       : WidgetMouseEvent::eRightButtonFlag
-                                 : WidgetMouseEvent::eNoButtonFlag;
+  int16_t buttons = sPointerDown ? button == MouseButton::eLeft
+                                       ? MouseButtonsFlag::eLeftFlag
+                                       : MouseButtonsFlag::eRightFlag
+                                 : MouseButtonsFlag::eNoButtons;
   WinPointerInfo pointerInfo(pointerId, penInfo.tiltX, penInfo.tiltY, pressure,
                              buttons);
 
   // The aLParam of WM_POINTER* is the screen location. Convert it to client
   // location
   LPARAM newLParam = lParamToClient(aLParam);
   DispatchMouseEvent(message, aWParam, newLParam, false, button,
                      MouseEvent_Binding::MOZ_SOURCE_PEN, &pointerInfo);
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -233,17 +233,17 @@ class nsWindow final : public nsWindowBa
   virtual void SetWindowClass(const nsAString& xulWinType) override;
 
   /**
    * Event helpers
    */
   virtual bool DispatchMouseEvent(
       mozilla::EventMessage aEventMessage, WPARAM wParam, LPARAM lParam,
       bool aIsContextMenuKey = false,
-      int16_t aButton = mozilla::WidgetMouseEvent::eLeftButton,
+      int16_t aButton = mozilla::MouseButton::eLeft,
       uint16_t aInputSource =
           mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE,
       WinPointerInfo* aPointerInfo = nullptr);
   virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent* aEvent,
                                    nsEventStatus& aStatus);
   void DispatchPendingEvents();
   bool DispatchPluginEvent(UINT aMessage, WPARAM aWParam, LPARAM aLParam,
                            bool aDispatchPendingEvents);
--- a/xpcom/build/Services.py
+++ b/xpcom/build/Services.py
@@ -47,16 +47,19 @@ service('ActivityDistributor', 'nsIHttpA
 service('HistoryService', 'mozilla::IHistory',
         "@mozilla.org/browser/history;1")
 service('ThirdPartyUtil', 'mozIThirdPartyUtil',
         "@mozilla.org/thirdpartyutil;1")
 service('URIFixup', 'nsIURIFixup',
         "@mozilla.org/docshell/urifixup;1")
 service('Bits', 'nsIBits',
         "@mozilla.org/bits;1")
+# NB: this should also expose nsIXULAppInfo, as does Services.jsm.
+service('AppInfoService', 'nsIXULRuntime',
+        "@mozilla.org/xre/app-info;1")
 
 # The definition file needs access to the definitions of the particular
 # interfaces. If you add a new interface here, make sure the necessary includes
 # are also listed in the following code snippet.
 CPP_INCLUDES = """
 #include "mozilla/Likely.h"
 #include "mozilla/Services.h"
 #include "mozIThirdPartyUtil.h"
@@ -80,16 +83,17 @@ CPP_INCLUDES = """
 #include "nsISocketTransportService.h"
 #include "nsIURIClassifier.h"
 #include "nsIHttpActivityObserver.h"
 #include "nsIAsyncShutdown.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIGfxInfo.h"
 #include "nsIURIFixup.h"
 #include "nsIBits.h"
+#include "nsIXULRuntime.h"
 """
 
 #####
 # Codegen Logic
 #
 # The following code consumes the data listed above to generate the files
 # Services.h, Services.cpp, and services.rs which provide access to these
 # service getters in both rust and C++ code.
--- a/xpcom/rust/moz_task/src/lib.rs
+++ b/xpcom/rust/moz_task/src/lib.rs
@@ -13,21 +13,22 @@ extern crate nsstring;
 extern crate xpcom;
 
 use nserror::{nsresult, NS_OK};
 use nsstring::{nsACString, nsCString};
 use std::{
     marker::PhantomData,
     mem, ptr,
     sync::atomic::{AtomicBool, Ordering},
+    ffi::CStr,
 };
 use xpcom::{
     getter_addrefs,
     interfaces::{nsIEventTarget, nsIRunnable, nsISupports, nsIThread},
-    xpcom, xpcom_method, AtomicRefcnt, NulTerminatedCStr, RefCounted, RefPtr, XpCom,
+    xpcom, xpcom_method, AtomicRefcnt, RefCounted, RefPtr, XpCom,
 };
 
 extern "C" {
     fn NS_GetCurrentThreadEventTarget(result: *mut *const nsIThread) -> nsresult;
     fn NS_GetMainThreadEventTarget(result: *mut *const nsIThread) -> nsresult;
     fn NS_IsMainThread() -> bool;
     fn NS_NewNamedThreadWithDefaultStackSize(
         name: *const nsACString,
@@ -136,17 +137,17 @@ impl TaskRunnable {
 pub type ThreadPtrHandle<T> = RefPtr<ThreadPtrHolder<T>>;
 
 /// A Rust analog to `nsMainThreadPtrHolder` that wraps an `nsISupports` object
 /// with thread-safe refcounting. The holder keeps one reference to the wrapped
 /// object that's released when the holder's refcount reaches zero.
 pub struct ThreadPtrHolder<T: XpCom + 'static> {
     ptr: *const T,
     marker: PhantomData<T>,
-    name: NulTerminatedCStr,
+    name: &'static CStr,
     owning_thread: RefPtr<nsIThread>,
     refcnt: AtomicRefcnt,
 }
 
 unsafe impl<T: XpCom + 'static> Send for ThreadPtrHolder<T> {}
 unsafe impl<T: XpCom + 'static> Sync for ThreadPtrHolder<T> {}
 
 unsafe impl<T: XpCom + 'static> RefCounted for ThreadPtrHolder<T> {
@@ -179,17 +180,17 @@ unsafe impl<T: XpCom + 'static> RefCount
             Box::from_raw(self as *const Self as *mut Self);
         }
     }
 }
 
 impl<T: XpCom + 'static> ThreadPtrHolder<T> {
     /// Creates a new owning thread pointer holder. Returns an error if the
     /// thread manager has shut down. Panics if `name` isn't a valid C string.
-    pub fn new(name: NulTerminatedCStr, ptr: RefPtr<T>) -> Result<RefPtr<Self>, nsresult> {
+    pub fn new(name: &'static CStr, ptr: RefPtr<T>) -> Result<RefPtr<Self>, nsresult> {
         let owning_thread = get_current_thread()?;
         // Take ownership of the `RefPtr`. This does _not_ decrement its
         // refcount, which is what we want. Once we've released all references
         // to the holder, we'll release the wrapped `RefPtr`.
         let raw: *const T = &*ptr;
         mem::forget(ptr);
         unsafe {
             let boxed = Box::new(ThreadPtrHolder {
--- a/xpcom/rust/nserror/src/lib.rs
+++ b/xpcom/rust/nserror/src/lib.rs
@@ -47,16 +47,28 @@ impl fmt::Display for nsresult {
 }
 
 impl fmt::Debug for nsresult {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{}", self.error_name())
     }
 }
 
+impl<T, E> From<Result<T, E>> for nsresult
+where
+    E: Into<nsresult>,
+{
+    fn from(result: Result<T, E>) -> nsresult {
+        match result {
+            Ok(_) => NS_OK,
+            Err(e) => e.into(),
+        }
+    }
+}
+
 impl Error for nsresult {}
 
 extern "C" {
     fn Gecko_GetErrorName(rv: nsresult, cstr: *mut nsACString);
 }
 
 mod error_list {
     include!(concat!(env!("MOZ_TOPOBJDIR"), "/xpcom/base/error_list.rs"));
deleted file mode 100644
--- a/xpcom/rust/xpcom/src/cstr.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-/* 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/. */
-
-use libc::c_char;
-
-/// Creates a static C string by adding a nul terminator to `$str`.
-#[macro_export]
-macro_rules! c_str {
-    ($str:expr) => {
-        $crate::NulTerminatedCStr(concat!($str, '\0').as_bytes())
-    };
-}
-
-/// A nul-terminated, static C string. This should only be created via the
-/// `c_str` macro.
-pub struct NulTerminatedCStr(pub &'static [u8]);
-
-impl NulTerminatedCStr {
-    /// Returns a raw pointer to this string, asserting that it's
-    /// nul-terminated. This pointer can be passed to any C function expecting a
-    /// `const char*`, or any XPIDL method expecting an `in string`.
-    #[inline]
-    pub fn as_ptr(&self) -> *const c_char {
-        assert_eq!(self.0.last(), Some(&0), "C strings must be nul-terminated");
-        self.0 as *const [u8] as *const c_char
-    }
-}
--- a/xpcom/rust/xpcom/src/lib.rs
+++ b/xpcom/rust/xpcom/src/lib.rs
@@ -23,19 +23,16 @@ extern crate thin_vec;
 extern crate xpcom_macros;
 #[doc(hidden)]
 pub use xpcom_macros::*;
 
 // Helper functions and data structures are exported in the root of the crate.
 mod base;
 pub use base::*;
 
-mod cstr;
-pub use cstr::*;
-
 // Declarative macro to generate XPCOM method stubs.
 mod method;
 pub use method::*;
 
 mod refptr;
 pub use refptr::*;
 
 mod statics;
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -50,16 +50,17 @@
 #include "nsGlobalWindow.h"
 #include "XULDocument.h"
 #include "nsXULTooltipListener.h"
 
 #include "prenv.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
+#include "mozilla/XULStore.h"
 #include "mozilla/dom/BarProps.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
 
 using namespace mozilla;
 using dom::AutoNoJSAPI;
@@ -1598,25 +1599,18 @@ nsresult nsXULWindow::GetPersistentValue
   if (!docURI) {
     return NS_ERROR_FAILURE;
   }
   nsAutoCString utf8uri;
   nsresult rv = docURI->GetSpec(utf8uri);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ConvertUTF8toUTF16 uri(utf8uri);
 
-  if (!mLocalStore) {
-    mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
-    if (NS_WARN_IF(!mLocalStore)) {
-      return NS_ERROR_NOT_INITIALIZED;
-    }
-  }
-
-  rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
-                             aValue);
+  nsDependentAtomString attrString(aAttr);
+  rv = XULStore::GetValue(uri, windowElementId, attrString, aValue);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
     // Convert attributes from outer size to inner size for top-level
     // windows, see bug 1444525 & co.
     ConvertWindowSize(this, aAttr, ConversionDirection::OuterToInner, aValue);
@@ -1656,25 +1650,19 @@ nsresult nsXULWindow::SetPersistentValue
   nsAutoString maybeConvertedValue(aValue);
   if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
     // Make sure we store the <window> attributes as outer window size, see
     // bug 1444525 & co.
     ConvertWindowSize(this, aAttr, ConversionDirection::InnerToOuter,
                       maybeConvertedValue);
   }
 
-  if (!mLocalStore) {
-    mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
-    if (NS_WARN_IF(!mLocalStore)) {
-      return NS_ERROR_NOT_INITIALIZED;
-    }
-  }
-
-  return mLocalStore->SetValue(
-      uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
+  nsDependentAtomString attrString(aAttr);
+  return XULStore::SetValue(uri, windowElementId, attrString,
+                            maybeConvertedValue);
 }
 
 NS_IMETHODIMP nsXULWindow::SavePersistentAttributes() {
   // can happen when the persistence timer fires at an inopportune time
   // during window shutdown
   if (!mDocShell) return NS_ERROR_FAILURE;
 
   nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -28,17 +28,16 @@
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIXULWindow.h"
 #include "nsIPrompt.h"
 #include "nsIAuthPrompt.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsIWidgetListener.h"
 #include "nsITabParent.h"
-#include "nsIXULStore.h"
 
 namespace mozilla {
 namespace dom {
 class Element;
 }  // namespace dom
 }  // namespace mozilla
 
 class nsAtom;
@@ -190,13 +189,12 @@ class nsXULWindow : public nsIBaseWindow
  private:
   // GetPrimaryTabParentSize is called from xpidl methods and we don't have a
   // good way to annotate those with MOZ_CAN_RUN_SCRIPT yet.  It takes no
   // refcounted args other than "this", and the "this" uses seem ok.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
   GetPrimaryTabParentSize(int32_t* aWidth, int32_t* aHeight);
   nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight);
   nsresult SetPrimaryTabParentSize(int32_t aWidth, int32_t aHeight);
-  nsCOMPtr<nsIXULStore> mLocalStore;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID)
 #endif /* nsXULWindow_h__ */