diff options
Diffstat (limited to 'Source/WebKit')
38 files changed, 2176 insertions, 4549 deletions
diff --git a/Source/WebKit/Android.mk b/Source/WebKit/Android.mk index 36664dd..c6f429b 100644 --- a/Source/WebKit/Android.mk +++ b/Source/WebKit/Android.mk @@ -16,8 +16,10 @@ ## LOCAL_SRC_FILES := \ + android/WebCoreSupport/CacheResult.cpp \ android/WebCoreSupport/CachedFramePlatformDataAndroid.cpp \ android/WebCoreSupport/ChromeClientAndroid.cpp \ + android/WebCoreSupport/ChromiumInit.cpp \ android/WebCoreSupport/ContextMenuClientAndroid.cpp \ android/WebCoreSupport/DeviceMotionClientAndroid.cpp \ android/WebCoreSupport/DeviceOrientationClientAndroid.cpp \ @@ -31,11 +33,6 @@ LOCAL_SRC_FILES := \ android/WebCoreSupport/PlatformBridge.cpp \ android/WebCoreSupport/ResourceLoaderAndroid.cpp \ android/WebCoreSupport/UrlInterceptResponse.cpp \ - -ifeq ($(HTTP_STACK),chrome) -LOCAL_SRC_FILES += \ - android/WebCoreSupport/ChromiumInit.cpp \ - android/WebCoreSupport/CacheResult.cpp \ android/WebCoreSupport/WebCache.cpp \ android/WebCoreSupport/WebCookieJar.cpp \ android/WebCoreSupport/WebUrlLoader.cpp \ @@ -45,7 +42,6 @@ LOCAL_SRC_FILES += \ android/WebCoreSupport/WebResourceRequest.cpp \ android/WebCoreSupport/WebResponse.cpp \ android/WebCoreSupport/WebViewClientError.cpp -endif # HTTP_STACK == chrome LOCAL_SRC_FILES += \ android/RenderSkinAndroid.cpp \ @@ -54,6 +50,10 @@ LOCAL_SRC_FILES += \ \ android/icu/unicode/ucnv.cpp \ \ + android/content/address_detector.cpp \ + android/content/content_detector.cpp \ + android/content/PhoneEmailDetector.cpp \ + \ android/jni/AndroidHitTestResult.cpp \ android/jni/CacheManager.cpp \ android/jni/CookieManager.cpp \ @@ -63,13 +63,11 @@ LOCAL_SRC_FILES += \ android/jni/GeolocationPermissionsBridge.cpp \ android/jni/JavaBridge.cpp \ android/jni/JavaSharedClient.cpp \ - android/jni/JniUtil.cpp \ android/jni/MIMETypeRegistry.cpp \ android/jni/MockGeolocation.cpp \ android/jni/PictureSet.cpp \ android/jni/WebCoreFrameBridge.cpp \ android/jni/WebCoreJni.cpp \ - android/jni/WebCoreResourceLoader.cpp \ android/jni/WebFrameView.cpp \ android/jni/WebHistory.cpp \ android/jni/WebIconDatabase.cpp \ @@ -78,7 +76,6 @@ LOCAL_SRC_FILES += \ android/jni/WebViewCore.cpp \ android/jni/ViewStateSerializer.cpp \ \ - android/nav/CacheBuilder.cpp \ android/nav/CachedFrame.cpp \ android/nav/CachedHistory.cpp \ android/nav/CachedInput.cpp \ diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp index 64799a8..c573b4e 100644 --- a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp +++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.cpp @@ -70,7 +70,6 @@ #include "SkRect.h" #include "TextEncoding.h" #include "WebCoreFrameBridge.h" -#include "WebCoreResourceLoader.h" #include "WebHistory.h" #include "WebIconDatabase.h" #include "WebFrameView.h" diff --git a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h index 51b67ba..6eb4745 100644 --- a/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h +++ b/Source/WebKit/android/WebCoreSupport/FrameLoaderClientAndroid.h @@ -26,7 +26,6 @@ #ifndef FrameLoaderClientAndroid_h #define FrameLoaderClientAndroid_h -#include "CacheBuilder.h" #include "FrameLoaderClient.h" #include "ResourceResponse.h" #include "WebIconDatabase.h" @@ -216,9 +215,6 @@ namespace android { // WebIconDatabaseClient api virtual void didAddIconForPageUrl(const String& pageUrl); - // FIXME: this doesn't really go here, but it's better than Frame - CacheBuilder& getCacheBuilder() { return m_cacheBuilder; } - void enableOnDemandPlugins() { m_onDemandPluginsEnabled = true; } void dispatchDidChangeIcons(); @@ -227,7 +223,6 @@ namespace android { virtual void didSaveToPageCache() { } virtual void didRestoreFromPageCache() { } private: - CacheBuilder m_cacheBuilder; Frame* m_frame; WebFrame* m_webFrame; PluginManualLoader* m_manualLoader; @@ -263,7 +258,6 @@ namespace android { ErrorFileNotFound = -14, ErrorTooManyRequests = -15 }; - friend class CacheBuilder; }; } diff --git a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp index 8e4f56c..b684a1a 100644 --- a/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp +++ b/Source/WebKit/android/WebCoreSupport/PlatformBridge.cpp @@ -43,7 +43,7 @@ #include "WebViewCore.h" #include "npruntime.h" -#include <surfaceflinger/SurfaceComposerClient.h> +#include <gui/SurfaceComposerClient.h> #include <ui/DisplayInfo.h> #include <ui/PixelFormat.h> #include <wtf/android/AndroidThreading.h> @@ -73,49 +73,25 @@ String PlatformBridge::getSignedPublicKeyAndChallengeString(unsigned index, cons void PlatformBridge::setCookies(const Document* document, const KURL& url, const String& value) { -#if USE(CHROME_NETWORK_STACK) std::string cookieValue(value.utf8().data()); GURL cookieGurl(url.string().utf8().data()); bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); WebCookieJar::get(isPrivateBrowsing)->cookieStore()->SetCookie(cookieGurl, cookieValue); -#else - CookieClient* client = JavaSharedClient::GetCookieClient(); - if (!client) - return; - - client->setCookies(url, value); -#endif } String PlatformBridge::cookies(const Document* document, const KURL& url) { -#if USE(CHROME_NETWORK_STACK) GURL cookieGurl(url.string().utf8().data()); bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); std::string cookies = WebCookieJar::get(isPrivateBrowsing)->cookieStore()->GetCookies(cookieGurl); String cookieString(cookies.c_str()); return cookieString; -#else - CookieClient* client = JavaSharedClient::GetCookieClient(); - if (!client) - return String(); - - return client->cookies(url); -#endif } bool PlatformBridge::cookiesEnabled(const Document* document) { -#if USE(CHROME_NETWORK_STACK) bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); return WebCookieJar::get(isPrivateBrowsing)->allowCookies(); -#else - CookieClient* client = JavaSharedClient::GetCookieClient(); - if (!client) - return false; - - return client->cookiesEnabled(); -#endif } NPObject* PlatformBridge::pluginScriptableObject(Widget* widget) @@ -173,15 +149,11 @@ int PlatformBridge::screenHeightInDocCoord(const WebCore::FrameView* frameView) String PlatformBridge::computeDefaultLanguage() { -#if USE(CHROME_NETWORK_STACK) String acceptLanguages = WebRequestContext::acceptLanguage(); size_t length = acceptLanguages.find(','); if (length == std::string::npos) length = acceptLanguages.length(); return acceptLanguages.substring(0, length); -#else - return "en"; -#endif } void PlatformBridge::updateViewport(FrameView* frameView) diff --git a/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp index 7f54810..92c39b8 100644 --- a/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp +++ b/Source/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp @@ -29,7 +29,6 @@ #include "Frame.h" #include "FrameLoaderClientAndroid.h" #include "WebCoreFrameBridge.h" -#include "WebCoreResourceLoader.h" #include "WebUrlLoader.h" #include "WebViewCore.h" @@ -42,18 +41,13 @@ PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start( { // Called on main thread FrameLoaderClientAndroid* clientAndroid = static_cast<FrameLoaderClientAndroid*>(client); -#if USE(CHROME_NETWORK_STACK) WebViewCore* webViewCore = WebViewCore::getWebViewCore(clientAndroid->getFrame()->view()); bool isMainFrame = !(clientAndroid->getFrame()->tree() && clientAndroid->getFrame()->tree()->parent()); return WebUrlLoader::start(client, handle, request, isMainResource, isMainFrame, isSync, webViewCore->webRequestContext()); -#else - return clientAndroid->webFrame()->startLoadingResource(handle, request, isMainResource, isSync); -#endif } bool ResourceLoaderAndroid::willLoadFromCache(const WebCore::KURL& url, int64_t identifier) { -#if USE(CHROME_NETWORK_STACK) // This method is used to determine if a POST request can be repeated from // cache, but you cannot really know until you actually try to read from the // cache. Even if we checked now, something else could come along and wipe @@ -63,9 +57,6 @@ bool ResourceLoaderAndroid::willLoadFromCache(const WebCore::KURL& url, int64_t // reload. Then in FrameLoaderClientImpl::dispatchWillSendRequest, we // fix-up the cache policy of the request to force a load from the cache. return true; -#else - return WebCoreResourceLoader::willLoadFromCache(url, identifier); -#endif } } diff --git a/Source/WebKit/android/content/PhoneEmailDetector.cpp b/Source/WebKit/android/content/PhoneEmailDetector.cpp new file mode 100644 index 0000000..d188c0b --- /dev/null +++ b/Source/WebKit/android/content/PhoneEmailDetector.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#undef WEBKIT_IMPLEMENTATION +#undef LOG + +#include "base/utf_string_conversions.h" +#include "net/base/escape.h" +#include "PhoneEmailDetector.h" +#include "WebString.h" + +#define LOG_TAG "PhoneNumberDetector" +#include <cutils/log.h> + +#define PHONE_PATTERN "(200) /-.\\ 100 -. 0000" + +static const char kTelSchemaPrefix[] = "tel:"; +static const char kEmailSchemaPrefix[] = "mailto:"; + +void FindReset(FindState* state); +void FindResetNumber(FindState* state); +FoundState FindPartialNumber(const UChar* chars, unsigned length, + FindState* s); +struct FindState; + +static FoundState FindPartialEMail(const UChar* , unsigned length, FindState* ); +static bool IsDomainChar(UChar ch); +static bool IsMailboxChar(UChar ch); + +PhoneEmailDetector::PhoneEmailDetector() + : m_foundResult(FOUND_NONE) +{ +} + +bool PhoneEmailDetector::FindContent(const string16::const_iterator& begin, + const string16::const_iterator& end, + size_t* start_pos, + size_t* end_pos) +{ + FindReset(&m_findState); + m_foundResult = FindPartialNumber(begin, end - begin, &m_findState); + if (m_foundResult == FOUND_COMPLETE) + m_prefix = kTelSchemaPrefix; + else { + FindReset(&m_findState); + m_foundResult = FindPartialEMail(begin, end - begin, &m_findState); + m_prefix = kEmailSchemaPrefix; + } + *start_pos = m_findState.mStartResult; + *end_pos = m_findState.mEndResult; + return m_foundResult == FOUND_COMPLETE; +} + +std::string PhoneEmailDetector::GetContentText(const WebKit::WebRange& range) +{ + if (m_foundResult == FOUND_COMPLETE) { + if (m_prefix == kTelSchemaPrefix) + return UTF16ToUTF8(m_findState.mStore); + else + return UTF16ToUTF8(range.toPlainText()); + } + return std::string(); +} + +GURL PhoneEmailDetector::GetIntentURL(const std::string& content_text) +{ + return GURL(m_prefix + + EscapeQueryParamValue(content_text, true)); +} + +void FindReset(FindState* state) +{ + memset(state, 0, sizeof(FindState)); + state->mCurrent = ' '; + FindResetNumber(state); +} + +void FindResetNumber(FindState* state) +{ + state->mOpenParen = false; + state->mPattern = (char*) PHONE_PATTERN; + state->mStorePtr = state->mStore; +} + +FoundState FindPartialNumber(const UChar* chars, unsigned length, + FindState* s) +{ + char* pattern = s->mPattern; + UChar* store = s->mStorePtr; + const UChar* start = chars; + const UChar* end = chars + length; + const UChar* lastDigit = 0; + string16 search16(chars, length); + std::string searchSpace = UTF16ToUTF8(search16); + do { + bool initialized = s->mInitialized; + while (chars < end) { + if (initialized == false) { + s->mBackTwo = s->mBackOne; + s->mBackOne = s->mCurrent; + } + UChar ch = s->mCurrent = *chars; + do { + char patternChar = *pattern; + switch (patternChar) { + case '2': + if (initialized == false) { + s->mStartResult = chars - start; + initialized = true; + } + case '0': + case '1': + if (ch < patternChar || ch > '9') + goto resetPattern; + *store++ = ch; + pattern++; + lastDigit = chars; + goto nextChar; + case '\0': + if (WTF::isASCIIDigit(ch) == false) { + *store = '\0'; + goto checkMatch; + } + goto resetPattern; + case ' ': + if (ch == patternChar) + goto nextChar; + break; + case '(': + if (ch == patternChar) { + s->mStartResult = chars - start; + initialized = true; + s->mOpenParen = true; + } + goto commonPunctuation; + case ')': + if ((ch == patternChar) ^ s->mOpenParen) + goto resetPattern; + default: + commonPunctuation: + if (ch == patternChar) { + pattern++; + goto nextChar; + } + } + } while (++pattern); // never false + nextChar: + chars++; + } + break; +resetPattern: + if (s->mContinuationNode) + return FOUND_NONE; + FindResetNumber(s); + pattern = s->mPattern; + store = s->mStorePtr; + } while (++chars < end); +checkMatch: + if (WTF::isASCIIDigit(s->mBackOne != '1' ? s->mBackOne : s->mBackTwo)) { + return FOUND_NONE; + } + *store = '\0'; + s->mStorePtr = store; + s->mPattern = pattern; + s->mEndResult = lastDigit - start + 1; + char pState = pattern[0]; + return pState == '\0' ? FOUND_COMPLETE : pState == '(' || (WTF::isASCIIDigit(pState) && WTF::isASCIIDigit(pattern[-1])) ? + FOUND_NONE : FOUND_PARTIAL; +} + +FoundState FindPartialEMail(const UChar* chars, unsigned length, + FindState* s) +{ + // the following tables were generated by tests/browser/focusNavigation/BrowserDebug.cpp + // hand-edit at your own risk + static const int domainTwoLetter[] = { + 0x02df797c, // a followed by: [cdefgilmnoqrstuwxz] + 0x036e73fb, // b followed by: [abdefghijmnorstvwyz] + 0x03b67ded, // c followed by: [acdfghiklmnorsuvxyz] + 0x02005610, // d followed by: [ejkmoz] + 0x001e00d4, // e followed by: [ceghrstu] + 0x00025700, // f followed by: [ijkmor] + 0x015fb9fb, // g followed by: [abdefghilmnpqrstuwy] + 0x001a3400, // h followed by: [kmnrtu] + 0x000f7818, // i followed by: [delmnoqrst] + 0x0000d010, // j followed by: [emop] + 0x0342b1d0, // k followed by: [eghimnprwyz] + 0x013e0507, // l followed by: [abcikrstuvy] + 0x03fffccd, // m followed by: [acdghklmnopqrstuvwxyz] + 0x0212c975, // n followed by: [acefgilopruz] + 0x00001000, // o followed by: [m] + 0x014e3cf1, // p followed by: [aefghklmnrstwy] + 0x00000001, // q followed by: [a] + 0x00504010, // r followed by: [eouw] + 0x032a7fdf, // s followed by: [abcdeghijklmnortvyz] + 0x026afeec, // t followed by: [cdfghjklmnoprtvwz] + 0x03041441, // u followed by: [agkmsyz] + 0x00102155, // v followed by: [aceginu] + 0x00040020, // w followed by: [fs] + 0x00000000, // x + 0x00180010, // y followed by: [etu] + 0x00401001, // z followed by: [amw] + }; + + static char const* const longDomainNames[] = { + "\x03" "ero" "\x03" "rpa", // aero, arpa + "\x02" "iz", // biz + "\x02" "at" "\x02" "om" "\x03" "oop", // cat, com, coop + NULL, // d + "\x02" "du", // edu + NULL, // f + "\x02" "ov", // gov + NULL, // h + "\x03" "nfo" "\x02" "nt", // info, int + "\x03" "obs", // jobs + NULL, // k + NULL, // l + "\x02" "il" "\x03" "obi" "\x05" "useum", // mil, mobi, museum + "\x03" "ame" "\x02" "et", // name, net + "\x02" "rg", // , org + "\x02" "ro", // pro + NULL, // q + NULL, // r + NULL, // s + "\x05" "ravel", // travel + NULL, // u + NULL, // v + NULL, // w + NULL, // x + NULL, // y + NULL, // z + }; + + const UChar* start = chars; + const UChar* end = chars + length; + while (chars < end) { + UChar ch = *chars++; + if (ch != '@') + continue; + const UChar* atLocation = chars - 1; + // search for domain + ch = *chars++ | 0x20; // convert uppercase to lower + if (ch < 'a' || ch > 'z') + continue; + while (chars < end) { + ch = *chars++; + if (IsDomainChar(ch) == false) + goto nextAt; + if (ch != '.') + continue; + UChar firstLetter = *chars++ | 0x20; // first letter of the domain + if (chars >= end) + return FOUND_NONE; // only one letter; must be at least two + firstLetter -= 'a'; + if (firstLetter > 'z' - 'a') + continue; // non-letter followed '.' + int secondLetterMask = domainTwoLetter[firstLetter]; + ch = *chars | 0x20; // second letter of the domain + ch -= 'a'; + if (ch >= 'z' - 'a') + continue; + bool secondMatch = (secondLetterMask & 1 << ch) != 0; + const char* wordMatch = longDomainNames[firstLetter]; + int wordIndex = 0; + while (wordMatch != NULL) { + int len = *wordMatch++; + char match; + do { + match = wordMatch[wordIndex]; + if (match < 0x20) + goto foundDomainStart; + if (chars[wordIndex] != match) + break; + wordIndex++; + } while (true); + wordMatch += len; + if (*wordMatch == '\0') + break; + wordIndex = 0; + } + if (secondMatch) { + wordIndex = 1; + foundDomainStart: + chars += wordIndex; + if (chars < end) { + ch = *chars; + if (ch != '.') { + if (IsDomainChar(ch)) + goto nextDot; + } else if (chars + 1 < end && IsDomainChar(chars[1])) + goto nextDot; + } + // found domain. Search backwards from '@' for beginning of email address + s->mEndResult = chars - start; + chars = atLocation; + if (chars <= start) + goto nextAt; + ch = *--chars; + if (ch == '.') + goto nextAt; // mailbox can't end in period + do { + if (IsMailboxChar(ch) == false) { + chars++; + break; + } + if (chars == start) + break; + ch = *--chars; + } while (true); + UChar firstChar = *chars; + if (firstChar == '.' || firstChar == '@') // mailbox can't start with period or be empty + goto nextAt; + s->mStartResult = chars - start; + return FOUND_COMPLETE; + } + nextDot: + ; + } +nextAt: + chars = atLocation + 1; + } + return FOUND_NONE; +} + +bool IsDomainChar(UChar ch) +{ + static const unsigned body[] = {0x03ff6000, 0x07fffffe, 0x07fffffe}; // 0-9 . - A-Z a-z + ch -= 0x20; + if (ch > 'z' - 0x20) + return false; + return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; +} + +bool IsMailboxChar(UChar ch) +{ + // According to http://en.wikipedia.org/wiki/Email_address + // ! # $ % & ' * + - . / 0-9 = ? + // A-Z ^ _ + // ` a-z { | } ~ + static const unsigned body[] = {0xa3ffecfa, 0xc7fffffe, 0x7fffffff}; + ch -= 0x20; + if (ch > '~' - 0x20) + return false; + return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; +} diff --git a/Source/WebKit/android/content/PhoneEmailDetector.h b/Source/WebKit/android/content/PhoneEmailDetector.h new file mode 100644 index 0000000..b61de62 --- /dev/null +++ b/Source/WebKit/android/content/PhoneEmailDetector.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "content/content_detector.h" +#include "PlatformString.h" + +#define NAVIGATION_MAX_PHONE_LENGTH 14 + +struct FindState { + int mStartResult; + int mEndResult; + char* mPattern; + UChar mStore[NAVIGATION_MAX_PHONE_LENGTH + 1]; + UChar* mStorePtr; + UChar mBackOne; + UChar mBackTwo; + UChar mCurrent; + bool mOpenParen; + bool mInitialized; + bool mContinuationNode; +}; + +enum FoundState { + FOUND_NONE, + FOUND_PARTIAL, + FOUND_COMPLETE +}; + +// Searches for phone numbers (US only) or email addresses based off of the navcache code +class PhoneEmailDetector : public ContentDetector { +public: + PhoneEmailDetector(); + virtual ~PhoneEmailDetector() {} + +private: + // Implementation of ContentDetector. + virtual bool FindContent(const string16::const_iterator& begin, + const string16::const_iterator& end, + size_t* start_pos, + size_t* end_pos); + + virtual std::string GetContentText(const WebKit::WebRange& range); + virtual GURL GetIntentURL(const std::string& content_text); + virtual size_t GetMaximumContentLength() { + return NAVIGATION_MAX_PHONE_LENGTH * 4; + } + + DISALLOW_COPY_AND_ASSIGN(PhoneEmailDetector); + + FindState m_findState; + FoundState m_foundResult; + const char* m_prefix; +}; diff --git a/Source/WebKit/android/content/address_detector.cpp b/Source/WebKit/android/content/address_detector.cpp new file mode 100644 index 0000000..504d37a --- /dev/null +++ b/Source/WebKit/android/content/address_detector.cpp @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +// Magic pretend-to-be-a-chromium-build flags +#undef WEBKIT_IMPLEMENTATION +#undef LOG + +#include "content/address_detector.h" + +#include <bitset> + +#include "base/utf_string_conversions.h" +#include "net/base/escape.h" +#include "WebString.h" + +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +namespace { + +// Prefix used for geographical address intent URIs. +static const char kAddressSchemaPrefix[] = "geo:0,0?q="; + +// Maximum text length to be searched for address detection. +static const size_t kMaxAddressLength = 500; + +// Minimum number of words in an address after the house number +// before a state is expected to be found. +// A value too high can miss short addresses. +const size_t kMinAddressWords = 3; + +// Maximum number of words allowed in an address between the house number +// and the state, both not included. +const size_t kMaxAddressWords = 12; + +// Maximum number of lines allowed in an address between the house number +// and the state, both not included. +const size_t kMaxAddressLines = 5; + +// Maximum length allowed for any address word between the house number +// and the state, both not included. +const size_t kMaxAddressNameWordLength = 25; + +// Maximum number of words after the house number in which the location name +// should be found. +const size_t kMaxLocationNameDistance = 4; + +// Number of digits for a valid zip code. +const size_t kZipDigits = 5; + +// Number of digits for a valid zip code in the Zip Plus 4 format. +const size_t kZipPlus4Digits = 9; + +// Maximum number of digits of a house number, including possible hyphens. +const size_t kMaxHouseDigits = 5; + +// Additional characters used as new line delimiters. +const char16 kNewlineDelimiters[] = { + ',', + '*', + 0x2022, // Unicode bullet +}; + +char16 SafePreviousChar(const string16::const_iterator& it, + const string16::const_iterator& begin) { + if (it == begin) + return ' '; + return *(it - 1); +} + +char16 SafeNextChar(const string16::const_iterator& it, + const string16::const_iterator& end) { + if (it == end) + return ' '; + return *(it + 1); +} + +bool WordLowerCaseEqualsASCII(string16::const_iterator word_begin, + string16::const_iterator word_end, const char* ascii_to_match) { + for (string16::const_iterator it = word_begin; it != word_end; + ++it, ++ascii_to_match) { + if (!*ascii_to_match || base::ToLowerASCII(*it) != *ascii_to_match) + return false; + } + return *ascii_to_match == 0 || *ascii_to_match == ' '; +} + +bool LowerCaseEqualsASCIIWithPlural(string16::const_iterator word_begin, + string16::const_iterator word_end, const char* ascii_to_match, + bool allow_plural) { + for (string16::const_iterator it = word_begin; it != word_end; + ++it, ++ascii_to_match) { + if (!*ascii_to_match && allow_plural && *it == 's' && it + 1 == word_end) + return true; + + if (!*ascii_to_match || base::ToLowerASCII(*it) != *ascii_to_match) + return false; + } + return *ascii_to_match == 0; +} + +} // anonymous namespace + + +AddressDetector::AddressDetector() { +} + +AddressDetector::~AddressDetector() { +} + +std::string AddressDetector::GetContentText(const WebKit::WebRange& range) { + // Get the address and replace unicode bullets with commas. + string16 address_16 = CollapseWhitespace(range.toPlainText(), false); + std::replace(address_16.begin(), address_16.end(), + static_cast<char16>(0x2022), static_cast<char16>(',')); + return UTF16ToUTF8(address_16); +} + +GURL AddressDetector::GetIntentURL(const std::string& content_text) { + return GURL(kAddressSchemaPrefix + + EscapeQueryParamValue(content_text, true)); +} + +size_t AddressDetector::GetMaximumContentLength() { + return kMaxAddressLength; +} + +bool AddressDetector::FindContent(const string16::const_iterator& begin, + const string16::const_iterator& end, size_t* start_pos, size_t* end_pos) { + HouseNumberParser house_number_parser; + + // Keep going through the input string until a potential house number is + // detected. Start tokenizing the following words to find a valid + // street name within a word range. Then, find a state name followed + // by a valid zip code for that state. Also keep a look for any other + // possible house numbers to continue from in case of no match and for + // state names not followed by a zip code (e.g. New York, NY 10000). + const string16 newline_delimiters = kNewlineDelimiters; + const string16 delimiters = kWhitespaceUTF16 + newline_delimiters; + for (string16::const_iterator it = begin; it != end; ) { + Word house_number; + if (!house_number_parser.Parse(it, end, &house_number)) + return false; + + String16Tokenizer tokenizer(house_number.end, end, delimiters); + tokenizer.set_options(String16Tokenizer::RETURN_DELIMS); + + std::vector<Word> words; + words.push_back(house_number); + + bool found_location_name = false; + bool continue_on_house_number = true; + size_t next_house_number_word = 0; + size_t num_lines = 1; + + // Don't include the house number in the word count. + size_t next_word = 1; + for (; next_word <= kMaxAddressWords + 1; ++next_word) { + + // Extract a new word from the tokenizer. + if (next_word == words.size()) { + do { + if (!tokenizer.GetNext()) + return false; + + // Check the number of address lines. + if (tokenizer.token_is_delim() && newline_delimiters.find( + *tokenizer.token_begin()) != string16::npos) { + ++num_lines; + } + } while (tokenizer.token_is_delim()); + + if (num_lines > kMaxAddressLines) + break; + + words.push_back(Word(tokenizer.token_begin(), tokenizer.token_end())); + } + + // Check the word length. If too long, don't try to continue from + // the next house number as no address can hold this word. + const Word& current_word = words[next_word]; + DCHECK_GT(std::distance(current_word.begin, current_word.end), 0); + size_t current_word_length = std::distance( + current_word.begin, current_word.end); + if (current_word_length > kMaxAddressNameWordLength) { + continue_on_house_number = false; + break; + } + + // Check if the new word is a valid house number. + // This is used to properly resume parsing in case the maximum number + // of words is exceeded. + if (next_house_number_word == 0 && + house_number_parser.Parse(current_word.begin, current_word.end, NULL)) { + next_house_number_word = next_word; + continue; + } + + // Look for location names in the words after the house number. + // A range limitation is introduced to avoid matching + // anything that starts with a number before a legitimate address. + if (next_word <= kMaxLocationNameDistance && + IsValidLocationName(current_word)) { + found_location_name = true; + continue; + } + + // Don't count the house number. + if (next_word > kMinAddressWords) { + // Looking for the state is likely to add new words to the list while + // checking for multi-word state names. + size_t state_first_word = next_word; + size_t state_last_word, state_index; + if (FindStateStartingInWord(&words, state_first_word, &state_last_word, + &tokenizer, &state_index)) { + + // A location name should have been found at this point. + if (!found_location_name) + break; + + // Explicitly exclude "et al", as "al" is a valid state code. + if (current_word_length == 2 && words.size() > 2) { + const Word& previous_word = words[state_first_word - 1]; + if (previous_word.end - previous_word.begin == 2 && + LowerCaseEqualsASCII(previous_word.begin, previous_word.end, + "et") && + LowerCaseEqualsASCII(current_word.begin, current_word.end, + "al")) + break; + } + + // Extract one more word from the tokenizer if not already available. + size_t zip_word = state_last_word + 1; + if (zip_word == words.size()) { + do { + if (!tokenizer.GetNext()) { + // Zip is optional + *start_pos = words[0].begin - begin; + *end_pos = words[state_last_word].end - begin; + return true; + } + } while (tokenizer.token_is_delim()); + words.push_back(Word(tokenizer.token_begin(), + tokenizer.token_end())); + } + + // Check the parsing validity and state range of the zip code. + next_word = state_last_word; + if (!IsZipValid(words[zip_word], state_index)) + continue; + + *start_pos = words[0].begin - begin; + *end_pos = words[zip_word].end - begin; + return true; + } + } + } + + // Avoid skipping too many words because of a non-address number + // at the beginning of the contents to parse. + if (continue_on_house_number && next_house_number_word > 0) { + it = words[next_house_number_word].begin; + } else { + DCHECK(!words.empty()); + next_word = std::min(next_word, words.size() - 1); + it = words[next_word].end; + } + } + + return false; +} + +bool AddressDetector::HouseNumberParser::IsPreDelimiter( + char16 character) { + return character == ':' || IsPostDelimiter(character); +} + +bool AddressDetector::HouseNumberParser::IsPostDelimiter( + char16 character) { + return IsWhitespace(character) || strchr(",\"'", character); +} + +void AddressDetector::HouseNumberParser::RestartOnNextDelimiter() { + ResetState(); + for (; it_ != end_ && !IsPreDelimiter(*it_); ++it_) {} +} + +void AddressDetector::HouseNumberParser::AcceptChars(size_t num_chars) { + size_t offset = std::min(static_cast<size_t>(std::distance(it_, end_)), + num_chars); + it_ += offset; + result_chars_ += offset; +} + +void AddressDetector::HouseNumberParser::SkipChars(size_t num_chars) { + it_ += std::min(static_cast<size_t>(std::distance(it_, end_)), num_chars); +} + +void AddressDetector::HouseNumberParser::ResetState() { + num_digits_ = 0; + result_chars_ = 0; +} + +bool AddressDetector::HouseNumberParser::CheckFinished(Word* word) const { + // There should always be a number after a hyphen. + if (result_chars_ == 0 || SafePreviousChar(it_, begin_) == '-') + return false; + + if (word) { + word->begin = it_ - result_chars_; + word->end = it_; + } + return true; +} + +bool AddressDetector::HouseNumberParser::Parse( + const string16::const_iterator& begin, + const string16::const_iterator& end, Word* word) { + it_ = begin_ = begin; + end_ = end; + ResetState(); + + // Iterations only used as a fail-safe against any buggy infinite loops. + size_t iterations = 0; + size_t max_iterations = end - begin + 1; + for (; it_ != end_ && iterations < max_iterations; ++iterations) { + + // Word finished case. + if (IsPostDelimiter(*it_)) { + if (CheckFinished(word)) + return true; + else if (result_chars_) + ResetState(); + + SkipChars(1); + continue; + } + + // More digits. There should be no more after a letter was found. + if (IsAsciiDigit(*it_)) { + if (num_digits_ >= kMaxHouseDigits) { + RestartOnNextDelimiter(); + } else { + AcceptChars(1); + ++num_digits_; + } + continue; + } + + if (IsAsciiAlpha(*it_)) { + // Handle special case 'one'. + if (result_chars_ == 0) { + if (it_ + 3 <= end_ && LowerCaseEqualsASCII(it_, it_ + 3, "one")) + AcceptChars(3); + else + RestartOnNextDelimiter(); + continue; + } + + // There should be more than 1 character because of result_chars. + DCHECK_GT(result_chars_, 0U); + DCHECK_NE(it_, begin_); + char16 previous = SafePreviousChar(it_, begin_); + if (IsAsciiDigit(previous)) { + // Check cases like '12A'. + char16 next = SafeNextChar(it_, end_); + if (IsPostDelimiter(next)) { + AcceptChars(1); + continue; + } + + // Handle cases like 12a, 1st, 2nd, 3rd, 7th. + if (IsAsciiAlpha(next)) { + char16 last_digit = previous; + char16 first_letter = base::ToLowerASCII(*it_); + char16 second_letter = base::ToLowerASCII(next); + bool is_teen = SafePreviousChar(it_ - 1, begin_) == '1' && + num_digits_ == 2; + + switch (last_digit - '0') { + case 1: + if ((first_letter == 's' && second_letter == 't') || + (first_letter == 't' && second_letter == 'h' && is_teen)) { + AcceptChars(2); + continue; + } + break; + + case 2: + if ((first_letter == 'n' && second_letter == 'd') || + (first_letter == 't' && second_letter == 'h' && is_teen)) { + AcceptChars(2); + continue; + } + break; + + case 3: + if ((first_letter == 'r' && second_letter == 'd') || + (first_letter == 't' && second_letter == 'h' && is_teen)) { + AcceptChars(2); + continue; + } + break; + + case 0: + // Explicitly exclude '0th'. + if (num_digits_ == 1) + break; + + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + if (first_letter == 't' && second_letter == 'h') { + AcceptChars(2); + continue; + } + break; + + default: + NOTREACHED(); + } + } + } + + RestartOnNextDelimiter(); + continue; + } + + if (*it_ == '-' && num_digits_ > 0) { + AcceptChars(1); + ++num_digits_; + continue; + } + + RestartOnNextDelimiter(); + SkipChars(1); + } + + if (iterations >= max_iterations) + return false; + + return CheckFinished(word); +} + +bool AddressDetector::FindStateStartingInWord(WordList* words, + size_t state_first_word, size_t* state_last_word, + String16Tokenizer* tokenizer, size_t* state_index) { + + // Bitmasks containing the allowed suffixes for 2-letter state codes. + static const int state_two_letter_suffix[23] = { + 0x02060c00, // A followed by: [KLRSZ]. + 0x00000000, // B. + 0x00084001, // C followed by: [AOT]. + 0x00000014, // D followed by: [CE]. + 0x00000000, // E. + 0x00001800, // F followed by: [LM]. + 0x00100001, // G followed by: [AU]. + 0x00000100, // H followed by: [I]. + 0x00002809, // I followed by: [ADLN]. + 0x00000000, // J. + 0x01040000, // K followed by: [SY]. + 0x00000001, // L followed by: [A]. + 0x000ce199, // M followed by: [ADEHINOPST]. + 0x0120129c, // N followed by: [CDEHJMVY]. + 0x00020480, // O followed by: [HKR]. + 0x00420001, // P followed by: [ARW]. + 0x00000000, // Q. + 0x00000100, // R followed by: [I]. + 0x0000000c, // S followed by: [CD]. + 0x00802000, // T followed by: [NX]. + 0x00080000, // U followed by: [T]. + 0x00080101, // V followed by: [AIT]. + 0x01200101 // W followed by: [AIVY]. + }; + + // Accumulative number of states for the 2-letter code indexed by the first. + static const int state_two_letter_accumulative[24] = { + 0, 5, 5, 8, 10, 10, 12, 14, + 15, 19, 19, 21, 22, 32, 40, 43, + 46, 46, 47, 49, 51, 52, 55, 59 + }; + + // State names sorted alphabetically with their lengths. + // There can be more than one possible name for a same state if desired. + static const struct StateNameInfo { + const char* string; + char first_word_length; + char length; + char state_index; // Relative to two-character code alphabetical order. + } state_names[59] = { + { "alabama", 7, 7, 1 }, { "alaska", 6, 6, 0 }, + { "american samoa", 8, 14, 3 }, { "arizona", 7, 7, 4 }, + { "arkansas", 8, 8, 2 }, + { "california", 10, 10, 5 }, { "colorado", 8, 8, 6 }, + { "connecticut", 11, 11, 7 }, { "delaware", 8, 8, 9 }, + { "district of columbia", 8, 20, 8 }, + { "federated states of micronesia", 9, 30, 11 }, { "florida", 7, 7, 10 }, + { "guam", 4, 4, 13 }, { "georgia", 7, 7, 12 }, + { "hawaii", 6, 6, 14 }, + { "idaho", 5, 5, 16 }, { "illinois", 8, 8, 17 }, { "indiana", 7, 7, 18 }, + { "iowa", 4, 4, 15 }, + { "kansas", 6, 6, 19 }, { "kentucky", 8, 8, 20 }, + { "louisiana", 9, 9, 21 }, + { "maine", 5, 5, 24 }, { "marshall islands", 8, 16, 25 }, + { "maryland", 8, 8, 23 }, { "massachusetts", 13, 13, 22 }, + { "michigan", 8, 8, 26 }, { "minnesota", 9, 9, 27 }, + { "mississippi", 11, 11, 30 }, { "missouri", 8, 8, 28 }, + { "montana", 7, 7, 31 }, + { "nebraska", 8, 8, 34 }, { "nevada", 6, 6, 38 }, + { "new hampshire", 3, 13, 35 }, { "new jersey", 3, 10, 36 }, + { "new mexico", 3, 10, 37 }, { "new york", 3, 8, 39 }, + { "north carolina", 5, 14, 32 }, { "north dakota", 5, 12, 33 }, + { "northern mariana islands", 8, 24, 29 }, + { "ohio", 4, 4, 40 }, { "oklahoma", 8, 8, 41 }, { "oregon", 6, 6, 42 }, + { "palau", 5, 5, 45 }, { "pennsylvania", 12, 12, 43 }, + { "puerto rico", 6, 11, 44 }, + { "rhode island", 5, 5, 46 }, + { "south carolina", 5, 14, 47 }, { "south dakota", 5, 12, 48 }, + { "tennessee", 9, 9, 49 }, { "texas", 5, 5, 50 }, + { "utah", 4, 4, 51 }, + { "vermont", 7, 7, 54 }, { "virgin islands", 6, 14, 53 }, + { "virginia", 8, 8, 52 }, + { "washington", 10, 10, 55 }, { "west virginia", 4, 13, 57 }, + { "wisconsin", 9, 9, 56 }, { "wyoming", 7, 7, 58 } + }; + + // Accumulative number of states for sorted names indexed by the first letter. + // Required a different one since there are codes that don't share their + // first letter with the name of their state (MP = Northern Mariana Islands). + static const int state_names_accumulative[24] = { + 0, 5, 5, 8, 10, 10, 12, 14, + 15, 19, 19, 21, 22, 31, 40, 43, + 46, 46, 47, 49, 51, 52, 55, 59 + }; + + DCHECK_EQ(state_names_accumulative[arraysize(state_names_accumulative) - 1], + static_cast<int>(ARRAYSIZE_UNSAFE(state_names))); + + const Word& first_word = words->at(state_first_word); + int length = first_word.end - first_word.begin; + if (length < 2 || !IsAsciiAlpha(*first_word.begin)) + return false; + + // No state names start with x, y, z. + char16 first_letter = base::ToLowerASCII(*first_word.begin); + if (first_letter > 'w') + return false; + + DCHECK(first_letter >= 'a'); + int first_index = first_letter - 'a'; + + // Look for two-letter state names. + if (length == 2 && IsAsciiAlpha(*(first_word.begin + 1))) { + char16 second_letter = base::ToLowerASCII(*(first_word.begin + 1)); + DCHECK(second_letter >= 'a'); + + int second_index = second_letter - 'a'; + if (!(state_two_letter_suffix[first_index] & (1 << second_index))) + return false; + + std::bitset<32> previous_suffixes = state_two_letter_suffix[first_index] & + ((1 << second_index) - 1); + *state_last_word = state_first_word; + *state_index = state_two_letter_accumulative[first_index] + + previous_suffixes.count(); + return true; + } + + // Look for full state names by their first letter. Discard by length. + for (int state = state_names_accumulative[first_index]; + state < state_names_accumulative[first_index + 1]; ++state) { + if (state_names[state].first_word_length != length) + continue; + + bool state_match = false; + size_t state_word = state_first_word; + for (int pos = 0; true; ) { + if (!WordLowerCaseEqualsASCII(words->at(state_word).begin, + words->at(state_word).end, &state_names[state].string[pos])) + break; + + pos += words->at(state_word).end - words->at(state_word).begin + 1; + if (pos >= state_names[state].length) { + state_match = true; + break; + } + + // Ran out of words, extract more from the tokenizer. + if (++state_word == words->size()) { + do { + if (!tokenizer->GetNext()) + break; + } while (tokenizer->token_is_delim()); + words->push_back(Word(tokenizer->token_begin(), tokenizer->token_end())); + } + } + + if (state_match) { + *state_last_word = state_word; + *state_index = state_names[state].state_index; + return true; + } + } + + return false; +} + +bool AddressDetector::IsZipValid(const Word& word, size_t state_index) { + size_t length = word.end - word.begin; + if (length != kZipDigits && length != kZipPlus4Digits + 1) + return false; + + for (string16::const_iterator it = word.begin; it != word.end; ++it) { + size_t pos = it - word.begin; + if (IsAsciiDigit(*it) || (*it == '-' && pos == kZipDigits)) + continue; + return false; + } + return IsZipValidForState(word, state_index); +} + +bool AddressDetector::IsZipValidForState(const Word& word, size_t state_index) +{ + enum USState { + AP = -4, // AP (military base in the Pacific) + AA = -3, // AA (military base inside the US) + AE = -2, // AE (military base outside the US) + XX = -1, // (not in use) + AK = 0, // AK Alaska + AL = 1, // AL Alabama + AR = 2, // AR Arkansas + AS = 3, // AS American Samoa + AZ = 4, // AZ Arizona + CA = 5, // CA California + CO = 6, // CO Colorado + CT = 7, // CT Connecticut + DC = 8, // DC District of Columbia + DE = 9, // DE Delaware + FL = 10, // FL Florida + FM = 11, // FM Federated States of Micronesia + GA = 12, // GA Georgia + GU = 13, // GU Guam + HI = 14, // HI Hawaii + IA = 15, // IA Iowa + ID = 16, // ID Idaho + IL = 17, // IL Illinois + IN = 18, // IN Indiana + KS = 19, // KS Kansas + KY = 20, // KY Kentucky + LA = 21, // LA Louisiana + MA = 22, // MA Massachusetts + MD = 23, // MD Maryland + ME = 24, // ME Maine + MH = 25, // MH Marshall Islands + MI = 26, // MI Michigan + MN = 27, // MN Minnesota + MO = 28, // MO Missouri + MP = 29, // MP Northern Mariana Islands + MS = 30, // MS Mississippi + MT = 31, // MT Montana + NC = 32, // NC North Carolina + ND = 33, // ND North Dakota + NE = 34, // NE Nebraska + NH = 35, // NH New Hampshire + NJ = 36, // NJ New Jersey + NM = 37, // NM New Mexico + NV = 38, // NV Nevada + NY = 39, // NY New York + OH = 40, // OH Ohio + OK = 41, // OK Oklahoma + OR = 42, // OR Oregon + PA = 43, // PA Pennsylvania + PR = 44, // PR Puerto Rico + PW = 45, // PW Palau + RI = 46, // RI Rhode Island + SC = 47, // SC South Carolina + SD = 48, // SD South Dakota + TN = 49, // TN Tennessee + TX = 50, // TX Texas + UT = 51, // UT Utah + VA = 52, // VA Virginia + VI = 53, // VI Virgin Islands + VT = 54, // VT Vermont + WA = 55, // WA Washington + WI = 56, // WI Wisconsin + WV = 57, // WV West Virginia + WY = 58, // WY Wyoming + }; + + static const USState stateForZipPrefix[] = { + // 0 1 2 3 4 5 6 7 8 9 + XX, XX, XX, XX, XX, NY, PR, PR, VI, PR, // 000-009 + MA, MA, MA, MA, MA, MA, MA, MA, MA, MA, // 010-019 + MA, MA, MA, MA, MA, MA, MA, MA, RI, RI, // 020-029 + NH, NH, NH, NH, NH, NH, NH, NH, NH, ME, // 030-039 + ME, ME, ME, ME, ME, ME, ME, ME, ME, ME, // 040-049 + VT, VT, VT, VT, VT, MA, VT, VT, VT, VT, // 050-059 + CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 060-069 + NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, // 070-079 + NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, // 080-089 + AE, AE, AE, AE, AE, AE, AE, AE, AE, XX, // 090-099 + NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 100-109 + NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 110-119 + NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 120-129 + NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 130-139 + NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 140-149 + PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 150-159 + PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 160-169 + PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 170-179 + PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 180-189 + PA, PA, PA, PA, PA, PA, PA, DE, DE, DE, // 190-199 + DC, VA, DC, DC, DC, DC, MD, MD, MD, MD, // 200-209 + MD, MD, MD, XX, MD, MD, MD, MD, MD, MD, // 210-219 + VA, VA, VA, VA, VA, VA, VA, VA, VA, VA, // 220-229 + VA, VA, VA, VA, VA, VA, VA, VA, VA, VA, // 230-239 + VA, VA, VA, VA, VA, VA, VA, WV, WV, WV, // 240-249 + WV, WV, WV, WV, WV, WV, WV, WV, WV, WV, // 250-259 + WV, WV, WV, WV, WV, WV, WV, WV, WV, XX, // 260-269 + NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, // 270-279 + NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, // 280-289 + SC, SC, SC, SC, SC, SC, SC, SC, SC, SC, // 290-299 + GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, // 300-309 + GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, // 310-319 + FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, // 320-329 + FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, // 330-339 + AA, FL, FL, XX, FL, XX, FL, FL, XX, FL, // 340-349 + AL, AL, AL, XX, AL, AL, AL, AL, AL, AL, // 350-359 + AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // 360-369 + TN, TN, TN, TN, TN, TN, TN, TN, TN, TN, // 370-379 + TN, TN, TN, TN, TN, TN, MS, MS, MS, MS, // 380-389 + MS, MS, MS, MS, MS, MS, MS, MS, GA, GA, // 390-399 + KY, KY, KY, KY, KY, KY, KY, KY, KY, KY, // 400-409 + KY, KY, KY, KY, KY, KY, KY, KY, KY, XX, // 410-419 + KY, KY, KY, KY, KY, KY, KY, KY, XX, XX, // 420-429 + OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 430-439 + OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 440-449 + OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 450-459 + IN, IN, IN, IN, IN, IN, IN, IN, IN, IN, // 460-469 + IN, IN, IN, IN, IN, IN, IN, IN, IN, IN, // 470-479 + MI, MI, MI, MI, MI, MI, MI, MI, MI, MI, // 480-489 + MI, MI, MI, MI, MI, MI, MI, MI, MI, MI, // 490-499 + IA, IA, IA, IA, IA, IA, IA, IA, IA, IA, // 500-509 + IA, IA, IA, IA, IA, IA, IA, XX, XX, XX, // 510-519 + IA, IA, IA, IA, IA, IA, IA, IA, IA, XX, // 520-529 + WI, WI, WI, XX, WI, WI, XX, WI, WI, WI, // 530-539 + WI, WI, WI, WI, WI, WI, WI, WI, WI, WI, // 540-549 + MN, MN, XX, MN, MN, MN, MN, MN, MN, MN, // 550-559 + MN, MN, MN, MN, MN, MN, MN, MN, XX, DC, // 560-569 + SD, SD, SD, SD, SD, SD, SD, SD, XX, XX, // 570-579 + ND, ND, ND, ND, ND, ND, ND, ND, ND, XX, // 580-589 + MT, MT, MT, MT, MT, MT, MT, MT, MT, MT, // 590-599 + IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, // 600-609 + IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, // 610-619 + IL, XX, IL, IL, IL, IL, IL, IL, IL, IL, // 620-629 + MO, MO, XX, MO, MO, MO, MO, MO, MO, MO, // 630-639 + MO, MO, XX, XX, MO, MO, MO, MO, MO, MO, // 640-649 + MO, MO, MO, MO, MO, MO, MO, MO, MO, XX, // 650-659 + KS, KS, KS, XX, KS, KS, KS, KS, KS, KS, // 660-669 + KS, KS, KS, KS, KS, KS, KS, KS, KS, KS, // 670-679 + NE, NE, XX, NE, NE, NE, NE, NE, NE, NE, // 680-689 + NE, NE, NE, NE, XX, XX, XX, XX, XX, XX, // 690-699 + LA, LA, XX, LA, LA, LA, LA, LA, LA, XX, // 700-709 + LA, LA, LA, LA, LA, XX, AR, AR, AR, AR, // 710-719 + AR, AR, AR, AR, AR, AR, AR, AR, AR, AR, // 720-729 + OK, OK, XX, TX, OK, OK, OK, OK, OK, OK, // 730-739 + OK, OK, XX, OK, OK, OK, OK, OK, OK, OK, // 740-749 + TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 750-759 + TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 760-769 + TX, XX, TX, TX, TX, TX, TX, TX, TX, TX, // 770-779 + TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 780-789 + TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 790-799 + CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, // 800-809 + CO, CO, CO, CO, CO, CO, CO, XX, XX, XX, // 810-819 + WY, WY, WY, WY, WY, WY, WY, WY, WY, WY, // 820-829 + WY, WY, ID, ID, ID, ID, ID, ID, ID, XX, // 830-839 + UT, UT, UT, UT, UT, UT, UT, UT, XX, XX, // 840-849 + AZ, AZ, AZ, AZ, XX, AZ, AZ, AZ, XX, AZ, // 850-859 + AZ, XX, XX, AZ, AZ, AZ, XX, XX, XX, XX, // 860-869 + NM, NM, NM, NM, NM, NM, XX, NM, NM, NM, // 870-879 + NM, NM, NM, NM, NM, TX, XX, XX, XX, NV, // 880-889 + NV, NV, XX, NV, NV, NV, XX, NV, NV, XX, // 890-899 + CA, CA, CA, CA, CA, CA, CA, CA, CA, XX, // 900-909 + CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 910-919 + CA, CA, CA, CA, CA, CA, CA, CA, CA, XX, // 920-929 + CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 930-939 + CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 940-949 + CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 950-959 + CA, CA, AP, AP, AP, AP, AP, HI, HI, GU, // 960-969 + OR, OR, OR, OR, OR, OR, OR, OR, OR, OR, // 970-979 + WA, WA, WA, WA, WA, WA, WA, XX, WA, WA, // 980-989 + WA, WA, WA, WA, WA, AK, AK, AK, AK, AK, // 990-999 + }; + + if (!word.begin || !word.end || (word.end - word.begin) < 3) + return false; + const char16* zipPtr = word.begin; + if (zipPtr[0] < '0' || zipPtr[0] > '9' || + zipPtr[1] < '0' || zipPtr[1] > '9' || + zipPtr[2] < '0' || zipPtr[2] > '9') + return false; + + int zip = zipPtr[0] - '0'; + zip *= 10; + zip += zipPtr[1] - '0'; + zip *= 10; + zip += zipPtr[2] - '0'; + return stateForZipPrefix[zip] == (int) state_index; +} + +static const char* s_rawStreetSuffixes[] = { + "allee", "alley", "ally", "aly", + "anex", "annex", "anx", "arc", "arcade", "av", "ave", "aven", "avenu", + "avenue", "avn", "avnue", "bayoo", "bayou", "bch", "beach", "bend", + "bg", "bgs", "blf", "blfs", "bluf", "bluff", "bluffs", "blvd", "bnd", + "bot", "bottm", "bottom", "boul", "boulevard", "boulv", "br", "branch", + "brdge", "brg", "bridge", "brk", "brks", "brnch", "brook", "brooks", + "btm", "burg", "burgs", "byp", "bypa", "bypas", "bypass", "byps", "byu", + "camp", "canyn", "canyon", "cape", "causeway", "causway", "cen", "cent", + "center", "centers", "centr", "centre", "cir", "circ", "circl", + "circle", "circles", "cirs", "ck", "clb", "clf", "clfs", "cliff", + "cliffs", "club", "cmn", "cmp", "cnter", "cntr", "cnyn", "common", + "cor", "corner", "corners", "cors", "course", "court", "courts", "cove", + "coves", "cp", "cpe", "cr", "crcl", "crcle", "crecent", "creek", "cres", + "crescent", "cresent", "crest", "crk", "crossing", "crossroad", + "crscnt", "crse", "crsent", "crsnt", "crssing", "crssng", "crst", "crt", + "cswy", "ct", "ctr", "ctrs", "cts", "curv", "curve", "cv", "cvs", "cyn", + "dale", "dam", "div", "divide", "dl", "dm", "dr", "driv", "drive", + "drives", "drs", "drv", "dv", "dvd", "est", "estate", "estates", "ests", + "exp", "expr", "express", "expressway", "expw", "expy", "ext", + "extension", "extensions", "extn", "extnsn", "exts", "fall", "falls", + "ferry", "field", "fields", "flat", "flats", "fld", "flds", "fls", + "flt", "flts", "ford", "fords", "forest", "forests", "forg", "forge", + "forges", "fork", "forks", "fort", "frd", "frds", "freeway", "freewy", + "frg", "frgs", "frk", "frks", "frry", "frst", "frt", "frway", "frwy", + "fry", "ft", "fwy", "garden", "gardens", "gardn", "gateway", "gatewy", + "gatway", "gdn", "gdns", "glen", "glens", "gln", "glns", "grden", + "grdn", "grdns", "green", "greens", "grn", "grns", "grov", "grove", + "groves", "grv", "grvs", "gtway", "gtwy", "harb", "harbor", "harbors", + "harbr", "haven", "havn", "hbr", "hbrs", "height", "heights", "hgts", + "highway", "highwy", "hill", "hills", "hiway", "hiwy", "hl", "hllw", + "hls", "hollow", "hollows", "holw", "holws", "hrbor", "ht", "hts", + "hvn", "hway", "hwy", "inlet", "inlt", "is", "island", "islands", + "isle", "isles", "islnd", "islnds", "iss", "jct", "jction", "jctn", + "jctns", "jcts", "junction", "junctions", "junctn", "juncton", "key", + "keys", "knl", "knls", "knol", "knoll", "knolls", "ky", "kys", "la", + "lake", "lakes", "land", "landing", "lane", "lanes", "lck", "lcks", + "ldg", "ldge", "lf", "lgt", "lgts", "light", "lights", "lk", "lks", + "ln", "lndg", "lndng", "loaf", "lock", "locks", "lodg", "lodge", "loop", + "loops", "mall", "manor", "manors", "mdw", "mdws", "meadow", "meadows", + "medows", "mews", "mill", "mills", "mission", "missn", "ml", "mls", + "mnr", "mnrs", "mnt", "mntain", "mntn", "mntns", "motorway", "mount", + "mountain", "mountains", "mountin", "msn", "mssn", "mt", "mtin", "mtn", + "mtns", "mtwy", "nck", "neck", "opas", "orch", "orchard", "orchrd", + "oval", "overpass", "ovl", "park", "parks", "parkway", "parkways", + "parkwy", "pass", "passage", "path", "paths", "pike", "pikes", "pine", + "pines", "pk", "pkway", "pkwy", "pkwys", "pky", "pl", "place", "plain", + "plaines", "plains", "plaza", "pln", "plns", "plz", "plza", "pne", + "pnes", "point", "points", "port", "ports", "pr", "prairie", "prarie", + "prk", "prr", "prt", "prts", "psge", "pt", "pts", "rad", "radial", + "radiel", "radl", "ramp", "ranch", "ranches", "rapid", "rapids", "rd", + "rdg", "rdge", "rdgs", "rds", "rest", "ridge", "ridges", "riv", "river", + "rivr", "rnch", "rnchs", "road", "roads", "route", "row", "rpd", "rpds", + "rst", "rte", "rue", "run", "rvr", "shl", "shls", "shoal", "shoals", + "shoar", "shoars", "shore", "shores", "shr", "shrs", "skwy", "skyway", + "smt", "spg", "spgs", "spng", "spngs", "spring", "springs", "sprng", + "sprngs", "spur", "spurs", "sq", "sqr", "sqre", "sqrs", "sqs", "squ", + "square", "squares", "st", "sta", "station", "statn", "stn", "str", + "stra", "strav", "strave", "straven", "stravenue", "stravn", "stream", + "street", "streets", "streme", "strm", "strt", "strvn", "strvnue", + "sts", "sumit", "sumitt", "summit", "ter", "terr", "terrace", + "throughway", "tpk", "tpke", "tr", "trace", "traces", "track", "tracks", + "trafficway", "trail", "trails", "trak", "trce", "trfy", "trk", "trks", + "trl", "trls", "trnpk", "trpk", "trwy", "tunel", "tunl", "tunls", + "tunnel", "tunnels", "tunnl", "turnpike", "turnpk", "un", "underpass", + "union", "unions", "uns", "upas", "valley", "valleys", "vally", "vdct", + "via", "viadct", "viaduct", "view", "views", "vill", "villag", + "village", "villages", "ville", "villg", "villiage", "vis", "vist", + "vista", "vl", "vlg", "vlgs", "vlly", "vly", "vlys", "vst", "vsta", + "vw", "vws", "walk", "walks", "wall", "way", "ways", "well", "wells", + "wl", "wls", "wy", "xing", "xrd", + 0, +}; + +bool AddressDetector::IsValidLocationName(const Word& word) { + using namespace WTF; + static HashSet<String> streetNames; + if (!streetNames.size()) { + const char** suffixes = s_rawStreetSuffixes; + while (const char* suffix = *suffixes) { + int index = suffix[0] - 'a'; + streetNames.add(suffix); + suffixes++; + } + } + char16 first_letter = base::ToLowerASCII(*word.begin); + if (first_letter > 'z' || first_letter < 'a') + return false; + int index = first_letter - 'a'; + int length = std::distance(word.begin, word.end); + if (*word.end == '.') + length--; + String value(word.begin, length); + return streetNames.contains(value.lower()); +} diff --git a/Source/WebKit/android/content/address_detector.h b/Source/WebKit/android/content/address_detector.h new file mode 100644 index 0000000..be34375 --- /dev/null +++ b/Source/WebKit/android/content/address_detector.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTENT_RENDERER_ANDROID_ADDRESS_DETECTOR_H_ +#define CONTENT_RENDERER_ANDROID_ADDRESS_DETECTOR_H_ +#pragma once + +#include "build/build_config.h" // Needed for OS_ANDROID + +#if defined(OS_ANDROID) + +#include <vector> + +#include "base/string_tokenizer.h" +#include "base/string_util.h" +#include "content/content_detector.h" + +// Finds a geographical address (currently US only) in the given text string. +class AddressDetector : public ContentDetector { + public: + AddressDetector(); + virtual ~AddressDetector(); + + // Implementation of ContentDetector. + virtual bool FindContent(const string16::const_iterator& begin, + const string16::const_iterator& end, + size_t* start_pos, + size_t* end_pos) OVERRIDE; + + private: + friend class AddressDetectorTest; + + virtual std::string GetContentText(const WebKit::WebRange& range) OVERRIDE; + virtual GURL GetIntentURL(const std::string& content_text) OVERRIDE; + virtual size_t GetMaximumContentLength() OVERRIDE; + + // Internal structs and classes. Required to be visible by the unit tests. + struct Word { + string16::const_iterator begin; + string16::const_iterator end; + + Word() {} + Word(const string16::const_iterator& begin_it, + const string16::const_iterator& end_it) + : begin(begin_it), + end(end_it) { + DCHECK(begin_it <= end_it); + } + }; + + class HouseNumberParser { + public: + HouseNumberParser() {} + + bool Parse(const string16::const_iterator& begin, + const string16::const_iterator& end, + Word* word); + + private: + static inline bool IsPreDelimiter(char16 character); + static inline bool IsPostDelimiter(char16 character); + inline void RestartOnNextDelimiter(); + + inline bool CheckFinished(Word* word) const; + inline void AcceptChars(size_t num_chars); + inline void SkipChars(size_t num_chars); + inline void ResetState(); + + // Iterators to the beginning, current position and ending of the string + // being currently parsed. + string16::const_iterator begin_; + string16::const_iterator it_; + string16::const_iterator end_; + + // Number of digits found in the current result candidate. + size_t num_digits_; + + // Number of characters previous to the current iterator that belong + // to the current result candidate. + size_t result_chars_; + + DISALLOW_COPY_AND_ASSIGN(HouseNumberParser); + }; + + typedef std::vector<Word> WordList; + typedef StringTokenizerT<string16, string16::const_iterator> + String16Tokenizer; + + static bool FindStateStartingInWord(WordList* words, + size_t state_first_word, + size_t* state_last_word, + String16Tokenizer* tokenizer, + size_t* state_index); + + static bool IsValidLocationName(const Word& word); + static bool IsZipValid(const Word& word, size_t state_index); + static bool IsZipValidForState(const Word& word, size_t state_index); + + DISALLOW_COPY_AND_ASSIGN(AddressDetector); +}; + +#endif // defined(OS_ANDROID) + +#endif // CONTENT_RENDERER_ANDROID_ADDRESS_DETECTOR_H_ diff --git a/Source/WebKit/android/content/content_detector.cpp b/Source/WebKit/android/content/content_detector.cpp new file mode 100644 index 0000000..b29a457 --- /dev/null +++ b/Source/WebKit/android/content/content_detector.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +// Magic pretend-to-be-a-chromium-build flags +#undef WEBKIT_IMPLEMENTATION +#undef LOG + +#include "content/content_detector.h" + +#include "public/android/WebDOMTextContentWalker.h" +#include "public/android/WebHitTestInfo.h" + +using WebKit::WebDOMTextContentWalker; +using WebKit::WebRange; + +ContentDetector::Result ContentDetector::FindTappedContent( + const WebKit::WebHitTestInfo& hit_test) { + WebKit::WebRange range = FindContentRange(hit_test); + if (range.isNull()) + return Result(); + + std::string text = GetContentText(range); + GURL intent_url = GetIntentURL(text); + return Result(range, text, intent_url); +} + +WebRange ContentDetector::FindContentRange( + const WebKit::WebHitTestInfo& hit_test) { + WebDOMTextContentWalker content_walker(hit_test, GetMaximumContentLength()); + string16 content = content_walker.content(); + if (content.empty()) + return WebRange(); + + size_t selected_offset = content_walker.hitOffsetInContent(); + for (size_t start_offset = 0; start_offset < content.length();) { + size_t relative_start, relative_end; + if (!FindContent(content.begin() + start_offset, + content.end(), &relative_start, &relative_end)) { + break; + } else { + size_t content_start = start_offset + relative_start; + size_t content_end = start_offset + relative_end; + DCHECK(content_end <= content.length()); + + if (selected_offset >= content_start && selected_offset < content_end) { + WebRange range = content_walker.contentOffsetsToRange( + content_start, content_end); + DCHECK(!range.isNull()); + return range; + } else { + start_offset += relative_end; + } + } + } + + return WebRange(); +} diff --git a/Source/WebKit/android/content/content_detector.h b/Source/WebKit/android/content/content_detector.h new file mode 100644 index 0000000..041cbc9 --- /dev/null +++ b/Source/WebKit/android/content/content_detector.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CONTENT_RENDERER_ANDROID_CONTENT_DETECTOR_H_ +#define CONTENT_RENDERER_ANDROID_CONTENT_DETECTOR_H_ +#pragma once + +#include "build/build_config.h" // Needed for OS_ANDROID + +#if defined(OS_ANDROID) + +#include "base/string_util.h" +#include "googleurl/src/gurl.h" +#include "public/WebRange.h" + +namespace WebKit { +class WebHitTestInfo; +} + +// Base class for text-based content detectors. +class ContentDetector { + public: + + // Holds the content detection results. + struct Result { + bool valid; // Flag indicating if the result is valid. + WebKit::WebRange range; // Range describing the content boundaries. + std::string text; // Processed text of the content. + GURL intent_url; // URL of the intent that should process this content. + + Result() : valid(false) {} + + Result(const WebKit::WebRange& range, + const std::string& text, + const GURL& intent_url) + : valid(true), + range(range), + text(text), + intent_url(intent_url) {} + }; + + virtual ~ContentDetector() {} + + // Returns a WebKit range delimiting the contents found around the tapped + // position. If no content is found a null range will be returned. + Result FindTappedContent(const WebKit::WebHitTestInfo& hit_test); + + protected: + // Parses the input string defined by the begin/end iterators returning true + // if the desired content is found. The start and end positions relative to + // the input iterators are returned in start_pos and end_pos. + // The end position is assumed to be non-inclusive. + virtual bool FindContent(const string16::const_iterator& begin, + const string16::const_iterator& end, + size_t* start_pos, + size_t* end_pos) = 0; + + // Extracts and processes the text of the detected content. + virtual std::string GetContentText(const WebKit::WebRange& range) = 0; + + // Returns the intent URL that should process the content, if any. + virtual GURL GetIntentURL(const std::string& content_text) = 0; + + // Returns the maximum length of text to be extracted around the tapped + // position in order to search for content. + virtual size_t GetMaximumContentLength() = 0; + + ContentDetector() {} + WebKit::WebRange FindContentRange(const WebKit::WebHitTestInfo& hit_test); + + DISALLOW_COPY_AND_ASSIGN(ContentDetector); +}; + +#endif // defined(OS_ANDROID) + +#endif // CONTENT_RENDERER_ANDROID_CONTENT_DETECTOR_H_ diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.cpp b/Source/WebKit/android/jni/AndroidHitTestResult.cpp index 6f94488..f5dcc48 100644 --- a/Source/WebKit/android/jni/AndroidHitTestResult.cpp +++ b/Source/WebKit/android/jni/AndroidHitTestResult.cpp @@ -28,6 +28,9 @@ #include "config.h" #include "AndroidHitTestResult.h" +#include "content/address_detector.h" +#include "content/PhoneEmailDetector.h" +#include "android/WebHitTestInfo.h" #include "Document.h" #include "Element.h" #include "Frame.h" @@ -63,6 +66,7 @@ static struct { jfieldID m_TapHighlightColor; jfieldID m_EnclosingParentRects; jfieldID m_HasFocus; + jfieldID m_IntentUrl; } gHitTestGlue; struct field { @@ -89,6 +93,7 @@ static void InitJni(JNIEnv* env) { hitTestClass, "mTouchRects", "[Landroid/graphics/Rect;", &gHitTestGlue.m_TouchRects }, { hitTestClass, "mEditable", "Z", &gHitTestGlue.m_Editable }, { hitTestClass, "mLinkUrl", "Ljava/lang/String;", &gHitTestGlue.m_LinkUrl }, + { hitTestClass, "mIntentUrl", "Ljava/lang/String;", &gHitTestGlue.m_IntentUrl }, { hitTestClass, "mAnchorText", "Ljava/lang/String;", &gHitTestGlue.m_AnchorText }, { hitTestClass, "mImageUrl", "Ljava/lang/String;", &gHitTestGlue.m_ImageUrl }, { hitTestClass, "mAltDisplayString", "Ljava/lang/String;", &gHitTestGlue.m_AltDisplayString }, @@ -135,13 +140,29 @@ void AndroidHitTestResult::buildHighlightRects() RenderObject* renderer = node->renderer(); Vector<FloatQuad> quads; renderer->absoluteFocusRingQuads(quads); - for (int i = 0; i < quads.size(); i++) { + for (size_t i = 0; i < quads.size(); i++) { IntRect boundingBox = quads[i].enclosingBoundingBox(); boundingBox.move(-frameOffset.x(), -frameOffset.y()); m_highlightRects.append(boundingBox); } } +void AndroidHitTestResult::searchContentDetectors() +{ + AddressDetector address; + PhoneEmailDetector phoneEmail; + WebKit::WebHitTestInfo webHitTest(m_hitTestResult); + m_searchResult = address.FindTappedContent(webHitTest); + if (!m_searchResult.valid) { + m_searchResult = phoneEmail.FindTappedContent(webHitTest); + } + if (m_searchResult.valid) { + m_highlightRects.clear(); + RefPtr<Range> range = (PassRefPtr<Range>) m_searchResult.range; + range->textRects(m_highlightRects, true); + } +} + void setStringField(JNIEnv* env, jobject obj, jfieldID field, const String& str) { jstring jstr = wtfStringToJstring(env, str, false); @@ -149,6 +170,13 @@ void setStringField(JNIEnv* env, jobject obj, jfieldID field, const String& str) env->DeleteLocalRef(jstr); } +void setStringField(JNIEnv* env, jobject obj, jfieldID field, const GURL& url) +{ + jstring jstr = stdStringToJstring(env, url.spec(), false); + env->SetObjectField(obj, field, jstr); + env->DeleteLocalRef(jstr); +} + void setRectArray(JNIEnv* env, jobject obj, jfieldID field, Vector<IntRect> &rects) { jobjectArray array = intRectVectorToRectArray(env, rects); @@ -176,6 +204,8 @@ jobject AndroidHitTestResult::createJavaObject(JNIEnv* env) SET_BOOL(Editable, m_hitTestResult.isContentEditable()); SET_STRING(LinkUrl, m_hitTestResult.absoluteLinkURL().string()); + if (m_searchResult.valid) + SET_STRING(IntentUrl, m_searchResult.intent_url); SET_STRING(ImageUrl, m_hitTestResult.absoluteImageURL().string()); SET_STRING(AltDisplayString, m_hitTestResult.altDisplayString()); TextDirection titleTextDirection; @@ -201,8 +231,8 @@ jobject AndroidHitTestResult::createJavaObject(JNIEnv* env) Vector<IntRect> AndroidHitTestResult::enclosingParentRects(Node* node) { - int lastX; int count = 0; + int lastX = 0; Vector<IntRect> rects; while (node && count < 5) { @@ -214,7 +244,7 @@ Vector<IntRect> AndroidHitTestResult::enclosingParentRects(Node* node) node->document()->frame()); IntRect rect = render->absoluteBoundingBoxRect(); rect.move(-frameOffset.x(), -frameOffset.y()); - if (rect.x() != lastX) { + if (count == 0 || rect.x() != lastX) { rects.append(rect); lastX = rect.x(); count++; diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.h b/Source/WebKit/android/jni/AndroidHitTestResult.h index f9709ac..5bbfc6b 100644 --- a/Source/WebKit/android/jni/AndroidHitTestResult.h +++ b/Source/WebKit/android/jni/AndroidHitTestResult.h @@ -26,6 +26,7 @@ #ifndef AndroidHitTestResult_h #define AndroidHitTestResult_h +#include "content/content_detector.h" #include "Element.h" #include "HitTestResult.h" #include "IntRect.h" @@ -48,6 +49,7 @@ public: void setURLElement(WebCore::Element* element); void buildHighlightRects(); + void searchContentDetectors(); jobject createJavaObject(JNIEnv*); @@ -57,6 +59,7 @@ private: WebViewCore* m_webViewCore; WebCore::HitTestResult m_hitTestResult; Vector<WebCore::IntRect> m_highlightRects; + ContentDetector::Result m_searchResult; }; } // namespace android diff --git a/Source/WebKit/android/jni/CacheManager.cpp b/Source/WebKit/android/jni/CacheManager.cpp index d319054..b34776d 100644 --- a/Source/WebKit/android/jni/CacheManager.cpp +++ b/Source/WebKit/android/jni/CacheManager.cpp @@ -25,8 +25,6 @@ #include "config.h" -#if USE(CHROME_NETWORK_STACK) - #include "ChromiumIncludes.h" #include "WebCache.h" #include "WebCoreJni.h" @@ -140,5 +138,3 @@ int registerCacheManager(JNIEnv* env) } } // namespace android - -#endif // USE(CHROME_NETWORK_STACK) diff --git a/Source/WebKit/android/jni/CookieManager.cpp b/Source/WebKit/android/jni/CookieManager.cpp index 357d158..fca612f 100644 --- a/Source/WebKit/android/jni/CookieManager.cpp +++ b/Source/WebKit/android/jni/CookieManager.cpp @@ -40,49 +40,30 @@ static const char* javaCookieManagerClass = "android/webkit/CookieManager"; static bool acceptCookie(JNIEnv*, jobject) { -#if USE(CHROME_NETWORK_STACK) // This is a static method which gets the cookie policy for all WebViews. We // always apply the same configuration to the contexts for both regular and // private browsing, so expect the same result here. bool regularAcceptCookies = WebCookieJar::get(false)->allowCookies(); ASSERT(regularAcceptCookies == WebCookieJar::get(true)->allowCookies()); return regularAcceptCookies; -#else - // The Android HTTP stack is implemented Java-side. - ASSERT_NOT_REACHED(); - return false; -#endif } static jstring getCookie(JNIEnv* env, jobject, jstring url, jboolean privateBrowsing) { -#if USE(CHROME_NETWORK_STACK) GURL gurl(jstringToStdString(env, url)); CookieOptions options; options.set_include_httponly(); std::string cookies = WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->GetCookiesWithOptions(gurl, options); return stdStringToJstring(env, cookies); -#else - // The Android HTTP stack is implemented Java-side. - ASSERT_NOT_REACHED(); - return jstring(); -#endif } static bool hasCookies(JNIEnv*, jobject, jboolean privateBrowsing) { -#if USE(CHROME_NETWORK_STACK) return WebCookieJar::get(privateBrowsing)->getNumCookiesInDatabase() > 0; -#else - // The Android HTTP stack is implemented Java-side. - ASSERT_NOT_REACHED(); - return false; -#endif } static void removeAllCookie(JNIEnv*, jobject) { -#if USE(CHROME_NETWORK_STACK) WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->DeleteAll(true); // This will lazily create a new private browsing context. However, if the // context doesn't already exist, there's no need to create it, as cookies @@ -94,84 +75,62 @@ static void removeAllCookie(JNIEnv*, jobject) // The Java code removes cookies directly from the backing database, so we do the same, // but with a NULL callback so it's asynchronous. WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->FlushStore(NULL); -#endif } static void removeExpiredCookie(JNIEnv*, jobject) { -#if USE(CHROME_NETWORK_STACK) // This simply forces a GC. The getters delete expired cookies so won't return expired cookies anyway. WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->GetAllCookies(); WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->GetAllCookies(); -#endif } static void removeSessionCookies(WebCookieJar* cookieJar) { -#if USE(CHROME_NETWORK_STACK) CookieMonster* cookieMonster = cookieJar->cookieStore()->GetCookieMonster(); CookieList cookies = cookieMonster->GetAllCookies(); for (CookieList::const_iterator iter = cookies.begin(); iter != cookies.end(); ++iter) { if (iter->IsSessionCookie()) cookieMonster->DeleteCanonicalCookie(*iter); } -#endif } static void removeSessionCookie(JNIEnv*, jobject) { -#if USE(CHROME_NETWORK_STACK) removeSessionCookies(WebCookieJar::get(false)); removeSessionCookies(WebCookieJar::get(true)); -#endif } static void setAcceptCookie(JNIEnv*, jobject, jboolean accept) { -#if USE(CHROME_NETWORK_STACK) // This is a static method which configures the cookie policy for all // WebViews, so we configure the contexts for both regular and private // browsing. WebCookieJar::get(false)->setAllowCookies(accept); WebCookieJar::get(true)->setAllowCookies(accept); -#endif } static void setCookie(JNIEnv* env, jobject, jstring url, jstring value, jboolean privateBrowsing) { -#if USE(CHROME_NETWORK_STACK) GURL gurl(jstringToStdString(env, url)); std::string line(jstringToStdString(env, value)); CookieOptions options; options.set_include_httponly(); WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->SetCookieWithOptions(gurl, line, options); -#endif } static void flushCookieStore(JNIEnv*, jobject) { -#if USE(CHROME_NETWORK_STACK) WebCookieJar::flush(); -#endif } static bool acceptFileSchemeCookies(JNIEnv*, jobject) { -#if USE(CHROME_NETWORK_STACK) return WebCookieJar::acceptFileSchemeCookies(); -#else - // File scheme cookies are always accepted with the Android HTTP stack. - return true; -#endif } static void setAcceptFileSchemeCookies(JNIEnv*, jobject, jboolean accept) { -#if USE(CHROME_NETWORK_STACK) WebCookieJar::setAcceptFileSchemeCookies(accept); -#else - // File scheme cookies are always accepted with the Android HTTP stack. -#endif } static JNINativeMethod gCookieManagerMethods[] = { diff --git a/Source/WebKit/android/jni/JavaBridge.cpp b/Source/WebKit/android/jni/JavaBridge.cpp index 204ac4e..4c8234b 100644 --- a/Source/WebKit/android/jni/JavaBridge.cpp +++ b/Source/WebKit/android/jni/JavaBridge.cpp @@ -472,12 +472,10 @@ void JavaBridge::RemovePackageName(JNIEnv* env, jobject obj, jstring packageName void JavaBridge::UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy, jstring newExList) { -#if USE(CHROME_NETWORK_STACK) std::string proxy = jstringToStdString(env, newProxy); std::string exList = jstringToStdString(env, newExList); WebCache::get(false)->proxy()->UpdateProxySettings(proxy, exList); WebCache::get(true)->proxy()->UpdateProxySettings(proxy, exList); -#endif } diff --git a/Source/WebKit/android/jni/JniUtil.cpp b/Source/WebKit/android/jni/JniUtil.cpp deleted file mode 100644 index 651016e..0000000 --- a/Source/WebKit/android/jni/JniUtil.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2010, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#include "ChromiumIncludes.h" -#include <JNIHelp.h> - -namespace android { - -static const char* javaJniUtilClass = "android/webkit/JniUtil"; - -static bool useChromiumHttpStack(JNIEnv*, jobject) -{ -#if USE(CHROME_NETWORK_STACK) - return true; -#else - return false; -#endif -} - -static JNINativeMethod gJniUtilMethods[] = { - { "nativeUseChromiumHttpStack", "()Z", (void*) useChromiumHttpStack }, -}; - -int registerJniUtil(JNIEnv* env) -{ -#ifndef NDEBUG - jclass jniUtil = env->FindClass(javaJniUtilClass); - ALOG_ASSERT(jniUtil, "Unable to find class"); - env->DeleteLocalRef(jniUtil); -#endif - return jniRegisterNativeMethods(env, javaJniUtilClass, gJniUtilMethods, NELEM(gJniUtilMethods)); -} - -} // namespace android diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp index 2724d6b..d0f3830 100644 --- a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp +++ b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -83,7 +83,6 @@ #include "WebArchiveAndroid.h" #include "WebCache.h" #include "WebCoreJni.h" -#include "WebCoreResourceLoader.h" #include "WebHistory.h" #include "WebIconDatabase.h" #include "WebFrameView.h" @@ -229,8 +228,6 @@ WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* mJavaFrame = new JavaBrowserFrame; mJavaFrame->mObj = env->NewWeakGlobalRef(obj); mJavaFrame->mHistoryList = env->NewWeakGlobalRef(historyList); - mJavaFrame->mStartLoadingResource = env->GetMethodID(clazz, "startLoadingResource", - "(ILjava/lang/String;Ljava/lang/String;Ljava/util/HashMap;[BJIZZZLjava/lang/String;Ljava/lang/String;)Landroid/webkit/LoadListener;"); mJavaFrame->mMaybeSavePassword = env->GetMethodID(clazz, "maybeSavePassword", "([BLjava/lang/String;Ljava/lang/String;)V"); mJavaFrame->mShouldInterceptRequest = @@ -286,7 +283,6 @@ WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); env->DeleteLocalRef(clazz); - ALOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource"); ALOG_ASSERT(mJavaFrame->mMaybeSavePassword, "Could not find method maybeSavePassword"); ALOG_ASSERT(mJavaFrame->mShouldInterceptRequest, "Could not find method shouldInterceptRequest"); ALOG_ASSERT(mJavaFrame->mLoadStarted, "Could not find method loadStarted"); @@ -401,94 +397,6 @@ private: int m_size; }; -PassRefPtr<WebCore::ResourceLoaderAndroid> -WebFrame::startLoadingResource(WebCore::ResourceHandle* loader, - const WebCore::ResourceRequest& request, - bool mainResource, - bool synchronous) -{ - ALOGV("::WebCore:: startLoadingResource(%p, %s)", - loader, request.url().string().latin1().data()); - - JNIEnv* env = getJNIEnv(); - AutoJObject javaFrame = mJavaFrame->frame(env); - if (!javaFrame.get()) - return 0; - - WTF::String method = request.httpMethod(); - WebCore::HTTPHeaderMap headers = request.httpHeaderFields(); - - WTF::String urlStr = request.url().string(); - int colon = urlStr.find(':'); - bool allLower = true; - for (int index = 0; index < colon; index++) { - UChar ch = urlStr[index]; - if (!WTF::isASCIIAlpha(ch)) - break; - allLower &= WTF::isASCIILower(ch); - if (index == colon - 1 && !allLower) { - urlStr = urlStr.substring(0, colon).lower() - + urlStr.substring(colon); - } - } - ALOGV("%s lower=%s", __FUNCTION__, urlStr.latin1().data()); - jstring jUrlStr = wtfStringToJstring(env, urlStr); - jstring jMethodStr = NULL; - if (!method.isEmpty()) - jMethodStr = wtfStringToJstring(env, method); - WebCore::FormData* formdata = request.httpBody(); - jbyteArray jPostDataStr = getPostData(request); - jobject jHeaderMap = createJavaMapFromHTTPHeaders(env, headers); - - // Convert the WebCore Cache Policy to a WebView Cache Policy. - int cacheMode = 0; // WebSettings.LOAD_NORMAL - switch (request.cachePolicy()) { - case WebCore::ReloadIgnoringCacheData: - cacheMode = 2; // WebSettings.LOAD_NO_CACHE - break; - case WebCore::ReturnCacheDataDontLoad: - cacheMode = 3; // WebSettings.LOAD_CACHE_ONLY - break; - case WebCore::ReturnCacheDataElseLoad: - cacheMode = 1; // WebSettings.LOAD_CACHE_ELSE_NETWORK - break; - case WebCore::UseProtocolCachePolicy: - default: - break; - } - - ALOGV("::WebCore:: startLoadingResource %s with cacheMode %d", urlStr.ascii().data(), cacheMode); - - ResourceHandleInternal* loaderInternal = loader->getInternal(); - jstring jUsernameString = loaderInternal->m_user.isEmpty() ? - NULL : wtfStringToJstring(env, loaderInternal->m_user); - jstring jPasswordString = loaderInternal->m_pass.isEmpty() ? - NULL : wtfStringToJstring(env, loaderInternal->m_pass); - - bool isUserGesture = UserGestureIndicator::processingUserGesture(); - jobject jLoadListener = - env->CallObjectMethod(javaFrame.get(), mJavaFrame->mStartLoadingResource, - (int)loader, jUrlStr, jMethodStr, jHeaderMap, - jPostDataStr, formdata ? formdata->identifier(): 0, - cacheMode, mainResource, isUserGesture, - synchronous, jUsernameString, jPasswordString); - - env->DeleteLocalRef(jUrlStr); - env->DeleteLocalRef(jMethodStr); - env->DeleteLocalRef(jPostDataStr); - env->DeleteLocalRef(jHeaderMap); - env->DeleteLocalRef(jUsernameString); - env->DeleteLocalRef(jPasswordString); - if (checkException(env)) - return 0; - - RefPtr<WebCore::ResourceLoaderAndroid> h; - if (jLoadListener) - h = WebCoreResourceLoader::create(env, jLoadListener); - env->DeleteLocalRef(jLoadListener); - return h; -} - UrlInterceptResponse* WebFrame::shouldInterceptRequest(const WTF::String& url) { @@ -529,7 +437,6 @@ WebFrame::reportError(int errorCode, const WTF::String& description, WTF::String WebFrame::convertIDNToUnicode(const WebCore::KURL& url) { WTF::String converted = url.string(); -#if USE(CHROME_NETWORK_STACK) const WTF::String host = url.host(); if (host.find("xn--") == notFound) // no punycode IDN found. return converted; @@ -542,7 +449,6 @@ WebFrame::convertIDNToUnicode(const WebCore::KURL& url) { newUrl.setHost(convertedHost); converted = newUrl.string(); } -#endif return converted; } @@ -886,7 +792,6 @@ WebFrame::density() const return dpi; } -#if USE(CHROME_NETWORK_STACK) void WebFrame::didReceiveAuthenticationChallenge(WebUrlLoaderClient* client, const std::string& host, const std::string& realm, bool useCachedCredentials, bool suppressDialog) { @@ -998,7 +903,6 @@ void WebFrame::setCertificate(const std::string& cert) checkException(env); } -#endif // USE(CHROME_NETWORK_STACK) void WebFrame::autoLogin(const std::string& loginHeader) { @@ -1188,10 +1092,8 @@ static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAss { ScriptController::initializeThreading(); -#if USE(CHROME_NETWORK_STACK) // needs to be called before any other chromium code initChromium(); -#endif // Create a new page ChromeClientAndroid* chromeC = new ChromeClientAndroid; @@ -1722,12 +1624,7 @@ static void ClearWebCoreCache() static void ClearWebViewCache() { -#if USE(CHROME_NETWORK_STACK) WebCache::get(false /*privateBrowsing*/)->clear(); -#else - // The Android network stack provides a WebView cache in CacheManager.java. - // Clearing this is handled entirely Java-side. -#endif } static void ClearCache(JNIEnv *env, jobject obj) @@ -1902,8 +1799,6 @@ static jboolean GetShouldStartScrolledRight(JNIEnv *env, jobject obj, return startScrolledRight; } -#if USE(CHROME_NETWORK_STACK) - static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword) { WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle); @@ -2008,34 +1903,6 @@ static void SslClientCert(JNIEnv *env, jobject obj, int handle, jbyteArray pkey, client->sslClientCert(privateKey.release(), certificate); } -#else - -static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword) -{ - ALOGW("Chromium authentication API called, but libchromium is not available"); -} - -static void AuthenticationCancel(JNIEnv *env, jobject obj, int handle) -{ - ALOGW("Chromium authentication API called, but libchromium is not available"); -} - -static void SslCertErrorProceed(JNIEnv *env, jobject obj, int handle) -{ - ALOGW("Chromium SSL API called, but libchromium is not available"); -} - -static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_error) -{ - ALOGW("Chromium SSL API called, but libchromium is not available"); -} - -static void SslClientCert(JNIEnv *env, jobject obj, int handle, jbyteArray privateKey, jobjectArray chain) -{ - ALOGW("Chromium SSL API called, but libchromium is not available"); -} -#endif // USE(CHROME_NETWORK_STACK) - // ---------------------------------------------------------------------------- /* diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.h b/Source/WebKit/android/jni/WebCoreFrameBridge.h index eaee63c..13f99af 100644 --- a/Source/WebKit/android/jni/WebCoreFrameBridge.h +++ b/Source/WebKit/android/jni/WebCoreFrameBridge.h @@ -64,10 +64,6 @@ class WebFrame : public WebCoreRefObject { // helper function static WebFrame* getWebFrame(const WebCore::Frame* frame); - virtual PassRefPtr<WebCore::ResourceLoaderAndroid> startLoadingResource(WebCore::ResourceHandle*, - const WebCore::ResourceRequest& request, bool mainResource, - bool synchronous); - UrlInterceptResponse* shouldInterceptRequest(const WTF::String& url); void reportError(int errorCode, const WTF::String& description, @@ -117,7 +113,6 @@ class WebFrame : public WebCoreRefObject { float density() const; -#if USE(CHROME_NETWORK_STACK) void didReceiveAuthenticationChallenge(WebUrlLoaderClient*, const std::string& host, const std::string& realm, bool useCachedCredentials, bool suppressDialog); void reportSslCertError(WebUrlLoaderClient* client, int cert_error, const std::string& cert, const std::string& url); void requestClientCert(WebUrlLoaderClient* client, const std::string& hostAndPort); @@ -125,7 +120,6 @@ class WebFrame : public WebCoreRefObject { void didReceiveData(const char* data, int size); void didFinishLoading(); void setCertificate(const std::string& cert); -#endif void maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request); diff --git a/Source/WebKit/android/jni/WebCoreJni.cpp b/Source/WebKit/android/jni/WebCoreJni.cpp index 65b6d20..5a142c8 100644 --- a/Source/WebKit/android/jni/WebCoreJni.cpp +++ b/Source/WebKit/android/jni/WebCoreJni.cpp @@ -79,8 +79,6 @@ jstring wtfStringToJstring(JNIEnv* env, const WTF::String& str, bool validOnZero return length || validOnZeroLength ? env->NewString(str.characters(), length) : 0; } - -#if USE(CHROME_NETWORK_STACK) string16 jstringToString16(JNIEnv* env, jstring jstr) { if (!jstr || !env) @@ -114,8 +112,6 @@ jstring stdStringToJstring(JNIEnv* env, const std::string& str, bool validOnZero return !str.empty() || validOnZeroLength ? env->NewStringUTF(str.c_str()) : 0; } -#endif - jobjectArray intRectVectorToRectArray(JNIEnv* env, Vector<WebCore::IntRect>& rects) { jclass rectClass = env->FindClass("android/graphics/Rect"); diff --git a/Source/WebKit/android/jni/WebCoreJni.h b/Source/WebKit/android/jni/WebCoreJni.h index 7a46f7b..25aa986 100644 --- a/Source/WebKit/android/jni/WebCoreJni.h +++ b/Source/WebKit/android/jni/WebCoreJni.h @@ -82,7 +82,6 @@ WTF::String jstringToWtfString(JNIEnv*, jstring); // an empty WTF String returns 0. jstring wtfStringToJstring(JNIEnv*, const WTF::String&, bool validOnZeroLength = false); -#if USE(CHROME_NETWORK_STACK) string16 jstringToString16(JNIEnv*, jstring); std::string jstringToStdString(JNIEnv*, jstring); @@ -90,7 +89,6 @@ std::string jstringToStdString(JNIEnv*, jstring); // passing in an empty std::string will result in an empty jstring. Otherwise // an empty std::string returns 0. jstring stdStringToJstring(JNIEnv*, const std::string&, bool validOnZeroLength = false); -#endif jobjectArray intRectVectorToRectArray(JNIEnv*, Vector<WebCore::IntRect>&); diff --git a/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp index 64aeb7e..2644dab 100644 --- a/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp +++ b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp @@ -73,7 +73,6 @@ namespace android { extern int registerWebFrame(JNIEnv*); extern int registerJavaBridge(JNIEnv*); -extern int registerJniUtil(JNIEnv*); extern int registerResourceLoader(JNIEnv*); extern int registerWebViewCore(JNIEnv*); extern int registerWebHistory(JNIEnv*); @@ -92,9 +91,7 @@ extern int registerMediaPlayerVideo(JNIEnv*); #endif extern int registerDeviceMotionAndOrientationManager(JNIEnv*); extern int registerCookieManager(JNIEnv*); -#if USE(CHROME_NETWORK_STACK) extern int registerCacheManager(JNIEnv*); -#endif } @@ -105,9 +102,7 @@ struct RegistrationMethod { static RegistrationMethod gWebCoreRegMethods[] = { { "JavaBridge", android::registerJavaBridge }, - { "JniUtil", android::registerJniUtil }, { "WebFrame", android::registerWebFrame }, - { "WebCoreResourceLoader", android::registerResourceLoader }, { "WebViewCore", android::registerWebViewCore }, { "WebHistory", android::registerWebHistory }, { "WebIconDatabase", android::registerWebIconDatabase }, @@ -125,9 +120,7 @@ static RegistrationMethod gWebCoreRegMethods[] = { #endif { "DeviceMotionAndOrientationManager", android::registerDeviceMotionAndOrientationManager }, { "CookieManager", android::registerCookieManager }, -#if USE(CHROME_NETWORK_STACK) { "CacheManager", android::registerCacheManager }, -#endif }; EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) diff --git a/Source/WebKit/android/jni/WebCoreResourceLoader.cpp b/Source/WebKit/android/jni/WebCoreResourceLoader.cpp deleted file mode 100644 index f0861ff..0000000 --- a/Source/WebKit/android/jni/WebCoreResourceLoader.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define LOG_TAG "webcoreglue" - -#include "config.h" -#include "WebCoreResourceLoader.h" - -#include "ResourceError.h" -#include "ResourceHandle.h" -#include "ResourceHandleClient.h" -#include "ResourceHandleInternal.h" -#include "ResourceResponse.h" -#include "SkUtils.h" -#include "WebCoreJni.h" - -#include <JNIHelp.h> -#include <JNIUtility.h> -#include <SkTypes.h> -#include <stdlib.h> -#include <utils/misc.h> -#include <wtf/Platform.h> -#include <wtf/text/CString.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -static struct resourceloader_t { - jfieldID mObject; - jmethodID mCancelMethodID; - jmethodID mDownloadFileMethodID; - jmethodID mWillLoadFromCacheMethodID; - jmethodID mPauseLoadMethodID; -} gResourceLoader; - -// ---------------------------------------------------------------------------- - -#define GET_NATIVE_HANDLE(env, obj) ((WebCore::ResourceHandle*)env->GetIntField(obj, gResourceLoader.mObject)) -#define SET_NATIVE_HANDLE(env, obj, handle) (env->SetIntField(obj, gResourceLoader.mObject, handle)) - -//----------------------------------------------------------------------------- -// ResourceLoadHandler - -PassRefPtr<WebCore::ResourceLoaderAndroid> WebCoreResourceLoader::create(JNIEnv *env, jobject jLoadListener) -{ - return adoptRef<WebCore::ResourceLoaderAndroid>(new WebCoreResourceLoader(env, jLoadListener)); -} - -WebCoreResourceLoader::WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener) - : mPausedLoad(false) -{ - mJLoader = env->NewGlobalRef(jLoadListener); -} - -WebCoreResourceLoader::~WebCoreResourceLoader() -{ - JNIEnv* env = JSC::Bindings::getJNIEnv(); - SET_NATIVE_HANDLE(env, mJLoader, 0); - env->DeleteGlobalRef(mJLoader); - mJLoader = 0; -} - -void WebCoreResourceLoader::cancel() -{ - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(mJLoader, gResourceLoader.mCancelMethodID); - checkException(env); -} - -void WebCoreResourceLoader::downloadFile() -{ - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(mJLoader, gResourceLoader.mDownloadFileMethodID); - checkException(env); -} - -void WebCoreResourceLoader::pauseLoad(bool pause) -{ - if (mPausedLoad == pause) - return; - - mPausedLoad = pause; - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(mJLoader, gResourceLoader.mPauseLoadMethodID, pause); - checkException(env); -} - -/* -* This static method is called to check to see if a POST response is in -* the cache. This may be slow, but is only used during a navigation to -* a POST response. -*/ -bool WebCoreResourceLoader::willLoadFromCache(const WebCore::KURL& url, int64_t identifier) -{ - JNIEnv* env = JSC::Bindings::getJNIEnv(); - WTF::String urlStr = url.string(); - jstring jUrlStr = wtfStringToJstring(env, urlStr); - jclass resourceLoader = env->FindClass("android/webkit/LoadListener"); - bool val = env->CallStaticBooleanMethod(resourceLoader, gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr, identifier); - checkException(env); - env->DeleteLocalRef(resourceLoader); - env->DeleteLocalRef(jUrlStr); - - return val; -} - -// ---------------------------------------------------------------------------- -void WebCoreResourceLoader::SetResponseHeader(JNIEnv* env, jobject obj, jint nativeResponse, jstring key, jstring val) -{ - WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse; - ALOG_ASSERT(response, "nativeSetResponseHeader must take a valid response pointer!"); - - ALOG_ASSERT(key, "How did a null value become a key?"); - if (val) - response->setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, val)); -} - -jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode, - jstring statusText, jstring mimeType, jlong expectedLength, - jstring encoding) -{ - ALOG_ASSERT(url, "Must have a url in the response!"); - WebCore::KURL kurl(WebCore::ParsedURLString, jstringToWtfString(env, url)); - WTF::String encodingStr; - WTF::String mimeTypeStr; - if (mimeType) { - mimeTypeStr = jstringToWtfString(env, mimeType); - ALOGV("Response setMIMEType: %s", mimeTypeStr.latin1().data()); - } - if (encoding) { - encodingStr = jstringToWtfString(env, encoding); - ALOGV("Response setTextEncodingName: %s", encodingStr.latin1().data()); - } - WebCore::ResourceResponse* response = new WebCore::ResourceResponse( - kurl, mimeTypeStr, (long long)expectedLength, - encodingStr, WTF::String()); - response->setHTTPStatusCode(statusCode); - if (statusText) { - WTF::String status = jstringToWtfString(env, statusText); - response->setHTTPStatusText(status); - ALOGV("Response setStatusText: %s", status.latin1().data()); - } - return (int)response; -} - -void WebCoreResourceLoader::ReceivedResponse(JNIEnv* env, jobject obj, jint nativeResponse) -{ - WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); - ALOG_ASSERT(handle, "nativeReceivedResponse must take a valid handle!"); - // ResourceLoader::didFail() can set handle to be NULL, we need to check - if (!handle) - return; - - WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse; - ALOG_ASSERT(response, "nativeReceivedResponse must take a valid resource pointer!"); - handle->client()->didReceiveResponse(handle, *response); - // As the client makes a copy of the response, delete it here. - delete response; -} - -void WebCoreResourceLoader::AddData(JNIEnv* env, jobject obj, jbyteArray dataArray, jint length) -{ - ALOGV("webcore_resourceloader data(%d)", length); - - WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); - ALOG_ASSERT(handle, "nativeAddData must take a valid handle!"); - // ResourceLoader::didFail() can set handle to be NULL, we need to check - if (!handle) - return; - - SkAutoMemoryUsageProbe mup("android_webcore_resourceloader_nativeAddData"); - - bool result = false; - jbyte * data = env->GetByteArrayElements(dataArray, NULL); - - ALOG_ASSERT(handle->client(), "Why do we not have a client?"); - handle->client()->didReceiveData(handle, (const char *)data, length, length); - env->ReleaseByteArrayElements(dataArray, data, JNI_ABORT); -} - -void WebCoreResourceLoader::Finished(JNIEnv* env, jobject obj) -{ - ALOGV("webcore_resourceloader finished"); - WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); - ALOG_ASSERT(handle, "nativeFinished must take a valid handle!"); - // ResourceLoader::didFail() can set handle to be NULL, we need to check - if (!handle) - return; - - ALOG_ASSERT(handle->client(), "Why do we not have a client?"); - handle->client()->didFinishLoading(handle, 0); -} - -jstring WebCoreResourceLoader::RedirectedToUrl(JNIEnv* env, jobject obj, - jstring baseUrl, jstring redirectTo, jint nativeResponse) -{ - ALOGV("webcore_resourceloader redirectedToUrl"); - WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); - ALOG_ASSERT(handle, "nativeRedirectedToUrl must take a valid handle!"); - // ResourceLoader::didFail() can set handle to be NULL, we need to check - if (!handle) - return NULL; - - ALOG_ASSERT(handle->client(), "Why do we not have a client?"); - WebCore::ResourceRequest r = handle->firstRequest(); - WebCore::KURL url(WebCore::KURL(WebCore::ParsedURLString, jstringToWtfString(env, baseUrl)), - jstringToWtfString(env, redirectTo)); - WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse; - // If the url fails to resolve the relative path, return null. - if (url.protocol().isEmpty()) { - delete response; - return NULL; - } else { - // Ensure the protocol is lowercase. - url.setProtocol(url.protocol().lower()); - } - // Set the url after updating the protocol. - r.setURL(url); - if (r.httpMethod() == "POST") { - r.setHTTPMethod("GET"); - r.clearHTTPReferrer(); - r.setHTTPBody(0); - r.setHTTPContentType(""); - } - handle->client()->willSendRequest(handle, r, *response); - delete response; - return wtfStringToJstring(env, url.string()); -} - -void WebCoreResourceLoader::Error(JNIEnv* env, jobject obj, jint id, jstring description, - jstring failingUrl) -{ - ALOGV("webcore_resourceloader error"); - WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); - ALOG_ASSERT(handle, "nativeError must take a valid handle!"); - // ResourceLoader::didFail() can set handle to be NULL, we need to check - if (!handle) - return; - - handle->client()->didFail(handle, WebCore::ResourceError("", id, - jstringToWtfString(env, failingUrl), jstringToWtfString(env, description))); -} - -// ---------------------------------------------------------------------------- - -/* - * JNI registration. - */ -static JNINativeMethod gResourceloaderMethods[] = { - /* name, signature, funcPtr */ - { "nativeSetResponseHeader", "(ILjava/lang/String;Ljava/lang/String;)V", - (void*) WebCoreResourceLoader::SetResponseHeader }, - { "nativeCreateResponse", "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLjava/lang/String;)I", - (void*) WebCoreResourceLoader::CreateResponse }, - { "nativeReceivedResponse", "(I)V", - (void*) WebCoreResourceLoader::ReceivedResponse }, - { "nativeAddData", "([BI)V", - (void*) WebCoreResourceLoader::AddData }, - { "nativeFinished", "()V", - (void*) WebCoreResourceLoader::Finished }, - { "nativeRedirectedToUrl", "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;", - (void*) WebCoreResourceLoader::RedirectedToUrl }, - { "nativeError", "(ILjava/lang/String;Ljava/lang/String;)V", - (void*) WebCoreResourceLoader::Error } -}; - -int registerResourceLoader(JNIEnv* env) -{ - jclass resourceLoader = env->FindClass("android/webkit/LoadListener"); - LOG_FATAL_IF(resourceLoader == NULL, - "Unable to find class android/webkit/LoadListener"); - - gResourceLoader.mObject = - env->GetFieldID(resourceLoader, "mNativeLoader", "I"); - LOG_FATAL_IF(gResourceLoader.mObject == NULL, - "Unable to find android/webkit/LoadListener.mNativeLoader"); - - gResourceLoader.mCancelMethodID = - env->GetMethodID(resourceLoader, "cancel", "()V"); - LOG_FATAL_IF(gResourceLoader.mCancelMethodID == NULL, - "Could not find method cancel on LoadListener"); - - gResourceLoader.mDownloadFileMethodID = - env->GetMethodID(resourceLoader, "downloadFile", "()V"); - LOG_FATAL_IF(gResourceLoader.mDownloadFileMethodID == NULL, - "Could not find method downloadFile on LoadListener"); - - gResourceLoader.mPauseLoadMethodID = - env->GetMethodID(resourceLoader, "pauseLoad", "(Z)V"); - LOG_FATAL_IF(gResourceLoader.mPauseLoadMethodID == NULL, - "Could not find method pauseLoad on LoadListener"); - - gResourceLoader.mWillLoadFromCacheMethodID = - env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;J)Z"); - LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL, - "Could not find static method willLoadFromCache on LoadListener"); - - env->DeleteLocalRef(resourceLoader); - - return jniRegisterNativeMethods(env, "android/webkit/LoadListener", - gResourceloaderMethods, NELEM(gResourceloaderMethods)); -} - -} /* namespace android */ diff --git a/Source/WebKit/android/jni/WebCoreResourceLoader.h b/Source/WebKit/android/jni/WebCoreResourceLoader.h deleted file mode 100644 index 0e34a5b..0000000 --- a/Source/WebKit/android/jni/WebCoreResourceLoader.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WebCoreResourceLoader_h -#define WebCoreResourceLoader_h - -#include <KURL.h> -#include <ResourceLoaderAndroid.h> -#include <jni.h> - -namespace android { - -class WebCoreResourceLoader : public WebCore::ResourceLoaderAndroid -{ -public: - static PassRefPtr<WebCore::ResourceLoaderAndroid> create(JNIEnv *env, jobject jLoadListener); - virtual ~WebCoreResourceLoader(); - - /** - * Call to java to cancel the current load. - */ - virtual void cancel(); - - /** - * Call to java to download the current load rather than feed it - * back to WebCore - */ - virtual void downloadFile(); - - virtual void pauseLoad(bool); - - /** - * Call to java to find out if this URL is in the cache - */ - static bool willLoadFromCache(const WebCore::KURL& url, int64_t identifier); - - // Native jni functions - static void SetResponseHeader(JNIEnv*, jobject, jint, jstring, jstring); - static jint CreateResponse(JNIEnv*, jobject, jstring, jint, jstring, - jstring, jlong, jstring); - static void ReceivedResponse(JNIEnv*, jobject, jint); - static void AddData(JNIEnv*, jobject, jbyteArray, jint); - static void Finished(JNIEnv*, jobject); - static jstring RedirectedToUrl(JNIEnv*, jobject, jstring, jstring, jint); - static void Error(JNIEnv*, jobject, jint, jstring, jstring); - -protected: - WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener); -private: - jobject mJLoader; - bool mPausedLoad; -}; - -} // end namespace android - -#endif diff --git a/Source/WebKit/android/jni/WebSettings.cpp b/Source/WebKit/android/jni/WebSettings.cpp index 9f7c2fa..1a1cc33 100644 --- a/Source/WebKit/android/jni/WebSettings.cpp +++ b/Source/WebKit/android/jni/WebSettings.cpp @@ -144,9 +144,7 @@ struct FieldIds { mAutoFillProfilePhoneNumber = env->GetFieldID(autoFillProfileClass, "mPhoneNumber", "Ljava/lang/String;"); env->DeleteLocalRef(autoFillProfileClass); #endif -#if USE(CHROME_NETWORK_STACK) mOverrideCacheMode = env->GetFieldID(clazz, "mOverrideCacheMode", "I"); -#endif ALOG_ASSERT(mLayoutAlgorithm, "Could not find field mLayoutAlgorithm"); ALOG_ASSERT(mTextSize, "Could not find field mTextSize"); @@ -265,9 +263,7 @@ struct FieldIds { jfieldID mAutoFillProfileCountry; jfieldID mAutoFillProfilePhoneNumber; #endif -#if USE(CHROME_NETWORK_STACK) jfieldID mOverrideCacheMode; -#endif }; static struct FieldIds* gFieldIds; @@ -367,7 +363,6 @@ public: str = (jstring)env->GetObjectField(obj, gFieldIds->mUserAgent); WebFrame::getWebFrame(pFrame)->setUserAgent(jstringToWtfString(env, str)); -#if USE(CHROME_NETWORK_STACK) WebViewCore::getWebViewCore(pFrame->view())->setWebRequestContextUserAgent(); jint cacheMode = env->GetIntField(obj, gFieldIds->mOverrideCacheMode); @@ -375,7 +370,6 @@ public: str = (jstring)env->GetObjectField(obj, gFieldIds->mAcceptLanguage); WebRequestContext::setAcceptLanguage(jstringToWtfString(env, str)); -#endif jint size = env->GetIntField(obj, gFieldIds->mMinimumFontSize); s->setMinimumFontSize(size); @@ -589,9 +583,7 @@ public: // This is required to enable the XMLTreeViewer when loading an XML document that // has no style attached to it. http://trac.webkit.org/changeset/79799 s->setDeveloperExtrasEnabled(true); -#if !ENABLE(ANDROID_NAVCACHE) s->setSpatialNavigationEnabled(true); -#endif } }; diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp index 2a81bd5..b9a21de 100644 --- a/Source/WebKit/android/jni/WebViewCore.cpp +++ b/Source/WebKit/android/jni/WebViewCore.cpp @@ -34,6 +34,7 @@ #include "BaseLayerAndroid.h" #include "CachedNode.h" #include "CachedRoot.h" +#include "content/address_detector.h" #include "Chrome.h" #include "ChromeClientAndroid.h" #include "ChromiumIncludes.h" @@ -96,6 +97,7 @@ #include "ProgressTracker.h" #include "Range.h" #include "RenderBox.h" +#include "RenderImage.h" #include "RenderInline.h" #include "RenderLayer.h" #include "RenderPart.h" @@ -176,6 +178,69 @@ FILE* gRenderTreeFile = 0; namespace android { +// Copied from CacheBuilder, not sure if this is needed/correct +IntRect getAreaRect(const HTMLAreaElement* area) +{ + Node* node = area->document(); + while ((node = node->traverseNextNode()) != NULL) { + RenderObject* renderer = node->renderer(); + if (renderer && renderer->isRenderImage()) { + RenderImage* image = static_cast<RenderImage*>(renderer); + HTMLMapElement* map = image->imageMap(); + if (map) { + Node* n; + for (n = map->firstChild(); n; + n = n->traverseNextNode(map)) { + if (n == area) { + if (area->isDefault()) + return image->absoluteBoundingBoxRect(); + return area->computeRect(image); + } + } + } + } + } + return IntRect(); +} + +// Copied from CacheBuilder, not sure if this is needed/correct +// TODO: See if this is even needed (I suspect not), and if not remove it +bool validNode(Frame* startFrame, void* matchFrame, + void* matchNode) +{ + if (matchFrame == startFrame) { + if (matchNode == NULL) + return true; + Node* node = startFrame->document(); + while (node != NULL) { + if (node == matchNode) { + const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ? + getAreaRect(static_cast<HTMLAreaElement*>(node)) : node->getRect(); + // Consider nodes with empty rects that are not at the origin + // to be valid, since news.google.com has valid nodes like this + if (rect.x() == 0 && rect.y() == 0 && rect.isEmpty()) + return false; + return true; + } + node = node->traverseNextNode(); + } + DBG_NAV_LOGD("frame=%p valid node=%p invalid\n", matchFrame, matchNode); + return false; + } + Frame* child = startFrame->tree()->firstChild(); + while (child) { + bool result = validNode(child, matchFrame, matchNode); + if (result) + return result; + child = child->tree()->nextSibling(); + } +#if DEBUG_NAV_UI + if (startFrame->tree()->parent() == NULL) + DBG_NAV_LOGD("frame=%p node=%p false\n", matchFrame, matchNode); +#endif + return false; +} + static SkTDArray<WebViewCore*> gInstanceList; void WebViewCore::addInstance(WebViewCore* inst) { @@ -386,9 +451,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m #if ENABLE(TOUCH_EVENTS) , m_forwardingTouchEvents(false) #endif -#if USE(CHROME_NETWORK_STACK) , m_webRequestContext(0) -#endif { ALOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!"); @@ -460,9 +523,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m WebViewCore::addInstance(this); -#if USE(CHROME_NETWORK_STACK) AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0); -#endif // Static initialisation of certain important V8 static data gets performed at system startup when // libwebcore gets loaded. We now need to associate the WebCore thread with V8 to complete @@ -530,13 +591,6 @@ static bool layoutIfNeededRecursive(WebCore::Frame* f) return success && !v->needsLayout(); } -#if ENABLE(ANDROID_NAVCACHE) -CacheBuilder& WebViewCore::cacheBuilder() -{ - return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder(); -} -#endif - WebCore::Node* WebViewCore::currentFocus() { return focusedFrame()->document()->focusedNode(); @@ -660,11 +714,6 @@ void WebViewCore::recordPictureSet(PictureSet* content) height = view->contentsHeight(); } -#if ENABLE(ANDROID_NAVCACHE) - if (cacheBuilder().pictureSetDisabled()) - content->clear(); -#endif - #if USE(ACCELERATED_COMPOSITING) // The invals are not always correct when the content size has changed. For // now, let's just reset the inval so that it invalidates the entire content @@ -703,56 +752,6 @@ void WebViewCore::recordPictureSet(PictureSet* content) // Rebuild the pictureset (webkit repaint) rebuildPictureSet(content); - -#if ENABLE(ANDROID_NAVCACHE) - WebCore::Node* oldFocusNode = currentFocus(); - m_frameCacheOutOfDate = true; - WebCore::IntRect oldBounds; - int oldSelStart = 0; - int oldSelEnd = 0; - if (oldFocusNode) { - oldBounds = oldFocusNode->getRect(); - getSelectionOffsets(oldFocusNode, oldSelStart, oldSelEnd); - } else - oldBounds = WebCore::IntRect(0,0,0,0); - unsigned latestVersion = 0; - if (m_check_domtree_version) { - // as domTreeVersion only increment, we can just check the sum to see - // whether we need to update the frame cache - for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) { - const Document* doc = frame->document(); - latestVersion += doc->domTreeVersion() + doc->styleVersion(); - } - } - DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p" - " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}" - " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}" - " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d", - m_lastFocused, oldFocusNode, - m_lastFocusedBounds.x(), m_lastFocusedBounds.y(), - m_lastFocusedBounds.width(), m_lastFocusedBounds.height(), - oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(), - m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd, - m_check_domtree_version ? "true" : "false", - latestVersion, m_domtree_version); - if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds - && m_lastFocusedSelStart == oldSelStart - && m_lastFocusedSelEnd == oldSelEnd - && !m_findIsUp - && (!m_check_domtree_version || latestVersion == m_domtree_version)) - { - return; - } - m_focusBoundsChanged |= m_lastFocused == oldFocusNode - && m_lastFocusedBounds != oldBounds; - m_lastFocused = oldFocusNode; - m_lastFocusedBounds = oldBounds; - m_lastFocusedSelStart = oldSelStart; - m_lastFocusedSelEnd = oldSelEnd; - m_domtree_version = latestVersion; - DBG_NAV_LOG("call updateFrameCache"); - updateFrameCache(); -#endif } // note: updateCursorBounds is called directly by the WebView thread @@ -1088,11 +1087,6 @@ void WebViewCore::didFirstLayout() || loadType == WebCore::FrameLoadTypeSame); checkException(env); -#if ENABLE(ANDROID_NAVCACHE) - DBG_NAV_LOG("call updateFrameCache"); - m_check_domtree_version = false; - updateFrameCache(); -#endif m_history.setDidFirstLayout(true); } @@ -1380,13 +1374,6 @@ void WebViewCore::dumpRenderTree(bool useFile) #endif } -void WebViewCore::dumpNavTree() -{ -#if DUMP_NAV_CACHE - cacheBuilder().mDebug.print(); -#endif -} - HTMLElement* WebViewCore::retrieveElement(int x, int y, const QualifiedName& tagName) { @@ -1452,7 +1439,7 @@ WTF::String WebViewCore::retrieveImageSource(int x, int y) WTF::String WebViewCore::requestLabel(WebCore::Frame* frame, WebCore::Node* node) { - if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) { + if (node && validNode(m_mainFrame, frame, node)) { RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label"); unsigned length = list->length(); for (unsigned i = 0; i < length; i++) { @@ -1508,104 +1495,6 @@ void WebViewCore::revealSelection() focusedFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); } -#if ENABLE(ANDROID_NAVCACHE) -void WebViewCore::updateCacheOnNodeChange() -{ - gCursorBoundsMutex.lock(); - bool hasCursorBounds = m_hasCursorBounds; - Frame* frame = (Frame*) m_cursorFrame; - Node* node = (Node*) m_cursorNode; - IntRect bounds = m_cursorHitBounds; - gCursorBoundsMutex.unlock(); - if (!hasCursorBounds || !node) - return; - if (CacheBuilder::validNode(m_mainFrame, frame, node)) { - RenderObject* renderer = node->renderer(); - if (renderer && renderer->style()->visibility() != HIDDEN) { - IntRect absBox = renderer->absoluteBoundingBoxRect(); - int globalX, globalY; - CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY); - absBox.move(globalX, globalY); - if (absBox == bounds) - return; - DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)", - absBox.x(), absBox.y(), absBox.width(), absBox.height(), - bounds.x(), bounds.y(), bounds.width(), bounds.height()); - } - } - DBG_NAV_LOGD("updateFrameCache node=%p", node); - updateFrameCache(); -} - -void WebViewCore::updateFrameCache() -{ - if (!m_frameCacheOutOfDate) { - DBG_NAV_LOG("!m_frameCacheOutOfDate"); - return; - } - - // If there is a pending style recalculation, do not update the frame cache. - // Until the recalculation is complete, there may be internal objects that - // are in an inconsistent state (such as font pointers). - // In any event, there's not much point to updating the cache while a style - // recalculation is pending, since it will simply have to be updated again - // once the recalculation is complete. - // TODO: Do we need to reschedule an update for after the style is recalculated? - if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->isPendingStyleRecalc()) { - ALOGW("updateFrameCache: pending style recalc, ignoring."); - return; - } - m_frameCacheOutOfDate = false; - CachedRoot* tempCacheRoot = new CachedRoot(); - tempCacheRoot->init(m_mainFrame, &m_history); -#if USE(ACCELERATED_COMPOSITING) - GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer(); - if (graphicsLayer) - tempCacheRoot->setRootLayer(graphicsLayer->contentLayer()); -#endif - CacheBuilder& builder = cacheBuilder(); - WebCore::Settings* settings = m_mainFrame->page()->settings(); - builder.allowAllTextDetection(); -#ifdef ANDROID_META_SUPPORT - if (settings) { - if (!settings->formatDetectionAddress()) - builder.disallowAddressDetection(); - if (!settings->formatDetectionEmail()) - builder.disallowEmailDetection(); - if (!settings->formatDetectionTelephone()) - builder.disallowPhoneDetection(); - } -#endif - builder.buildCache(tempCacheRoot); - SkPicture* tempPict = new SkPicture(); - recordPicture(tempPict); - tempCacheRoot->setPicture(tempPict); - SkSafeUnref(tempPict); - tempPict = 0; - tempCacheRoot->setTextGeneration(m_textGeneration); - WebCoreViewBridge* window = m_mainFrame->view()->platformWidget(); - tempCacheRoot->setVisibleRect(WebCore::IntRect(m_scrollOffsetX, - m_scrollOffsetY, window->width(), window->height())); - gFrameCacheMutex.lock(); - delete m_frameCacheKit; - m_frameCacheKit = tempCacheRoot; - m_updatedFrameCache = true; -#if DEBUG_NAV_UI - const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus(); - DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)", - cachedFocusNode ? cachedFocusNode->index() : 0, - cachedFocusNode ? cachedFocusNode->nodePointer() : 0); -#endif - gFrameCacheMutex.unlock(); -} - -void WebViewCore::updateFrameCacheIfLoading() -{ - if (!m_check_domtree_version) - updateFrameCache(); -} -#endif - struct TouchNodeData { Node* mUrlNode; Node* mInnerNode; @@ -2018,6 +1907,7 @@ AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool do } } if (!nodeDataList.size()) { + androidHitResult.searchContentDetectors(); return androidHitResult; } // finally select the node with the largest overlap with the fat point @@ -2065,6 +1955,8 @@ AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool do m_scrollOffsetX, m_scrollOffsetY); } } + } else { + androidHitResult.searchContentDetectors(); } return androidHitResult; } @@ -2243,7 +2135,7 @@ Node* WebViewCore::cursorNodeIsPlugin() { Frame* frame = (Frame*) m_cursorFrame; Node* node = (Node*) m_cursorNode; gCursorBoundsMutex.unlock(); - if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node) + if (hasCursorBounds && validNode(m_mainFrame, frame, node) && nodeIsPlugin(node)) { return node; } @@ -2269,7 +2161,7 @@ void WebViewCore::moveMouseIfLatest(int moveGeneration, void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node) { DBG_NAV_LOGD("frame=%p node=%p", frame, node); - if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node) + if (!node || !validNode(m_mainFrame, frame, node) || !node->isElementNode()) return; // Code borrowed from FocusController::advanceFocus @@ -2290,7 +2182,7 @@ void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y, HitTestResult* { DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame, x, y, m_scrollOffsetX, m_scrollOffsetY); - if (!frame || !CacheBuilder::validNode(m_mainFrame, frame, 0)) + if (!frame || !validNode(m_mainFrame, frame, 0)) frame = m_mainFrame; // mouse event expects the position in the window coordinate m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY); @@ -2300,9 +2192,6 @@ void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y, HitTestResult* WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false, false, WTF::currentTime()); frame->eventHandler()->handleMouseMoveEvent(mouseEvent, hoveredNode); -#if ENABLE(ANDROID_NAVCACHE) - updateCacheOnNodeChange(); -#endif } Position WebViewCore::getPositionForOffset(Node* node, int offset) @@ -2426,7 +2315,7 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i // initialize the selection if necessary if (selection->rangeCount() == 0) { if (m_currentNodeDomNavigationAxis - && CacheBuilder::validNode(m_mainFrame, + && validNode(m_mainFrame, m_mainFrame, m_currentNodeDomNavigationAxis)) { RefPtr<Range> rangeRef = selection->frame()->document()->createRange(); @@ -2438,7 +2327,7 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i } else if (currentFocus()) { selection->setPosition(currentFocus(), 0, ec); } else if (m_cursorNode - && CacheBuilder::validNode(m_mainFrame, + && validNode(m_mainFrame, m_mainFrame, m_cursorNode)) { RefPtr<Range> rangeRef = selection->frame()->document()->createRange(); @@ -2858,7 +2747,7 @@ String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, in if (!m_currentNodeDomNavigationAxis) m_currentNodeDomNavigationAxis = currentFocus(); if (!m_currentNodeDomNavigationAxis - || !CacheBuilder::validNode(m_mainFrame, m_mainFrame, + || !validNode(m_mainFrame, m_mainFrame, m_currentNodeDomNavigationAxis)) m_currentNodeDomNavigationAxis = body; Node* currentNode = m_currentNodeDomNavigationAxis; @@ -2959,7 +2848,7 @@ bool WebViewCore::isVisible(Node* node) while (currentNode && currentNode != body) { RenderStyle* style = currentNode->computedStyle(); if (style && - (style->display() == NONE || style->visibility() == HIDDEN)) { + (style->display() == WebCore::NONE || style->visibility() == WebCore::HIDDEN)) { return false; } currentNode = currentNode->parentNode(); @@ -3145,7 +3034,7 @@ void WebViewCore::setFocusControllerActive(bool active) void WebViewCore::saveDocumentState(WebCore::Frame* frame) { - if (!CacheBuilder::validNode(m_mainFrame, frame, 0)) + if (!validNode(m_mainFrame, frame, 0)) frame = m_mainFrame; WebCore::HistoryItem *item = frame->loader()->history()->currentItem(); @@ -3427,7 +3316,7 @@ void WebViewCore::touchUp(int touchGeneration, moveMouse(frame, x, y); m_lastGeneration = touchGeneration; } - if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) { + if (frame && validNode(m_mainFrame, frame, 0)) { frame->loader()->resetMultipleFormSubmissionProtection(); } DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p" @@ -3456,7 +3345,7 @@ static bool shouldSuppressKeyboard(const WebCore::Node* node) { // in which case, 'fake' is set to true bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr, bool fake) { - bool valid = !framePtr || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr); + bool valid = !framePtr || validNode(m_mainFrame, framePtr, nodePtr); WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame); if (valid && nodePtr) { // Need to special case area tags because an image map could have an area element in the middle @@ -3501,15 +3390,8 @@ bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* node autoFill->formFieldFocused(static_cast<HTMLFormControlElement*>(focusNode)); } #endif - if (!fake) { -#if ENABLE(ANDROID_NAVCACHE) - // Force an update of the navcache as this will fire off a - // message to WebView that *must* have an updated focus. - m_frameCacheOutOfDate = true; - updateFrameCache(); -#endif + if (!fake) initEditField(focusNode); - } } else if (!fake) { requestKeyboard(false); } @@ -3943,14 +3825,14 @@ void WebViewCore::updateTextSelection() AutoJObject javaObject = m_javaGlue->object(env); if (!javaObject.get()) return; - WebCore::Node* focusNode = currentFocus(); + VisibleSelection selection = focusedFrame()->selection()->selection(); int start = 0; int end = 0; - if (focusNode) - getSelectionOffsets(focusNode, start, end); - SelectText* selectText = createSelectText(focusedFrame()->selection()->selection()); + if (selection.isCaretOrRange()) + getSelectionOffsets(selection.start().anchorNode(), start, end); + SelectText* selectText = createSelectText(selection); env->CallVoidMethod(javaObject.get(), - m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode), + m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(currentFocus()), start, end, m_textGeneration, reinterpret_cast<int>(selectText)); checkException(env); } @@ -4128,14 +4010,14 @@ void WebViewCore::keepScreenOn(bool screenOn) { bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node, const IntRect& originalAbsoluteBounds) { - bool valid = CacheBuilder::validNode(m_mainFrame, frame, node); + bool valid = validNode(m_mainFrame, frame, node); if (!valid) return false; RenderObject* renderer = node->renderer(); if (!renderer) return false; IntRect absBounds = node->hasTagName(HTMLNames::areaTag) - ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node)) + ? getAreaRect(static_cast<HTMLAreaElement*>(node)) : renderer->absoluteBoundingBoxRect(); return absBounds == originalAbsoluteBounds; } @@ -4232,7 +4114,6 @@ bool WebViewCore::drawIsPaused() const return false; } -#if USE(CHROME_NETWORK_STACK) void WebViewCore::setWebRequestContextUserAgent() { // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet @@ -4260,7 +4141,6 @@ WebRequestContext* WebViewCore::webRequestContext() } return m_webRequestContext.get(); } -#endif void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect) { @@ -4506,9 +4386,6 @@ static void ClearContent(JNIEnv* env, jobject obj, jint nativeClass) static void UpdateFrameCacheIfLoading(JNIEnv* env, jobject obj, jint nativeClass) { -#if ENABLE(ANDROID_NAVCACHE) - reinterpret_cast<WebViewCore*>(nativeClass)->updateFrameCacheIfLoading(); -#endif } static void SetSize(JNIEnv* env, jobject obj, jint nativeClass, jint width, @@ -4705,6 +4582,7 @@ static void SendListBoxChoices(JNIEnv* env, jobject obj, jint nativeClass, viewImpl->popupReply(array, count); } +// TODO: Move this to WebView.cpp since it is only needed there static jstring FindAddress(JNIEnv* env, jobject obj, jstring addr, jboolean caseInsensitive) { @@ -4714,9 +4592,9 @@ static jstring FindAddress(JNIEnv* env, jobject obj, jstring addr, if (!length) return 0; const jchar* addrChars = env->GetStringChars(addr, 0); - int start, end; - bool success = CacheBuilder::FindAddress(addrChars, length, - &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE; + size_t start, end; + AddressDetector detector; + bool success = detector.FindContent(addrChars, addrChars + length, &start, &end); jstring ret = 0; if (success) ret = env->NewString(addrChars + start, end - start); @@ -4813,11 +4691,6 @@ static void MoveMouseIfLatest(JNIEnv* env, jobject obj, jint nativeClass, static void UpdateFrameCache(JNIEnv* env, jobject obj, jint nativeClass) { -#if ENABLE(ANDROID_NAVCACHE) - WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass); - ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); - viewImpl->updateFrameCache(); -#endif } static jint GetContentMinPrefWidth(JNIEnv* env, jobject obj, jint nativeClass) @@ -4888,10 +4761,6 @@ static void DumpRenderTree(JNIEnv* env, jobject obj, jint nativeClass, static void DumpNavTree(JNIEnv* env, jobject obj, jint nativeClass) { - WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass); - ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); - - viewImpl->dumpNavTree(); } static void SetJsFlags(JNIEnv* env, jobject obj, jint nativeClass, jstring flags) @@ -5090,10 +4959,8 @@ static void AutoFillForm(JNIEnv* env, jobject obj, jint nativeClass, static void CloseIdleConnections(JNIEnv* env, jobject obj, jint nativeClass) { -#if USE(CHROME_NETWORK_STACK) WebCache::get(true)->closeIdleConnections(); WebCache::get(false)->closeIdleConnections(); -#endif } static void nativeCertTrustChanged(JNIEnv *env, jobject obj) diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h index 785b659..bf7c36b 100644 --- a/Source/WebKit/android/jni/WebViewCore.h +++ b/Source/WebKit/android/jni/WebViewCore.h @@ -26,9 +26,6 @@ #ifndef WebViewCore_h #define WebViewCore_h -#ifndef DISABLE_NAVCACHE -#include "CacheBuilder.h" -#endif #include "CachedHistory.h" #include "DeviceMotionAndOrientationManager.h" #include "DOMSelection.h" @@ -430,14 +427,9 @@ namespace android { jobject getWebViewJavaObject(); void setBackgroundColor(SkColor c); -#ifndef DISABLE_NAVCACHE - void updateFrameCache(); - void updateCacheOnNodeChange(); - void updateFrameCacheIfLoading(); -#endif + void dumpDomTree(bool); void dumpRenderTree(bool); - void dumpNavTree(); /* We maintain a list of active plugins. The list is edited by the pluginview itself. The list is used to service invals to the plugin @@ -584,11 +576,9 @@ namespace android { // The actual content (without title bar) size in doc coordinate int screenWidth() const { return m_screenWidth; } int screenHeight() const { return m_screenHeight; } -#if USE(CHROME_NETWORK_STACK) void setWebRequestContextUserAgent(); void setWebRequestContextCacheMode(int mode); WebRequestContext* webRequestContext(); -#endif // Attempts to scroll the layer to the x,y coordinates of rect. The // layer is the id of the LayerAndroid. void scrollRenderLayer(int layer, const SkRect& rect); @@ -653,9 +643,6 @@ namespace android { URL = 7, }; -#ifndef DISABLE_NAVCACHE - CacheBuilder& cacheBuilder(); -#endif WebCore::Node* currentFocus(); // Create a set of pictures to represent the drawn DOM, driven by // the invalidated region and the time required to draw (used to draw) @@ -808,10 +795,7 @@ namespace android { bool m_forwardingTouchEvents; #endif -#if USE(CHROME_NETWORK_STACK) scoped_refptr<WebRequestContext> m_webRequestContext; -#endif - }; } // namespace android diff --git a/Source/WebKit/android/nav/CacheBuilder.cpp b/Source/WebKit/android/nav/CacheBuilder.cpp deleted file mode 100644 index 9759819..0000000 --- a/Source/WebKit/android/nav/CacheBuilder.cpp +++ /dev/null @@ -1,3137 +0,0 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "CachedPrefix.h" -#include "CachedNode.h" -#include "CachedRoot.h" -#include "ColumnInfo.h" -#include "Document.h" -#include "EventListener.h" -#include "EventNames.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameLoaderClientAndroid.h" -#include "FrameTree.h" -#include "FrameView.h" -//#include "GraphicsContext.h" -#include "HTMLAreaElement.h" -#include "HTMLImageElement.h" -#include "HTMLInputElement.h" -#include "HTMLMapElement.h" -#include "HTMLNames.h" -#include "HTMLOptionElement.h" -#include "HTMLSelectElement.h" -#include "HTMLTextAreaElement.h" -#include "InlineTextBox.h" -#include "KURL.h" -#include "LayerAndroid.h" -#include "PluginView.h" -#include "RegisteredEventListener.h" -#include "RenderImage.h" -#include "RenderInline.h" -#include "RenderLayerBacking.h" -#include "RenderListBox.h" -#include "RenderTextControl.h" -#include "RenderView.h" -#include "RenderWidget.h" -#include "SkCanvas.h" -#include "SkPoint.h" -#include "Text.h" -#include "WebCoreFrameBridge.h" -#include "WebCoreViewBridge.h" -#include "Widget.h" -#include <wtf/unicode/Unicode.h> - -#ifdef DUMP_NAV_CACHE_USING_PRINTF - FILE* gNavCacheLogFile = NULL; - android::Mutex gWriteLogMutex; -#endif - -#include "CacheBuilder.h" - -#define MINIMUM_FOCUSABLE_WIDTH 3 -#define MINIMUM_FOCUSABLE_HEIGHT 3 -#define MAXIMUM_FOCUS_RING_COUNT 32 - -namespace android { - -CacheBuilder* CacheBuilder::Builder(Frame* frame) { - return &((FrameLoaderClientAndroid*) frame->loader()->client())->getCacheBuilder(); -} - -Frame* CacheBuilder::FrameAnd(CacheBuilder* cacheBuilder) { - FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*) - ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder)); - return loader->getFrame(); -} - -Frame* CacheBuilder::FrameAnd(const CacheBuilder* cacheBuilder) { - FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*) - ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder)); - return loader->getFrame(); -} - -CacheBuilder::LayerTracker::~LayerTracker() { - // Check for a stacking context to prevent a crash in layers without a - // parent. - if (mRenderLayer && mRenderLayer->stackingContext()) - // Restore the scroll position of the layer. Does not affect layers - // without overflow scroll as the layer will not be scrolled. - mRenderLayer->scrollToOffset(mScroll.x(), mScroll.y()); -} - -#if DUMP_NAV_CACHE - -static bool hasEventListener(Node* node, const AtomicString& eventType) { - if (!node->isElementNode()) - return false; - Element* element = static_cast<Element*>(node); - EventListener* listener = element->getAttributeEventListener(eventType); - return 0 != listener; -} - -#define DEBUG_BUFFER_SIZE 256 -#define DEBUG_WRAP_SIZE 150 -#define DEBUG_WRAP_MAX 170 - -Frame* CacheBuilder::Debug::frameAnd() const { - CacheBuilder* nav = (CacheBuilder*) ((char*) this - OFFSETOF(CacheBuilder, mDebug)); - return CacheBuilder::FrameAnd(nav); -} - -void CacheBuilder::Debug::attr(const AtomicString& name, const AtomicString& value) { - if (name.isNull() || name.isEmpty() || value.isNull() || value.isEmpty()) - return; - uChar(name.characters(), name.length(), false); - print("="); - wideString(value.characters(), value.length(), false); - print(" "); -} - -void CacheBuilder::Debug::comma(const char* str) { - print(str); - print(", "); -} - -void CacheBuilder::Debug::flush() { - int len; - do { - int limit = mIndex; - if (limit < DEBUG_WRAP_SIZE) - return; - if (limit < DEBUG_WRAP_MAX) - len = limit; - else { - limit = DEBUG_WRAP_MAX; - len = DEBUG_WRAP_SIZE; - while (len < limit) { - char test = mBuffer[len]; - if (test < '/' || (test > '9' && test < 'A') || (test > 'Z' && test < 'a') || test > 'z') - break; - len++; - } - while (len > 0 && mBuffer[len - 1] == '\\') - len--; - while (mBuffer[len] == '"') - len++; - } - const char* prefix = mPrefix; - if (prefix[0] == '\"') { - // see if we're inside a quote - int quoteCount = 0; - for (int index = 0; index < len; index++) { - if (mBuffer[index] == '\\') { - index++; - continue; - } - if (mBuffer[index] == '\n') { - quoteCount = 0; - continue; - } - if (mBuffer[index] == '"') - quoteCount++; - } - if ((quoteCount & 1) == 0) - prefix = "\n"; - } - DUMP_NAV_LOGD("%.*s", len, mBuffer); - int copy = mIndex - len; - strcpy(mBuffer, prefix); - memcpy(&mBuffer[strlen(prefix)], &mBuffer[len], copy); - mIndex = strlen(prefix) + copy; - } while (true); -} - -void CacheBuilder::Debug::frameName(char*& namePtr, const char* max) const { - if (namePtr >= max) - return; - Frame* frame = frameAnd(); - Frame* parent = frame->tree()->parent(); - if (parent) - Builder(parent)->mDebug.frameName(namePtr, max); - const AtomicString& name = frame->tree()->name(); - if (name.length() == 0) - return; - unsigned index = 0; - if (name.startsWith(AtomicString("opener"))) - index = 6; - for (; index < name.length(); index++) { - char ch = name[index]; - if (ch <= ' ') - ch = '_'; - if (WTF::isASCIIAlphanumeric(ch) || ch == '_') - *namePtr++ = ch; - } -} - -void CacheBuilder::Debug::frames() { - Frame* frame = frameAnd(); - Document* doc = frame->document(); - if (doc == NULL) - return; - bool top = frame->tree()->parent() == NULL; - if (top) { -#ifdef DUMP_NAV_CACHE_USING_PRINTF - gWriteLogMutex.lock(); - ASSERT(gNavCacheLogFile == NULL); - gNavCacheLogFile = fopen(NAV_CACHE_LOG_FILE, "a"); -#endif - groups(); - } - Frame* child = frame->tree()->firstChild(); - bool hasChild = child != NULL; - if (top && hasChild) - DUMP_NAV_LOGD("\nnamespace TEST_SPACE {\n\n"); - while (child) { - Builder(child)->mDebug.frames(); - child = child->tree()->nextSibling(); - } - if (hasChild) { - child = frame->tree()->firstChild(); - while (child) { - char childName[256]; - char* childNamePtr = childName; - Builder(child)->mDebug.frameName(childNamePtr, childNamePtr + sizeof(childName) - 1); - *childNamePtr = '\0'; - if (child == frame->tree()->firstChild()) - DUMP_NAV_LOGD("DebugTestFrameGroup TEST%s_GROUP[] = {\n", childName); - Frame* next = child->tree()->nextSibling(); - Document* doc = child->document(); - if (doc != NULL) { - RenderObject* renderer = doc->renderer(); - if (renderer != NULL) { - RenderLayer* layer = renderer->enclosingLayer(); - if (layer != NULL) { - DUMP_NAV_LOGD("{ "); - DUMP_NAV_LOGD("TEST%s_RECTS, ", childName); - DUMP_NAV_LOGD("TEST%s_RECT_COUNT, ", childName); - DUMP_NAV_LOGD("TEST%s_RECTPARTS, ", childName); - DUMP_NAV_LOGD("TEST%s_BOUNDS,\n", childName); - DUMP_NAV_LOGD("TEST%s_WIDTH, ", childName); - DUMP_NAV_LOGD("TEST%s_HEIGHT,\n", childName); - DUMP_NAV_LOGD("0, 0, 0, 0,\n"); - DUMP_NAV_LOGD("TEST%s_FOCUS, ", childName); - Frame* grandChild = child->tree()->firstChild(); - if (grandChild) { - char grandChildName[256]; - char* grandChildNamePtr = grandChildName; - Builder(grandChild)->mDebug.frameName(grandChildNamePtr, - grandChildNamePtr + sizeof(grandChildName) - 1); - *grandChildNamePtr = '\0'; - DUMP_NAV_LOGD("TEST%s_GROUP, ", grandChildName); - DUMP_NAV_LOGD("sizeof(TEST%s_GROUP) / sizeof(DebugTestFrameGroup), ", grandChildName); - } else - DUMP_NAV_LOGD("NULL, 0, "); - DUMP_NAV_LOGD("\"%s\"\n", childName); - DUMP_NAV_LOGD("}%c\n", next ? ',' : ' '); - } - } - } - child = next; - } - DUMP_NAV_LOGD("};\n"); - } - if (top) { - if (hasChild) - DUMP_NAV_LOGD("\n} // end of namespace\n\n"); - char name[256]; - char* frameNamePtr = name; - frameName(frameNamePtr, frameNamePtr + sizeof(name) - 1); - *frameNamePtr = '\0'; - DUMP_NAV_LOGD("DebugTestFrameGroup TEST%s_GROUP = {\n", name); - DUMP_NAV_LOGD("TEST%s_RECTS, ", name); - DUMP_NAV_LOGD("TEST%s_RECT_COUNT, ", name); - DUMP_NAV_LOGD("TEST%s_RECTPARTS, ", name); - DUMP_NAV_LOGD("TEST%s_BOUNDS,\n", name); - DUMP_NAV_LOGD("TEST%s_WIDTH, ", name); - DUMP_NAV_LOGD("TEST%s_HEIGHT,\n", name); - DUMP_NAV_LOGD("TEST%s_MAX_H, ", name); - DUMP_NAV_LOGD("TEST%s_MIN_H, ", name); - DUMP_NAV_LOGD("TEST%s_MAX_V, ", name); - DUMP_NAV_LOGD("TEST%s_MIN_V,\n", name); - DUMP_NAV_LOGD("TEST%s_FOCUS, ", name); - if (hasChild) { - child = frame->tree()->firstChild(); - char childName[256]; - char* childNamePtr = childName; - Builder(child)->mDebug.frameName(childNamePtr, childNamePtr + sizeof(childName) - 1); - *childNamePtr = '\0'; - DUMP_NAV_LOGD("TEST_SPACE::TEST%s_GROUP, ", childName); - DUMP_NAV_LOGD("sizeof(TEST_SPACE::TEST%s_GROUP) / sizeof(DebugTestFrameGroup), \n" ,childName); - } else - DUMP_NAV_LOGD("NULL, 0, "); - DUMP_NAV_LOGD("\"%s\"\n", name); - DUMP_NAV_LOGD("};\n"); -#ifdef DUMP_NAV_CACHE_USING_PRINTF - if (gNavCacheLogFile) - fclose(gNavCacheLogFile); - gNavCacheLogFile = NULL; - gWriteLogMutex.unlock(); -#endif - } -} - -void CacheBuilder::Debug::init(char* buffer, size_t size) { - mBuffer = buffer; - mBufferSize = size; - mIndex = 0; - mPrefix = ""; -} - -void CacheBuilder::Debug::groups() { - Frame* frame = frameAnd(); - Frame* child = frame->tree()->firstChild(); - bool hasChild = child != NULL; - if (frame->tree()->parent() == NULL && hasChild) - DUMP_NAV_LOGD("namespace TEST_SPACE {\n\n"); - while (child) { - Builder(child)->mDebug.groups(); - child = child->tree()->nextSibling(); - } - if (frame->tree()->parent() == NULL && hasChild) - DUMP_NAV_LOGD("\n} // end of namespace\n\n"); - Document* doc = frame->document(); - char name[256]; - char* frameNamePtr = name; - frameName(frameNamePtr, frameNamePtr + sizeof(name) - 1); - *frameNamePtr = '\0'; - if (doc == NULL) { - DUMP_NAV_LOGD("// %s has no document\n", name); - return; - } - RenderObject* renderer = doc->renderer(); - if (renderer == NULL) { - DUMP_NAV_LOGD("// %s has no renderer\n", name); - return; - } - RenderLayer* layer = renderer->enclosingLayer(); - if (layer == NULL) { - DUMP_NAV_LOGD("// %s has no enclosingLayer\n", name); - return; - } - Node* node = doc; - Node* focus = doc->focusedNode(); - bool atLeastOne = false; - do { - if ((atLeastOne |= isFocusable(node)) != false) - break; - } while ((node = node->traverseNextNode()) != NULL); - int focusIndex = -1; - if (atLeastOne == false) { - DUMP_NAV_LOGD("static DebugTestNode TEST%s_RECTS[] = {\n" - "{{0, 0, 0, 0}, \"\", 0, -1, \"\", {0, 0, 0, 0}, false, 0}\n" - "};\n\n", name); - DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = 1;" - " // no focusable nodes\n", name); - DUMP_NAV_LOGD("#define TEST%s_RECTPARTS NULL\n", name); - } else { - node = doc; - int count = 1; - DUMP_NAV_LOGD("static DebugTestNode TEST%s_RECTS[] = {\n", name); - do { - String properties; - if (hasEventListener(node, eventNames().clickEvent)) - properties.append("ONCLICK | "); - if (hasEventListener(node, eventNames().mousedownEvent)) - properties.append("MOUSEDOWN | "); - if (hasEventListener(node, eventNames().mouseupEvent)) - properties.append("MOUSEUP | "); - if (hasEventListener(node, eventNames().mouseoverEvent)) - properties.append("MOUSEOVER | "); - if (hasEventListener(node, eventNames().mouseoutEvent)) - properties.append("MOUSEOUT | "); - if (hasEventListener(node, eventNames().keydownEvent)) - properties.append("KEYDOWN | "); - if (hasEventListener(node, eventNames().keyupEvent)) - properties.append("KEYUP | "); - if (CacheBuilder::HasFrame(node)) - properties.append("FRAME | "); - if (focus == node) { - properties.append("FOCUS | "); - focusIndex = count; - } - if (node->isKeyboardFocusable(NULL)) - properties.append("KEYBOARD_FOCUSABLE | "); - if (node->isMouseFocusable()) - properties.append("MOUSE_FOCUSABLE | "); - if (node->isFocusable()) - properties.append("SIMPLE_FOCUSABLE | "); - if (properties.isEmpty()) - properties.append("0"); - else - properties.truncate(properties.length() - 3); - IntRect rect = node->getRect(); - if (node->hasTagName(HTMLNames::areaTag)) - rect = getAreaRect(static_cast<HTMLAreaElement*>(node)); - char buffer[DEBUG_BUFFER_SIZE]; - memset(buffer, 0, sizeof(buffer)); - mBuffer = buffer; - mBufferSize = sizeof(buffer); - mPrefix = "\"\n\""; - mIndex = snprintf(buffer, sizeof(buffer), "{{%d, %d, %d, %d}, ", rect.x(), rect.y(), - rect.width(), rect.height()); - localName(node); - uChar(properties.characters(), properties.length(), false); - print(", "); - int parentIndex = ParentIndex(node, count, node->parentNode()); - char scratch[256]; - snprintf(scratch, sizeof(scratch), "%d", parentIndex); - comma(scratch); - Element* element = static_cast<Element*>(node); - if (node->isElementNode() && element->hasID()) - wideString(element->getIdAttribute()); - else if (node->isTextNode()) { - #if 01 // set to one to abbreviate text that can be omitted from the address detection code - if (rect.isEmpty() && node->textContent().length() > 100) { - wideString(node->textContent().characters(), 100, false); - snprintf(scratch, sizeof(scratch), "/* + %d bytes */", - node->textContent().length() - 100); - print(scratch); - } else - #endif - wideString(node->textContent().characters(), node->textContent().length(), true); - } else if (node->hasTagName(HTMLNames::aTag) || - node->hasTagName(HTMLNames::areaTag)) - { - HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(node); - wideString(anchor->href()); - } else if (node->hasTagName(HTMLNames::imgTag)) { - HTMLImageElement* image = static_cast<HTMLImageElement*>(node); - wideString(image->src()); - } else - print("\"\""); - RenderObject* renderer = node->renderer(); - int tabindex = node->isElementNode() ? node->tabIndex() : 0; - RenderLayer* layer = 0; - if (renderer) { - const IntRect& absB = renderer->absoluteBoundingBoxRect(); - bool hasLayer = renderer->hasLayer(); - layer = hasLayer ? toRenderBoxModelObject(renderer)->layer() : 0; - snprintf(scratch, sizeof(scratch), ", {%d, %d, %d, %d}, %s" - ", %d, %s, %s},", - absB.x(), absB.y(), absB.width(), absB.height(), - renderer->hasOverflowClip() ? "true" : "false", tabindex, - hasLayer ? "true" : "false", - hasLayer && layer->isComposited() ? "true" : "false"); - // TODO: add renderer->style()->visibility() - print(scratch); - } else - print(", {0, 0, 0, 0}, false, 0},"); - - flush(); - snprintf(scratch, sizeof(scratch), "// %d: ", count); - mPrefix = "\n// "; - print(scratch); - //print(renderer ? renderer->information().ascii() : "NO_RENDER_INFO"); - if (node->isElementNode()) { - Element* element = static_cast<Element*>(node); - NamedNodeMap* attrs = element->attributes(); - unsigned length = attrs->length(); - if (length > 0) { - newLine(); - print("// attr: "); - for (unsigned i = 0; i < length; i++) { - Attribute* a = attrs->attributeItem(i); - attr(a->localName(), a->value()); - } - } - } - if (renderer) { - RenderStyle* style = renderer->style(); - snprintf(scratch, sizeof(scratch), "// renderStyle:" - " visibility=%s hasBackGround=%d" - " tapHighlightColor().alpha()=0x%02x" - " isTransparent()=%s", - style->visibility() == HIDDEN ? "HIDDEN" : "VISIBLE", - renderer->hasBackground(), style->tapHighlightColor().alpha(), - renderer->isTransparent() ? "true" : "false"); - newLine(); - print(scratch); - RenderBlock* renderBlock = static_cast<RenderBlock*>(renderer); - if (renderer->isRenderBlock() && renderBlock->hasColumns()) { - const RenderBox* box = static_cast<RenderBox*>(renderer); - const IntRect& oRect = box->visibleOverflowRect(); - snprintf(scratch, sizeof(scratch), "// renderBlock:" - " columnCount=%d columnGap=%d direction=%d" - " hasOverflowClip=%d overflow=(%d,%d,w=%d,h=%d)", - renderBlock->columnInfo()->columnCount(), renderBlock->columnGap(), - renderBlock->style()->direction(), renderer->hasOverflowClip(), - oRect.x(), oRect.y(), oRect.width(), oRect.height()); - newLine(); - print(scratch); - } - } - #if USE(ACCELERATED_COMPOSITING) - if (renderer && renderer->hasLayer()) { - RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); - RenderLayerBacking* back = layer->backing(); - GraphicsLayer* grLayer = back ? back->graphicsLayer() : 0; - LayerAndroid* aLayer = grLayer ? grLayer->platformLayer() : 0; - const SkPicture* pict = aLayer ? aLayer->picture() : 0; - const IntRect& r = renderer->absoluteBoundingBoxRect(); - snprintf(scratch, sizeof(scratch), "// layer:%p back:%p" - " gLayer:%p aLayer:%p pict:%p r:(%d,%d,w=%d,h=%d)", - layer, back, grLayer, aLayer, pict, r.x(), r.y(), - r.width(), r.height()); - newLine(); - print(scratch); - } - #endif - count++; - newLine(); - } while ((node = node->traverseNextNode()) != NULL); - DUMP_NAV_LOGD("}; // focusables = %d\n", count - 1); - DUMP_NAV_LOGD("\n"); - DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = %d;\n\n", name, count - 1); - // look for rects with multiple parts - node = doc; - count = 1; - bool hasRectParts = false; - int globalOffsetX, globalOffsetY; - GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY); - do { - IntRect rect; - bool _isFocusable = isFocusable(node) || (node->isTextNode() - && node->getRect().isEmpty() == false - ); - int nodeIndex = count++; - if (_isFocusable == false) - continue; - RenderObject* renderer = node->renderer(); - if (renderer == NULL) - continue; - WTF::Vector<IntRect> rects; - IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX); - IntRect focusBounds = IntRect(0, 0, INT_MAX, INT_MAX); - IntRect* rectPtr = &focusBounds; - int imageCount = 0; - if (node->isTextNode()) { - Text* textNode = (Text*) node; - if (CacheBuilder::ConstructTextRects(textNode, 0, textNode, - INT_MAX, globalOffsetX, globalOffsetY, rectPtr, - clipBounds, &rects) == false) - continue; - } else { - IntRect nodeBounds = node->getRect(); - continue; - } - unsigned arraySize = rects.size(); - if (arraySize > 1 || (arraySize == 1 && (rectPtr->width() != rect.width())) || - rectPtr->height() != rect.height()) { - if (hasRectParts == false) { - DUMP_NAV_LOGD("static DebugTestRectPart TEST%s_RECTPARTS[] = {\n", name); - hasRectParts = true; - } - if (node->isTextNode() == false) { - unsigned rectIndex = 0; - for (; rectIndex < arraySize; rectIndex++) { - rectPtr = &rects.at(rectIndex); - DUMP_NAV_LOGD("{ %d, %d, %d, %d, %d }, // %d\n", nodeIndex, - rectPtr->x(), rectPtr->y(), rectPtr->width(), - rectPtr->height(), rectIndex + 1); - } - } else { - RenderText* renderText = (RenderText*) node->renderer(); - InlineTextBox* textBox = renderText->firstTextBox(); - unsigned rectIndex = 0; - while (textBox) { - FloatPoint pt = renderText->localToAbsolute(); - IntRect rect = textBox->selectionRect((int) pt.x(), (int) pt.y(), 0, INT_MAX); - mIndex = 0; - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, "{ %d, %d, %d, %d, %d", - nodeIndex, rect.x(), rect.y(), rect.width(), rect.height()); - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d", - textBox->len(), 0 /*textBox->selectionHeight()*/, - 0 /*textBox->selectionTop()*/); - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d", - 0 /*textBox->spaceAdd()*/, textBox->start(), 0 /*textBox->textPos()*/); - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d, %d", - textBox->x(), textBox->y(), textBox->logicalWidth(), textBox->logicalHeight()); - int baseline = textBox->renderer()->style(textBox->isFirstLineStyle())->font().ascent(); - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d }, // %d ", - baseline, imageCount, ++rectIndex); - wideString(node->textContent().characters() + textBox->start(), textBox->len(), true); - DUMP_NAV_LOGD("%.*s\n", mIndex, mBuffer); - textBox = textBox->nextTextBox(); - } - } - } - } while ((node = node->traverseNextNode()) != NULL); - if (hasRectParts) - DUMP_NAV_LOGD("{0}\n};\n\n"); - else - DUMP_NAV_LOGD("static DebugTestRectPart* TEST%s_RECTPARTS = NULL;\n", name); - } - int contentsWidth = layer->width(); - int contentsHeight = layer->height(); - DUMP_NAV_LOGD("static int TEST%s_FOCUS = %d;\n", name, focusIndex); - DUMP_NAV_LOGD("static int TEST%s_WIDTH = %d;\n", name, contentsWidth); - DUMP_NAV_LOGD("static int TEST%s_HEIGHT = %d;\n\n", name, contentsHeight); -} - -bool CacheBuilder::Debug::isFocusable(Node* node) { - if (node->hasTagName(HTMLNames::areaTag)) - return true; - if (node->renderer() == false) - return false; - if (node->isKeyboardFocusable(NULL)) - return true; - if (node->isMouseFocusable()) - return true; - if (node->isFocusable()) - return true; - if (CacheBuilder::AnyIsClick(node)) - return false; - if (CacheBuilder::HasTriggerEvent(node)) - return true; - return false; -} - -void CacheBuilder::Debug::localName(Node* node) { - const AtomicString& local = node->localName(); - if (node->isTextNode()) - print("\"#text\""); - else - wideString(local.characters(), local.length(), false); - print(", "); -} - -void CacheBuilder::Debug::newLine(int indent) { - if (mPrefix[0] != '\n') - print(&mPrefix[0], 1); - flush(); - int lastnewline = mIndex - 1; - while (lastnewline >= 0 && mBuffer[lastnewline] != '\n') - lastnewline--; - lastnewline++; - char* buffer = mBuffer; - if (lastnewline > 0) { - DUMP_NAV_LOGD("%.*s", lastnewline, buffer); - mIndex -= lastnewline; - buffer += lastnewline; - } - size_t prefixLen = strlen(mPrefix); - int minPrefix = prefixLen - 1; - while (minPrefix >= 0 && mPrefix[minPrefix] != '\n') - minPrefix--; - minPrefix = prefixLen - minPrefix - 1; - if (mIndex > minPrefix) - DUMP_NAV_LOGD("%.*s\n", mIndex, buffer); - mIndex = 0; - setIndent(indent); -} - -int CacheBuilder::Debug::ParentIndex(Node* node, int count, Node* parent) -{ - if (parent == NULL) - return -1; - ASSERT(node != parent); - int result = count; - Node* previous = node; - do { - result--; - previous = previous->traversePreviousNode(); - } while (previous && previous != parent); - if (previous != NULL) - return result; - result = count; - do { - result++; - } while ((node = node->traverseNextNode()) != NULL && node != parent); - if (node != NULL) - return result; - ASSERT(0); - return -1; -} - -void CacheBuilder::Debug::print(const char* name) { - print(name, strlen(name)); -} - -void CacheBuilder::Debug::print(const char* name, unsigned len) { - do { - if (mIndex + len >= DEBUG_BUFFER_SIZE) - flush(); - int copyLen = mIndex + len < DEBUG_BUFFER_SIZE ? - len : DEBUG_BUFFER_SIZE - mIndex; - memcpy(&mBuffer[mIndex], name, copyLen); - mIndex += copyLen; - name += copyLen; - len -= copyLen; - } while (len > 0); - mBuffer[mIndex] = '\0'; -} - -void CacheBuilder::Debug::setIndent(int indent) -{ - char scratch[64]; - snprintf(scratch, sizeof(scratch), "%.*s", indent, - " "); - print(scratch); -} - -void CacheBuilder::Debug::uChar(const UChar* name, unsigned len, bool hex) { - const UChar* end = name + len; - bool wroteHex = false; - while (name < end) { - unsigned ch = *name++; - if (ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0) - ch = ' '; - if (ch < ' ' || ch == 0x7f) { - if (hex) { - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, "\\x%02x", ch); - wroteHex = true; - } else - mBuffer[mIndex++] = '?'; - } else if (ch >= 0x80) { - if (hex) { - if (ch < 0x800) - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, - "\\x%02x\\x%02x", ch >> 6 | 0xc0, (ch & 0x3f) | 0x80); - else - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, - "\\x%02x\\x%02x\\x%02x", ch >> 12 | 0xe0, - (ch >> 6 & 0x3f) | 0x80, (ch & 0x3f) | 0x80); - wroteHex = true; - } else - mBuffer[mIndex++] = '?'; - } else { - if (wroteHex && WTF::isASCIIHexDigit((UChar) ch)) - mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, - "\" \""); - else if (ch == '"' || ch == '\\') - mBuffer[mIndex++] = '\\'; - mBuffer[mIndex++] = ch; - wroteHex = false; - } - if (mIndex + 1 >= DEBUG_BUFFER_SIZE) - flush(); - } - flush(); -} - -void CacheBuilder::Debug::validateFrame() { - Frame* frame = frameAnd(); - Page* page = frame->page(); - ASSERT(page); - ASSERT((int) page > 0x10000); - Frame* child = frame->tree()->firstChild(); - while (child) { - Builder(child)->mDebug.validateFrame(); - child = child->tree()->nextSibling(); - } -} - -void CacheBuilder::Debug::wideString(const UChar* chars, int length, bool hex) { - if (length == 0) - print("\"\""); - else { - print("\""); - uChar(chars, length, hex); - print("\""); - } -} - -void CacheBuilder::Debug::wideString(const String& str) { - wideString(str.characters(), str.length(), false); -} - -#endif // DUMP_NAV_CACHE - -CacheBuilder::CacheBuilder() -{ - mAllowableTypes = ALL_CACHEDNODE_BITS; -#ifdef DUMP_NAV_CACHE_USING_PRINTF - gNavCacheLogFile = NULL; -#endif -} - -void CacheBuilder::adjustForColumns(const ClipColumnTracker& track, - CachedNode* node, IntRect* bounds, RenderBlock* renderer) -{ - if (!renderer->hasColumns()) - return; - int x = 0; - int y = 0; - int tx = track.mBounds.x(); - int ty = track.mBounds.y(); - int columnGap = track.mColumnGap; - size_t limit = track.mColumnInfo->columnCount(); - for (size_t index = 0; index < limit; index++) { - IntRect column = renderer->columnRectAt(track.mColumnInfo, index); - column.move(tx, ty); - IntRect test = *bounds; - test.move(x, y); - if (column.contains(test)) { - if ((x | y) == 0) - return; - *bounds = test; - node->move(x, y); - return; - } - int xOffset = column.width() + columnGap; - x += track.mDirection == LTR ? xOffset : -xOffset; - y -= column.height(); - } -} - -// Checks if a node has one of event listener types. -bool CacheBuilder::NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length) { - for (int i = 0; i < length; ++i) { - if (!node->getEventListeners(eventTypes[i]).isEmpty()) - return true; - } - return false; -} - -bool CacheBuilder::AnyChildIsClick(Node* node) -{ - AtomicString eventTypes[5] = { - eventNames().clickEvent, - eventNames().mousedownEvent, - eventNames().mouseupEvent, - eventNames().keydownEvent, - eventNames().keyupEvent - }; - - Node* child = node->firstChild(); - while (child != NULL) { - if (child->isFocusable() || - NodeHasEventListeners(child, eventTypes, 5)) - return true; - if (AnyChildIsClick(child)) - return true; - child = child->nextSibling(); - } - return false; -} - -bool CacheBuilder::AnyIsClick(Node* node) -{ - if (node->hasTagName(HTMLNames::bodyTag)) - return AnyChildIsClick(node); - - AtomicString eventTypeSetOne[4] = { - eventNames().mouseoverEvent, - eventNames().mouseoutEvent, - eventNames().keydownEvent, - eventNames().keyupEvent - }; - - if (!NodeHasEventListeners(node, eventTypeSetOne, 4)) - return false; - - AtomicString eventTypeSetTwo[3] = { - eventNames().clickEvent, - eventNames().mousedownEvent, - eventNames().mouseupEvent - }; - - if (NodeHasEventListeners(node, eventTypeSetTwo, 3)) - return false; - - return AnyChildIsClick(node); -} - -void CacheBuilder::buildCache(CachedRoot* root) -{ - Frame* frame = FrameAnd(this); - mPictureSetDisabled = false; - BuildFrame(frame, frame, root, (CachedFrame*) root); - root->finishInit(); // set up frame parent pointers, child pointers - setData((CachedFrame*) root); -} - -static Node* ParentWithChildren(Node* node) -{ - Node* parent = node; - while ((parent = parent->parentNode())) { - if (parent->childNodeCount() > 1) - return parent; - } - return 0; -} - -// FIXME -// Probably this should check for null instead of the caller. If the -// Tracker object is the last thing in the dom, checking for null in the -// caller in some cases fails to set up Tracker state which may be useful -// to the nodes parsed immediately after the tracked noe. -static Node* OneAfter(Node* node) -{ - Node* parent = node; - Node* sibling = NULL; - while ((parent = parent->parentNode()) != NULL) { - sibling = parent->nextSibling(); - if (sibling != NULL) - break; - } - return sibling; -} - -// return true if this renderer is really a pluinview, and it wants -// key-events (i.e. focus) -static bool checkForPluginViewThatWantsFocus(RenderObject* renderer) { - if (renderer->isWidget()) { - Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); - if (widget && (widget->isPluginView() || widget->isPluginViewBase())) { - // check if this plugin really wants key events (TODO) - return true; - } - } - return false; -} - -#if USE(ACCELERATED_COMPOSITING) -static void AddLayer(CachedFrame* frame, size_t index, const IntPoint& location, int id) -{ - DBG_NAV_LOGD("frame=%p index=%d loc=(%d,%d) id=%d", frame, index, - location.x(), location.y(), id); - CachedLayer cachedLayer; - cachedLayer.setCachedNodeIndex(index); - cachedLayer.setOffset(location); - cachedLayer.setUniqueId(id); - frame->add(cachedLayer); -} -#endif - -// when new focus is found, push it's parent on a stack - // as long as more focii are found with the same (grand) parent, note it - // (which only requires retrieving the last parent on the stack) -// when the parent's last child is found, pop the stack -// different from Tracker in that Tracker only pushes focii with children - -// making this work with focus - child focus - grandchild focus is tricky -// if I keep the generation number, I may be able to more quickly determine that -// a node is a grandchild of the focus's parent -// this additionally requires being able to find the grandchild's parent - -// keep nodes that are focusable -void CacheBuilder::BuildFrame(Frame* root, Frame* frame, - CachedRoot* cachedRoot, CachedFrame* cachedFrame) -{ - WTF::Vector<FocusTracker> tracker(1); // sentinel - { - FocusTracker* baseTracker = tracker.data(); - bzero(baseTracker, sizeof(FocusTracker)); - baseTracker->mCachedNodeIndex = -1; - } - WTF::Vector<LayerTracker> layerTracker(1); // sentinel - bzero(layerTracker.data(), sizeof(LayerTracker)); - WTF::Vector<ClipColumnTracker> clipTracker(1); // sentinel - bzero(clipTracker.data(), sizeof(ClipColumnTracker)); - WTF::Vector<TabIndexTracker> tabIndexTracker(1); // sentinel - bzero(tabIndexTracker.data(), sizeof(TabIndexTracker)); -#if DUMP_NAV_CACHE - char* frameNamePtr = cachedFrame->mDebug.mFrameName; - Builder(frame)->mDebug.frameName(frameNamePtr, frameNamePtr + - sizeof(cachedFrame->mDebug.mFrameName) - 1); - *frameNamePtr = '\0'; - int nodeIndex = 1; -#endif - NodeWalk walk; - Document* doc = frame->document(); - Node* parent = doc; - CachedNode cachedParentNode; - cachedParentNode.init(parent); -#if DUMP_NAV_CACHE - cachedParentNode.mDebug.mNodeIndex = nodeIndex; -#endif - cachedFrame->add(cachedParentNode); - Node* node = parent; - int cacheIndex = 1; - int colorIndex = 0; // assume no special css ring colors - const void* lastStyleDataPtr = 0; - int textInputIndex = 0; - Node* focused = doc->focusedNode(); - if (focused) - cachedRoot->setFocusBounds(focused->getRect()); - int globalOffsetX, globalOffsetY; - GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY); -#if USE(ACCELERATED_COMPOSITING) - // The frame itself might be composited so we need to track the layer. Do - // not track the base frame's layer as the main content is draw as part of - // BaseLayerAndroid's picture. - if (frame != root && frame->contentRenderer() - && frame->contentRenderer()->usesCompositing() && node->lastChild()) - TrackLayer(layerTracker, frame->contentRenderer(), node->lastChild(), - globalOffsetX, globalOffsetY); -#endif - while (walk.mMore || (node = node->traverseNextNode()) != NULL) { -#if DUMP_NAV_CACHE - nodeIndex++; -#endif - FocusTracker* last = &tracker.last(); - int lastChildIndex = cachedFrame->size() - 1; - while (node == last->mLastChild) { - if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex)) - cacheIndex--; - tracker.removeLast(); - lastChildIndex = last->mCachedNodeIndex; - last = &tracker.last(); - } - do { - const ClipColumnTracker* lastClip = &clipTracker.last(); - if (node != lastClip->mLastChild) - break; - clipTracker.removeLast(); - } while (true); - do { - const LayerTracker* lastLayer = &layerTracker.last(); - if (node != lastLayer->mLastChild) - break; - layerTracker.removeLast(); - } while (true); - do { - const TabIndexTracker* lastTabIndex = &tabIndexTracker.last(); - if (node != lastTabIndex->mLastChild) - break; - tabIndexTracker.removeLast(); - } while (true); - Frame* child = HasFrame(node); - CachedNode cachedNode; - if (child != NULL) { - if (child->document() == NULL) - continue; - RenderObject* nodeRenderer = node->renderer(); - if (nodeRenderer != NULL && nodeRenderer->style()->visibility() == HIDDEN) - continue; - CachedFrame cachedChild; - cachedChild.init(cachedRoot, cacheIndex, child); - int childFrameIndex = cachedFrame->childCount(); - cachedFrame->addFrame(cachedChild); - cachedNode.init(node); - cachedNode.setIndex(cacheIndex++); - cachedNode.setDataIndex(childFrameIndex); - cachedNode.setType(FRAME_CACHEDNODETYPE); -#if DUMP_NAV_CACHE - cachedNode.mDebug.mNodeIndex = nodeIndex; - cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex( - node, nodeIndex, NULL); -#endif - cachedFrame->add(cachedNode); - CachedFrame* childPtr = cachedFrame->lastChild(); - BuildFrame(root, child, cachedRoot, childPtr); - continue; - } - int tabIndex = node->tabIndex(); - Node* lastChild = node->lastChild(); - if (tabIndex <= 0) - tabIndex = tabIndexTracker.last().mTabIndex; - else if (tabIndex > 0 && lastChild) { - DBG_NAV_LOGD("tabIndex=%d node=%p", tabIndex, node); - tabIndexTracker.grow(tabIndexTracker.size() + 1); - TabIndexTracker& indexTracker = tabIndexTracker.last(); - indexTracker.mTabIndex = tabIndex; - indexTracker.mLastChild = OneAfter(lastChild); - } - RenderObject* nodeRenderer = node->renderer(); - bool isTransparent = false; - bool hasCursorRing = true; - if (nodeRenderer != NULL) { - RenderStyle* style = nodeRenderer->style(); - if (style->visibility() == HIDDEN) - continue; - isTransparent = nodeRenderer->hasBackground() == false; -#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR - hasCursorRing = style->tapHighlightColor().alpha() > 0; -#endif -#if USE(ACCELERATED_COMPOSITING) - // If this renderer has its own layer and the layer is composited, - // start tracking it. - if (lastChild && nodeRenderer->hasLayer() && toRenderBoxModelObject(nodeRenderer)->layer()->backing()) - TrackLayer(layerTracker, nodeRenderer, lastChild, globalOffsetX, globalOffsetY); -#endif - } - bool more = walk.mMore; - walk.reset(); - // GetGlobalBounds(node, &bounds, false); - bool hasClip = false; - bool hasMouseOver = false; - bool isUnclipped = false; - bool isFocus = node == focused; - bool takesFocus = false; - int columnGap = 0; - int imageCount = 0; - TextDirection direction = LTR; - String exported; - CachedNodeType type = NORMAL_CACHEDNODETYPE; - CachedInput cachedInput; - IntRect bounds; - IntRect absBounds; - IntRect originalAbsBounds; - ColumnInfo* columnInfo = NULL; - if (node->hasTagName(HTMLNames::areaTag)) { - type = AREA_CACHEDNODETYPE; - HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node); - bounds = getAreaRect(area); - originalAbsBounds = bounds; - bounds.move(globalOffsetX, globalOffsetY); - absBounds = bounds; - isUnclipped = true; // FIXME: areamaps require more effort to detect - // assume areamaps are always visible for now - takesFocus = true; - goto keepNode; - } - if (nodeRenderer == NULL) - continue; - - // some common setup - absBounds = nodeRenderer->absoluteBoundingBoxRect(); - originalAbsBounds = absBounds; - absBounds.move(globalOffsetX, globalOffsetY); - hasClip = nodeRenderer->hasOverflowClip(); - - if (checkForPluginViewThatWantsFocus(nodeRenderer)) { - bounds = absBounds; - isUnclipped = true; - takesFocus = true; - type = PLUGIN_CACHEDNODETYPE; - goto keepNode; - } - // Only use the root contentEditable element - if (node->rendererIsEditable() && !node->parentOrHostNode()->rendererIsEditable()) { - bounds = absBounds; - takesFocus = true; - type = CONTENT_EDITABLE_CACHEDNODETYPE; - goto keepNode; - } - if (nodeRenderer->isRenderBlock()) { - RenderBlock* renderBlock = (RenderBlock*) nodeRenderer; - if (renderBlock->hasColumns()) { - columnInfo = renderBlock->columnInfo(); - columnGap = renderBlock->columnGap(); - direction = renderBlock->style()->direction(); - } - } - if ((hasClip != false || columnInfo != NULL) && lastChild) { - clipTracker.grow(clipTracker.size() + 1); - ClipColumnTracker& clip = clipTracker.last(); - clip.mBounds = absBounds; - clip.mLastChild = OneAfter(lastChild); - clip.mNode = node; - clip.mColumnInfo = columnInfo; - clip.mColumnGap = columnGap; - clip.mHasClip = hasClip; - clip.mDirection = direction; - if (columnInfo != NULL) { - const IntRect& oRect = ((RenderBox*)nodeRenderer)->visualOverflowRect(); - clip.mBounds.move(oRect.x(), oRect.y()); - } - } - if (node->isTextNode() && mAllowableTypes != NORMAL_CACHEDNODE_BITS) { - if (last->mSomeParentTakesFocus) // don't look at text inside focusable node - continue; - CachedNodeType checkType; - if (isFocusableText(&walk, more, node, &checkType, - &exported) == false) - continue; - #if DUMP_NAV_CACHE - { - char buffer[DEBUG_BUFFER_SIZE]; - mDebug.init(buffer, sizeof(buffer)); - mDebug.print("text link found: "); - mDebug.wideString(exported); - DUMP_NAV_LOGD("%s\n", buffer); - } - #endif - type = checkType; - // !!! test ! is the following line correctly needed for frames to work? - cachedNode.init(node); - const ClipColumnTracker& clipTrack = clipTracker.last(); - const IntRect& clip = clipTrack.mHasClip ? clipTrack.mBounds : - IntRect(0, 0, INT_MAX, INT_MAX); - if (ConstructTextRects((WebCore::Text*) node, walk.mStart, - (WebCore::Text*) walk.mFinalNode, walk.mEnd, globalOffsetX, - globalOffsetY, &bounds, clip, &cachedNode.mCursorRing) == false) - continue; - absBounds = bounds; - cachedNode.setBounds(bounds); - if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) - continue; - if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) - continue; - isUnclipped = true; // FIXME: to hide or partially occlude synthesized links, each - // focus ring will also need the offset and length of characters - // used to produce it - goto keepTextNode; - } - if (node->hasTagName(WebCore::HTMLNames::inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(node); - if (input->isTextField()) { - if (input->readOnly()) - continue; - type = TEXT_INPUT_CACHEDNODETYPE; - cachedInput.init(); - cachedInput.setAutoComplete(input->autoComplete()); - cachedInput.setSpellcheck(input->spellcheck()); - cachedInput.setFormPointer(input->form()); - cachedInput.setIsTextField(true); - exported = input->value().threadsafeCopy(); - cachedInput.setMaxLength(input->maxLength()); - cachedInput.setTypeFromElement(input); - // If this does not need to be threadsafe, we can use crossThreadString(). - // See http://trac.webkit.org/changeset/49160. - cachedInput.setName(input->name().string().threadsafeCopy()); - // can't detect if this is drawn on top (example: deviant.com login parts) - isUnclipped = isTransparent; - } else if (input->isInputTypeHidden()) - continue; - else if (input->isRadioButton() || input->isCheckbox()) - isTransparent = false; - } else if (node->hasTagName(HTMLNames::textareaTag)) { - HTMLTextAreaElement* area = static_cast<HTMLTextAreaElement*>(node); - if (area->readOnly()) - continue; - cachedInput.init(); - type = TEXT_INPUT_CACHEDNODETYPE; - cachedInput.setFormPointer(area->form()); - cachedInput.setIsTextArea(true); - cachedInput.setSpellcheck(area->spellcheck()); - exported = area->value().threadsafeCopy(); - } else if (node->hasTagName(HTMLNames::aTag)) { - const HTMLAnchorElement* anchorNode = - (const HTMLAnchorElement*) node; - if (!anchorNode->isFocusable() && !HasTriggerEvent(node)) - continue; - if (node->disabled()) - continue; - hasMouseOver = NodeHasEventListeners(node, &eventNames().mouseoverEvent, 1); - type = ANCHOR_CACHEDNODETYPE; - KURL href = anchorNode->href(); - if (!href.isEmpty() && !WebCore::protocolIsJavaScript(href.string())) - // Set the exported string for all non-javascript anchors. - exported = href.string().threadsafeCopy(); - } else if (node->hasTagName(HTMLNames::selectTag)) { - type = SELECT_CACHEDNODETYPE; - } - if (type == TEXT_INPUT_CACHEDNODETYPE) { - RenderTextControl* renderText = - static_cast<RenderTextControl*>(nodeRenderer); - if (isFocus) - cachedRoot->setSelection(renderText->selectionStart(), renderText->selectionEnd()); - // FIXME: Are we sure there will always be a style and font, and it's correct? - RenderStyle* style = nodeRenderer->style(); - if (style) { - isUnclipped |= !style->hasAppearance(); - int lineHeight = -1; - Length lineHeightLength = style->lineHeight(); - // If the lineHeight is negative, WebTextView will calculate it - // based on the text size, using the Paint. - // See RenderStyle.computedLineHeight. - if (lineHeightLength.isPositive()) - lineHeight = style->computedLineHeight(); - cachedInput.setLineHeight(lineHeight); - cachedInput.setTextSize(style->font().size()); - cachedInput.setIsRtlText(style->direction() == RTL - || style->textAlign() == WebCore::RIGHT - || style->textAlign() == WebCore::WEBKIT_RIGHT); - } - cachedInput.setPaddingLeft(renderText->paddingLeft() + renderText->borderLeft()); - cachedInput.setPaddingTop(renderText->paddingTop() + renderText->borderTop()); - cachedInput.setPaddingRight(renderText->paddingRight() + renderText->borderRight()); - cachedInput.setPaddingBottom(renderText->paddingBottom() + renderText->borderBottom()); - } - takesFocus = true; - bounds = absBounds; - if (type != ANCHOR_CACHEDNODETYPE) { - bool isFocusable = node->isKeyboardFocusable(NULL) || - node->isMouseFocusable() || node->isFocusable(); - if (isFocusable == false) { - if (node->disabled()) - continue; - bool overOrOut = HasOverOrOut(node); - bool hasTrigger = HasTriggerEvent(node); - if (overOrOut == false && hasTrigger == false) - continue; - takesFocus = hasTrigger; - } - } - keepNode: - cachedNode.init(node); - cachedNode.setBounds(bounds); - cachedNode.mCursorRing.append(bounds); - keepTextNode: - IntRect clip = hasClip ? bounds : absBounds; - size_t clipIndex = clipTracker.size(); - if (clipTracker.last().mNode == node) - clipIndex -= 1; - while (--clipIndex > 0) { - const ClipColumnTracker& clipTrack = clipTracker.at(clipIndex); - if (clipTrack.mHasClip == false) { - adjustForColumns(clipTrack, &cachedNode, &absBounds, static_cast<RenderBlock*>(nodeRenderer)); - continue; - } - const IntRect& parentClip = clipTrack.mBounds; - if (hasClip == false && type == ANCHOR_CACHEDNODETYPE) - clip = parentClip; - else - clip.intersect(parentClip); - hasClip = true; - } - bool isInLayer = false; -#if USE(ACCELERATED_COMPOSITING) - // If this renderer has a composited parent layer (including itself), - // add the node to the cached layer. - // FIXME: does not work for area rects - RenderLayer* enclosingLayer = nodeRenderer->enclosingLayer(); - if (enclosingLayer && enclosingLayer->enclosingCompositingLayer()) { - LayerAndroid* layer = layerTracker.last().mLayer; - if (layer) { - const IntRect& layerClip = layerTracker.last().mBounds; - if (!layerClip.isEmpty() && !cachedNode.clip(layerClip)) { - DBG_NAV_LOGD("skipped on layer clip %d", cacheIndex); - continue; // skip this node if outside of the clip - } - isInLayer = true; - isUnclipped = true; // assume that layers do not have occluded nodes - hasClip = false; - AddLayer(cachedFrame, cachedFrame->size(), layerClip.location(), - layer->uniqueId()); - } - } -#endif - if (hasClip) { - if (clip.isEmpty()) - continue; // skip this node if clip prevents all drawing - else if (cachedNode.clip(clip) == false) - continue; // skip this node if outside of the clip - } - cachedNode.setExport(exported); - cachedNode.setHasCursorRing(hasCursorRing); - cachedNode.setHasMouseOver(hasMouseOver); - cachedNode.setHitBounds(absBounds); - cachedNode.setIndex(cacheIndex); - cachedNode.setIsFocus(isFocus); - cachedNode.setIsInLayer(isInLayer); - cachedNode.setIsTransparent(isTransparent); - cachedNode.setIsUnclipped(isUnclipped); - cachedNode.setOriginalAbsoluteBounds(originalAbsBounds); - cachedNode.setParentIndex(last->mCachedNodeIndex); - cachedNode.setParentGroup(ParentWithChildren(node)); - cachedNode.setSingleImage(imageCount == 1); - cachedNode.setTabIndex(tabIndex); - cachedNode.setType(type); - if (type == TEXT_INPUT_CACHEDNODETYPE) { - cachedFrame->add(cachedInput); - cachedNode.setDataIndex(textInputIndex); - textInputIndex++; - } else - cachedNode.setDataIndex(-1); -#if DUMP_NAV_CACHE - cachedNode.mDebug.mNodeIndex = nodeIndex; - cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex( - node, nodeIndex, (Node*) cachedNode.parentGroup()); -#endif - cachedFrame->add(cachedNode); - { - int lastIndex = cachedFrame->size() - 1; - if (node == focused) { - CachedNode* cachedNodePtr = cachedFrame->getIndex(lastIndex); - cachedRoot->setCachedFocus(cachedFrame, cachedNodePtr); - } - if (lastChild != NULL) { - tracker.grow(tracker.size() + 1); - FocusTracker& working = tracker.last(); - working.mCachedNodeIndex = lastIndex; - working.mLastChild = OneAfter(lastChild); - last = &tracker.at(tracker.size() - 2); - working.mSomeParentTakesFocus = last->mSomeParentTakesFocus | takesFocus; - } - } - cacheIndex++; - } - while (tracker.size() > 1) { - FocusTracker* last = &tracker.last(); - int lastChildIndex = cachedFrame->size() - 1; - if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex)) - cacheIndex--; - tracker.removeLast(); - } -} - -bool CacheBuilder::CleanUpContainedNodes(CachedRoot* cachedRoot, - CachedFrame* cachedFrame, const FocusTracker* last, int lastChildIndex) -{ - // if outer is body, disable outer - // or if there's more than one inner, disable outer - // or if inner is keyboard focusable, disable outer - // else disable inner by removing it - int childCount = lastChildIndex - last->mCachedNodeIndex; - if (childCount == 0) - return false; - CachedNode* lastCached = cachedFrame->getIndex(last->mCachedNodeIndex); - Node* lastNode = (Node*) lastCached->nodePointer(); - if ((childCount > 1 && lastNode->hasTagName(HTMLNames::selectTag) == false) || - lastNode->hasTagName(HTMLNames::bodyTag) || - lastNode->hasTagName(HTMLNames::formTag)) { - lastCached->setBounds(IntRect(0, 0, 0, 0)); - lastCached->mCursorRing.clear(); - return false; - } - CachedNode* onlyChildCached = cachedFrame->lastNode(); - Node* onlyChild = (Node*) onlyChildCached->nodePointer(); - bool outerIsMouseMoveOnly = - lastNode->isKeyboardFocusable(NULL) == false && - lastNode->isMouseFocusable() == false && - lastNode->isFocusable() == false && - HasOverOrOut(lastNode) == true && - HasTriggerEvent(lastNode) == false; - if (onlyChildCached->parent() == lastCached) - onlyChildCached->setParentIndex(lastCached->parentIndex()); - bool hasFocus = lastCached->isFocus() || onlyChildCached->isFocus(); - if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL) - || onlyChildCached->isPlugin()) { - int index = lastCached->index(); - *lastCached = *onlyChildCached; - lastCached->setIndex(index); - CachedFrame* frame = cachedFrame->hasFrame(lastCached); - if (frame) - frame->setIndexInParent(index); - } - cachedFrame->removeLast(); - if (hasFocus) - cachedRoot->setCachedFocus(cachedFrame, cachedFrame->lastNode()); - return true; -} - -Node* CacheBuilder::currentFocus() const -{ - Frame* frame = FrameAnd(this); - Document* doc = frame->document(); - if (doc != NULL) { - Node* focus = doc->focusedNode(); - if (focus != NULL) - return focus; - } - Frame* child = frame->tree()->firstChild(); - while (child) { - CacheBuilder* cacheBuilder = Builder(child); - Node* focus = cacheBuilder->currentFocus(); - if (focus) - return focus; - child = child->tree()->nextSibling(); - } - return NULL; -} - -static bool strCharCmp(const char* matches, const UChar* test, int wordLength, - int wordCount) -{ - for (int index = 0; index < wordCount; index++) { - for (int inner = 0; inner < wordLength; inner++) { - if (matches[inner] != test[inner]) { - matches += wordLength; - goto next; - } - } - return true; -next: - ; - } - return false; -} - -static const int stateTwoLetter[] = { - 0x02060c00, // A followed by: [KLRSZ] - 0x00000000, // B - 0x00084001, // C followed by: [AOT] - 0x00000014, // D followed by: [CE] - 0x00000000, // E - 0x00001800, // F followed by: [LM] - 0x00100001, // G followed by: [AU] - 0x00000100, // H followed by: [I] - 0x00002809, // I followed by: [ADLN] - 0x00000000, // J - 0x01040000, // K followed by: [SY] - 0x00000001, // L followed by: [A] - 0x000ce199, // M followed by: [ADEHINOPST] - 0x0120129c, // N followed by: [CDEHJMVY] - 0x00020480, // O followed by: [HKR] - 0x00420001, // P followed by: [ARW] - 0x00000000, // Q - 0x00000100, // R followed by: [I] - 0x0000000c, // S followed by: [CD] - 0x00802000, // T followed by: [NX] - 0x00080000, // U followed by: [T] - 0x00080101, // V followed by: [AIT] - 0x01200101 // W followed by: [AIVY] -}; - -static const char firstIndex[] = { - 0, 5, 5, 8, 10, 10, 12, 14, - 15, 19, 19, 21, 22, 32, 40, 43, - 46, 46, 47, 49, 51, 52, 55, 59 -}; - -// from http://infolab.stanford.edu/~manku/bitcount/bitcount.html -#define TWO(c) (0x1u << (c)) -#define MASK(c) (((unsigned int)(-1)) / (TWO(TWO(c)) + 1u)) -#define COUNT(x,c) ((x) & MASK(c)) + (((x) >> (TWO(c))) & MASK(c)) - -int bitcount (unsigned int n) -{ - n = COUNT(n, 0); - n = COUNT(n, 1); - n = COUNT(n, 2); - n = COUNT(n, 3); - return COUNT(n, 4); -} - -#undef TWO -#undef MASK -#undef COUNT - -static bool isUnicodeSpace(UChar ch) -{ - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0; -} - -static bool validZip(int stateIndex, const UChar* zipPtr) -{ - enum USState { - AP = -4, // AP (military base in the Pacific) - AA = -3, // AA (military base inside the US) - AE = -2, // AE (military base outside the US) - XX = -1, // (not in use) - AK = 0, // AK Alaska - AL = 1, // AL Alabama - AR = 2, // AR Arkansas - AS = 3, // AS American Samoa - AZ = 4, // AZ Arizona - CA = 5, // CA California - CO = 6, // CO Colorado - CT = 7, // CT Connecticut - DC = 8, // DC District of Columbia - DE = 9, // DE Delaware - FL = 10, // FL Florida - FM = 11, // FM Federated States of Micronesia - GA = 12, // GA Georgia - GU = 13, // GU Guam - HI = 14, // HI Hawaii - IA = 15, // IA Iowa - ID = 16, // ID Idaho - IL = 17, // IL Illinois - IN = 18, // IN Indiana - KS = 19, // KS Kansas - KY = 20, // KY Kentucky - LA = 21, // LA Louisiana - MA = 22, // MA Massachusetts - MD = 23, // MD Maryland - ME = 24, // ME Maine - MH = 25, // MH Marshall Islands - MI = 26, // MI Michigan - MN = 27, // MN Minnesota - MO = 28, // MO Missouri - MP = 29, // MP Northern Mariana Islands - MS = 30, // MS Mississippi - MT = 31, // MT Montana - NC = 32, // NC North Carolina - ND = 33, // ND North Dakota - NE = 34, // NE Nebraska - NH = 35, // NH New Hampshire - NJ = 36, // NJ New Jersey - NM = 37, // NM New Mexico - NV = 38, // NV Nevada - NY = 39, // NY New York - OH = 40, // OH Ohio - OK = 41, // OK Oklahoma - OR = 42, // OR Oregon - PA = 43, // PA Pennsylvania - PR = 44, // PR Puerto Rico - PW = 45, // PW Palau - RI = 46, // RI Rhode Island - SC = 47, // SC South Carolina - SD = 48, // SD South Dakota - TN = 49, // TN Tennessee - TX = 50, // TX Texas - UT = 51, // UT Utah - VA = 52, // VA Virginia - VI = 53, // VI Virgin Islands - VT = 54, // VT Vermont - WA = 55, // WA Washington - WI = 56, // WI Wisconsin - WV = 57, // WV West Virginia - WY = 58, // WY Wyoming - }; - - static const USState stateForZipPrefix[] = { - // 0 1 2 3 4 5 6 7 8 9 - XX, XX, XX, XX, XX, NY, PR, PR, VI, PR, // 000-009 - MA, MA, MA, MA, MA, MA, MA, MA, MA, MA, // 010-019 - MA, MA, MA, MA, MA, MA, MA, MA, RI, RI, // 020-029 - NH, NH, NH, NH, NH, NH, NH, NH, NH, ME, // 030-039 - ME, ME, ME, ME, ME, ME, ME, ME, ME, ME, // 040-049 - VT, VT, VT, VT, VT, MA, VT, VT, VT, VT, // 050-059 - CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 060-069 - NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, // 070-079 - NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, NJ, // 080-089 - AE, AE, AE, AE, AE, AE, AE, AE, AE, XX, // 090-099 - NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 100-109 - NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 110-119 - NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 120-129 - NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 130-139 - NY, NY, NY, NY, NY, NY, NY, NY, NY, NY, // 140-149 - PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 150-159 - PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 160-169 - PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 170-179 - PA, PA, PA, PA, PA, PA, PA, PA, PA, PA, // 180-189 - PA, PA, PA, PA, PA, PA, PA, DE, DE, DE, // 190-199 - DC, VA, DC, DC, DC, DC, MD, MD, MD, MD, // 200-209 - MD, MD, MD, XX, MD, MD, MD, MD, MD, MD, // 210-219 - VA, VA, VA, VA, VA, VA, VA, VA, VA, VA, // 220-229 - VA, VA, VA, VA, VA, VA, VA, VA, VA, VA, // 230-239 - VA, VA, VA, VA, VA, VA, VA, WV, WV, WV, // 240-249 - WV, WV, WV, WV, WV, WV, WV, WV, WV, WV, // 250-259 - WV, WV, WV, WV, WV, WV, WV, WV, WV, XX, // 260-269 - NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, // 270-279 - NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, // 280-289 - SC, SC, SC, SC, SC, SC, SC, SC, SC, SC, // 290-299 - GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, // 300-309 - GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, // 310-319 - FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, // 320-329 - FL, FL, FL, FL, FL, FL, FL, FL, FL, FL, // 330-339 - AA, FL, FL, XX, FL, XX, FL, FL, XX, FL, // 340-349 - AL, AL, AL, XX, AL, AL, AL, AL, AL, AL, // 350-359 - AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // 360-369 - TN, TN, TN, TN, TN, TN, TN, TN, TN, TN, // 370-379 - TN, TN, TN, TN, TN, TN, MS, MS, MS, MS, // 380-389 - MS, MS, MS, MS, MS, MS, MS, MS, GA, GA, // 390-399 - KY, KY, KY, KY, KY, KY, KY, KY, KY, KY, // 400-409 - KY, KY, KY, KY, KY, KY, KY, KY, KY, XX, // 410-419 - KY, KY, KY, KY, KY, KY, KY, KY, XX, XX, // 420-429 - OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 430-439 - OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 440-449 - OH, OH, OH, OH, OH, OH, OH, OH, OH, OH, // 450-459 - IN, IN, IN, IN, IN, IN, IN, IN, IN, IN, // 460-469 - IN, IN, IN, IN, IN, IN, IN, IN, IN, IN, // 470-479 - MI, MI, MI, MI, MI, MI, MI, MI, MI, MI, // 480-489 - MI, MI, MI, MI, MI, MI, MI, MI, MI, MI, // 490-499 - IA, IA, IA, IA, IA, IA, IA, IA, IA, IA, // 500-509 - IA, IA, IA, IA, IA, IA, IA, XX, XX, XX, // 510-519 - IA, IA, IA, IA, IA, IA, IA, IA, IA, XX, // 520-529 - WI, WI, WI, XX, WI, WI, XX, WI, WI, WI, // 530-539 - WI, WI, WI, WI, WI, WI, WI, WI, WI, WI, // 540-549 - MN, MN, XX, MN, MN, MN, MN, MN, MN, MN, // 550-559 - MN, MN, MN, MN, MN, MN, MN, MN, XX, DC, // 560-569 - SD, SD, SD, SD, SD, SD, SD, SD, XX, XX, // 570-579 - ND, ND, ND, ND, ND, ND, ND, ND, ND, XX, // 580-589 - MT, MT, MT, MT, MT, MT, MT, MT, MT, MT, // 590-599 - IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, // 600-609 - IL, IL, IL, IL, IL, IL, IL, IL, IL, IL, // 610-619 - IL, XX, IL, IL, IL, IL, IL, IL, IL, IL, // 620-629 - MO, MO, XX, MO, MO, MO, MO, MO, MO, MO, // 630-639 - MO, MO, XX, XX, MO, MO, MO, MO, MO, MO, // 640-649 - MO, MO, MO, MO, MO, MO, MO, MO, MO, XX, // 650-659 - KS, KS, KS, XX, KS, KS, KS, KS, KS, KS, // 660-669 - KS, KS, KS, KS, KS, KS, KS, KS, KS, KS, // 670-679 - NE, NE, XX, NE, NE, NE, NE, NE, NE, NE, // 680-689 - NE, NE, NE, NE, XX, XX, XX, XX, XX, XX, // 690-699 - LA, LA, XX, LA, LA, LA, LA, LA, LA, XX, // 700-709 - LA, LA, LA, LA, LA, XX, AR, AR, AR, AR, // 710-719 - AR, AR, AR, AR, AR, AR, AR, AR, AR, AR, // 720-729 - OK, OK, XX, TX, OK, OK, OK, OK, OK, OK, // 730-739 - OK, OK, XX, OK, OK, OK, OK, OK, OK, OK, // 740-749 - TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 750-759 - TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 760-769 - TX, XX, TX, TX, TX, TX, TX, TX, TX, TX, // 770-779 - TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 780-789 - TX, TX, TX, TX, TX, TX, TX, TX, TX, TX, // 790-799 - CO, CO, CO, CO, CO, CO, CO, CO, CO, CO, // 800-809 - CO, CO, CO, CO, CO, CO, CO, XX, XX, XX, // 810-819 - WY, WY, WY, WY, WY, WY, WY, WY, WY, WY, // 820-829 - WY, WY, ID, ID, ID, ID, ID, ID, ID, XX, // 830-839 - UT, UT, UT, UT, UT, UT, UT, UT, XX, XX, // 840-849 - AZ, AZ, AZ, AZ, XX, AZ, AZ, AZ, XX, AZ, // 850-859 - AZ, XX, XX, AZ, AZ, AZ, XX, XX, XX, XX, // 860-869 - NM, NM, NM, NM, NM, NM, XX, NM, NM, NM, // 870-879 - NM, NM, NM, NM, NM, TX, XX, XX, XX, NV, // 880-889 - NV, NV, XX, NV, NV, NV, XX, NV, NV, XX, // 890-899 - CA, CA, CA, CA, CA, CA, CA, CA, CA, XX, // 900-909 - CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 910-919 - CA, CA, CA, CA, CA, CA, CA, CA, CA, XX, // 920-929 - CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 930-939 - CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 940-949 - CA, CA, CA, CA, CA, CA, CA, CA, CA, CA, // 950-959 - CA, CA, AP, AP, AP, AP, AP, HI, HI, GU, // 960-969 - OR, OR, OR, OR, OR, OR, OR, OR, OR, OR, // 970-979 - WA, WA, WA, WA, WA, WA, WA, XX, WA, WA, // 980-989 - WA, WA, WA, WA, WA, AK, AK, AK, AK, AK, // 990-999 - }; - - if (!zipPtr || !zipPtr[0] || !zipPtr[1] || !zipPtr[2]) - return false; - if (zipPtr[0] < '0' || zipPtr[0] > '9' || - zipPtr[1] < '0' || zipPtr[1] > '9' || - zipPtr[2] < '0' || zipPtr[2] > '9') - return false; - - int zip = zipPtr[0] - '0'; - zip *= 10; - zip += zipPtr[1] - '0'; - zip *= 10; - zip += zipPtr[2] - '0'; - return stateForZipPrefix[zip] == stateIndex; -} - -#define MAX_PLACE_NAME_LENGTH 25 // the longest allowable one word place name - -CacheBuilder::FoundState CacheBuilder::FindAddress(const UChar* chars, - unsigned length, int* start, int* end, bool caseInsensitive) -{ - FindState addressState; - FindReset(&addressState); - addressState.mWords[0] = addressState.mStarts[0] = chars; - addressState.mCaseInsensitive = caseInsensitive; - FoundState state = FindPartialAddress(chars, chars, length, &addressState); - if (state == FOUND_PARTIAL && addressState.mProgress == ZIP_CODE && - addressState.mNumberCount == 0) { - addressState.mProgress = FIND_STREET; - state = FindPartialAddress(NULL, NULL, 0, &addressState); - } - *start = addressState.mStartResult; - *end = addressState.mEndResult; - return state; -} - -CacheBuilder::FoundState CacheBuilder::FindPartialAddress(const UChar* baseChars, - const UChar* chars, unsigned length, FindState* s) -{ - // lower case letters are optional; trailing asterisk is optional 's' - static char const* const longStreetNames[] = { - "\x04" "LleY" "\x04" "NneX" "\x05" "RCade" "\x05" "VEnue" "\x06" "LAMEDA", // A - "\x04" "aYoU" "\x04" "eaCH" "\x03" "eND" "\x05" "LuFf*" "\x05" "oTtoM" - "\x08" "ouLeVarD" "\x05" "Ranch" "\x05" "RidGe" "\x05" "RooK*" - "\x04" "urG*" "\x05" "YPass" "\x07" "roadWAY", // B - "\x05" "AMINO" - "\x03" "amP" "\x05" "anYoN" "\x03" "aPE" "\x07" "auSeWaY" "\x06" "enTeR*" - "\x06" "IRcle*" "\x05" "LiFf*" "\x03" "LuB" "\x05" "oMmoN" "\x06" "ORner*" - "\x05" "ouRSE" "\x05" "ourT*" "\x04" "oVe*" "\x04" "ReeK" "\x07" "REScent" - "\x04" "ReST" "\x07" "ROSSING" "\x08" "ROSSROAD" "\x04" "URVe" - "\x05" "AMINO" "\x06" "IRCULO" "\x07" "REScent", // C - "\x03" "aLe" "\x02" "aM" "\x05" "iVide" "\x05" "Rive*", // D - "\x06" "STate*" "\x09" "XPresswaY" "\x09" "XTension*", // E - "\x04" "ALL*" "\x04" "eRrY" "\x05" "ieLD*" "\x04" "LaT*" "\x04" "oRD*" - "\x05" "oReST" "\x05" "oRGe*" "\x04" "oRK*" "\x03" "orT" "\x06" "reeWaY", // F - "\x06" "arDeN*" "\x06" "aTeWaY" "\x04" "LeN*" "\x05" "ReeN*" "\x05" "RoVe*", // G - "\x06" "arBoR*" "\x04" "aVeN" "\x06" "eighTS" "\x06" "ighWaY" "\x04" "iLl*" - "\x05" "OLloW", // H - "\x04" "NLeT" "\x06" "Sland*" "\x03" "SLE", // I - "\x08" "unCTion*", // J - "\x03" "eY*" "\x05" "NoLl*", // K - "\x04" "aKe*" "\x03" "AND" "\x06" "aNDinG" "\x03" "aNe" "\x05" "iGhT*" - "\x03" "oaF" "\x04" "oCK*" "\x04" "oDGe" "\x03" "OOP", // L - "\x03" "ALL" "\x05" "aNoR*" "\x06" "eaDoW*" "\x03" "EWS" "\x04" "iLl*" - "\x06" "iSsioN" "\x07" "oTorWaY" "\x04" "ounT" "\x08" "ounTaiN*", // M - "\x03" "eCK", // N - "\x06" "RCHard" "\x03" "VAL" "\x07" "verPASs", // O - "\x04" "ARK*" "\x07" "arKWaY*" "\x03" "ASS" "\x06" "aSsaGE" "\x03" "ATH" - "\x03" "IKE" "\x04" "iNE*" "\x04" "Lace" "\x05" "LaiN*" "\x04" "LaZa" - "\x05" "oinT*" "\x04" "oRT*" "\x06" "Rairie" "\x06" "RIVADA", // P - NULL, - "\x05" "ADiaL" "\x03" "AMP" "\x04" "aNCH" "\x05" "aPiD*" - "\x03" "eST" - "\x05" "iDGe*" "\x04" "IVer" "\x04" "oaD*" "\x04" "ouTE" "\x02" "OW" - "\x02" "UE" "\x02" "UN", // R - "\x05" "HoaL*" "\x05" "HoRe*" "\x05" "KyWaY" "\x06" "PrinG*" "\x04" "PUR*" - "\x06" "Quare*" "\x06" "TAtion" "\x08" "TRAvenue" "\x05" "TReaM" - "\x06" "Treet*" "\x05" "uMmiT" "\x07" "PeeDWaY", // S - "\x06" "ERrace" "\x09" "hRoughWaY" "\x04" "RaCE" "\x04" "RAcK" "\x09" "RaFficwaY" - "\x04" "RaiL" "\x05" "UNneL" "\x07" "urnPiKE", // T - "\x08" "nderPASs" "\x05" "Nion*", // U - "\x06" "aLleY*" "\x06" "IAduct" "\x04" "ieW*" "\x07" "iLlaGe*" "\x04" "iLle" - "\x04" "ISta", // V - "\x04" "ALK*" "\x03" "ALL" "\x03" "AY*" "\x04" "eLl*", // W - "\x03" "ING" "\x02" "RD", // X - }; - - static char const* const longStateNames[] = { - "\x8e" "la" "\x85" "bama" "\x02" "\x84" "ska" "\x01" "\x8f" "merican Samoa" "\x04" - "\x91" "r" "\x86" "izona" "\x05" "\x87" "kansas" "\x03", - NULL, - "\x8b" "alifornia" "\x06" "\x95" "o" "\x87" "lorado" "\x07" "\x8a" "nnecticut" "\x08", - "\x89" "elaware" "\x0a" "\x95" "istrict of Columbia" "\x09", - NULL, - "\x9f" "ederated States of Micronesia" "\x0c" "\x88" "lorida" "\x0b", - "\x85" "uam" "\x0e" "\x88" "eorgia" "\x0d", - "\x87" "awaii" "\x0f", - "\x86" "daho" "\x11" "\x89" "llinois" "\x12" "\x88" "ndiana" "\x13" "\x85" - "owa" "\x10", - NULL, - "\x87" "ansas" "\x14" "\x89" "entucky" "\x15", - "\x8a" "ouisiana" "\x16", - "\x86" "aine" "\x19" "\x99" "ar" "\x8e" "shall Islands" "\x1a" "\x86" "yland" "\x18" - "\x8e" "assachusetts" "\x17" "\x93" "i" "\x87" "chigan" "\x1b" - "\x88" "nnesota" "\x1c" "\x93" "iss" "\x88" "issippi" "\x1f" "\x85" - "ouri" "\x1d" "\x88" "ontana" "\x20", - "\x90" "e" "\x87" "braska" "\x23" "\x85" "vada" "\x27" "\xa5" "ew " "\x8a" - "Hampshire" "\x24" "\x87" "Jersey" "\x25" "\x87" "Mexico" "\x26" - "\x85" "York" "\x28" "\x98" "orth " "\x89" "Carolina" "\x21" "\x87" - "Dakota" "\x22" "\x99" "orthern Mariana Islands" "\x1e", - "\x85" "hio" "\x29" "\x89" "klahoma" "\x2a" "\x87" "regon" "\x2b", - "\x86" "alau" "\x2e" "\x8d" "ennsylvania" "\x2c" "\x8c" "uerto Rico" "\x2d", - NULL, - "\x8d" "hode Island" "\x2f", - "\x98" "outh " "\x89" "Carolina" "\x30" "\x87" "Dakota" "\x31", - "\x90" "e" "\x88" "nnessee" "\x32" "\x84" "xas" "\x33", - "\x85" "tah" "\x34", - "\x88" "ermont" "\x37" "\x94" "irgin" "\x89" " Islands" "\x36" "\x83" "ia" "\x35", - "\x8b" "ashington" "\x38" "\x8e" "est Virginia" "\x3a" "\x8a" "isconsin" "\x39" - "\x88" "yoming" "\x3b" - }; - -#if 0 // DEBUG_NAV_UI - static char const* const progressNames[] = { - "NO_ADDRESS", - "SKIP_TO_SPACE", - "HOUSE_NUMBER", - "NUMBER_TRAILING_SPACE", - "ADDRESS_LINE", - "STATE_NAME", - "SECOND_HALF", - "ZIP_CODE", - "PLUS_4", - "FIND_STREET" - }; -#endif - // strategy: US only support at first - // look for a 1 - 5 digit number for a street number (no support for 'One Microsoft Way') - // ignore if preceded by '#', Suite, Ste, Rm - // look for two or more words (up to 5? North Frank Lloyd Wright Blvd) - // note: "The Circle at North Hills St." has six words, and a lower 'at' -- allow at, by, of, in, the, and, ... ? - // if a word starts with a lowercase letter, no match - // allow: , . - # / (for 1/2) ' " - // don't look for street name type yet - // look for one or two delimiters to represent possible 2nd addr line and city name - // look for either full state name, or state two letters, and/or zip code (5 or 9 digits) - // now look for street suffix, either in full or abbreviated form, with optional 's' if there's an asterisk - - s->mCurrentStart = chars; - s->mEnd = chars + length; - int candIndex = 0; - bool retryState; - bool mustBeAllUpper = false; - bool secondHalf = false; - chars -= 1; - UChar ch = s->mCurrent; - while (++chars <= s->mEnd) { - UChar prior = ch; - ch = chars < s->mEnd ? *chars : ' '; - switch (s->mProgress) { - case NO_ADDRESS: - if (WTF::isASCIIDigit(ch) == false) { - if (ch != 'O') // letter 'O', not zero - continue; - if (s->mEnd - chars < 3) - continue; - prior = *++chars; - ch = *++chars; - if ((prior != 'n' || ch != 'e') && (prior != 'N' || ch != 'E')) - continue; - if (isUnicodeSpace(*++chars) == false) - continue; - s->mProgress = ADDRESS_LINE; - s->mStartResult = chars - 3 - s->mCurrentStart; - continue; - } - if (isUnicodeSpace(prior) == false) { - s->mProgress = SKIP_TO_SPACE; - continue; - } - s->mNumberCount = 1; - s->mProgress = HOUSE_NUMBER; - s->mStartResult = chars - s->mCurrentStart; - continue; - case SKIP_TO_SPACE: - if (isUnicodeSpace(ch) == false) - continue; - break; - case HOUSE_NUMBER: - if (WTF::isASCIIDigit(ch)) { - if (++s->mNumberCount >= 6) - s->mProgress = SKIP_TO_SPACE; - continue; - } - if (WTF::isASCIIUpper(ch)) { // allow one letter after house number, e.g. 12A SKOLFIELD PL, HARPSWELL, ME 04079 - if (WTF::isASCIIDigit(prior) == false) - s->mProgress = SKIP_TO_SPACE; - continue; - } - if (ch == '-') { - if (s->mNumberCount > 0) { // permit 21-23 ELM ST - ++s->mNumberCount; - continue; - } - } - s->mNumberCount = 0; - s->mProgress = NUMBER_TRAILING_SPACE; - case NUMBER_TRAILING_SPACE: - if (isUnicodeSpace(ch)) - continue; - if (0 && WTF::isASCIIDigit(ch)) { - s->mNumberCount = 1; - s->mProgress = HOUSE_NUMBER; - s->mStartResult = chars - s->mCurrentStart; - continue; - } - if (WTF::isASCIIDigit(ch) == false && WTF::isASCIIUpper(ch) == false) - break; - s->mProgress = ADDRESS_LINE; - case ADDRESS_LINE: - if (WTF::isASCIIAlpha(ch) || ch == '\'' || ch == '-' || ch == '&' || ch == '(' || ch == ')') { - if (++s->mLetterCount > 1) { - s->mNumberWords &= ~(1 << s->mWordCount); - continue; - } - if (s->mNumberCount >= 5) - break; -// FIXME: the test below was added to give up on a non-address, but it -// incorrectly discards addresses where the house number is in one node -// and the street name is in the next; I don't recall what the failing case -// is that suggested this fix. -// if (s->mWordCount == 0 && s->mContinuationNode) -// return FOUND_NONE; - s->newWord(baseChars, chars); - if (WTF::isASCIILower(ch) && s->mNumberCount == 0) - s->mFirstLower = chars; - s->mNumberCount = 0; - if (WTF::isASCIILower(ch) || (WTF::isASCIIAlpha(ch) == false && ch != '-')) - s->mNumberWords &= ~(1 << s->mWordCount); - s->mUnparsed = true; - continue; - } else if (s->mLetterCount >= MAX_PLACE_NAME_LENGTH) { - break; - } else if (s->mFirstLower != NULL) { - if (s->mCaseInsensitive) - goto resetWord; - size_t length = chars - s->mFirstLower; - if (length > 3) - break; - if (length == 3 && strCharCmp("and" "the", s->mFirstLower, 3, 2) == false) - break; - if (length == 2 && strCharCmp("at" "by" "el" "in" "of", s->mFirstLower, 2, 5) == false) - break; - goto resetWord; - } - if (ch == ',' || ch == '*') { // delimits lines - // asterisk as delimiter: http://www.sa.sc.edu/wellness/members.html - if (++s->mLineCount > 5) - break; - goto lookForState; - } - if (isUnicodeSpace(ch) || prior == '-') { - lookForState: - if (s->mUnparsed == false) - continue; - const UChar* candidate = s->mWords[s->mWordCount]; - UChar firstLetter = candidate[0]; - if (WTF::isASCIIUpper(firstLetter) == false && WTF::isASCIIDigit(firstLetter) == false) - goto resetWord; - s->mWordCount++; - if ((s->mWordCount == 2 && s->mNumberWords == 3 && WTF::isASCIIDigit(s->mWords[1][1])) || // choose second of 8888 333 Main - (s->mWordCount >= sizeof(s->mWords) / sizeof(s->mWords[0]) - 1)) { // subtract 1 since state names may have two parts - // search for simple number already stored since first potential house # didn't pan out - if (s->mNumberWords == 0) - break; - int shift = 0; - while ((s->mNumberWords & (1 << shift)) == 0) - shift++; - s->mNumberWords >>= ++shift; - if (s->mBases[0] != s->mBases[shift]) // if we're past the original node, bail - break; - s->shiftWords(shift); - s->mStartResult = s->mWords[0] - s->mStarts[0]; - s->mWordCount -= shift; - // FIXME: need to adjust lineCount to account for discarded delimiters - } - if (s->mWordCount < 4) - goto resetWord; - firstLetter -= 'A'; - if (firstLetter > 'W' - 'A') - goto resetWord; - UChar secondLetter = candidate[1]; - if (prior == '-') - s->mLetterCount--; // trim trailing dashes, to accept CA-94043 - if (s->mLetterCount == 2) { - secondLetter -= 'A'; - if (secondLetter > 'Z' - 'A') - goto resetWord; - if ((stateTwoLetter[firstLetter] & 1 << secondLetter) != 0) { - // special case to ignore 'et al' - if (strCharCmp("ET", s->mWords[s->mWordCount - 2], 2, 1) == false) { - s->mStateWord = s->mWordCount - 1; - s->mZipHint = firstIndex[firstLetter] + - bitcount(stateTwoLetter[firstLetter] & ((1 << secondLetter) - 1)); - goto foundStateName; - } - } - goto resetWord; - } - s->mStates = longStateNames[firstLetter]; - if (s->mStates == NULL) - goto resetWord; - mustBeAllUpper = false; - s->mProgress = STATE_NAME; - unsigned char section = s->mStates[0]; - ASSERT(section > 0x80); - s->mSectionLength = section & 0x7f; - candIndex = 1; - secondHalf = false; - s->mStateWord = s->mWordCount - 1; - goto stateName; - } - if (WTF::isASCIIDigit(ch)) { - if (s->mLetterCount == 0) { - if (++s->mNumberCount > 1) - continue; - if (s->mWordCount == 0 && s->mContinuationNode) - return FOUND_NONE; - s->newWord(baseChars, chars); - s->mNumberWords |= 1 << s->mWordCount; - s->mUnparsed = true; - } - continue; - } - if (ch == '.') { // optionally can follow letters - if (s->mLetterCount == 0) - break; - if (s->mNumberCount > 0) - break; - continue; - } - if (ch == '/') // between numbers (1/2) between words (12 Main / Ste 4d) - goto resetWord; - if (ch == '#') // can precede numbers, allow it to appear randomly - goto resetWord; - if (ch == '"') // sometimes parts of addresses are quoted (FIXME: cite an example here) - continue; - break; - case SECOND_HALF: - if (WTF::isASCIIAlpha(ch)) { - if (s->mLetterCount == 0) { - s->newWord(baseChars, chars); - s->mWordCount++; - } - s->mLetterCount++; - continue; - } - if (WTF::isASCIIDigit(ch) == false) { - if (s->mLetterCount > 0) { - s->mProgress = STATE_NAME; - candIndex = 0; - secondHalf = true; - goto stateName; - } - continue; - } - s->mProgress = ADDRESS_LINE; - goto resetState; - case STATE_NAME: - stateName: - // pick up length of first section - do { - int stateIndex = 1; - int skip = 0; - int prefix = 0; - bool subStr = false; - do { - unsigned char match = s->mStates[stateIndex]; - if (match >= 0x80) { - if (stateIndex == s->mSectionLength) - break; - subStr = true; - // if (skip > 0) - // goto foundStateName; - prefix = candIndex; - skip = match & 0x7f; - match = s->mStates[++stateIndex]; - } - UChar candChar = s->mWords[s->mWordCount - 1][candIndex]; - if (mustBeAllUpper && WTF::isASCIILower(candChar)) - goto skipToNext; - if (match != candChar) { - if (match != WTF::toASCIILower(candChar)) { - skipToNext: - if (subStr == false) - break; - if (stateIndex == s->mSectionLength) { - if (secondHalf) { - s->mProgress = ADDRESS_LINE; - goto resetState; - } - break; - } - stateIndex += skip; - skip = 0; - candIndex = prefix; - continue; // try next substring - } - mustBeAllUpper = true; - } - int nextindex = stateIndex + 1; - if (++candIndex >= s->mLetterCount && s->mStates[nextindex] == ' ') { - s->mProgress = SECOND_HALF; - s->mStates += nextindex; - s->mSectionLength -= nextindex; - goto resetWord; - } - if (nextindex + 1 == s->mSectionLength || skip == 2) { - s->mZipHint = s->mStates[nextindex] - 1; - goto foundStateName; - } - stateIndex += 1; - skip -= 1; - } while (true); - s->mStates += s->mSectionLength; - ASSERT(s->mStates[0] == 0 || (unsigned) s->mStates[0] > 0x80); - s->mSectionLength = s->mStates[0] & 0x7f; - candIndex = 1; - subStr = false; - } while (s->mSectionLength != 0); - s->mProgress = ADDRESS_LINE; - goto resetState; - foundStateName: - s->mEndResult = chars - s->mCurrentStart; - s->mEndWord = s->mWordCount - 1; - s->mProgress = ZIP_CODE; - // a couple of delimiters is an indication that the state name is good - // or, a non-space / non-alpha-digit is also good - s->mZipDelimiter = s->mLineCount > 2 - || isUnicodeSpace(ch) == false - || chars == s->mEnd; - if (WTF::isASCIIDigit(ch)) - s->mZipStart = chars; - goto resetState; - case ZIP_CODE: - if (WTF::isASCIIDigit(ch)) { - int count = ++s->mNumberCount; - if (count == 1) { - if (WTF::isASCIIDigit(prior)) - ++s->mNumberCount; - else - s->mZipStart = chars; - } - if (count <= 9) - continue; - } else if (isUnicodeSpace(ch)) { - if (s->mNumberCount == 0) { - s->mZipDelimiter = true; // two spaces delimit state name - continue; - } - } else if (ch == '-') { - if (s->mNumberCount == 5 && validZip(s->mZipHint, s->mZipStart)) { - s->mNumberCount = 0; - s->mProgress = PLUS_4; - continue; - } - if (s->mNumberCount == 0) - s->mZipDelimiter = true; - } else if (WTF::isASCIIAlpha(ch) == false) - s->mZipDelimiter = true; - else { - if (s->mLetterCount == 0) { - s->newWord(baseChars, chars); - s->mUnparsed = true; - } - ++s->mLetterCount; - } - if (s->mNumberCount == 5 || s->mNumberCount == 9) { - if (validZip(s->mZipHint, s->mZipStart) == false) - goto noZipMatch; - s->mEndResult = chars - s->mCurrentStart; - s->mEndWord = s->mWordCount - 1; - } else if (s->mZipDelimiter == false) { - noZipMatch: - --chars; - s->mProgress = ADDRESS_LINE; - continue; - } - s->mProgress = FIND_STREET; - goto findStreet; - case PLUS_4: - if (WTF::isASCIIDigit(ch)) { - if (++s->mNumberCount <= 4) - continue; - } - if (isUnicodeSpace(ch)) { - if (s->mNumberCount == 0) - continue; - } - if (s->mNumberCount == 4) { - if (WTF::isASCIIAlpha(ch) == false) { - s->mEndResult = chars - s->mCurrentStart; - s->mEndWord = s->mWordCount - 1; - } - } else if (s->mNumberCount != 0) - break; - s->mProgress = FIND_STREET; - case FIND_STREET: - findStreet: - retryState = false; - for (int wordsIndex = s->mStateWord - 1; wordsIndex >= 0; --wordsIndex) { - const UChar* test = s->mWords[wordsIndex]; - UChar letter = test[0]; - letter -= 'A'; - if (letter > 'X' - 'A') - continue; - const char* names = longStreetNames[letter]; - if (names == NULL) - continue; - int offset; - while ((offset = *names++) != 0) { - int testIndex = 1; - bool abbr = false; - for (int idx = 0; idx < offset; idx++) { - char nameLetter = names[idx]; - char testUpper = WTF::toASCIIUpper(test[testIndex]); - if (nameLetter == '*') { - if (testUpper == 'S') - testIndex++; - break; - } - bool fullOnly = WTF::isASCIILower(nameLetter); - nameLetter = WTF::toASCIIUpper(nameLetter); - if (testUpper == nameLetter) { - if (abbr && fullOnly) - goto nextTest; - testIndex++; - continue; - } - if (fullOnly == false) - goto nextTest; - abbr = true; - } - letter = &test[testIndex] < s->mEnds[wordsIndex] ? - test[testIndex] : ' '; - if (WTF::isASCIIAlpha(letter) == false && WTF::isASCIIDigit(letter) == false) { - if (s->mNumberWords != 0) { - int shift = 0; - int wordReduction = -1; - do { - while ((s->mNumberWords & (1 << shift)) == 0) - shift++; - if (shift > wordsIndex) - break; - wordReduction = shift; - } while (s->mNumberWords >> ++shift != 0); - if (wordReduction >= 0) { - if (s->mContinuationNode) { - if (retryState) - break; - return FOUND_NONE; - } - s->mStartResult = s->mWords[wordReduction] - s->mStarts[wordReduction]; - } - } - if (wordsIndex != s->mStateWord - 1) - return FOUND_COMPLETE; - retryState = true; - } - nextTest: - names += offset; - } - } - if (retryState) { - s->mProgress = ADDRESS_LINE; - s->mStates = NULL; - continue; - } - if (s->mNumberWords != 0) { - unsigned shift = 0; - while ((s->mNumberWords & (1 << shift)) == 0) - shift++; - s->mNumberWords >>= ++shift; - if (s->mBases[0] != s->mBases[shift]) - return FOUND_NONE; - s->shiftWords(shift); - s->mStartResult = s->mWords[0] - s->mStarts[0]; - s->mWordCount -= shift; - s->mProgress = ADDRESS_LINE; - --chars; - continue; - } - break; - } - if (s->mContinuationNode) - return FOUND_NONE; - s->mProgress = NO_ADDRESS; - s->mWordCount = s->mLineCount = 0; - s->mNumberWords = 0; - resetState: - s->mStates = NULL; - resetWord: - s->mNumberCount = s->mLetterCount = 0; - s->mFirstLower = NULL; - s->mUnparsed = false; - } - s->mCurrent = ch; - return s->mProgress == NO_ADDRESS ? FOUND_NONE : FOUND_PARTIAL; -} - -// Recogize common email patterns only. Currently has lots of state, walks text forwards and backwards -- will be -// a real challenge to adapt to walk text across multiple nodes, I imagine -// FIXME: it's too hard for the caller to call these incrementally -- it's probably best for this to -// either walk the node tree directly or make a callout to get the next or previous node, if there is one -// walking directly will avoid adding logic in caller to track the multiple partial or full nodes that compose this -// text pattern. -CacheBuilder::FoundState CacheBuilder::FindPartialEMail(const UChar* chars, unsigned length, - FindState* s) -{ - // the following tables were generated by tests/browser/focusNavigation/BrowserDebug.cpp - // hand-edit at your own risk - static const int domainTwoLetter[] = { - 0x02df797c, // a followed by: [cdefgilmnoqrstuwxz] - 0x036e73fb, // b followed by: [abdefghijmnorstvwyz] - 0x03b67ded, // c followed by: [acdfghiklmnorsuvxyz] - 0x02005610, // d followed by: [ejkmoz] - 0x001e00d4, // e followed by: [ceghrstu] - 0x00025700, // f followed by: [ijkmor] - 0x015fb9fb, // g followed by: [abdefghilmnpqrstuwy] - 0x001a3400, // h followed by: [kmnrtu] - 0x000f7818, // i followed by: [delmnoqrst] - 0x0000d010, // j followed by: [emop] - 0x0342b1d0, // k followed by: [eghimnprwyz] - 0x013e0507, // l followed by: [abcikrstuvy] - 0x03fffccd, // m followed by: [acdghklmnopqrstuvwxyz] - 0x0212c975, // n followed by: [acefgilopruz] - 0x00001000, // o followed by: [m] - 0x014e3cf1, // p followed by: [aefghklmnrstwy] - 0x00000001, // q followed by: [a] - 0x00504010, // r followed by: [eouw] - 0x032a7fdf, // s followed by: [abcdeghijklmnortvyz] - 0x026afeec, // t followed by: [cdfghjklmnoprtvwz] - 0x03041441, // u followed by: [agkmsyz] - 0x00102155, // v followed by: [aceginu] - 0x00040020, // w followed by: [fs] - 0x00000000, // x - 0x00180010, // y followed by: [etu] - 0x00401001, // z followed by: [amw] - }; - - static char const* const longDomainNames[] = { - "\x03" "ero" "\x03" "rpa", // aero, arpa - "\x02" "iz", // biz - "\x02" "at" "\x02" "om" "\x03" "oop", // cat, com, coop - NULL, // d - "\x02" "du", // edu - NULL, // f - "\x02" "ov", // gov - NULL, // h - "\x03" "nfo" "\x02" "nt", // info, int - "\x03" "obs", // jobs - NULL, // k - NULL, // l - "\x02" "il" "\x03" "obi" "\x05" "useum", // mil, mobi, museum - "\x03" "ame" "\x02" "et", // name, net - "\x02" "rg", // , org - "\x02" "ro", // pro - NULL, // q - NULL, // r - NULL, // s - "\x05" "ravel", // travel - NULL, // u - NULL, // v - NULL, // w - NULL, // x - NULL, // y - NULL, // z - }; - - const UChar* start = chars; - const UChar* end = chars + length; - while (chars < end) { - UChar ch = *chars++; - if (ch != '@') - continue; - const UChar* atLocation = chars - 1; - // search for domain - ch = *chars++ | 0x20; // convert uppercase to lower - if (ch < 'a' || ch > 'z') - continue; - while (chars < end) { - ch = *chars++; - if (IsDomainChar(ch) == false) - goto nextAt; - if (ch != '.') - continue; - UChar firstLetter = *chars++ | 0x20; // first letter of the domain - if (chars >= end) - return FOUND_NONE; // only one letter; must be at least two - firstLetter -= 'a'; - if (firstLetter > 'z' - 'a') - continue; // non-letter followed '.' - int secondLetterMask = domainTwoLetter[firstLetter]; - ch = *chars | 0x20; // second letter of the domain - ch -= 'a'; - if (ch >= 'z' - 'a') - continue; - bool secondMatch = (secondLetterMask & 1 << ch) != 0; - const char* wordMatch = longDomainNames[firstLetter]; - int wordIndex = 0; - while (wordMatch != NULL) { - int len = *wordMatch++; - char match; - do { - match = wordMatch[wordIndex]; - if (match < 0x20) - goto foundDomainStart; - if (chars[wordIndex] != match) - break; - wordIndex++; - } while (true); - wordMatch += len; - if (*wordMatch == '\0') - break; - wordIndex = 0; - } - if (secondMatch) { - wordIndex = 1; - foundDomainStart: - chars += wordIndex; - if (chars < end) { - ch = *chars; - if (ch != '.') { - if (IsDomainChar(ch)) - goto nextDot; - } else if (chars + 1 < end && IsDomainChar(chars[1])) - goto nextDot; - } - // found domain. Search backwards from '@' for beginning of email address - s->mEndResult = chars - start; - chars = atLocation; - if (chars <= start) - goto nextAt; - ch = *--chars; - if (ch == '.') - goto nextAt; // mailbox can't end in period - do { - if (IsMailboxChar(ch) == false) { - chars++; - break; - } - if (chars == start) - break; - ch = *--chars; - } while (true); - UChar firstChar = *chars; - if (firstChar == '.' || firstChar == '@') // mailbox can't start with period or be empty - goto nextAt; - s->mStartResult = chars - start; - return FOUND_COMPLETE; - } - nextDot: - ; - } -nextAt: - chars = atLocation + 1; - } - return FOUND_NONE; -} - -#define PHONE_PATTERN "(200) /-.\\ 100 -. 0000" // poor man's regex: parens optional, any one of punct, digit smallest allowed - -CacheBuilder::FoundState CacheBuilder::FindPartialNumber(const UChar* chars, unsigned length, - FindState* s) -{ - char* pattern = s->mPattern; - UChar* store = s->mStorePtr; - const UChar* start = chars; - const UChar* end = chars + length; - const UChar* lastDigit = NULL; - do { - bool initialized = s->mInitialized; - while (chars < end) { - if (initialized == false) { - s->mBackTwo = s->mBackOne; - s->mBackOne = s->mCurrent; - } - UChar ch = s->mCurrent = *chars; - do { - char patternChar = *pattern; - switch (patternChar) { - case '2': - if (initialized == false) { - s->mStartResult = chars - start; - initialized = true; - } - case '0': - case '1': - if (ch < patternChar || ch > '9') - goto resetPattern; - *store++ = ch; - pattern++; - lastDigit = chars; - goto nextChar; - case '\0': - if (WTF::isASCIIDigit(ch) == false) { - *store = '\0'; - goto checkMatch; - } - goto resetPattern; - case ' ': - if (ch == patternChar) - goto nextChar; - break; - case '(': - if (ch == patternChar) { - s->mStartResult = chars - start; - initialized = true; - s->mOpenParen = true; - } - goto commonPunctuation; - case ')': - if ((ch == patternChar) ^ s->mOpenParen) - goto resetPattern; - default: - commonPunctuation: - if (ch == patternChar) { - pattern++; - goto nextChar; - } - } - } while (++pattern); // never false - nextChar: - chars++; - } - break; -resetPattern: - if (s->mContinuationNode) - return FOUND_NONE; - FindResetNumber(s); - pattern = s->mPattern; - store = s->mStorePtr; - } while (++chars < end); -checkMatch: - if (WTF::isASCIIDigit(s->mBackOne != '1' ? s->mBackOne : s->mBackTwo)) - return FOUND_NONE; - *store = '\0'; - s->mStorePtr = store; - s->mPattern = pattern; - s->mEndResult = lastDigit - start + 1; - char pState = pattern[0]; - return pState == '\0' ? FOUND_COMPLETE : pState == '(' || (WTF::isASCIIDigit(pState) && WTF::isASCIIDigit(pattern[-1])) ? - FOUND_NONE : FOUND_PARTIAL; -} - -CacheBuilder::FoundState CacheBuilder::FindPhoneNumber(const UChar* chars, unsigned length, - int* start, int* end) -{ - FindState state; - FindReset(&state); - FoundState result = FindPartialNumber(chars, length, &state); - *start = state.mStartResult; - *end = state.mEndResult; - return result; -} - -void CacheBuilder::FindReset(FindState* state) -{ - memset(state, 0, sizeof(FindState)); - state->mCurrent = ' '; - FindResetNumber(state); -} - -void CacheBuilder::FindResetNumber(FindState* state) -{ - state->mOpenParen = false; - state->mPattern = (char*) PHONE_PATTERN; - state->mStorePtr = state->mStore; -} - -IntRect CacheBuilder::getAreaRect(const HTMLAreaElement* area) -{ - Node* node = area->document(); - while ((node = node->traverseNextNode()) != NULL) { - RenderObject* renderer = node->renderer(); - if (renderer && renderer->isRenderImage()) { - RenderImage* image = static_cast<RenderImage*>(renderer); - HTMLMapElement* map = image->imageMap(); - if (map) { - Node* n; - for (n = map->firstChild(); n; - n = n->traverseNextNode(map)) { - if (n == area) { - if (area->isDefault()) - return image->absoluteBoundingBoxRect(); - return area->computeRect(image); - } - } - } - } - } - return IntRect(); -} - -void CacheBuilder::GetGlobalOffset(Node* node, int* x, int * y) -{ - GetGlobalOffset(node->document()->frame(), x, y); -} - -void CacheBuilder::GetGlobalOffset(Frame* frame, int* x, int* y) -{ -// TIMER_PROBE(__FUNCTION__); - ASSERT(x); - ASSERT(y); - *x = 0; - *y = 0; - if (!frame->view()) - return; - Frame* parent; - while ((parent = frame->tree()->parent()) != NULL) { - const WebCore::IntRect& rect = frame->view()->platformWidget()->getBounds(); - *x += rect.x(); - *y += rect.y(); - frame = parent; - } - // TIMER_PROBE_END(); -} - -Frame* CacheBuilder::HasFrame(Node* node) -{ - RenderObject* renderer = node->renderer(); - if (renderer == NULL) - return NULL; - if (renderer->isWidget() == false) - return NULL; - Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); - if (widget == NULL) - return NULL; - if (widget->isFrameView() == false) - return NULL; - return static_cast<FrameView*>(widget)->frame(); -} - -bool CacheBuilder::HasOverOrOut(Node* node) -{ - // eventNames are thread-local data, I avoid using 'static' variable here. - AtomicString eventTypes[2] = { - eventNames().mouseoverEvent, - eventNames().mouseoutEvent - }; - - return NodeHasEventListeners(node, eventTypes, 2); -} - -bool CacheBuilder::HasTriggerEvent(Node* node) -{ - AtomicString eventTypes[5] = { - eventNames().clickEvent, - eventNames().mousedownEvent, - eventNames().mouseupEvent, - eventNames().keydownEvent, - eventNames().keyupEvent - }; - - return NodeHasEventListeners(node, eventTypes, 5); -} - -// #define EMAIL_PATTERN "x@y.d" // where 'x' is letters, numbers, and '-', '.', '_' ; 'y' is 'x' without the underscore, and 'd' is a valid domain -// - 0x2D . 0x2E 0-9 0x30-39 A-Z 0x41-5A _ 0x5F a-z 0x61-7A - -bool CacheBuilder::IsDomainChar(UChar ch) -{ - static const unsigned body[] = {0x03ff6000, 0x07fffffe, 0x07fffffe}; // 0-9 . - A-Z a-z - ch -= 0x20; - if (ch > 'z' - 0x20) - return false; - return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; -} - -bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node, - CachedNodeType* type, String* exported) const -{ - Text* textNode = static_cast<Text*>(node); - StringImpl* string = textNode->dataImpl(); - const UChar* baseChars = string->characters(); -// const UChar* originalBase = baseChars; - int length = string->length(); - int index = 0; - while (index < length && isUnicodeSpace(baseChars[index])) - index++; - if (index >= length) - return false; - if (more == false) { - walk->mStart = 0; - walk->mEnd = 0; - walk->mFinalNode = node; - walk->mLastInline = NULL; - } - // starting with this node, search forward for email, phone number, and address - // if any of the three is found, track it so that the remaining can be looked for later - FoundState state = FOUND_NONE; - RenderText* renderer = (RenderText*) node->renderer(); - bool foundBetter = false; - InlineTextBox* baseInline = walk->mLastInline != NULL ? walk->mLastInline : - renderer->firstTextBox(); - if (baseInline == NULL) - return false; - int start = walk->mEnd; - InlineTextBox* saveInline; - int baseStart, firstStart = start; - saveInline = baseInline; - baseStart = start; - for (CachedNodeType checkType = ADDRESS_CACHEDNODETYPE; - checkType <= PHONE_CACHEDNODETYPE; - checkType = static_cast<CachedNodeType>(checkType + 1)) - { - if ((1 << (checkType - 1) & mAllowableTypes) == 0) - continue; - InlineTextBox* inlineTextBox = baseInline; - FindState findState; - FindReset(&findState); - start = baseStart; - if (checkType == ADDRESS_CACHEDNODETYPE) { - findState.mBases[0] = baseChars; - findState.mWords[0] = baseChars + start; - findState.mStarts[0] = baseChars + start; - } - Node* lastPartialNode = NULL; - int lastPartialEnd = -1; - bool lastPartialMore = false; - bool firstPartial = true; - InlineTextBox* lastPartialInline = NULL; - do { - do { - const UChar* chars = baseChars + start; - length = inlineTextBox == NULL ? 0 : - inlineTextBox->end() - start + 1; - bool wasInitialized = findState.mInitialized; - switch (checkType) { - case ADDRESS_CACHEDNODETYPE: - state = FindPartialAddress(baseChars, chars, length, &findState); - break; - case EMAIL_CACHEDNODETYPE: - state = FindPartialEMail(chars, length, &findState); - break; - case PHONE_CACHEDNODETYPE: - state = FindPartialNumber(chars, length, &findState); - break; - default: - ASSERT(0); - } - findState.mInitialized = state != FOUND_NONE; - if (wasInitialized != findState.mInitialized) - firstStart = start; - if (state == FOUND_PARTIAL) { - lastPartialNode = node; - lastPartialEnd = findState.mEndResult + start; - lastPartialMore = firstPartial && - lastPartialEnd < (int) string->length(); - firstPartial = false; - lastPartialInline = inlineTextBox; - findState.mContinuationNode = true; - } else if (state == FOUND_COMPLETE) { - if (foundBetter == false || walk->mStart > findState.mStartResult) { - walk->mStart = findState.mStartResult + firstStart; - if (findState.mEndResult > 0) { - walk->mFinalNode = node; - walk->mEnd = findState.mEndResult + start; - walk->mMore = node == textNode && - walk->mEnd < (int) string->length(); - walk->mLastInline = inlineTextBox; - } else { - walk->mFinalNode = lastPartialNode; - walk->mEnd = lastPartialEnd; - walk->mMore = lastPartialMore; - walk->mLastInline = lastPartialInline; - } - *type = checkType; - if (checkType == PHONE_CACHEDNODETYPE) { - const UChar* store = findState.mStore; - *exported = String(store); - } else { - Node* temp = textNode; - length = 1; - start = walk->mStart; - exported->truncate(0); - do { - Text* tempText = static_cast<Text*>(temp); - StringImpl* string = tempText->dataImpl(); - int end = tempText == walk->mFinalNode ? - walk->mEnd : string->length(); - exported->append(String(string->substring( - start, end - start))); - ASSERT(end > start); - length += end - start + 1; - if (temp == walk->mFinalNode) - break; - start = 0; - do { - temp = temp->traverseNextNode(); - ASSERT(temp); - } while (temp->isTextNode() == false); - // add a space in between text nodes to avoid - // words collapsing together - exported->append(" "); - } while (true); - } - foundBetter = true; - } - goto tryNextCheckType; - } else if (findState.mContinuationNode) - break; - if (inlineTextBox == NULL) - break; - inlineTextBox = inlineTextBox->nextTextBox(); - if (inlineTextBox == NULL) - break; - start = inlineTextBox->start(); - if (state == FOUND_PARTIAL && node == textNode) - findState.mContinuationNode = false; - } while (true); - if (state == FOUND_NONE) - break; - // search for next text node, if any - Text* nextNode; - do { - do { - do { - if (node) - node = node->traverseNextNode(); - if (node == NULL || node->hasTagName(HTMLNames::aTag) - || node->hasTagName(HTMLNames::inputTag) - || node->hasTagName(HTMLNames::textareaTag)) { - if (state == FOUND_PARTIAL && - checkType == ADDRESS_CACHEDNODETYPE && - findState.mProgress == ZIP_CODE && - findState.mNumberCount == 0) { - baseChars = NULL; - inlineTextBox = NULL; - start = 0; - findState.mProgress = FIND_STREET; - goto finalNode; - } - goto tryNextCheckType; - } - } while (node->isTextNode() == false); - nextNode = static_cast<Text*>(node); - renderer = (RenderText*) nextNode->renderer(); - } while (renderer == NULL); - baseInline = renderer->firstTextBox(); - } while (baseInline == NULL); - string = nextNode->dataImpl(); - baseChars = string->characters(); - inlineTextBox = baseInline; - start = inlineTextBox->start(); - finalNode: - findState.mEndResult = 0; - } while (true); -tryNextCheckType: - node = textNode; - baseInline = saveInline; - string = textNode->dataImpl(); - baseChars = string->characters(); - } - if (foundBetter) { - CachedNodeType temp = *type; - switch (temp) { - case ADDRESS_CACHEDNODETYPE: { - static const char geoString[] = "geo:0,0?q="; - exported->insert(String(geoString), 0); - int index = sizeof(geoString) - 1; - String escapedComma("%2C"); - while ((index = exported->find(',', index)) >= 0) - exported->replace(index, 1, escapedComma); - } break; - case EMAIL_CACHEDNODETYPE: { - String encoded = WebCore::encodeWithURLEscapeSequences(*exported); - exported->swap(encoded); - exported->insert(WTF::String("mailto:"), 0); - } break; - case PHONE_CACHEDNODETYPE: - exported->insert(WTF::String("tel:"), 0); - break; - default: - break; - } - return true; - } -noTextMatch: - walk->reset(); - return false; -} - -bool CacheBuilder::IsMailboxChar(UChar ch) -{ - // According to http://en.wikipedia.org/wiki/Email_address - // ! # $ % & ' * + - . / 0-9 = ? - // A-Z ^ _ - // ` a-z { | } ~ - static const unsigned body[] = {0xa3ffecfa, 0xc7fffffe, 0x7fffffff}; - ch -= 0x20; - if (ch > '~' - 0x20) - return false; - return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; -} - -bool CacheBuilder::setData(CachedFrame* cachedFrame) -{ - Frame* frame = FrameAnd(this); - Document* doc = frame->document(); - if (doc == NULL) - return false; - RenderObject* renderer = doc->renderer(); - if (renderer == NULL) - return false; - RenderLayer* layer = renderer->enclosingLayer(); - if (layer == NULL) - return false; - if (!frame->view()) - return false; - int x, y; - GetGlobalOffset(frame, &x, &y); - WebCore::IntRect viewBounds = frame->view()->platformWidget()->getBounds(); - if ((x | y) != 0) - viewBounds.setLocation(WebCore::IntPoint(x, y)); - cachedFrame->setLocalViewBounds(viewBounds); - cachedFrame->setContentsSize(layer->scrollWidth(), layer->scrollHeight()); - if (cachedFrame->childCount() == 0) - return true; - CachedFrame* lastCachedFrame = cachedFrame->lastChild(); - cachedFrame = cachedFrame->firstChild(); - do { - CacheBuilder* cacheBuilder = Builder((Frame* )cachedFrame->framePointer()); - cacheBuilder->setData(cachedFrame); - } while (cachedFrame++ != lastCachedFrame); - return true; -} - -#if USE(ACCELERATED_COMPOSITING) -void CacheBuilder::TrackLayer(WTF::Vector<LayerTracker>& layerTracker, - RenderObject* nodeRenderer, Node* lastChild, int offsetX, int offsetY) -{ - RenderLayer* layer = nodeRenderer->enclosingLayer(); - RenderLayerBacking* back = layer->backing(); - if (!back) - return; - GraphicsLayer* grLayer = back->graphicsLayer(); - if (back->hasContentsLayer()) - grLayer = back->foregroundLayer(); - if (!grLayer) - return; - LayerAndroid* aLayer = grLayer->platformLayer(); - if (!aLayer) - return; - IntPoint scroll(layer->scrollXOffset(), layer->scrollYOffset()); -#if ENABLE(ANDROID_OVERFLOW_SCROLL) - // If this is an overflow element, track the content layer. - if (layer->hasOverflowScroll() && aLayer->getChild(0)) - aLayer = aLayer->getChild(0)->getChild(0); - if (!aLayer) - return; - // Prevent a crash when scrolling a layer that does not have a parent. - if (layer->stackingContext()) - layer->scrollToOffset(0, 0); -#endif - layerTracker.grow(layerTracker.size() + 1); - LayerTracker& indexTracker = layerTracker.last(); - indexTracker.mLayer = aLayer; - indexTracker.mRenderLayer = layer; - indexTracker.mBounds = enclosingIntRect(aLayer->bounds()); - // Use the absolute location of the layer as the bounds location. This - // provides the original offset of nodes in the layer so that we can - // translate nodes between their original location and the layer's new - // location. - indexTracker.mBounds.setLocation(layer->absoluteBoundingBox().location()); - indexTracker.mBounds.move(offsetX, offsetY); - indexTracker.mScroll = scroll; - indexTracker.mLastChild = OneAfter(lastChild); - DBG_NAV_LOGD("layer=%p [%d] bounds=(%d,%d,w=%d,h=%d)", aLayer, - aLayer->uniqueId(), indexTracker.mBounds.x(), indexTracker.mBounds.y(), - indexTracker.mBounds.width(), indexTracker.mBounds.height()); -} -#endif - -bool CacheBuilder::validNode(Frame* startFrame, void* matchFrame, - void* matchNode) -{ - if (matchFrame == startFrame) { - if (matchNode == NULL) - return true; - Node* node = startFrame->document(); - while (node != NULL) { - if (node == matchNode) { - const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ? - getAreaRect(static_cast<HTMLAreaElement*>(node)) : node->getRect(); - // Consider nodes with empty rects that are not at the origin - // to be valid, since news.google.com has valid nodes like this - if (rect.x() == 0 && rect.y() == 0 && rect.isEmpty()) - return false; - return true; - } - node = node->traverseNextNode(); - } - DBG_NAV_LOGD("frame=%p valid node=%p invalid\n", matchFrame, matchNode); - return false; - } - Frame* child = startFrame->tree()->firstChild(); - while (child) { - bool result = validNode(child, matchFrame, matchNode); - if (result) - return result; - child = child->tree()->nextSibling(); - } -#if DEBUG_NAV_UI - if (startFrame->tree()->parent() == NULL) - DBG_NAV_LOGD("frame=%p node=%p false\n", matchFrame, matchNode); -#endif - return false; -} - -static int Area(const IntRect& rect) -{ - return rect.width() * rect.height(); -} - -bool CacheBuilder::AddPartRect(IntRect& bounds, int x, int y, - WTF::Vector<IntRect>* result, IntRect* focusBounds) -{ - if (bounds.isEmpty()) - return true; - bounds.move(x, y); - if (bounds.maxX() <= 0 || bounds.maxY() <= 0) - return true; - IntRect* work = result->begin() - 1; - IntRect* end = result->end(); - while (++work < end) { - if (work->contains(bounds)) - return true; - if (bounds.contains(*work)) { - *work = bounds; - focusBounds->unite(bounds); - return true; - } - if ((bounds.x() != work->x() || bounds.width() != work->width()) && - (bounds.y() != work->y() || bounds.height() != work->height())) - continue; - IntRect test = *work; - test.unite(bounds); - if (Area(test) > Area(*work) + Area(bounds)) - continue; - *work = test; - focusBounds->unite(bounds); - return true; - } - if (result->size() >= MAXIMUM_FOCUS_RING_COUNT) - return false; - result->append(bounds); - if (focusBounds->isEmpty()) - *focusBounds = bounds; - else - focusBounds->unite(bounds); - return true; -} - -static inline bool isNotSpace(UChar c) -{ - return c <= 0xA0 ? isUnicodeSpace(c) == false : - WTF::Unicode::direction(c) != WTF::Unicode::WhiteSpaceNeutral; -} - -bool CacheBuilder::ConstructTextRect(Text* textNode, - InlineTextBox* textBox, int start, int relEnd, int x, int y, - IntRect* focusBounds, const IntRect& clipBounds, WTF::Vector<IntRect>* result) -{ - RenderText* renderText = (RenderText*) textNode->renderer(); - EVisibility vis = renderText->style()->visibility(); - StringImpl* string = textNode->dataImpl(); - const UChar* chars = string->characters(); - FloatPoint pt = renderText->localToAbsolute(); - do { - int textBoxStart = textBox->start(); - int textBoxEnd = textBoxStart + textBox->len(); - if (textBoxEnd <= start) - continue; - if (textBoxEnd > relEnd) - textBoxEnd = relEnd; - IntRect bounds = textBox->selectionRect((int) pt.x(), (int) pt.y(), - start, textBoxEnd); - bounds.intersect(clipBounds); - if (bounds.isEmpty()) - continue; - bool drawable = false; - for (int index = start; index < textBoxEnd; index++) - if ((drawable |= isNotSpace(chars[index])) != false) - break; - if (drawable && vis != HIDDEN) { - if (AddPartRect(bounds, x, y, result, focusBounds) == false) - return false; - } - if (textBoxEnd == relEnd) - break; - } while ((textBox = textBox->nextTextBox()) != NULL); - return true; -} - -bool CacheBuilder::ConstructTextRects(Text* node, int start, - Text* last, int end, int x, int y, IntRect* focusBounds, - const IntRect& clipBounds, WTF::Vector<IntRect>* result) -{ - result->clear(); - *focusBounds = IntRect(0, 0, 0, 0); - do { - RenderText* renderText = (RenderText*) node->renderer(); - int relEnd = node == last ? end : renderText->textLength(); - InlineTextBox *textBox = renderText->firstTextBox(); - if (textBox != NULL) { - do { - if ((int) textBox->end() >= start) - break; - } while ((textBox = textBox->nextTextBox()) != NULL); - if (textBox && ConstructTextRect(node, textBox, start, relEnd, - x, y, focusBounds, clipBounds, result) == false) - return false; - } - start = 0; - do { - if (node == last) - return true; - node = (Text*) node->traverseNextNode(); - ASSERT(node != NULL); - } while (node->isTextNode() == false || node->renderer() == NULL); - } while (true); -} - -} diff --git a/Source/WebKit/android/nav/CacheBuilder.h b/Source/WebKit/android/nav/CacheBuilder.h deleted file mode 100644 index 8f1cc72..0000000 --- a/Source/WebKit/android/nav/CacheBuilder.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CacheBuilder_h -#define CacheBuilder_h - -#include "CachedDebug.h" -#include "CachedNodeType.h" -#include "IntRect.h" -#include "PlatformString.h" -#include "TextDirection.h" -#include <wtf/Forward.h> -#include <wtf/Vector.h> - -#define NAVIGATION_MAX_PHONE_LENGTH 14 - -using namespace WebCore; - -namespace WebCore { - -class ColumnInfo; -class Document; -class Frame; -class HTMLAreaElement; -class InlineTextBox; -class LayerAndroid; -class Node; -class PlatformGraphicsContext; -class RenderBlock; -class RenderFlow; -class RenderLayer; -class RenderObject; -class Text; - -} - -namespace android { - -class CachedFrame; -class CachedNode; -class CachedRoot; - -class CacheBuilder { -public: - enum Direction { - UNINITIALIZED = -1, - LEFT, - RIGHT, - UP, - DOWN, - DIRECTION_COUNT, - UP_DOWN = UP & DOWN, // mask and result - RIGHT_DOWN = RIGHT & DOWN, // mask and result - }; - enum FoundState { - FOUND_NONE, - FOUND_PARTIAL, - FOUND_COMPLETE - }; - CacheBuilder(); - void allowAllTextDetection() { mAllowableTypes = ALL_CACHEDNODE_BITS; } - void buildCache(CachedRoot* root); - static bool ConstructPartRects(Node* node, const IntRect& bounds, - IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result, - int* imageCountPtr); - Node* currentFocus() const; - void disallowAddressDetection() { mAllowableTypes = (CachedNodeBits) ( - mAllowableTypes & ~ADDRESS_CACHEDNODE_BIT); } - void disallowEmailDetection() { mAllowableTypes = (CachedNodeBits) ( - mAllowableTypes & ~EMAIL_CACHEDNODE_BIT); } - void disallowPhoneDetection() { mAllowableTypes = (CachedNodeBits) ( - mAllowableTypes & ~PHONE_CACHEDNODE_BIT); } - static FoundState FindAddress(const UChar* , unsigned length, int* start, - int* end, bool caseInsensitive); - static IntRect getAreaRect(const HTMLAreaElement* area); - static void GetGlobalOffset(Frame* , int* x, int * y); - static void GetGlobalOffset(Node* , int* x, int * y); - bool pictureSetDisabled() { return mPictureSetDisabled; } - static bool validNode(Frame* startFrame, void* framePtr, void* nodePtr); -private: - enum AddressProgress { - NO_ADDRESS, - SKIP_TO_SPACE, - HOUSE_NUMBER, - NUMBER_TRAILING_SPACE, - ADDRESS_LINE, - STATE_NAME, - SECOND_HALF, - ZIP_CODE, - PLUS_4, - FIND_STREET - }; - struct NodeWalk { - NodeWalk() { reset(); } - int mStart; - int mEnd; - Node* mFinalNode; - InlineTextBox* mLastInline; - bool mMore; - void reset() { mMore = false; } - }; - struct BoundsPart { - IntRect mRect; - int mStart; - int mEnd; - }; - struct Bounds { - typedef bool (*FindText)(BoundsPart* result, InlineTextBox* , const String& match); - IntRect mNodeBounds; - BoundsPart mPart; - WTF::Vector<BoundsPart> mParts; - char mStore[NAVIGATION_MAX_PHONE_LENGTH + 1]; - int mPartIndex; - Node* mNode; - Node* mFinalNode; - void reset() { mNode = NULL; } - }; - struct FindState { - int mStartResult; - int mEndResult; - const UChar* mCurrentStart; - const UChar* mEnd; - AddressProgress mProgress; - int mNumberCount; - int mLetterCount; - unsigned mWordCount; - int mLineCount; - const UChar* mFirstLower; - const UChar* mZipStart; - const UChar* mBases[16]; // FIXME: random guess, maybe too small, maybe too big - const UChar* mWords[16]; - const UChar* mEnds[16]; - const UChar* mStarts[16]; // text is not necessarily contiguous - const char* mStates; - int mEndWord; - int mStateWord; - int mZipHint; - int mSectionLength; - unsigned mNumberWords; // must contain as many bits as mWords contains elements - char* mPattern; - UChar mStore[NAVIGATION_MAX_PHONE_LENGTH + 1]; - UChar* mStorePtr; - UChar mBackOne; - UChar mBackTwo; - UChar mCurrent; - bool mUnparsed; - bool mZipDelimiter; - bool mOpenParen; - bool mInitialized; - bool mContinuationNode; - bool mCaseInsensitive; - void shiftWords(int shift) { - memmove(mBases, &mBases[shift], (sizeof(mBases) / - sizeof(mBases[0]) - shift) * sizeof(mBases[0])); - memmove(mWords, &mWords[shift], (sizeof(mWords) / - sizeof(mWords[0]) - shift) * sizeof(mWords[0])); - memmove(mEnds, &mEnds[shift], (sizeof(mEnds) / - sizeof(mEnds[0]) - shift) * sizeof(mEnds[0])); - memmove(mStarts, &mStarts[shift], (sizeof(mStarts) / - sizeof(mStarts[0]) - shift) * sizeof(mStarts[0])); - } - void newWord(const UChar* baseChars, const UChar* chars) { - mBases[mWordCount] = baseChars; - mWords[mWordCount] = chars; - mEnds[mWordCount] = mEnd; - mStarts[mWordCount] = mCurrentStart; - } - }; - struct Tracker { - Node* mLastChild; - }; - struct ClipColumnTracker : Tracker { - Node* mNode; - IntRect mBounds; - ColumnInfo* mColumnInfo; - int mColumnGap; - TextDirection mDirection; - bool mHasClip; - }; - struct LayerTracker : Tracker { - LayerAndroid* mLayer; - RenderLayer* mRenderLayer; - IntRect mBounds; - IntPoint mScroll; - ~LayerTracker(); - }; - struct TabIndexTracker : Tracker { - int mTabIndex; - }; - struct FocusTracker : TabIndexTracker { - int mCachedNodeIndex; - bool mSomeParentTakesFocus; - }; - void adjustForColumns(const ClipColumnTracker& track, - CachedNode* node, IntRect* bounds, RenderBlock*); - static bool AddPartRect(IntRect& bounds, int x, int y, - WTF::Vector<IntRect>* result, IntRect* focusBounds); - static bool AnyIsClick(Node* node); - static bool AnyChildIsClick(Node* node); - static bool NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length); - void BuildFrame(Frame* root, Frame* frame, - CachedRoot* cachedRoot, CachedFrame* cachedFrame); - bool CleanUpContainedNodes(CachedRoot* cachedRoot, CachedFrame* cachedFrame, - const FocusTracker* last, int lastChildIndex); - static bool ConstructTextRect(Text* textNode, - InlineTextBox* textBox, int start, int relEnd, int x, int y, - IntRect* focusBounds, const IntRect& clip, WTF::Vector<IntRect>* result); - static bool ConstructTextRects(Text* node, int start, - Text* last, int end, int x, int y, IntRect* focusBounds, - const IntRect& clip, WTF::Vector<IntRect>* result); - static FoundState FindPartialAddress(const UChar* , const UChar* , unsigned length, FindState* ); - static FoundState FindPartialEMail(const UChar* , unsigned length, FindState* ); - static FoundState FindPartialNumber(const UChar* , unsigned length, FindState* ); - static FoundState FindPhoneNumber(const UChar* chars, unsigned length, int* start, int* end); - static void FindReset(FindState* ); - static void FindResetNumber(FindState* ); - static Frame* FrameAnd(CacheBuilder* focusNav); - static Frame* FrameAnd(const CacheBuilder* focusNav); - static CacheBuilder* Builder(Frame* ); - static Frame* HasFrame(Node* ); - static bool HasOverOrOut(Node* ); - static bool HasTriggerEvent(Node* ); - static bool IsDomainChar(UChar ch); - bool isFocusableText(NodeWalk* , bool oldMore, Node* , CachedNodeType* type, - String* exported) const; //returns true if it is focusable - static bool IsMailboxChar(UChar ch); - static bool IsRealNode(Frame* , Node* ); - int overlap(int left, int right); // returns distance scale factor as 16.16 scalar - bool setData(CachedFrame* ); -#if USE(ACCELERATED_COMPOSITING) - void TrackLayer(WTF::Vector<LayerTracker>& layerTracker, - RenderObject* nodeRenderer, Node* lastChild, int offsetX, int offsetY); -#endif - Node* tryFocus(Direction direction); - Node* trySegment(Direction direction, int mainStart, int mainEnd); - CachedNodeBits mAllowableTypes; - bool mPictureSetDisabled; -#if DUMP_NAV_CACHE -public: - class Debug { -public: - void frameName(char*& namePtr, const char* max) const; - void init(char* buffer, size_t size); - static int ParentIndex(Node* node, int count, Node* parent); - void print() { frames(); } - void print(const char* name); - void wideString(const String& str); -private: - void attr(const AtomicString& name, const AtomicString& value); - void comma(const char* str); - void flush(); - Frame* frameAnd() const; - void frames(); - void groups(); - bool isFocusable(Node* node); - void localName(Node* node); - void newLine(int indent = 0); - void print(const char* name, unsigned len); - void setIndent(int ); - void uChar(const UChar* name, unsigned len, bool hex); - void validateFrame(); - void validateStringData(); - void wideString(const UChar* chars, int length, bool hex); - char* mBuffer; - size_t mBufferSize; - int mIndex; - const char* mPrefix; - int mMinPrefix; - } mDebug; -#endif -}; - -} - -#endif diff --git a/Source/WebKit/android/nav/CachedNode.h b/Source/WebKit/android/nav/CachedNode.h index 321b7fd..82d55ed 100644 --- a/Source/WebKit/android/nav/CachedNode.h +++ b/Source/WebKit/android/nav/CachedNode.h @@ -184,7 +184,6 @@ public: bool wantsKeyEvents() const { return isTextInput() || isPlugin() || isContentEditable() || isFrame(); } private: - friend class CacheBuilder; WTF::String mExport; WebCore::IntRect mBounds; WebCore::IntRect mHitBounds; diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp index 2bd76f5..162d6bd 100644 --- a/Source/WebKit/android/nav/WebView.cpp +++ b/Source/WebKit/android/nav/WebView.cpp @@ -144,9 +144,8 @@ struct JavaGlue { } m_javaGlue; WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, - bool isHighEndGfx) : - m_ring((WebViewCore*) viewImpl) - , m_isHighEndGfx(isHighEndGfx) + bool isHighEndGfx) + : m_isHighEndGfx(isHighEndGfx) { memset(m_extras, 0, DRAW_EXTRAS_SIZE * sizeof(DrawExtra*)); jclass clazz = env->FindClass("android/webkit/WebView"); @@ -193,7 +192,6 @@ WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, m_heightCanMeasure = false; m_lastDx = 0; m_lastDxTime = 0; - m_ringAnimationEnd = 0; m_baseLayer = 0; m_glDrawFunctor = 0; m_isDrawingPaused = false; @@ -230,33 +228,6 @@ DrawExtra* getDrawExtra(DrawExtras extras) return m_extras[extras - 1]; } -DrawExtra* getDrawExtraLegacy(DrawExtras extras) -{ - CachedRoot* root = getFrameCache(AllowNewer); - if (!root) { - DBG_NAV_LOG("!root"); - if (extras == DrawExtrasCursorRing) - resetCursorRing(); - } - DrawExtra* extra = getDrawExtra(extras); - if (!extra) { - switch (extras) { - case DrawExtrasCursorRing: - if (drawCursorPreamble(root) && m_ring.setup()) { - if (m_ring.m_isPressed || m_ringAnimationEnd == UINT_MAX) - extra = &m_ring; - drawCursorPostamble(); - } - break; - // Just to prevent compiler warnings - case DrawExtrasSelection: - case DrawExtrasNone: - break; - } - } - return extra; -} - void stopGL() { #if USE(ACCELERATED_COMPOSITING) @@ -335,60 +306,6 @@ void scrollRectOnScreen(const IntRect& rect) viewInvalidate(); } -void resetCursorRing() -{ - m_ringAnimationEnd = 0; - m_viewImpl->m_hasCursorBounds = false; -} - -bool drawCursorPreamble(CachedRoot* root) -{ - if (!root) return false; - const CachedFrame* frame; - const CachedNode* node = root->currentCursor(&frame); - if (!node) { - DBG_NAV_LOGV("%s", "!node"); - resetCursorRing(); - return false; - } - m_ring.setIsButton(node); - if (node->isHidden()) { - DBG_NAV_LOG("node->isHidden()"); - m_viewImpl->m_hasCursorBounds = false; - return false; - } -#if USE(ACCELERATED_COMPOSITING) - if (node->isInLayer() && root->rootLayer()) { - LayerAndroid* layer = root->rootLayer(); - layer->updateFixedLayersPositions(m_visibleRect); - layer->updatePositions(); - } -#endif - setVisibleRect(root); - m_ring.m_root = root; - m_ring.m_frame = frame; - m_ring.m_node = node; - SkMSec time = SkTime::GetMSecs(); - m_ring.m_isPressed = time < m_ringAnimationEnd - && m_ringAnimationEnd != UINT_MAX; - return true; -} - -void drawCursorPostamble() -{ - if (m_ringAnimationEnd == UINT_MAX) - return; - SkMSec time = SkTime::GetMSecs(); - if (time < m_ringAnimationEnd) { - // views assume that inval bounds coordinates are non-negative - WebCore::IntRect invalBounds(0, 0, INT_MAX, INT_MAX); - invalBounds.intersect(m_ring.m_absBounds); - postInvalidateDelayed(m_ringAnimationEnd - time, invalBounds); - } else { - hideCursor(const_cast<CachedRoot*>(m_ring.m_root)); - } -} - bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, WebCore::IntRect& webViewRect, int titleBarHeight, WebCore::IntRect& clip, float scale, int extras) @@ -400,7 +317,6 @@ bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, if (!m_glWebViewState) { TilesManager::instance()->setHighEndGfx(m_isHighEndGfx); m_glWebViewState = new GLWebViewState(); - m_glWebViewState->glExtras()->setCursorRingExtra(&m_ring); if (m_baseLayer->content()) { SkRegion region; SkIRect rect; @@ -411,7 +327,7 @@ bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, } } - DrawExtra* extra = getDrawExtraLegacy((DrawExtras) extras); + DrawExtra* extra = getDrawExtra((DrawExtras) extras); unsigned int pic = m_glWebViewState->currentPictureCounter(); m_glWebViewState->glExtras()->setDrawExtra(extra); @@ -459,7 +375,7 @@ PictureSet* draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras, bool spli if (content->draw(canvas)) ret = split ? new PictureSet(*content) : 0; - DrawExtra* extra = getDrawExtraLegacy(extras); + DrawExtra* extra = getDrawExtra(extras); if (extra) extra->draw(canvas, 0); @@ -778,7 +694,6 @@ bool moveCursor(int keyCode, int count, bool ignoreScroll) } bool result = false; if (cachedNode) { - showCursorUntimed(); m_viewImpl->updateCursorBounds(root, cachedFrame, cachedNode); root->setCursor(const_cast<CachedFrame*>(cachedFrame), const_cast<CachedNode*>(cachedNode)); @@ -860,7 +775,6 @@ void selectBestAt(const WebCore::IntRect& rect) WebCore::IntRect bounds = node->bounds(frame); root->rootHistory()->setMouseBounds(bounds); m_viewImpl->updateCursorBounds(root, frame, node); - showCursorTimed(); root->setCursor(const_cast<CachedFrame*>(frame), const_cast<CachedNode*>(node)); } @@ -921,11 +835,6 @@ bool motionUp(int x, int y, int slop) (WebCore::Frame*) frame->framePointer(), (WebCore::Node*) result->nodePointer(), rx, ry); } - if (result->isTextInput() || result->isSelect() - || result->isContentEditable()) { - showCursorUntimed(); - } else - showCursorTimed(); return pageScrolled; } @@ -1012,21 +921,6 @@ void setFindIsUp(bool up) m_viewImpl->m_findIsUp = up; } -void showCursorTimed() -{ - DBG_NAV_LOG(""); - m_ringAnimationEnd = SkTime::GetMSecs() + PRESSED_STATE_DURATION; - viewInvalidate(); -} - -void showCursorUntimed() -{ - DBG_NAV_LOG(""); - m_ring.m_isPressed = false; - m_ringAnimationEnd = UINT_MAX; - viewInvalidate(); -} - void setHeightCanMeasure(bool measure) { m_heightCanMeasure = measure; @@ -1311,7 +1205,8 @@ int getHandleLayerId(SelectText::HandleId handleId, SkIRect& cursorRect) { m_baseLayer->updateLayerPositions(m_visibleRect); LayerAndroid* root = compositeRoot(); LayerAndroid* layer = root ? root->findById(layerId) : 0; - rect = layer->drawTransform()->mapRect(rect); + if (layer && layer->drawTransform()) + rect = layer->drawTransform()->mapRect(rect); } cursorRect.set(rect.x(), rect.y(), rect.maxX(), rect.maxY()); return layerId; @@ -1323,12 +1218,10 @@ private: // local state for WebView CachedRoot* m_frameCacheUI; // navigation data ready for use WebViewCore* m_viewImpl; int m_generation; // associate unique ID with sent kit focus to match with ui - SkMSec m_ringAnimationEnd; // Corresponds to the same-named boolean on the java side. bool m_heightCanMeasure; int m_lastDx; SkMSec m_lastDxTime; - CursorRing m_ring; DrawExtra* m_extras[DRAW_EXTRAS_SIZE]; BaseLayerAndroid* m_baseLayer; Functor* m_glDrawFunctor; @@ -1551,19 +1444,7 @@ static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj) static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj) { -#if ENABLE(ANDROID_NAVCACHE) - const CachedNode* focus = getFocusNode(env, obj); - if (!focus) return false; - // Plugins handle shift and arrows whether or not they have focus. - if (focus->isPlugin()) return true; - const CachedNode* cursor = getCursorNode(env, obj); - // ContentEditable nodes should only receive shift and arrows if they have - // both the cursor and the focus. - return cursor && cursor->nodePointer() == focus->nodePointer() - && cursor->isContentEditable(); -#else return true; -#endif } static jobject nativeCursorNodeBounds(JNIEnv *env, jobject obj) @@ -1954,12 +1835,6 @@ static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect) static void nativeSelectAt(JNIEnv *env, jobject obj, jint x, jint y) { - WebView* view = GET_NATIVE_VIEW(env, obj); - ALOG_ASSERT(view, "view not set in %s", __FUNCTION__); - WebCore::IntRect rect = IntRect(x, y , 1, 1); - view->selectBestAt(rect); - if (view->hasCursorNode()) - view->showCursorUntimed(); } static jobject nativeLayerBounds(JNIEnv* env, jobject obj, jint jlayer) @@ -2050,7 +1925,6 @@ static void nativeSetFindIsUp(JNIEnv *env, jobject obj, jboolean isUp) static void nativeShowCursorTimed(JNIEnv *env, jobject obj) { - GET_NATIVE_VIEW(env, obj)->showCursorTimed(); } static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure) @@ -2133,7 +2007,6 @@ static bool nativeMoveCursorToNextTextInput(JNIEnv *env, jobject obj) const WebCore::IntRect& bounds = next->bounds(frame); root->rootHistory()->setMouseBounds(bounds); view->getWebViewCore()->updateCursorBounds(root, frame, next); - view->showCursorUntimed(); root->setCursor(const_cast<CachedFrame*>(frame), const_cast<CachedNode*>(next)); view->sendMoveFocus(static_cast<WebCore::Frame*>(frame->framePointer()), @@ -2392,11 +2265,7 @@ static void nativeSetPauseDrawing(JNIEnv *env, jobject obj, jint nativeView, static bool nativeDisableNavcache(JNIEnv *env, jobject obj) { -#if ENABLE(ANDROID_NAVCACHE) - return false; -#else return true; -#endif } static void nativeSetTextSelection(JNIEnv *env, jobject obj, jint nativeView, diff --git a/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp b/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp index 4b99b31..513d251 100644 --- a/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp +++ b/Source/WebKit/android/plugins/ANPSurfaceInterface.cpp @@ -32,7 +32,7 @@ #include "SkANP.h" #include "android_graphics.h" #include <JNIUtility.h> -#include <surfaceflinger/Surface.h> +#include <gui/Surface.h> #include <ui/Rect.h> #include <ui/Region.h> #include <utils/RefBase.h> diff --git a/Source/WebKit/chromium/public/WebRange.h b/Source/WebKit/chromium/public/WebRange.h index 89fc8f6..3da3d95 100644 --- a/Source/WebKit/chromium/public/WebRange.h +++ b/Source/WebKit/chromium/public/WebRange.h @@ -37,10 +37,10 @@ namespace WebCore { class Range; } namespace WTF { template <typename T> class PassRefPtr; } #endif +namespace WebCore { class Node; } namespace WebKit { -class WebNode; class WebRangePrivate; class WebString; @@ -64,8 +64,8 @@ public: WEBKIT_API int startOffset() const; WEBKIT_API int endOffset() const; - WEBKIT_API WebNode startContainer(int& exceptionCode) const; - WEBKIT_API WebNode endContainer(int& exceptionCode) const; + WEBKIT_API WebCore::Node* startContainer(int& exceptionCode) const; + WEBKIT_API WebCore::Node* endContainer(int& exceptionCode) const; WEBKIT_API WebString toHTMLText() const; WEBKIT_API WebString toPlainText() const; diff --git a/Source/WebKit/chromium/public/android/WebDOMTextContentWalker.h b/Source/WebKit/chromium/public/android/WebDOMTextContentWalker.h new file mode 100644 index 0000000..26ba589 --- /dev/null +++ b/Source/WebKit/chromium/public/android/WebDOMTextContentWalker.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebDOMTextContentWalker_h +#define WebDOMTextContentWalker_h + +#include "../WebPrivateOwnPtr.h" +#include "../WebRange.h" +#include "../WebString.h" + +namespace WebCore { +class DOMTextContentWalker; +class Node; +} + +namespace WebKit { + +class WebHitTestInfo; + +class WebDOMTextContentWalker { +public: + WebDOMTextContentWalker(); + ~WebDOMTextContentWalker(); + + // Creates a new text content walker centered in the position described by the hit test. + // The maximum length of the contents retrieved by the walker is defined by maxLength. + WEBKIT_API WebDOMTextContentWalker(const WebHitTestInfo&, size_t maxLength); + + // Creates a new text content walker centered in the selected offset of the given text node. + // The maximum length of the contents retrieved by the walker is defined by maxLength. + WEBKIT_API WebDOMTextContentWalker(WebCore::Node* textNode, size_t offset, size_t maxLength); + + // Text content retrieved by the walker. + WEBKIT_API WebString content() const; + + // Position of the initial text node offset in the content string. + WEBKIT_API size_t hitOffsetInContent() const; + + // Convert start/end positions in the content text string into a WebKit text range. + WEBKIT_API WebRange contentOffsetsToRange(size_t startInContent, size_t endInContent); + +protected: + WebPrivateOwnPtr<WebCore::DOMTextContentWalker> m_private; +}; + +} // namespace WebKit + +#endif diff --git a/Source/WebKit/chromium/public/android/WebHitTestInfo.h b/Source/WebKit/chromium/public/android/WebHitTestInfo.h new file mode 100644 index 0000000..79b354e --- /dev/null +++ b/Source/WebKit/chromium/public/android/WebHitTestInfo.h @@ -0,0 +1,76 @@ +/* +* Copyright (C) 2011 Google Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebHitTestInfo_h +#define WebHitTestInfo_h + +#include "../WebPoint.h" +#include "../WebPrivateOwnPtr.h" +#include "../WebURL.h" + +namespace WebCore { +class HitTestResult; +class Node; +} + +namespace WebKit { + +// Properties of a hit test result, i.e. properties of the nodes at a given point +// (the hit point) on the page. Both urls may be populated at the same time, for +// example in the instance of an <img> inside an <a>. +class WebHitTestInfo { +public: + WebHitTestInfo(); + WebHitTestInfo(const WebHitTestInfo&); + ~WebHitTestInfo(); + + // The absolute URL of the link returned by the hit test. + WEBKIT_API WebURL linkURL() const; + + // The absolute URL of the image returned by the hit test. + WEBKIT_API WebURL imageURL() const; + + // The node that got hit. + WEBKIT_API WebCore::Node* node() const; + + // Point coordinates of the hit. + WEBKIT_API WebPoint point() const; + + // True iff the hit was on an editable field or node. + WEBKIT_API bool isContentEditable() const; + +#if WEBKIT_IMPLEMENTATION + WebHitTestInfo(const WebCore::HitTestResult&); + WebHitTestInfo& operator=(const WebCore::HitTestResult&); + operator WebCore::HitTestResult() const; +#endif + +protected: + WebPrivateOwnPtr<WebCore::HitTestResult> m_private; +}; + +} // namespace WebKit + +#endif diff --git a/Source/WebKit/chromium/src/WebRange.cpp b/Source/WebKit/chromium/src/WebRange.cpp index 3dd000d..5ea3990 100644 --- a/Source/WebKit/chromium/src/WebRange.cpp +++ b/Source/WebKit/chromium/src/WebRange.cpp @@ -32,7 +32,6 @@ #include "WebRange.h" #include "Range.h" -#include "WebNode.h" #include "WebString.h" #include <wtf/PassRefPtr.h> @@ -66,14 +65,14 @@ int WebRange::endOffset() const return m_private->endOffset(); } -WebNode WebRange::startContainer(int& exceptionCode) const +Node* WebRange::startContainer(int& exceptionCode) const { - return PassRefPtr<Node>(m_private->startContainer(exceptionCode)); + return m_private->startContainer(exceptionCode); } -WebNode WebRange::endContainer(int& exceptionCode) const +Node* WebRange::endContainer(int& exceptionCode) const { - return PassRefPtr<Node>(m_private->endContainer(exceptionCode)); + return m_private->endContainer(exceptionCode); } WebString WebRange::toHTMLText() const diff --git a/Source/WebKit/chromium/src/android/WebDOMTextContentWalker.cpp b/Source/WebKit/chromium/src/android/WebDOMTextContentWalker.cpp new file mode 100644 index 0000000..80155fb --- /dev/null +++ b/Source/WebKit/chromium/src/android/WebDOMTextContentWalker.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "android/WebDOMTextContentWalker.h" + +#include "DOMTextContentWalker.h" +#include "Element.h" +#include "Node.h" +#include "Range.h" +#include "RenderObject.h" +#include "Text.h" +#include "VisiblePosition.h" +#include "android/WebHitTestInfo.h" + +using namespace WebCore; + +namespace WebKit { + +WebDOMTextContentWalker::WebDOMTextContentWalker() +{ +} + +WebDOMTextContentWalker::~WebDOMTextContentWalker() +{ + m_private.reset(0); +} + +WebDOMTextContentWalker::WebDOMTextContentWalker(const WebHitTestInfo& hitTestInfo, size_t maxLength) +{ + Node* node = hitTestInfo.node(); + if (!node) + return; + + Element* element = node->parentElement(); + if (!node->inDocument() && element && element->inDocument()) + node = element; + m_private.reset(new DOMTextContentWalker(node->renderer()->positionForPoint(hitTestInfo.point()), maxLength)); +} + +WebDOMTextContentWalker::WebDOMTextContentWalker(Node* node, size_t offset, size_t maxLength) +{ + if (!node || !node->isTextNode() || offset >= node->nodeValue().length()) + return; + + m_private.reset(new DOMTextContentWalker(VisiblePosition(Position(static_cast<Text*>(node), offset).parentAnchoredEquivalent(), DOWNSTREAM), maxLength)); +} + +WebString WebDOMTextContentWalker::content() const +{ + return m_private->content(); +} + +size_t WebDOMTextContentWalker::hitOffsetInContent() const +{ + return m_private->hitOffsetInContent(); +} + +WebRange WebDOMTextContentWalker::contentOffsetsToRange(size_t startInContent, size_t endInContent) +{ + return m_private->contentOffsetsToRange(startInContent, endInContent); +} + +} // namespace WebKit diff --git a/Source/WebKit/chromium/src/android/WebHitTestInfo.cpp b/Source/WebKit/chromium/src/android/WebHitTestInfo.cpp new file mode 100644 index 0000000..948c6fb --- /dev/null +++ b/Source/WebKit/chromium/src/android/WebHitTestInfo.cpp @@ -0,0 +1,95 @@ +/* +* Copyright (C) 2011 Google Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "android/WebHitTestInfo.h" + +#include "Element.h" +#include "HitTestResult.h" +#include "KURL.h" +#include "Node.h" +#include "RenderObject.h" +#include "VisiblePosition.h" + +using namespace WebCore; + +namespace WebKit { + +WebHitTestInfo::WebHitTestInfo() +{ +} + +WebHitTestInfo::WebHitTestInfo(const WebHitTestInfo& testInfo) + : m_private(new HitTestResult(testInfo)) +{ +} + +WebURL WebHitTestInfo::linkURL() const +{ + return m_private->absoluteLinkURL(); +} + +WebHitTestInfo::~WebHitTestInfo() +{ + m_private.reset(0); +} + +WebURL WebHitTestInfo::imageURL() const +{ + return m_private->absoluteImageURL(); +} + +Node* WebHitTestInfo::node() const +{ + return m_private->innerNode(); +} + +WebPoint WebHitTestInfo::point() const +{ + return WebPoint(m_private->localPoint()); +} + +bool WebHitTestInfo::isContentEditable() const +{ + return m_private->isContentEditable(); +} + +WebHitTestInfo::WebHitTestInfo(const HitTestResult& result) +{ + m_private.reset(new HitTestResult(result)); +} + +WebHitTestInfo& WebHitTestInfo::operator=(const HitTestResult& result) +{ + m_private.reset(new HitTestResult(result)); + return *this; +} + +WebHitTestInfo::operator HitTestResult() const +{ + return *m_private.get(); +} + +} // namespace WebKit |