Adding Inline Search to FeedDemon (After Fixing IHTMLTxtRange Select Bug)

One of the features I’ve been planning to add to the upcoming FeedDemon 2.6 is an inline search toolbar similar to the one in Firefox, and I’m pleased to announce that I’ve finally coded it.  But it was harder that I anticipated, primarily due to a weird IE bug (more about that later). 

In the current version of FeedDemon, hitting Ctrl+F in the browser displays IE’s "Find" dialog, shown below:

While this does the job, it’s not nearly as useful as Firefox’s inline search, which enables highlighting every instance of a keyword on the current page.  So I decided to intercept Ctrl+F and display my own inline search toolbar (click for full screenshot):

 

After a few hours of coding, I had it working great – or so I thought.  Then I did an inline search for the word "the" on CNN’s web site, and FeedDemon came crashing down.  An inline search on Techmeme had the same result.  Boom!


Note: The rest of this post is intended for frustrated developers Googling for help on the problem I ran into.


I traced the crash to a call to the Select method of MSHTML’s IHTMLTxtRange interface.  Now, you might think I’d be happy to figure out where the bug was, but I wasn’t – I’m never happy to discover that a bug resides in code I didn’t write, since that means hours of trial-and-error (literally) trying to resolve it.

After numerous false starts, I finally discovered that the crash occurred when IHTMLTxtRange.FindText locates a match in text that isn’t visible (inside a hidden DIV, for example).  Using the Select method on a visible text range works fine, but using it on a range that’s hidden results in the less-than-helpful exception "could not complete the operation due to error 800a025e."

Luckily, once I realized the problem, I was able to work around it with some hackish code.  The first step was to catch the exception, and then figure out the next visible element after the hidden text range so I could perform a “FindText” on it (in other words, skip over the hidden match).  In Delphi code, it looks something like this:

// ...code prior to this calls IHTMLTxtRange.FindText to locate the keyword...

// SafeSelectRange wraps IHTMLTxtRange.Select and returns False when it raises an exception


if SafeSelectRange(txtr) then

   Result := True

else

begin

   // get the parent element of this text range

   if (txtr.parentElement = nil) then

      exit;

   // find the element after this one

   nNextSrcIdx := txtr.parentElement.sourceIndex + 1;

   if nNextSrcIdx > iDoc2.all.length then

      exit;

   elNext := iDoc2.all.item(nNextSrcIdx, null) as IHTMLElement;

   if elNext = nil then

      exit;

   // move the text range to this element

   txtr.moveToElementText(elNext);

   // retry the 'FindText' call again (but don't retry forever)

   inc(nNumRetries);

   if nNumRetries < MAX_RETRIES then

      bRetry := True;

end;

I’ve stripped a lot of the logic from this code to make it more presentable, but hopefully this is enough to help other developers running into the same hair-pulling bug.

6 thoughts on “Adding Inline Search to FeedDemon (After Fixing IHTMLTxtRange Select Bug)

  1. hehe I’ve had all kinds of pain working with IHTMLTxtRange, and in VB6 too which makes this sort of problem really annoying to track down.
    Fortunately I never had to deal with hidden divs (it was an editor, they were displayed) so I didn’t hit this particular bug ;)

  2. You can see the actual code IE uses to call this function in res://shdoclc/find.dlg

  3. Wow rich, I didn’t know about that (although it doesn’t work in IE7). The entire find dialog uses publically documented APIs – that’s cool!
    And it’s even amusing, I’ve had to work with the text range enough to know what stuff like selection.type == “None” or “Text” and even “Control” means. Kinda surprising how much of that code I understand after moving on from the VB development world 2 or 3 years ago.

  4. I just tried the new inline search today. Awesome feature! I don’t know how I lived without it for so long and I’ve only used it once so far! Thanks for putting the effort into this.

Comments are closed.