Merge mozilla-central to inbound. a=merge CLOSED TREE
authorshindli <shindli@mozilla.com>
Mon, 22 Apr 2019 12:51:50 +0300
changeset 470332 ab1da7fa2ad0aa1c9d6d0a9e2f63e6492cd1b712
parent 470316 2e9187dc17a928962173b04bf39dcdee57805dd8 (current diff)
parent 470331 b783cd5203ea589bb7505852e5108ed142d2d37a (diff)
child 470333 06eb1ecb4aed1e84554693ceeebddc93c5f4f686
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
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
testing/web-platform/meta/css/css-transitions/hidden-container-001.html.ini
testing/web-platform/tests/css/css-transitions/hidden-container-001.html
xpcom/rust/xpcom/src/cstr.rs
--- 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__ */