Backed out 3 changesets (bug 1719546) for causing SM bustages at Bidi.h. CLOSED TREE
authorButkovits Atila <abutkovits@mozilla.com>
Tue, 19 Oct 2021 21:09:47 +0300
changeset 596378 c00a194497881c0eb6ad3574729c9e275cd26bd5
parent 596377 4912996ac38406438b174570ce1f8201508dac03
child 596379 55a504b8e61760bd89e5d34cbd313f4ff0dbd00a
push id38896
push userabutkovits@mozilla.com
push dateTue, 19 Oct 2021 21:51:00 +0000
treeherdermozilla-central@e9071741b84c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1719546
milestone95.0a1
backs outc4d6ffc7e44521efefa033fe81cba6e1326706f0
a1f7ed6c42510b8a97ea6fe0393ec0c20f43a1f3
e69fc596f2c3f306c3bab26d6c41f652f4e124d9
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
Backed out 3 changesets (bug 1719546) for causing SM bustages at Bidi.h. CLOSED TREE Backed out changeset c4d6ffc7e445 (bug 1719546) Backed out changeset a1f7ed6c4251 (bug 1719546) Backed out changeset e69fc596f2c3 (bug 1719546)
dom/base/Selection.cpp
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
intl/components/gtest/TestBidi.cpp
intl/components/gtest/moz.build
intl/components/moz.build
intl/components/src/Bidi.cpp
intl/components/src/Bidi.h
intl/unicharutil/util/nsBidiUtils.h
layout/base/moz.build
layout/base/nsBidi.cpp
layout/base/nsBidi.h
layout/base/nsBidiPresUtils.cpp
layout/base/nsBidiPresUtils.h
layout/base/nsCaret.cpp
layout/base/nsCaret.h
layout/base/nsLayoutUtils.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/generic/WritingModes.h
layout/generic/nsFrameList.cpp
layout/generic/nsFrameSelection.cpp
layout/generic/nsFrameSelection.h
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsIFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsImageFrame.cpp
layout/generic/nsTextFrame.cpp
layout/xul/nsTextBoxFrame.cpp
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -4,17 +4,16 @@
  * 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/. */
 
 /*
  * Implementation of mozilla::dom::Selection
  */
 
 #include "mozilla/dom/Selection.h"
-#include "mozilla/intl/Bidi.h"
 
 #include "mozilla/AccessibleCaretEventHub.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoCopyListener.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ContentIterator.h"
@@ -381,19 +380,17 @@ bool Selection::IsEditorSelection() cons
 Nullable<int16_t> Selection::GetCaretBidiLevel(
     mozilla::ErrorResult& aRv) const {
   MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
 
   if (!mFrameSelection) {
     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
     return Nullable<int16_t>();
   }
-  mozilla::intl::Bidi::EmbeddingLevel caretBidiLevel =
-      static_cast<mozilla::intl::Bidi::EmbeddingLevel>(
-          mFrameSelection->GetCaretBidiLevel());
+  nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
   return (caretBidiLevel & BIDI_LEVEL_UNDEFINED)
              ? Nullable<int16_t>()
              : Nullable<int16_t>(caretBidiLevel);
 }
 
 void Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel,
                                   mozilla::ErrorResult& aRv) {
   MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
@@ -401,17 +398,17 @@ void Selection::SetCaretBidiLevel(const 
   if (!mFrameSelection) {
     aRv.Throw(NS_ERROR_NOT_INITIALIZED);
     return;
   }
   if (aCaretBidiLevel.IsNull()) {
     mFrameSelection->UndefineCaretBidiLevel();
   } else {
     mFrameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(
-        mozilla::intl::Bidi::EmbeddingLevel(aCaretBidiLevel.Value()));
+        aCaretBidiLevel.Value());
   }
 }
 
 /**
  * Test whether the supplied range points to a single table element.
  * Result is one of the TableSelectionMode constants. "None" means
  * a table element isn't selected.
  */
@@ -1355,18 +1352,17 @@ nsIFrame* Selection::GetPrimaryOrCaretFr
 
   if (!mFrameSelection) {
     return nullptr;
   }
 
   CaretAssociationHint hint = mFrameSelection->GetHint();
 
   if (aVisual) {
-    mozilla::intl::Bidi::EmbeddingLevel caretBidiLevel =
-        mFrameSelection->GetCaretBidiLevel();
+    nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
 
     return nsCaret::GetCaretFrameForNodeOffset(
         mFrameSelection, aContent, aOffset, hint, caretBidiLevel,
         /* aReturnUnadjustedFrame = */ nullptr, aOffsetUsed);
   }
 
   return nsFrameSelection::GetFrameForNodeOffset(aContent, aOffset, hint,
                                                  aOffsetUsed);
@@ -3297,20 +3293,19 @@ void Selection::Modify(const nsAString& 
     }
     uint32_t focusOffset = FocusOffset();
     CollapseInLimiter(focusNode, focusOffset);
   }
 
   // If the paragraph direction of the focused frame is right-to-left,
   // we may have to swap the direction of movement.
   if (nsIFrame* frame = GetPrimaryFrameForFocusNode(visual)) {
-    mozilla::intl::Bidi::Direction paraDir =
-        nsBidiPresUtils::ParagraphDirection(frame);
-
-    if (paraDir == mozilla::intl::Bidi::Direction::RTL && visual) {
+    nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
+
+    if (paraDir == NSBIDI_RTL && visual) {
       if (amount == eSelectBeginLine) {
         amount = eSelectEndLine;
         forward = !forward;
       } else if (amount == eSelectEndLine) {
         amount = eSelectBeginLine;
         forward = !forward;
       }
     }
@@ -3474,38 +3469,36 @@ void Selection::SetStartAndEndInternal(I
 nsresult Selection::SelectionLanguageChange(bool aLangRTL) {
   if (!mFrameSelection) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
 
   // if the direction of the language hasn't changed, nothing to do
-  mozilla::intl::Bidi::EmbeddingLevel kbdBidiLevel =
-      aLangRTL ? mozilla::intl::Bidi::EmbeddingLevel::RTL()
-               : mozilla::intl::Bidi::EmbeddingLevel::LTR();
+  nsBidiLevel kbdBidiLevel = aLangRTL ? NSBIDI_RTL : NSBIDI_LTR;
   if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
     return NS_OK;
   }
 
   frameSelection->mKbdBidiLevel = kbdBidiLevel;
 
   nsIFrame* focusFrame = GetPrimaryFrameForFocusNode(false);
   if (!focusFrame) {
     return NS_ERROR_FAILURE;
   }
 
   auto [frameStart, frameEnd] = focusFrame->GetOffsets();
   RefPtr<nsPresContext> context = GetPresContext();
-  mozilla::intl::Bidi::EmbeddingLevel levelBefore, levelAfter;
+  nsBidiLevel levelBefore, levelAfter;
   if (!context) {
     return NS_ERROR_FAILURE;
   }
 
-  mozilla::intl::Bidi::EmbeddingLevel level = focusFrame->GetEmbeddingLevel();
+  nsBidiLevel level = focusFrame->GetEmbeddingLevel();
   int32_t focusOffset = static_cast<int32_t>(FocusOffset());
   if ((focusOffset != frameStart) && (focusOffset != frameEnd))
     // the cursor is not at a frame boundary, so the level of both the
     // characters (logically) before and after the cursor is equal to the frame
     // level
     levelBefore = levelAfter = level;
   else {
     // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find
@@ -3513,40 +3506,36 @@ nsresult Selection::SelectionLanguageCha
     nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
     nsPrevNextBidiLevels levels =
         frameSelection->GetPrevNextBidiLevels(focusContent, focusOffset, false);
 
     levelBefore = levels.mLevelBefore;
     levelAfter = levels.mLevelAfter;
   }
 
-  if (levelBefore.IsSameDirection(levelAfter)) {
+  if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
     // if cursor is between two characters with the same orientation, changing
     // the keyboard language must toggle the cursor level between the level of
     // the character with the lowest level (if the new language corresponds to
     // the orientation of that character) and this level plus 1 (if the new
     // language corresponds to the opposite orientation)
-    if ((level != levelBefore) && (level != levelAfter)) {
+    if ((level != levelBefore) && (level != levelAfter))
       level = std::min(levelBefore, levelAfter);
-    }
-    if (level.IsSameDirection(kbdBidiLevel)) {
+    if (IS_SAME_DIRECTION(level, kbdBidiLevel))
       frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(level);
-    } else {
-      frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(
-          mozilla::intl::Bidi::EmbeddingLevel(level + 1));
-    }
+    else
+      frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(level + 1);
   } else {
     // if cursor is between characters with opposite orientations, changing the
     // keyboard language must change the cursor level to that of the adjacent
     // character with the orientation corresponding to the new language.
-    if (levelBefore.IsSameDirection(kbdBidiLevel)) {
+    if (IS_SAME_DIRECTION(levelBefore, kbdBidiLevel))
       frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(levelBefore);
-    } else {
+    else
       frameSelection->SetCaretBidiLevelAndMaybeSchedulePaint(levelAfter);
-    }
   }
 
   // The caret might have moved, so invalidate the desired position
   // for future usages of up-arrow or down-arrow
   frameSelection->InvalidateDesiredCaretPos();
 
   return NS_OK;
 }
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -7,17 +7,16 @@
 
 #include "mozilla/gfx/Helpers.h"
 #include "nsXULElement.h"
 
 #include "nsMathUtils.h"
 
 #include "nsContentUtils.h"
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/SVGImageContext.h"
 #include "mozilla/SVGObserverUtils.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/GeneratePlaceholderCanvasData.h"
 #include "nsPresContext.h"
@@ -3499,21 +3498,21 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
     if (mMissingFonts) {
       mMissingFonts->Flush();
     }
   }
 
   using ContextState = CanvasRenderingContext2D::ContextState;
 
   virtual void SetText(const char16_t* aText, int32_t aLength,
-                       mozilla::intl::Bidi::Direction aDirection) override {
+                       nsBidiDirection aDirection) override {
     mFontgrp->UpdateUserFonts();  // ensure user font generation is current
     // adjust flags for current direction run
     gfx::ShapedTextFlags flags = mTextRunFlags;
-    if (aDirection == mozilla::intl::Bidi::Direction::RTL) {
+    if (aDirection == NSBIDI_RTL) {
       flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
     } else {
       flags &= ~gfx::ShapedTextFlags::TEXT_IS_RTL;
     }
     mTextRun = mFontgrp->MakeTextRun(
         aText, aLength, mDrawTarget, mAppUnitsPerDevPixel, flags,
         nsTextFrameUtils::Flags::DontSkipDrawingForPendingUserFonts,
         mMissingFonts.get());
@@ -3869,19 +3868,17 @@ TextMetrics* CanvasRenderingContext2D::D
                                     aOp == TextDrawOperation::MEASURE;
   processor.mFontgrp = currentFontStyle;
 
   nscoord totalWidthCoord;
 
   // calls bidi algo twice since it needs the full text width and the
   // bounding boxes before rendering anything
   aError = nsBidiPresUtils::ProcessText(
-      textToDraw.get(), textToDraw.Length(),
-      isRTL ? mozilla::intl::Bidi::EmbeddingLevel::RTL()
-            : mozilla::intl::Bidi::EmbeddingLevel::LTR(),
+      textToDraw.get(), textToDraw.Length(), isRTL ? NSBIDI_RTL : NSBIDI_LTR,
       presShell->GetPresContext(), processor, nsBidiPresUtils::MODE_MEASURE,
       nullptr, 0, &totalWidthCoord, &mBidiEngine);
   if (aError.Failed()) {
     return nullptr;
   }
 
   float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
 
@@ -4012,19 +4009,17 @@ TextMetrics* CanvasRenderingContext2D::D
 
   // save the previous bounding box
   gfxRect boundingBox = processor.mBoundingBox;
 
   // don't ever need to measure the bounding box twice
   processor.mDoMeasureBoundingBox = false;
 
   aError = nsBidiPresUtils::ProcessText(
-      textToDraw.get(), textToDraw.Length(),
-      isRTL ? mozilla::intl::Bidi::EmbeddingLevel::RTL()
-            : mozilla::intl::Bidi::EmbeddingLevel::LTR(),
+      textToDraw.get(), textToDraw.Length(), isRTL ? NSBIDI_RTL : NSBIDI_LTR,
       presShell->GetPresContext(), processor, nsBidiPresUtils::MODE_DRAW,
       nullptr, 0, nullptr, &mBidiEngine);
 
   if (aError.Failed()) {
     return nullptr;
   }
 
   mTarget->SetTransform(oldTransform);
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -4,29 +4,28 @@
 
 #ifndef CanvasRenderingContext2D_h
 #define CanvasRenderingContext2D_h
 
 #include <vector>
 #include "mozilla/dom/BasicRenderingContext2D.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SurfaceFromElementResult.h"
 #include "mozilla/UniquePtr.h"
 #include "FilterDescription.h"
 #include "gfx2DGlue.h"
 #include "nsICanvasRenderingContextInternal.h"
+#include "nsBidi.h"
 #include "nsColor.h"
-#include "nsIFrame.h"
 
 class gfxFontGroup;
 class nsGlobalWindowInner;
 class nsXULElement;
 
 namespace mozilla {
 class ErrorResult;
 class PresShell;
@@ -793,17 +792,17 @@ class CanvasRenderingContext2D final : p
     RefPtr<Element> mElement;
     // Path of the hit region in the 2d context coordinate space (not user
     // space)
     RefPtr<gfx::Path> mPath;
   };
 
   nsTArray<RegionInfo> mHitRegionsOptions;
 
-  mozilla::intl::Bidi mBidiEngine;
+  nsBidi mBidiEngine;
 
   /**
    * Returns true if a shadow should be drawn along with a
    * drawing operation.
    */
   bool NeedToDrawShadow() {
     const ContextState& state = CurrentState();
 
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -5,33 +5,32 @@
 
 #include "EditorBase.h"
 
 #include "mozilla/DebugOnly.h"  // for DebugOnly
 
 #include <stdio.h>   // for nullptr, stdout
 #include <string.h>  // for strcmp
 
-#include "ChangeAttributeTransaction.h"  // for ChangeAttributeTransaction
-#include "CompositionTransaction.h"      // for CompositionTransaction
-#include "CreateElementTransaction.h"    // for CreateElementTransaction
-#include "DeleteNodeTransaction.h"       // for DeleteNodeTransaction
-#include "DeleteRangeTransaction.h"      // for DeleteRangeTransaction
-#include "DeleteTextTransaction.h"       // for DeleteTextTransaction
-#include "EditAggregateTransaction.h"    // for EditAggregateTransaction
-#include "EditTransactionBase.h"         // for EditTransactionBase
-#include "EditorEventListener.h"         // for EditorEventListener
-#include "gfxFontUtils.h"                // for gfxFontUtils
-#include "HTMLEditUtils.h"               // for HTMLEditUtils
-#include "InsertNodeTransaction.h"       // for InsertNodeTransaction
-#include "InsertTextTransaction.h"       // for InsertTextTransaction
-#include "JoinNodeTransaction.h"         // for JoinNodeTransaction
-#include "PlaceholderTransaction.h"      // for PlaceholderTransaction
-#include "SplitNodeTransaction.h"        // for SplitNodeTransaction
-#include "mozilla/intl/Bidi.h"
+#include "ChangeAttributeTransaction.h"       // for ChangeAttributeTransaction
+#include "CompositionTransaction.h"           // for CompositionTransaction
+#include "CreateElementTransaction.h"         // for CreateElementTransaction
+#include "DeleteNodeTransaction.h"            // for DeleteNodeTransaction
+#include "DeleteRangeTransaction.h"           // for DeleteRangeTransaction
+#include "DeleteTextTransaction.h"            // for DeleteTextTransaction
+#include "EditAggregateTransaction.h"         // for EditAggregateTransaction
+#include "EditTransactionBase.h"              // for EditTransactionBase
+#include "EditorEventListener.h"              // for EditorEventListener
+#include "gfxFontUtils.h"                     // for gfxFontUtils
+#include "HTMLEditUtils.h"                    // for HTMLEditUtils
+#include "InsertNodeTransaction.h"            // for InsertNodeTransaction
+#include "InsertTextTransaction.h"            // for InsertTextTransaction
+#include "JoinNodeTransaction.h"              // for JoinNodeTransaction
+#include "PlaceholderTransaction.h"           // for PlaceholderTransaction
+#include "SplitNodeTransaction.h"             // for SplitNodeTransaction
 #include "mozilla/BasePrincipal.h"            // for BasePrincipal
 #include "mozilla/CheckedInt.h"               // for CheckedInt
 #include "mozilla/ComposerCommandsUpdater.h"  // for ComposerCommandsUpdater
 #include "mozilla/ContentEvents.h"            // for InternalClipboardEvent
 #include "mozilla/CSSEditUtils.h"             // for CSSEditUtils
 #include "mozilla/EditAction.h"               // for EditSubAction
 #include "mozilla/EditorDOMPoint.h"           // for EditorDOMPoint
 #include "mozilla/EditorSpellCheck.h"         // for EditorSpellCheck
@@ -5763,23 +5762,22 @@ EditorBase::AutoCaretBidiLevelManager::A
   if (NS_WARN_IF(!frameSelection)) {
     mFailed = true;
     return;
   }
 
   nsPrevNextBidiLevels levels = frameSelection->GetPrevNextBidiLevels(
       aPointAtCaret.GetContainerAsContent(), aPointAtCaret.Offset(), true);
 
-  mozilla::intl::Bidi::EmbeddingLevel levelBefore = levels.mLevelBefore;
-  mozilla::intl::Bidi::EmbeddingLevel levelAfter = levels.mLevelAfter;
-
-  mozilla::intl::Bidi::EmbeddingLevel currentCaretLevel =
-      frameSelection->GetCaretBidiLevel();
-
-  mozilla::intl::Bidi::EmbeddingLevel levelOfDeletion;
+  nsBidiLevel levelBefore = levels.mLevelBefore;
+  nsBidiLevel levelAfter = levels.mLevelAfter;
+
+  nsBidiLevel currentCaretLevel = frameSelection->GetCaretBidiLevel();
+
+  nsBidiLevel levelOfDeletion;
   levelOfDeletion = (nsIEditor::eNext == aDirectionAndAmount ||
                      nsIEditor::eNextWord == aDirectionAndAmount)
                         ? levelAfter
                         : levelBefore;
 
   if (currentCaretLevel == levelOfDeletion) {
     return;  // Perform the deletion
   }
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_EditorBase_h
 #define mozilla_EditorBase_h
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/Assertions.h"          // for MOZ_ASSERT, etc.
 #include "mozilla/EditAction.h"          // for EditAction and EditSubAction
 #include "mozilla/EditorDOMPoint.h"      // for EditorDOMPoint
 #include "mozilla/EventForwards.h"       // for InputEventTargetRanges
 #include "mozilla/Maybe.h"               // for Maybe
 #include "mozilla/OwningNonNull.h"       // for OwningNonNull
 #include "mozilla/TypeInState.h"         // for PropItem, StyleCache
 #include "mozilla/RangeBoundary.h"       // for RawRangeBoundary, RangeBoundary
@@ -24,16 +23,17 @@
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Text.h"
 #include "nsAtom.h"    // for nsAtom, nsStaticAtom
 #include "nsCOMPtr.h"  // for already_AddRefed, nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "nsIContentInlines.h"       // for nsINode::IsEditable()
 #include "nsIEditor.h"               // for nsIEditor, etc.
+#include "nsIFrame.h"                // for nsBidiLevel
 #include "nsISelectionController.h"  // for nsISelectionController constants
 #include "nsISelectionListener.h"    // for nsISelectionListener
 #include "nsISupportsImpl.h"         // for EditorBase::Release, etc.
 #include "nsIWeakReferenceUtils.h"   // for nsWeakPtr
 #include "nsLiteralString.h"         // for NS_LITERAL_STRING
 #include "nsPIDOMWindow.h"           // for nsPIDOMWindowInner, etc.
 #include "nsString.h"                // for nsCString
 #include "nsTArray.h"                // for nsTArray and nsAutoTArray
@@ -1980,17 +1980,17 @@ class EditorBase : public nsIEditor,
 
     /**
      * MaybeUpdateCaretBidiLevel() may update caret bidi level and schedule to
      * paint it if they are necessary.
      */
     void MaybeUpdateCaretBidiLevel(const EditorBase& aEditorBase) const;
 
    private:
-    Maybe<mozilla::intl::Bidi::EmbeddingLevel> mNewCaretBidiLevel;
+    Maybe<nsBidiLevel> mNewCaretBidiLevel;
     bool mFailed = false;
     bool mCanceled = false;
   };
 
   /**
    * UndefineCaretBidiLevel() resets bidi level of the caret.
    */
   void UndefineCaretBidiLevel() const;
deleted file mode 100644
--- a/intl/components/gtest/TestBidi.cpp
+++ /dev/null
@@ -1,278 +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/. */
-#include "gtest/gtest.h"
-
-#include "mozilla/intl/Bidi.h"
-#include "mozilla/Span.h"
-namespace mozilla::intl {
-
-struct VisualRun {
-  Span<const char16_t> string;
-  Bidi::Direction direction;
-};
-
-/**
- * An iterator for visual runs in a paragraph. See Bug 1736597 for integrating
- * this into the public API.
- */
-class MOZ_STACK_CLASS VisualRunIter {
- public:
-  VisualRunIter(Bidi& aBidi, Span<const char16_t> aParagraph,
-                Bidi::EmbeddingLevel aLevel)
-      : mBidi(aBidi), mParagraph(aParagraph) {
-    // Crash in case of errors by calling unwrap. If this were a real API, this
-    // would be a TryCreate call.
-    mBidi.SetParagraph(aParagraph, aLevel).unwrap();
-    mRunCount = mBidi.CountRuns().unwrap();
-  }
-
-  Maybe<VisualRun> Next() {
-    if (mRunIndex >= mRunCount) {
-      return Nothing();
-    }
-
-    int32_t stringIndex = -1;
-    int32_t stringLength = -1;
-
-    Bidi::Direction direction =
-        mBidi.GetVisualRun(mRunIndex, &stringIndex, &stringLength);
-
-    Span<const char16_t> string(mParagraph.Elements() + stringIndex,
-                                stringLength);
-    mRunIndex++;
-    return Some(VisualRun{string, direction});
-  }
-
- private:
-  Bidi& mBidi;
-  Span<const char16_t> mParagraph = Span<const char16_t>();
-  int32_t mRunIndex = 0;
-  int32_t mRunCount = 0;
-};
-
-struct LogicalRun {
-  Span<const char16_t> string;
-  Bidi::EmbeddingLevel embeddingLevel;
-};
-
-/**
- * An iterator for logical runs in a paragraph. See Bug 1736597 for integrating
- * this into the public API.
- */
-class MOZ_STACK_CLASS LogicalRunIter {
- public:
-  LogicalRunIter(Bidi& aBidi, Span<const char16_t> aParagraph,
-                 Bidi::EmbeddingLevel aLevel)
-      : mBidi(aBidi), mParagraph(aParagraph) {
-    // Crash in case of errors by calling unwrap. If this were a real API, this
-    // would be a TryCreate call.
-    mBidi.SetParagraph(aParagraph, aLevel).unwrap();
-    mBidi.CountRuns().unwrap();
-  }
-
-  Maybe<LogicalRun> Next() {
-    if (mRunIndex >= static_cast<int32_t>(mParagraph.Length())) {
-      return Nothing();
-    }
-
-    int32_t logicalLimit;
-
-    Bidi::EmbeddingLevel embeddingLevel;
-    mBidi.GetLogicalRun(mRunIndex, &logicalLimit, &embeddingLevel);
-
-    Span<const char16_t> string(mParagraph.Elements() + mRunIndex,
-                                logicalLimit - mRunIndex);
-
-    mRunIndex = logicalLimit;
-    return Some(LogicalRun{string, embeddingLevel});
-  }
-
- private:
-  Bidi& mBidi;
-  Span<const char16_t> mParagraph = Span<const char16_t>();
-  int32_t mRunIndex = 0;
-};
-
-TEST(IntlBidi, SimpleLTR)
-{
-  Bidi bidi{};
-  LogicalRunIter logicalRunIter(bidi, MakeStringSpan(u"this is a paragraph"),
-                                Bidi::EmbeddingLevel::DefaultLTR());
-  ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
-  ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::LTR);
-
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u"this is a paragraph"));
-    ASSERT_EQ(logicalRun->embeddingLevel, 0);
-    ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::LTR);
-  }
-
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isNothing());
-  }
-}
-
-TEST(IntlBidi, SimpleRTL)
-{
-  Bidi bidi{};
-  LogicalRunIter logicalRunIter(bidi, MakeStringSpan(u"فايرفوكس رائع"),
-                                Bidi::EmbeddingLevel::DefaultLTR());
-  ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 1);
-  ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::RTL);
-
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u"فايرفوكس رائع"));
-    ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::RTL);
-    ASSERT_EQ(logicalRun->embeddingLevel, 1);
-  }
-
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isNothing());
-  }
-}
-
-TEST(IntlBidi, MultiLevel)
-{
-  Bidi bidi{};
-  LogicalRunIter logicalRunIter(
-      bidi, MakeStringSpan(u"Firefox is awesome: رائع Firefox"),
-      Bidi::EmbeddingLevel::DefaultLTR());
-  ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
-  ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::Mixed);
-
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u"Firefox is awesome: "));
-    ASSERT_EQ(logicalRun->embeddingLevel, 0);
-  }
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u"رائع"));
-    ASSERT_EQ(logicalRun->embeddingLevel, 1);
-  }
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u" Firefox"));
-    ASSERT_EQ(logicalRun->embeddingLevel, 0);
-  }
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isNothing());
-  }
-}
-
-TEST(IntlBidi, RtlOverride)
-{
-  Bidi bidi{};
-  // Set the paragraph using the RTL embedding mark U+202B, and the LTR
-  // embedding mark U+202A to increase the embedding level. This mark switches
-  // the weakly directional character "_". This demonstrates that embedding
-  // levels can be computed.
-  LogicalRunIter logicalRunIter(
-      bidi, MakeStringSpan(u"ltr\u202b___رائع___\u202a___ltr__"),
-      Bidi::EmbeddingLevel::DefaultLTR());
-  ASSERT_EQ(bidi.GetParagraphEmbeddingLevel(), 0);
-  ASSERT_EQ(bidi.GetParagraphDirection(), Bidi::ParagraphDirection::Mixed);
-
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u"ltr"));
-    ASSERT_EQ(logicalRun->embeddingLevel, 0);
-    ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::LTR);
-  }
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u"\u202b___رائع___"));
-    ASSERT_EQ(logicalRun->embeddingLevel, 1);
-    ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::RTL);
-  }
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isSome());
-    ASSERT_EQ(logicalRun->string, MakeStringSpan(u"\u202a___ltr__"));
-    ASSERT_EQ(logicalRun->embeddingLevel, 2);
-    ASSERT_EQ(logicalRun->embeddingLevel.Direction(), Bidi::Direction::LTR);
-  }
-  {
-    auto logicalRun = logicalRunIter.Next();
-    ASSERT_TRUE(logicalRun.isNothing());
-  }
-}
-
-TEST(IntlBidi, VisualRuns)
-{
-  Bidi bidi{};
-
-  VisualRunIter visualRunIter(
-      bidi,
-      MakeStringSpan(
-          u"first visual run التشغيل البصري الثاني third visual run"),
-      Bidi::EmbeddingLevel::DefaultLTR());
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isSome());
-    ASSERT_EQ(run->string, MakeStringSpan(u"first visual run "));
-    ASSERT_EQ(run->direction, Bidi::Direction::LTR);
-  }
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isSome());
-    ASSERT_EQ(run->string, MakeStringSpan(u"التشغيل البصري الثاني"));
-    ASSERT_EQ(run->direction, Bidi::Direction::RTL);
-  }
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isSome());
-    ASSERT_EQ(run->string, MakeStringSpan(u" third visual run"));
-    ASSERT_EQ(run->direction, Bidi::Direction::LTR);
-  }
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isNothing());
-  }
-}
-
-TEST(IntlBidi, VisualRunsWithEmbeds)
-{
-  // Compare this test to the logical order test.
-  Bidi bidi{};
-  VisualRunIter visualRunIter(
-      bidi, MakeStringSpan(u"ltr\u202b___رائع___\u202a___ltr___"),
-      Bidi::EmbeddingLevel::DefaultLTR());
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isSome());
-    ASSERT_EQ(run->string, MakeStringSpan(u"ltr"));
-    ASSERT_EQ(run->direction, Bidi::Direction::LTR);
-  }
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isSome());
-    ASSERT_EQ(run->string, MakeStringSpan(u"\u202a___ltr___"));
-    ASSERT_EQ(run->direction, Bidi::Direction::LTR);
-  }
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isSome());
-    ASSERT_EQ(run->string, MakeStringSpan(u"\u202b___رائع___"));
-    ASSERT_EQ(run->direction, Bidi::Direction::RTL);
-  }
-  {
-    Maybe<VisualRun> run = visualRunIter.Next();
-    ASSERT_TRUE(run.isNothing());
-  }
-}
-
-}  // namespace mozilla::intl
--- a/intl/components/gtest/moz.build
+++ b/intl/components/gtest/moz.build
@@ -1,16 +1,15 @@
 # -*- 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/.
 
 UNIFIED_SOURCES += [
-    "TestBidi.cpp",
     "TestCalendar.cpp",
     "TestCollator.cpp",
     "TestCurrency.cpp",
     "TestDateTimeFormat.cpp",
     "TestListFormat.cpp",
     "TestLocale.cpp",
     "TestLocaleCanonicalizer.cpp",
     "TestMeasureUnit.cpp",
--- a/intl/components/moz.build
+++ b/intl/components/moz.build
@@ -1,15 +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/.
 EXPORTS.mozilla.intl = [
-    "src/Bidi.h",
     "src/Calendar.h",
     "src/Collator.h",
     "src/Currency.h",
     "src/DateTimeFormat.h",
     "src/DateTimePatternGenerator.h",
     "src/ICU4CGlue.h",
     "src/ICU4CLibrary.h",
     "src/ICUError.h",
@@ -23,17 +22,16 @@ EXPORTS.mozilla.intl = [
     "src/NumberRangeFormat.h",
     "src/PluralRules.h",
     "src/RelativeTimeFormat.h",
     "src/String.h",
     "src/TimeZone.h",
 ]
 
 UNIFIED_SOURCES += [
-    "src/Bidi.cpp",
     "src/Calendar.cpp",
     "src/Collator.cpp",
     "src/Currency.cpp",
     "src/DateTimeFormat.cpp",
     "src/DateTimePatternGenerator.cpp",
     "src/ICU4CGlue.cpp",
     "src/ICU4CLibrary.cpp",
     "src/ListFormat.cpp",
deleted file mode 100644
--- a/intl/components/src/Bidi.cpp
+++ /dev/null
@@ -1,163 +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/. */
-
-#include "mozilla/intl/Bidi.h"
-#include "mozilla/Casting.h"
-#include "mozilla/intl/ICU4CGlue.h"
-
-#include "unicode/ubidi.h"
-
-namespace mozilla::intl {
-
-Bidi::Bidi() { mBidi = ubidi_open(); }
-Bidi::~Bidi() { ubidi_close(mBidi.GetMut()); }
-
-ICUResult Bidi::SetParagraph(Span<const char16_t> aParagraph,
-                             Bidi::EmbeddingLevel aLevel) {
-  // Do not allow any reordering of the runs, as this can change the
-  // performance characteristics of working with runs. In the default mode,
-  // the levels can be iterated over directly, rather than relying on computing
-  // logical runs on the fly. This can have negative performance characteristics
-  // compared to iterating over the levels.
-  //
-  // In the UBIDI_REORDER_RUNS_ONLY the levels are encoded with additional
-  // information which can be safely ignored in this Bidi implementation.
-  // Note that this check is here since setting the mode must be done before
-  // calls to setting the paragraph.
-  MOZ_ASSERT(ubidi_getReorderingMode(mBidi.GetMut()) == UBIDI_REORDER_DEFAULT);
-
-  UErrorCode status = U_ZERO_ERROR;
-  ubidi_setPara(mBidi.GetMut(), aParagraph.Elements(),
-                AssertedCast<int32_t>(aParagraph.Length()), aLevel, nullptr,
-                &status);
-
-  mLevels = nullptr;
-
-  return ToICUResult(status);
-}
-
-Bidi::ParagraphDirection Bidi::GetParagraphDirection() const {
-  switch (ubidi_getDirection(mBidi.GetConst())) {
-    case UBIDI_LTR:
-      return Bidi::ParagraphDirection::LTR;
-    case UBIDI_RTL:
-      return Bidi::ParagraphDirection::RTL;
-    case UBIDI_MIXED:
-      return Bidi::ParagraphDirection::Mixed;
-    case UBIDI_NEUTRAL:
-      // This is only used in `ubidi_getBaseDirection` which is unused in this
-      // API.
-      MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
-  };
-  return Bidi::ParagraphDirection::Mixed;
-}
-
-/* static */
-void Bidi::ReorderVisual(const EmbeddingLevel* aLevels, int32_t aLength,
-                         int32_t* aIndexMap) {
-  ubidi_reorderVisual(reinterpret_cast<const uint8_t*>(aLevels), aLength,
-                      aIndexMap);
-}
-
-static Bidi::Direction ToBidiDirection(UBiDiDirection aDirection) {
-  switch (aDirection) {
-    case UBIDI_LTR:
-      return Bidi::Direction::LTR;
-    case UBIDI_RTL:
-      return Bidi::Direction::RTL;
-    case UBIDI_MIXED:
-    case UBIDI_NEUTRAL:
-      MOZ_ASSERT_UNREACHABLE("Unexpected UBiDiDirection value.");
-  }
-  return Bidi::Direction::LTR;
-}
-
-Result<int32_t, ICUError> Bidi::CountRuns() {
-  UErrorCode status = U_ZERO_ERROR;
-  int32_t runCount = ubidi_countRuns(mBidi.GetMut(), &status);
-  if (U_FAILURE(status)) {
-    return Err(ToICUError(status));
-  }
-
-  mLength = ubidi_getProcessedLength(mBidi.GetConst());
-  mLevels = mLength > 0 ? reinterpret_cast<const Bidi::EmbeddingLevel*>(
-                              ubidi_getLevels(mBidi.GetMut(), &status))
-                        : nullptr;
-  if (U_FAILURE(status)) {
-    return Err(ToICUError(status));
-  }
-
-  return runCount;
-}
-
-void Bidi::GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimitOut,
-                         Bidi::EmbeddingLevel* aLevelOut) {
-  MOZ_ASSERT(mLevels, "CountRuns hasn't been run?");
-  MOZ_RELEASE_ASSERT(aLogicalStart < mLength, "Out of bound");
-  EmbeddingLevel level = mLevels[aLogicalStart];
-  int32_t limit;
-  for (limit = aLogicalStart + 1; limit < mLength; limit++) {
-    if (mLevels[limit] != level) {
-      break;
-    }
-  }
-  *aLogicalLimitOut = limit;
-  *aLevelOut = level;
-}
-
-bool Bidi::EmbeddingLevel::IsDefaultLTR() const {
-  return mValue == UBIDI_DEFAULT_LTR;
-};
-
-bool Bidi::EmbeddingLevel::IsDefaultRTL() const {
-  return mValue == UBIDI_DEFAULT_RTL;
-};
-
-bool Bidi::EmbeddingLevel::IsRTL() const {
-  // If the least significant bit is 1, then the embedding level
-  // is right-to-left.
-  // If the least significant bit is 0, then the embedding level
-  // is left-to-right.
-  return (mValue & 0x1) == 1;
-};
-
-bool Bidi::EmbeddingLevel::IsLTR() const { return !IsRTL(); };
-
-bool Bidi::EmbeddingLevel::IsSameDirection(EmbeddingLevel aOther) const {
-  return (((mValue ^ aOther) & 1) == 0);
-}
-
-Bidi::EmbeddingLevel Bidi::EmbeddingLevel::LTR() {
-  return Bidi::EmbeddingLevel(0);
-};
-
-Bidi::EmbeddingLevel Bidi::EmbeddingLevel::RTL() {
-  return Bidi::EmbeddingLevel(1);
-};
-
-Bidi::EmbeddingLevel Bidi::EmbeddingLevel::DefaultLTR() {
-  return Bidi::EmbeddingLevel(UBIDI_DEFAULT_LTR);
-};
-
-Bidi::EmbeddingLevel Bidi::EmbeddingLevel::DefaultRTL() {
-  return Bidi::EmbeddingLevel(UBIDI_DEFAULT_RTL);
-};
-
-Bidi::Direction Bidi::EmbeddingLevel::Direction() {
-  return IsRTL() ? Direction::RTL : Direction::LTR;
-};
-
-uint8_t Bidi::EmbeddingLevel::Value() const { return mValue; }
-
-Bidi::EmbeddingLevel Bidi::GetParagraphEmbeddingLevel() const {
-  return Bidi::EmbeddingLevel(ubidi_getParaLevel(mBidi.GetConst()));
-}
-
-Bidi::Direction Bidi::GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
-                                   int32_t* aLength) {
-  return ToBidiDirection(
-      ubidi_getVisualRun(mBidi.GetMut(), aRunIndex, aLogicalStart, aLength));
-}
-
-}  // namespace mozilla::intl
deleted file mode 100644
--- a/intl/components/src/Bidi.h
+++ /dev/null
@@ -1,241 +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/. */
-#ifndef intl_components_Bidi_h_
-#define intl_components_Bidi_h_
-
-#include "mozilla/intl/ICU4CGlue.h"
-
-struct UBiDi;
-
-namespace mozilla::intl {
-
-/**
- * This component is a Mozilla-focused API for working with bidirectional (bidi)
- * text. Text is commonly displayed left to right (LTR), especially for
- * Latin-based alphabets. However, languages like Arabic and Hebrew displays
- * text right to left (RTL). When displaying text, LTR and RTL text can be
- * combined together in the same paragraph. This class gives tools for working
- * with unidirectional, and mixed direction paragraphs.
- *
- * See the Unicode Bidirectional Algorithm document for implementation details:
- * https://unicode.org/reports/tr9/
- */
-class Bidi final {
- public:
-  Bidi();
-  ~Bidi();
-
-  // Not copyable or movable
-  Bidi(const Bidi&) = delete;
-  Bidi& operator=(const Bidi&) = delete;
-
-  /**
-   * This enum unambiguously classifies text runs as either being left to right,
-   * or right to left.
-   */
-  enum class Direction : uint8_t {
-    // Left to right text.
-    LTR = 0,
-    // Right to left text.
-    RTL = 1,
-  };
-
-  /**
-   * This enum indicates the text direction for the set paragraph. Some
-   * paragraphs are unidirectional, where they only have one direction, or a
-   * paragraph could use both LTR and RTL. In this case the paragraph's
-   * direction would be mixed.
-   */
-  enum ParagraphDirection { LTR, RTL, Mixed };
-
-  /**
-   * Embedding levels are numbers that indicate how deeply the bidi text is
-   * embedded, and the direction of text on that embedding level. When switching
-   * between strongly LTR code points and strongly RTL code points the embedding
-   * level normally switches between an embedding level of 0 (LTR) and 1 (RTL).
-   * The only time the embedding level increases is if the embedding code points
-   * are used. This is the Left-to-Right Embedding (LRE) code point (U+202A), or
-   * the Right-to-Left Embedding (RLE) code point (U+202B). The minimum
-   * embedding level of text is zero, and the maximum explicit depth is 125.
-   *
-   * The most significant bit is reserved for additional meaning. It can be used
-   * to signify in certain APIs that the text should by default be LTR or RTL if
-   * no strongly directional code points are found.
-   *
-   * Bug 1736595: At the time of this writing, some places in Gecko code use a 1
-   * in the most significant bit to indicate that an embedding level has not
-   * been set. This leads to an ambiguous understanding of what the most
-   * significant bit actually means.
-   */
-  class EmbeddingLevel {
-   public:
-    explicit EmbeddingLevel(uint8_t aValue) : mValue(aValue) {}
-    explicit EmbeddingLevel(int aValue)
-        : mValue(static_cast<uint8_t>(aValue)) {}
-
-    EmbeddingLevel() = default;
-
-    // Enable the copy operators, but disable move as this is only a uint8_t.
-    EmbeddingLevel(const EmbeddingLevel& other) = default;
-    EmbeddingLevel& operator=(const EmbeddingLevel& other) = default;
-
-    /**
-     * Determine the direction of the embedding level by looking at the least
-     * significant bit. If it is 0, then it is LTR. If it is 1, then it is RTL.
-     */
-    Direction Direction();
-
-    /**
-     * Create a left-to-right embedding level.
-     */
-    static EmbeddingLevel LTR();
-
-    /**
-     * Create an right-to-left embedding level.
-     */
-    static EmbeddingLevel RTL();
-
-    /**
-     * When passed into `SetParagraph`, the direction is determined by first
-     * strongly directional character, with the default set to left-to-right if
-     * none is found.
-     *
-     * This is encoded with the highest bit set to 1.
-     */
-    static EmbeddingLevel DefaultLTR();
-
-    /**
-     * When passed into `SetParagraph`, the direction is determined by first
-     * strongly directional character, with the default set to right-to-left if
-     * none is found.
-     *
-     * * This is encoded with the highest and lowest bits set to 1.
-     */
-    static EmbeddingLevel DefaultRTL();
-
-    bool IsDefaultLTR() const;
-    bool IsDefaultRTL() const;
-    bool IsLTR() const;
-    bool IsRTL() const;
-    bool IsSameDirection(EmbeddingLevel aOther) const;
-
-    /**
-     * Get the underlying value as a uint8_t.
-     */
-    uint8_t Value() const;
-
-    /**
-     * Implicitly convert to the underlying value.
-     */
-    operator uint8_t() const { return mValue; }
-
-   private:
-    uint8_t mValue = 0;
-  };
-
-  /**
-   * Set the current paragraph of text to analyze for its bidi properties. This
-   * performs the Unicode bidi algorithm as specified by:
-   * https://unicode.org/reports/tr9/
-   *
-   * After setting the text, the other getter methods can be used to find out
-   * the directionality of the paragraph text.
-   */
-  ICUResult SetParagraph(Span<const char16_t> aParagraph,
-                         EmbeddingLevel aLevel);
-
-  /**
-   * Get the embedding level for the paragraph that was set by SetParagraph.
-   */
-  EmbeddingLevel GetParagraphEmbeddingLevel() const;
-
-  /**
-   * Get the directionality of the paragraph text that was set by SetParagraph.
-   */
-  ParagraphDirection GetParagraphDirection() const;
-
-  /**
-   * Get the number of runs. This function may invoke the actual reordering on
-   * the Bidi object, after SetParagraph may have resolved only the levels of
-   * the text. Therefore, `CountRuns` may have to allocate memory, and may fail
-   * doing so.
-   */
-  Result<int32_t, ICUError> CountRuns();
-
-  /**
-   * Get the next logical run. The logical runs are a run of text that has the
-   * same directionality and embedding level. These runs are in memory order,
-   * and not in display order.
-   *
-   * Important! `Bidi::CountRuns` must be called before calling this method.
-   *
-   * @param aLogicalStart is the offset into the paragraph text that marks the
-   *      logical start of the text.
-   * @param aLogicalLimitOut is an out param that is the length of the string
-   *      that makes up the logical run.
-   * @param aLevelOut is an out parameter that returns the embedding level for
-   *      the run
-   */
-  void GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimitOut,
-                     EmbeddingLevel* aLevelOut);
-
-  /**
-   * This is a convenience function that does not use the ICU Bidi object.
-   * It is intended to be used for when an application has determined the
-   * embedding levels of objects (character sequences) and just needs to have
-   * them reordered (L2).
-   *
-   * @param aLevels is an array with `aLength` levels that have been
-   *      determined by the application.
-   *
-   * @param aLength is the number of levels in the array, or, semantically,
-   *      the number of objects to be reordered. It must be greater than 0.
-   *
-   * @param aIndexMap is a pointer to an array of `aLength`
-   *      indexes which will reflect the reordering of the characters.
-   *      The array does not need to be initialized.
-   *      The index map will result in
-   *        `aIndexMap[aVisualIndex]==aLogicalIndex`.
-   */
-  static void ReorderVisual(const EmbeddingLevel* aLevels, int32_t aLength,
-                            int32_t* aIndexMap);
-
-  /**
-   * Get one run's logical start, length, and directionality. In an RTL run, the
-   * character at the logical start is visually on the right of the displayed
-   * run. The length is the number of characters in the run.
-   * `Bidi::CountRuns` should be called before the runs are retrieved.
-   *
-   * @param aRunIndex is the number of the run in visual order, in the
-   *      range `[0..CountRuns-1]`.
-   *
-   * @param aLogicalStart is the first logical character index in the text.
-   *      The pointer may be `nullptr` if this index is not needed.
-   *
-   * @param aLength is the number of characters (at least one) in the run.
-   *      The pointer may be `nullptr` if this is not needed.
-   *
-   * Note that in right-to-left runs, the code places modifier letters before
-   * base characters and second surrogates before first ones.
-   */
-  Direction GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
-                         int32_t* aLength);
-
- private:
-  ICUPointer<UBiDi> mBidi = ICUPointer<UBiDi>(nullptr);
-
-  /**
-   * An array of levels that is the same length as the paragraph from
-   * `Bidi::SetParagraph`.
-   */
-  const EmbeddingLevel* mLevels = nullptr;
-
-  /**
-   * The length of the paragraph from `Bidi::SetParagraph`.
-   */
-  int32_t mLength = 0;
-};
-
-}  // namespace mozilla::intl
-#endif
--- a/intl/unicharutil/util/nsBidiUtils.h
+++ b/intl/unicharutil/util/nsBidiUtils.h
@@ -47,16 +47,35 @@ enum nsCharType {
 };
 
 /**
  * This specifies the language directional property of a character set.
  */
 typedef enum nsCharType nsCharType;
 
 /**
+ * Find the direction of an embedding level or paragraph level set by
+ * the Unicode Bidi Algorithm. (Even levels are left-to-right, odd
+ * levels right-to-left.
+ */
+#define IS_LEVEL_RTL(level) (((level)&1) == 1)
+
+/**
+ * Check whether two bidi levels have the same parity and thus the same
+ * directionality
+ */
+#define IS_SAME_DIRECTION(level1, level2) (((level1 ^ level2) & 1) == 0)
+
+/**
+ * Convert from nsBidiLevel to nsBidiDirection
+ */
+#define DIRECTION_FROM_LEVEL(level) \
+  ((IS_LEVEL_RTL(level)) ? NSBIDI_RTL : NSBIDI_LTR)
+
+/**
  * definitions of bidirection character types by category
  */
 
 #define CHARTYPE_IS_RTL(val) \
   (((val) == eCharType_RightToLeft) || ((val) == eCharType_RightToLeftArabic))
 
 #define CHARTYPE_IS_WEAK(val)                       \
   (((val) == eCharType_EuropeanNumberSeparator) ||  \
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -34,16 +34,17 @@ XPIDL_MODULE = "layout_base"
 
 EXPORTS += [
     "CaretAssociationHint.h",
     "FrameProperties.h",
     "LayoutConstants.h",
     "LayoutLogging.h",
     "MobileViewportManager.h",
     "nsAutoLayoutPhase.h",
+    "nsBidi.h",
     "nsBidiPresUtils.h",
     "nsCaret.h",
     "nsChangeHint.h",
     "nsCompatibility.h",
     "nsCounterManager.h",
     "nsCSSFrameConstructor.h",
     "nsFrameManager.h",
     "nsFrameTraversal.h",
@@ -103,16 +104,17 @@ UNIFIED_SOURCES += [
     "AccessibleCaretManager.cpp",
     "DisplayPortUtils.cpp",
     "GeckoMVMContext.cpp",
     "GeometryUtils.cpp",
     "LayoutLogging.cpp",
     "LayoutTelemetryTools.cpp",
     "MobileViewportManager.cpp",
     "MotionPathUtils.cpp",
+    "nsBidi.cpp",
     "nsBidiPresUtils.cpp",
     "nsCaret.cpp",
     "nsCounterManager.cpp",
     "nsCSSColorUtils.cpp",
     "nsCSSFrameConstructor.cpp",
     "nsDocumentViewer.cpp",
     "nsFrameManager.cpp",
     "nsFrameTraversal.cpp",
new file mode 100644
--- /dev/null
+++ b/layout/base/nsBidi.cpp
@@ -0,0 +1,42 @@
+/* -*- 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 http://mozilla.org/MPL/2.0/. */
+
+#include "nsBidi.h"
+
+nsresult nsBidi::CountRuns(int32_t* aRunCount) {
+  UErrorCode errorCode = U_ZERO_ERROR;
+  *aRunCount = ubidi_countRuns(mBiDi, &errorCode);
+  if (U_SUCCESS(errorCode)) {
+    mLength = ubidi_getProcessedLength(mBiDi);
+    mLevels = mLength > 0 ? ubidi_getLevels(mBiDi, &errorCode) : nullptr;
+  }
+  return ICUUtils::UErrorToNsResult(errorCode);
+}
+
+void nsBidi::GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit,
+                           nsBidiLevel* aLevel) {
+  MOZ_ASSERT(mLevels, "CountRuns hasn't been run?");
+  MOZ_RELEASE_ASSERT(aLogicalStart < mLength, "Out of bound");
+  // This function implements an alternative approach to get logical
+  // run that is based on levels of characters, which would avoid O(n^2)
+  // performance issue when used in a loop over runs.
+  // Per comment in ubidi_getLogicalRun, that function doesn't use this
+  // approach because levels have special interpretation when reordering
+  // mode is UBIDI_REORDER_RUNS_ONLY. Since we don't use this mode in
+  // Gecko, it should be safe to just use levels for this function.
+  MOZ_ASSERT(ubidi_getReorderingMode(mBiDi) != UBIDI_REORDER_RUNS_ONLY,
+             "Don't support UBIDI_REORDER_RUNS_ONLY mode");
+
+  nsBidiLevel level = mLevels[aLogicalStart];
+  int32_t limit;
+  for (limit = aLogicalStart + 1; limit < mLength; limit++) {
+    if (mLevels[limit] != level) {
+      break;
+    }
+  }
+  *aLogicalLimit = limit;
+  *aLevel = level;
+}
new file mode 100644
--- /dev/null
+++ b/layout/base/nsBidi.h
@@ -0,0 +1,208 @@
+/* -*- 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 http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsBidi_h__
+#define nsBidi_h__
+
+#include "unicode/ubidi.h"
+#include "ICUUtils.h"
+#include "nsIFrame.h"  // for nsBidiLevel/nsBidiDirection declarations
+
+// nsBidi implemented as a simple wrapper around the bidi reordering engine
+// from ICU.
+// We could eliminate this and let callers use the ICU functions directly
+// once we no longer care about building without ICU available.
+
+class nsBidi {
+ public:
+  /** @brief Default constructor.
+   *
+   * The nsBidi object is initially empty. It is assigned
+   * the Bidi properties of a paragraph by <code>SetPara()</code>.
+   */
+  nsBidi() { mBiDi = ubidi_open(); }
+
+  /** @brief Destructor. */
+  ~nsBidi() { ubidi_close(mBiDi); }
+
+  /**
+   * Perform the Unicode Bidi algorithm.
+   *
+   * @param aText is a pointer to the single-paragraph text that the
+   *      Bidi algorithm will be performed on
+   *      (step (P1) of the algorithm is performed externally).
+   *      <strong>The text must be (at least) <code>aLength</code> long.
+   *      </strong>
+   *
+   * @param aLength is the length of the text; if <code>aLength==-1</code> then
+   *      the text must be zero-terminated.
+   *
+   * @param aParaLevel specifies the default level for the paragraph;
+   *      it is typically 0 (LTR) or 1 (RTL).
+   *      If the function shall determine the paragraph level from the text,
+   *      then <code>aParaLevel</code> can be set to
+   *      either <code>NSBIDI_DEFAULT_LTR</code>
+   *      or <code>NSBIDI_DEFAULT_RTL</code>;
+   *      if there is no strongly typed character, then
+   *      the desired default is used (0 for LTR or 1 for RTL).
+   *      Any other value between 0 and <code>NSBIDI_MAX_EXPLICIT_LEVEL</code>
+   *      is also valid, with odd levels indicating RTL.
+   */
+  nsresult SetPara(const char16_t* aText, int32_t aLength,
+                   nsBidiLevel aParaLevel) {
+    UErrorCode error = U_ZERO_ERROR;
+    ubidi_setPara(mBiDi, reinterpret_cast<const UChar*>(aText), aLength,
+                  aParaLevel, nullptr, &error);
+    return ICUUtils::UErrorToNsResult(error);
+  }
+
+  /**
+   * Get the directionality of the text.
+   *
+   * @param aDirection receives a <code>NSBIDI_XXX</code> value that indicates
+   *       if the entire text represented by this object is unidirectional,
+   *       and which direction, or if it is mixed-directional.
+   *
+   * @see nsBidiDirection
+   */
+  nsBidiDirection GetDirection() {
+    return nsBidiDirection(ubidi_getDirection(mBiDi));
+  }
+
+  /**
+   * Get the paragraph level of the text.
+   *
+   * @param aParaLevel receives a <code>NSBIDI_XXX</code> value indicating
+   *                   the paragraph level
+   *
+   * @see nsBidiLevel
+   */
+  nsBidiLevel GetParaLevel() { return ubidi_getParaLevel(mBiDi); }
+
+  /**
+   * Get a logical run.
+   * This function returns information about a run and is used
+   * to retrieve runs in logical order.<p>
+   * This is especially useful for line-breaking on a paragraph.
+   * <code>CountRuns</code> should be called before this.
+   * before the runs are retrieved.
+   *
+   * @param aLogicalStart is the first character of the run.
+   *
+   * @param aLogicalLimit will receive the limit of the run.
+   *      The l-value that you point to here may be the
+   *      same expression (variable) as the one for
+   *      <code>aLogicalStart</code>.
+   *      This pointer cannot be <code>nullptr</code>.
+   *
+   * @param aLevel will receive the level of the run.
+   *      This pointer cannot be <code>nullptr</code>.
+   */
+  void GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit,
+                     nsBidiLevel* aLevel);
+
+  /**
+   * Get the number of runs.
+   * This function may invoke the actual reordering on the
+   * <code>nsBidi</code> object, after <code>SetPara</code>
+   * may have resolved only the levels of the text. Therefore,
+   * <code>CountRuns</code> may have to allocate memory,
+   * and may fail doing so.
+   *
+   * @param aRunCount will receive the number of runs.
+   */
+  nsresult CountRuns(int32_t* aRunCount);
+
+  /**
+   * Get one run's logical start, length, and directionality,
+   * which can be 0 for LTR or 1 for RTL.
+   * In an RTL run, the character at the logical start is
+   * visually on the right of the displayed run.
+   * The length is the number of characters in the run.<p>
+   * <code>CountRuns</code> should be called
+   * before the runs are retrieved.
+   *
+   * @param aRunIndex is the number of the run in visual order, in the
+   *      range <code>[0..CountRuns-1]</code>.
+   *
+   * @param aLogicalStart is the first logical character index in the text.
+   *      The pointer may be <code>nullptr</code> if this index is not needed.
+   *
+   * @param aLength is the number of characters (at least one) in the run.
+   *      The pointer may be <code>nullptr</code> if this is not needed.
+   *
+   * @returns the directionality of the run,
+   *       <code>NSBIDI_LTR==0</code> or <code>NSBIDI_RTL==1</code>,
+   *       never <code>NSBIDI_MIXED</code>.
+   *
+   * @see CountRuns<p>
+   *
+   * Example:
+   * @code
+   *  int32_t i, count, logicalStart, visualIndex=0, length;
+   *  nsBidiDirection dir;
+   *  pBidi->CountRuns(&count);
+   *  for(i=0; i<count; ++i) {
+   *    dir = pBidi->GetVisualRun(i, &logicalStart, &length);
+   *    if(NSBIDI_LTR==dir) {
+   *      do { // LTR
+   *        show_char(text[logicalStart++], visualIndex++);
+   *      } while(--length>0);
+   *    } else {
+   *      logicalStart+=length;  // logicalLimit
+   *      do { // RTL
+   *        show_char(text[--logicalStart], visualIndex++);
+   *      } while(--length>0);
+   *    }
+   *  }
+   * @endcode
+   *
+   * Note that in right-to-left runs, code like this places
+   * modifier letters before base characters and second surrogates
+   * before first ones.
+   */
+  nsBidiDirection GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
+                               int32_t* aLength) {
+    return nsBidiDirection(
+        ubidi_getVisualRun(mBiDi, aRunIndex, aLogicalStart, aLength));
+  }
+
+  /**
+   * This is a convenience function that does not use a nsBidi object.
+   * It is intended to be used for when an application has determined the levels
+   * of objects (character sequences) and just needs to have them reordered
+   * (L2). This is equivalent to using <code>GetVisualMap</code> on a
+   * <code>nsBidi</code> object.
+   *
+   * @param aLevels is an array with <code>aLength</code> levels that have been
+   *      determined by the application.
+   *
+   * @param aLength is the number of levels in the array, or, semantically,
+   *      the number of objects to be reordered.
+   *      It must be <code>aLength>0</code>.
+   *
+   * @param aIndexMap is a pointer to an array of <code>aLength</code>
+   *      indexes which will reflect the reordering of the characters.
+   *      The array does not need to be initialized.<p>
+   *      The index map will result in
+   *        <code>aIndexMap[aVisualIndex]==aLogicalIndex</code>.
+   */
+  static void ReorderVisual(const nsBidiLevel* aLevels, int32_t aLength,
+                            int32_t* aIndexMap) {
+    ubidi_reorderVisual(aLevels, aLength, aIndexMap);
+  }
+
+ private:
+  nsBidi(const nsBidi&) = delete;
+  void operator=(const nsBidi&) = delete;
+
+  UBiDi* mBiDi;
+  // The two fields below are updated when CountRuns is called.
+  const nsBidiLevel* mLevels = nullptr;
+  int32_t mLength = 0;
+};
+
+#endif  // _nsBidi_h_
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1,18 +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 http://mozilla.org/MPL/2.0/. */
 
 #include "nsBidiPresUtils.h"
 
-#include "mozilla/intl/Bidi.h"
-#include "mozilla/Casting.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/dom/Text.h"
 
 #include "gfxContext.h"
 #include "nsFontMetrics.h"
 #include "nsGkAtoms.h"
@@ -36,17 +34,16 @@
 #include "nsRubyBaseContainerFrame.h"
 #include "nsRubyTextContainerFrame.h"
 #include <algorithm>
 
 #undef NOISY_BIDI
 #undef REALLY_NOISY_BIDI
 
 using namespace mozilla;
-using EmbeddingLevel = mozilla::intl::Bidi::EmbeddingLevel;
 
 static const char16_t kSpace = 0x0020;
 static const char16_t kZWSP = 0x200B;
 static const char16_t kLineSeparator = 0x2028;
 static const char16_t kObjectSubstitute = 0xFFFC;
 static const char16_t kLRE = 0x202A;
 static const char16_t kRLE = 0x202B;
 static const char16_t kLRO = 0x202D;
@@ -161,17 +158,17 @@ struct MOZ_STACK_CLASS BidiParagraphData
   nsAutoString mBuffer;
   AutoTArray<char16_t, 16> mEmbeddingStack;
   AutoTArray<FrameInfo, 16> mLogicalFrames;
   nsTHashMap<nsPtrHashKey<const nsIContent>, int32_t> mContentToFrameIndex;
   // Cached presentation context for the frames we're processing.
   nsPresContext* mPresContext;
   bool mIsVisual;
   bool mRequiresBidi;
-  EmbeddingLevel mParaLevel;
+  nsBidiLevel mParaLevel;
   nsIContent* mPrevContent;
 
   /**
    * This class is designed to manage the process of mapping a frame to
    * the line that it's in, when we know that (a) the frames we ask it
    * about are always in the block's lines and (b) each successive frame
    * we ask it about is the same as or after (in depth-first search
    * order) the previous.
@@ -333,59 +330,47 @@ struct MOZ_STACK_CLASS BidiParagraphData
           mIsVisual = false;
           break;
         }
       }
     }
   }
 
   nsresult SetPara() {
-    if (mPresContext->GetBidiEngine()
-            .SetParagraph(mBuffer, mParaLevel)
-            .isErr()) {
-      return NS_ERROR_FAILURE;
-    };
-    return NS_OK;
+    return mPresContext->GetBidiEngine().SetPara(mBuffer.get(), BufferLength(),
+                                                 mParaLevel);
   }
 
   /**
-   * mParaLevel can be intl::Bidi::Direction::LTR as well as
-   * intl::Bidi::Direction::LTR or intl::Bidi::Direction::RTL.
-   * GetParagraphEmbeddingLevel() returns the actual (resolved) paragraph level
-   * which is always either intl::Bidi::Direction::LTR or
-   * intl::Bidi::Direction::RTL
+   * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
+   * GetParaLevel() returns the actual (resolved) paragraph level which is
+   * always either NSBIDI_LTR or NSBIDI_RTL
    */
-  EmbeddingLevel GetParagraphEmbeddingLevel() {
-    EmbeddingLevel paraLevel = mParaLevel;
-    if (paraLevel == EmbeddingLevel::DefaultLTR() ||
-        paraLevel == EmbeddingLevel::DefaultRTL()) {
-      paraLevel = mPresContext->GetBidiEngine().GetParagraphEmbeddingLevel();
+  nsBidiLevel GetParaLevel() {
+    nsBidiLevel paraLevel = mParaLevel;
+    if (paraLevel == NSBIDI_DEFAULT_LTR || paraLevel == NSBIDI_DEFAULT_RTL) {
+      paraLevel = mPresContext->GetBidiEngine().GetParaLevel();
     }
     return paraLevel;
   }
 
-  intl::Bidi::ParagraphDirection GetParagraphDirection() {
-    return mPresContext->GetBidiEngine().GetParagraphDirection();
+  nsBidiDirection GetDirection() {
+    return mPresContext->GetBidiEngine().GetDirection();
   }
 
   nsresult CountRuns(int32_t* runCount) {
-    auto result = mPresContext->GetBidiEngine().CountRuns();
-    if (result.isErr()) {
-      return NS_ERROR_FAILURE;
-    }
-    *runCount = result.unwrap();
-    return NS_OK;
+    return mPresContext->GetBidiEngine().CountRuns(runCount);
   }
 
   void GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit,
-                     EmbeddingLevel* aLevel) {
+                     nsBidiLevel* aLevel) {
     mPresContext->GetBidiEngine().GetLogicalRun(aLogicalStart, aLogicalLimit,
                                                 aLevel);
     if (mIsVisual) {
-      *aLevel = GetParagraphEmbeddingLevel();
+      *aLevel = GetParaLevel();
     }
   }
 
   void ResetData() {
     mLogicalFrames.Clear();
     mContentToFrameIndex.Clear();
     mBuffer.SetLength(0);
     mPrevContent = nullptr;
@@ -475,33 +460,33 @@ struct MOZ_STACK_CLASS BidiParagraphData
     }
   }
 };
 
 struct MOZ_STACK_CLASS BidiLineData {
   AutoTArray<nsIFrame*, 16> mLogicalFrames;
   AutoTArray<nsIFrame*, 16> mVisualFrames;
   AutoTArray<int32_t, 16> mIndexMap;
-  AutoTArray<EmbeddingLevel, 16> mLevels;
+  AutoTArray<uint8_t, 16> mLevels;
   bool mIsReordered;
 
   BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine) {
     /**
      * Initialize the logically-ordered array of frames using the top-level
      * frames of a single line
      */
     bool isReordered = false;
     bool hasRTLFrames = false;
     bool hasVirtualControls = false;
 
-    auto appendFrame = [&](nsIFrame* frame, EmbeddingLevel level) {
+    auto appendFrame = [&](nsIFrame* frame, nsBidiLevel level) {
       mLogicalFrames.AppendElement(frame);
       mLevels.AppendElement(level);
       mIndexMap.AppendElement(0);
-      if (level.IsRTL()) {
+      if (IS_LEVEL_RTL(level)) {
         hasRTLFrames = true;
       }
     };
 
     bool firstFrame = true;
     for (nsIFrame* frame = aFirstFrameOnLine; frame && aNumFramesOnLine--;
          frame = frame->GetNextSibling()) {
       FrameBidiData bidiData = nsBidiPresUtils::GetFrameBidiData(frame);
@@ -512,18 +497,18 @@ struct MOZ_STACK_CLASS BidiLineData {
         appendFrame(NS_BIDI_CONTROL_FRAME, bidiData.precedingControl);
         hasVirtualControls = true;
       }
       appendFrame(frame, bidiData.embeddingLevel);
       firstFrame = false;
     }
 
     // Reorder the line
-    mozilla::intl::Bidi::ReorderVisual(mLevels.Elements(), FrameCount(),
-                                       mIndexMap.Elements());
+    nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(),
+                          mIndexMap.Elements());
 
     // Strip virtual frames
     if (hasVirtualControls) {
       auto originalCount = mLogicalFrames.Length();
       AutoTArray<int32_t, 16> realFrameMap;
       realFrameMap.SetCapacity(originalCount);
       size_t count = 0;
       for (auto i : IntegerRange(originalCount)) {
@@ -875,18 +860,17 @@ nsresult nsBidiPresUtils::ResolveParagra
   }
   aBpd->mBuffer.ReplaceChar(kSeparators, kSpace);
 
   int32_t runCount;
 
   nsresult rv = aBpd->SetPara();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  intl::Bidi::EmbeddingLevel embeddingLevel =
-      aBpd->GetParagraphEmbeddingLevel();
+  nsBidiLevel embeddingLevel = aBpd->GetParaLevel();
 
   rv = aBpd->CountRuns(&runCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   int32_t runLength = 0;     // the length of the current run of text
   int32_t logicalLimit = 0;  // the end of the current run + 1
   int32_t numRun = -1;
   int32_t fragmentLength = 0;  // the length of the current text frame
@@ -908,19 +892,18 @@ nsresult nsBidiPresUtils::ResolveParagra
       frameCount, runCount);
 #    ifdef REALLY_NOISY_BIDI
   printf(" block frame tree=:\n");
   aBpd->mCurrentBlock->List(stdout);
 #    endif
 #  endif
 #endif
 
-  if (runCount == 1 && frameCount == 1 &&
-      aBpd->GetParagraphDirection() == intl::Bidi::ParagraphDirection::LTR &&
-      aBpd->GetParagraphEmbeddingLevel() == 0) {
+  if (runCount == 1 && frameCount == 1 && aBpd->GetDirection() == NSBIDI_LTR &&
+      aBpd->GetParaLevel() == 0) {
     // We have a single left-to-right frame in a left-to-right paragraph,
     // without bidi isolation from the surrounding text.
     // Make sure that the embedding level and base level frame properties aren't
     // set (because if they are this frame used to have some other direction,
     // so we can't do this optimization), and we're done.
     nsIFrame* frame = aBpd->FrameAt(0);
     if (frame != NS_BIDI_CONTROL_FRAME) {
       FrameBidiData bidiData = frame->GetBidiData();
@@ -932,23 +915,23 @@ nsresult nsBidiPresUtils::ResolveParagra
 #endif
         frame->AddStateBits(NS_FRAME_IS_BIDI);
         return NS_OK;
       }
     }
   }
 
   BidiParagraphData::FrameInfo lastRealFrame;
-  EmbeddingLevel lastEmbeddingLevel = kBidiLevelNone;
-  EmbeddingLevel precedingControl = kBidiLevelNone;
+  nsBidiLevel lastEmbeddingLevel = kBidiLevelNone;
+  nsBidiLevel precedingControl = kBidiLevelNone;
 
   auto storeBidiDataToFrame = [&]() {
     FrameBidiData bidiData;
     bidiData.embeddingLevel = embeddingLevel;
-    bidiData.baseLevel = aBpd->GetParagraphEmbeddingLevel();
+    bidiData.baseLevel = aBpd->GetParaLevel();
     // If a control character doesn't have a lower embedding level than
     // both the preceding and the following frame, it isn't something
     // needed for getting the correct result. This optimization should
     // remove almost all of embeds and overrides, and some of isolates.
     if (precedingControl >= embeddingLevel ||
         precedingControl >= lastEmbeddingLevel) {
       bidiData.precedingControl = kBidiLevelNone;
     } else {
@@ -1518,21 +1501,21 @@ nsIFrame* nsBidiPresUtils::GetFirstLeaf(
   }
   return firstLeaf;
 }
 
 FrameBidiData nsBidiPresUtils::GetFrameBidiData(nsIFrame* aFrame) {
   return GetFirstLeaf(aFrame)->GetBidiData();
 }
 
-EmbeddingLevel nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame) {
+nsBidiLevel nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame) {
   return GetFirstLeaf(aFrame)->GetEmbeddingLevel();
 }
 
-EmbeddingLevel nsBidiPresUtils::GetFrameBaseLevel(const nsIFrame* aFrame) {
+nsBidiLevel nsBidiPresUtils::GetFrameBaseLevel(const nsIFrame* aFrame) {
   const nsIFrame* firstLeaf = aFrame;
   while (!IsBidiLeaf(firstLeaf)) {
     firstLeaf = firstLeaf->PrincipalChildList().FirstChild();
   }
   return firstLeaf->GetBaseLevel();
 }
 
 void nsBidiPresUtils::IsFirstOrLast(nsIFrame* aFrame,
@@ -1883,17 +1866,17 @@ nscoord nsBidiPresUtils::RepositionInlin
   } else {
     index = count - 1;
     step = -1;
     limit = -1;
   }
   for (; index != limit; index += step) {
     frame = aBld->VisualFrameAt(index);
     start += RepositionFrame(
-        frame, !(aBld->mLevels[aBld->mIndexMap[index]].IsRTL()), start,
+        frame, !(IS_LEVEL_RTL(aBld->mLevels[aBld->mIndexMap[index]])), start,
         &continuationStates, aLineWM, false, aContainerSize);
   }
   return start;
 }
 
 bool nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
                                      int32_t aNumFramesOnLine,
                                      nsIFrame** aFirstVisual,
@@ -2091,17 +2074,17 @@ RemoveDiacritics(char16_t* aText,
       aText[i - offset] = aText[i];
     }
     aTextLength = i - offset;
     aText[aTextLength] = 0;
   }
 }
 #endif
 
-void nsBidiPresUtils::CalculateCharType(intl::Bidi* aBidiEngine,
+void nsBidiPresUtils::CalculateCharType(nsBidi* aBidiEngine,
                                         const char16_t* aText, int32_t& aOffset,
                                         int32_t aCharTypeLimit,
                                         int32_t& aRunLimit, int32_t& aRunLength,
                                         int32_t& aRunCount, uint8_t& aCharType,
                                         uint8_t& aPrevCharType)
 
 {
   bool strongTypeFound = false;
@@ -2155,83 +2138,79 @@ void nsBidiPresUtils::CalculateCharType(
 
       strongTypeFound = true;
       aCharType = charType;
     }
   }
   aOffset = offset;
 }
 
-nsresult nsBidiPresUtils::ProcessText(const char16_t* aText, size_t aLength,
-                                      EmbeddingLevel aBaseLevel,
+nsresult nsBidiPresUtils::ProcessText(const char16_t* aText, int32_t aLength,
+                                      nsBidiLevel aBaseLevel,
                                       nsPresContext* aPresContext,
                                       BidiProcessor& aprocessor, Mode aMode,
                                       nsBidiPositionResolve* aPosResolve,
                                       int32_t aPosResolveCount, nscoord* aWidth,
-                                      mozilla::intl::Bidi* aBidiEngine) {
+                                      nsBidi* aBidiEngine) {
   NS_ASSERTION((aPosResolve == nullptr) != (aPosResolveCount > 0),
                "Incorrect aPosResolve / aPosResolveCount arguments");
 
+  int32_t runCount;
+
   nsAutoString textBuffer(aText, aLength);
   textBuffer.ReplaceChar(kSeparators, kSpace);
   const char16_t* text = textBuffer.get();
 
-  if (aBidiEngine->SetParagraph(Span(text, aLength), aBaseLevel).isErr()) {
-    return NS_ERROR_FAILURE;
-  }
+  nsresult rv = aBidiEngine->SetPara(text, aLength, aBaseLevel);
+  if (NS_FAILED(rv)) return rv;
 
-  auto result = aBidiEngine->CountRuns();
-  if (result.isErr()) {
-    return NS_ERROR_FAILURE;
-  }
-  int32_t runCount = result.unwrap();
+  rv = aBidiEngine->CountRuns(&runCount);
+  if (NS_FAILED(rv)) return rv;
 
   nscoord xOffset = 0;
   nscoord width, xEndRun = 0;
   nscoord totalWidth = 0;
   int32_t i, start, limit, length;
   uint32_t visualStart = 0;
   uint8_t charType;
   uint8_t prevType = eCharType_LeftToRight;
 
   for (int nPosResolve = 0; nPosResolve < aPosResolveCount; ++nPosResolve) {
     aPosResolve[nPosResolve].visualIndex = kNotFound;
     aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
     aPosResolve[nPosResolve].visualWidth = kNotFound;
   }
 
   for (i = 0; i < runCount; i++) {
-    mozilla::intl::Bidi::Direction dir =
-        aBidiEngine->GetVisualRun(i, &start, &length);
+    nsBidiDirection dir = aBidiEngine->GetVisualRun(i, &start, &length);
 
-    EmbeddingLevel level;
+    nsBidiLevel level;
     aBidiEngine->GetLogicalRun(start, &limit, &level);
 
-    dir = level.Direction();
+    dir = DIRECTION_FROM_LEVEL(level);
     int32_t subRunLength = limit - start;
     int32_t lineOffset = start;
-    int32_t typeLimit = std::min(limit, AssertedCast<int32_t>(aLength));
+    int32_t typeLimit = std::min(limit, aLength);
     int32_t subRunCount = 1;
     int32_t subRunLimit = typeLimit;
 
     /*
      * If |level| is even, i.e. the direction of the run is left-to-right, we
      * render the subruns from left to right and increment the x-coordinate
      * |xOffset| by the width of each subrun after rendering.
      *
      * If |level| is odd, i.e. the direction of the run is right-to-left, we
      * render the subruns from right to left. We begin by incrementing |xOffset|
      * by the width of the whole run, and then decrement it by the width of each
      * subrun before rendering. After rendering all the subruns, we restore the
      * x-coordinate of the end of the run for the start of the next run.
      */
 
-    if (dir == intl::Bidi::Direction::RTL) {
-      aprocessor.SetText(text + start, subRunLength,
-                         intl::Bidi::Direction::RTL);
+    if (dir == NSBIDI_RTL) {
+      aprocessor.SetText(text + start, subRunLength, dir);
       width = aprocessor.GetWidth();
       xOffset += width;
       xEndRun = xOffset;
     }
 
     while (subRunCount > 0) {
       // CalculateCharType can increment subRunCount if the run
       // contains mixed character types
@@ -2243,17 +2222,17 @@ nsresult nsBidiPresUtils::ProcessText(co
       if (int32_t(runVisualText.Length()) < subRunLength)
         return NS_ERROR_OUT_OF_MEMORY;
       FormatUnicodeText(aPresContext, runVisualText.BeginWriting(),
                         subRunLength, (nsCharType)charType);
 
       aprocessor.SetText(runVisualText.get(), subRunLength, dir);
       width = aprocessor.GetWidth();
       totalWidth += width;
-      if (dir == mozilla::intl::Bidi::Direction::RTL) {
+      if (dir == NSBIDI_RTL) {
         xOffset -= width;
       }
       if (aMode == MODE_DRAW) {
         aprocessor.DrawText(xOffset, width);
       }
 
       /*
        * The caller may request to calculate the visual position of one
@@ -2313,17 +2292,17 @@ nsresult nsBidiPresUtils::ProcessText(co
              *    ^^^^^^ (subWidth)
              *    ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
              *          ^^ (posResolve->visualWidth)
              */
             nscoord subWidth;
             // The position in the text where this run's "left part" begins.
             const char16_t* visualLeftPart;
             const char16_t* visualRightSide;
-            if (dir == mozilla::intl::Bidi::Direction::RTL) {
+            if (dir == NSBIDI_RTL) {
               // One day, son, this could all be replaced with
               // mPresContext->GetBidiEngine().GetVisualIndex() ...
               posResolve->visualIndex =
                   visualStart +
                   (subRunLength - (posResolve->logicalIndex + 1 - start));
               // Skipping to the "left part".
               visualLeftPart = text + posResolve->logicalIndex + 1;
               // Skipping to the right side of the current character
@@ -2342,26 +2321,26 @@ nsresult nsBidiPresUtils::ProcessText(co
             subWidth = aprocessor.GetWidth();
             aprocessor.SetText(visualRightSide, visualLeftLength + 1, dir);
             posResolve->visualLeftTwips = xOffset + subWidth;
             posResolve->visualWidth = aprocessor.GetWidth() - subWidth;
           }
         }
       }
 
-      if (dir == intl::Bidi::Direction::LTR) {
+      if (dir == NSBIDI_LTR) {
         xOffset += width;
       }
 
       --subRunCount;
       start = lineOffset;
       subRunLimit = typeLimit;
       subRunLength = typeLimit - lineOffset;
     }  // while
-    if (dir == intl::Bidi::Direction::RTL) {
+    if (dir == NSBIDI_RTL) {
       xOffset = xEndRun;
     }
 
     visualStart += length;
   }  // for
 
   if (aWidth) {
     *aWidth = totalWidth;
@@ -2383,18 +2362,18 @@ class MOZ_STACK_CLASS nsIRenderingContex
         mFontMetrics(aFontMetrics),
         mPt(aPt),
         mText(nullptr),
         mLength(0) {}
 
   ~nsIRenderingContextBidiProcessor() { mFontMetrics->SetTextRunRTL(false); }
 
   virtual void SetText(const char16_t* aText, int32_t aLength,
-                       intl::Bidi::Direction aDirection) override {
-    mFontMetrics->SetTextRunRTL(aDirection == intl::Bidi::Direction::RTL);
+                       nsBidiDirection aDirection) override {
+    mFontMetrics->SetTextRunRTL(aDirection == NSBIDI_RTL);
     mText = aText;
     mLength = aLength;
   }
 
   virtual nscoord GetWidth() override {
     return nsLayoutUtils::AppUnitWidthOfString(mText, mLength, *mFontMetrics,
                                                mTextRunConstructionDrawTarget);
   }
@@ -2415,35 +2394,34 @@ class MOZ_STACK_CLASS nsIRenderingContex
   DrawTarget* mTextRunConstructionDrawTarget;
   nsFontMetrics* mFontMetrics;
   nsPoint mPt;
   const char16_t* mText;
   int32_t mLength;
 };
 
 nsresult nsBidiPresUtils::ProcessTextForRenderingContext(
-    const char16_t* aText, int32_t aLength, EmbeddingLevel aBaseLevel,
+    const char16_t* aText, int32_t aLength, nsBidiLevel aBaseLevel,
     nsPresContext* aPresContext, gfxContext& aRenderingContext,
     DrawTarget* aTextRunConstructionDrawTarget, nsFontMetrics& aFontMetrics,
     Mode aMode, nscoord aX, nscoord aY, nsBidiPositionResolve* aPosResolve,
     int32_t aPosResolveCount, nscoord* aWidth) {
   nsIRenderingContextBidiProcessor processor(&aRenderingContext,
                                              aTextRunConstructionDrawTarget,
                                              &aFontMetrics, nsPoint(aX, aY));
   return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor, aMode,
                      aPosResolve, aPosResolveCount, aWidth,
                      &aPresContext->GetBidiEngine());
 }
 
 /* static */
-EmbeddingLevel nsBidiPresUtils::BidiLevelFromStyle(
-    ComputedStyle* aComputedStyle) {
+nsBidiLevel nsBidiPresUtils::BidiLevelFromStyle(ComputedStyle* aComputedStyle) {
   if (aComputedStyle->StyleTextReset()->mUnicodeBidi &
       NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
-    return EmbeddingLevel::DefaultLTR();
+    return NSBIDI_DEFAULT_LTR;
   }
 
   if (aComputedStyle->StyleVisibility()->mDirection == StyleDirection::Rtl) {
-    return EmbeddingLevel::RTL();
+    return NSBIDI_RTL;
   }
 
-  return EmbeddingLevel::LTR();
+  return NSBIDI_LTR;
 }
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -3,17 +3,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/. */
 
 #ifndef nsBidiPresUtils_h___
 #define nsBidiPresUtils_h___
 
 #include "gfxContext.h"
-#include "mozilla/intl/Bidi.h"
+#include "nsBidi.h"
 #include "nsBidiUtils.h"
 #include "nsHashKeys.h"
 #include "nsCoord.h"
 #include "nsTArray.h"
 #include "nsLineBox.h"
 
 #ifdef DrawText
 #  undef DrawText
@@ -159,17 +159,17 @@ class nsBidiPresUtils {
      *  passing an index would be impossible.
      *
      * @param aText The string of text.
      * @param aLength The length of the string of text.
      * @param aDirection The direction of the text. The string will never have
      *  mixed direction.
      */
     virtual void SetText(const char16_t* aText, int32_t aLength,
-                         mozilla::intl::Bidi::Direction aDirection) = 0;
+                         nsBidiDirection aDirection) = 0;
 
     /**
      * Returns the measured width of the text given in SetText. If SetText was
      * not called with valid parameters, the result of this call is undefined.
      * This call is guaranteed to only be called once between SetText calls.
      * Will be invoked before DrawText.
      */
     virtual nscoord GetWidth() = 0;
@@ -224,46 +224,52 @@ class nsBidiPresUtils {
 
   /**
    * Reorder plain text using the Unicode Bidi algorithm and send it to
    * a rendering context for rendering.
    *
    * @param[in] aText  the string to be rendered (in logical order)
    * @param aLength the number of characters in the string
    * @param aBaseLevel the base embedding level of the string
+   *  odd values are right-to-left; even values are left-to-right, plus special
+   *  constants as follows (defined in nsBidi.h)
+   *  NSBIDI_LTR - left-to-right string
+   *  NSBIDI_RTL - right-to-left string
+   *  NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
+   *                       default is left-to-right
+   *  NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
+   *                       default is right-to-left
+   *
    * @param aPresContext the presentation context
    * @param aRenderingContext the rendering context to render to
    * @param aTextRunConstructionContext the rendering context to be used to
    * construct the textrun (affects font hinting)
    * @param aX the x-coordinate to render the string
    * @param aY the y-coordinate to render the string
    * @param[in,out] aPosResolve array of logical positions to resolve into
    * visual positions; can be nullptr if this functionality is not required
    * @param aPosResolveCount number of items in the aPosResolve array
    */
-  static nsresult RenderText(const char16_t* aText, int32_t aLength,
-                             mozilla::intl::Bidi::EmbeddingLevel aBaseLevel,
-                             nsPresContext* aPresContext,
-                             gfxContext& aRenderingContext,
-                             DrawTarget* aTextRunConstructionDrawTarget,
-                             nsFontMetrics& aFontMetrics, nscoord aX,
-                             nscoord aY,
-                             nsBidiPositionResolve* aPosResolve = nullptr,
-                             int32_t aPosResolveCount = 0) {
+  static nsresult RenderText(
+      const char16_t* aText, int32_t aLength, nsBidiLevel aBaseLevel,
+      nsPresContext* aPresContext, gfxContext& aRenderingContext,
+      DrawTarget* aTextRunConstructionDrawTarget, nsFontMetrics& aFontMetrics,
+      nscoord aX, nscoord aY, nsBidiPositionResolve* aPosResolve = nullptr,
+      int32_t aPosResolveCount = 0) {
     return ProcessTextForRenderingContext(
         aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
         aTextRunConstructionDrawTarget, aFontMetrics, MODE_DRAW, aX, aY,
         aPosResolve, aPosResolveCount, nullptr);
   }
 
-  static nscoord MeasureTextWidth(
-      const char16_t* aText, int32_t aLength,
-      mozilla::intl::Bidi::EmbeddingLevel aBaseLevel,
-      nsPresContext* aPresContext, gfxContext& aRenderingContext,
-      nsFontMetrics& aFontMetrics) {
+  static nscoord MeasureTextWidth(const char16_t* aText, int32_t aLength,
+                                  nsBidiLevel aBaseLevel,
+                                  nsPresContext* aPresContext,
+                                  gfxContext& aRenderingContext,
+                                  nsFontMetrics& aFontMetrics) {
     nscoord length;
     nsresult rv = ProcessTextForRenderingContext(
         aText, aLength, aBaseLevel, aPresContext, aRenderingContext,
         aRenderingContext.GetDrawTarget(), aFontMetrics, MODE_MEASURE, 0, 0,
         nullptr, 0, &length);
     return NS_SUCCEEDED(rv) ? length : 0;
   }
 
@@ -306,103 +312,109 @@ class nsBidiPresUtils {
   /**
    * Get the bidi data of the given (inline) frame.
    */
   static mozilla::FrameBidiData GetFrameBidiData(nsIFrame* aFrame);
 
   /**
    * Get the bidi embedding level of the given (inline) frame.
    */
-  static mozilla::intl::Bidi::EmbeddingLevel GetFrameEmbeddingLevel(
-      nsIFrame* aFrame);
+  static nsBidiLevel GetFrameEmbeddingLevel(nsIFrame* aFrame);
 
   /**
    * Get the bidi base level of the given (inline) frame.
    */
-  static mozilla::intl::Bidi::EmbeddingLevel GetFrameBaseLevel(
-      const nsIFrame* aFrame);
+  static nsBidiLevel GetFrameBaseLevel(const nsIFrame* aFrame);
 
   /**
-   * Get a mozilla::intl::Bidi::Direction representing the direction implied by
-   * the bidi base level of the frame.
-   * @return mozilla::intl::Bidi::Direction
+   * Get an nsBidiDirection representing the direction implied by the
+   * bidi base level of the frame.
+   * @return NSBIDI_LTR (left-to-right) or NSBIDI_RTL (right-to-left)
+   *  NSBIDI_MIXED will never be returned.
    */
-  static mozilla::intl::Bidi::Direction ParagraphDirection(
-      const nsIFrame* aFrame) {
-    return GetFrameBaseLevel(aFrame).Direction();
+  static nsBidiDirection ParagraphDirection(const nsIFrame* aFrame) {
+    return DIRECTION_FROM_LEVEL(GetFrameBaseLevel(aFrame));
   }
 
   /**
-   * Get a mozilla::intl::Bidi::Direction representing the direction implied by
-   * the bidi embedding level of the frame.
-   * @return mozilla::intl::Bidi::Direction
+   * Get an nsBidiDirection representing the direction implied by the
+   * bidi embedding level of the frame.
+   * @return NSBIDI_LTR (left-to-right) or NSBIDI_RTL (right-to-left)
+   *  NSBIDI_MIXED will never be returned.
    */
-  static mozilla::intl::Bidi::Direction FrameDirection(nsIFrame* aFrame) {
-    return GetFrameEmbeddingLevel(aFrame).Direction();
+  static nsBidiDirection FrameDirection(nsIFrame* aFrame) {
+    return DIRECTION_FROM_LEVEL(GetFrameEmbeddingLevel(aFrame));
   }
 
   static bool IsFrameInParagraphDirection(nsIFrame* aFrame) {
     return ParagraphDirection(aFrame) == FrameDirection(aFrame);
   }
 
   // This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
   // because it uses the frame pointer passed in without drilling down to
   // the leaf frame.
   static bool IsReversedDirectionFrame(const nsIFrame* aFrame) {
     mozilla::FrameBidiData bidiData = aFrame->GetBidiData();
-    return !bidiData.embeddingLevel.IsSameDirection(bidiData.baseLevel);
+    return !IS_SAME_DIRECTION(bidiData.embeddingLevel, bidiData.baseLevel);
   }
 
   enum Mode { MODE_DRAW, MODE_MEASURE };
 
   /**
    * Reorder plain text using the Unicode Bidi algorithm and send it to
    * a processor for rendering or measuring
    *
    * @param[in] aText  the string to be processed (in logical order)
    * @param aLength the number of characters in the string
    * @param aBaseLevel the base embedding level of the string
+   *  odd values are right-to-left; even values are left-to-right, plus special
+   *  constants as follows (defined in nsBidi.h)
+   *  NSBIDI_LTR - left-to-right string
+   *  NSBIDI_RTL - right-to-left string
+   *  NSBIDI_DEFAULT_LTR - auto direction determined by first strong character,
+   *                       default is left-to-right
+   *  NSBIDI_DEFAULT_RTL - auto direction determined by first strong character,
+   *                       default is right-to-left
+   *
    * @param aPresContext the presentation context
    * @param aprocessor the bidi processor
    * @param aMode the operation to process
    *  MODE_DRAW - invokes DrawText on the processor for each substring
    *  MODE_MEASURE - does not invoke DrawText on the processor
    *  Note that the string is always measured, regardless of mode
    * @param[in,out] aPosResolve array of logical positions to resolve into
    *  visual positions; can be nullptr if this functionality is not required
    * @param aPosResolveCount number of items in the aPosResolve array
    * @param[out] aWidth Pointer to where the width will be stored (may be null)
    */
-  static nsresult ProcessText(const char16_t* aText, size_t aLength,
-                              mozilla::intl::Bidi::EmbeddingLevel aBaseLevel,
+  static nsresult ProcessText(const char16_t* aText, int32_t aLength,
+                              nsBidiLevel aBaseLevel,
                               nsPresContext* aPresContext,
                               BidiProcessor& aprocessor, Mode aMode,
                               nsBidiPositionResolve* aPosResolve,
                               int32_t aPosResolveCount, nscoord* aWidth,
-                              mozilla::intl::Bidi* aBidiEngine);
+                              nsBidi* aBidiEngine);
 
   /**
    * Use style attributes to determine the base paragraph level to pass to the
    * bidi algorithm.
    *
-   * If |unicode-bidi| is set to "[-moz-]plaintext", returns
-   * EmbeddingLevel::DefaultLTR, in other words the direction is determined from
-   * the first strong character in the text according to rules P2 and P3 of the
-   * bidi algorithm, or LTR if there is no strong character.
+   * If |unicode-bidi| is set to "[-moz-]plaintext", returns NSBIDI_DEFAULT_LTR,
+   * in other words the direction is determined from the first strong character
+   * in the text according to rules P2 and P3 of the bidi algorithm, or LTR if
+   * there is no strong character.
    *
-   * Otherwise returns EmbeddingLevel::LTR or EmbeddingLevel::RTL depending on
-   * the value of |direction|
+   * Otherwise returns NSBIDI_LTR or NSBIDI_RTL depending on the value of
+   * |direction|
    */
-  static mozilla::intl::Bidi::EmbeddingLevel BidiLevelFromStyle(
-      mozilla::ComputedStyle* aComputedStyle);
+  static nsBidiLevel BidiLevelFromStyle(mozilla::ComputedStyle* aComputedStyle);
 
  private:
   static nsresult ProcessTextForRenderingContext(
-      const char16_t* aText, int32_t aLength,
-      mozilla::intl::Bidi::EmbeddingLevel aBaseLevel,
+      const char16_t* aText, int32_t aLength, nsBidiLevel aBaseLevel,
       nsPresContext* aPresContext, gfxContext& aRenderingContext,
       DrawTarget* aTextRunConstructionDrawTarget, nsFontMetrics& aFontMetrics,
       Mode aMode,
       nscoord aX,                         // DRAW only
       nscoord aY,                         // DRAW only
       nsBidiPositionResolve* aPosResolve, /* may be null */
       int32_t aPosResolveCount, nscoord* aWidth /* may be null */);
 
@@ -555,18 +567,18 @@ class nsBidiPresUtils {
    * @param aFirstIndex  index of aFrame in mLogicalFrames
    * @param aLastIndex   index of the last frame to be removed
    *
    * @see Resolve()
    * @see EnsureBidiContinuation()
    */
   static void RemoveBidiContinuation(BidiParagraphData* aBpd, nsIFrame* aFrame,
                                      int32_t aFirstIndex, int32_t aLastIndex);
-  static void CalculateCharType(mozilla::intl::Bidi* aBidiEngine,
-                                const char16_t* aText, int32_t& aOffset,
-                                int32_t aCharTypeLimit, int32_t& aRunLimit,
-                                int32_t& aRunLength, int32_t& aRunCount,
-                                uint8_t& aCharType, uint8_t& aPrevCharType);
+  static void CalculateCharType(nsBidi* aBidiEngine, const char16_t* aText,
+                                int32_t& aOffset, int32_t aCharTypeLimit,
+                                int32_t& aRunLimit, int32_t& aRunLength,
+                                int32_t& aRunCount, uint8_t& aCharType,
+                                uint8_t& aPrevCharType);
 
   static void StripBidiControlCharacters(char16_t* aText, int32_t& aTextLength);
 };
 
 #endif /* nsBidiPresUtils_h___ */
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -7,17 +7,16 @@
 /* the caret is the text cursor used, e.g., when editing */
 
 #include "nsCaret.h"
 
 #include <algorithm>
 
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
-#include "mozilla/intl/Bidi.h"
 #include "nsCOMPtr.h"
 #include "nsFontMetrics.h"
 #include "nsITimer.h"
 #include "nsFrameSelection.h"
 #include "nsIFrame.h"
 #include "nsIScrollableFrame.h"
 #include "nsIContent.h"
 #include "nsIFrameInlines.h"
@@ -35,18 +34,16 @@
 #include "mozilla/dom/Selection.h"
 #include "nsIBidiKeyboard.h"
 #include "nsContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
-using EmbeddingLevel = mozilla::intl::Bidi::EmbeddingLevel;
-
 // The bidi indicator hangs off the caret to one side, to show which
 // direction the typing is in. It needs to be at least 2x2 to avoid looking like
 // an insignificant dot
 static const int32_t kMinBidiIndicatorPixels = 2;
 
 // The default caret blinking rate (in ms of blinking interval)
 static const uint32_t kDefaultCaretBlinkRate = 500;
 
@@ -387,18 +384,17 @@ nsIFrame* nsCaret::GetFrameAndOffset(con
   }
 
   if (!focusNode || !focusNode->IsContent() || !aSelection) {
     return nullptr;
   }
 
   nsIContent* contentNode = focusNode->AsContent();
   nsFrameSelection* frameSelection = aSelection->GetFrameSelection();
-  mozilla::intl::Bidi::EmbeddingLevel bidiLevel =
-      frameSelection->GetCaretBidiLevel();
+  nsBidiLevel bidiLevel = frameSelection->GetCaretBidiLevel();
 
   return nsCaret::GetCaretFrameForNodeOffset(
       frameSelection, contentNode, focusOffset, frameSelection->GetHint(),
       bidiLevel, aUnadjustedFrame, aFrameOffset);
 }
 
 /* static */
 nsIFrame* nsCaret::GetGeometry(const Selection* aSelection, nsRect* aRect) {
@@ -643,18 +639,17 @@ void nsCaret::StopBlinking() {
   if (mBlinkTimer) {
     mBlinkTimer->Cancel();
     mBlinkRate = 0;
   }
 }
 
 nsIFrame* nsCaret::GetCaretFrameForNodeOffset(
     nsFrameSelection* aFrameSelection, nsIContent* aContentNode,
-    int32_t aOffset, CaretAssociationHint aFrameHint,
-    mozilla::intl::Bidi::EmbeddingLevel aBidiLevel,
+    int32_t aOffset, CaretAssociationHint aFrameHint, nsBidiLevel aBidiLevel,
     nsIFrame** aReturnUnadjustedFrame, int32_t* aReturnOffset) {
   if (!aFrameSelection) {
     return nullptr;
   }
 
   PresShell* presShell = aFrameSelection->GetPresShell();
   if (!presShell) {
     return nullptr;
@@ -696,20 +691,18 @@ nsIFrame* nsCaret::GetCaretFrameForNodeO
     // If there has been a reflow, take the caret Bidi level to be the level of
     // the current frame
     if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
       aBidiLevel = theFrame->GetEmbeddingLevel();
     }
 
     nsIFrame* frameBefore;
     nsIFrame* frameAfter;
-    mozilla::intl::Bidi::EmbeddingLevel
-        levelBefore;  // Bidi level of the character before the caret
-    mozilla::intl::Bidi::EmbeddingLevel
-        levelAfter;  // Bidi level of the character after the caret
+    nsBidiLevel levelBefore;  // Bidi level of the character before the caret
+    nsBidiLevel levelAfter;   // Bidi level of the character after the caret
 
     auto [start, end] = theFrame->GetOffsets();
     if (start == 0 || end == 0 || start == theFrameOffset ||
         end == theFrameOffset) {
       nsPrevNextBidiLevels levels =
           aFrameSelection->GetPrevNextBidiLevels(aContentNode, aOffset, false);
 
       /* Boundary condition, we need to know the Bidi levels of the characters
@@ -722,111 +715,105 @@ nsIFrame* nsCaret::GetCaretFrameForNodeO
 
         if ((levelBefore != levelAfter) || (aBidiLevel != levelBefore)) {
           aBidiLevel = std::max(aBidiLevel,
                                 std::min(levelBefore, levelAfter));  // rule c3
           aBidiLevel = std::min(aBidiLevel,
                                 std::max(levelBefore, levelAfter));  // rule c4
           if (aBidiLevel == levelBefore ||                           // rule c1
               (aBidiLevel > levelBefore && aBidiLevel < levelAfter &&
-               aBidiLevel.IsSameDirection(levelBefore)) ||  // rule c5
+               IS_SAME_DIRECTION(aBidiLevel, levelBefore)) ||  // rule c5
               (aBidiLevel < levelBefore && aBidiLevel > levelAfter &&
-               aBidiLevel.IsSameDirection(levelBefore)))  // rule c9
+               IS_SAME_DIRECTION(aBidiLevel, levelBefore)))  // rule c9
           {
             if (theFrame != frameBefore) {
               if (frameBefore) {  // if there is a frameBefore, move into it
                 theFrame = frameBefore;
                 std::tie(start, end) = theFrame->GetOffsets();
                 theFrameOffset = end;
               } else {
                 // if there is no frameBefore, we must be at the beginning of
                 // the line so we stay with the current frame. Exception: when
                 // the first frame on the line has a different Bidi level from
                 // the paragraph level, there is no real frame for the caret to
                 // be in. We have to find the visually first frame on the line.
-                mozilla::intl::Bidi::EmbeddingLevel baseLevel =
-                    frameAfter->GetBaseLevel();
+                nsBidiLevel baseLevel = frameAfter->GetBaseLevel();
                 if (baseLevel != levelAfter) {
                   nsPeekOffsetStruct pos(eSelectBeginLine, eDirPrevious, 0,
                                          nsPoint(0, 0), false, true, false,
                                          true, false);
                   if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) {
                     theFrame = pos.mResultFrame;
                     theFrameOffset = pos.mContentOffset;
                   }
                 }
               }
             }
           } else if (aBidiLevel == levelAfter ||  // rule c2
                      (aBidiLevel > levelBefore && aBidiLevel < levelAfter &&
-                      aBidiLevel.IsSameDirection(levelAfter)) ||  // rule c6
+                      IS_SAME_DIRECTION(aBidiLevel, levelAfter)) ||  // rule c6
                      (aBidiLevel < levelBefore && aBidiLevel > levelAfter &&
-                      aBidiLevel.IsSameDirection(levelAfter)))  // rule c10
+                      IS_SAME_DIRECTION(aBidiLevel, levelAfter)))  // rule c10
           {
             if (theFrame != frameAfter) {
               if (frameAfter) {
                 // if there is a frameAfter, move into it
                 theFrame = frameAfter;
                 std::tie(start, end) = theFrame->GetOffsets();
                 theFrameOffset = start;
               } else {
                 // if there is no frameAfter, we must be at the end of the line
                 // so we stay with the current frame.
                 // Exception: when the last frame on the line has a different
                 // Bidi level from the paragraph level, there is no real frame
                 // for the caret to be in. We have to find the visually last
                 // frame on the line.
-                mozilla::intl::Bidi::EmbeddingLevel baseLevel =
-                    frameBefore->GetBaseLevel();
+                nsBidiLevel baseLevel = frameBefore->GetBaseLevel();
                 if (baseLevel != levelBefore) {
                   nsPeekOffsetStruct pos(eSelectEndLine, eDirNext, 0,
                                          nsPoint(0, 0), false, true, false,
                                          true, false);
                   if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) {
                     theFrame = pos.mResultFrame;
                     theFrameOffset = pos.mContentOffset;
                   }
                 }
               }
             }
           } else if (aBidiLevel > levelBefore &&
                      aBidiLevel < levelAfter &&  // rule c7/8
                      // before and after have the same parity
-                     levelBefore.IsSameDirection(levelAfter) &&
+                     IS_SAME_DIRECTION(levelBefore, levelAfter) &&
                      // caret has different parity
-                     !aBidiLevel.IsSameDirection(levelAfter)) {
+                     !IS_SAME_DIRECTION(aBidiLevel, levelAfter)) {
             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(
                     frameAfter, eDirNext, aBidiLevel, &theFrame))) {
               std::tie(start, end) = theFrame->GetOffsets();
               levelAfter = theFrame->GetEmbeddingLevel();
-              if (aBidiLevel.IsRTL()) {
-                // c8: caret to the right of the rightmost character
-                theFrameOffset = levelAfter.IsRTL() ? start : end;
-              } else {
-                // c7: caret to the left of the leftmost character
-                theFrameOffset = levelAfter.IsRTL() ? end : start;
-              }
+              if (IS_LEVEL_RTL(aBidiLevel))  // c8: caret to the right of the
+                                             // rightmost character
+                theFrameOffset = IS_LEVEL_RTL(levelAfter) ? start : end;
+              else  // c7: caret to the left of the leftmost character
+                theFrameOffset = IS_LEVEL_RTL(levelAfter) ? end : start;
             }
           } else if (aBidiLevel < levelBefore &&
                      aBidiLevel > levelAfter &&  // rule c11/12
                      // before and after have the same parity
-                     levelBefore.IsSameDirection(levelAfter) &&
+                     IS_SAME_DIRECTION(levelBefore, levelAfter) &&
                      // caret has different parity
-                     !aBidiLevel.IsSameDirection(levelAfter)) {
+                     !IS_SAME_DIRECTION(aBidiLevel, levelAfter)) {
             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(
                     frameBefore, eDirPrevious, aBidiLevel, &theFrame))) {
               std::tie(start, end) = theFrame->GetOffsets();
               levelBefore = theFrame->GetEmbeddingLevel();
-              if (aBidiLevel.IsRTL()) {
-                // c12: caret to the left of the leftmost character
-                theFrameOffset = levelBefore.IsRTL() ? end : start;
-              } else {
-                // c11: caret to the right of the rightmost character
-                theFrameOffset = levelBefore.IsRTL() ? start : end;
-              }
+              if (IS_LEVEL_RTL(aBidiLevel))  // c12: caret to the left of the
+                                             // leftmost character
+                theFrameOffset = IS_LEVEL_RTL(levelBefore) ? end : start;
+              else  // c11: caret to the right of the rightmost character
+                theFrameOffset = IS_LEVEL_RTL(levelBefore) ? start : end;
             }
           }
         }
       }
     }
   }
 
   *aReturnOffset = theFrameOffset;
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -4,17 +4,16 @@
  * 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/. */
 
 /* the caret is the text cursor used, e.g., when editing */
 
 #ifndef nsCaret_h__
 #define nsCaret_h__
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Selection.h"
 #include "nsCoord.h"
 #include "nsISelectionListener.h"
 #include "nsIWeakReferenceUtils.h"
 #include "CaretAssociationHint.h"
 #include "nsPoint.h"
 #include "nsRect.h"
@@ -175,18 +174,17 @@ class nsCaret final : public nsISelectio
    * aSelection is not collapsed.
    * This rect does not include any extra decorations for bidi.
    * @param aRect must be non-null
    */
   static nsIFrame* GetGeometry(const mozilla::dom::Selection* aSelection,
                                nsRect* aRect);
   static nsIFrame* GetCaretFrameForNodeOffset(
       nsFrameSelection* aFrameSelection, nsIContent* aContentNode,
-      int32_t aOffset, CaretAssociationHint aFrameHint,
-      mozilla::intl::Bidi::EmbeddingLevel aBidiLevel,
+      int32_t aOffset, CaretAssociationHint aFrameHint, uint8_t aBidiLevel,
       nsIFrame** aReturnUnadjustedFrame, int32_t* aReturnOffset);
   static nsRect GetGeometryForFrame(nsIFrame* aFrame, int32_t aFrameOffset,
                                     nscoord* aBidiIndicatorSize);
 
   // Get the frame and frame offset based on the focus node and focus offset
   // of aSelection. If aOverrideNode and aOverride are provided, use them
   // instead.
   // @param aFrameOffset return the frame offset if non-null.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -47,17 +47,16 @@
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/InspectorFontFace.h"
 #include "mozilla/dom/KeyframeEffect.h"
 #include "mozilla/dom/SVGViewportElement.h"
 #include "mozilla/dom/UIEvent.h"
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/PathHelpers.h"
@@ -1538,20 +1537,17 @@ nsIFrame* nsLayoutUtils::GetNearestOverf
 // static
 nsRect nsLayoutUtils::GetScrolledRect(nsIFrame* aScrolledFrame,
                                       const nsRect& aScrolledFrameOverflowArea,
                                       const nsSize& aScrollPortSize,
                                       StyleDirection aDirection) {
   WritingMode wm = aScrolledFrame->GetWritingMode();
   // Potentially override the frame's direction to use the direction found
   // by ScrollFrameHelper::GetScrolledFrameDir()
-  wm.SetDirectionFromBidiLevel(
-      aDirection == StyleDirection::Rtl
-          ? mozilla::intl::Bidi::EmbeddingLevel::RTL()
-          : mozilla::intl::Bidi::EmbeddingLevel::LTR());
+  wm.SetDirectionFromBidiLevel(aDirection == StyleDirection::Rtl ? 1 : 0);
 
   nscoord x1 = aScrolledFrameOverflowArea.x,
           x2 = aScrolledFrameOverflowArea.XMost(),
           y1 = aScrolledFrameOverflowArea.y,
           y2 = aScrolledFrameOverflowArea.YMost();
 
   const bool isHorizontalWM = !wm.IsVertical();
   const bool isVerticalWM = wm.IsVertical();
@@ -5551,18 +5547,17 @@ nscoord nsLayoutUtils::AppUnitWidthOfStr
 
 nscoord nsLayoutUtils::AppUnitWidthOfStringBidi(const char16_t* aString,
                                                 uint32_t aLength,
                                                 const nsIFrame* aFrame,
                                                 nsFontMetrics& aFontMetrics,
                                                 gfxContext& aContext) {
   nsPresContext* presContext = aFrame->PresContext();
   if (presContext->BidiEnabled()) {
-    mozilla::intl::Bidi::EmbeddingLevel level =
-        nsBidiPresUtils::BidiLevelFromStyle(aFrame->Style());
+    nsBidiLevel level = nsBidiPresUtils::BidiLevelFromStyle(aFrame->Style());
     return nsBidiPresUtils::MeasureTextWidth(
         aString, aLength, level, presContext, aContext, aFontMetrics);
   }
   aFontMetrics.SetTextRunRTL(false);
   aFontMetrics.SetVertical(aFrame->GetWritingMode().IsVertical());
   aFontMetrics.SetTextOrientation(aFrame->StyleVisibility()->mTextOrientation);
   return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
                                              aContext.GetDrawTarget());
@@ -5631,18 +5626,17 @@ void nsLayoutUtils::DrawString(const nsI
     aFontMetrics.SetVertical(WritingMode(aComputedStyle).IsVertical());
   }
 
   aFontMetrics.SetTextOrientation(
       aComputedStyle->StyleVisibility()->mTextOrientation);
 
   nsPresContext* presContext = aFrame->PresContext();
   if (presContext->BidiEnabled()) {
-    mozilla::intl::Bidi::EmbeddingLevel level =
-        nsBidiPresUtils::BidiLevelFromStyle(aComputedStyle);
+    nsBidiLevel level = nsBidiPresUtils::BidiLevelFromStyle(aComputedStyle);
     rv = nsBidiPresUtils::RenderText(aString, aLength, level, presContext,
                                      *aContext, aContext->GetDrawTarget(),
                                      aFontMetrics, aPoint.x, aPoint.y);
   }
   if (NS_FAILED(rv)) {
     aFontMetrics.SetTextRunRTL(false);
     DrawUniDirString(aString, aLength, aPoint, aFontMetrics, *aContext);
   }
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -95,16 +95,17 @@
 #include "mozilla/dom/ImageTracker.h"
 
 // Needed for Start/Stop of Image Animation
 #include "imgIContainer.h"
 #include "nsIImageLoadingContent.h"
 
 #include "nsBidiUtils.h"
 #include "nsServiceManagerUtils.h"
+#include "nsBidi.h"
 
 #include "mozilla/dom/URL.h"
 #include "mozilla/ServoCSSParser.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
@@ -2677,21 +2678,21 @@ uint64_t nsPresContext::GetRestyleGenera
 
 uint64_t nsPresContext::GetUndisplayedRestyleGeneration() const {
   if (!mRestyleManager) {
     return 0;
   }
   return mRestyleManager->GetUndisplayedRestyleGeneration();
 }
 
-mozilla::intl::Bidi& nsPresContext::GetBidiEngine() {
+nsBidi& nsPresContext::GetBidiEngine() {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mBidiEngine) {
-    mBidiEngine.reset(new mozilla::intl::Bidi());
+    mBidiEngine.reset(new nsBidi());
   }
   return *mBidiEngine;
 }
 
 void nsPresContext::FlushFontFeatureValues() {
   if (!mPresShell) {
     return;  // we've been torn down
   }
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -4,17 +4,16 @@
  * 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/. */
 
 /* a presentation of a document, part 1 */
 
 #ifndef nsPresContext_h___
 #define nsPresContext_h___
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/AppUnits.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/MediaEmulationData.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/PreferenceSheet.h"
 #include "mozilla/PresShellForwards.h"
@@ -39,16 +38,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsChangeHint.h"
 #include "gfxTypes.h"
 #include "gfxRect.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "Units.h"
 
+class nsBidi;
 class nsIPrintSettings;
 class nsDocShell;
 class nsIDocShell;
 class nsITheme;
 class nsITimer;
 class nsIContent;
 class nsIFrame;
 class nsFrameManager;
@@ -1074,17 +1074,17 @@ class nsPresContext : public nsISupports
   bool HasWarnedAboutTooLargeDashedOrDottedRadius() const {
     return mHasWarnedAboutTooLargeDashedOrDottedRadius;
   }
 
   void SetHasWarnedAboutTooLargeDashedOrDottedRadius() {
     mHasWarnedAboutTooLargeDashedOrDottedRadius = true;
   }
 
-  mozilla::intl::Bidi& GetBidiEngine();
+  nsBidi& GetBidiEngine();
 
   gfxFontFeatureValueSet* GetFontFeatureValuesLookup() const {
     return mFontFeatureValuesLookup;
   }
 
  protected:
   friend class nsRunnableMethod<nsPresContext>;
   void ThemeChangedInternal();
@@ -1202,17 +1202,17 @@ class nsPresContext : public nsISupports
   gfxSize mLastFontInflationScreenSize;
 
   int32_t mCurAppUnitsPerDevPixel;
   int32_t mAutoQualityMinFontSizePixelsPref;
 
   nsCOMPtr<nsITheme> mTheme;
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
 
-  mozilla::UniquePtr<mozilla::intl::Bidi> mBidiEngine;
+  mozilla::UniquePtr<nsBidi> mBidiEngine;
 
   AutoTArray<TransactionInvalidations, 4> mTransactions;
 
   // text performance metrics
   mozilla::UniquePtr<gfxTextPerfMetrics> mTextPerf;
 
   mozilla::UniquePtr<gfxMissingFontRecorder> mMissingFonts;
 
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -4,17 +4,16 @@
  * 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 WritingModes_h_
 #define WritingModes_h_
 
 #include <ostream>
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/EnumeratedRange.h"
 
 #include "nsRect.h"
 #include "nsBidiUtils.h"
 
 // It is the caller's responsibility to operate on logical-coordinate objects
 // with matched writing modes. Failure to do so will be a runtime bug; the
@@ -520,18 +519,18 @@ class WritingMode {
    * If it turns out that our bidi direction already matches what plaintext
    * resolution determined, there's nothing to do here. If it didn't (i.e. if
    * the rtl-ness doesn't match), then we correct the direction by flipping the
    * same bits that get flipped in the constructor's CSS 'direction'-based
    * chunk.
    *
    * XXX change uint8_t to UBiDiLevel after bug 924851
    */
-  void SetDirectionFromBidiLevel(mozilla::intl::Bidi::EmbeddingLevel level) {
-    if (level.IsRTL() == IsBidiLTR()) {
+  void SetDirectionFromBidiLevel(uint8_t level) {
+    if (IS_LEVEL_RTL(level) == IsBidiLTR()) {
       mWritingMode ^= StyleWritingMode::RTL | StyleWritingMode::INLINE_REVERSED;
     }
   }
 
   /**
    * Compare two WritingModes for equality.
    */
   bool operator==(const WritingMode& aOther) const {
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.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 http://mozilla.org/MPL/2.0/. */
 
 #include "nsFrameList.h"
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/ArenaObjectID.h"
 #include "mozilla/PresShell.h"
 #include "nsBidiPresUtils.h"
 #include "nsContainerFrame.h"
 #include "nsGkAtoms.h"
 #include "nsILineIterator.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
@@ -301,26 +300,25 @@ void nsFrameList::List(FILE* out) const 
 #endif
 
 nsIFrame* nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const {
   if (!mFirstChild) return nullptr;
 
   nsIFrame* parent = mFirstChild->GetParent();
   if (!parent) return aFrame ? aFrame->GetPrevSibling() : LastChild();
 
-  mozilla::intl::Bidi::Direction paraDir =
-      nsBidiPresUtils::ParagraphDirection(mFirstChild);
+  nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(mFirstChild);
 
   nsAutoLineIterator iter = parent->GetLineIterator();
   if (!iter) {
     // Parent is not a block Frame
     if (parent->IsLineFrame()) {
       // Line frames are not bidi-splittable, so need to consider bidi
       // reordering
-      if (paraDir == mozilla::intl::Bidi::Direction::LTR) {
+      if (paraDir == NSBIDI_LTR) {
         return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
       } else {  // RTL
         return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
       }
     } else {
       // Just get the next or prev sibling, depending on block and frame
       // direction.
       if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
@@ -342,56 +340,55 @@ nsIFrame* nsFrameList::GetPrevVisualFor(
     thisLine = iter->GetNumLines();
   }
 
   nsIFrame* frame = nullptr;
 
   if (aFrame) {
     auto line = iter->GetLine(thisLine).unwrap();
 
-    if (paraDir == mozilla::intl::Bidi::Direction::LTR) {
+    if (paraDir == NSBIDI_LTR) {
       frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, line.mFirstFrameOnLine,
                                                 line.mNumFramesOnLine);
     } else {  // RTL
       frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, line.mFirstFrameOnLine,
                                                  line.mNumFramesOnLine);
     }
   }
 
   if (!frame && thisLine > 0) {
     // Get the last frame of the previous line
     auto line = iter->GetLine(thisLine - 1).unwrap();
 
-    if (paraDir == mozilla::intl::Bidi::Direction::LTR) {
+    if (paraDir == NSBIDI_LTR) {
       frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, line.mFirstFrameOnLine,
                                                 line.mNumFramesOnLine);
     } else {  // RTL
       frame = nsBidiPresUtils::GetFrameToRightOf(
           nullptr, line.mFirstFrameOnLine, line.mNumFramesOnLine);
     }
   }
   return frame;
 }
 
 nsIFrame* nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const {
   if (!mFirstChild) return nullptr;
 
   nsIFrame* parent = mFirstChild->GetParent();
   if (!parent) return aFrame ? aFrame->GetPrevSibling() : mFirstChild;
 
-  mozilla::intl::Bidi::Direction paraDir =
-      nsBidiPresUtils::ParagraphDirection(mFirstChild);
+  nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(mFirstChild);
 
   nsAutoLineIterator iter = parent->GetLineIterator();
   if (!iter) {
     // Parent is not a block Frame
     if (parent->IsLineFrame()) {
       // Line frames are not bidi-splittable, so need to consider bidi
       // reordering
-      if (paraDir == mozilla::intl::Bidi::Direction::LTR) {
+      if (paraDir == NSBIDI_LTR) {
         return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
       } else {  // RTL
         return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
       }
     } else {
       // Just get the next or prev sibling, depending on block and frame
       // direction.
       if (nsBidiPresUtils::IsFrameInParagraphDirection(mFirstChild)) {
@@ -413,31 +410,31 @@ nsIFrame* nsFrameList::GetNextVisualFor(
     thisLine = -1;
   }
 
   nsIFrame* frame = nullptr;
 
   if (aFrame) {
     auto line = iter->GetLine(thisLine).unwrap();
 
-    if (paraDir == mozilla::intl::Bidi::Direction::LTR) {
+    if (paraDir == NSBIDI_LTR) {
       frame = nsBidiPresUtils::GetFrameToRightOf(aFrame, line.mFirstFrameOnLine,
                                                  line.mNumFramesOnLine);
     } else {  // RTL
       frame = nsBidiPresUtils::GetFrameToLeftOf(aFrame, line.mFirstFrameOnLine,
                                                 line.mNumFramesOnLine);
     }
   }
 
   int32_t numLines = iter->GetNumLines();
   if (!frame && thisLine < numLines - 1) {
     // Get the first frame of the next line
     auto line = iter->GetLine(thisLine + 1).unwrap();
 
-    if (paraDir == mozilla::intl::Bidi::Direction::LTR) {
+    if (paraDir == NSBIDI_LTR) {
       frame = nsBidiPresUtils::GetFrameToRightOf(
           nullptr, line.mFirstFrameOnLine, line.mNumFramesOnLine);
     } else {  // RTL
       frame = nsBidiPresUtils::GetFrameToLeftOf(nullptr, line.mFirstFrameOnLine,
                                                 line.mNumFramesOnLine);
     }
   }
   return frame;
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Implementation of nsFrameSelection
  */
 
 #include "nsFrameSelection.h"
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/Logging.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/ScrollTypes.h"
@@ -595,35 +594,33 @@ nsresult nsFrameSelection::ConstrainFram
   //
 
   aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
 
   return NS_OK;
 }
 
 void nsFrameSelection::SetCaretBidiLevelAndMaybeSchedulePaint(
-    mozilla::intl::Bidi::EmbeddingLevel aLevel) {
+    nsBidiLevel aLevel) {
   // If the current level is undefined, we have just inserted new text.
   // In this case, we don't want to reset the keyboard language
   mCaret.mBidiLevel = aLevel;
 
   RefPtr<nsCaret> caret;
   if (mPresShell && (caret = mPresShell->GetCaret())) {
     caret->SchedulePaint();
   }
 }
 
-mozilla::intl::Bidi::EmbeddingLevel nsFrameSelection::GetCaretBidiLevel()
-    const {
+nsBidiLevel nsFrameSelection::GetCaretBidiLevel() const {
   return mCaret.mBidiLevel;
 }
 
 void nsFrameSelection::UndefineCaretBidiLevel() {
-  mCaret.mBidiLevel = mozilla::intl::Bidi::EmbeddingLevel(mCaret.mBidiLevel |
-                                                          BIDI_LEVEL_UNDEFINED);
+  mCaret.mBidiLevel |= BIDI_LEVEL_UNDEFINED;
 }
 
 #ifdef PRINT_RANGE
 void printRange(nsRange* aDomRange) {
   if (!aDomRange) {
     printf("NULL Range\n");
   }
   nsINode* startNode = aDomRange->GetStartContainer();
@@ -660,20 +657,19 @@ static nsINode* GetClosestInclusiveTable
     current = current->GetParent();
   }
   return nullptr;
 }
 
 static nsDirection GetCaretDirection(const nsIFrame& aFrame,
                                      nsDirection aDirection,
                                      bool aVisualMovement) {
-  const mozilla::intl::Bidi::Direction paragraphDirection =
+  const nsBidiDirection paragraphDirection =
       nsBidiPresUtils::ParagraphDirection(&aFrame);
-  return (aVisualMovement &&
-          paragraphDirection == mozilla::intl::Bidi::Direction::RTL)
+  return (aVisualMovement && paragraphDirection == NSBIDI_RTL)
              ? nsDirection(1 - aDirection)
              : aDirection;
 }
 
 nsresult nsFrameSelection::MoveCaret(nsDirection aDirection,
                                      bool aContinueSelection,
                                      const nsSelectionAmount aAmount,
                                      CaretMovementStyle aMovementStyle) {
@@ -927,18 +923,17 @@ nsPrevNextBidiLevels nsFrameSelection::G
     nsIContent* aNode, uint32_t aContentOffset, CaretAssociateHint aHint,
     bool aJumpLines) {
   // Get the level of the frames on each side
   nsIFrame* currentFrame;
   int32_t currentOffset;
   nsDirection direction;
 
   nsPrevNextBidiLevels levels{};
-  levels.SetData(nullptr, nullptr, mozilla::intl::Bidi::EmbeddingLevel::LTR(),
-                 mozilla::intl::Bidi::EmbeddingLevel::LTR());
+  levels.SetData(nullptr, nullptr, 0, 0);
 
   currentFrame = GetFrameForNodeOffset(
       aNode, static_cast<int32_t>(aContentOffset), aHint, &currentOffset);
   if (!currentFrame) {
     return levels;
   }
 
   auto [frameStart, frameEnd] = currentFrame->GetOffsets();
@@ -947,30 +942,29 @@ nsPrevNextBidiLevels nsFrameSelection::G
     direction = eDirPrevious;
   } else if (frameStart == currentOffset) {
     direction = eDirPrevious;
   } else if (frameEnd == currentOffset) {
     direction = eDirNext;
   } else {
     // we are neither at the beginning nor at the end of the frame, so we have
     // no worries
-    mozilla::intl::Bidi::EmbeddingLevel currentLevel =
-        currentFrame->GetEmbeddingLevel();
+    nsBidiLevel currentLevel = currentFrame->GetEmbeddingLevel();
     levels.SetData(currentFrame, currentFrame, currentLevel, currentLevel);
     return levels;
   }
 
   nsIFrame* newFrame =
       currentFrame
           ->GetFrameFromDirection(direction, false, aJumpLines, true, false)
           .mFrame;
 
   FrameBidiData currentBidi = currentFrame->GetBidiData();
-  mozilla::intl::Bidi::EmbeddingLevel currentLevel = currentBidi.embeddingLevel;
-  mozilla::intl::Bidi::EmbeddingLevel newLevel =
+  nsBidiLevel currentLevel = currentBidi.embeddingLevel;
+  nsBidiLevel newLevel =
       newFrame ? newFrame->GetEmbeddingLevel() : currentBidi.baseLevel;
 
   // If not jumping lines, disregard br frames, since they might be positioned
   // incorrectly.
   // XXX This could be removed once bug 339786 is fixed.
   if (!aJumpLines) {
     if (currentFrame->IsBrFrame()) {
       currentFrame = nullptr;
@@ -985,23 +979,22 @@ nsPrevNextBidiLevels nsFrameSelection::G
   if (direction == eDirNext)
     levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
   else
     levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
 
   return levels;
 }
 
-nsresult nsFrameSelection::GetFrameFromLevel(
-    nsIFrame* aFrameIn, nsDirection aDirection,
-    mozilla::intl::Bidi::EmbeddingLevel aBidiLevel,
-    nsIFrame** aFrameOut) const {
+nsresult nsFrameSelection::GetFrameFromLevel(nsIFrame* aFrameIn,
+                                             nsDirection aDirection,
+                                             nsBidiLevel aBidiLevel,
+                                             nsIFrame** aFrameOut) const {
   NS_ENSURE_STATE(mPresShell);
-  mozilla::intl::Bidi::EmbeddingLevel foundLevel =
-      mozilla::intl::Bidi::EmbeddingLevel::LTR();
+  nsBidiLevel foundLevel = 0;
   nsIFrame* foundFrame = aFrameIn;
 
   nsCOMPtr<nsIFrameEnumerator> frameTraversal;
   nsresult result;
   nsCOMPtr<nsIFrameTraversal> trav(
       do_CreateInstance(kFrameTraversalCID, &result));
   if (NS_FAILED(result)) return result;
 
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -2,17 +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 nsFrameSelection_h___
 #define nsFrameSelection_h___
 
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/Result.h"
 #include "mozilla/TextRange.h"
 #include "mozilla/UniquePtr.h"
 #include "nsIFrame.h"
@@ -21,17 +20,17 @@
 #include "nsISelectionListener.h"
 #include "nsITableCellLayout.h"
 #include "WordMovementType.h"
 #include "CaretAssociationHint.h"
 #include "nsBidiPresUtils.h"
 
 class nsRange;
 
-#define BIDI_LEVEL_UNDEFINED mozilla::intl::Bidi::EmbeddingLevel(0x80)
+#define BIDI_LEVEL_UNDEFINED 0x80
 
 //----------------------------------------------------------------------
 
 // Selection interface
 
 struct SelectionDetails {
   SelectionDetails()
       : mStart(), mEnd(), mSelectionType(mozilla::SelectionType::eInvalid) {
@@ -168,27 +167,26 @@ struct MOZ_STACK_CLASS nsPeekOffsetStruc
   // logically after the caret".
   //
   // Used with: eSelectLine, eSelectBeginLine, eSelectEndLine.
   mozilla::CaretAssociationHint mAttach;
 };
 
 struct nsPrevNextBidiLevels {
   void SetData(nsIFrame* aFrameBefore, nsIFrame* aFrameAfter,
-               mozilla::intl::Bidi::EmbeddingLevel aLevelBefore,
-               mozilla::intl::Bidi::EmbeddingLevel aLevelAfter) {
+               nsBidiLevel aLevelBefore, nsBidiLevel aLevelAfter) {
     mFrameBefore = aFrameBefore;
     mFrameAfter = aFrameAfter;
     mLevelBefore = aLevelBefore;
     mLevelAfter = aLevelAfter;
   }
   nsIFrame* mFrameBefore;
   nsIFrame* mFrameAfter;
-  mozilla::intl::Bidi::EmbeddingLevel mLevelBefore;
-  mozilla::intl::Bidi::EmbeddingLevel mLevelAfter;
+  nsBidiLevel mLevelBefore;
+  nsBidiLevel mLevelAfter;
 };
 
 namespace mozilla {
 class SelectionChangeEventDispatcher;
 namespace dom {
 class Selection;
 }  // namespace dom
 
@@ -472,23 +470,22 @@ class nsFrameSelection final {
   enum class SelectionIntoView { IfChanged, Yes };
   MOZ_CAN_RUN_SCRIPT nsresult PageMove(bool aForward, bool aExtend,
                                        nsIFrame* aFrame,
                                        SelectionIntoView aSelectionIntoView);
 
   void SetHint(CaretAssociateHint aHintRight) { mCaret.mHint = aHintRight; }
   CaretAssociateHint GetHint() const { return mCaret.mHint; }
 
-  void SetCaretBidiLevelAndMaybeSchedulePaint(
-      mozilla::intl::Bidi::EmbeddingLevel aLevel);
+  void SetCaretBidiLevelAndMaybeSchedulePaint(nsBidiLevel aLevel);
 
   /**
    * GetCaretBidiLevel gets the caret bidi level.
    */
-  mozilla::intl::Bidi::EmbeddingLevel GetCaretBidiLevel() const;
+  nsBidiLevel GetCaretBidiLevel() const;
 
   /**
    * UndefineCaretBidiLevel sets the caret bidi level to "undefined".
    */
   void UndefineCaretBidiLevel();
 
   /**
    * PhysicalMove will generally be called from the nsiselectioncontroller
@@ -693,17 +690,17 @@ class nsFrameSelection final {
    *
    * @param aPresContext is the context to use
    * @param aFrameIn is the frame to start from
    * @param aDirection is the direction to scan
    * @param aBidiLevel is the level to search for
    * @param aFrameOut will hold the frame returned
    */
   nsresult GetFrameFromLevel(nsIFrame* aFrameIn, nsDirection aDirection,
-                             mozilla::intl::Bidi::EmbeddingLevel aBidiLevel,
+                             nsBidiLevel aBidiLevel,
                              nsIFrame** aFrameOut) const;
 
   /**
    * MaintainSelection will track the normal selection as being "sticky".
    * Dragging or extending selection will never allow for a subset
    * (or the whole) of the maintained selection to become unselected.
    * Primary use: double click selecting then dragging on second click
    *
@@ -1038,26 +1035,25 @@ class nsFrameSelection final {
   int16_t mSelectionChangeReasons = nsISelectionListener::NO_REASON;
   // For visual display purposes.
   int16_t mDisplaySelection = nsISelectionController::SELECTION_OFF;
 
   struct Caret {
     // Hint to tell if the selection is at the end of this line or beginning of
     // next.
     CaretAssociateHint mHint = mozilla::CARET_ASSOCIATE_BEFORE;
-    mozilla::intl::Bidi::EmbeddingLevel mBidiLevel = BIDI_LEVEL_UNDEFINED;
+    nsBidiLevel mBidiLevel = BIDI_LEVEL_UNDEFINED;
 
     bool IsVisualMovement(bool aContinueSelection,
                           CaretMovementStyle aMovementStyle) const;
   };
 
   Caret mCaret;
 
-  mozilla::intl::Bidi::EmbeddingLevel mKbdBidiLevel =
-      mozilla::intl::Bidi::EmbeddingLevel::LTR();
+  nsBidiLevel mKbdBidiLevel = NSBIDI_LTR;
 
   class DesiredCaretPos {
    public:
     // the position requested by the Key Handling for up down
     nsresult FetchPos(nsPoint& aDesiredCaretPos,
                       const mozilla::PresShell& aPresShell,
                       mozilla::dom::Selection& aNormalSelection) const;
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -16,17 +16,16 @@
 #include "nsCOMPtr.h"
 #include "nsIContentViewer.h"
 #include "nsPresContext.h"
 #include "nsView.h"
 #include "nsViewportInfo.h"
 #include "nsContainerFrame.h"
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/dom/DocumentInlines.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "nsFontMetrics.h"
 #include "nsBoxLayoutState.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsScrollbarFrame.h"
 #include "nsINode.h"
 #include "nsIScrollbarMediator.h"
@@ -7113,18 +7112,17 @@ nsRect ScrollFrameHelper::GetScrolledRec
 }
 
 StyleDirection ScrollFrameHelper::GetScrolledFrameDir() const {
   // If the scrolled frame has unicode-bidi: plaintext, the paragraph
   // direction set by the text content overrides the direction of the frame
   if (mScrolledFrame->StyleTextReset()->mUnicodeBidi &
       NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
     if (nsIFrame* child = mScrolledFrame->PrincipalChildList().FirstChild()) {
-      return nsBidiPresUtils::ParagraphDirection(child) ==
-                     mozilla::intl::Bidi::Direction::LTR
+      return nsBidiPresUtils::ParagraphDirection(child) == NSBIDI_LTR
                  ? StyleDirection::Ltr
                  : StyleDirection::Rtl;
     }
   }
   return IsBidiLTR() ? StyleDirection::Ltr : StyleDirection::Rtl;
 }
 
 nsRect ScrollFrameHelper::GetUnsnappedScrolledRectInternal(
--- a/layout/generic/nsIFrame.cpp
+++ b/layout/generic/nsIFrame.cpp
@@ -20,17 +20,16 @@
 #include "mozilla/dom/DocumentInlines.h"
 #include "mozilla/dom/AncestorIterator.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/PathHelpers.h"
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StaticAnalysisFunctions.h"
 #include "mozilla/StaticPrefs_layout.h"
 #include "mozilla/StaticPrefs_print.h"
 #include "mozilla/SVGMaskFrame.h"
@@ -1693,18 +1692,17 @@ nsRect nsIFrame::GetPaddingRect() const 
 }
 
 WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
                                          nsIFrame* aSubFrame) const {
   MOZ_ASSERT(aSelfWM == GetWritingMode());
   WritingMode writingMode = aSelfWM;
 
   if (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
-    mozilla::intl::Bidi::EmbeddingLevel frameLevel =
-        nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
+    nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
     writingMode.SetDirectionFromBidiLevel(frameLevel);
   }
 
   return writingMode;
 }
 
 nsRect nsIFrame::GetMarginRect() const {
   return GetMarginRectRelativeToSelf() + GetPosition();
@@ -7920,19 +7918,18 @@ void nsIFrame::ListGeneric(nsACString& a
   bool hasNormalPosition;
   nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
   if (hasNormalPosition) {
     aTo += nsPrintfCString(" normal-position=%s",
                            ConvertToString(normalPosition, aFlags).c_str());
   }
   if (HasProperty(BidiDataProperty())) {
     FrameBidiData bidi = GetBidiData();
-    aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel.Value(),
-                           bidi.embeddingLevel.Value(),
-                           bidi.precedingControl.Value());
+    aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel,
+                           bidi.embeddingLevel, bidi.precedingControl);
   }
   if (IsTransformed()) {
     aTo += nsPrintfCString(" transformed");
   }
   if (ChildrenHavePerspective()) {
     aTo += nsPrintfCString(" perspective");
   }
   if (Extend3DContext()) {
@@ -8103,17 +8100,17 @@ nsresult nsIFrame::GetPointFromOffset(in
       // Find the direction of the frame from the EmbeddingLevelProperty,
       // which is the resolved bidi level set in
       // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
       // If the embedding level isn't set, just use the CSS direction
       // property.
       bool hasBidiData;
       FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
       bool isRTL = hasBidiData
-                       ? bidiData.embeddingLevel.IsRTL()
+                       ? IS_LEVEL_RTL(bidiData.embeddingLevel)
                        : StyleVisibility()->mDirection == StyleDirection::Rtl;
       if ((!isRTL && inOffset > newOffset) ||
           (isRTL && inOffset <= newOffset)) {
         pt = contentRect.TopRight();
       }
     }
   }
   *outPoint = pt;
@@ -9100,18 +9097,17 @@ Result<bool, nsresult> nsIFrame::IsVisua
   MOZ_TRY(aLineIterator->CheckLineOrder(aLine, &isReordered, &firstFrame,
                                         &lastFrame));
 
   nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
   if (!*framePtr) {
     return true;
   }
 
-  bool frameIsRTL = (nsBidiPresUtils::FrameDirection(*framePtr) ==
-                     mozilla::intl::Bidi::Direction::RTL);
+  bool frameIsRTL = (nsBidiPresUtils::FrameDirection(*framePtr) == NSBIDI_RTL);
   if ((frameIsRTL == lineIsRTL) == (aDirection == eDirPrevious)) {
     nsIFrame::GetFirstLeaf(framePtr);
   } else {
     nsIFrame::GetLastLeaf(framePtr);
   }
   return *framePtr == this;
 }
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -73,17 +73,16 @@
 #include "nsStyleStruct.h"
 #include "Visibility.h"
 #include "nsChangeHint.h"
 #include "mozilla/ComputedStyleInlines.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/CompositorHitTestInfo.h"
 #include "mozilla/gfx/MatrixFwd.h"
-#include "mozilla/intl/Bidi.h"
 #include "nsDisplayItemTypes.h"
 #include "nsPresContext.h"
 #include "nsTHashSet.h"
 
 #ifdef ACCESSIBILITY
 #  include "mozilla/a11y/AccTypes.h"
 #endif
 
@@ -355,16 +354,86 @@ class nsReflowStatus final {
 };
 
 #define NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics) \
   aStatus.UpdateTruncated(aReflowInput, aMetrics);
 
 // Convert nsReflowStatus to a human-readable string.
 std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus);
 
+/**
+ * nsBidiLevel is the type of the level values in our Unicode Bidi
+ * implementation.
+ * It holds an embedding level and indicates the visual direction
+ * by its bit 0 (even/odd value).<p>
+ *
+ * <li><code>aParaLevel</code> can be set to the
+ * pseudo-level values <code>NSBIDI_DEFAULT_LTR</code>
+ * and <code>NSBIDI_DEFAULT_RTL</code>.</li></ul>
+ *
+ * @see nsBidi::SetPara
+ *
+ * <p>The related constants are not real, valid level values.
+ * <code>NSBIDI_DEFAULT_XXX</code> can be used to specify
+ * a default for the paragraph level for
+ * when the <code>SetPara</code> function
+ * shall determine it but there is no
+ * strongly typed character in the input.<p>
+ *
+ * Note that the value for <code>NSBIDI_DEFAULT_LTR</code> is even
+ * and the one for <code>NSBIDI_DEFAULT_RTL</code> is odd,
+ * just like with normal LTR and RTL level values -
+ * these special values are designed that way. Also, the implementation
+ * assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd.
+ *
+ * @see NSBIDI_DEFAULT_LTR
+ * @see NSBIDI_DEFAULT_RTL
+ * @see NSBIDI_LEVEL_OVERRIDE
+ * @see NSBIDI_MAX_EXPLICIT_LEVEL
+ */
+typedef uint8_t nsBidiLevel;
+
+/**
+ * Paragraph level setting.
+ * If there is no strong character, then set the paragraph level to 0
+ * (left-to-right).
+ */
+#define NSBIDI_DEFAULT_LTR 0xfe
+
+/**
+ * Paragraph level setting.
+ * If there is no strong character, then set the paragraph level to 1
+ * (right-to-left).
+ */
+#define NSBIDI_DEFAULT_RTL 0xff
+
+/**
+ * Maximum explicit embedding level.
+ * (The maximum resolved level can be up to
+ * <code>NSBIDI_MAX_EXPLICIT_LEVEL+1</code>).
+ */
+#define NSBIDI_MAX_EXPLICIT_LEVEL 125
+
+/** Bit flag for level input.
+ *  Overrides directional properties.
+ */
+#define NSBIDI_LEVEL_OVERRIDE 0x80
+
+/**
+ * <code>nsBidiDirection</code> values indicate the text direction.
+ */
+enum nsBidiDirection {
+  /** All left-to-right text This is a 0 value. */
+  NSBIDI_LTR,
+  /** All right-to-left text This is a 1 value. */
+  NSBIDI_RTL,
+  /** Mixed-directional text. */
+  NSBIDI_MIXED
+};
+
 namespace mozilla {
 
 // https://drafts.csswg.org/css-align-3/#baseline-sharing-group
 enum class BaselineSharingGroup {
   // NOTE Used as an array index so must be 0 and 1.
   First = 0,
   Last = 1,
 };
@@ -399,26 +468,25 @@ struct IntrinsicSize {
 
   bool operator==(const IntrinsicSize& rhs) const {
     return width == rhs.width && height == rhs.height;
   }
   bool operator!=(const IntrinsicSize& rhs) const { return !(*this == rhs); }
 };
 
 // Pseudo bidi embedding level indicating nonexistence.
-static const mozilla::intl::Bidi::EmbeddingLevel kBidiLevelNone =
-    mozilla::intl::Bidi::EmbeddingLevel(0xff);
+static const nsBidiLevel kBidiLevelNone = 0xff;
 
 struct FrameBidiData {
-  mozilla::intl::Bidi::EmbeddingLevel baseLevel;
-  mozilla::intl::Bidi::EmbeddingLevel embeddingLevel;
+  nsBidiLevel baseLevel;
+  nsBidiLevel embeddingLevel;
   // The embedding level of virtual bidi formatting character before
   // this frame if any. kBidiLevelNone is used to indicate nonexistence
   // or unnecessity of such virtual character.
-  mozilla::intl::Bidi::EmbeddingLevel precedingControl;
+  nsBidiLevel precedingControl;
 };
 
 }  // namespace mozilla
 
 /// Generic destructor for frame properties. Calls delete.
 template <typename T>
 static void DeleteValue(T* aPropertyValue) {
   delete aPropertyValue;
@@ -1325,23 +1393,19 @@ class nsIFrame : public nsQueryFrame {
     bool exists;
     mozilla::FrameBidiData bidiData = GetProperty(BidiDataProperty(), &exists);
     if (!exists) {
       bidiData.precedingControl = mozilla::kBidiLevelNone;
     }
     return bidiData;
   }
 
-  mozilla::intl::Bidi::EmbeddingLevel GetBaseLevel() const {
-    return GetBidiData().baseLevel;
-  }
-
-  mozilla::intl::Bidi::EmbeddingLevel GetEmbeddingLevel() const {
-    return GetBidiData().embeddingLevel;
-  }
+  nsBidiLevel GetBaseLevel() const { return GetBidiData().baseLevel; }
+
+  nsBidiLevel GetEmbeddingLevel() const { return GetBidiData().embeddingLevel; }
 
   /**
    * Return the distance between the border edge of the frame and the
    * margin edge of the frame.  Like GetRect(), returns the dimensions
    * as of the most recent reflow.
    *
    * This doesn't include any margin collapsing that may have occurred.
    * It also doesn't consider GetSkipSides()/GetLogicalSkipSides(), so
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -7,17 +7,16 @@
 /* rendering object for replaced elements with image data */
 
 #include "nsImageFrame.h"
 
 #include "TextDrawTarget.h"
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "gfxUtils.h"
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
@@ -1430,41 +1429,41 @@ void nsImageFrame::DisplayAltText(nsPres
     uint32_t maxFit;  // number of characters that fit
     nscoord strWidth =
         MeasureString(str, strLen, iSize, maxFit, aRenderingContext, *fm);
 
     // Display the text
     nsresult rv = NS_ERROR_FAILURE;
 
     if (aPresContext->BidiEnabled()) {
-      mozilla::intl::Bidi::EmbeddingLevel level;
+      nsBidiDirection dir;
       nscoord x, y;
 
       if (isVertical) {
         x = pt.x + maxDescent;
         if (wm.IsBidiLTR()) {
           y = aRect.y;
-          level = mozilla::intl::Bidi::EmbeddingLevel::LTR();
+          dir = NSBIDI_LTR;
         } else {
           y = aRect.YMost() - strWidth;
-          level = mozilla::intl::Bidi::EmbeddingLevel::RTL();
+          dir = NSBIDI_RTL;
         }
       } else {
         y = pt.y + maxAscent;
         if (wm.IsBidiLTR()) {
           x = aRect.x;
-          level = mozilla::intl::Bidi::EmbeddingLevel::LTR();
+          dir = NSBIDI_LTR;
         } else {
           x = aRect.XMost() - strWidth;
-          level = mozilla::intl::Bidi::EmbeddingLevel::RTL();
+          dir = NSBIDI_RTL;
         }
       }
 
       rv = nsBidiPresUtils::RenderText(
-          str, maxFit, level, aPresContext, aRenderingContext,
+          str, maxFit, dir, aPresContext, aRenderingContext,
           aRenderingContext.GetDrawTarget(), *fm, x, y);
     }
     if (NS_FAILED(rv)) {
       nsLayoutUtils::DrawUniDirString(str, maxFit,
                                       isVertical
                                           ? nsPoint(pt.x + maxDescent, pt.y)
                                           : nsPoint(pt.x, pt.y + maxAscent),
                                       *fm, aRenderingContext);
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -64,17 +64,16 @@
 #include "nsTextFragment.h"
 #include "nsGkAtoms.h"
 #include "nsFrameSelection.h"
 #include "nsRange.h"
 #include "nsCSSRendering.h"
 #include "nsContentUtils.h"
 #include "nsLineBreaker.h"
 #include "nsIFrameInlines.h"
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/intl/WordBreaker.h"
 #include "mozilla/ServoStyleSet.h"
 
 #include <algorithm>
 #include <limits>
 #include <type_traits>
 #ifdef ACCESSIBILITY
 #  include "nsAccessibilityService.h"
@@ -1931,17 +1930,17 @@ bool BuildTextRunsScanner::ContinueTextR
     }
 
     // Map inline-end and inline-start to physical sides for checking presence
     // of non-zero margin/border/padding.
     Side side1 = wm.PhysicalSide(eLogicalSideIEnd);
     Side side2 = wm.PhysicalSide(eLogicalSideIStart);
     // If the frames have an embedding level that is opposite to the writing
     // mode, we need to swap which sides we're checking.
-    if (aFrame1->GetEmbeddingLevel().IsRTL() == wm.IsBidiLTR()) {
+    if (IS_LEVEL_RTL(aFrame1->GetEmbeddingLevel()) == wm.IsBidiLTR()) {
       std::swap(side1, side2);
     }
 
     if (PreventCrossBoundaryShaping(aFrame1, ancestor, side1) ||
         PreventCrossBoundaryShaping(aFrame2, ancestor, side2)) {
       return false;
     }
   }
@@ -2392,17 +2391,17 @@ already_AddRefed<gfxTextRun> BuildTextRu
   }
 
   if (flags2 & nsTextFrameUtils::Flags::HasTab) {
     flags |= gfx::ShapedTextFlags::TEXT_ENABLE_SPACING;
   }
   if (flags2 & nsTextFrameUtils::Flags::HasShy) {
     flags |= gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS;
   }
-  if (mBidiEnabled && (firstFrame->GetEmbeddingLevel().IsRTL())) {
+  if (mBidiEnabled && (IS_LEVEL_RTL(firstFrame->GetEmbeddingLevel()))) {
     flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
   }
   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
     flags2 |= nsTextFrameUtils::Flags::TrailingWhitespace;
   }
   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
     flags |= gfx::ShapedTextFlags::TEXT_TRAILING_ARABICCHAR;
   }
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -3,17 +3,16 @@
 /* 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 "nsTextBoxFrame.h"
 
 #include "gfx2DGlue.h"
 #include "gfxUtils.h"
-#include "mozilla/intl/Bidi.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/gfx/2D.h"
 #include "nsFontMetrics.h"
 #include "nsReadableUtils.h"
@@ -480,18 +479,17 @@ void nsTextBoxFrame::DrawText(gfxContext
       aOverrideColor ? *aOverrideColor : StyleText()->mColor.ToColor());
   ColorPattern colorPattern(color);
   aRenderingContext.SetDeviceColor(color);
 
   nsresult rv = NS_ERROR_FAILURE;
 
   if (mState & NS_FRAME_IS_BIDI) {
     presContext->SetBidiEnabled();
-    mozilla::intl::Bidi::EmbeddingLevel level =
-        nsBidiPresUtils::BidiLevelFromStyle(Style());
+    nsBidiLevel level = nsBidiPresUtils::BidiLevelFromStyle(Style());
     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
       // We let the RenderText function calculate the mnemonic's
       // underline position for us.
       nsBidiPositionResolve posResolve;
       posResolve.logicalIndex = mAccessKeyInfo->mAccesskeyIndex;
       rv = nsBidiPresUtils::RenderText(
           mCroppedTitle.get(), mCroppedTitle.Length(), level, presContext,
           aRenderingContext, refDrawTarget, *fontMet, baselinePt.x,