Modified: trunk/Source/WebKit/blackberry/WebKitSupport/InputHandler.cpp (125597 => 125598)
--- trunk/Source/WebKit/blackberry/WebKitSupport/InputHandler.cpp 2012-08-14 20:44:35 UTC (rev 125597)
+++ trunk/Source/WebKit/blackberry/WebKitSupport/InputHandler.cpp 2012-08-14 20:45:10 UTC (rev 125598)
@@ -57,9 +57,11 @@
#include "SpellChecker.h"
#include "TextCheckerClient.h"
#include "TextIterator.h"
+#include "VisiblePosition.h"
#include "WebPageClient.h"
#include "WebPage_p.h"
#include "WebSettings.h"
+#include "visible_units.h"
#include <BlackBerryPlatformKeyboardEvent.h>
#include <BlackBerryPlatformLog.h>
@@ -72,6 +74,7 @@
#define ENABLE_SPELLING_LOG 0
static const unsigned MaxLearnTextDataSize = 500;
+static const unsigned MaxSpellCheckingStringLength = 250;
using namespace BlackBerry::Platform;
using namespace WebCore;
@@ -531,28 +534,49 @@
RefPtr<WebCore::TextCheckingRequest> request = textCheckingRequest;
int32_t sequenceId = request->sequence();
- int requestLength = request->text().length();
- if (!requestLength /* || requestLength > maxSpellCheckStringLength */) {
+
+ // Check if field explicitly asked for spellchecking.
+ if (DOMSupport::elementSupportsSpellCheck(m_currentFocusElement.get()) != DOMSupport::On) {
spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
return;
}
+ unsigned requestLength = request->text().length();
+
+ if (requestLength < 2) {
+ spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
+ return;
+ }
+
+ if (requestLength > MaxSpellCheckingStringLength) {
+ // Cancel this request and send it off in newly created chunks.
+ spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
+ if (m_currentFocusElement && m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->selection()) {
+ // Convert from position back to selection so we can expand the range to include the previous line. This should handle cases when the user hits
+ // enter to finish composing a word and create a new line.
+ VisiblePosition caretPosition = m_currentFocusElement->document()->frame()->selection()->start();
+ VisibleSelection visibleSelection = VisibleSelection(previousLinePosition(caretPosition, caretPosition.lineDirectionPointForBlockDirectionNavigation()), caretPosition);
+ spellCheckBlock(visibleSelection, TextCheckingProcessIncremental);
+ }
+ return;
+ }
+
wchar_t* checkingString = (wchar_t*)malloc(sizeof(wchar_t) * (requestLength + 1));
if (!checkingString) {
- BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelCritical, "InputHandler::requestCheckingOfString Cannot allocate memory for string.");
+ logAlways(LogLevelCritical, "InputHandler::requestCheckingOfString Cannot allocate memory for string.");
spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
return;
}
- int stringLength = 0;
- if (!convertStringToWchar(request->text(), checkingString, requestLength + 1, &stringLength)) {
- BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelCritical, "InputHandler::requestCheckingOfString Failed to convert String to wchar type.");
+ int paragraphLength = 0;
+ if (!convertStringToWchar(request->text(), checkingString, requestLength + 1, ¶graphLength)) {
+ logAlways(LogLevelCritical, "InputHandler::requestCheckingOfString Failed to convert String to wchar type.");
free(checkingString);
spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
return;
}
- int32_t transactionId = m_webPage->m_client->checkSpellingOfStringAsync(checkingString, stringLength);
+ int32_t transactionId = m_webPage->m_client->checkSpellingOfStringAsync(checkingString, paragraphLength);
free(checkingString);
// If the call to the input service did not go through, then cancel the request so we don't block endlessly.
@@ -620,27 +644,34 @@
// transactionId here is for use with the input service. We need to translate this to sequenceId used with SpellChecker.
int32_t sequenceId = convertTransactionIdToSequenceId(transactionId);
- SpellChecker* sp = getSpellChecker();
- if (!sp || !sequenceId) {
- InputLog(LogLevelWarn, "InputHandler::spellCheckingRequestProcessed failed to process the request with sequenceId %d", sequenceId);
+ SpellChecker* spellChecker = getSpellChecker();
+ if (!spellChecker || !sequenceId) {
+ SpellingLog(LogLevelWarn, "InputHandler::spellCheckingRequestProcessed Failed to process the request with sequenceId %d", sequenceId);
spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
return;
}
- sp->didCheckSucceeded(sequenceId, results);
+ spellChecker->didCheckSucceeded(sequenceId, results);
}
+void InputHandler::cancelAllSpellCheckingRequests()
+{
+ for (std::map<int32_t, int32_t>::iterator it = m_sequenceMap.begin(); it != m_sequenceMap.end(); ++it)
+ spellCheckingRequestCancelled(it->second, true /* isSequenceId */);
+ m_sequenceMap.clear();
+}
+
void InputHandler::spellCheckingRequestCancelled(int32_t id, bool isSequenceId)
{
if (!isActiveTextEdit())
return;
int32_t sequenceId = isSequenceId ? id : convertTransactionIdToSequenceId(id);
- SpellChecker* sp = getSpellChecker();
- if (!sp) {
- InputLog(LogLevelWarn, "InputHandler::spellCheckingRequestCancelled failed to cancel the request with sequenceId %d", sequenceId);
+ SpellChecker* spellChecker = getSpellChecker();
+ if (!spellChecker) {
+ SpellingLog(LogLevelWarn, "InputHandler::spellCheckingRequestCancelled failed to cancel the request with sequenceId %d", sequenceId);
return;
}
- sp->didCheckCanceled(sequenceId);
+ spellChecker->didCheckCanceled(sequenceId);
}
SpellChecker* InputHandler::getSpellChecker()
@@ -719,6 +750,9 @@
// If the frame selection isn't focused, focus it.
if (!m_currentFocusElement->document()->frame()->selection()->isFocused())
m_currentFocusElement->document()->frame()->selection()->setFocused(true);
+
+ // Cancel any spellcheck requests that might be ongoing.
+ cancelAllSpellCheckingRequests();
}
// Clear the node details.
@@ -748,11 +782,23 @@
void InputHandler::setElementFocused(Element* element)
{
ASSERT(DOMSupport::isTextBasedContentEditableElement(element));
- ASSERT(element->document() && element->document()->frame());
+ ASSERT(element && element->document() && element->document()->frame());
- if (element->document()->frame()->selection()->isFocused() != isInputModeEnabled())
- element->document()->frame()->selection()->setFocused(isInputModeEnabled());
+#ifdef ENABLE_SPELLING_LOG
+ BlackBerry::Platform::StopWatch timer;
+ timer.start();
+#endif
+ if (!element || !(element->document()))
+ return;
+
+ Frame* frame = element->document()->frame();
+ if (!frame)
+ return;
+
+ if (frame->selection()->isFocused() != isInputModeEnabled())
+ frame->selection()->setFocused(isInputModeEnabled());
+
// Clear the existing focus node details.
setElementUnfocused(true /*refocusOccuring*/);
@@ -774,8 +820,93 @@
if (!m_delayKeyboardVisibilityChange)
notifyClientOfKeyboardVisibilityChange(true);
+
+#ifdef ENABLE_SPELLING_LOG
+ SpellingLog(LogLevelInfo, "InputHandler::setElementFocused Focusing the field took %f seconds.", timer.elapsed());
+#endif
+
+ // Check if the field explicitly asks for spellchecking.
+ if (DOMSupport::elementSupportsSpellCheck(element) != DOMSupport::On)
+ return;
+
+ // Spellcheck the field in its entirety.
+ VisibleSelection focusedBlock = DOMSupport::visibleSelectionForInputElement(element);
+ spellCheckBlock(focusedBlock, TextCheckingProcessBatch);
+
+#ifdef ENABLE_SPELLING_LOG
+ SpellingLog(LogLevelInfo, "InputHandler::setElementFocused Spellchecking the field increased the total time to focus to %f seconds.", timer.elapsed());
+#endif
}
+void InputHandler::spellCheckBlock(VisibleSelection& visibleSelection, TextCheckingProcessType textCheckingProcessType)
+{
+ if (!isActiveTextEdit())
+ return;
+
+ SpellChecker* spellChecker = getSpellChecker();
+ if (!spellChecker) {
+ SpellingLog(LogLevelInfo, "InputHandler::spellCheckBlock Failed to spellcheck the current focused element.");
+ return;
+ }
+
+ RefPtr<Range> rangeForSpellChecking = visibleSelection.toNormalizedRange();
+
+ // If we have a batch request, try to send off the entire block.
+ if (textCheckingProcessType == TextCheckingProcessBatch) {
+ // If total block text is under the limited amount, send the entire chunk.
+ if (rangeForSpellChecking->text().length() < MaxSpellCheckingStringLength) {
+ spellChecker->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling, TextCheckingProcessBatch, rangeForSpellChecking, rangeForSpellChecking));
+ return;
+ }
+ }
+
+ // Since we couldn't check the entire block at once, set up starting and ending markers to fire incrementally.
+ VisiblePosition startPos = visibleSelection.visibleStart();
+ VisiblePosition startOfCurrentLine = startOfLine(startPos);
+ VisiblePosition endOfCurrentLine = endOfLine(startPos);
+
+ while (startOfCurrentLine != endOfCurrentLine) {
+ // Create a selection with the start and end points of the line, and convert to Range to create a SpellCheckRequest.
+ rangeForSpellChecking = VisibleSelection(startOfCurrentLine, endOfCurrentLine).toNormalizedRange();
+
+ if (rangeForSpellChecking->text().length() >= MaxSpellCheckingStringLength) {
+ // Iterate through words from the start of the line to the end.
+ rangeForSpellChecking = getRangeForSpellCheckWithFineGranularity(startOfCurrentLine, endOfCurrentLine);
+ if (!rangeForSpellChecking) {
+ SpellingLog(LogLevelWarn, "InputHandler::spellCheckBlock Failed to set text range for spellchecking");
+ return;
+ }
+ startOfCurrentLine = VisiblePosition(rangeForSpellChecking->endPosition());
+ } else {
+ startOfCurrentLine = nextLinePosition(startOfCurrentLine, startOfCurrentLine.lineDirectionPointForBlockDirectionNavigation());
+ endOfCurrentLine = endOfLine(startOfCurrentLine);
+ // If we are at the last line, nextLinePosition will return the position at the end of the line. If we're not at the end, wrap with a call to startOfLine to be safe.
+ if (startOfCurrentLine != endOfCurrentLine)
+ startOfCurrentLine = startOfLine(startOfCurrentLine);
+ }
+
+ SpellingLog(LogLevelInfo, "InputHandler::spellCheckBlock Substring text is '%s', of size %d", rangeForSpellChecking->text().latin1().data(), rangeForSpellChecking->text().length());
+
+ // Call spellcheck with substring.
+ spellChecker->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling, TextCheckingProcessBatch, rangeForSpellChecking, rangeForSpellChecking));
+ }
+}
+
+PassRefPtr<Range> InputHandler::getRangeForSpellCheckWithFineGranularity(VisiblePosition startPosition, VisiblePosition endPosition)
+{
+ VisiblePosition endOfCurrentWord = endOfWord(startPosition);
+ RefPtr<Range> rangeForSpellChecking;
+ while (endOfCurrentWord != endPosition) {
+ rangeForSpellChecking = VisibleSelection(startPosition, endOfCurrentWord).toNormalizedRange();
+ // If we exceed the MaxSpellCheckingStringLength limit, then go back one word and return this range.
+ if (rangeForSpellChecking->text().length() >= MaxSpellCheckingStringLength)
+ return VisibleSelection(startPosition, endOfWord(previousWordPosition(endOfCurrentWord))).toNormalizedRange();
+
+ endOfCurrentWord = endOfWord(nextWordPosition(endOfCurrentWord));
+ }
+ return 0;
+}
+
bool InputHandler::openDatePopup(HTMLInputElement* element, BlackBerryInputType type)
{
if (!element || element->disabled() || !DOMSupport::isDateTimeInputField(element))