Repository: qutebrowser/qutebrowser

Issue:
Sometimes `:search-next` says the search string wasn't found on the page when it should.
qutebrowser v0.10.1
Git commit: v0.10.0-227-gc8fe9ccdf (2017-03-12 19:18:26 +0000)
Backend: QtWebEngine (Chromium 53.0.2785.148)

CPython: 3.6.0
Qt: 5.8.0
PyQt: 5.8


To reproduce, just search some text on the page, then hold down `n` (assuming `:search-next` is bound to it, which it is by default) and occasionally a message will popup saying it couldn't find that text on the page.


Discussion:
I tried reproducing this with QupZilla and the QtWebEngine demobrowser, but can't reproduce this there.
I've narrowed down the issue: if you `search-next` on a long page that takes time to search and has the text you search for, then if you `search-next` again, but before the previous `search-next` has found the next found text, then it will show this warning.

Sorry if this is confusingly written.

I found this by searching 'googlevideo' on https://easylist.to/easylist/easylist.txt

My guess is that commands to `search-next` need to be ignored if a previous `search-next` command hasn't finished.
Oh, right - looks like Qt does that intentionally:

```c++
quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitively, bool findBackward)
{
    Q_D(WebContentsAdapter);
    if (d->lastFindRequestId > d->webContentsDelegate->lastReceivedFindReply()) {
        // There are cases where the render process will overwrite a previous request
        // with the new search and we'll have a dangling callback, leaving the application
        // waiting for it forever.
        // Assume that any unfinished find has been unsuccessful when a new one is started
        // to cover that case.
        d->adapterClient->didFindText(d->lastFindRequestId, 0);
    }
```
I would be up for taking a look at this. Would the desired solution be ignoring `search-next` or `-prev` commands until the last search is completed? I can implement and do some testing on really large txt files.
From IRC:

```
21:07 <coolp_jim> The-Compiler: thanks for the ping on the incremental_search PR, I don't mind looking into the :search-{next,prev} issue either, I went ahead and commented on that thread.
21:07 <coolp_jim> errr issue.
21:08 <coolp_jim> Is delaying search next/prev calls until the current search is complete an OK solution?
21:13 <The-Compiler> coolp_jim: hmm, I wonder how well that'd work with incremental search
21:14 <The-Compiler> I wonder if we could do something like ignoring the false callback when we know there's still an old search in progress and a new one was started
21:15 <coolp_jim> That would keep things more responsive for sure.
21:16 <The-Compiler> i.e. keep track of running (not called back yet) searches, if we start a new search, set some ignore_next_callback variable, and if we get a callback with that set, ignore it
21:18 <The-Compiler> and hope there are no race conditions that way :-/
21:24 <coolp_jim> OK cool! I'll be able to do a bit of work on it here soon. I took a quick look into the failed tests for the inc_search PR, still familiarizing with the test suite (never used `tox` before, it's neat).
```

Resolving patch:
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -126,13 +126,19 @@ class WebEngineSearch(browsertab.AbstractSearch):
     def __init__(self, parent=None):
         super().__init__(parent)
         self._flags = QWebEnginePage.FindFlags(0)
+        self.num_of_searches = 0
 
     def _find(self, text, flags, callback, caller):
         """Call findText on the widget."""
         self.search_displayed = True
+        self.num_of_searches += 1
 
         def wrapped_callback(found):
             """Wrap the callback to do debug logging."""
+            self.num_of_searches -= 1
+            if self.num_of_searches > 0:
+                return
+
             found_text = 'found' if found else "didn't find"
             if flags:
                 flag_text = 'with flags {}'.format(debug.qflags_key(


Test patch: