editor/AsyncSpellCheckTestHelper.jsm
author Michael Comella <michael.l.comella@gmail.com>
Thu, 21 Sep 2017 15:40:55 -0700
changeset 431826 b18dcb98aa98f90d8cbabf8f54db9804f3bca2f2
parent 401430 56bf52d4ddeb1dc7019c4f9e23961670d0a31414
child 455106 b38d59f71915f78922b46a7c7bc65a48488c45f1
permissions -rw-r--r--
Bug 1399683 - Change padding of content view rather than self in onSizeChanged. r=sebastian, a=sledru To be honest, I'm not sure why this works. onSizeChanged seems to be a callback to notify the user that your layout has completed and not a place to update layout params. However, it makes *slightly* more sense that you could update your children's layout from this view, which is what this patch changes the code to do. I think I could figure out why this works if I dug further into [1] but I have other things to focus on. I don't think this is the most correct solution, but it is likely the smallest and least risky change we can make to fix this issue, which is ideal because we'd like to uplift this to the 57 Beta. A more correct solution would override the Activity Stream RecyclerView and provide different desired measurements to its parent so that the new size is set *during* layout rather than after it, which would make the layout process more efficient. However, this type of code is less commonly written day-to-day by developers so it's harder to write, harder to maintain, and I'd have to read a lot of [1] in order to write it. :) [1]: https://developer.android.com/guide/topics/ui/how-android-draws.html MozReview-Commit-ID: AceUvDYGWCR

/* 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/. */

this.EXPORTED_SYMBOLS = [
  "onSpellCheck",
];

const SPELL_CHECK_ENDED_TOPIC = "inlineSpellChecker-spellCheck-ended";
const SPELL_CHECK_STARTED_TOPIC = "inlineSpellChecker-spellCheck-started";

const { classes: Cc, interfaces: Ci, utils: Cu } = Components;

/**
 * Waits until spell checking has stopped on the given element.
 *
 * When a spell check is pending, this waits indefinitely until the spell check
 * ends.  When a spell check is not pending, it waits a small number of turns of
 * the event loop: if a spell check begins, it resumes waiting indefinitely for
 * the end, and otherwise it stops waiting and calls the callback.
 *
 * This this can therefore trap spell checks that have not started at the time
 * of calling, spell checks that have already started, multiple consecutive
 * spell checks, and the absence of spell checks altogether.
 *
 * @param editableElement  The element being spell checked.
 * @param callback         Called when spell check has completed or enough turns
 *                         of the event loop have passed to determine it has not
 *                         started.
 */
function onSpellCheck(editableElement, callback) {
  let editor = editableElement.editor;
  if (!editor) {
    let win = editableElement.ownerGlobal;
    editor = win.QueryInterface(Ci.nsIInterfaceRequestor).
                 getInterface(Ci.nsIWebNavigation).
                 QueryInterface(Ci.nsIInterfaceRequestor).
                 getInterface(Ci.nsIEditingSession).
                 getEditorForWindow(win);
  }
  if (!editor)
    throw new Error("Unable to find editor for element " + editableElement);

  try {
    // False is important here.  Pass false so that the inline spell checker
    // isn't created if it doesn't already exist.
    var isc = editor.getInlineSpellChecker(false);
  }
  catch (err) {
    // getInlineSpellChecker throws if spell checking is not enabled instead of
    // just returning null, which seems kind of lame.  (Spell checking is not
    // enabled on Android.)  The point here is only to determine whether spell
    // check is pending, and if getInlineSpellChecker throws, then it's not
    // pending.
  }
  let waitingForEnded = isc && isc.spellCheckPending;
  let count = 0;

  function observe(subj, topic, data) {
    if (subj != editor)
      return;
    count = 0;
    let expectedTopic = waitingForEnded ? SPELL_CHECK_ENDED_TOPIC :
                        SPELL_CHECK_STARTED_TOPIC;
    if (topic != expectedTopic)
      Cu.reportError("Expected " + expectedTopic + " but got " + topic + "!");
    waitingForEnded = !waitingForEnded;
  }

  let os = Cc["@mozilla.org/observer-service;1"].
           getService(Ci.nsIObserverService);
  os.addObserver(observe, SPELL_CHECK_STARTED_TOPIC);
  os.addObserver(observe, SPELL_CHECK_ENDED_TOPIC);

  let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  timer.init(function tick() {
    // Wait an arbitrarily large number -- 50 -- turns of the event loop before
    // declaring that no spell checks will start.
    if (waitingForEnded || ++count < 50)
      return;
    timer.cancel();
    os.removeObserver(observe, SPELL_CHECK_STARTED_TOPIC);
    os.removeObserver(observe, SPELL_CHECK_ENDED_TOPIC);
    callback();
  }, 0, Ci.nsITimer.TYPE_REPEATING_SLACK);
};