Merge mozilla-central to autoland. a=merge CLOSED TREE
authorshindli <shindli@mozilla.com>
Thu, 17 Jan 2019 18:17:19 +0200
changeset 454287 17af08445298eac6597da43c2610d06f4b6a3dc0
parent 454286 accc4d418bd11369da1dca52b3ed0ae6d7dea31d (current diff)
parent 454275 1bbec154fb73b0fa3756a9ed417815c87fc82b4e (diff)
child 454288 b179bb22821b9311b1824e725abdc945ab3f57f4
push id35392
push userncsoregi@mozilla.com
push dateThu, 17 Jan 2019 21:53:28 +0000
treeherdermozilla-central@24982570fc83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -132,17 +132,17 @@
           <description id="identity-popup-permission-reload-hint">&identity.permissionsReloadHint;</description>
           <description id="identity-popup-permission-empty-hint">&identity.permissionsEmpty;</description>
         </vbox>
       </hbox>
 
       <!-- Clear Site Data Button -->
       <vbox hidden="true"
             id="identity-popup-clear-sitedata-footer"
-            class="panel-footer identity-popup-footer">
+            class="panel-footer">
         <button id="identity-popup-clear-sitedata-button"
                 label="&identity.clearSiteData;"
                 oncommand="gIdentityHandler.clearSiteData(event); gIdentityHandler.recordClick('clear_sitedata');"/>
       </vbox>
     </panelview>
 
     <!-- Security SubView -->
     <panelview id="identity-popup-securityView"
@@ -221,17 +221,17 @@
                 oncommand="gIdentityHandler.disableMixedContentProtection()"/>
         <button when-mixedcontent="active-loaded"
                 label="&identity.enableMixedContentBlocking.label;"
                 accesskey="&identity.enableMixedContentBlocking.accesskey;"
                 class="panel-button"
                 oncommand="gIdentityHandler.enableMixedContentProtection()"/>
       </vbox>
 
-      <vbox id="identity-popup-more-info-footer" class="panel-footer identity-popup-footer">
+      <vbox id="identity-popup-more-info-footer" class="panel-footer">
         <!-- More Security Information -->
         <button id="identity-popup-more-info"
                 label="&identity.moreInfoLinkText2;"
                 oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
       </vbox>
 
     </panelview>
 
@@ -241,32 +241,32 @@
                title="&contentBlocking.trackersView.label;"
                descriptionheightworkaround="true">
         <vbox id="identity-popup-trackersView-list" class="identity-popup-content-blocking-list">
         </vbox>
         <hbox id="identity-popup-trackersView-strict-info">
           <image/>
           <label>&contentBlocking.trackersView.strictInfo.label;</label>
         </hbox>
-        <vbox class="panel-footer identity-popup-footer">
+        <vbox class="panel-footer">
           <button id="identity-popup-trackersView-settings-button"
                   label="&contentBlocking.manageSettings.label;"
                   accesskey="&contentBlocking.manageSettings.accesskey;"
                   oncommand="ContentBlocking.openPreferences();"/>
         </vbox>
     </panelview>
 
     <!-- Cookies SubView -->
     <panelview id="identity-popup-cookiesView"
                role="document"
                title="&contentBlocking.cookiesView.label;"
                descriptionheightworkaround="true">
         <vbox id="identity-popup-cookiesView-list" class="identity-popup-content-blocking-list">
         </vbox>
-        <vbox class="panel-footer identity-popup-footer">
+        <vbox class="panel-footer">
           <button id="identity-popup-cookiesView-settings-button"
                   label="&contentBlocking.manageSettings.label;"
                   accesskey="&contentBlocking.manageSettings.accesskey;"
                   oncommand="ContentBlocking.openPreferences();"/>
         </vbox>
     </panelview>
 
     <!-- Report Breakage SubView -->
@@ -284,17 +284,17 @@
             <label id="identity-popup-breakageReportView-collection-url"/>
           </vbox>
           <vbox class="identity-popup-breakageReportView-collection-section">
             <label class="identity-popup-breakageReportView-collection-label">&contentBlocking.breakageReportView.collection.comments.label;</label>
             <textbox multiline="true" id="identity-popup-breakageReportView-collection-comments"/>
           </vbox>
         </vbox>
         <vbox id="identity-popup-breakageReportView-footer"
-              class="panel-footer identity-popup-footer">
+              class="panel-footer">
           <button id="identity-popup-breakageReportView-cancel"
                   label="&contentBlocking.breakageReportView.cancel.label;"
                   oncommand="ContentBlocking.backToMainView();"/>
           <button id="identity-popup-breakageReportView-submit"
                   default="true"
                   label="&contentBlocking.breakageReportView.sendReport.label;"
                   oncommand="ContentBlocking.submitBreakageReport();"/>
         </vbox>
--- a/editor/spellchecker/EditorSpellCheck.cpp
+++ b/editor/spellchecker/EditorSpellCheck.cpp
@@ -436,16 +436,26 @@ EditorSpellCheck::CheckCurrentWord(const
 NS_IMETHODIMP
 EditorSpellCheck::CheckCurrentWordNoSuggest(const nsAString& aSuggestedWord,
                                             bool* aIsMisspelled) {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
   return mSpellChecker->CheckWord(aSuggestedWord, aIsMisspelled, nullptr);
 }
 
+RefPtr<CheckWordPromise> EditorSpellCheck::CheckCurrentWordsNoSuggest(
+    const nsTArray<nsString>& aSuggestedWords) {
+  if (NS_WARN_IF(!mSpellChecker)) {
+    return CheckWordPromise::CreateAndReject(NS_ERROR_NOT_INITIALIZED,
+                                             __func__);
+  }
+
+  return mSpellChecker->CheckWords(aSuggestedWords);
+}
+
 NS_IMETHODIMP
 EditorSpellCheck::ReplaceWord(const nsAString& aMisspelledWord,
                               const nsAString& aReplaceWord,
                               bool aAllOccurrences) {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
   return mSpellChecker->Replace(aMisspelledWord, aReplaceWord, aAllOccurrences);
 }
--- a/editor/spellchecker/EditorSpellCheck.h
+++ b/editor/spellchecker/EditorSpellCheck.h
@@ -1,17 +1,18 @@
 /* -*- 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_EditorSpellCheck_h
 #define mozilla_EditorSpellCheck_h
 
-#include "nsCOMPtr.h"  // for nsCOMPtr
+#include "mozilla/mozSpellChecker.h"  // for mozilla::CheckWordPromise
+#include "nsCOMPtr.h"                 // for nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsIEditorSpellCheck.h"  // for NS_DECL_NSIEDITORSPELLCHECK, etc
 #include "nsISupportsImpl.h"
 #include "nsString.h"  // for nsString
 #include "nsTArray.h"  // for nsTArray
 #include "nscore.h"    // for nsresult
 
 class mozSpellChecker;
@@ -37,16 +38,29 @@ class EditorSpellCheck final : public ns
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(EditorSpellCheck)
 
   /* Declare all methods in the nsIEditorSpellCheck interface */
   NS_DECL_NSIEDITORSPELLCHECK
 
   mozSpellChecker* GetSpellChecker();
 
+  /**
+   * Like CheckCurrentWord, checks the word you give it, returning true via
+   * promise if it's misspelled.
+   * This is faster than CheckCurrentWord because it does not compute
+   * any suggestions.
+   *
+   * Watch out: this does not clear any suggestions left over from previous
+   * calls to CheckCurrentWord, so there may be suggestions, but they will be
+   * invalid.
+   */
+  RefPtr<mozilla::CheckWordPromise> CheckCurrentWordsNoSuggest(
+      const nsTArray<nsString>& aSuggestedWords);
+
  protected:
   virtual ~EditorSpellCheck();
 
   RefPtr<mozSpellChecker> mSpellChecker;
   RefPtr<EditorBase> mEditor;
 
   nsTArray<nsString> mSuggestedWordList;
 
--- a/extensions/spellcheck/hunspell/glue/PRemoteSpellcheckEngine.ipdl
+++ b/extensions/spellcheck/hunspell/glue/PRemoteSpellcheckEngine.ipdl
@@ -8,16 +8,17 @@ namespace mozilla {
 
 sync protocol PRemoteSpellcheckEngine {
   manager PContent;
 
 parent:
   async __delete__();
 
   sync Check(nsString aWord) returns (bool aIsMisspelled);
+  async CheckAsync(nsString[] aWord) returns (bool[] aIsMisspelled);
 
   sync CheckAndSuggest(nsString aWord) returns (bool aIsMisspelled, nsString[] aSuggestions);
 
   sync SetDictionary(nsString aDictionary) returns (bool success);
 
   /*
    * Set current dictionary from list of dictionary name.
    *
--- a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.cpp
+++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.cpp
@@ -40,16 +40,33 @@ mozilla::ipc::IPCResult RemoteSpellcheck
     const nsString& aWord, bool* aIsMisspelled) {
   nsresult rv = mSpellChecker->CheckWord(aWord, aIsMisspelled, nullptr);
 
   // If CheckWord failed, we can't tell whether the word is correctly spelled.
   if (NS_FAILED(rv)) *aIsMisspelled = false;
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult RemoteSpellcheckEngineParent::RecvCheckAsync(
+    nsTArray<nsString>&& aWords, CheckAsyncResolver&& aResolve) {
+  nsTArray<bool> misspells;
+  misspells.SetCapacity(aWords.Length());
+  for (auto& word : aWords) {
+    bool misspelled;
+    nsresult rv = mSpellChecker->CheckWord(word, &misspelled, nullptr);
+    // If CheckWord failed, we can't tell whether the word is correctly spelled
+    if (NS_FAILED(rv)) {
+      misspelled = false;
+    }
+    misspells.AppendElement(misspelled);
+  }
+  aResolve(std::move(misspells));
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult RemoteSpellcheckEngineParent::RecvCheckAndSuggest(
     const nsString& aWord, bool* aIsMisspelled,
     InfallibleTArray<nsString>* aSuggestions) {
   nsresult rv = mSpellChecker->CheckWord(aWord, aIsMisspelled, aSuggestions);
   if (NS_FAILED(rv)) {
     aSuggestions->Clear();
     *aIsMisspelled = false;
   }
--- a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.h
+++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.h
@@ -25,16 +25,19 @@ class RemoteSpellcheckEngineParent : pub
 
   virtual mozilla::ipc::IPCResult RecvSetDictionaryFromList(
       nsTArray<nsString>&& aList,
       SetDictionaryFromListResolver&& aResolve) override;
 
   virtual mozilla::ipc::IPCResult RecvCheck(const nsString& aWord,
                                             bool* aIsMisspelled) override;
 
+  virtual mozilla::ipc::IPCResult RecvCheckAsync(
+      nsTArray<nsString>&& aWord, CheckAsyncResolver&& aResolve) override;
+
   virtual mozilla::ipc::IPCResult RecvCheckAndSuggest(
       const nsString& aWord, bool* aIsMisspelled,
       InfallibleTArray<nsString>* aSuggestions) override;
 
  private:
   RefPtr<mozSpellChecker> mSpellChecker;
 };
 
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -63,41 +63,45 @@
 #include "nsRange.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsITextControlElement.h"
 #include "prtime.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
+using namespace mozilla::ipc;
 
 // Set to spew messages to the console about what is happening.
 //#define DEBUG_INLINESPELL
 
 // the number of milliseconds that we will take at once to do spellchecking
 #define INLINESPELL_CHECK_TIMEOUT 1
 
 // The number of words to check before we look at the time to see if
 // INLINESPELL_CHECK_TIMEOUT ms have elapsed. This prevents us from getting
 // stuck and not moving forward because the INLINESPELL_CHECK_TIMEOUT might
 // be too short to a low-end machine.
 #define INLINESPELL_MINIMUM_WORDS_BEFORE_TIMEOUT 5
 
+// The maximum number of words to check word via IPC.
+#define INLINESPELL_MAXIMUM_CHUNKED_WORDS_PER_TASK 25
+
 // These notifications are broadcast when spell check starts and ends.  STARTED
 // must always be followed by ENDED.
 #define INLINESPELL_STARTED_TOPIC "inlineSpellChecker-spellCheck-started"
 #define INLINESPELL_ENDED_TOPIC "inlineSpellChecker-spellCheck-ended"
 
 static const char kMaxSpellCheckSelectionSize[] =
     "extensions.spellcheck.inline.max-misspellings";
 static const PRTime kMaxSpellCheckTimeInUsec =
     INLINESPELL_CHECK_TIMEOUT * PR_USEC_PER_MSEC;
 
 mozInlineSpellStatus::mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker)
-    : mSpellChecker(aSpellChecker), mWordCount(0) {}
+    : mSpellChecker(aSpellChecker) {}
 
 // mozInlineSpellStatus::InitForEditorChange
 //
 //    This is the most complicated case. For changes, we need to compute the
 //    range of stuff that changed based on the old and new caret positions,
 //    as well as use a range possibly provided by the editor (start and end,
 //    which are usually nullptr) to get a range with the union of these.
 
@@ -515,17 +519,16 @@ mozInlineSpellChecker::mozInlineSpellChe
       mDisabledAsyncToken(0),
       mNeedsCheckAfterNavigation(false),
       mFullSpellCheckScheduled(false),
       mIsListeningToEditSubActions(false) {
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs)
     prefs->GetIntPref(kMaxSpellCheckSelectionSize,
                       &mMaxNumWordsInSpellSelection);
-  mMaxMisspellingsPerCheck = mMaxNumWordsInSpellSelection * 3 / 4;
 }
 
 mozInlineSpellChecker::~mozInlineSpellChecker() {}
 
 EditorSpellCheck* mozInlineSpellChecker::GetEditorSpellCheck() {
   return mSpellCheck ? mSpellCheck : mPendingSpellCheck;
 }
 
@@ -1190,18 +1193,16 @@ nsresult mozInlineSpellChecker::DoSpellC
     // were cleared above. We also need to clear the word count so that we
     // check all words instead of stopping early.
     status->mRange = ranges[idx];
     rv = DoSpellCheck(aWordUtil, aSpellCheckSelection, status, &doneChecking);
     NS_ENSURE_SUCCESS(rv, rv);
     MOZ_ASSERT(
         doneChecking,
         "We gave the spellchecker one word, but it didn't finish checking?!?!");
-
-    status->mWordCount = 0;
   }
 
   return NS_OK;
 }
 
 // mozInlineSpellChecker::DoSpellCheck
 //
 //    This function checks words intersecting the given range, excluding those
@@ -1233,22 +1234,28 @@ nsresult mozInlineSpellChecker::DoSpellC
 //    but we ran out of time, this will be false and the range will be
 //    updated with the stuff that still needs checking.
 
 nsresult mozInlineSpellChecker::DoSpellCheck(
     mozInlineSpellWordUtil& aWordUtil, Selection* aSpellCheckSelection,
     const UniquePtr<mozInlineSpellStatus>& aStatus, bool* aDoneChecking) {
   *aDoneChecking = true;
 
-  NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
+  if (NS_WARN_IF(!mSpellCheck)) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (SpellCheckSelectionIsFull()) {
+    return NS_OK;
+  }
 
   // get the editor for ShouldSpellCheckNode, this may fail in reasonable
   // circumstances since the editor could have gone away
   RefPtr<TextEditor> textEditor = mTextEditor;
-  if (!textEditor) {
+  if (!textEditor || textEditor->Destroyed()) {
     return NS_ERROR_FAILURE;
   }
 
   if (aStatus->mRange->Collapsed()) return NS_OK;
 
   // see if the selection has any ranges, if not, then we can optimize checking
   // range inclusion later (we have no ranges when we are initially checking or
   // when there are no misspelled words yet).
@@ -1286,35 +1293,44 @@ nsresult mozInlineSpellChecker::DoSpellC
   // aWordUtil.SetPosition flushes pending notifications, check editor again.
   if (!mTextEditor) {
     return NS_ERROR_FAILURE;
   }
 
   int32_t wordsChecked = 0;
   PRTime beginTime = PR_Now();
 
+  nsTArray<nsString> words;
+  nsTArray<NodeOffsetRange> checkRanges;
   nsAutoString wordText;
   NodeOffsetRange wordNodeOffsetRange;
   bool dontCheckWord;
+  // XXX Spellchecker API isn't async on chrome process.
+  static const size_t requestChunkSize =
+      XRE_IsContentProcess() ? INLINESPELL_MAXIMUM_CHUNKED_WORDS_PER_TASK : 1;
   while (NS_SUCCEEDED(aWordUtil.GetNextWord(wordText, &wordNodeOffsetRange,
                                             &dontCheckWord)) &&
          !wordNodeOffsetRange.Empty()) {
     // get the range for the current word.
-    nsINode* beginNode = wordNodeOffsetRange.Begin().mNode;
-    nsINode* endNode = wordNodeOffsetRange.End().mNode;
-    int32_t beginOffset = wordNodeOffsetRange.Begin().mOffset;
-    int32_t endOffset = wordNodeOffsetRange.End().mOffset;
+    nsINode* beginNode = wordNodeOffsetRange.Begin().Node();
+    nsINode* endNode = wordNodeOffsetRange.End().Node();
+    int32_t beginOffset = wordNodeOffsetRange.Begin().Offset();
+    int32_t endOffset = wordNodeOffsetRange.End().Offset();
 
     // see if we've done enough words in this round and run out of time.
     if (wordsChecked >= INLINESPELL_MINIMUM_WORDS_BEFORE_TIMEOUT &&
         PR_Now() > PRTime(beginTime + kMaxSpellCheckTimeInUsec)) {
 // stop checking, our time limit has been exceeded.
 #ifdef DEBUG_INLINESPELL
-      printf("We have run out of the time, schedule next round.");
+      printf("We have run out of the time, schedule next round.\n");
 #endif
+      CheckCurrentWordsNoSuggest(aSpellCheckSelection, words, checkRanges);
+      words.Clear();
+      checkRanges.Clear();
+
       // move the range to encompass the stuff that needs checking.
       nsresult rv = aStatus->mRange->SetStart(beginNode, beginOffset);
       if (NS_FAILED(rv)) {
         // The range might be unhappy because the beginning is after the
         // end. This is possible when the requested end was in the middle
         // of a word, just ignore this situation and assume we're done.
         return NS_OK;
       }
@@ -1361,59 +1377,111 @@ nsresult mozInlineSpellChecker::DoSpellC
     // exclusion range. Because the exclusion range is a multiple of a word,
     // this is sufficient.
     if (aStatus->mNoCheckRange &&
         aStatus->mNoCheckRange->IsPointInRange(*beginNode, beginOffset, erv)) {
       continue;
     }
 
     // check spelling and add to selection if misspelled
-    bool isMisspelled;
     mozInlineSpellWordUtil::NormalizeWord(wordText);
-    nsresult rv =
-        mSpellCheck->CheckCurrentWordNoSuggest(wordText, &isMisspelled);
-    if (NS_FAILED(rv)) continue;
-
+    words.AppendElement(wordText);
+    checkRanges.AppendElement(wordNodeOffsetRange);
     wordsChecked++;
-    if (isMisspelled) {
-      // misspelled words count extra toward the max
-      RefPtr<nsRange> wordRange;
-      // If we somehow can't make a range for this word, just ignore it.
-      if (NS_SUCCEEDED(aWordUtil.MakeRange(wordNodeOffsetRange.Begin(),
-                                           wordNodeOffsetRange.End(),
-                                           getter_AddRefs(wordRange)))) {
-        AddRange(aSpellCheckSelection, wordRange);
-        aStatus->mWordCount++;
-        if (aStatus->mWordCount >= mMaxMisspellingsPerCheck ||
-            SpellCheckSelectionIsFull()) {
-          break;
-        }
-      }
+    if (words.Length() >= requestChunkSize) {
+      CheckCurrentWordsNoSuggest(aSpellCheckSelection, words, checkRanges);
+      words.Clear();
+      checkRanges.Clear();
     }
   }
 
+  CheckCurrentWordsNoSuggest(aSpellCheckSelection, words, checkRanges);
+
   return NS_OK;
 }
 
 // An RAII helper that calls ChangeNumPendingSpellChecks on destruction.
-class AutoChangeNumPendingSpellChecks {
+class MOZ_RAII AutoChangeNumPendingSpellChecks final {
  public:
-  AutoChangeNumPendingSpellChecks(mozInlineSpellChecker* aSpellChecker,
-                                  int32_t aDelta)
-      : mSpellChecker(aSpellChecker), mDelta(aDelta) {}
+  explicit AutoChangeNumPendingSpellChecks(mozInlineSpellChecker* aSpellChecker,
+                                           int32_t aDelta
+                                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mSpellChecker(aSpellChecker), mDelta(aDelta) {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  }
 
   ~AutoChangeNumPendingSpellChecks() {
     mSpellChecker->ChangeNumPendingSpellChecks(mDelta);
   }
 
  private:
   RefPtr<mozInlineSpellChecker> mSpellChecker;
   int32_t mDelta;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+void mozInlineSpellChecker::CheckCurrentWordsNoSuggest(
+    Selection* aSpellCheckSelection, const nsTArray<nsString>& aWords,
+    const nsTArray<NodeOffsetRange>& aRanges) {
+  if (aWords.IsEmpty()) {
+    return;
+  }
+
+  ChangeNumPendingSpellChecks(1);
+
+  RefPtr<mozInlineSpellChecker> self = this;
+  RefPtr<Selection> spellCheckerSelection = aSpellCheckSelection;
+  uint32_t token = mDisabledAsyncToken;
+  mSpellCheck->CheckCurrentWordsNoSuggest(aWords)->Then(
+      GetMainThreadSerialEventTarget(), __func__,
+      [self, spellCheckerSelection, aRanges,
+       token](const nsTArray<bool>& aIsMisspelled) {
+        if (token != self->mDisabledAsyncToken) {
+          // This result is never used
+          return;
+        }
+
+        if (!self->mTextEditor || self->mTextEditor->Destroyed()) {
+          return;
+        }
+
+        AutoChangeNumPendingSpellChecks pendingChecks(self, -1);
+
+        if (self->SpellCheckSelectionIsFull()) {
+          return;
+        }
+
+        for (size_t i = 0; i < aIsMisspelled.Length(); i++) {
+          if (!aIsMisspelled[i]) {
+            continue;
+          }
+
+          RefPtr<nsRange> wordRange =
+              mozInlineSpellWordUtil::MakeRange(aRanges[i]);
+          // If we somehow can't make a range for this word, just ignore
+          // it.
+          if (wordRange) {
+            self->AddRange(spellCheckerSelection, wordRange);
+          }
+        }
+      },
+      [self, token](nsresult aRv) {
+        if (!self->mTextEditor || self->mTextEditor->Destroyed()) {
+          return;
+        }
+
+        if (token != self->mDisabledAsyncToken) {
+          // This result is never used
+          return;
+        }
+
+        self->ChangeNumPendingSpellChecks(-1);
+      });
+}
+
 // mozInlineSpellChecker::ResumeCheck
 //
 //    Called by the resume event when it fires. We will try to pick up where
 //    the last resume left off.
 
 nsresult mozInlineSpellChecker::ResumeCheck(
     UniquePtr<mozInlineSpellStatus>&& aStatus) {
   // Observers should be notified that spell check has ended only after spell
--- a/extensions/spellcheck/src/mozInlineSpellChecker.h
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.h
@@ -5,21 +5,21 @@
 
 #ifndef mozilla_mozInlineSpellChecker_h
 #define mozilla_mozInlineSpellChecker_h
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMEventListener.h"
 #include "nsIEditorSpellCheck.h"
 #include "nsIInlineSpellChecker.h"
+#include "mozInlineSpellWordUtil.h"
 #include "nsRange.h"
 #include "nsWeakReference.h"
 
 class InitEditorSpellCheckCallback;
-class mozInlineSpellWordUtil;
 class mozInlineSpellChecker;
 class mozInlineSpellResume;
 class UpdateCurrentDictionaryCallback;
 
 namespace mozilla {
 class EditorSpellCheck;
 class TextEditor;
 enum class EditSubAction : int32_t;
@@ -47,21 +47,16 @@ class mozInlineSpellStatus {
 
   nsresult FinishInitOnEvent(mozInlineSpellWordUtil& aWordUtil);
 
   // Return true if we plan to spell-check everything
   bool IsFullSpellCheck() const { return mOp == eOpChange && !mRange; }
 
   RefPtr<mozInlineSpellChecker> mSpellChecker;
 
-  // The total number of words checked in this sequence, using this tally tells
-  // us when to stop. This count is preserved as we continue checking in new
-  // messages.
-  int32_t mWordCount;
-
   // what happened?
   enum Operation {
     eOpChange,        // for SpellCheckAfterEditorChange except
                       // deleteSelection
     eOpChangeDelete,  // for SpellCheckAfterEditorChange with
                       // deleteSelection
     eOpNavigation,    // for HandleNavigationEvent
     eOpSelection,     // re-check all misspelled words
@@ -134,23 +129,16 @@ class mozInlineSpellChecker final : publ
 
   RefPtr<mozilla::TextEditor> mTextEditor;
   RefPtr<mozilla::EditorSpellCheck> mSpellCheck;
   RefPtr<mozilla::EditorSpellCheck> mPendingSpellCheck;
 
   int32_t mNumWordsInSpellSelection;
   int32_t mMaxNumWordsInSpellSelection;
 
-  // How many misspellings we can add at once. This is often less than the max
-  // total number of misspellings. When you have a large textarea prepopulated
-  // with text with many misspellings, we can hit this limit. By making it
-  // lower than the total number of misspelled words, new text typed by the
-  // user can also have spellchecking in it.
-  int32_t mMaxMisspellingsPerCheck;
-
   // we need to keep track of the current text position in the document
   // so we can spell check the old word when the user clicks around the
   // document.
   nsCOMPtr<nsINode> mCurrentSelectionAnchorNode;
   uint32_t mCurrentSelectionOffset;
 
   // Tracks the number of pending spell checks *and* async operations that may
   // lead to spell checks, like updating the current dictionary.  This is
@@ -277,11 +265,15 @@ class mozInlineSpellChecker final : publ
   // track the number of pending spell checks and async operations that may lead
   // to spell checks, notifying observers accordingly
   void ChangeNumPendingSpellChecks(int32_t aDelta,
                                    mozilla::TextEditor* aTextEditor = nullptr);
   void NotifyObservers(const char* aTopic, mozilla::TextEditor* aTextEditor);
 
   void StartToListenToEditSubActions() { mIsListeningToEditSubActions = true; }
   void EndListeningToEditSubActions() { mIsListeningToEditSubActions = false; }
+
+  void CheckCurrentWordsNoSuggest(mozilla::dom::Selection* aSpellCheckSelection,
+                                  const nsTArray<nsString>& aWords,
+                                  const nsTArray<NodeOffsetRange>& aRanges);
 };
 
 #endif  // #ifndef mozilla_mozInlineSpellChecker_h
--- a/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
@@ -325,16 +325,29 @@ nsresult mozInlineSpellWordUtil::MakeRan
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   range.forget(aRange);
 
   return NS_OK;
 }
 
+// static
+already_AddRefed<nsRange> mozInlineSpellWordUtil::MakeRange(
+    const NodeOffsetRange& aRange) {
+  RefPtr<nsRange> range = new nsRange(aRange.Begin().Node());
+  nsresult rv =
+      range->SetStartAndEnd(aRange.Begin().Node(), aRange.Begin().Offset(),
+                            aRange.End().Node(), aRange.End().Offset());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+  return range.forget();
+}
+
 /*********** Word Splitting ************/
 
 // classifies a given character in the DOM word
 enum CharClass {
   CHAR_CLASS_WORD,
   CHAR_CLASS_SEPARATOR,
   CHAR_CLASS_END_OF_INPUT
 };
--- a/extensions/spellcheck/src/mozInlineSpellWordUtil.h
+++ b/extensions/spellcheck/src/mozInlineSpellWordUtil.h
@@ -17,46 +17,49 @@
 class nsRange;
 class nsINode;
 
 namespace mozilla {
 class TextEditor;
 }  // namespace mozilla
 
 struct NodeOffset {
-  nsINode* mNode;
+  nsCOMPtr<nsINode> mNode;
   int32_t mOffset;
 
-  NodeOffset() : mNode(nullptr), mOffset(0) {}
+  NodeOffset() : mOffset(0) {}
   NodeOffset(nsINode* aNode, int32_t aOffset)
       : mNode(aNode), mOffset(aOffset) {}
 
   bool operator==(const NodeOffset& aOther) const {
     return mNode == aOther.mNode && mOffset == aOther.mOffset;
   }
 
   bool operator!=(const NodeOffset& aOther) const { return !(*this == aOther); }
+
+  nsINode* Node() const { return mNode.get(); }
+  int32_t Offset() const { return mOffset; }
 };
 
 class NodeOffsetRange {
  private:
   NodeOffset mBegin;
   NodeOffset mEnd;
   bool mEmpty;
 
  public:
   NodeOffsetRange() : mEmpty(true) {}
   NodeOffsetRange(NodeOffset b, NodeOffset e)
       : mBegin(b), mEnd(e), mEmpty(false) {}
 
-  NodeOffset Begin() { return mBegin; }
+  NodeOffset Begin() const { return mBegin; }
 
-  NodeOffset End() { return mEnd; }
+  NodeOffset End() const { return mEnd; }
 
-  bool Empty() { return mEmpty; }
+  bool Empty() const { return mEmpty; }
 };
 
 /**
  *    This class extracts text from the DOM and builds it into a single string.
  *    The string includes whitespace breaks whereever non-inline elements begin
  *    and end. This string is broken into "real words", following somewhat
  *    complex rules; for example substrings that look like URLs or
  *    email addresses are treated as single words, but otherwise many kinds of
@@ -99,16 +102,17 @@ class MOZ_STACK_CLASS mozInlineSpellWord
   // THIS CHANGES THE CURRENT POSITION AND RANGE. It is designed to be called
   // before you actually generate the range you are interested in and iterate
   // the words in it.
   nsresult GetRangeForWord(nsINode* aWordNode, int32_t aWordOffset,
                            nsRange** aRange);
 
   // Convenience functions, object must be initialized
   nsresult MakeRange(NodeOffset aBegin, NodeOffset aEnd, nsRange** aRange);
+  static already_AddRefed<nsRange> MakeRange(const NodeOffsetRange& aRange);
 
   // Moves to the the next word in the range, and retrieves it's text and range.
   // An empty word and a nullptr range are returned when we are done checking.
   // aSkipChecking will be set if the word is "special" and shouldn't be
   // checked (e.g., an email address).
   nsresult GetNextWord(nsAString& aText, NodeOffsetRange* aNodeOffsetRange,
                        bool* aSkipChecking);
 
--- a/extensions/spellcheck/src/mozSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozSpellChecker.cpp
@@ -111,16 +111,44 @@ nsresult mozSpellChecker::NextMisspelled
       selOffset = end;
     } while (end != -1);
     mTextServicesDocument->NextBlock();
     selOffset = 0;
   }
   return NS_OK;
 }
 
+RefPtr<CheckWordPromise> mozSpellChecker::CheckWords(
+    const nsTArray<nsString> &aWords) {
+  if (XRE_IsContentProcess()) {
+    return mEngine->SendCheckAsync(aWords)->Then(
+        GetMainThreadSerialEventTarget(), __func__,
+        [](nsTArray<bool> &&aIsMisspelled) {
+          return CheckWordPromise::CreateAndResolve(std::move(aIsMisspelled),
+                                                    __func__);
+        },
+        [](mozilla::ipc::ResponseRejectReason &&aReason) {
+          return CheckWordPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
+                                                   __func__);
+        });
+  }
+
+  nsTArray<bool> misspells;
+  misspells.SetCapacity(aWords.Length());
+  for (auto &word : aWords) {
+    bool misspelled;
+    nsresult rv = CheckWord(word, &misspelled, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return CheckWordPromise::CreateAndReject(rv, __func__);
+    }
+    misspells.AppendElement(misspelled);
+  }
+  return CheckWordPromise::CreateAndResolve(std::move(misspells), __func__);
+}
+
 nsresult mozSpellChecker::CheckWord(const nsAString &aWord, bool *aIsMisspelled,
                                     nsTArray<nsString> *aSuggestions) {
   nsresult result;
   bool correct;
 
   if (XRE_IsContentProcess()) {
     nsString wordwrapped = nsString(aWord);
     bool rv;
--- a/extensions/spellcheck/src/mozSpellChecker.h
+++ b/extensions/spellcheck/src/mozSpellChecker.h
@@ -1,30 +1,32 @@
 /* -*- 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 mozSpellChecker_h__
 #define mozSpellChecker_h__
 
+#include "mozilla/MozPromise.h"
 #include "nsCOMPtr.h"
 #include "nsCOMArray.h"
 #include "nsString.h"
 #include "mozIPersonalDictionary.h"
 #include "mozISpellCheckingEngine.h"
 #include "nsClassHashtable.h"
 #include "nsTArray.h"
 #include "nsCycleCollectionParticipant.h"
 
 class mozEnglishWordUtils;
 
 namespace mozilla {
 class RemoteSpellcheckEngineChild;
 class TextServicesDocument;
+typedef MozPromise<nsTArray<bool>, nsresult, false> CheckWordPromise;
 }  // namespace mozilla
 
 class mozSpellChecker final {
  public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(mozSpellChecker)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(mozSpellChecker)
 
   static already_AddRefed<mozSpellChecker> Create() {
@@ -59,16 +61,23 @@ class mozSpellChecker final {
    * @param aSuggestions is an array of nsStrings which represent the
    * suggested replacements for the misspelled word. The array will be empty
    * if there aren't any suggestions.
    */
   nsresult CheckWord(const nsAString& aWord, bool* aIsMisspelled,
                      nsTArray<nsString>* aSuggestions);
 
   /**
+   * This is a flavor of CheckWord, is async version of CheckWord.
+   * @Param aWords is array of words to check
+   */
+  RefPtr<mozilla::CheckWordPromise> CheckWords(
+      const nsTArray<nsString>& aWords);
+
+  /**
    * Replaces the old word with the specified new word.
    * @param aOldWord is the word to be replaced.
    * @param aNewWord is the word that is to replace old word.
    * @param aAllOccurrences will replace all occurrences of old
    * word, in the document, with new word when it is true. If
    * false, it will replace the 1st occurrence only!
    */
   nsresult Replace(const nsAString& aOldWord, const nsAString& aNewWord,
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -106,17 +106,17 @@ class FeatureHolder final {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FeatureHolder);
 
   // In order to avoid multiple lookup for the same table, we have a special
   // array for tables and their results. The Features are stored in a separate
   // array together with the references to their tables.
 
   class TableData {
    public:
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TableData);
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FeatureHolder::TableData);
 
     explicit TableData(const nsACString& aTable) : mTable(aTable) {}
 
     nsCString mTable;
     LookupResultArray mResults;
 
    private:
     ~TableData() = default;