summaryrefslogtreecommitdiff
path: root/Source/WebKit/blackberry/WebKitSupport/InPageSearchManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/blackberry/WebKitSupport/InPageSearchManager.cpp')
-rw-r--r--Source/WebKit/blackberry/WebKitSupport/InPageSearchManager.cpp227
1 files changed, 195 insertions, 32 deletions
diff --git a/Source/WebKit/blackberry/WebKitSupport/InPageSearchManager.cpp b/Source/WebKit/blackberry/WebKitSupport/InPageSearchManager.cpp
index 09cfd55bc..c4322de57 100644
--- a/Source/WebKit/blackberry/WebKitSupport/InPageSearchManager.cpp
+++ b/Source/WebKit/blackberry/WebKitSupport/InPageSearchManager.cpp
@@ -24,30 +24,69 @@
#include "DocumentMarkerController.h"
#include "Editor.h"
#include "Frame.h"
+#include "Node.h"
#include "Page.h"
+#include "Range.h"
+#include "TextIterator.h"
+#include "Timer.h"
#include "WebPage_p.h"
-static const int TextMatchMarkerLimit = 100;
+static const double MaxScopingDuration = 0.1;
using namespace WebCore;
namespace BlackBerry {
namespace WebKit {
+class InPageSearchManager::DeferredScopeStringMatches {
+public:
+ DeferredScopeStringMatches(InPageSearchManager* ipsm, Frame* scopingFrame, const String& text, bool reset)
+ : m_searchManager(ipsm)
+ , m_scopingFrame(scopingFrame)
+ , m_timer(this, &DeferredScopeStringMatches::doTimeout)
+ , m_searchText(text)
+ , m_reset(reset)
+ {
+ m_timer.startOneShot(0.0);
+ }
+
+private:
+ void doTimeout(Timer<DeferredScopeStringMatches>*)
+ {
+ m_searchManager->callScopeStringMatches(this, m_scopingFrame, m_searchText, m_reset);
+ }
+ InPageSearchManager* m_searchManager;
+ Frame* m_scopingFrame;
+ Timer<DeferredScopeStringMatches> m_timer;
+ String m_searchText;
+ bool m_reset;
+};
+
InPageSearchManager::InPageSearchManager(WebPagePrivate* page)
: m_webPage(page)
, m_activeMatch(0)
+ , m_resumeScopingFromRange(0)
+ , m_activeMatchCount(0)
+ , m_scopingComplete(false)
+ , m_scopingCaseInsensitive(false)
+ , m_locatingActiveMatch(false)
+ , m_highlightAllMatches(false)
+ , m_activeMatchIndex(0)
{
}
InPageSearchManager::~InPageSearchManager()
{
+ cancelPendingScopingEffort();
}
-bool InPageSearchManager::findNextString(const String& text, bool forward)
+bool InPageSearchManager::findNextString(const String& text, FindOptions findOptions, bool wrap, bool highlightAllMatches)
{
+ m_highlightAllMatches = highlightAllMatches;
+
if (!text.length()) {
clearTextMatches();
+ cancelPendingScopingEffort();
m_activeSearchString = String();
return false;
}
@@ -61,17 +100,16 @@ bool InPageSearchManager::findNextString(const String& text, bool forward)
if (m_activeMatch && !m_activeMatch->boundaryPointsValid())
m_activeMatch = 0;
- RefPtr<Range> searchStartingPoint(m_activeMatch);
- if (m_activeSearchString != text) { // Start a new search.
+ ExceptionCode ec = 0;
+ RefPtr<Range> searchStartingPoint = m_activeMatch ? m_activeMatch->cloneRange(ec) : 0;
+ bool newSearch = m_activeSearchString != text;
+ bool forward = !(findOptions & WebCore::Backwards);
+ if (newSearch) { // Start a new search.
m_activeSearchString = text;
+ cancelPendingScopingEffort();
+ m_scopingCaseInsensitive = findOptions & CaseInsensitive;
m_webPage->m_page->unmarkAllTextMatches();
- m_activeMatchCount = m_webPage->m_page->markAllMatchesForText(text, CaseInsensitive, true /* shouldHighlight */, TextMatchMarkerLimit);
- if (!m_activeMatchCount) {
- clearTextMatches();
- return false;
- }
- } else { // Search same string for next occurrence.
- setMarkerActive(m_activeMatch.get(), false /* active */);
+ } else {
// Searching for same string should start from the end of last match.
if (m_activeMatch) {
if (forward)
@@ -90,23 +128,27 @@ bool InPageSearchManager::findNextString(const String& text, bool forward)
Frame* currentActiveMatchFrame = selection.isNone() && m_activeMatch ? m_activeMatch->ownerDocument()->frame() : m_webPage->focusedOrMainFrame();
- const FindOptions findOptions = (forward ? 0 : Backwards)
- | CaseInsensitive
- | StartInSelection;
-
- if (findAndMarkText(text, searchStartingPoint.get(), currentActiveMatchFrame, findOptions))
+ if (findAndMarkText(text, searchStartingPoint.get(), currentActiveMatchFrame, findOptions, newSearch))
return true;
Frame* startFrame = currentActiveMatchFrame;
do {
- currentActiveMatchFrame = DOMSupport::incrementFrame(currentActiveMatchFrame, forward, true /* wrapFlag */);
- if (findAndMarkText(text, 0, currentActiveMatchFrame, findOptions))
+ currentActiveMatchFrame = DOMSupport::incrementFrame(currentActiveMatchFrame, forward, wrap);
+
+ if (!currentActiveMatchFrame) {
+ // We should only ever have a null frame if we haven't found any
+ // matches and we're not wrapping. We have searched every frame.
+ ASSERT(!wrap);
+ return false;
+ }
+
+ if (findAndMarkText(text, 0, currentActiveMatchFrame, findOptions, newSearch))
return true;
- } while (currentActiveMatchFrame && startFrame != currentActiveMatchFrame);
+ } while (startFrame != currentActiveMatchFrame);
clearTextMatches();
- ASSERT_NOT_REACHED();
+ // FIXME: We need to notify client here.
return false;
}
@@ -117,7 +159,8 @@ bool InPageSearchManager::shouldSearchForText(const String& text)
// If the previous search string is prefix of new search string,
// don't search if the previous one has zero result.
- if (!m_activeMatchCount
+ if (m_scopingComplete
+ && !m_activeMatchCount
&& m_activeSearchString.length()
&& text.length() > m_activeSearchString.length()
&& m_activeSearchString == text.substring(0, m_activeSearchString.length()))
@@ -125,11 +168,27 @@ bool InPageSearchManager::shouldSearchForText(const String& text)
return true;
}
-bool InPageSearchManager::findAndMarkText(const String& text, Range* range, Frame* frame, const FindOptions& options)
+bool InPageSearchManager::findAndMarkText(const String& text, Range* range, Frame* frame, const FindOptions& options, bool isNewSearch)
{
- m_activeMatch = frame->editor()->findStringAndScrollToVisible(text, range, options);
- if (m_activeMatch) {
- setMarkerActive(m_activeMatch.get(), true /* active */);
+ if (RefPtr<Range> match = frame->editor()->findStringAndScrollToVisible(text, range, options)) {
+ // Move the highlight to the new match.
+ setActiveMatchAndMarker(match);
+
+ if (m_highlightAllMatches) {
+ // FIXME: If it is a not new search, we need to calculate activeMatchIndex and notify client.
+ if (isNewSearch)
+ scopeStringMatches(text, true /* reset */);
+ } else {
+ // When only showing single matches, cancel any scoping effort and ensure
+ // only the single active match is marked.
+ cancelPendingScopingEffort();
+ m_webPage->m_page->unmarkAllTextMatches();
+ m_activeMatch->ownerDocument()->markers()->addTextMatchMarker(m_activeMatch.get(), true);
+ frame->editor()->setMarkedTextMatchesAreHighlighted(true /* highlight */);
+ m_activeMatchCount = 1;
+ m_activeMatchIndex = 1;
+ }
+
return true;
}
return false;
@@ -139,16 +198,23 @@ void InPageSearchManager::clearTextMatches()
{
m_webPage->m_page->unmarkAllTextMatches();
m_activeMatch = 0;
+ m_activeMatchCount = 0;
+ m_activeMatchIndex = 0;
}
-void InPageSearchManager::setMarkerActive(Range* range, bool active)
+void InPageSearchManager::setActiveMatchAndMarker(PassRefPtr<Range> range)
{
- if (!range)
- return;
- Document* doc = m_activeMatch->ownerDocument();
- if (!doc)
- return;
- doc->markers()->setMarkersActive(range, active);
+ // Clear the old marker, update our range, and highlight the new range.
+ if (m_activeMatch.get()) {
+ if (Document* doc = m_activeMatch->ownerDocument())
+ doc->markers()->setMarkersActive(m_activeMatch.get(), false);
+ }
+
+ m_activeMatch = range;
+ if (m_activeMatch.get()) {
+ if (Document* doc = m_activeMatch->ownerDocument())
+ doc->markers()->setMarkersActive(m_activeMatch.get(), true);
+ }
}
void InPageSearchManager::frameUnloaded(const Frame* frame)
@@ -161,6 +227,8 @@ void InPageSearchManager::frameUnloaded(const Frame* frame)
Frame* currentActiveMatchFrame = m_activeMatch->ownerDocument()->frame();
if (currentActiveMatchFrame == frame) {
+ // FIXME: We need to re-scope this frame instead of cancelling all effort?
+ cancelPendingScopingEffort();
m_activeMatch = 0;
m_activeSearchString = String();
m_activeMatchCount = 0;
@@ -171,5 +239,100 @@ void InPageSearchManager::frameUnloaded(const Frame* frame)
}
}
+void InPageSearchManager::scopeStringMatches(const String& text, bool reset, Frame* scopingFrame)
+{
+ if (reset) {
+ m_activeMatchCount = 0;
+ m_resumeScopingFromRange = 0;
+ m_scopingComplete = false;
+ m_locatingActiveMatch = true;
+ // New search should always start from mainFrame.
+ scopeStringMatchesSoon(m_webPage->mainFrame(), text, false /* reset */);
+ return;
+ }
+
+ if (m_resumeScopingFromRange && scopingFrame != m_resumeScopingFromRange->ownerDocument()->frame())
+ m_resumeScopingFromRange = 0;
+
+ RefPtr<Range> searchRange(rangeOfContents(scopingFrame->document()));
+ Node* originalEndContainer = searchRange->endContainer();
+ int originalEndOffset = searchRange->endOffset();
+ ExceptionCode ec = 0, ec2 = 0;
+ if (m_resumeScopingFromRange) {
+ searchRange->setStart(m_resumeScopingFromRange->startContainer(), m_resumeScopingFromRange->startOffset(ec2) + 1, ec);
+ if (ec || ec2) {
+ m_scopingComplete = true; // We should stop scoping because of some stale data.
+ return;
+ }
+ }
+
+ int matchCount = 0;
+ bool timeout = false;
+ double startTime = currentTime();
+ do {
+ RefPtr<Range> resultRange(findPlainText(searchRange.get(), text, m_scopingCaseInsensitive ? CaseInsensitive : 0));
+ if (resultRange->collapsed(ec)) {
+ if (!resultRange->startContainer()->isInShadowTree())
+ break;
+ searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec);
+ searchRange->setEnd(originalEndContainer, originalEndOffset, ec);
+ continue;
+ }
+
+ if (scopingFrame->editor()->insideVisibleArea(resultRange.get())) {
+ ++matchCount;
+ bool foundActiveMatch = false;
+ if (m_locatingActiveMatch && areRangesEqual(resultRange.get(), m_activeMatch.get())) {
+ foundActiveMatch = true;
+ m_locatingActiveMatch = false;
+ m_activeMatchIndex = m_activeMatchCount + matchCount;
+ // FIXME: We need to notify client with m_activeMatchIndex.
+ }
+ resultRange->ownerDocument()->markers()->addTextMatchMarker(resultRange.get(), foundActiveMatch);
+ }
+ searchRange->setStart(resultRange->endContainer(ec), resultRange->endOffset(ec), ec);
+ Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
+ if (searchRange->collapsed(ec) && shadowTreeRoot)
+ searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
+ m_resumeScopingFromRange = resultRange;
+ timeout = (currentTime() - startTime) >= MaxScopingDuration;
+ } while (!timeout);
+
+ if (matchCount > 0) {
+ scopingFrame->editor()->setMarkedTextMatchesAreHighlighted(true /* highlight */);
+ m_activeMatchCount += matchCount;
+ }
+
+ if (timeout)
+ scopeStringMatchesSoon(scopingFrame, text, false /* reset */);
+ else {
+ // Scoping is done for this frame.
+ Frame* nextFrame = DOMSupport::incrementFrame(scopingFrame, true /* forward */, false /* wrapFlag */);
+ if (!nextFrame) {
+ m_scopingComplete = true;
+ return; // Scoping is done for all frames;
+ }
+ scopeStringMatchesSoon(nextFrame, text, false /* reset */);
+ }
+}
+
+void InPageSearchManager::scopeStringMatchesSoon(Frame* scopingFrame, const String& text, bool reset)
+{
+ m_deferredScopingWork.append(new DeferredScopeStringMatches(this, scopingFrame, text, reset));
+}
+
+void InPageSearchManager::callScopeStringMatches(DeferredScopeStringMatches* caller, Frame* scopingFrame, const String& text, bool reset)
+{
+ m_deferredScopingWork.remove(m_deferredScopingWork.find(caller));
+ scopeStringMatches(text, reset, scopingFrame);
+ delete caller;
+}
+
+void InPageSearchManager::cancelPendingScopingEffort()
+{
+ deleteAllValues(m_deferredScopingWork);
+ m_deferredScopingWork.clear();
+}
+
}
}