Changeset 101731 in webkit


Ignore:
Timestamp:
Dec 1, 2011 6:15:48 PM (12 years ago)
Author:
commit-queue@webkit.org
Message:

Asynchronous SpellChecker should consider multiple requests.
https://bugs.webkit.org/show_bug.cgi?id=72939

Patch by Shinya Kawanaka <shinyak@google.com> on 2011-12-01
Reviewed by Hajime Morita.

Source/WebCore:

Now SpellChecker saves a request when it is processing the previous spellcheck request.
If there is a request having the same root editable element, the older request is replaced by newer request.

Test: editing/spelling/spellcheck-queue.html

  • editing/SpellChecker.cpp:

(WebCore::SpellChecker::SpellCheckRequest::SpellCheckRequest):

A structure to have spell check request.

(WebCore::SpellChecker::SpellCheckRequest::sequence):
(WebCore::SpellChecker::SpellCheckRequest::range):
(WebCore::SpellChecker::SpellCheckRequest::text):
(WebCore::SpellChecker::SpellCheckRequest::mask):
(WebCore::SpellChecker::SpellCheckRequest::rootEditableElement):
(WebCore::SpellChecker::SpellChecker):
(WebCore::SpellChecker::createRequest):
(WebCore::SpellChecker::timerFiredToProcessQueuedRequest):

When timer is fired, queued request is processed if any.

(WebCore::SpellChecker::canCheckAsynchronously):
(WebCore::SpellChecker::requestCheckingFor):

When the spellchecker is processing another request, the latest request is queued.

(WebCore::SpellChecker::invokeRequest):
(WebCore::SpellChecker::enqueueRequest):

Enqueues a request. If there is an older request whose root editable element is the same as the request,
it will be replaced.

(WebCore::SpellChecker::didCheck):

  • editing/SpellChecker.h:

LayoutTests:

Tests for multiple spellcheck requests.

  • editing/spelling/spellcheck-queue-expected.txt: Added.
  • editing/spelling/spellcheck-queue.html: Added.
  • platform/gtk/Skipped:
  • platform/qt/Skipped:
Location:
trunk
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r101730 r101731  
     12011-12-01  Shinya Kawanaka  <shinyak@google.com>
     2
     3        Asynchronous SpellChecker should consider multiple requests.
     4        https://bugs.webkit.org/show_bug.cgi?id=72939
     5
     6        Reviewed by Hajime Morita.
     7
     8        Tests for multiple spellcheck requests.
     9
     10        * editing/spelling/spellcheck-queue-expected.txt: Added.
     11        * editing/spelling/spellcheck-queue.html: Added.
     12        * platform/gtk/Skipped:
     13        * platform/qt/Skipped:
     14
    1152011-12-01  Takashi Toyoshima  <toyoshim@chromium.org>
    216
  • trunk/LayoutTests/platform/gtk/Skipped

    r101682 r101731  
    12171217editing/spelling/spelling-backspace-between-lines.html
    12181218editing/spelling/spellcheck-paste.html
     1219editing/spelling/spellcheck-queue.html
    12191220
    12201221# For https://bugs.webkit.org/show_bug.cgi?id=50758
  • trunk/LayoutTests/platform/qt/Skipped

    r101682 r101731  
    10021002# EditorClient::requestCheckingOfString() is not implemented
    10031003editing/spelling/spellcheck-paste.html
     1004editing/spelling/spellcheck-queue.html
    10041005
    10051006# [Qt][GTK] editing/spelling/spellcheck-async.html fails
  • trunk/Source/WebCore/ChangeLog

    r101730 r101731  
     12011-12-01  Shinya Kawanaka  <shinyak@google.com>
     2
     3        Asynchronous SpellChecker should consider multiple requests.
     4        https://bugs.webkit.org/show_bug.cgi?id=72939
     5
     6        Reviewed by Hajime Morita.
     7
     8        Now SpellChecker saves a request when it is processing the previous spellcheck request.
     9        If there is a request having the same root editable element, the older request is replaced by newer request.
     10
     11        Test: editing/spelling/spellcheck-queue.html
     12
     13        * editing/SpellChecker.cpp:
     14        (WebCore::SpellChecker::SpellCheckRequest::SpellCheckRequest):
     15          A structure to have spell check request.
     16        (WebCore::SpellChecker::SpellCheckRequest::sequence):
     17        (WebCore::SpellChecker::SpellCheckRequest::range):
     18        (WebCore::SpellChecker::SpellCheckRequest::text):
     19        (WebCore::SpellChecker::SpellCheckRequest::mask):
     20        (WebCore::SpellChecker::SpellCheckRequest::rootEditableElement):
     21        (WebCore::SpellChecker::SpellChecker):
     22        (WebCore::SpellChecker::createRequest):
     23        (WebCore::SpellChecker::timerFiredToProcessQueuedRequest):
     24          When timer is fired, queued request is processed if any.
     25        (WebCore::SpellChecker::canCheckAsynchronously):
     26        (WebCore::SpellChecker::requestCheckingFor):
     27          When the spellchecker is processing another request, the latest request is queued.
     28        (WebCore::SpellChecker::invokeRequest):
     29        (WebCore::SpellChecker::enqueueRequest):
     30          Enqueues a request. If there is an older request whose root editable element is the same as the request,
     31          it will be replaced.
     32        (WebCore::SpellChecker::didCheck):
     33        * editing/SpellChecker.h:
     34
    1352011-12-01  Takashi Toyoshima  <toyoshim@chromium.org>
    236
  • trunk/Source/WebCore/editing/SpellChecker.cpp

    r100890 r101731  
    4646namespace WebCore {
    4747
     48class SpellChecker::SpellCheckRequest : public RefCounted<SpellChecker::SpellCheckRequest> {
     49public:
     50    SpellCheckRequest(int sequence, PassRefPtr<Range> range, const String& text, TextCheckingTypeMask mask)
     51        : m_sequence(sequence)
     52        , m_range(range)
     53        , m_text(text)
     54        , m_mask(mask)
     55        , m_rootEditableElement(m_range->startContainer()->rootEditableElement())
     56    {
     57    }
     58
     59    int sequence() const { return m_sequence; }
     60    Range* range() const { return m_range.get(); }
     61    const String& text() const { return m_text; }
     62    TextCheckingTypeMask mask() const { return m_mask; }
     63    Element* rootEditableElement() const { return m_rootEditableElement; }
     64
     65private:
     66    int m_sequence;
     67    RefPtr<Range> m_range;
     68    String m_text;
     69    TextCheckingTypeMask m_mask;
     70    Element* m_rootEditableElement;
     71};
     72
    4873SpellChecker::SpellChecker(Frame* frame)
    4974    : m_frame(frame)
    50     , m_requestSequence(0)
     75    , m_lastRequestedSequence(0)
     76    , m_timerToProcessQueuedRequest(this, &SpellChecker::timerFiredToProcessQueuedRequest)
    5177{
    5278}
     
    6490}
    6591
    66 bool SpellChecker::initRequest(PassRefPtr<Range> range)
     92PassRefPtr<SpellChecker::SpellCheckRequest> SpellChecker::createRequest(TextCheckingTypeMask mask, PassRefPtr<Range> range)
    6793{
    6894    ASSERT(canCheckAsynchronously(range.get()));
     
    7096    String text = range->text();
    7197    if (!text.length())
    72         return false;
    73 
    74     m_requestRange = range;
    75     m_requestText = text;
    76     m_requestSequence++;
    77 
    78     return true;
    79 }
    80 
    81 void SpellChecker::clearRequest()
    82 {
    83     m_requestRange.clear();
    84     m_requestText = String();
     98        return PassRefPtr<SpellCheckRequest>();
     99
     100    return adoptRef(new SpellCheckRequest(++m_lastRequestedSequence, range, text, mask));
     101}
     102
     103void SpellChecker::timerFiredToProcessQueuedRequest(Timer<SpellChecker>*)
     104{
     105    ASSERT(!m_requestQueue.isEmpty());
     106    if (m_requestQueue.isEmpty())
     107        return;
     108
     109    invokeRequest(m_requestQueue.takeFirst());
    85110}
    86111
     
    92117bool SpellChecker::canCheckAsynchronously(Range* range) const
    93118{
    94     return client() && isCheckable(range) && isAsynchronousEnabled() && !isBusy();
    95 }
    96 
    97 bool SpellChecker::isBusy() const
    98 {
    99     return m_requestRange.get();
    100 }
    101 
    102 bool SpellChecker::isValid(int sequence) const
    103 {
    104     return m_requestRange.get() && m_requestText.length() && m_requestSequence == sequence;
     119    return client() && isCheckable(range) && isAsynchronousEnabled();
    105120}
    106121
     
    115130        return;
    116131
    117     doRequestCheckingFor(mask, range);
    118 }
    119 
    120 void SpellChecker::doRequestCheckingFor(TextCheckingTypeMask mask, PassRefPtr<Range> range)
    121 {
    122     ASSERT(canCheckAsynchronously(range.get()));
    123 
    124     if (!initRequest(range))
    125         return;
    126     client()->requestCheckingOfString(this, m_requestSequence, mask, m_requestText);
     132    RefPtr<SpellCheckRequest> request(createRequest(mask, range));
     133    if (!request)
     134        return;
     135
     136    if (m_timerToProcessQueuedRequest.isActive() || m_processingRequest) {
     137        enqueueRequest(request.release());
     138        return;
     139    }
     140
     141    invokeRequest(request.release());
     142}
     143
     144void SpellChecker::invokeRequest(PassRefPtr<SpellCheckRequest> request)
     145{
     146    ASSERT(!m_processingRequest);
     147
     148    client()->requestCheckingOfString(this, request->sequence(), request->mask(), request->text());
     149    m_processingRequest = request;
     150}
     151
     152void SpellChecker::enqueueRequest(PassRefPtr<SpellCheckRequest> request)
     153{
     154    ASSERT(request);
     155
     156    for (RequestQueue::iterator it = m_requestQueue.begin(); it != m_requestQueue.end(); ++it) {
     157        if (request->rootEditableElement() != (*it)->rootEditableElement())
     158            continue;
     159
     160        *it = request;
     161        return;
     162    }
     163
     164    m_requestQueue.append(request);
    127165}
    128166
     
    160198void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
    161199{
    162     if (!isValid(sequence))
    163         return;
    164 
    165     if (!isCheckable(m_requestRange.get())) {
    166         clearRequest();
     200    ASSERT(m_processingRequest);
     201
     202    ASSERT(m_processingRequest->sequence() == sequence);
     203    if (m_processingRequest->sequence() != sequence) {
     204        m_requestQueue.clear();
    167205        return;
    168206    }
    169207
    170208    int startOffset = 0;
    171     PositionIterator start = m_requestRange->startPosition();
     209    PositionIterator start = m_processingRequest->range()->startPosition();
    172210    for (size_t i = 0; i < results.size(); ++i) {
    173211        if (results[i].type != TextCheckingTypeSpelling && results[i].type != TextCheckingTypeGrammar)
     
    187225        // JavaScript applications, retrieve the words in the specified region and compare them with
    188226        // the original ones.
    189         RefPtr<Range> range = Range::create(m_requestRange->ownerDocument(), start, end);
     227        RefPtr<Range> range = Range::create(m_processingRequest->range()->ownerDocument(), start, end);
    190228        // FIXME: Use textContent() compatible string conversion.
    191229        String destination = range->text();
    192         String source = m_requestText.substring(results[i].location, results[i].length);
     230        String source = m_processingRequest->text().substring(results[i].location, results[i].length);
    193231        if (destination == source)
    194             m_requestRange->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
     232            m_processingRequest->range()->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
    195233
    196234        startOffset = results[i].location;
    197235    }
    198236
    199     clearRequest();
     237    m_processingRequest.clear();
     238    if (!m_requestQueue.isEmpty())
     239        m_timerToProcessQueuedRequest.startOneShot(0);
    200240}
    201241
  • trunk/Source/WebCore/editing/SpellChecker.h

    r100890 r101731  
    2929#include "PlatformString.h"
    3030#include "TextChecking.h"
     31#include "Timer.h"
     32#include <wtf/Deque.h>
    3133#include <wtf/RefPtr.h>
    3234#include <wtf/Noncopyable.h>
     
    4850
    4951    bool isAsynchronousEnabled() const;
    50     bool canCheckAsynchronously(Range*) const;
    51     bool isBusy() const;
    52     bool isValid(int sequence) const;
    5352    bool isCheckable(Range*) const;
    5453    void requestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
     
    5655
    5756private:
    58     bool initRequest(PassRefPtr<Range>);
    59     void clearRequest();
    60     void doRequestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
     57    class SpellCheckRequest;
     58    typedef Deque<RefPtr<SpellCheckRequest> > RequestQueue;
     59
     60    bool canCheckAsynchronously(Range*) const;
     61    PassRefPtr<SpellCheckRequest> createRequest(TextCheckingTypeMask, PassRefPtr<Range>);
    6162    TextCheckerClient* client() const;
     63    void timerFiredToProcessQueuedRequest(Timer<SpellChecker>*);
     64    void invokeRequest(PassRefPtr<SpellCheckRequest>);
     65    void enqueueRequest(PassRefPtr<SpellCheckRequest>);
    6266
    6367    Frame* m_frame;
     68    int m_lastRequestedSequence;
    6469
    65     RefPtr<Range> m_requestRange;
    66     String m_requestText;
    67     int m_requestSequence;
     70    Timer<SpellChecker> m_timerToProcessQueuedRequest;
     71
     72    RefPtr<SpellCheckRequest> m_processingRequest;
     73    RequestQueue m_requestQueue;
    6874};
    6975
Note: See TracChangeset for help on using the changeset viewer.