diff options
Diffstat (limited to 'WebCore/platform')
353 files changed, 37602 insertions, 4981 deletions
diff --git a/WebCore/platform/ArrayImpl.cpp b/WebCore/platform/ArrayImpl.cpp new file mode 100644 index 0000000..330120f --- /dev/null +++ b/WebCore/platform/ArrayImpl.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2004 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "ArrayImpl.h" + +#include <limits> +#include <new> +#include <stddef.h> +#include <string.h> + +namespace WebCore { + +ArrayImpl::ArrayPrivate::ArrayPrivate(size_t pItemSize, size_t pNumItems) : + numItems(pNumItems), + itemSize(pItemSize), + data(pNumItems > 0 ? static_cast<char *>(fastMalloc(itemSize * numItems)) : NULL) +{ +} + +ArrayImpl::ArrayPrivate::~ArrayPrivate() +{ + fastFree(data); +} + + +ArrayImpl::ArrayImpl(size_t itemSize, size_t numItems) : + d(new ArrayPrivate(itemSize, numItems)) +{ +} + +ArrayImpl::ArrayImpl(const ArrayImpl &a) : + d(a.d) +{ +} + +ArrayImpl::~ArrayImpl() +{ +} + +ArrayImpl &ArrayImpl::operator=(const ArrayImpl &a) +{ + d = a.d; + return *this; +} + +void *ArrayImpl::data() const +{ + return d->data; +} + +bool ArrayImpl::resize(size_t newSize) +{ + if (newSize != d->numItems) { + char *newData; + + if (newSize != 0) { + size_t maxSize = std::numeric_limits<size_t>::max() / d->itemSize; + if (newSize > maxSize) + return false; + newData = static_cast<char *>(fastRealloc(d->data, newSize * d->itemSize)); + if (!newData) + return false; + } else { + newData = 0; + fastFree(d->data); + } + + d->data = newData; + d->numItems = newSize; + } + + return true; +} + +void ArrayImpl::duplicate(const void *data, size_t newSize) +{ + if (data == NULL) { + newSize = 0; + } + + if (!d->hasOneRef()) + d = new ArrayPrivate(d->itemSize, newSize); + + if (d->numItems != newSize) { + resize(newSize); + } + + memcpy(d->data, data, newSize * d->itemSize); +} + +void ArrayImpl::detach() +{ + if (!d->hasOneRef()) + duplicate(d->data, d->numItems); +} + +bool ArrayImpl::fill(const void *item, int numItems) +{ + if (numItems == -1) { + numItems = d->numItems; + } + + if ((unsigned)numItems != d->numItems) { + if (!resize(numItems)) { + return false; + } + } + + for (int i = 0; i < numItems; i++) { + memcpy(&d->data[i * d->itemSize], item, d->itemSize); + } + + return true; +} + +bool ArrayImpl::operator==(const ArrayImpl &a) const +{ + return d->numItems == a.d->numItems && d->itemSize == a.d->itemSize + && (d->data == a.d->data || memcmp(d->data, a.d->data, d->itemSize*d->numItems) == 0); +} + +} diff --git a/WebCore/platform/ArrayImpl.h b/WebCore/platform/ArrayImpl.h new file mode 100644 index 0000000..a4cfc1c --- /dev/null +++ b/WebCore/platform/ArrayImpl.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 ArrayImpl_h +#define ArrayImpl_h + +#include <wtf/RefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class ArrayImpl +{ + public: + ArrayImpl(size_t itemSize, size_t numItems = 0); + ~ArrayImpl(); + + ArrayImpl(const ArrayImpl &); + ArrayImpl &operator=(const ArrayImpl &); + + void *at(size_t pos) const { return &d->data[pos * d->itemSize]; } + + void *data() const; + unsigned size() const; + bool resize(size_t size); + void duplicate(const void *data, size_t size); + bool fill(const void *item, int size = -1); + void detach(); + + bool operator==(const ArrayImpl &) const; + + private: + class ArrayPrivate : public RefCounted<ArrayPrivate> + { + public: + ArrayPrivate(size_t pNumItems, size_t pItemSize); + ~ArrayPrivate(); + + size_t numItems; + size_t itemSize; + char *data; + }; + + RefPtr<ArrayPrivate> d; +}; + +inline unsigned ArrayImpl::size() const +{ + return d->numItems; +} + +} + +#endif diff --git a/WebCore/platform/ContextMenuItem.h b/WebCore/platform/ContextMenuItem.h index 22e042f..01a012d 100644 --- a/WebCore/platform/ContextMenuItem.h +++ b/WebCore/platform/ContextMenuItem.h @@ -167,6 +167,8 @@ namespace WebCore { bool checked; bool enabled; }; +#elif defined ANDROID + typedef void* PlatformMenuItemDescription; #elif PLATFORM(WX) typedef wxMenuItem* PlatformMenuItemDescription; #endif diff --git a/WebCore/platform/Cursor.h b/WebCore/platform/Cursor.h index c021ebc..98b0db6 100644 --- a/WebCore/platform/Cursor.h +++ b/WebCore/platform/Cursor.h @@ -59,7 +59,7 @@ namespace WebCore { #if PLATFORM(WIN) class SharedCursor : public RefCounted<SharedCursor> { public: - SharedCursor(HCURSOR nativeCursor) : RefCounted<SharedCursor>(0), m_nativeCursor(nativeCursor) {} + SharedCursor(HCURSOR nativeCursor) : m_nativeCursor(nativeCursor) {} ~SharedCursor() { DestroyIcon(m_nativeCursor); } diff --git a/WebCore/platform/DeprecatedArray.h b/WebCore/platform/DeprecatedArray.h new file mode 100644 index 0000000..6c358f3 --- /dev/null +++ b/WebCore/platform/DeprecatedArray.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2004 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 DeprecatedArray_h +#define DeprecatedArray_h + +#include "ArrayImpl.h" +#include <wtf/Platform.h> + +namespace WebCore { + +template <class T> class DeprecatedArray { +public: + DeprecatedArray() : impl(sizeof(T)) { } + DeprecatedArray(int i) : impl(sizeof(T), i) { } + + bool isEmpty() { return impl.size() == 0; } + T &at(unsigned u) { return *(T *)impl.at(u); } + const T &at(unsigned u) const { return *(T *)impl.at(u); } + T *data() { return (T *)impl.data(); } + const T *data() const { return (T *)impl.data(); } + unsigned size() const { return impl.size(); } + unsigned count() const { return impl.size(); } + bool resize(unsigned size) { return impl.resize(size); } + DeprecatedArray<T>& duplicate(const DeprecatedArray<T> &a) { impl.duplicate(a.data(), a.size()); return *this; } + DeprecatedArray<T>& duplicate(const T *data, int size) { impl.duplicate(data, size); return *this; } + void detach() { impl.detach(); } + bool fill(const T &item, int size=-1) { return impl.fill(&item, size); } + DeprecatedArray<T>& assign(const DeprecatedArray<T> &a) { return *this = a; } + + T &operator[](int i) { return *(T *)impl.at(i); } + const T &operator[](int i) const { return *(T *)impl.at(i); } +#if PLATFORM(WIN_OS) + // FIXME: Look into this strange compile error on Win32. + T &operator[](unsigned i) { return *(T *)impl.at(i); } + const T &operator[](unsigned i) const { return *(T *)impl.at(i); } +#endif + bool operator==(const DeprecatedArray<T> &a) const { return impl == a.impl; } + bool operator!=(const DeprecatedArray<T> &a) const { return !(*this == a); } + operator const T*() const { return data(); } + + private: + ArrayImpl impl; +}; + +typedef DeprecatedArray<char> DeprecatedByteArray; + +} // namespace WebCore + +#endif // DeprecatedArray_h diff --git a/WebCore/platform/DeprecatedCString.cpp b/WebCore/platform/DeprecatedCString.cpp new file mode 100644 index 0000000..2702d70 --- /dev/null +++ b/WebCore/platform/DeprecatedCString.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2003, 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "DeprecatedCString.h" + +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> + +using namespace WTF; + +namespace WebCore { + +DeprecatedCString::DeprecatedCString() +{ +} + +DeprecatedCString::DeprecatedCString(int size) : DeprecatedByteArray(size) +{ + if( size>0 && data() ) + { + data()[0] = 0; // first null + data()[size-1] = 0; // last byte + } + // else null +} + + +DeprecatedCString::DeprecatedCString(const char *str) +{ + size_t len; + if( str && (len=strlen(str)+1) && resize(len) ) // include null + strcpy( data(), str ); + // else null +} + + +DeprecatedCString::DeprecatedCString(const char *str, unsigned max) +{ + if( str && max ) + { + // perform a truncated strlen on str + const char* p = str; + unsigned len = 1; // for the null + while( *p++ && len<max ) + len ++; + + if( resize(len) ) + { + char *dest = data(); + strncpy( dest, str, len ); + dest[len-1] = 0; // re-terminate + } + } + // else null +} + +bool DeprecatedCString::isEmpty() const +{ return length()==0; } + + +unsigned DeprecatedCString::length() const +{ + const char *d = data(); + return d ? strlen(d) : 0; +} + + +bool DeprecatedCString::resize(unsigned len) +{ + bool success = DeprecatedByteArray::resize(len); + if( success && len>0 ) + data()[len-1] = 0; // always terminate last byte + + return success; +} + + +bool DeprecatedCString::truncate(unsigned pos) +{ + return resize(pos+1); +} + + +DeprecatedCString DeprecatedCString::lower() const +{ + // convert + DeprecatedCString tmp = *this; // copy + char* str = tmp.data(); + if( str ) + { + while( *str != 0 ) + { + *str = toASCIILower(*str); + str++; + } + } + + return tmp; +} + + +DeprecatedCString DeprecatedCString::upper() const +{ + DeprecatedCString tmp = *this; // copy + char* str = tmp.data(); + if( str ) + { + while( *str != 0 ) + { + *str = toASCIIUpper(*str); + str++; + } + } + + return tmp; +} + + +inline DeprecatedCString DeprecatedCString::left(unsigned len) const +{ return mid(0, len); } + + +inline DeprecatedCString DeprecatedCString::right(unsigned len) const +{ return mid(length() - len, len); } + + +DeprecatedCString DeprecatedCString::mid(unsigned index, unsigned len) const +{ + unsigned size = length(); + if( data() && index<size ) // return null if index out-of-range + { + // clip length + if( len > size - index ) + len = size - index; + + // copy and return + return DeprecatedCString( &(data()[index]), len+1); // include nul + } + + // degenerate case + return DeprecatedCString(); +} + +int DeprecatedCString::find(const char *sub, int index, bool cs) const +{ + const char* str = data(); + if( str && str[0] && sub && index>=0 ) // don't search empty strings + { + // advance until we get to index + int pos = 0; + while( pos < index ) + if( str[pos++] == 0 ) + return -1; // index is beyond end of str + + // now search from index onward + while( str[index] != 0 ) + { + char a, b; + + // compare until we reach the end or a mismatch + pos = 0; + if( cs ) + while( (a=sub[pos]) && (b=str[index]) && a==b ) + pos++, index++; + else + while( (a=sub[pos]) && (b=str[index]) && toASCIILower(a)==toASCIILower(b) ) + pos++, index++; + + // reached the end of our compare string without a mismatch? + if( sub[pos] == 0 ) + return index - pos; + + index ++; + } + } + + return -1; +} + +int DeprecatedCString::contains(char c, bool cs) const +{ + unsigned found = 0; + unsigned len = length(); + + if (len) { + const char *str = data(); + + if (cs) { + for (unsigned i = 0; i != len; ++i) { + found += str[i] == c; + } + } else { + c = toASCIILower(c); + + for (unsigned i = 0; i != len; ++i) { + char chr = str[i]; + chr = toASCIILower(chr); + found += chr == c; + } + } + } + + return found; +} + +DeprecatedCString &DeprecatedCString::operator=(const char *assignFrom) +{ + duplicate(assignFrom, (assignFrom ? strlen(assignFrom) : 0) + 1); + return *this; +} + +DeprecatedCString& DeprecatedCString::append(const char *s) +{ + if (s) { + unsigned len2 = strlen(s); + if (len2) { + detach(); + unsigned len1 = length(); + if (DeprecatedByteArray::resize(len1 + len2 + 1)) + memcpy(data() + len1, s, len2 + 1); + } + } + + return *this; +} + +DeprecatedCString &DeprecatedCString::append(char c) +{ + detach(); + unsigned len = length(); + + if (DeprecatedByteArray::resize(len + 2)) { + *(data() + len) = c; + *(data() + len + 1) = '\0'; + } + + return *this; +} + +DeprecatedCString &DeprecatedCString::replace(char c1, char c2) +{ + unsigned len = length(); + + if (len) { + // Search for the first instance of c1 before detaching, + // just in case there is nothing to replace. In that case + // we don't want to detach this from other shared instances + // since we have no need to modify it. + unsigned i; + { + const char *s = data(); + for (i = 0; i != len; ++i) { + if (s[i] == c1) { + break; + } + } + } + + if (i != len) { + detach(); + char *s = data(); + // Start at the first instance of c1; no need to rescan earlier chars. + for (; i != len; ++i) { + if (s[i] == c1) { + s[i] = c2; + } + } + } + } + + return *this; +} + +bool operator==(const DeprecatedCString &s1, const char *s2) +{ + if (s1.size() == 0 && !s2) + return true; + if (s1.size() == 0 && s2) + return false; + return strcmp(s1, s2) == 0; +} + +} diff --git a/WebCore/platform/DeprecatedCString.h b/WebCore/platform/DeprecatedCString.h new file mode 100644 index 0000000..a10eb82 --- /dev/null +++ b/WebCore/platform/DeprecatedCString.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2003 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 DeprecatedCString_h +#define DeprecatedCString_h + +#include "DeprecatedArray.h" +#include <string.h> + +namespace WebCore { + +class DeprecatedCString : public DeprecatedByteArray { +public: + DeprecatedCString(); + DeprecatedCString(int); + DeprecatedCString(const char *); + DeprecatedCString(const char *, unsigned); + + DeprecatedCString &operator=(const char *); + + bool isEmpty() const; + bool isNull() const { return data() == 0; } + int find(const char *, int index=0, bool cs=true) const; + int contains(char, bool cs=true) const; + unsigned length() const; + bool truncate(unsigned); + DeprecatedCString lower() const; + DeprecatedCString upper() const; + DeprecatedCString left(unsigned) const; + DeprecatedCString right(unsigned) const; + DeprecatedCString mid(unsigned, unsigned len=0xffffffff) const; + + DeprecatedCString &append(char); + DeprecatedCString &append(const char *); + DeprecatedCString &replace(char, char); + + operator const char *() const { return data(); } + DeprecatedCString &operator+=(const char *s) { return append(s); } + DeprecatedCString &operator+=(char c) { return append(c); } + +private: + bool resize(unsigned); +}; + +bool operator==(const DeprecatedCString &s1, const char *s2); +inline bool operator==(const char *s1, const DeprecatedCString &s2) { return s2 == s1; } +inline bool operator!=(const DeprecatedCString &s1, const char *s2) { return !(s1 == s2); } +inline bool operator!=(const char *s1, const DeprecatedCString &s2) { return !(s1 == s2); } + +} + +#endif diff --git a/WebCore/platform/DeprecatedString.cpp b/WebCore/platform/DeprecatedString.cpp new file mode 100644 index 0000000..3656224 --- /dev/null +++ b/WebCore/platform/DeprecatedString.cpp @@ -0,0 +1,2679 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "DeprecatedString.h" + +#include "CString.h" +#include "FloatConversion.h" +#include "Logging.h" +#include "PlatformString.h" +#include "RegularExpression.h" +#include "TextEncoding.h" +#include <kjs/dtoa.h> +#include <kjs/identifier.h> +#include <stdarg.h> +#include <stdio.h> +#include <wtf/Platform.h> +#include <wtf/StringExtras.h> + +#if PLATFORM(WIN_OS) +#include <windows.h> +#endif + +#if PLATFORM(QT) +#include <QString> +#endif + +using namespace std; +using namespace KJS; +using namespace WTF; + +namespace WebCore { + +COMPILE_ASSERT(sizeof(DeprecatedChar) == 2, deprecated_char_is_2_bytes) + +#define CHECK_FOR_HANDLE_LEAKS 0 + +#if PLATFORM(SYMBIAN) +#undef CHECK_FOR_HANDLE_LEAKS +// symbian:fixme need page aligned allocations as Symbian platform does not have support for valloc +#define CHECK_FOR_HANDLE_LEAKS 1 +#endif + +#define ALLOC_QCHAR_GOOD_SIZE(X) (X) +#define ALLOC_CHAR_GOOD_SIZE(X) (X) + +#define ALLOC_CHAR(N) (char*)fastMalloc(N) +#define REALLOC_CHAR(P, N) (char *)fastRealloc(P, N) +#define DELETE_CHAR(P) fastFree(P) + +#define WEBCORE_ALLOCATE_CHARACTERS(N) (DeprecatedChar*)fastMalloc(sizeof(DeprecatedChar)*(N)) +#define WEBCORE_REALLOCATE_CHARACTERS(P, N) (DeprecatedChar *)fastRealloc(P, sizeof(DeprecatedChar)*(N)) +#define DELETE_QCHAR(P) fastFree(P) + +#ifndef CHECK_FOR_HANDLE_LEAKS +struct HandleNode; +struct HandlePageNode; + +static HandleNode *allocateNode(HandlePageNode *pageNode); +static HandlePageNode *allocatePageNode(); + +static HandlePageNode *usedNodeAllocationPages = 0; +static HandlePageNode *freeNodeAllocationPages = 0; + +static inline void initializeHandleNodes() +{ + if (freeNodeAllocationPages == 0) + freeNodeAllocationPages = allocatePageNode(); +} +#endif + +static inline DeprecatedStringData **allocateHandle() +{ +#ifdef CHECK_FOR_HANDLE_LEAKS + return static_cast<DeprecatedStringData **>(fastMalloc(sizeof(DeprecatedStringData *))); +#else + + initializeHandleNodes(); + + return reinterpret_cast<DeprecatedStringData **>(allocateNode(freeNodeAllocationPages)); +#endif +} + +static void freeHandle(DeprecatedStringData **); + +#define IS_ASCII_QCHAR(c) ((c).unicode() > 0 && (c).unicode() <= 0xff) + +static const int caseDelta = ('a' - 'A'); + +const char * const DeprecatedString::null = 0; + +DeprecatedStringData *DeprecatedString::shared_null = 0; +DeprecatedStringData **DeprecatedString::shared_null_handle = 0; + +// ------------------------------------------------------------------------- +// Utility functions +// ------------------------------------------------------------------------- + +static inline int ucstrcmp( const DeprecatedString &as, const DeprecatedString &bs ) +{ + const DeprecatedChar *a = as.unicode(); + const DeprecatedChar *b = bs.unicode(); + if ( a == b ) + return 0; + if ( a == 0 ) + return 1; + if ( b == 0 ) + return -1; + int l = min(as.length(), bs.length()); + while ( l-- && *a == *b ) + a++,b++; + if ( l == -1 ) + return ( as.length() - bs.length() ); + return a->unicode() - b->unicode(); +} + +static bool equal(const DeprecatedChar *a, const char *b, int l) +{ + ASSERT(l >= 0); + while (l--) { + if (*a != *b) + return false; + a++; b++; + } + return true; +} + +// Not a "true" case insensitive compare; only insensitive for plain ASCII. + +static bool equalCaseInsensitive(const char *a, const char *b, int l) +{ + ASSERT(l >= 0); + while (l--) { + if (toASCIILower(*a) != toASCIILower(*b)) + return false; + a++; b++; + } + return true; +} + +static bool equalCaseInsensitive(const DeprecatedChar *a, const char *b, int l) +{ + ASSERT(l >= 0); + while (l--) { + if (toASCIILower(a->unicode()) != static_cast<unsigned char>(toASCIILower(*b))) + return false; + a++; b++; + } + return true; +} + +static bool equalCaseInsensitive(const DeprecatedChar *a, const DeprecatedChar *b, int l) +{ + ASSERT(l >= 0); + while (l--) { + if (toASCIILower(a->unicode()) != toASCIILower(b->unicode())) + return false; + a++; b++; + } + return true; +} + +static inline bool equalCaseInsensitive(char c1, char c2) +{ + return toASCIILower(c1) == toASCIILower(c2); +} + +static inline bool equalCaseInsensitive(DeprecatedChar c1, char c2) +{ + return toASCIILower(c1.unicode()) == static_cast<unsigned char>(toASCIILower(c2)); +} + +static bool isCharacterAllowedInBase(DeprecatedChar c, int base) +{ + ::UChar uc = c.unicode(); + if (uc > 0x7F) + return false; + if (isASCIIDigit(uc)) + return uc - '0' < base; + if (isASCIIAlpha(uc)) { + if (base > 36) + base = 36; + return (uc >= 'a' && uc < 'a' + base - 10) + || (uc >= 'A' && uc < 'A' + base - 10); + } + return false; +} + +// ------------------------------------------------------------------------- +// DeprecatedStringData +// ------------------------------------------------------------------------- + +// FIXME, make constructor explicity take a 'copy' flag. +// This can be used to hand off ownership of allocated data when detaching and +// deleting QStrings. + +DeprecatedStringData::DeprecatedStringData() : + refCount(1), _length(0), _unicode(0), _ascii(0), _maxUnicode(WEBCORE_DS_INTERNAL_BUFFER_UCHARS), _isUnicodeValid(0), _isHeapAllocated(0), _maxAscii(WEBCORE_DS_INTERNAL_BUFFER_CHARS), _isAsciiValid(1) +{ + _ascii = _internalBuffer; + _internalBuffer[0] = 0; +} + +void DeprecatedStringData::initialize() +{ + refCount = 1; + _length = 0; + _unicode = 0; + _ascii = _internalBuffer; + _maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; + _isUnicodeValid = 0; + _maxAscii = WEBCORE_DS_INTERNAL_BUFFER_CHARS; + _isAsciiValid = 1; + _internalBuffer[0] = 0; + _isHeapAllocated = 0; +} + +// Don't copy data. +DeprecatedStringData::DeprecatedStringData(DeprecatedChar *u, unsigned l, unsigned m) : + refCount(1), _length(l), _unicode(u), _ascii(0), _maxUnicode(m), _isUnicodeValid(1), _isHeapAllocated(0), _maxAscii(WEBCORE_DS_INTERNAL_BUFFER_CHARS), _isAsciiValid(0) +{ + ASSERT(m >= l); +} + +// Don't copy data. +void DeprecatedStringData::initialize(DeprecatedChar *u, unsigned l, unsigned m) +{ + ASSERT(m >= l); + refCount = 1; + _length = l; + _unicode = u; + _ascii = 0; + _maxUnicode = m; + _isUnicodeValid = 1; + _maxAscii = 0; + _isAsciiValid = 0; + _isHeapAllocated = 0; +} + +// Copy data +DeprecatedStringData::DeprecatedStringData(const DeprecatedChar *u, unsigned l) +{ + initialize (u, l); +} + +// Copy data +void DeprecatedStringData::initialize(const DeprecatedChar *u, unsigned l) +{ + refCount = 1; + _length = l; + _ascii = 0; + _isUnicodeValid = 1; + _maxAscii = 0; + _isAsciiValid = 0; + _isHeapAllocated = 0; + + if (l > WEBCORE_DS_INTERNAL_BUFFER_UCHARS) { + _maxUnicode = ALLOC_QCHAR_GOOD_SIZE(l); + _unicode = WEBCORE_ALLOCATE_CHARACTERS(_maxUnicode); + memcpy(_unicode, u, l*sizeof(DeprecatedChar)); + } else { + _maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; + _unicode = (DeprecatedChar *)_internalBuffer; + if (l) + memcpy(_internalBuffer, u, l*sizeof(DeprecatedChar)); + } +} + + +// Copy data +DeprecatedStringData::DeprecatedStringData(const char *a, unsigned l) +{ + initialize(a, l); +} + + +// Copy data +void DeprecatedStringData::initialize(const char *a, unsigned l) +{ + refCount = 1; + _length = l; + _unicode = 0; + _isUnicodeValid = 0; + _maxUnicode = 0; + _isAsciiValid = 1; + _isHeapAllocated = 0; + + if (l > WEBCORE_DS_INTERNAL_BUFFER_CHARS) { + _maxAscii = ALLOC_CHAR_GOOD_SIZE(l+1); + _ascii = ALLOC_CHAR(_maxAscii); + if (a) + memcpy(_ascii, a, l); + _ascii[l] = 0; + } else { + _maxAscii = WEBCORE_DS_INTERNAL_BUFFER_CHARS; + _ascii = _internalBuffer; + if (a) + memcpy(_internalBuffer, a, l); + _internalBuffer[l] = 0; + } +} + +DeprecatedStringData* DeprecatedStringData::createAndAdopt(DeprecatedStringData &o) +{ + DeprecatedStringData* data = new DeprecatedStringData(); + data->adopt(o); + return data; +} + +void DeprecatedStringData::adopt(DeprecatedStringData& o) +{ + ASSERT(refCount == 1); + _length = o._length; + _unicode = o._unicode; + _ascii = o._ascii; + _maxUnicode = o._maxUnicode; + _isUnicodeValid = o._isUnicodeValid; + _isHeapAllocated = 0; + _maxAscii = o._maxAscii; + _isAsciiValid = o._isAsciiValid; + + // Handle the case where either the Unicode or 8-bit pointer was + // pointing to the internal buffer. We need to point at the + // internal buffer in the new object, and copy the characters. + if (_unicode == reinterpret_cast<DeprecatedChar *>(o._internalBuffer)) { + if (_isUnicodeValid) { + ASSERT(!_isAsciiValid || _ascii != o._internalBuffer); + ASSERT(_length <= WEBCORE_DS_INTERNAL_BUFFER_UCHARS); + memcpy(_internalBuffer, o._internalBuffer, _length * sizeof(DeprecatedChar)); + _unicode = reinterpret_cast<DeprecatedChar *>(_internalBuffer); + } else { + _unicode = 0; + } + } + if (_ascii == o._internalBuffer) { + if (_isAsciiValid) { + ASSERT(_length <= WEBCORE_DS_INTERNAL_BUFFER_CHARS); + memcpy(_internalBuffer, o._internalBuffer, _length); + _internalBuffer[_length] = 0; + _ascii = _internalBuffer; + } else { + _ascii = 0; + } + } + + // Clean up the other DeprecatedStringData just enough so that it can be destroyed + // cleanly. It's not in a good enough state to use, but that's OK. It just + // needs to be in a state where ~DeprecatedStringData won't do anything harmful, + // and setting these to 0 will do that (preventing any double-free problems). + o._unicode = 0; + o._ascii = 0; +} + +DeprecatedStringData *DeprecatedString::makeSharedNull() +{ + if (!shared_null) { + shared_null = new DeprecatedStringData; + shared_null->ref(); + shared_null->_maxAscii = 0; + shared_null->_maxUnicode = 0; + shared_null->_unicode = (DeprecatedChar *)&shared_null->_internalBuffer[0]; + shared_null->_isUnicodeValid = 1; + } + return shared_null; +} + +DeprecatedStringData **DeprecatedString::makeSharedNullHandle() +{ + if (!shared_null_handle) { + shared_null_handle = allocateHandle(); + *shared_null_handle = makeSharedNull(); + } + return shared_null_handle; +} + +DeprecatedStringData::~DeprecatedStringData() +{ + ASSERT(refCount == 0); + if (_unicode && !isUnicodeInternal()) + DELETE_QCHAR(_unicode); + if (_ascii && !isAsciiInternal()) + DELETE_CHAR(_ascii); +} + +void DeprecatedStringData::increaseAsciiSize(unsigned size) +{ + ASSERT(this != DeprecatedString::shared_null); + + unsigned newSize = (unsigned)ALLOC_CHAR_GOOD_SIZE((size * 3 + 1) / 2); + + if (!_isAsciiValid) + makeAscii(); + ASSERT(_isAsciiValid); + + if (isAsciiInternal()) { + char *newAscii = ALLOC_CHAR(newSize); + if (_length) + memcpy(newAscii, _ascii, _length); + _ascii = newAscii; + } else { + _ascii = REALLOC_CHAR(_ascii, newSize); + } + + _maxAscii = newSize; + _isAsciiValid = 1; + _isUnicodeValid = 0; +} + + +void DeprecatedStringData::increaseUnicodeSize(unsigned size) +{ + ASSERT(size > _length); + ASSERT(this != DeprecatedString::shared_null); + + unsigned newSize = (unsigned)ALLOC_QCHAR_GOOD_SIZE((size * 3 + 1) / 2); + + if (!_isUnicodeValid) + makeUnicode(); + ASSERT(_isUnicodeValid); + + if (isUnicodeInternal()) { + DeprecatedChar *newUni = WEBCORE_ALLOCATE_CHARACTERS(newSize); + if (_length) + memcpy(newUni, _unicode, _length*sizeof(DeprecatedChar)); + _unicode = newUni; + } else { + _unicode = WEBCORE_REALLOCATE_CHARACTERS(_unicode, newSize); + } + + _maxUnicode = newSize; + _isUnicodeValid = 1; + _isAsciiValid = 0; +} + + +char *DeprecatedStringData::makeAscii() +{ + ASSERT(this != DeprecatedString::shared_null); + + if (_isUnicodeValid){ + DeprecatedChar copyBuf[WEBCORE_DS_INTERNAL_BUFFER_CHARS]; + DeprecatedChar *str; + + if (_ascii && !isAsciiInternal()) + DELETE_QCHAR(_ascii); + + if (_length < WEBCORE_DS_INTERNAL_BUFFER_CHARS){ + if (isUnicodeInternal()) { + unsigned i = _length; + DeprecatedChar *tp = ©Buf[0], *fp = _unicode; + while (i--) + *tp++ = *fp++; + str = ©Buf[0]; + _isUnicodeValid = 0; + } + else + str = _unicode; + _ascii = _internalBuffer; + _maxAscii = WEBCORE_DS_INTERNAL_BUFFER_CHARS; + } + else { + unsigned newSize = ALLOC_CHAR_GOOD_SIZE(_length+1); + _ascii = ALLOC_CHAR(newSize); + _maxAscii = newSize; + str = _unicode; + } + + unsigned i = _length; + char* cp = _ascii; + while (i--) + // FIXME: this converts non-Latin1 characters to '\0', which may be not what we want in some cases. + // In particular, toDouble() may fail to report errors, believing that the string ends earlier + // than it actually does. + *cp++ = (*str++).latin1(); + *cp = 0; + + _isAsciiValid = 1; + } + else if (!_isAsciiValid) + FATAL("ASCII character cache not valid"); + + return _ascii; +} + + +DeprecatedChar *DeprecatedStringData::makeUnicode() +{ + ASSERT(this != DeprecatedString::shared_null); + + if (_isAsciiValid){ + char copyBuf[WEBCORE_DS_INTERNAL_BUFFER_CHARS]; + char *str; + + if (_unicode && !isUnicodeInternal()) + DELETE_QCHAR(_unicode); + + if (_length <= WEBCORE_DS_INTERNAL_BUFFER_UCHARS){ + if (isAsciiInternal()) { + unsigned i = _length; + char *tp = ©Buf[0], *fp = _ascii; + while (i--) + *tp++ = *fp++; + str = ©Buf[0]; + _isAsciiValid = 0; + } + else + str = _ascii; + _unicode = (DeprecatedChar *)_internalBuffer; + _maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; + } + else { + unsigned newSize = ALLOC_QCHAR_GOOD_SIZE(_length); + _unicode = WEBCORE_ALLOCATE_CHARACTERS(newSize); + _maxUnicode = newSize; + str = _ascii; + } + unsigned i = _length; + DeprecatedChar *cp = _unicode; + while ( i-- ) + *cp++ = *str++; + + _isUnicodeValid = 1; + } + else if (!_isUnicodeValid) + FATAL("invalid character cache"); + + return _unicode; +} + + +// ------------------------------------------------------------------------- +// DeprecatedString +// ------------------------------------------------------------------------- + + +DeprecatedString DeprecatedString::number(int n) +{ + DeprecatedString qs; + qs.setNum(n); + return qs; +} + +DeprecatedString DeprecatedString::number(unsigned n) +{ + DeprecatedString qs; + qs.setNum(n); + return qs; +} + +DeprecatedString DeprecatedString::number(long n) +{ + DeprecatedString qs; + qs.setNum(n); + return qs; +} + +DeprecatedString DeprecatedString::number(unsigned long n) +{ + DeprecatedString qs; + qs.setNum(n); + return qs; +} + +DeprecatedString DeprecatedString::number(double n) +{ + DeprecatedString qs; + qs.setNum(n); + return qs; +} + +inline void DeprecatedString::detachIfInternal() +{ + DeprecatedStringData *oldData = *dataHandle; + if (oldData->refCount > 1 && oldData == &internalData) { + DeprecatedStringData *newData = DeprecatedStringData::createAndAdopt(*oldData); + newData->_isHeapAllocated = 1; + newData->refCount = oldData->refCount; + oldData->refCount = 1; + oldData->deref(); + *dataHandle = newData; + } +} + +const DeprecatedChar *DeprecatedString::stableUnicode() +{ + // if we're using the internal data of another string, detach now + if (!dataHandle[0]->_isHeapAllocated && *dataHandle != &internalData) { + detach(); + } + return unicode(); +} + + +DeprecatedString::~DeprecatedString() +{ + ASSERT(dataHandle); + ASSERT(dataHandle[0]->refCount != 0); + + // Only free the handle if no other string has a reference to the + // data. The handle will be freed by the string that has the + // last reference to data. + bool needToFreeHandle = dataHandle[0]->refCount == 1 && *dataHandle != shared_null; + + // Copy our internal data if necessary, other strings still need it. + detachIfInternal(); + + // Remove our reference. This should always be the last reference + // if *dataHandle points to our internal DeprecatedStringData. If we just detached, + // this will remove the extra ref from the new handle. + dataHandle[0]->deref(); + + ASSERT(*dataHandle != &internalData || dataHandle[0]->refCount == 0); + + if (needToFreeHandle) + freeHandle(dataHandle); + +#ifndef NDEBUG + dataHandle = 0; +#endif +} + + +DeprecatedString::DeprecatedString() +{ + internalData.deref(); + dataHandle = makeSharedNullHandle(); + dataHandle[0]->ref(); +} + + +// Careful, just used by DeprecatedConstString +DeprecatedString::DeprecatedString(DeprecatedStringData *constData, bool /*dummy*/) +{ + internalData.deref(); + dataHandle = allocateHandle(); + *dataHandle = constData; + + // The DeprecatedConstString constructor allocated the DeprecatedStringData. + constData->_isHeapAllocated = 1; +} + + +DeprecatedString::DeprecatedString(DeprecatedChar qc) +{ + dataHandle = allocateHandle(); + + // Copy the DeprecatedChar. + if (IS_ASCII_QCHAR(qc)) { + char c = qc.unicode(); + *dataHandle = &internalData; + internalData.initialize( &c, 1 ); + } + else { + *dataHandle = &internalData; + internalData.initialize( &qc, 1 ); + } +} + +DeprecatedString::DeprecatedString(const DeprecatedChar *unicode, unsigned length) +{ + if (!unicode || !length) { + internalData.deref(); + dataHandle = makeSharedNullHandle(); + dataHandle[0]->ref(); + } else { + dataHandle = allocateHandle(); + + // Copy the DeprecatedChar * + *dataHandle = &internalData; + internalData.initialize(unicode, length); + } +} + +DeprecatedString::DeprecatedString(const char *chs) +{ + if (chs) { + internalData.initialize(chs,strlen(chs)); + dataHandle = allocateHandle(); + *dataHandle = &internalData; + } else { + internalData.deref(); + dataHandle = makeSharedNullHandle(); + dataHandle[0]->ref(); + } +} + +DeprecatedString::DeprecatedString(const char *chs, int len) +{ + dataHandle = allocateHandle(); + *dataHandle = &internalData; + internalData.initialize(chs,len); +} + +DeprecatedString::DeprecatedString(const DeprecatedString &qs) : dataHandle(qs.dataHandle) +{ + internalData.deref(); + dataHandle[0]->ref(); +} + +DeprecatedString &DeprecatedString::operator=(const DeprecatedString &qs) +{ + if (this == &qs) + return *this; + + // Free our handle if it isn't the shared null handle, and if no-one else is using it. + bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; + + qs.dataHandle[0]->ref(); + deref(); + + if (needToFreeHandle) + freeHandle(dataHandle); + + dataHandle = qs.dataHandle; + + return *this; +} + +DeprecatedString &DeprecatedString::operator=(const DeprecatedCString &qcs) +{ + return setLatin1(qcs); +} + +DeprecatedString &DeprecatedString::operator=(const char *chs) +{ + return setLatin1(chs); +} + +DeprecatedString &DeprecatedString::operator=(DeprecatedChar qc) +{ + return *this = DeprecatedString(qc); +} + +DeprecatedString &DeprecatedString::operator=(char ch) +{ + return *this = DeprecatedString(DeprecatedChar(ch)); +} + +DeprecatedChar DeprecatedString::at(unsigned i) const +{ + DeprecatedStringData *thisData = *dataHandle; + + if (i >= thisData->_length) + return 0; + + if (thisData->_isAsciiValid) { + return thisData->_ascii[i]; + } + + ASSERT(thisData->_isUnicodeValid); + return thisData->_unicode[i]; +} + +int DeprecatedString::compare(const DeprecatedString& s) const +{ + if (dataHandle[0]->_isAsciiValid && s.dataHandle[0]->_isAsciiValid) + return strcmp(ascii(), s.ascii()); + return ucstrcmp(*this,s); +} + +int DeprecatedString::compare(const char *chs) const +{ + if (!chs) + return isEmpty() ? 0 : 1; + DeprecatedStringData *d = dataHandle[0]; + if (d->_isAsciiValid) + return strcmp(ascii(), chs); + const DeprecatedChar *s = unicode(); + unsigned len = d->_length; + for (unsigned i = 0; i != len; ++i) { + char c2 = chs[i]; + if (!c2) + return 1; + DeprecatedChar c1 = s[i]; + if (c1.unicode() < c2) + return -1; + if (c1.unicode() > c2) + return 1; + } + return chs[len] ? -1 : 0; +} + +bool DeprecatedString::startsWith( const DeprecatedString& s ) const +{ + if (dataHandle[0]->_isAsciiValid){ + const char *asc = ascii(); + + for ( int i =0; i < (int) s.dataHandle[0]->_length; i++ ) { + if ( i >= (int) dataHandle[0]->_length || asc[i] != s[i] ) + return false; + } + } + else if (dataHandle[0]->_isUnicodeValid){ + const DeprecatedChar *uni = unicode(); + + for ( int i =0; i < (int) s.dataHandle[0]->_length; i++ ) { + if ( i >= (int) dataHandle[0]->_length || uni[i] != s[i] ) + return false; + } + } + else + FATAL("invalid character cache"); + + return true; +} + +bool DeprecatedString::startsWith(const char *prefix) const +{ + DeprecatedStringData *data = *dataHandle; + + unsigned prefixLength = strlen(prefix); + if (data->_isAsciiValid) { + return strncmp(prefix, data->_ascii, prefixLength) == 0; + } else { + ASSERT(data->_isUnicodeValid); + if (prefixLength > data->_length) { + return false; + } + const DeprecatedChar *uni = data->_unicode; + for (unsigned i = 0; i < prefixLength; ++i) { + if (uni[i] != prefix[i]) { + return false; + } + } + return true; + } +} + +bool DeprecatedString::startsWith(const char *prefix, bool caseSensitive) const +{ + if (caseSensitive) { + return startsWith(prefix); + } + + DeprecatedStringData *data = *dataHandle; + + unsigned prefixLength = strlen(prefix); + if (data->_isAsciiValid) { + return strncasecmp(prefix, data->_ascii, prefixLength) == 0; + } else { + ASSERT(data->_isUnicodeValid); + if (prefixLength > data->_length) { + return false; + } + const DeprecatedChar *uni = data->_unicode; + for (unsigned i = 0; i < prefixLength; ++i) { + if (!equalCaseInsensitive(uni[i], prefix[i])) { + return false; + } + } + return true; + } +} + +bool DeprecatedString::endsWith(const DeprecatedString& s) const +{ + const DeprecatedChar *uni = unicode(); + + int length = dataHandle[0]->_length; + int slength = s.dataHandle[0]->_length; + if (length < slength) + return false; + + for (int i = length - slength, j = 0; i < length; i++, j++) { + if (uni[i] != s[j]) + return false; + } + + return true; +} + +bool DeprecatedString::isNull() const +{ + return dataHandle == shared_null_handle; +} + +int DeprecatedString::find(DeprecatedChar qc, int index) const +{ + if (dataHandle[0]->_isAsciiValid) { + if (!IS_ASCII_QCHAR(qc)) + return -1; + return find(qc.unicode(), index); + } + return find(DeprecatedString(qc), index, true); +} + +int DeprecatedString::find(char ch, int index) const +{ + if (dataHandle[0]->_isAsciiValid){ + const char *cp = ascii(); + + if ( index < 0 ) + index += dataHandle[0]->_length; + + if (index >= (int)dataHandle[0]->_length) + return -1; + + for (int i = index; i < (int)dataHandle[0]->_length; i++) + if (cp[i] == ch) + return i; + } + else if (dataHandle[0]->_isUnicodeValid) + return find(DeprecatedChar(ch), index, true); + else + FATAL("invalid character cache"); + + return -1; +} + +int DeprecatedString::find(const DeprecatedString &str, int index, bool caseSensitive) const +{ + // FIXME, use the first character algorithm + /* + We use some weird hashing for efficiency's sake. Instead of + comparing strings, we compare the sum of str with that of + a part of this DeprecatedString. Only if that matches, we call memcmp + or ucstrnicmp. + + The hash value of a string is the sum of the cells of its + QChars. + */ + if ( index < 0 ) + index += dataHandle[0]->_length; + int lstr = str.dataHandle[0]->_length; + int lthis = dataHandle[0]->_length - index; + if ( (unsigned)lthis > dataHandle[0]->_length ) + return -1; + int delta = lthis - lstr; + if ( delta < 0 ) + return -1; + + const DeprecatedChar *uthis = unicode() + index; + const DeprecatedChar *ustr = str.unicode(); + unsigned hthis = 0; + unsigned hstr = 0; + int i; + if ( caseSensitive ) { + for ( i = 0; i < lstr; i++ ) { + hthis += uthis[i].unicode(); + hstr += ustr[i].unicode(); + } + i = 0; + while ( true ) { + if ( hthis == hstr && memcmp(uthis + i, ustr, lstr * sizeof(DeprecatedChar)) == 0 ) + return index + i; + if ( i == delta ) + return -1; + hthis += uthis[i + lstr].unicode(); + hthis -= uthis[i].unicode(); + i++; + } + } else { + for ( i = 0; i < lstr; i++ ) { + hthis += toASCIILower(uthis[i].unicode()); + hstr += toASCIILower(ustr[i].unicode()); + } + i = 0; + while ( true ) { + if ( hthis == hstr && equalCaseInsensitive(uthis + i, ustr, lstr) ) + return index + i; + if ( i == delta ) + return -1; + hthis += toASCIILower(uthis[i + lstr].unicode()); + hthis -= toASCIILower(uthis[i].unicode()); + i++; + } + } +} + +// This function should be as fast as possible, every little bit helps. +// Our usage patterns are typically small strings. In time trials +// this simplistic algorithm is much faster than Boyer-Moore or hash +// based algorithms. +int DeprecatedString::find(const char *chs, int index, bool caseSensitive) const +{ + if (!chs || index < 0) + return -1; + + DeprecatedStringData *data = *dataHandle; + + int chsLength = strlen(chs); + int n = data->_length - index; + if (n < 0) + return -1; + n -= chsLength - 1; + if (n <= 0) + return -1; + + const char *chsPlusOne = chs + 1; + int chsLengthMinusOne = chsLength - 1; + + if (data->_isAsciiValid) { + char *ptr = data->_ascii + index - 1; + if (caseSensitive) { + char c = *chs; + do { + if (*++ptr == c && memcmp(ptr + 1, chsPlusOne, chsLengthMinusOne) == 0) { + return data->_length - chsLength - n + 1; + } + } while (--n); + } else { + unsigned char lc = toASCIILower(*chs); + do { + if (toASCIILower(*++ptr) == lc && equalCaseInsensitive(ptr + 1, chsPlusOne, chsLengthMinusOne)) { + return data->_length - chsLength - n + 1; + } + } while (--n); + } + } else { + ASSERT(data->_isUnicodeValid); + + const DeprecatedChar *ptr = data->_unicode + index - 1; + if (caseSensitive) { + DeprecatedChar c = *chs; + do { + if (*++ptr == c && equal(ptr + 1, chsPlusOne, chsLengthMinusOne)) { + return data->_length - chsLength - n + 1; + } + } while (--n); + } else { + unsigned char lc = toASCIILower(*chs); + do { + if (toASCIILower((++ptr)->unicode()) == lc && equalCaseInsensitive(ptr + 1, chsPlusOne, chsLengthMinusOne)) { + return data->_length - chsLength - n + 1; + } + } while (--n); + } + } + + return -1; +} + +int DeprecatedString::find(const RegularExpression &qre, int index) const +{ + if ( index < 0 ) + index += dataHandle[0]->_length; + return qre.match( *this, index ); +} + +int DeprecatedString::findRev(char ch, int index) const +{ + if (dataHandle[0]->_isAsciiValid){ + const char *cp = ascii(); + + if (index < 0) + index += dataHandle[0]->_length; + if (index > (int)dataHandle[0]->_length) + return -1; + + for (int i = index; i >= 0; i--) { + if (cp[i] == ch) + return i; + } + } + else if (dataHandle[0]->_isUnicodeValid) + return findRev(DeprecatedString(DeprecatedChar(ch)), index); + else + FATAL("invalid character cache"); + + return -1; +} + +int DeprecatedString::findRev(const char *chs, int index) const +{ + return findRev(DeprecatedString(chs), index); +} + +int DeprecatedString::findRev( const DeprecatedString& str, int index, bool cs ) const +{ + // FIXME, use the first character algorithm + /* + See DeprecatedString::find() for explanations. + */ + int lthis = dataHandle[0]->_length; + if ( index < 0 ) + index += lthis; + + int lstr = str.dataHandle[0]->_length; + int delta = lthis - lstr; + if ( index < 0 || index > lthis || delta < 0 ) + return -1; + if ( index > delta ) + index = delta; + + const DeprecatedChar *uthis = unicode(); + const DeprecatedChar *ustr = str.unicode(); + unsigned hthis = 0; + unsigned hstr = 0; + int i; + if ( cs ) { + for ( i = 0; i < lstr; i++ ) { + hthis += uthis[index + i].unicode(); + hstr += ustr[i].unicode(); + } + i = index; + while ( true ) { + if ( hthis == hstr && memcmp(uthis + i, ustr, lstr * sizeof(DeprecatedChar)) == 0 ) + return i; + if ( i == 0 ) + return -1; + i--; + hthis -= uthis[i + lstr].unicode(); + hthis += uthis[i].unicode(); + } + } else { + for ( i = 0; i < lstr; i++ ) { + hthis += uthis[index + i].lower().unicode(); + hstr += ustr[i].lower().unicode(); + } + i = index; + while ( true ) { + if ( hthis == hstr && equalCaseInsensitive(uthis + i, ustr, lstr) ) + return i; + if ( i == 0 ) + return -1; + i--; + hthis -= uthis[i + lstr].lower().unicode(); + hthis += uthis[i].lower().unicode(); + } + } + + // Should never get here. + return -1; +} + + +int DeprecatedString::contains(DeprecatedChar c, bool cs) const +{ + int count = 0; + + DeprecatedStringData *data = *dataHandle; + + if (data->_isAsciiValid) { + if (!IS_ASCII_QCHAR(c)) + return 0; + const char *cPtr = data->_ascii; + int n = data->_length; + char ac = c.unicode(); + if (cs) { // case sensitive + while (n--) + count += *cPtr++ == ac; + } else { // case insensitive + unsigned char lc = toASCIILower(ac); + while (n--) { + count += toASCIILower(*cPtr++) == lc; + } + } + } else { + ASSERT(data->_isUnicodeValid); + const DeprecatedChar *uc = data->_unicode; + int n = data->_length; + if (cs) { // case sensitive + while ( n-- ) + count += *uc++ == c; + } else { // case insensitive + ::UChar lc = toASCIILower(c.unicode()); + while (n--) { + count += toASCIILower(uc->unicode()) == lc; + uc++; + } + } + } + + return count; +} + +int DeprecatedString::contains(char ch) const +{ + return contains(DeprecatedChar(ch), true); +} + +int DeprecatedString::contains(const char *str, bool caseSensitive) const +{ + if (!str) + return 0; + + int len = strlen(str); + char c = *str; + + DeprecatedStringData *data = *dataHandle; + int n = data->_length; + + n -= len - 1; + if (n <= 0) + return 0; + + int count = 0; + + if (data->_isAsciiValid) { + const char *p = data->_ascii; + if (caseSensitive) { + do { + count += *p == c && memcmp(p + 1, str + 1, len - 1) == 0; + p++; + } while (--n); + } else { + char lc = toASCIILower(c); + do { + count += toASCIILower(*p) == lc && equalCaseInsensitive(p + 1, str + 1, len - 1); + p++; + } while (--n); + } + } else { + ASSERT(data->_isUnicodeValid); + const DeprecatedChar *p = data->_unicode; + if (caseSensitive) { + do { + count += *p == c && equal(p + 1, str + 1, len - 1); + p++; + } while (--n); + } else { + unsigned char lc = toASCIILower(c); + do { + count += toASCIILower(p->unicode()) == lc && equalCaseInsensitive(p + 1, str + 1, len - 1); + p++; + } while (--n); + } + } + + return count; +} + +int DeprecatedString::contains(const DeprecatedString &str, bool caseSensitive) const +{ + if (str.isEmpty()) + return 0; + + const DeprecatedChar *strP = str.unicode(); + int len = str.dataHandle[0]->_length; + DeprecatedChar c = *strP; + + const DeprecatedChar *p = unicode(); + int n = dataHandle[0]->_length; + + n -= len - 1; + if (n <= 0) + return 0; + + int count = 0; + + if (caseSensitive) { + int byteCount = len * sizeof(DeprecatedChar); + do { + count += *p == c && memcmp(p, strP, byteCount) == 0; + ++p; + } while (--n); + } else { + do { + count += p->lower() == c && equalCaseInsensitive(p, strP, len) == 0; + ++p; + } while (--n); + } + + return count; +} + +bool DeprecatedString::isAllASCII() const +{ + DeprecatedStringData *data = *dataHandle; + + int n = data->_length; + if (data->_isAsciiValid) { + const char *p = data->_ascii; + while (n--) { + unsigned char c = *p++; + if (c > 0x7F) { + return false; + } + } + } else { + ASSERT(data->_isUnicodeValid); + const DeprecatedChar *p = data->_unicode; + while (n--) { + if ((*p++).unicode() > 0x7F) { + return false; + } + } + } + + return true; +} + +bool DeprecatedString::isAllLatin1() const +{ + DeprecatedStringData *data = *dataHandle; + + if (data->_isAsciiValid) { + return true; + } + + ASSERT(data->_isUnicodeValid); + int n = data->_length; + const DeprecatedChar *p = data->_unicode; + while (n--) { + if ((*p++).unicode() > 0xFF) { + return false; + } + } + + return true; +} + +bool DeprecatedString::hasFastLatin1() const +{ + DeprecatedStringData *data = *dataHandle; + return data->_isAsciiValid; +} + +void DeprecatedString::copyLatin1(char *buffer, unsigned position, unsigned maxLength) const +{ + DeprecatedStringData *data = *dataHandle; + + int length = data->_length; + if (position > static_cast<unsigned>(length)) + length = 0; + else + length -= position; + if (static_cast<unsigned>(length) > maxLength) + length = static_cast<int>(maxLength); + + buffer[length] = 0; + + if (data->_isAsciiValid) { + memcpy(buffer, data->_ascii + position, length); + return; + } + + ASSERT(data->_isUnicodeValid); + const DeprecatedChar* uc = data->_unicode + position; + while (length--) + *buffer++ = (*uc++).latin1(); +} + +short DeprecatedString::toShort(bool *ok, int base) const +{ + int v = toInt(ok, base); + short sv = v; + if (sv != v) { + if (ok) + *ok = false; + return 0; + } + return sv; +} + +unsigned short DeprecatedString::toUShort(bool *ok, int base) const +{ + unsigned v = toUInt(ok, base); + unsigned short sv = v; + if (sv != v) { + if (ok) + *ok = false; + return 0; + } + return sv; +} + +template <typename IntegralType> static inline +IntegralType toIntegralType(const DeprecatedString& string, bool *ok, int base) +{ + static const IntegralType integralMax = std::numeric_limits<IntegralType>::max(); + static const bool isSigned = std::numeric_limits<IntegralType>::is_signed; + const DeprecatedChar* p = string.unicode(); + const IntegralType maxMultiplier = integralMax / base; + + int length = string.length(); + IntegralType value = 0; + bool isOk = false; + bool isNegative = false; + + if (!p) + goto bye; + + // skip leading whitespace + while (length && p->isSpace()) { + length--; + p++; + } + + if (isSigned && length && *p == '-') { + length--; + p++; + isNegative = true; + } else if (length && *p == '+') { + length--; + p++; + } + + if (!length || !isCharacterAllowedInBase(*p, base)) + goto bye; + + while (length && isCharacterAllowedInBase(*p, base)) { + length--; + IntegralType digitValue; + ::UChar c = p->unicode(); + if (isASCIIDigit(c)) + digitValue = c - '0'; + else if (c >= 'a') + digitValue = c - 'a' + 10; + else + digitValue = c - 'A' + 10; + + if (value > maxMultiplier || (value == maxMultiplier && digitValue > (integralMax % base) + isNegative)) + goto bye; + + value = base * value + digitValue; + p++; + } + + if (isNegative) + value = -value; + + // skip trailing space + while (length && p->isSpace()) { + length--; + p++; + } + + if (!length) + isOk = true; +bye: + if (ok) + *ok = isOk; + return isOk ? value : 0; +} + +int DeprecatedString::toInt(bool *ok, int base) const +{ + return toIntegralType<int>(*this, ok, base); +} + +int64_t DeprecatedString::toInt64(bool *ok, int base) const +{ + return toIntegralType<int64_t>(*this, ok, base); +} + +unsigned DeprecatedString::toUInt(bool *ok, int base) const +{ + return toIntegralType<unsigned>(*this, ok, base); +} + +uint64_t DeprecatedString::toUInt64(bool *ok, int base) const +{ + return toIntegralType<uint64_t>(*this, ok, base); +} + +double DeprecatedString::toDouble(bool *ok) const +{ + if (isEmpty()) { + if (ok) + *ok = false; + return 0; + } + const char *s = latin1(); + char *end; + double val = kjs_strtod(s, &end); + if (ok) + *ok = end == 0 || *end == '\0'; + return val; +} + +float DeprecatedString::toFloat(bool* ok) const +{ + // FIXME: this will return ok even when the string does not fit into a float + return narrowPrecisionToFloat(toDouble(ok)); +} + +DeprecatedString DeprecatedString::left(unsigned len) const +{ + return mid(0, len); +} + +DeprecatedString DeprecatedString::right(unsigned len) const +{ + return mid(length() - len, len); +} + +DeprecatedString DeprecatedString::mid(unsigned start, unsigned len) const +{ + if (dataHandle && *dataHandle) { + DeprecatedStringData &data = **dataHandle; + + // clip length + if (start >= data._length) + return DeprecatedString(); + + if (len > data._length - start) + len = data._length - start; + + if (len == 0) + return DeprecatedString(); + + if (start == 0 && len == data._length) + return *this; + + ASSERT(start + len >= start && // unsigned overflow + start + len <= data._length); // past the end + + // ascii case + if (data._isAsciiValid && data._ascii) + return DeprecatedString(&data._ascii[start] , len); + + // unicode case + if (data._isUnicodeValid && data._unicode) + return DeprecatedString(&data._unicode[start], len); + } + + // degenerate case + return DeprecatedString(); +} + +DeprecatedString DeprecatedString::copy() const +{ + // does not need to be a deep copy + return DeprecatedString(*this); +} + +DeprecatedString DeprecatedString::lower() const +{ + DeprecatedString s(*this); + DeprecatedStringData *d = *s.dataHandle; + int l = d->_length; + if (l) { + bool detached = false; + if (d->_isAsciiValid) { + char *p = d->_ascii; + while (l--) { + char c = *p; + // FIXME: Doesn't work for 0x80-0xFF. + if (c >= 'A' && c <= 'Z') { + if (!detached) { + s.detach(); + d = *s.dataHandle; + p = d->_ascii + d->_length - l - 1; + detached = true; + } + *p = c + ('a' - 'A'); + } + p++; + } + } + else { + ASSERT(d->_isUnicodeValid); + DeprecatedChar *p = d->_unicode; + while (l--) { + DeprecatedChar c = *p; + // FIXME: Doesn't work for 0x80-0xFF. + if (IS_ASCII_QCHAR(c)) { + if (c.unicode() >= 'A' && c.unicode() <= 'Z') { + if (!detached) { + s.detach(); + d = *s.dataHandle; + p = d->_unicode + d->_length - l - 1; + detached = true; + } + *p = c.unicode() + ('a' - 'A'); + } + } else { + DeprecatedChar clower = c.lower(); + if (clower != c) { + if (!detached) { + s.detach(); + d = *s.dataHandle; + p = d->_unicode + d->_length - l - 1; + detached = true; + } + *p = clower; + } + } + p++; + } + } + } + return s; +} + +DeprecatedString DeprecatedString::stripWhiteSpace() const +{ + if ( isEmpty() ) // nothing to do + return *this; + if ( !at(0).isSpace() && !at(dataHandle[0]->_length-1).isSpace() ) + return *this; + + int start = 0; + int end = dataHandle[0]->_length - 1; + + DeprecatedString result = fromLatin1(""); + while ( start<=end && at(start).isSpace() ) // skip white space from start + start++; + if ( start > end ) { // only white space + return result; + } + while ( end && at(end).isSpace() ) // skip white space from end + end--; + int l = end - start + 1; + + if (dataHandle[0]->_isAsciiValid){ + result.setLength( l ); + if ( l ) + memcpy(const_cast<char*>(result.dataHandle[0]->ascii()), &ascii()[start], l ); + } + else if (dataHandle[0]->_isUnicodeValid){ + result.setLength( l ); + if ( l ) + memcpy(result.forceUnicode(), &unicode()[start], sizeof(DeprecatedChar)*l ); + } + else + FATAL("invalid character cache"); + return result; +} + +DeprecatedString DeprecatedString::simplifyWhiteSpace() const +{ + if ( isEmpty() ) // nothing to do + return *this; + + DeprecatedString result; + + if (dataHandle[0]->_isAsciiValid){ + result.setLength( dataHandle[0]->_length ); + const char *from = ascii(); + const char *fromend = from + dataHandle[0]->_length; + int outc=0; + + char *to = const_cast<char*>(result.ascii()); + while ( true ) { + while ( from!=fromend && DeprecatedChar(*from).isSpace() ) + from++; + while ( from!=fromend && !DeprecatedChar(*from).isSpace() ) + to[outc++] = *from++; + if ( from!=fromend ) + to[outc++] = ' '; + else + break; + } + if ( outc > 0 && to[outc-1] == ' ' ) + outc--; + result.truncate( outc ); + } + else if (dataHandle[0]->_isUnicodeValid){ + result.setLength( dataHandle[0]->_length ); + const DeprecatedChar *from = unicode(); + const DeprecatedChar *fromend = from + dataHandle[0]->_length; + int outc=0; + + DeprecatedChar *to = result.forceUnicode(); + while ( true ) { + while ( from!=fromend && from->isSpace() ) + from++; + while ( from!=fromend && !from->isSpace() ) + to[outc++] = *from++; + if ( from!=fromend ) + to[outc++] = ' '; + else + break; + } + if ( outc > 0 && to[outc-1] == ' ' ) + outc--; + result.truncate( outc ); + } + else + FATAL("invalid character cache"); + + return result; +} + +void DeprecatedString::deref() +{ + dataHandle[0]->deref(); +} + + +DeprecatedString &DeprecatedString::setUnicode(const DeprecatedChar *uni, unsigned len) +{ + detachAndDiscardCharacters(); + + // Free our handle if it isn't the shared null handle, and if no-one else is using it. + bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; + + if (len == 0) { + deref(); + if (needToFreeHandle) + freeHandle(dataHandle); + dataHandle = makeSharedNullHandle(); + dataHandle[0]->ref(); + } else if (len > dataHandle[0]->_maxUnicode || dataHandle[0]->refCount != 1 || !dataHandle[0]->_isUnicodeValid) { + deref(); + if (needToFreeHandle) + freeHandle(dataHandle); + dataHandle = allocateHandle(); + *dataHandle = new DeprecatedStringData(uni, len); + dataHandle[0]->_isHeapAllocated = 1; + } else { + if ( uni ) + memcpy( (void *)unicode(), uni, sizeof(DeprecatedChar)*len ); + dataHandle[0]->_length = len; + dataHandle[0]->_isAsciiValid = 0; + } + + return *this; +} + + +DeprecatedString &DeprecatedString::setLatin1(const char *str, int len) +{ + if ( str == 0 ) + return setUnicode(0,0); + if ( len < 0 ) + len = strlen(str); + + detachAndDiscardCharacters(); + + // Free our handle if it isn't the shared null handle, and if no-one else is using it. + bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; + + if (len+1 > (int)dataHandle[0]->_maxAscii || dataHandle[0]->refCount != 1 || !dataHandle[0]->_isAsciiValid) { + deref(); + if (needToFreeHandle) + freeHandle(dataHandle); + dataHandle = allocateHandle(); + *dataHandle = new DeprecatedStringData(str,len); + dataHandle[0]->_isHeapAllocated = 1; + } else { + strcpy(const_cast<char*>(ascii()), str ); + dataHandle[0]->_length = len; + dataHandle[0]->_isUnicodeValid = 0; + } + return *this; +} + +DeprecatedString &DeprecatedString::setNum(short n) +{ + return format("%d", n); +} + +DeprecatedString &DeprecatedString::setNum(unsigned short n) +{ + return format("%u", n); +} + +DeprecatedString &DeprecatedString::setNum(int n) +{ + return format("%d", n); +} + +DeprecatedString &DeprecatedString::setNum(unsigned n) +{ + return format("%u", n); +} + +DeprecatedString &DeprecatedString::setNum(long n) +{ + return format("%ld", n); +} + +DeprecatedString &DeprecatedString::setNum(unsigned long n) +{ + return format("%lu", n); +} + +DeprecatedString &DeprecatedString::setNum(double n) +{ + return format("%.6lg", n); +} + +DeprecatedString &DeprecatedString::format(const char *format, ...) +{ + // FIXME: this needs the same windows compat fixes as String::format + + va_list args; + va_start(args, format); + + // Do the format once to get the length. +#if COMPILER(MSVC) + int result = _vscprintf(format, args); +#else + char ch; + int result = vsnprintf(&ch, 1, format, args); +#endif + + // Handle the empty string case to simplify the code below. + if (result <= 0) { // POSIX returns 0 in error; Windows returns a negative number. + setUnicode(0, 0); + return *this; + } + unsigned len = result; + + // Arrange for storage for the resulting string. + detachAndDiscardCharacters(); + if (len >= dataHandle[0]->_maxAscii || dataHandle[0]->refCount != 1 || !dataHandle[0]->_isAsciiValid) { + // Free our handle if it isn't the shared null handle, and if no-one else is using it. + bool needToFreeHandle = dataHandle != shared_null_handle && dataHandle[0]->refCount == 1; + deref(); + if (needToFreeHandle) + freeHandle(dataHandle); + dataHandle = allocateHandle(); + *dataHandle = new DeprecatedStringData((char *)0, len); + dataHandle[0]->_isHeapAllocated = 1; + } else { + dataHandle[0]->_length = len; + dataHandle[0]->_isUnicodeValid = 0; + } + + // Now do the formatting again, guaranteed to fit. + vsprintf(const_cast<char*>(ascii()), format, args); + + va_end(args); + return *this; +} + +DeprecatedString &DeprecatedString::prepend(const DeprecatedString &qs) +{ + return insert(0, qs); +} + +DeprecatedString &DeprecatedString::prepend(const DeprecatedChar *characters, unsigned length) +{ + return insert(0, characters, length); +} + +DeprecatedString &DeprecatedString::append(const DeprecatedString &qs) +{ + return insert(dataHandle[0]->_length, qs); +} + +DeprecatedString &DeprecatedString::append(const char *characters, unsigned length) +{ + return insert(dataHandle[0]->_length, characters, length); +} + +DeprecatedString &DeprecatedString::append(const DeprecatedChar *characters, unsigned length) +{ + return insert(dataHandle[0]->_length, characters, length); +} + +DeprecatedString &DeprecatedString::insert(unsigned index, const char *insertChars, unsigned insertLength) +{ + if (insertLength == 0) + return *this; + + detach(); + + if (dataHandle[0]->_isAsciiValid){ + unsigned originalLength = dataHandle[0]->_length; + char *targetChars; + + // Ensure that we have enough space. + setLength (originalLength + insertLength); + targetChars = const_cast<char*>(ascii()); + + // Move tail to make space for inserted characters. + memmove (targetChars+index+insertLength, targetChars+index, originalLength-index); + + // Insert characters. + memcpy (targetChars+index, insertChars, insertLength); + + dataHandle[0]->_isUnicodeValid = 0; + } + else if (dataHandle[0]->_isUnicodeValid){ + unsigned originalLength = dataHandle[0]->_length; + DeprecatedChar *targetChars; + + // Ensure that we have enough space. + setLength (originalLength + insertLength); + targetChars = (DeprecatedChar *)unicode(); + + // Move tail to make space for inserted characters. + memmove (targetChars+(index+insertLength), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); + + // Insert characters. + unsigned i = insertLength; + DeprecatedChar *target = targetChars+index; + + while (i--) + *target++ = *insertChars++; + } + else + FATAL("invalid character cache"); + + return *this; +} + + +DeprecatedString &DeprecatedString::insert(unsigned index, const DeprecatedString &qs) +{ + if (qs.dataHandle[0]->_length == 0) + return *this; + + if (dataHandle[0]->_isAsciiValid && qs.isAllLatin1()) { + insert(index, qs.latin1(), qs.length()); + } + else { + unsigned insertLength = qs.dataHandle[0]->_length; + unsigned originalLength = dataHandle[0]->_length; + + forceUnicode(); + + // Ensure that we have enough space. + setLength (originalLength + insertLength); + DeprecatedChar *targetChars = const_cast<DeprecatedChar *>(unicode()); + + // Move tail to make space for inserted characters. + memmove (targetChars+(index+insertLength), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); + + // Insert characters. + if (qs.dataHandle[0]->_isAsciiValid){ + unsigned i = insertLength; + DeprecatedChar *target = targetChars+index; + char *a = const_cast<char*>(qs.ascii()); + + while (i--) + *target++ = *a++; + } + else { + DeprecatedChar *insertChars = (DeprecatedChar *)qs.unicode(); + memcpy (targetChars+index, insertChars, insertLength*sizeof(DeprecatedChar)); + } + + dataHandle[0]->_isAsciiValid = 0; + } + + return *this; +} + + +DeprecatedString &DeprecatedString::insert(unsigned index, const DeprecatedChar *insertChars, unsigned insertLength) +{ + if (insertLength == 0) + return *this; + + forceUnicode(); + + unsigned originalLength = dataHandle[0]->_length; + setLength(originalLength + insertLength); + + DeprecatedChar *targetChars = const_cast<DeprecatedChar *>(unicode()); + if (originalLength > index) { + memmove(targetChars + index + insertLength, targetChars + index, (originalLength - index) * sizeof(DeprecatedChar)); + } + memcpy(targetChars + index, insertChars, insertLength * sizeof(DeprecatedChar)); + + return *this; +} + + +DeprecatedString &DeprecatedString::insert(unsigned index, DeprecatedChar qc) +{ + detach(); + + if (dataHandle[0]->_isAsciiValid && IS_ASCII_QCHAR(qc)){ + unsigned originalLength = dataHandle[0]->_length; + char insertChar = qc.unicode(); + char *targetChars; + + // Ensure that we have enough space. + setLength (originalLength + 1); + targetChars = const_cast<char*>(ascii()); + + // Move tail to make space for inserted character. + memmove (targetChars+index+1, targetChars+index, originalLength-index); + + // Insert character. + targetChars[index] = insertChar; + targetChars[dataHandle[0]->_length] = 0; + + dataHandle[0]->_isUnicodeValid = 0; + } + else { + unsigned originalLength = dataHandle[0]->_length; + + forceUnicode(); + + // Ensure that we have enough space. + setLength (originalLength + 1); + DeprecatedChar *targetChars = const_cast<DeprecatedChar *>(unicode()); + + // Move tail to make space for inserted character. + memmove (targetChars+(index+1), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); + + targetChars[index] = qc; + } + + return *this; +} + + +DeprecatedString &DeprecatedString::insert(unsigned index, char ch) +{ + detach(); + + if (dataHandle[0]->_isAsciiValid) { + unsigned originalLength = dataHandle[0]->_length; + char *targetChars; + + // Ensure that we have enough space. + setLength (originalLength + 1); + targetChars = const_cast<char*>(ascii()); + + // Move tail to make space for inserted character. + memmove (targetChars+index+1, targetChars+index, originalLength-index); + + // Insert character. + targetChars[index] = ch; + targetChars[dataHandle[0]->_length] = 0; + + dataHandle[0]->_isUnicodeValid = 0; + } + else if (dataHandle[0]->_isUnicodeValid){ + unsigned originalLength = dataHandle[0]->_length; + DeprecatedChar *targetChars; + + // Ensure that we have enough space. + setLength (originalLength + 1); + targetChars = (DeprecatedChar *)unicode(); + + // Move tail to make space for inserted character. + memmove (targetChars+(index+1), targetChars+index, (originalLength-index)*sizeof(DeprecatedChar)); + + targetChars[index] = (DeprecatedChar)ch; + } + else + FATAL("invalid character cache"); + + return *this; +} + +// Copy DeprecatedStringData if necessary. Must be called before the string data is mutated. +void DeprecatedString::detach() +{ + DeprecatedStringData *oldData = *dataHandle; + + if (oldData->refCount == 1 && oldData != shared_null) + return; + + // Copy data for this string so we can safely mutate it. + DeprecatedStringData *newData; + if (oldData->_isAsciiValid) + newData = new DeprecatedStringData(oldData->ascii(), oldData->_length); + else + newData = new DeprecatedStringData(oldData->unicode(), oldData->_length); + newData->_isHeapAllocated = 1; + + // There is now one less client for the old data. + oldData->deref(); + + // If the old data is our internal data, then we'll keep that. + // This decreases the chance we'll have to do a detachInternal later + // when this object is destroyed. + if (oldData == &internalData) { + newData->refCount = oldData->refCount; + oldData->refCount = 1; + *dataHandle = newData; + newData = oldData; + } + + // Create a new handle. + dataHandle = allocateHandle(); + *dataHandle = newData; +} + +void DeprecatedString::detachAndDiscardCharacters() +{ + // Missing optimization: Don't bother copying the old data if we detach. + detach(); +} + +DeprecatedString &DeprecatedString::remove(unsigned index, unsigned len) +{ + unsigned olen = dataHandle[0]->_length; + if ( index >= olen ) { + // range problems + } else if ( index + len >= olen ) { // index ok + setLength( index ); + } else if ( len != 0 ) { + // Missing optimization: Could avoid copying characters we are going to remove + // by making a special version of detach(). + + detach(); + + if (dataHandle[0]->_isAsciiValid){ + memmove( dataHandle[0]->ascii()+index, dataHandle[0]->ascii()+index+len, + sizeof(char)*(olen-index-len) ); + setLength( olen-len ); + dataHandle[0]->_isUnicodeValid = 0; + } + else if (dataHandle[0]->_isUnicodeValid){ + memmove( dataHandle[0]->unicode()+index, dataHandle[0]->unicode()+index+len, + sizeof(DeprecatedChar)*(olen-index-len) ); + setLength( olen-len ); + } + else + FATAL("invalid character cache"); + } + return *this; +} + +DeprecatedString &DeprecatedString::replace(unsigned index, unsigned len, const DeprecatedString& str) +{ + return remove(index, len).insert(index, str); +} + +DeprecatedString &DeprecatedString::replace(char pattern, const DeprecatedString &str) +{ + int slen = str.dataHandle[0]->_length; + int index = 0; + while ((index = find(pattern, index)) >= 0) { + replace(index, 1, str); + index += slen; + } + return *this; +} + +DeprecatedString &DeprecatedString::replace(DeprecatedChar pattern, const DeprecatedString &str) +{ + int slen = str.dataHandle[0]->_length; + int index = 0; + while ((index = find(pattern, index)) >= 0) { + replace(index, 1, str); + index += slen; + } + return *this; +} + +DeprecatedString &DeprecatedString::replace(const DeprecatedString &pattern, const DeprecatedString &str) +{ + if (pattern.isEmpty()) + return *this; + int plen = pattern.dataHandle[0]->_length; + int slen = str.dataHandle[0]->_length; + int index = 0; + while ((index = find(pattern, index)) >= 0) { + replace(index, plen, str); + index += slen; + } + return *this; +} + + +DeprecatedString &DeprecatedString::replace(const RegularExpression &qre, const DeprecatedString &str) +{ + if ( isEmpty() ) + return *this; + int index = 0; + int slen = str.dataHandle[0]->_length; + int len; + while ( index < (int)dataHandle[0]->_length ) { + index = qre.match( *this, index, &len); + if ( index >= 0 ) { + replace( index, len, str ); + index += slen; + if ( !len ) + break; // Avoid infinite loop on 0-length matches, e.g. [a-z]* + } + else + break; + } + return *this; +} + + +DeprecatedString &DeprecatedString::replace(DeprecatedChar oldChar, DeprecatedChar newChar) +{ + if (oldChar != newChar && find(oldChar) != -1) { + unsigned length = dataHandle[0]->_length; + + detach(); + if (dataHandle[0]->_isAsciiValid && IS_ASCII_QCHAR(newChar)) { + char *p = const_cast<char *>(ascii()); + dataHandle[0]->_isUnicodeValid = 0; + char oldC = oldChar.unicode(); + char newC = newChar.unicode(); + for (unsigned i = 0; i != length; ++i) { + if (p[i] == oldC) { + p[i] = newC; + } + } + } else { + DeprecatedChar *p = const_cast<DeprecatedChar *>(unicode()); + dataHandle[0]->_isAsciiValid = 0; + for (unsigned i = 0; i != length; ++i) { + if (p[i] == oldChar) { + p[i] = newChar; + } + } + } + } + + return *this; +} + + +DeprecatedChar *DeprecatedString::forceUnicode() +{ + detach(); + DeprecatedChar *result = const_cast<DeprecatedChar *>(unicode()); + dataHandle[0]->_isAsciiValid = 0; + return result; +} + + +// Increase buffer size if necessary. Newly allocated +// bytes will contain garbage. +void DeprecatedString::setLength(unsigned newLen) +{ + if (newLen == 0) { + setUnicode(0, 0); + return; + } + + // Missing optimization: Could avoid copying characters we are going to remove + // by making a special version of detach(). + detach(); + + ASSERT(dataHandle != shared_null_handle); + + if (dataHandle[0]->_isAsciiValid){ + if (newLen+1 > dataHandle[0]->_maxAscii) { + dataHandle[0]->increaseAsciiSize(newLen+1); + } + // Ensure null termination, although newly allocated + // bytes contain garbage. + dataHandle[0]->_ascii[newLen] = 0; + } + + if (dataHandle[0]->_isUnicodeValid){ + if (newLen > dataHandle[0]->_maxUnicode) { + dataHandle[0]->increaseUnicodeSize(newLen); + } + } + + dataHandle[0]->_length = newLen; +} + + +void DeprecatedString::truncate(unsigned newLen) +{ + if ( newLen < dataHandle[0]->_length ) + setLength( newLen ); +} + +void DeprecatedString::fill(DeprecatedChar qc, int len) +{ + detachAndDiscardCharacters(); + + // len == -1 means fill to string length. + if (len < 0) { + len = dataHandle[0]->_length; + } + + if (len == 0) { + if (dataHandle != shared_null_handle) { + ASSERT(dataHandle[0]->refCount == 1); + deref(); + freeHandle(dataHandle); + dataHandle = makeSharedNullHandle(); + shared_null->ref(); + } + } else { + if (dataHandle[0]->_isAsciiValid && IS_ASCII_QCHAR(qc)) { + setLength(len); + char *nd = const_cast<char*>(ascii()); + while (len--) + *nd++ = qc.unicode(); + dataHandle[0]->_isUnicodeValid = 0; + } else { + setLength(len); + DeprecatedChar *nd = forceUnicode(); + while (len--) + *nd++ = qc; + } + } +} + +DeprecatedString &DeprecatedString::append(DeprecatedChar qc) +{ + detach(); + + DeprecatedStringData *thisData = *dataHandle; + if (thisData->_isUnicodeValid && thisData->_length + 1 < thisData->_maxUnicode){ + thisData->_unicode[thisData->_length] = qc; + thisData->_length++; + thisData->_isAsciiValid = 0; + return *this; + } + else if (thisData->_isAsciiValid && IS_ASCII_QCHAR(qc) && thisData->_length + 2 < thisData->_maxAscii){ + thisData->_ascii[thisData->_length] = qc.unicode(); + thisData->_length++; + thisData->_ascii[thisData->_length] = 0; + thisData->_isUnicodeValid = 0; + return *this; + } + return insert(thisData->_length, qc); +} + +DeprecatedString &DeprecatedString::append(char ch) +{ + detach(); + + DeprecatedStringData *thisData = *dataHandle; + if (thisData->_isUnicodeValid && thisData->_length + 1 < thisData->_maxUnicode){ + thisData->_unicode[thisData->_length] = (DeprecatedChar)ch; + thisData->_length++; + thisData->_isAsciiValid = 0; + return *this; + } + else if (thisData->_isAsciiValid && thisData->_length + 2 < thisData->_maxAscii){ + thisData->_ascii[thisData->_length] = ch; + thisData->_length++; + thisData->_ascii[thisData->_length] = 0; + thisData->_isUnicodeValid = 0; + return *this; + } + return insert(thisData->_length, ch); +} + +void DeprecatedString::reserve(unsigned length) +{ + if (length > dataHandle[0]->_maxUnicode) { + detach(); + dataHandle[0]->increaseUnicodeSize(length); + } +} + +bool operator==(const DeprecatedString &s1, const DeprecatedString &s2) +{ + if (s1.dataHandle[0]->_isAsciiValid && s2.dataHandle[0]->_isAsciiValid) { + return strcmp(s1.ascii(), s2.ascii()) == 0; + } + return s1.dataHandle[0]->_length == s2.dataHandle[0]->_length + && memcmp(s1.unicode(), s2.unicode(), s1.dataHandle[0]->_length * sizeof(DeprecatedChar)) == 0; +} + +bool operator==(const DeprecatedString &s1, const char *chs) +{ + if (!chs) + return s1.isNull(); + DeprecatedStringData *d = s1.dataHandle[0]; + unsigned len = d->_length; + if (d->_isAsciiValid) { + const char *s = s1.ascii(); + for (unsigned i = 0; i != len; ++i) { + char c = chs[i]; + if (!c || s[i] != c) + return false; + } + } else { + const DeprecatedChar *s = s1.unicode(); + for (unsigned i = 0; i != len; ++i) { + char c = chs[i]; + if (!c || s[i] != c) + return false; + } + } + return chs[len] == '\0'; +} + +DeprecatedString operator+(const DeprecatedString &qs1, const DeprecatedString &qs2) +{ + return DeprecatedString(qs1) += qs2; +} + +DeprecatedString operator+(const DeprecatedString &qs, const char *chs) +{ + return DeprecatedString(qs) += chs; +} + +DeprecatedString operator+(const DeprecatedString &qs, DeprecatedChar qc) +{ + return DeprecatedString(qs) += qc; +} + +DeprecatedString operator+(const DeprecatedString &qs, char ch) +{ + return DeprecatedString(qs) += ch; +} + +DeprecatedString operator+(const char *chs, const DeprecatedString &qs) +{ + return DeprecatedString(chs) += qs; +} + +DeprecatedString operator+(DeprecatedChar qc, const DeprecatedString &qs) +{ + return DeprecatedString(qc) += qs; +} + +DeprecatedString operator+(char ch, const DeprecatedString &qs) +{ + return DeprecatedString(DeprecatedChar(ch)) += qs; +} + +DeprecatedConstString::DeprecatedConstString(const DeprecatedChar* unicode, unsigned length) : + DeprecatedString(new DeprecatedStringData((DeprecatedChar *)unicode, length, length), true) +{ +} + +DeprecatedConstString::~DeprecatedConstString() +{ + DeprecatedStringData *data = *dataHandle; + if (data->refCount > 1) { + DeprecatedChar *tp; + if (data->_length <= WEBCORE_DS_INTERNAL_BUFFER_UCHARS) { + data->_maxUnicode = WEBCORE_DS_INTERNAL_BUFFER_UCHARS; + tp = (DeprecatedChar *)&data->_internalBuffer[0]; + } else { + data->_maxUnicode = ALLOC_QCHAR_GOOD_SIZE(data->_length); + tp = WEBCORE_ALLOCATE_CHARACTERS(data->_maxUnicode); + } + memcpy(tp, data->_unicode, data->_length * sizeof(DeprecatedChar)); + data->_unicode = tp; + data->_isUnicodeValid = 1; + data->_isAsciiValid = 0; + } else { + data->_unicode = 0; + } +} + +struct HandlePageNode +{ + HandlePageNode *next; + HandlePageNode *previous; + void *nodes; +}; + +struct HandleNode { + union { + struct { + unsigned short next; + unsigned short previous; + } internalNode; + + HandleNode *freeNodes; // Always at block[0] in page. + + HandlePageNode *pageNode; // Always at block[1] in page + + void *handle; + } type; +}; + +#ifndef CHECK_FOR_HANDLE_LEAKS + +static const size_t pageSize = 4096; +static const uintptr_t pageMask = ~(pageSize - 1); +static const size_t nodeBlockSize = pageSize / sizeof(HandleNode); + +static HandleNode *initializeHandleNodeBlock(HandlePageNode *pageNode) +{ + unsigned i; + HandleNode* block; + HandleNode* aNode; + +#if PLATFORM(WIN_OS) + block = (HandleNode*)VirtualAlloc(0, pageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#elif PLATFORM(SYMBIAN) + // symbian::fixme needs to do page aligned allocation as valloc is not supported. + block = NULL; +#else + block = (HandleNode*)valloc(pageSize); +#endif + + for (i = 2; i < nodeBlockSize; i++) { + aNode = &block[i]; + if (i > 2) + aNode->type.internalNode.previous = i-1; + else + aNode->type.internalNode.previous = 0; + if (i != nodeBlockSize - 1) + aNode->type.internalNode.next = i+1; + else + aNode->type.internalNode.next = 0; + } + block[0].type.freeNodes = &block[nodeBlockSize - 1]; + block[1].type.pageNode = pageNode; + + return block; +} + +static HandlePageNode *allocatePageNode() +{ + HandlePageNode *node = (HandlePageNode *)fastMalloc(sizeof(HandlePageNode)); + node->next = node->previous = 0; + node->nodes = initializeHandleNodeBlock(node); + return node; +} + +static HandleNode *allocateNode(HandlePageNode *pageNode) +{ + HandleNode *block = (HandleNode *)pageNode->nodes; + HandleNode *freeNodes = block[0].type.freeNodes; + HandleNode *allocated; + + // Check to see if we're out of nodes. + if (freeNodes == 0) { + FATAL("out of nodes"); + return 0; + } + + // Remove node from end of free list + allocated = freeNodes; + if (allocated->type.internalNode.previous >= 2) { + block[0].type.freeNodes = block + allocated->type.internalNode.previous; + block[0].type.freeNodes->type.internalNode.next = 0; + } + else { + // Used last node on this page. + block[0].type.freeNodes = 0; + + freeNodeAllocationPages = freeNodeAllocationPages->previous; + if (freeNodeAllocationPages) + freeNodeAllocationPages->next = 0; + + pageNode->previous = usedNodeAllocationPages; + pageNode->next = 0; + if (usedNodeAllocationPages) + usedNodeAllocationPages->next = pageNode; + usedNodeAllocationPages = pageNode; + } + + return allocated; +} + +#endif + +void freeHandle(DeprecatedStringData **_free) +{ +#ifdef CHECK_FOR_HANDLE_LEAKS + fastFree(_free); + return; +#else + + HandleNode *free = (HandleNode *)_free; + HandleNode *base = (HandleNode *)((uintptr_t)free & pageMask); + HandleNode *freeNodes = base[0].type.freeNodes; + HandlePageNode *pageNode = base[1].type.pageNode; + + if (freeNodes == 0){ + free->type.internalNode.previous = 0; + } + else { + // Insert at head of free list. + free->type.internalNode.previous = freeNodes - base; + freeNodes->type.internalNode.next = free - base; + } + free->type.internalNode.next = 0; + base[0].type.freeNodes = free; + + // Remove page from used/free list and place on free list + if (freeNodeAllocationPages != pageNode) { + if (pageNode->previous) + pageNode->previous->next = pageNode->next; + if (pageNode->next) + pageNode->next->previous = pageNode->previous; + if (usedNodeAllocationPages == pageNode) + usedNodeAllocationPages = pageNode->previous; + + pageNode->previous = freeNodeAllocationPages; + pageNode->next = 0; + if (freeNodeAllocationPages) + freeNodeAllocationPages->next = pageNode; + freeNodeAllocationPages = pageNode; + } +#endif +} + +DeprecatedString DeprecatedString::fromUtf8(const char *chs) +{ + return UTF8Encoding().decode(chs, strlen(chs)).deprecatedString(); +} + +DeprecatedString DeprecatedString::fromUtf8(const char *chs, int len) +{ + return UTF8Encoding().decode(chs, len).deprecatedString(); +} + +DeprecatedCString DeprecatedString::utf8(int& length) const +{ + DeprecatedCString result = UTF8Encoding().encode((::UChar*)unicode(), this->length()).deprecatedCString(); + length = result.length(); + return result; +} + +DeprecatedString::DeprecatedString(const Identifier& str) +{ + if (str.isNull()) { + internalData.deref(); + dataHandle = makeSharedNullHandle(); + dataHandle[0]->ref(); + } else { + dataHandle = allocateHandle(); + *dataHandle = &internalData; + internalData.initialize(reinterpret_cast<const DeprecatedChar*>(str.data()), str.size()); + } +} + +DeprecatedString::DeprecatedString(const UString& str) +{ + if (str.isNull()) { + internalData.deref(); + dataHandle = makeSharedNullHandle(); + dataHandle[0]->ref(); + } else { + dataHandle = allocateHandle(); + *dataHandle = &internalData; + internalData.initialize(reinterpret_cast<const DeprecatedChar*>(str.data()), str.size()); + } +} + +#if PLATFORM(QT) +DeprecatedString::DeprecatedString(const QString& str) +{ + if (str.isNull()) { + internalData.deref(); + dataHandle = makeSharedNullHandle(); + dataHandle[0]->ref(); + } else { + dataHandle = allocateHandle(); + *dataHandle = &internalData; + internalData.initialize(reinterpret_cast<const DeprecatedChar*>(str.data()), str.length()); + } +} +#endif + +DeprecatedString::operator Identifier() const +{ + if (isNull()) + return Identifier(); + return Identifier(reinterpret_cast<const KJS::UChar*>(unicode()), length()); +} + +DeprecatedString::operator UString() const +{ + if (isNull()) + return UString(); + return UString(reinterpret_cast<const KJS::UChar*>(unicode()), length()); +} + +bool equalIgnoringCase(const DeprecatedString& a, const DeprecatedString& b) +{ + unsigned len = a.length(); + if (len != b.length()) + return false; + + DeprecatedStringData* dataA = a.dataHandle[0]; + DeprecatedStringData* dataB = b.dataHandle[0]; + + if (dataA->_isAsciiValid != dataB->_isAsciiValid) + return false; + + if (dataA->_isAsciiValid && dataB->_isAsciiValid) + return strncasecmp(dataA->_ascii, dataB->_ascii, len) == 0; + + ASSERT(dataA->_isUnicodeValid); + ASSERT(dataB->_isUnicodeValid); + return equalCaseInsensitive(dataA->_unicode, dataB->_unicode, len); +} + +} // namespace WebCore diff --git a/WebCore/platform/DeprecatedString.h b/WebCore/platform/DeprecatedString.h new file mode 100644 index 0000000..c413134 --- /dev/null +++ b/WebCore/platform/DeprecatedString.h @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 DeprecatedString_h +#define DeprecatedString_h + +#include "DeprecatedCString.h" +#include <wtf/ASCIICType.h> +#include <wtf/unicode/Unicode.h> + +/* On some ARM platforms GCC won't pack structures by default so sizeof(DeprecatedChar) + will end up being != 2 which causes crashes since the code depends on that. */ +#if COMPILER(GCC) && PLATFORM(FORCE_PACK) +#define PACK_STRUCT __attribute__((packed)) +#else +#define PACK_STRUCT +#endif + +#if PLATFORM(CF) +typedef const struct __CFString * CFStringRef; +#endif + +#if PLATFORM(MAC) +#ifdef __OBJC__ +@class NSString; +#else +class NSString; +#endif +#endif + +#if PLATFORM(QT) +class QString; +#endif + +#if PLATFORM(WX) +class wxString; +#endif + +namespace KJS { + class Identifier; + class UString; +} + +namespace WebCore { + +class RegularExpression; + +class DeprecatedChar { +public: + DeprecatedChar(); + DeprecatedChar(char); + DeprecatedChar(unsigned char); + DeprecatedChar(short); + DeprecatedChar(unsigned short); + DeprecatedChar(int); + DeprecatedChar(unsigned); + + unsigned short unicode() const; + char latin1() const; + bool isSpace() const; + DeprecatedChar lower() const; + DeprecatedChar upper() const; + +private: + unsigned short c; +} PACK_STRUCT; + +inline DeprecatedChar::DeprecatedChar() : c(0) +{ +} + +inline DeprecatedChar::DeprecatedChar(char ch) : c((unsigned char) ch) +{ +} + +inline DeprecatedChar::DeprecatedChar(unsigned char uch) : c(uch) +{ +} + +inline DeprecatedChar::DeprecatedChar(short n) : c(n) +{ +} + +inline DeprecatedChar::DeprecatedChar(unsigned short n) : c(n) +{ +} + +inline DeprecatedChar::DeprecatedChar(unsigned n) : c(n) +{ +} + +inline DeprecatedChar::DeprecatedChar(int n) : c(n) +{ +} + +inline unsigned short DeprecatedChar::unicode() const +{ + return c; +} + +inline bool DeprecatedChar::isSpace() const +{ +#if USE(ICU_UNICODE) + // Use isspace() for basic Latin-1. + // This will include newlines, which aren't included in unicode DirWS. + return c <= 0x7F ? WTF::isASCIISpace(c) : (u_charDirection(c) == U_WHITE_SPACE_NEUTRAL); +#elif USE(QT4_UNICODE) + return QChar(c).isSpace(); +#endif +} + +inline DeprecatedChar DeprecatedChar::lower() const +{ +#if USE(ICU_UNICODE) + // FIXME: If fast enough, we should just call u_tolower directly. + return c <= 0x7F ? WTF::toASCIILower(c) : u_tolower(c); +#elif USE(QT4_UNICODE) + return QChar(c).toLower().unicode(); +#endif +} + +inline DeprecatedChar DeprecatedChar::upper() const +{ +#if USE(ICU_UNICODE) + // FIXME: If fast enough, we should just call u_toupper directly. + return c <= 0x7F ? WTF::toASCIIUpper(c) : u_toupper(c); +#elif USE(QT4_UNICODE) + return QChar(c).toUpper().unicode(); +#endif +} + +inline char DeprecatedChar::latin1() const +{ + return c > 0xff ? 0 : c; +} + +inline bool operator==(DeprecatedChar qc1, DeprecatedChar qc2) +{ + return qc1.unicode() == qc2.unicode(); +} + +inline bool operator==(DeprecatedChar qc, char ch) +{ + return qc.unicode() == (unsigned char) ch; +} + +inline bool operator==(char ch, DeprecatedChar qc) +{ + return (unsigned char) ch == qc.unicode(); +} + +inline bool operator!=(DeprecatedChar qc1, DeprecatedChar qc2) +{ + return qc1.unicode() != qc2.unicode(); +} + +inline bool operator!=(DeprecatedChar qc, char ch) +{ + return qc.unicode() != (unsigned char) ch; +} + +inline bool operator!=(char ch, DeprecatedChar qc) +{ + return (unsigned char) ch != qc.unicode(); +} + +// Keep this struct to <= 46 bytes, that's what the system will allocate. +// Will be rounded up to a multiple of 4, so we're stuck at 44. + +#define WEBCORE_DS_INTERNAL_BUFFER_SIZE 20 +#define WEBCORE_DS_INTERNAL_BUFFER_CHARS WEBCORE_DS_INTERNAL_BUFFER_SIZE-1 +#define WEBCORE_DS_INTERNAL_BUFFER_UCHARS WEBCORE_DS_INTERNAL_BUFFER_SIZE/2 + +struct DeprecatedStringData +{ + // Uses shared null data. + DeprecatedStringData(); + void initialize(); + + // No copy. + DeprecatedStringData(DeprecatedChar *u, unsigned l, unsigned m); + void initialize(DeprecatedChar *u, unsigned l, unsigned m); + + // Copy bytes. + DeprecatedStringData(const DeprecatedChar *u, unsigned l); + void initialize(const DeprecatedChar *u, unsigned l); + + // Copy bytes. + DeprecatedStringData(const char *u, unsigned l); + void initialize(const char *u, unsigned l); + + // Move from destination to source. + static DeprecatedStringData* createAndAdopt(DeprecatedStringData &); + + ~DeprecatedStringData(); + +#ifdef WEBCORE_DS_DEBUG_ALLOCATIONS + void* operator new(size_t s); + void operator delete(void*p); +#endif + + inline void ref() { refCount++; } + inline void deref() { if (--refCount == 0 && _isHeapAllocated) delete this; } + + char *ascii(); + char *makeAscii(); + void increaseAsciiSize(unsigned size); + + DeprecatedChar *unicode(); + DeprecatedChar *makeUnicode(); + void increaseUnicodeSize(unsigned size); + + bool isUnicodeInternal() const { return (char *)_unicode == _internalBuffer; } + bool isAsciiInternal() const { return _ascii == _internalBuffer; } + + unsigned refCount; + unsigned _length; + mutable DeprecatedChar *_unicode; + mutable char *_ascii; + + unsigned _maxUnicode : 30; + bool _isUnicodeValid : 1; + bool _isHeapAllocated : 1; // Fragile, but the only way we can be sure the instance was created with 'new'. + unsigned _maxAscii : 31; + bool _isAsciiValid : 1; + + // _internalBuffer must be at the end - otherwise it breaks on archs that + // don't pack structs on byte boundary, like some versions of gcc on ARM + char _internalBuffer[WEBCORE_DS_INTERNAL_BUFFER_SIZE]; // Pad out to a (((size + 1) & ~15) + 14) size + +private: + void adopt(DeprecatedStringData&); + + DeprecatedStringData(const DeprecatedStringData &); + DeprecatedStringData &operator=(const DeprecatedStringData &); +}; + +class DeprecatedString; + +bool operator==(const DeprecatedString&, const DeprecatedString&); +bool operator==(const DeprecatedString&, const char*); + +class DeprecatedString { +public: + static const char * const null; + + DeprecatedString(); + DeprecatedString(DeprecatedChar); + DeprecatedString(const DeprecatedChar *, unsigned); + DeprecatedString(const char *); + DeprecatedString(const char *, int len); + DeprecatedString(const KJS::Identifier&); + DeprecatedString(const KJS::UString&); + + DeprecatedString(const DeprecatedString &); + DeprecatedString &operator=(const DeprecatedString &); + + ~DeprecatedString(); + + operator KJS::Identifier() const; + operator KJS::UString() const; + +#if PLATFORM(QT) + DeprecatedString(const QString&); + operator QString() const; +#endif + + static DeprecatedString fromLatin1(const char *); + static DeprecatedString fromLatin1(const char *, int len); + static DeprecatedString fromUtf8(const char *); + static DeprecatedString fromUtf8(const char *, int len); +#if PLATFORM(CF) + static DeprecatedString fromCFString(CFStringRef); +#endif +#if PLATFORM(MAC) + static DeprecatedString fromNSString(NSString*); +#endif +#if PLATFORM(SYMBIAN) + static DeprecatedString fromDes(const TDesC&); + static DeprecatedString fromDes(const TDesC8&); +#endif + DeprecatedString &operator=(char); + DeprecatedString &operator=(DeprecatedChar); + DeprecatedString &operator=(const char *); + DeprecatedString &operator=(const DeprecatedCString &); + + unsigned length() const; + + const DeprecatedChar *unicode() const; + const DeprecatedChar *stableUnicode(); + const char *latin1() const; + const char *ascii() const; + bool isAllASCII() const; + bool isAllLatin1() const; + bool hasFastLatin1() const; + void copyLatin1(char *buffer, unsigned position = 0, unsigned length = 0xffffffff) const; + DeprecatedCString utf8() const { int length; return utf8(length); } + DeprecatedCString utf8(int &length) const; + + bool isNull() const; + bool isEmpty() const; + + DeprecatedChar at(unsigned) const; + + int compare(const DeprecatedString &) const; + int compare(const char *) const; + + bool startsWith(const DeprecatedString &) const; + bool startsWith(const char *) const; + bool startsWith(const char *, bool caseSensitive) const; + + int find(char, int index = 0) const; + int find(DeprecatedChar, int index = 0) const; + int find(const char *, int index = 0, bool cs = true) const; + int find(const DeprecatedString &, int index = 0, bool cs = true) const; + int find(const RegularExpression &, int index = 0) const; + + int findRev(char, int index = -1) const; + int findRev(const DeprecatedString& str, int index, bool cs = true) const; + int findRev(const char *, int index = -1) const; + + int contains(char) const; + int contains(const char *, bool cs = true) const; + int contains(const DeprecatedString &, bool cs = true) const; + int contains(DeprecatedChar c, bool cs = true) const; + + bool endsWith(const DeprecatedString &) const; + + short toShort(bool *ok = 0, int base = 10) const; + unsigned short toUShort(bool *ok = 0, int base = 10) const; + int toInt(bool *ok = 0, int base = 10) const; + int64_t toInt64(bool *ok = 0, int base = 10) const; + unsigned toUInt(bool *ok = 0, int base = 10) const; + uint64_t toUInt64(bool *ok = 0, int base = 10) const; + + double toDouble(bool *ok = 0) const; + float toFloat(bool* ok = 0) const; + + static DeprecatedString number(int); + static DeprecatedString number(unsigned); + static DeprecatedString number(long); + static DeprecatedString number(unsigned long); + static DeprecatedString number(double); + + DeprecatedString left(unsigned) const; + DeprecatedString right(unsigned) const; + DeprecatedString mid(unsigned, unsigned len=0xffffffff) const; + + DeprecatedString copy() const; + + DeprecatedString lower() const; + DeprecatedString stripWhiteSpace() const; + DeprecatedString simplifyWhiteSpace() const; + + DeprecatedString &setUnicode(const DeprecatedChar *, unsigned); + DeprecatedString &setLatin1(const char *, int len=-1); + + DeprecatedString &setNum(short); + DeprecatedString &setNum(unsigned short); + DeprecatedString &setNum(int); + DeprecatedString &setNum(unsigned); + DeprecatedString &setNum(long); + DeprecatedString &setNum(unsigned long); + DeprecatedString &setNum(double); + + DeprecatedString& format(const char *, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + + DeprecatedString &append(const DeprecatedString &); + DeprecatedString &append(DeprecatedChar); + DeprecatedString &append(char); + DeprecatedString &insert(unsigned, const DeprecatedString &); + DeprecatedString &insert(unsigned, DeprecatedChar); + DeprecatedString &insert(unsigned, char); + DeprecatedString &insert(unsigned index, const char *insertChars, unsigned insertLength); + DeprecatedString &prepend(const DeprecatedString &); + DeprecatedString &remove(unsigned, unsigned); + DeprecatedString &remove(const DeprecatedChar &c) { return replace(DeprecatedString(c), ""); } + DeprecatedString &remove(const DeprecatedString &s) { return replace(s, ""); } + DeprecatedString &replace(unsigned index, unsigned len, const DeprecatedString &s); + DeprecatedString &replace(char, const DeprecatedString &); + DeprecatedString &replace(DeprecatedChar, const DeprecatedString &); + DeprecatedString &replace(const DeprecatedString &, const DeprecatedString &); + DeprecatedString &replace(const RegularExpression &, const DeprecatedString &); + DeprecatedString &replace(DeprecatedChar, DeprecatedChar); + + DeprecatedString &append(const DeprecatedChar *, unsigned length); + DeprecatedString &append(const char *, unsigned length); + DeprecatedString &insert(unsigned position, const DeprecatedChar *, unsigned length); + DeprecatedString &prepend(const DeprecatedChar *, unsigned length); + + void fill(DeprecatedChar, int len=-1); + void truncate(unsigned); + + void reserve(unsigned); + + bool operator!() const; + + const DeprecatedChar operator[](int) const; + + DeprecatedString &operator+=(const DeprecatedString &s) { return append(s); } + DeprecatedString &operator+=(DeprecatedChar c) { return append(c); } + DeprecatedString &operator+=(char c) { return append(c); } + +#if PLATFORM(CF) + CFStringRef getCFString() const; + void setBufferFromCFString(CFStringRef); +#endif + +#if PLATFORM(MAC) + NSString *getNSString() const; + +#ifdef __OBJC__ + operator NSString*() const { return getNSString(); } +#endif + +#endif + +#if PLATFORM(WX) + operator wxString() const; +#endif + +#if PLATFORM(SYMBIAN) + TPtrC des() const; + TPtrC8 des8() const; + void setBufferFromDes(const TDesC&); + void setBufferFromDes(const TDesC8&); +#endif + +private: + // Used by DeprecatedConstString. + DeprecatedString(DeprecatedStringData *constData, bool /*dummy*/); + void detach(); + void detachAndDiscardCharacters(); + void detachIfInternal(); + void detachInternal(); + void deref(); + DeprecatedChar *forceUnicode(); + void setLength(unsigned); + + DeprecatedStringData **dataHandle; + DeprecatedStringData internalData; + + static DeprecatedStringData *shared_null; + static DeprecatedStringData *makeSharedNull(); + static DeprecatedStringData **shared_null_handle; + static DeprecatedStringData **makeSharedNullHandle(); + + friend bool operator==(const DeprecatedString &, const DeprecatedString &); + friend bool operator==(const DeprecatedString &, const char *); + friend bool equalIgnoringCase(const DeprecatedString&, const DeprecatedString&); + + friend class DeprecatedConstString; + friend class QGDict; + friend struct DeprecatedStringData; +}; + +DeprecatedString operator+(const DeprecatedString &, const DeprecatedString &); +DeprecatedString operator+(const DeprecatedString &, const char *); +DeprecatedString operator+(const DeprecatedString &, DeprecatedChar); +DeprecatedString operator+(const DeprecatedString &, char); +DeprecatedString operator+(const char *, const DeprecatedString &); +DeprecatedString operator+(DeprecatedChar, const DeprecatedString &); +DeprecatedString operator+(char, const DeprecatedString &); + +bool equalIgnoringCase(const DeprecatedString&, const DeprecatedString&); +inline bool equalIgnoringCase(const DeprecatedString& a, const char* b) { return equalIgnoringCase(a, DeprecatedString(b)); } +inline bool equalIgnoringCase(const char* a, const DeprecatedString& b) { return equalIgnoringCase(DeprecatedString(a), b); } + +inline char *DeprecatedStringData::ascii() +{ + return _isAsciiValid ? _ascii : makeAscii(); +} + +inline DeprecatedChar *DeprecatedStringData::unicode() +{ + return _isUnicodeValid ? _unicode : makeUnicode(); +} + +inline unsigned DeprecatedString::length() const +{ + return dataHandle[0]->_length; +} + +inline bool DeprecatedString::isEmpty() const +{ + return dataHandle[0]->_length == 0; +} + +inline const char *DeprecatedString::latin1() const +{ + return dataHandle[0]->ascii(); +} + +inline const DeprecatedChar *DeprecatedString::unicode() const +{ + return dataHandle[0]->unicode(); +} + +#if PLATFORM(MAC) +#if PLATFORM(CF) +inline CFStringRef DeprecatedString::getCFString() const +{ + return (CFStringRef)getNSString(); +} +#endif +#endif + +inline DeprecatedString DeprecatedString::fromLatin1(const char *chs) +{ + return chs; +} + +inline DeprecatedString DeprecatedString::fromLatin1(const char *chs, int length) +{ + return DeprecatedString(chs, length); +} + +inline const char *DeprecatedString::ascii() const +{ + return latin1(); +} + +inline bool DeprecatedString::operator!() const +{ + return isNull(); +} + +inline const DeprecatedChar DeprecatedString::operator[](int index) const +{ + return at(index); +} + +inline bool operator==(const char *chs, const DeprecatedString &qs) +{ + return qs == chs; +} + +inline bool operator!=(const DeprecatedString &qs1, const DeprecatedString &qs2) +{ + return !(qs1 == qs2); +} + +inline bool operator!=(const DeprecatedString &qs, const char *chs) +{ + return !(qs == chs); +} + +inline bool operator!=(const char *chs, const DeprecatedString &qs) +{ + return !(qs == chs); +} + +inline bool operator<(const DeprecatedString &qs1, const DeprecatedString &qs2) +{ + return qs1.compare(qs2) < 0; +} + +inline bool operator<(const DeprecatedString &qs, const char *chs) +{ + return qs.compare(chs) < 0; +} + +inline bool operator<(const char *chs, const DeprecatedString &qs) +{ + return qs.compare(chs) > 0; +} + +inline bool operator<=(const DeprecatedString &qs1, const DeprecatedString &qs2) +{ + return qs1.compare(qs2) <= 0; +} + +inline bool operator<=(const DeprecatedString &qs, const char *chs) +{ + return qs.compare(chs) <= 0; +} + +inline bool operator<=(const char *chs, const DeprecatedString &qs) +{ + return qs.compare(chs) >= 0; +} + +inline bool operator>(const DeprecatedString &qs1, const DeprecatedString &qs2) +{ + return qs1.compare(qs2) > 0; +} + +inline bool operator>(const DeprecatedString &qs, const char *chs) +{ + return qs.compare(chs) > 0; +} + +inline bool operator>(const char *chs, const DeprecatedString &qs) +{ + return qs.compare(chs) < 0; +} + +inline bool operator>=(const DeprecatedString &qs1, const DeprecatedString &qs2) +{ + return qs1.compare(qs2) >= 0; +} + +inline bool operator>=(const DeprecatedString &qs, const char *chs) +{ + return qs.compare(chs) >= 0; +} + +inline bool operator>=(const char *chs, const DeprecatedString &qs) +{ + return qs.compare(chs) <= 0; +} + +class DeprecatedConstString : private DeprecatedString { +public: + DeprecatedConstString(const DeprecatedChar *, unsigned); + ~DeprecatedConstString(); + const DeprecatedString &string() const { return *this; } +}; + +} + +#endif diff --git a/WebCore/platform/wx/wxcode/gtk/non-kerned-drawing.cpp b/WebCore/platform/DeprecatedStringList.cpp index b86a9bc..ee82221 100644 --- a/WebCore/platform/wx/wxcode/gtk/non-kerned-drawing.cpp +++ b/WebCore/platform/DeprecatedStringList.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Kevin Watters, Kevin Ollivier. All rights reserved. + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,48 +20,55 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" -#include "GlyphBuffer.h" -#include "GraphicsContext.h" -#include "SimpleFontData.h" - -#include <wx/defs.h> -#include <wx/dcclient.h> -#include <wx/gdicmn.h> -#include <vector> +#include "DeprecatedStringList.h" namespace WebCore { -void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) +DeprecatedStringList DeprecatedStringList::split(const DeprecatedString &separator, const DeprecatedString &s, bool allowEmptyEntries) { -#if USE(WXGC) - wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); -#else - wxDC* dc = graphicsContext->platformContext(); -#endif - - wxFont wxfont = font->getWxFont(); - if (wxfont.IsOk()) - dc->SetFont(wxfont); - dc->SetTextForeground(color); + DeprecatedStringList result; - // convert glyphs to wxString - GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); - int offset = point.x(); - wxString text = wxEmptyString; - for (unsigned i = 0; i < numGlyphs; i++) { - text = text.Append((wxChar)glyphs[i]); - offset += glyphBuffer.advanceAt(from + i); + int startPos = 0; + int endPos; + while ((endPos = s.find(separator, startPos)) != -1) { + if (allowEmptyEntries || startPos != endPos) + result.append(s.mid(startPos, endPos - startPos)); + startPos = endPos + separator.length(); } + if (allowEmptyEntries || startPos != (int)s.length()) + result.append(s.mid(startPos)); + + return result; +} + +DeprecatedStringList DeprecatedStringList::split(const DeprecatedChar &separator, const DeprecatedString &s, bool allowEmptyEntries) +{ + return DeprecatedStringList::split(DeprecatedString(separator), s, allowEmptyEntries); +} + +DeprecatedString DeprecatedStringList::join(const DeprecatedString &separator) const +{ + DeprecatedString result; - // the y point is actually the bottom point of the text, turn it into the top - float height = font->ascent() - font->descent(); - wxCoord ypoint = (wxCoord) (point.y() - height); - - dc->DrawText(text, (wxCoord)point.x(), ypoint); + for (ConstIterator i = begin(), j = ++begin(); i != end(); ++i, ++j) { + result += *i; + if (j != end()) { + result += separator; + } + } + + return result; +} + +DeprecatedString DeprecatedStringList::pop_front() +{ + DeprecatedString front = first(); + remove(begin()); + return front; } } diff --git a/WebCore/platform/network/qt/AuthenticationChallenge.h b/WebCore/platform/DeprecatedStringList.h index 753ac6f..aa735bf 100644 --- a/WebCore/platform/network/qt/AuthenticationChallenge.h +++ b/WebCore/platform/DeprecatedStringList.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,23 +22,31 @@ * (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 AuthenticationChallenge_h -#define AuthenticationChallenge_h -#include "AuthenticationChallengeBase.h" +#ifndef DeprecatedStringList_h +#define DeprecatedStringList_h + +#include "DeprecatedString.h" +#include "DeprecatedValueList.h" + +#ifdef __OBJC__ +@class NSArray; +#endif namespace WebCore { -class AuthenticationChallenge : public AuthenticationChallengeBase { +class DeprecatedStringList : public DeprecatedValueList<DeprecatedString> { public: - AuthenticationChallenge() - { - } - - AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error) - : AuthenticationChallengeBase(protectionSpace, proposedCredential, previousFailureCount, response, error) - { - } + static DeprecatedStringList split(const DeprecatedString &, const DeprecatedString &, bool allowEmptyEntries = false); + static DeprecatedStringList split(const DeprecatedChar &, const DeprecatedString &, bool allowEmptyEntries = false); + + DeprecatedString join(const DeprecatedString &) const; + + DeprecatedString pop_front(); + +#ifdef __OBJC__ + NSArray *getNSArray() const; +#endif }; } diff --git a/WebCore/platform/DeprecatedValueListImpl.cpp b/WebCore/platform/DeprecatedValueListImpl.cpp index 5bbb697..10a4200 100644 --- a/WebCore/platform/DeprecatedValueListImpl.cpp +++ b/WebCore/platform/DeprecatedValueListImpl.cpp @@ -51,18 +51,17 @@ public: }; inline DeprecatedValueListImpl::Private::Private(void (*deleteFunc)(DeprecatedValueListImplNode*), - DeprecatedValueListImplNode* (*copyFunc)(DeprecatedValueListImplNode*)) - : RefCounted<Private>(0) - , head(NULL) - , tail(NULL) - , deleteNode(deleteFunc) - , copyNode(copyFunc) - , count(0) + DeprecatedValueListImplNode* (*copyFunc)(DeprecatedValueListImplNode*)) : + head(NULL), + tail(NULL), + deleteNode(deleteFunc), + copyNode(copyFunc), + count(0) { } inline DeprecatedValueListImpl::Private::Private(const Private &other) - : RefCounted<Private>(0) + : RefCounted<DeprecatedValueListImpl::Private>() , deleteNode(other.deleteNode) , copyNode(other.copyNode) , count(other.count) diff --git a/WebCore/platform/DragData.h b/WebCore/platform/DragData.h index c4b9337..d139352 100644 --- a/WebCore/platform/DragData.h +++ b/WebCore/platform/DragData.h @@ -52,6 +52,8 @@ typedef class wxDataObject* DragDataRef; #elif PLATFORM(GTK) // FIXME: this should probably be something gdk-specific typedef void* DragDataRef; +#elif defined ANDROID +typedef void* DragDataRef; #endif diff --git a/WebCore/platform/DragImage.h b/WebCore/platform/DragImage.h index 4c0d257..f3b06c6 100644 --- a/WebCore/platform/DragImage.h +++ b/WebCore/platform/DragImage.h @@ -66,6 +66,8 @@ namespace WebCore { typedef wxDragImage* DragImageRef; #elif PLATFORM(GTK) typedef void* DragImageRef; +#elif defined ANDROID + typedef void* DragImageRef; #endif IntSize dragImageSize(DragImageRef); diff --git a/WebCore/platform/FileSystem.h b/WebCore/platform/FileSystem.h index d2a3011..da31281 100644 --- a/WebCore/platform/FileSystem.h +++ b/WebCore/platform/FileSystem.h @@ -30,12 +30,6 @@ #ifndef FileSystem_h #define FileSystem_h -#if PLATFORM(GTK) -#include <gmodule.h> -#endif - -#include <time.h> - #include <wtf/Platform.h> typedef const struct __CFData* CFDataRef; @@ -47,38 +41,10 @@ class String; #if PLATFORM(WIN) typedef HANDLE PlatformFileHandle; -typedef FILETIME PlatformFileTime; -typedef HMODULE PlatformModule; const PlatformFileHandle invalidPlatformFileHandle = INVALID_HANDLE_VALUE; - -struct PlatformModuleVersion { - unsigned leastSig; - unsigned mostSig; - - PlatformModuleVersion(unsigned) - : leastSig(0) - , mostSig(0) - { - } - - PlatformModuleVersion(unsigned lsb, unsigned msb) - : leastSig(lsb) - , mostSig(msb) - { - } - -}; #else typedef int PlatformFileHandle; -typedef time_t PlatformFileTime; -#if PLATFORM(GTK) -typedef GModule* PlatformModule; -#else -typedef void* PlatformModule; -#endif const PlatformFileHandle invalidPlatformFileHandle = -1; - -typedef unsigned PlatformModuleVersion; #endif bool fileExists(const String&); @@ -89,7 +55,6 @@ bool getFileModificationTime(const String&, time_t& result); String pathByAppendingComponent(const String& path, const String& component); bool makeAllDirectories(const String& path); String homeDirectoryPath(); -String pathGetFileName(const String&); CString fileSystemRepresentation(const String&); @@ -100,9 +65,6 @@ CString openTemporaryFile(const char* prefix, PlatformFileHandle&); void closeFile(PlatformFileHandle&); int writeToFile(PlatformFileHandle, const char* data, int length); -// Methods for dealing with loadable modules -bool unloadModule(PlatformModule); - #if PLATFORM(WIN) String localUserSpecificStorageDirectory(); String roamingUserSpecificStorageDirectory(); diff --git a/WebCore/platform/KURL.cpp b/WebCore/platform/KURL.cpp index b858108..45922e7 100644 --- a/WebCore/platform/KURL.cpp +++ b/WebCore/platform/KURL.cpp @@ -28,28 +28,20 @@ #include "CString.h" #include "PlatformString.h" +#include "RegularExpression.h" #include "TextEncoding.h" - +#include <wtf/Vector.h> #if USE(ICU_UNICODE) #include <unicode/uidna.h> #elif USE(QT4_UNICODE) #include <QUrl> #endif -#include <stdio.h> - using namespace std; using namespace WTF; namespace WebCore { -typedef Vector<char, 512> CharBuffer; -typedef Vector<UChar, 512> UCharBuffer; - -// FIXME: This file makes too much use of the + operator on String. -// We either have to optimize that operator so it doesn't involve -// so many allocations, or change this to use Vector<UChar> instead. - enum URLCharacterClasses { // alpha SchemeFirstChar = 1 << 0, @@ -74,52 +66,56 @@ enum URLCharacterClasses { // "#" | "?" | "/" | nul PathSegmentEndChar = 1 << 5, + // digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f" + HexDigitChar = 1 << 6, + // not allowed in path - BadChar = 1 << 6 + BadChar = 1 << 7 + }; static const char hexDigits[17] = "0123456789ABCDEF"; static const unsigned char characterClassTable[256] = { /* 0 nul */ PathSegmentEndChar, /* 1 soh */ BadChar, - /* 2 stx */ BadChar, /* 3 etx */ BadChar, + /* 2 stx */ BadChar, /* 3 etx */ BadChar, /* 4 eot */ BadChar, /* 5 enq */ BadChar, /* 6 ack */ BadChar, /* 7 bel */ BadChar, - /* 8 bs */ BadChar, /* 9 ht */ BadChar, /* 10 nl */ BadChar, /* 11 vt */ BadChar, + /* 8 bs */ BadChar, /* 9 ht */ BadChar, /* 10 nl */ BadChar, /* 11 vt */ BadChar, /* 12 np */ BadChar, /* 13 cr */ BadChar, /* 14 so */ BadChar, /* 15 si */ BadChar, /* 16 dle */ BadChar, /* 17 dc1 */ BadChar, /* 18 dc2 */ BadChar, /* 19 dc3 */ BadChar, /* 20 dc4 */ BadChar, /* 21 nak */ BadChar, /* 22 syn */ BadChar, /* 23 etb */ BadChar, /* 24 can */ BadChar, /* 25 em */ BadChar, /* 26 sub */ BadChar, /* 27 esc */ BadChar, /* 28 fs */ BadChar, /* 29 gs */ BadChar, /* 30 rs */ BadChar, /* 31 us */ BadChar, /* 32 sp */ BadChar, /* 33 ! */ UserInfoChar, - /* 34 " */ BadChar, /* 35 # */ PathSegmentEndChar | BadChar, + /* 34 " */ BadChar, /* 35 # */ PathSegmentEndChar | BadChar, /* 36 $ */ UserInfoChar, /* 37 % */ UserInfoChar | HostnameChar | IPv6Char | BadChar, /* 38 & */ UserInfoChar, /* 39 ' */ UserInfoChar, - /* 40 ( */ UserInfoChar, /* 41 ) */ UserInfoChar, + /* 40 ( */ UserInfoChar, /* 41 ) */ UserInfoChar, /* 42 * */ UserInfoChar, /* 43 + */ SchemeChar | UserInfoChar, - /* 44 , */ UserInfoChar, - /* 45 - */ SchemeChar | UserInfoChar | HostnameChar, - /* 46 . */ SchemeChar | UserInfoChar | HostnameChar, + /* 44 , */ UserInfoChar, + /* 45 - */ SchemeChar | UserInfoChar | HostnameChar, + /* 46 . */ SchemeChar | UserInfoChar | HostnameChar, /* 47 / */ PathSegmentEndChar, - /* 48 0 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 49 1 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 50 2 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 51 3 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 52 4 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 53 5 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 54 6 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 55 7 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 56 8 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 57 9 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, + /* 48 0 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 49 1 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 50 2 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 51 3 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 52 4 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 53 5 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 54 6 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 55 7 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 56 8 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 57 9 */ SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, /* 58 : */ UserInfoChar | IPv6Char, /* 59 ; */ UserInfoChar, /* 60 < */ BadChar, /* 61 = */ UserInfoChar, /* 62 > */ BadChar, /* 63 ? */ PathSegmentEndChar | BadChar, /* 64 @ */ 0, - /* 65 A */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 66 B */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 67 C */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 68 D */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 69 E */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 70 F */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, + /* 65 A */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 66 B */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 67 C */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 68 D */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 69 E */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 70 F */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, /* 71 G */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, /* 72 H */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, /* 73 I */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, @@ -145,12 +141,12 @@ static const unsigned char characterClassTable[256] = { /* 94 ^ */ 0, /* 95 _ */ UserInfoChar | HostnameChar, /* 96 ` */ 0, - /* 97 a */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 98 b */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 99 c */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 100 d */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 101 e */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 102 f */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, + /* 97 a */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 98 b */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 99 c */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 100 d */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 101 e */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, + /* 102 f */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | HexDigitChar | IPv6Char, /* 103 g */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, /* 104 h */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, /* 105 i */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, @@ -208,157 +204,137 @@ static const unsigned char characterClassTable[256] = { }; static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int srcEnd); -static void encodeRelativeString(const String& rel, const TextEncoding&, CharBuffer& ouput); -static String substituteBackslashes(const String&); +static char* encodeRelativeString(const KURL &base, const DeprecatedString& rel, const TextEncoding&); +static DeprecatedString substituteBackslashes(const DeprecatedString &string); -static inline bool isSchemeFirstChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & SchemeFirstChar; } -static inline bool isSchemeFirstChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeFirstChar); } -static inline bool isSchemeChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & SchemeChar; } -static inline bool isSchemeChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeChar); } +static inline bool isSchemeFirstChar(unsigned char c) { return characterClassTable[c] & SchemeFirstChar; } +static inline bool isSchemeChar(unsigned char c) { return characterClassTable[c] & SchemeChar; } static inline bool isUserInfoChar(unsigned char c) { return characterClassTable[c] & UserInfoChar; } static inline bool isHostnameChar(unsigned char c) { return characterClassTable[c] & HostnameChar; } static inline bool isIPv6Char(unsigned char c) { return characterClassTable[c] & IPv6Char; } -static inline bool isPathSegmentEndChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & PathSegmentEndChar; } -static inline bool isPathSegmentEndChar(UChar c) { return c <= 0xff && (characterClassTable[c] & PathSegmentEndChar); } +static inline bool isPathSegmentEndChar(unsigned char c) { return characterClassTable[c] & PathSegmentEndChar; } static inline bool isBadChar(unsigned char c) { return characterClassTable[c] & BadChar; } +static inline bool isHexDigit(unsigned char c) { return characterClassTable[c] & HexDigitChar; } -static inline int hexDigitValue(UChar c) +static inline int hexDigitValue(unsigned char c) { - ASSERT(isASCIIHexDigit(c)); + ASSERT(isHexDigit(c)); if (c < 'A') return c - '0'; return (c - 'A' + 10) & 0xF; // handle both upper and lower case without a branch } -// Copies the source to the destination, assuming all the source characters are -// ASCII. The destination buffer must be large enough. Null characters are allowed -// in the source string, and no attempt is made to null-terminate the result. -static void copyASCII(const UChar* src, int length, char* dest) -{ - for (int i = 0; i < length; i++) - dest[i] = static_cast<char>(src[i]); -} - -// FIXME: Move to PlatformString.h eventually. -// Returns the index of the first index in string |s| of any of the characters -// in |toFind|. |toFind| should be a null-terminated string, all characters up -// to the null will be searched. Returns int if not found. -static int findFirstOf(const UChar* s, int sLen, int startPos, const char* toFind) -{ - for (int i = startPos; i < sLen; i++) { - const char* cur = toFind; - while (*cur) { - if (s[i] == *(cur++)) - return i; - } - } - return -1; -} - -// KURL - +#ifdef ANDROID_JAVASCRIPT_SECURITY inline bool KURL::protocolIs(const String& string, const char* protocol) { return WebCore::protocolIs(string, protocol); } +#endif + +// KURL -KURL::KURL() - : m_isValid(false) +KURL::KURL() : m_isValid(false) { } -KURL::KURL(const char* url) +KURL::KURL(const char *url) { - if (!url || url[0] != '/') { + if (url && url[0] == '/') { + // 5 for "file:", 1 for terminator + size_t urlLength = strlen(url) + 1; + Vector<char, 2048> buffer(urlLength + 5); + buffer[0] = 'f'; + buffer[1] = 'i'; + buffer[2] = 'l'; + buffer[3] = 'e'; + buffer[4] = ':'; + memcpy(&buffer[5], url, urlLength); + parse(buffer.data(), 0); + } else parse(url, 0); - return; - } - - size_t urlLength = strlen(url) + 1; - CharBuffer buffer(urlLength + 5); // 5 for "file:". - buffer[0] = 'f'; - buffer[1] = 'i'; - buffer[2] = 'l'; - buffer[3] = 'e'; - buffer[4] = ':'; - memcpy(&buffer[5], url, urlLength); - parse(buffer.data(), 0); } -KURL::KURL(const String& url) +KURL::KURL(const DeprecatedString &url) { - if (url[0] != '/') { - parse(url); - return; - } - - CharBuffer buffer(url.length() + 6); // 5 for "file:", 1 for terminator. - buffer[0] = 'f'; - buffer[1] = 'i'; - buffer[2] = 'l'; - buffer[3] = 'e'; - buffer[4] = ':'; - copyASCII(url.characters(), url.length(), &buffer[5]); - buffer[url.length() + 5] = '\0'; // Need null terminator. - - parse(buffer.data(), 0); + if (!url.isEmpty() && url[0] == '/') { + // 5 for "file:", 1 for terminator + Vector<char, 2048> buffer(url.length() + 6); + buffer[0] = 'f'; + buffer[1] = 'i'; + buffer[2] = 'l'; + buffer[3] = 'e'; + buffer[4] = ':'; + url.copyLatin1(&buffer[5]); + parse(buffer.data(), 0); + } else + parse(url.ascii(), &url); } -KURL::KURL(const KURL& base, const String& relative) +KURL::KURL(const KURL& base, const DeprecatedString& relative) { init(base, relative, UTF8Encoding()); } -KURL::KURL(const KURL& base, const String& relative, const TextEncoding& encoding) +KURL::KURL(const KURL& base, const DeprecatedString& relative, const TextEncoding& encoding) { init(base, relative, encoding); } -void KURL::init(const KURL& base, const String& relative, const TextEncoding& encoding) +void KURL::init(const KURL &base, const DeprecatedString &relative, const TextEncoding& encoding) { // Allow at least absolute URLs to resolve against an empty URL. if (!base.m_isValid && !base.isEmpty()) { - m_string = relative; m_isValid = false; return; } + + bool absolute = false; - // For compatibility with Win IE, treat backslashes as if they were slashes, - // as long as we're not dealing with javascript: or data: URLs. - String rel = relative; - if (rel.contains('\\') && !(protocolIs(rel, "javascript") || protocolIs(rel, "data"))) - rel = substituteBackslashes(rel); - - String* originalString = &rel; - - bool allASCII = charactersAreAllASCII(rel.characters(), rel.length()); - CharBuffer strBuffer; - char* str; - size_t len; + // for compatibility with Win IE, we must treat backslashes as if they were slashes, as long as we're not dealing with the javascript: schema + DeprecatedString substitutedRelative; +#ifdef ANDROID_JAVASCRIPT_SECURITY + bool shouldSubstituteBackslashes = relative.contains('\\') && !(protocolIs(relative, "javascript") || protocolIs(relative, "data")); +#else + bool shouldSubstituteBackslashes = relative.contains('\\') && !(relative.startsWith("javascript:", false) || relative.startsWith("data:", false)); +#endif + if (shouldSubstituteBackslashes) + substitutedRelative = substituteBackslashes(relative); + + const DeprecatedString &rel = shouldSubstituteBackslashes ? substitutedRelative : relative; + + bool allASCII = rel.isAllASCII(); + char *strBuffer; + const char *str; if (allASCII) { - len = rel.length(); - strBuffer.resize(len + 1); - copyASCII(rel.characters(), len, strBuffer.data()); - strBuffer[len] = 0; - str = strBuffer.data(); + strBuffer = 0; + str = rel.ascii(); } else { - originalString = 0; - encodeRelativeString(rel, encoding, strBuffer); - str = strBuffer.data(); - len = strlen(str); + strBuffer = encodeRelativeString(base, rel, encoding); + str = strBuffer; } - - // Get rid of leading whitespace. + + // workaround for sites that put leading whitespace whitespace on + // URL references + bool strippedStart = false; while (*str == ' ') { - originalString = 0; str++; - --len; + strippedStart = true; } - // Get rid of trailing whitespace. - while (len && str[len - 1] == ' ') { - originalString = 0; - str[--len] = '\0'; + // workaround for trailing whitespace - a bit more complicated cause we have to copy + // it would be even better to replace null-termination with a length parameter + int len = strlen(str); + int charsToChopOffEnd = 0; + for (int pos = len - 1; pos >= 0 && str[pos] == ' '; pos--) { + charsToChopOffEnd++; + } + if (charsToChopOffEnd > 0) { + char *newStrBuffer = (char *)fastMalloc((len + 1) - charsToChopOffEnd); + strncpy(newStrBuffer, str, len - charsToChopOffEnd); + newStrBuffer[len - charsToChopOffEnd] = '\0'; + fastFree(strBuffer); + strBuffer = newStrBuffer; + str = strBuffer; } // According to the RFC, the reference should be interpreted as an @@ -366,108 +342,119 @@ void KURL::init(const KURL& base, const String& relative, const TextEncoding& en // algorithm. If the URI reference is absolute it will have a // scheme, meaning that it will have a colon before the first // non-scheme element. - bool absolute = false; - char* p = str; + const char *p = str; if (isSchemeFirstChar(*p)) { ++p; while (isSchemeChar(*p)) { ++p; } if (*p == ':') { - if (p[1] != '/' && equalIgnoringCase(base.protocol(), String(str, p - str)) && base.isHierarchical()) { + if (p[1] != '/' && base.protocol().lower() == DeprecatedString(str, p - str).lower() && base.isHierarchical()) str = p + 1; - originalString = 0; - } else + else absolute = true; } } if (absolute) { - parse(str, originalString); + parse(str, (allASCII && !strippedStart && (charsToChopOffEnd == 0)) ? &rel : 0); } else { // If the base is empty or opaque (e.g. data: or javascript:), then the URL is invalid // unless the relative URL is a single fragment. if (!base.isHierarchical()) { - if (str[0] == '#') - parse(base.m_string.left(base.queryEndPos) + str); - else { - m_string = relative; + if (str[0] == '#') { + DeprecatedString newURL = base.urlString.left(base.queryEndPos) + str; + parse(newURL.ascii(), &newURL); + } else m_isValid = false; - } + + if (strBuffer) + fastFree(strBuffer); return; } - switch (str[0]) { + switch(str[0]) { case '\0': // the reference must be empty - the RFC says this is a // reference to the same document - *this = base; - break; + { + *this = base; + break; + } case '#': // must be fragment-only reference - parse(base.m_string.left(base.queryEndPos) + str); - break; + { + DeprecatedString newURL = base.urlString.left(base.queryEndPos) + str; + parse(newURL.ascii(), &newURL); + break; + } case '?': // query-only reference, special case needed for non-URL results - parse(base.m_string.left(base.pathEndPos) + str); - break; + { + DeprecatedString newURL = base.urlString.left(base.pathEndPos) + str; + parse(newURL.ascii(), &newURL); + break; + } case '/': // must be net-path or absolute-path reference - if (str[1] == '/') { - // net-path - parse(base.m_string.left(base.schemeEndPos + 1) + str); - } else { - // abs-path - parse(base.m_string.left(base.portEndPos) + str); + { + if (str[1] == '/') { + // net-path + DeprecatedString newURL = base.urlString.left(base.schemeEndPos + 1) + str; + parse(newURL.ascii(), &newURL); + } else { + // abs-path + DeprecatedString newURL = base.urlString.left(base.portEndPos) + str; + parse(newURL.ascii(), &newURL); + } + break; } - break; default: { // must be relative-path reference // Base part plus relative part plus one possible slash added in between plus terminating \0 byte. - CharBuffer buffer(base.pathEndPos + 1 + len + 1); - - char* bufferPos = buffer.data(); + Vector<char, 2048> buffer(base.pathEndPos + 1 + strlen(str) + 1); + char *bufferPos = buffer.data(); + // first copy everything before the path from the base - unsigned baseLength = base.m_string.length(); - const UChar* baseCharacters = base.m_string.characters(); - CharBuffer baseStringBuffer(baseLength); - for (unsigned i = 0; i < baseLength; ++i) - baseStringBuffer[i] = static_cast<char>(baseCharacters[i]); - const char* baseString = baseStringBuffer.data(); - const char* baseStringStart = baseString; - const char* pathStart = baseStringStart + base.portEndPos; - while (baseStringStart < pathStart) + const char *baseString = base.urlString.ascii(); + const char *baseStringStart = baseString; + const char *pathStart = baseStringStart + base.portEndPos; + while (baseStringStart < pathStart) { *bufferPos++ = *baseStringStart++; - char* bufferPathStart = bufferPos; - - // now copy the base path - const char* baseStringEnd = baseString + base.pathEndPos; + } + char *bufferPathStart = bufferPos; + // now copy the base path + const char *baseStringEnd = baseString + base.pathEndPos; + // go back to the last slash - while (baseStringEnd > baseStringStart && baseStringEnd[-1] != '/') + while (baseStringEnd > baseStringStart && baseStringEnd[-1] != '/') { baseStringEnd--; - + } + if (baseStringEnd == baseStringStart) { // no path in base, add a path separator if necessary - if (base.schemeEndPos + 1 != base.pathEndPos && *str != '\0' && *str != '?' && *str != '#') + if (base.schemeEndPos + 1 != base.pathEndPos && *str != '\0' && *str != '?' && *str != '#') { *bufferPos++ = '/'; + } } else { bufferPos += copyPathRemovingDots(bufferPos, baseStringStart, 0, baseStringEnd - baseStringStart); } - const char* relStringStart = str; - const char* relStringPos = relStringStart; - + const char *relStringStart = str; + const char *relStringPos = relStringStart; + while (*relStringPos != '\0' && *relStringPos != '?' && *relStringPos != '#') { if (relStringPos[0] == '.' && bufferPos[-1] == '/') { if (isPathSegmentEndChar(relStringPos[1])) { // skip over "." segment relStringPos += 1; - if (relStringPos[0] == '/') + if (relStringPos[0] == '/') { relStringPos++; + } continue; } else if (relStringPos[1] == '.' && isPathSegmentEndChar(relStringPos[2])) { // skip over ".." segment and rewind the last segment @@ -475,16 +462,19 @@ void KURL::init(const KURL& base, const String& relative, const TextEncoding& en // ".." segments - we choose to drop them since some web content // relies on this. relStringPos += 2; - if (relStringPos[0] == '/') + if (relStringPos[0] == '/') { relStringPos++; - if (bufferPos > bufferPathStart + 1) + } + if (bufferPos > bufferPathStart + 1) { bufferPos--; - while (bufferPos > bufferPathStart + 1 && bufferPos[-1] != '/') + } + while (bufferPos > bufferPathStart + 1 && bufferPos[-1] != '/') { bufferPos--; + } continue; } } - + *bufferPos = *relStringPos; relStringPos++; bufferPos++; @@ -495,12 +485,16 @@ void KURL::init(const KURL& base, const String& relative, const TextEncoding& en strcpy(bufferPos, relStringPos); parse(buffer.data(), 0); - + ASSERT(strlen(buffer.data()) + 1 <= buffer.size()); break; } } } + + if (strBuffer) { + fastFree(strBuffer); + } } bool KURL::hasPath() const @@ -508,79 +502,89 @@ bool KURL::hasPath() const return m_isValid && pathEndPos != portEndPos; } -String KURL::lastPathComponent() const +DeprecatedString KURL::lastPathComponent() const { if (!hasPath()) - return String(); + return DeprecatedString(); int end = pathEndPos - 1; - if (m_string[end] == '/') + if (urlString[end] == '/') --end; - int start = m_string.reverseFind('/', end); + int start = urlString.findRev('/', end); if (start < portEndPos) - return String(); + return DeprecatedString(); ++start; - return m_string.substring(start, end - start + 1); + return urlString.mid(start, end - start + 1); } -String KURL::protocol() const +DeprecatedString KURL::protocol() const { - if (!m_isValid) - return String(); + if (!m_isValid) { + return DeprecatedString(); + } - return m_string.left(schemeEndPos); + return urlString.left(schemeEndPos); } -String KURL::host() const +DeprecatedString KURL::host() const { - if (!m_isValid) - return String(); + if (!m_isValid) { + return DeprecatedString(); + } int start = (passwordEndPos == userStartPos) ? passwordEndPos : passwordEndPos + 1; - return decodeURLEscapeSequences(m_string.substring(start, hostEndPos - start)); + return decode_string(urlString.mid(start, hostEndPos - start)); } -unsigned short KURL::port() const +unsigned short int KURL::port() const { - if (!m_isValid) + if (!m_isValid) { return 0; + } - if (hostEndPos == portEndPos) - return 0; + if (hostEndPos != portEndPos) { + bool ok; + unsigned short result = urlString.mid(hostEndPos + 1, portEndPos - hostEndPos - 1).toUShort(&ok); + if (!ok) { + result = 0; + } + return result; + } - int number = m_string.substring(hostEndPos + 1, portEndPos - hostEndPos - 1).toInt(); - if (number < 0 || number > 0xFFFF) - return 0; - return number; + return 0; } -String KURL::pass() const +DeprecatedString KURL::pass() const { - if (!m_isValid) - return String(); + if (!m_isValid) { + return DeprecatedString(); + } - if (passwordEndPos == userEndPos) - return String(); + if (passwordEndPos == userEndPos) { + return DeprecatedString(); + } - return decodeURLEscapeSequences(m_string.substring(userEndPos + 1, passwordEndPos - userEndPos - 1)); + return decode_string(urlString.mid(userEndPos + 1, passwordEndPos - userEndPos - 1)); } -String KURL::user() const +DeprecatedString KURL::user() const { - if (!m_isValid) - return String(); + if (!m_isValid) { + return DeprecatedString(); + } - return decodeURLEscapeSequences(m_string.substring(userStartPos, userEndPos - userStartPos)); + return decode_string(urlString.mid(userStartPos, userEndPos - userStartPos)); } -String KURL::ref() const +DeprecatedString KURL::ref() const { - if (!m_isValid || fragmentEndPos == queryEndPos) - return String(); + if (!m_isValid || fragmentEndPos == queryEndPos) { + return DeprecatedString(); + } - return m_string.substring(queryEndPos + 1, fragmentEndPos - (queryEndPos + 1)); + return urlString.mid(queryEndPos + 1, fragmentEndPos - (queryEndPos + 1)); } bool KURL::hasRef() const @@ -588,6 +592,7 @@ bool KURL::hasRef() const return m_isValid && fragmentEndPos != queryEndPos; } +#ifdef ANDROID_JAVASCRIPT_SECURITY static inline void assertProtocolIsGood(const char* protocol) { #ifndef NDEBUG @@ -606,208 +611,209 @@ bool KURL::protocolIs(const char* protocol) const if (!m_isValid) return false; for (int i = 0; i < schemeEndPos; ++i) { - if (!protocol[i] || toASCIILower(m_string[i]) != protocol[i]) + if (!protocol[i] || toASCIILower(urlString[i].unicode()) != protocol[i]) return false; } return !protocol[schemeEndPos]; // We should have consumed all characters in the argument. } +#endif -String KURL::query() const +DeprecatedString KURL::query() const { - if (!m_isValid) - return String(); + if (!m_isValid) { + return DeprecatedString(); + } - return m_string.substring(pathEndPos, queryEndPos - pathEndPos); + return urlString.mid(pathEndPos, queryEndPos - pathEndPos); } -String KURL::path() const +DeprecatedString KURL::path() const { - if (!m_isValid) - return String(); + if (!m_isValid) { + return DeprecatedString(); + } - return decodeURLEscapeSequences(m_string.substring(portEndPos, pathEndPos - portEndPos)); + return decode_string(urlString.mid(portEndPos, pathEndPos - portEndPos)); } -void KURL::setProtocol(const String& s) +void KURL::setProtocol(const DeprecatedString &s) { if (!m_isValid) { - parse(s + ":" + m_string); + DeprecatedString newURL = s + ":" + urlString; + parse(newURL.ascii(), &newURL); return; } - parse(s + m_string.substring(schemeEndPos)); + DeprecatedString newURL = s + urlString.mid(schemeEndPos); + parse(newURL.ascii(), &newURL); } -void KURL::setHost(const String& s) +void KURL::setHost(const DeprecatedString &s) { - if (!m_isValid) - return; - - bool slashSlashNeeded = userStartPos == schemeEndPos + 1; - int hostStart = (passwordEndPos == userStartPos) ? passwordEndPos : passwordEndPos + 1; - - parse(m_string.left(hostStart) + (slashSlashNeeded ? "//" : "") + s + m_string.substring(hostEndPos)); + if (m_isValid) { + bool slashSlashNeeded = userStartPos == schemeEndPos + 1; + int hostStart = (passwordEndPos == userStartPos) ? passwordEndPos : passwordEndPos + 1; + + DeprecatedString newURL = urlString.left(hostStart) + (slashSlashNeeded ? "//" : DeprecatedString()) + s + urlString.mid(hostEndPos); + parse(newURL.ascii(), &newURL); + } } void KURL::setPort(unsigned short i) { - if (!m_isValid) - return; - - bool colonNeeded = portEndPos == hostEndPos; - int portStart = (colonNeeded ? hostEndPos : hostEndPos + 1); - - parse(m_string.left(portStart) + (colonNeeded ? ":" : "") + String::number(i) + m_string.substring(portEndPos)); + if (m_isValid) { + bool colonNeeded = portEndPos == hostEndPos; + int portStart = (colonNeeded ? hostEndPos : hostEndPos + 1); + DeprecatedString newURL = urlString.left(portStart) + (colonNeeded ? ":" : DeprecatedString()) + DeprecatedString::number(i) + urlString.mid(portEndPos); + parse(newURL.ascii(), &newURL); + } } -void KURL::setHostAndPort(const String& hostAndPort) +void KURL::setHostAndPort(const DeprecatedString& hostAndPort) { - if (!m_isValid) - return; - - bool slashSlashNeeded = userStartPos == schemeEndPos + 1; - int hostStart = (passwordEndPos == userStartPos) ? passwordEndPos : passwordEndPos + 1; - - parse(m_string.left(hostStart) + (slashSlashNeeded ? "//" : "") + hostAndPort + m_string.substring(portEndPos)); + if (m_isValid) { + bool slashSlashNeeded = userStartPos == schemeEndPos + 1; + int hostStart = (passwordEndPos == userStartPos) ? passwordEndPos : passwordEndPos + 1; + + DeprecatedString newURL = urlString.left(hostStart) + (slashSlashNeeded ? "//" : DeprecatedString()) + hostAndPort + urlString.mid(portEndPos); + parse(newURL.ascii(), &newURL); + } } -void KURL::setUser(const String& user) +void KURL::setUser(const DeprecatedString &user) { - if (!m_isValid) - return; - - String u; - int end = userEndPos; - if (!user.isEmpty()) { - u = user; - if (userStartPos == schemeEndPos + 1) - u = "//" + u; - // Add '@' if we didn't have one before. - if (end == hostEndPos || (end == passwordEndPos && m_string[end] != '@')) - u.append('@'); - } else { - // Remove '@' if we now have neither user nor password. - if (userEndPos == passwordEndPos && end != hostEndPos && m_string[end] == '@') - end += 1; + if (m_isValid) { + DeprecatedString u; + int end = userEndPos; + if (!user.isEmpty()) { + u = user; + if (userStartPos == schemeEndPos + 1) + u = "//" + u; + // Add '@' if we didn't have one before. + if (end == hostEndPos || (end == passwordEndPos && urlString[end] != '@')) + u += '@'; + } else { + // Remove '@' if we now have neither user nor password. + if (userEndPos == passwordEndPos && end != hostEndPos && urlString[end] == '@') + end += 1; + } + const DeprecatedString newURL = urlString.left(userStartPos) + u + urlString.mid(end); + parse(newURL.ascii(), &newURL); } - parse(m_string.left(userStartPos) + u + m_string.substring(end)); } -void KURL::setPass(const String& password) +void KURL::setPass(const DeprecatedString &password) { - if (!m_isValid) - return; - - String p; - int end = passwordEndPos; - if (!password.isEmpty()) { - p = ":" + password + "@"; - if (userEndPos == schemeEndPos + 1) - p = "//" + p; - // Eat the existing '@' since we are going to add our own. - if (end != hostEndPos && m_string[end] == '@') - end += 1; - } else { - // Remove '@' if we now have neither user nor password. - if (userStartPos == userEndPos && end != hostEndPos && m_string[end] == '@') - end += 1; + if (m_isValid) { + DeprecatedString p; + int end = passwordEndPos; + if (!password.isEmpty()) { + p = ':' + password + '@'; + if (userEndPos == schemeEndPos + 1) + p = "//" + p; + // Eat the existing '@' since we are going to add our own. + if (end != hostEndPos && urlString[end] == '@') + end += 1; + } else { + // Remove '@' if we now have neither user nor password. + if (userStartPos == userEndPos && end != hostEndPos && urlString[end] == '@') + end += 1; + } + const DeprecatedString newURL = urlString.left(userEndPos) + p + urlString.mid(end); + parse(newURL.ascii(), &newURL); } - parse(m_string.left(userEndPos) + p + m_string.substring(end)); } -void KURL::setRef(const String& s) +void KURL::setRef(const DeprecatedString &s) { - if (!m_isValid) - return; - parse(m_string.left(queryEndPos) + (s.isEmpty() ? "" : "#" + s)); + if (m_isValid) { + DeprecatedString newURL = urlString.left(queryEndPos) + (s.isEmpty() ? DeprecatedString() : "#" + s); + parse(newURL.ascii(), &newURL); + } } -void KURL::setQuery(const String& query) +void KURL::setQuery(const DeprecatedString &query) { - if (!m_isValid) - return; - - if ((query.isEmpty() || query[0] != '?') && !query.isNull()) - parse(m_string.left(pathEndPos) + "?" + query + m_string.substring(queryEndPos)); - else - parse(m_string.left(pathEndPos) + query + m_string.substring(queryEndPos)); + if (m_isValid) { + DeprecatedString q; + if (!query.isNull() && (query.isEmpty() || query[0] != '?')) { + q = "?" + query; + } else { + q = query; + } + DeprecatedString newURL = urlString.left(pathEndPos) + q + urlString.mid(queryEndPos); + parse(newURL.ascii(), &newURL); + } } -void KURL::setPath(const String& s) +void KURL::setPath(const DeprecatedString &s) { - if (!m_isValid) - return; - - parse(m_string.left(portEndPos) + encodeWithURLEscapeSequences(s) + m_string.substring(pathEndPos)); + if (m_isValid) { + DeprecatedString newURL = urlString.left(portEndPos) + encode_string(s) + urlString.mid(pathEndPos); + parse(newURL.ascii(), &newURL); + } } -String KURL::prettyURL() const +DeprecatedString KURL::prettyURL() const { - if (!m_isValid) - return m_string; - - Vector<UChar> result; + if (!m_isValid) { + return urlString; + } - append(result, protocol()); - result.append(':'); + DeprecatedString result = protocol() + ":"; - Vector<UChar> authority; + DeprecatedString authority; if (hostEndPos != passwordEndPos) { if (userEndPos != userStartPos) { - append(authority, user()); - authority.append('@'); + authority += user(); + authority += "@"; } - append(authority, host()); + authority += host(); if (port() != 0) { - authority.append(':'); - append(authority, String::number(port())); + authority += ":"; + authority += DeprecatedString::number(port()); } } - if (!authority.isEmpty()) { - result.append('/'); - result.append('/'); - result.append(authority); - } else if (protocolIs("file")) { - result.append('/'); - result.append('/'); - } + if (!authority.isEmpty()) + result += "//" + authority; + else if (protocol() == "file") + result += "//"; - append(result, path()); - append(result, query()); + result += path(); + result += query(); if (fragmentEndPos != queryEndPos) { - result.append('#'); - append(result, ref()); + result += "#" + ref(); } - return String::adopt(result); + return result; } -String decodeURLEscapeSequences(const String& str) +DeprecatedString KURL::decode_string(const DeprecatedString& urlString) { - return decodeURLEscapeSequences(str, UTF8Encoding()); + return decode_string(urlString, UTF8Encoding()); } -String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) +DeprecatedString KURL::decode_string(const DeprecatedString& urlString, const TextEncoding& encoding) { - Vector<UChar> result; + DeprecatedString result(""); - CharBuffer buffer; + Vector<char, 2048> buffer(0); - int length = str.length(); + int length = urlString.length(); int decodedPosition = 0; int searchPosition = 0; int encodedRunPosition; - while ((encodedRunPosition = str.find('%', searchPosition)) >= 0) { + while ((encodedRunPosition = urlString.find('%', searchPosition)) >= 0) { // Find the sequence of %-escape codes. int encodedRunEnd = encodedRunPosition; while (length - encodedRunEnd >= 3 - && str[encodedRunEnd] == '%' - && isASCIIHexDigit(str[encodedRunEnd + 1]) - && isASCIIHexDigit(str[encodedRunEnd + 2])) + && urlString[encodedRunEnd] == '%' + && isHexDigit(urlString[encodedRunEnd + 1].latin1()) + && isHexDigit(urlString[encodedRunEnd + 2].latin1())) encodedRunEnd += 3; if (encodedRunEnd == encodedRunPosition) { ++searchPosition; @@ -815,12 +821,16 @@ String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) } searchPosition = encodedRunEnd; + // Copy the entire %-escape sequence into an 8-bit buffer. + int encodedRunLength = encodedRunEnd - encodedRunPosition; + buffer.clear(); + buffer.grow(encodedRunLength + 1); + urlString.copyLatin1(buffer.data(), encodedRunPosition, encodedRunLength); + // Decode the %-escapes into bytes. - unsigned runLength = (encodedRunEnd - encodedRunPosition) / 3; - buffer.resize(runLength); - char* p = buffer.data(); - const UChar* q = str.characters() + encodedRunPosition; - for (unsigned i = 0; i < runLength; ++i) { + char *p = buffer.data(); + const char *q = buffer.data(); + while (*q) { *p++ = (hexDigitValue(q[1]) << 4) | hexDigitValue(q[2]); q += 3; } @@ -831,14 +841,13 @@ String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) continue; // Build up the string with what we just skipped and what we just decoded. - result.append(str.characters() + decodedPosition, encodedRunPosition - decodedPosition); - result.append(decoded.characters(), decoded.length()); + result.append(urlString.mid(decodedPosition, encodedRunPosition - decodedPosition)); + result.append(reinterpret_cast<const DeprecatedChar*>(decoded.characters()), decoded.length()); decodedPosition = encodedRunEnd; } - result.append(str.characters() + decodedPosition, length - decodedPosition); - - return String::adopt(result); + result.append(urlString.mid(decodedPosition, length - decodedPosition)); + return result; } bool KURL::isLocalFile() const @@ -847,15 +856,15 @@ bool KURL::isLocalFile() const // and including feed would allow feeds to potentially let someone's blog // read the contents of the clipboard on a drag, even without a drop. // Likewise with using the FrameLoader::shouldTreatURLAsLocal() function. - return protocolIs("file"); + return equalIgnoringCase(protocol(), "file"); } -static void appendEscapingBadChars(char*& buffer, const char* strStart, size_t length) +static void appendEscapingBadChars(char*& buffer, const char *strStart, size_t length) { - char* p = buffer; + char *p = buffer; - const char* str = strStart; - const char* strEnd = strStart + length; + const char *str = strStart; + const char *strEnd = strStart + length; while (str < strEnd) { unsigned char c = *str++; if (isBadChar(c)) { @@ -870,20 +879,20 @@ static void appendEscapingBadChars(char*& buffer, const char* strStart, size_t l *p++ = c; } } - + buffer = p; } // copy a path, accounting for "." and ".." segments -static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int srcEnd) +static int copyPathRemovingDots(char *dst, const char *src, int srcStart, int srcEnd) { - char* bufferPathStart = dst; + char *bufferPathStart = dst; // empty path is a special case, and need not have a leading slash if (srcStart != srcEnd) { - const char* baseStringStart = src + srcStart; - const char* baseStringEnd = src + srcEnd; - const char* baseStringPos = baseStringStart; + const char *baseStringStart = src + srcStart; + const char *baseStringEnd = src + srcEnd; + const char *baseStringPos = baseStringStart; // this code is unprepared for paths that do not begin with a // slash and we should always have one in the source string @@ -932,9 +941,9 @@ static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int sr return dst - bufferPathStart; } -static inline bool hasSlashDotOrDotDot(const char* str) +static inline bool hasSlashDotOrDotDot(const char *str) { - const unsigned char* p = reinterpret_cast<const unsigned char*>(str); + const unsigned char *p = reinterpret_cast<const unsigned char *>(str); if (!*p) return false; unsigned char pc = *p; @@ -951,30 +960,21 @@ static inline bool matchLetter(char c, char lowercaseLetter) return (c | 0x20) == lowercaseLetter; } -void KURL::parse(const String& string) -{ - // FIXME: This throws away the high bytes of all the characters in the string! - CharBuffer buffer(string.length() + 1); - copyASCII(string.characters(), string.length(), buffer.data()); - buffer[string.length()] = '\0'; - parse(buffer.data(), &string); -} - -void KURL::parse(const char* url, const String* originalString) +void KURL::parse(const char *url, const DeprecatedString *originalString) { m_isValid = true; if (!url || url[0] == '\0') { // valid URL must be non-empty - m_string = originalString ? *originalString : url; m_isValid = false; + urlString = url; return; } if (!isSchemeFirstChar(url[0])) { // scheme must start with an alphabetic character - m_string = originalString ? *originalString : url; m_isValid = false; + urlString = url; return; } @@ -985,8 +985,8 @@ void KURL::parse(const char* url, const String* originalString) } if (url[schemeEnd] != ':') { - m_string = originalString ? *originalString : url; m_isValid = false; + urlString = url; return; } @@ -1004,7 +1004,7 @@ void KURL::parse(const char* url, const String* originalString) if (hierarchical && url[schemeEnd + 2] == '/') { // part after the scheme must be a net_path, parse the authority section - // FIXME: Authority characters may be scanned twice. + // FIXME: authority characters may be scanned twice userStart += 2; userEnd = userStart; @@ -1015,7 +1015,7 @@ void KURL::parse(const char* url, const String* originalString) } userEnd++; } - + if (url[userEnd] == '@') { // actual end of the userinfo, start on the host if (colonPos != 0) { @@ -1035,8 +1035,8 @@ void KURL::parse(const char* url, const String* originalString) hostStart = userStart; } else { // invalid character - m_string = originalString ? *originalString : url; m_isValid = false; + urlString = url; return; } @@ -1052,8 +1052,8 @@ void KURL::parse(const char* url, const String* originalString) hostEnd++; } else { // invalid character - m_string = originalString ? *originalString : url; m_isValid = false; + urlString = url; return; } } else { @@ -1076,8 +1076,8 @@ void KURL::parse(const char* url, const String* originalString) if (!isPathSegmentEndChar(url[portEnd])) { // invalid character - m_string = originalString ? *originalString : url; m_isValid = false; + urlString = url; return; } } else { @@ -1087,7 +1087,7 @@ void KURL::parse(const char* url, const String* originalString) hostStart = hostEnd = passwordEnd; portStart = portEnd = hostEnd; } - + int pathStart = portEnd; int pathEnd = pathStart; int queryStart; @@ -1098,14 +1098,14 @@ void KURL::parse(const char* url, const String* originalString) if (!hierarchical) { while (url[pathEnd] != '\0' && url[pathEnd] != '?' && url[pathEnd] != '#') pathEnd++; - + queryStart = pathEnd; queryEnd = queryStart; if (url[queryStart] == '?') { while (url[queryEnd] != '\0' && url[queryEnd] != '#') queryEnd++; } - + fragmentStart = queryEnd; fragmentEnd = fragmentStart; if (url[fragmentStart] == '#') { @@ -1113,13 +1113,13 @@ void KURL::parse(const char* url, const String* originalString) fragmentEnd = fragmentStart; while (url[fragmentEnd] != '\0') fragmentEnd++; - } + } } else { while (url[pathEnd] != '\0' && url[pathEnd] != '?' && url[pathEnd] != '#') { pathEnd++; } - + queryStart = pathEnd; queryEnd = queryStart; if (url[queryStart] == '?') { @@ -1127,7 +1127,7 @@ void KURL::parse(const char* url, const String* originalString) queryEnd++; } } - + fragmentStart = queryEnd; fragmentEnd = fragmentStart; if (url[fragmentStart] == '#') { @@ -1148,8 +1148,9 @@ void KURL::parse(const char* url, const String* originalString) // copy in the scheme const char *schemeEndPtr = url + schemeEnd; - while (strPtr < schemeEndPtr) + while (strPtr < schemeEndPtr) { *p++ = *strPtr++; + } schemeEndPos = p - buffer.data(); // Check if we're http or https. @@ -1176,12 +1177,12 @@ void KURL::parse(const char* url, const String* originalString) && matchLetter(url[2], 'l') && matchLetter(url[3], 'e') && url[4] == ':'; - + // File URLs need a host part unless it is just file:// or file://localhost bool degenFilePath = pathStart == pathEnd && (hostStart == hostEnd || hostIsLocalHost); - + bool haveNonHostAuthorityPart = userStart != userEnd || passwordStart != passwordEnd || portStart != portEnd; // add ":" after scheme @@ -1204,41 +1205,46 @@ void KURL::parse(const char* url, const String* originalString) // copy in the user strPtr = url + userStart; - const char* userEndPtr = url + userEnd; - while (strPtr < userEndPtr) + const char *userEndPtr = url + userEnd; + while (strPtr < userEndPtr) { *p++ = *strPtr++; + } userEndPos = p - buffer.data(); - + // copy in the password if (passwordEnd != passwordStart) { *p++ = ':'; strPtr = url + passwordStart; - const char* passwordEndPtr = url + passwordEnd; - while (strPtr < passwordEndPtr) + const char *passwordEndPtr = url + passwordEnd; + while (strPtr < passwordEndPtr) { *p++ = *strPtr++; + } } passwordEndPos = p - buffer.data(); - + // If we had any user info, add "@" - if (p - buffer.data() != userStartPos) + if (p - buffer.data() != userStartPos) { *p++ = '@'; - + } + // copy in the host, except in the case of a file URL with authority="localhost" if (!(isFile && hostIsLocalHost && !haveNonHostAuthorityPart)) { strPtr = url + hostStart; - const char* hostEndPtr = url + hostEnd; - while (strPtr < hostEndPtr) + const char *hostEndPtr = url + hostEnd; + while (strPtr < hostEndPtr) { *p++ = *strPtr++; + } } hostEndPos = p - buffer.data(); - + // copy in the port if (hostEnd != portStart) { *p++ = ':'; strPtr = url + portStart; const char *portEndPtr = url + portEnd; - while (strPtr < portEndPtr) + while (strPtr < portEndPtr) { *p++ = *strPtr++; + } } portEndPos = p - buffer.data(); } else { @@ -1247,23 +1253,26 @@ void KURL::parse(const char* url, const String* originalString) // For canonicalization, ensure we have a '/' for no path. // Only do this for http and https. - if (isHTTPorHTTPS && pathEnd - pathStart == 0) + if (isHTTPorHTTPS && pathEnd - pathStart == 0) { *p++ = '/'; - + } + // add path, escaping bad characters + if (hierarchical && hasSlashDotOrDotDot(url)) { - CharBuffer path_buffer(pathEnd - pathStart + 1); + Vector<char, 4096> path_buffer(pathEnd - pathStart + 1); copyPathRemovingDots(path_buffer.data(), url, pathStart, pathEnd); appendEscapingBadChars(p, path_buffer.data(), strlen(path_buffer.data())); } else appendEscapingBadChars(p, url + pathStart, pathEnd - pathStart); pathEndPos = p - buffer.data(); - + + // add query, escaping bad characters appendEscapingBadChars(p, url + queryStart, queryEnd - queryStart); queryEndPos = p - buffer.data(); - + // add fragment, escaping bad characters if (fragmentEnd != queryEnd) { *p++ = '#'; @@ -1271,36 +1280,36 @@ void KURL::parse(const char* url, const String* originalString) } fragmentEndPos = p - buffer.data(); + // If we didn't end up actually changing the original string and + // it started as a DeprecatedString, just reuse it, to avoid extra + // allocation. + if (originalString && strncmp(buffer.data(), url, fragmentEndPos) == 0) { + urlString = *originalString; + } else + urlString = DeprecatedString(buffer.data(), fragmentEndPos); + ASSERT(p - buffer.data() <= (int)buffer.size()); +} - // If we didn't end up actually changing the original string and - // it was already in a String, reuse it to avoid extra allocation. - if (originalString && strncmp(buffer.data(), url, fragmentEndPos) == 0) - m_string = *originalString; - else - m_string = String(buffer.data(), fragmentEndPos); +bool operator==(const KURL &a, const KURL &b) +{ + return a.urlString == b.urlString; } bool equalIgnoringRef(const KURL& a, const KURL& b) { - if (a.queryEndPos != b.queryEndPos) - return false; - unsigned queryLength = a.queryEndPos; - for (unsigned i = 0; i < queryLength; ++i) - if (a.string()[i] != b.string()[i]) - return false; - return true; + return a.urlString.left(a.queryEndPos) == b.urlString.left(b.queryEndPos); } -String encodeWithURLEscapeSequences(const String& notEncodedString) +DeprecatedString KURL::encode_string(const DeprecatedString& notEncodedString) { - CString asUTF8 = notEncodedString.utf8(); - - CharBuffer buffer(asUTF8.length() * 3 + 1); - char* p = buffer.data(); + DeprecatedCString asUTF8 = notEncodedString.utf8(); + + Vector<char, 4096> buffer(asUTF8.length() * 3 + 1); + char *p = buffer.data(); - const char* str = asUTF8.data(); - const char* strEnd = str + asUTF8.length(); + const char *str = asUTF8; + const char *strEnd = str + asUTF8.length(); while (str < strEnd) { unsigned char c = *str++; if (isBadChar(c)) { @@ -1310,92 +1319,96 @@ String encodeWithURLEscapeSequences(const String& notEncodedString) } else *p++ = c; } + + DeprecatedString result(buffer.data(), p - buffer.data()); + + ASSERT(p - buffer.data() <= (int)buffer.size()); - ASSERT(p - buffer.data() <= static_cast<int>(buffer.size())); - - return String(buffer.data(), p - buffer.data()); + return result; } -// Appends the punycoded hostname identified by the given string and length to -// the output buffer. The result will not be null terminated. -static void appendEncodedHostname(UCharBuffer& buffer, const UChar* str, unsigned strLen) +static DeprecatedString encodeHostname(const DeprecatedString &s) { // Needs to be big enough to hold an IDN-encoded name. // For host names bigger than this, we won't do IDN encoding, which is almost certainly OK. const unsigned hostnameBufferLength = 2048; - if (strLen > hostnameBufferLength || charactersAreAllASCII(str, strLen)) - buffer.append(str, strLen); + if (s.isAllASCII() || s.length() > hostnameBufferLength) + return s; #if USE(ICU_UNICODE) - UChar hostnameBuffer[hostnameBufferLength]; + UChar buffer[hostnameBufferLength]; UErrorCode error = U_ZERO_ERROR; - int32_t numCharactersConverted = uidna_IDNToASCII(str, strLen, hostnameBuffer, - hostnameBufferLength, UIDNA_ALLOW_UNASSIGNED, 0, &error); - if (error != U_ZERO_ERROR) - buffer.append(hostnameBuffer, numCharactersConverted); + int32_t numCharactersConverted = uidna_IDNToASCII + (reinterpret_cast<const UChar *>(s.unicode()), s.length(), buffer, hostnameBufferLength, UIDNA_ALLOW_UNASSIGNED, 0, &error); + if (error != U_ZERO_ERROR) { + return s; + } + return DeprecatedString(reinterpret_cast<DeprecatedChar *>(buffer), numCharactersConverted); #elif USE(QT4_UNICODE) - QByteArray result = QUrl::toAce(String(str, strLen)); - buffer.append(result.constData(), result.length()); + QByteArray result = QUrl::toAce(s); + return DeprecatedString(result.constData(), result.length()); #endif } -static void findHostnamesInMailToURL(const UChar* str, int strLen, Vector<pair<int, int> >& nameRanges) +static Vector<pair<int, int> > findHostnamesInMailToURL(const DeprecatedString &s) { // In a mailto: URL, host names come after a '@' character and end with a '>' or ',' or '?' or end of string character. // Skip quoted strings so that characters in them don't confuse us. // When we find a '?' character, we are past the part of the URL that contains host names. - nameRanges.clear(); + Vector<pair<int, int> > a; int p = 0; while (1) { // Find start of host name or of quoted string. - int hostnameOrStringStart = findFirstOf(str, strLen, p, "\"@?"); - if (hostnameOrStringStart == -1) - return; - UChar c = str[hostnameOrStringStart]; + int hostnameOrStringStart = s.find(RegularExpression("[\"@?]"), p); + if (hostnameOrStringStart == -1) { + return a; + } + DeprecatedChar c = s[hostnameOrStringStart]; p = hostnameOrStringStart + 1; - if (c == '?') - return; - + if (c == '?') { + return a; + } + if (c == '@') { // Find end of host name. int hostnameStart = p; - int hostnameEnd = findFirstOf(str, strLen, p, ">,?"); + int hostnameEnd = s.find(RegularExpression("[>,?]"), p); bool done; if (hostnameEnd == -1) { - hostnameEnd = strLen; + hostnameEnd = s.length(); done = true; } else { p = hostnameEnd; done = false; } - nameRanges.append(make_pair(hostnameStart, hostnameEnd)); + a.append(make_pair(hostnameStart, hostnameEnd)); if (done) - return; + return a; } else { // Skip quoted string. ASSERT(c == '"'); while (1) { - int escapedCharacterOrStringEnd = findFirstOf(str, strLen, p, "\"\\"); + int escapedCharacterOrStringEnd = s.find(RegularExpression("[\"\\]"), p); if (escapedCharacterOrStringEnd == -1) - return; + return a; - c = str[escapedCharacterOrStringEnd]; + c = s[escapedCharacterOrStringEnd]; p = escapedCharacterOrStringEnd + 1; - + // If we are the end of the string, then break from the string loop back to the host name loop. if (c == '"') break; - + // Skip escaped character. ASSERT(c == '\\'); - if (p == strLen) - return; + if (p == static_cast<int>(s.length())) + return a; ++p; } @@ -1403,168 +1416,158 @@ static void findHostnamesInMailToURL(const UChar* str, int strLen, Vector<pair<i } } -static bool findHostnameInHierarchicalURL(const UChar* str, int strLen, int& startOffset, int& endOffset) +static bool findHostnameInHierarchicalURL(const DeprecatedString &s, int &startOffset, int &endOffset) { // Find the host name in a hierarchical URL. - // It comes after a "://" sequence, with scheme characters preceding, and - // this should be the first colon in the string. - // It ends with the end of the string or a ":" or a path segment ending character. + // It comes after a "://" sequence, with scheme characters preceding. + // If ends with the end of the string or a ":" or a path segment ending character. // If there is a "@" character, the host part is just the part after the "@". - int separator = findFirstOf(str, strLen, 0, ":"); - if (separator == -1 || separator + 2 >= strLen || - str[separator + 1] != '/' || str[separator + 2] != '/') + int separator = s.find("://"); + if (separator <= 0) { return false; + } // Check that all characters before the :// are valid scheme characters. - if (!isSchemeFirstChar(str[0])) + if (!isSchemeFirstChar(s[0].latin1())) { return false; + } for (int i = 1; i < separator; ++i) { - if (!isSchemeChar(str[i])) + if (!isSchemeChar(s[i].latin1())) { return false; + } } // Start after the separator. int authorityStart = separator + 3; // Find terminating character. - int hostnameEnd = strLen; - for (int i = authorityStart; i < strLen; ++i) { - UChar c = str[i]; - if (c == ':' || (isPathSegmentEndChar(c) && c != 0)) { + int length = s.length(); + int hostnameEnd = length; + for (int i = authorityStart; i < length; ++i) { + char c = s[i].latin1(); + if (c == ':' || (isPathSegmentEndChar(c) && c != '\0')) { hostnameEnd = i; break; } } // Find "@" for the start of the host name. - int userInfoTerminator = findFirstOf(str, strLen, authorityStart, "@"); + int userInfoTerminator = s.find('@', authorityStart); int hostnameStart; - if (userInfoTerminator == -1 || userInfoTerminator > hostnameEnd) + if (userInfoTerminator == -1 || userInfoTerminator > hostnameEnd) { hostnameStart = authorityStart; - else + } else { hostnameStart = userInfoTerminator + 1; + } startOffset = hostnameStart; endOffset = hostnameEnd; return true; } -// Converts all hostnames found in the given input to punycode, preserving the -// rest of the URL unchanged. The output will NOT be null-terminated. -static void encodeHostnames(const String& str, UCharBuffer& output) +static DeprecatedString encodeHostnames(const DeprecatedString &s) { - output.clear(); - - if (protocolIs(str, "mailto")) { - Vector<pair<int, int> > hostnameRanges; - findHostnamesInMailToURL(str.characters(), str.length(), hostnameRanges); + if (s.startsWith("mailto:", false)) { + const Vector<pair<int, int> > hostnameRanges = findHostnamesInMailToURL(s); int n = hostnameRanges.size(); - int p = 0; - for (int i = 0; i < n; ++i) { - const pair<int, int>& r = hostnameRanges[i]; - output.append(&str.characters()[p], r.first - p); - appendEncodedHostname(output, &str.characters()[r.first], r.second - r.first); - p = r.second; + if (n != 0) { + DeprecatedString result; + unsigned p = 0; + for (int i = 0; i < n; ++i) { + const pair<int, int> &r = hostnameRanges[i]; + result += s.mid(p, r.first); + result += encodeHostname(s.mid(r.first, r.second - r.first)); + p = r.second; + } + result += s.mid(p); + return result; } - // This will copy either everything after the last hostname, or the - // whole thing if there is no hostname. - output.append(&str.characters()[p], str.length() - p); } else { int hostStart, hostEnd; - if (findHostnameInHierarchicalURL(str.characters(), str.length(), hostStart, hostEnd)) { - output.append(str.characters(), hostStart); // Before hostname. - appendEncodedHostname(output, &str.characters()[hostStart], hostEnd - hostStart); - output.append(&str.characters()[hostEnd], str.length() - hostEnd); // After hostname. - } else { - // No hostname to encode, return the input. - output.append(str.characters(), str.length()); + if (findHostnameInHierarchicalURL(s, hostStart, hostEnd)) { + return s.left(hostStart) + encodeHostname(s.mid(hostStart, hostEnd - hostStart)) + s.mid(hostEnd); } } + return s; } -static void encodeRelativeString(const String& rel, const TextEncoding& encoding, CharBuffer& output) +static char *encodeRelativeString(const KURL &base, const DeprecatedString &rel, const TextEncoding& encoding) { - UCharBuffer s; - encodeHostnames(rel, s); + DeprecatedString s = encodeHostnames(rel); - TextEncoding pathEncoding(UTF8Encoding()); - TextEncoding otherEncoding = (encoding.isValid() && !protocolIs(rel, "mailto")) ? encoding : UTF8Encoding(); + char *strBuffer; + TextEncoding pathEncoding(UTF8Encoding()); + TextEncoding otherEncoding = (encoding.isValid() && !rel.startsWith("mailto:", false)) ? encoding : UTF8Encoding(); + int pathEnd = -1; if (pathEncoding != otherEncoding) { - // Find the first instance of either # or ?, keep pathEnd at -1 otherwise. - pathEnd = findFirstOf(s.data(), s.size(), 0, "#?"); + pathEnd = s.find(RegularExpression("[?#]")); } - if (pathEnd == -1) { - CString decoded = pathEncoding.encode(s.data(), s.size()); - output.resize(decoded.length()); - memcpy(output.data(), decoded.data(), decoded.length()); + CString decoded = pathEncoding.encode(reinterpret_cast<const UChar*>(s.unicode()), s.length()); + int decodedLength = decoded.length(); + strBuffer = static_cast<char *>(fastMalloc(decodedLength + 1)); + memcpy(strBuffer, decoded.data(), decodedLength); + strBuffer[decodedLength] = 0; } else { - CString pathDecoded = pathEncoding.encode(s.data(), pathEnd); - CString otherDecoded = otherEncoding.encode(s.data() + pathEnd, s.size() - pathEnd); - - output.resize(pathDecoded.length() + otherDecoded.length()); - memcpy(output.data(), pathDecoded.data(), pathDecoded.length()); - memcpy(output.data() + pathDecoded.length(), otherDecoded.data(), otherDecoded.length()); + int length = s.length(); + CString pathDecoded = pathEncoding.encode(reinterpret_cast<const UChar*>(s.unicode()), pathEnd); + CString otherDecoded = otherEncoding.encode(reinterpret_cast<const UChar*>(s.unicode()) + pathEnd, length - pathEnd); + int pathDecodedLength = pathDecoded.length(); + int otherDecodedLength = otherDecoded.length(); + strBuffer = static_cast<char *>(fastMalloc(pathDecodedLength + otherDecodedLength + 1)); + memcpy(strBuffer, pathDecoded.data(), pathDecodedLength); + memcpy(strBuffer + pathDecodedLength, otherDecoded.data(), otherDecodedLength); + strBuffer[pathDecodedLength + otherDecodedLength] = 0; } - output.append('\0'); // null-terminate the output. + + return strBuffer; } -static String substituteBackslashes(const String& string) +static DeprecatedString substituteBackslashes(const DeprecatedString &string) { int questionPos = string.find('?'); int hashPos = string.find('#'); - int pathEnd; - - if (hashPos >= 0 && (questionPos < 0 || questionPos > hashPos)) + unsigned pathEnd; + + if (hashPos >= 0 && (questionPos < 0 || questionPos > hashPos)) { pathEnd = hashPos; - else if (questionPos >= 0) + } else if (questionPos >= 0) { pathEnd = questionPos; - else + } else { pathEnd = string.length(); + } - return string.left(pathEnd).replace('\\','/') + string.substring(pathEnd); + return string.left(pathEnd).replace('\\','/') + string.mid(pathEnd); } bool KURL::isHierarchical() const { if (!m_isValid) return false; - ASSERT(m_string[schemeEndPos] == ':'); - return m_string[schemeEndPos + 1] == '/'; -} - -void KURL::copyToBuffer(CharBuffer& buffer) const -{ - // FIXME: This throws away the high bytes of all the characters in the string! - // That's fine for a valid URL, which is all ASCII, but not for invalid URLs. - buffer.resize(m_string.length()); - copyASCII(m_string.characters(), m_string.length(), buffer.data()); + ASSERT(urlString[schemeEndPos] == ':'); + return urlString[schemeEndPos + 1] == '/'; } +#ifdef ANDROID_JAVASCRIPT_SECURITY bool protocolIs(const String& url, const char* protocol) { - // Do the comparison without making a new string object. assertProtocolIsGood(protocol); + String stripped = url.stripWhiteSpace(); for (int i = 0; ; ++i) { if (!protocol[i]) - return url[i] == ':'; - if (toASCIILower(url[i]) != protocol[i]) + return stripped[i] == ':'; + if (toASCIILower(stripped[i]) != protocol[i]) return false; } } - -const KURL& blankURL() -{ - static KURL staticBlankURL("about:blank"); - return staticBlankURL; -} +#endif #ifndef NDEBUG void KURL::print() const { - printf("%s\n", m_string.utf8().data()); + printf("%s\n", urlString.ascii()); } #endif diff --git a/WebCore/platform/KURL.h b/WebCore/platform/KURL.h index 8728e4f..5c340d4 100644 --- a/WebCore/platform/KURL.h +++ b/WebCore/platform/KURL.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,10 +26,12 @@ #ifndef KURL_h #define KURL_h +#include "DeprecatedString.h" #include "PlatformString.h" +#include <wtf/Platform.h> #if PLATFORM(CF) -typedef const struct __CFURL* CFURLRef; +typedef const struct __CFURL * CFURLRef; #endif #if PLATFORM(MAC) @@ -39,142 +41,107 @@ typedef const struct __CFURL* CFURLRef; class NSURL; #endif #endif - #if PLATFORM(QT) class QUrl; #endif namespace WebCore { -class TextEncoding; + class KURL; + class TextEncoding; + + bool operator==(const KURL&, const KURL&); + inline bool operator!=(const KURL &a, const KURL &b) { return !(a == b); } + + bool equalIgnoringRef(const KURL&, const KURL&); class KURL { public: - // Generates a URL which contains a null string. KURL(); + KURL(const char*); + KURL(const KURL&, const DeprecatedString&); + KURL(const KURL&, const DeprecatedString&, const TextEncoding&); + KURL(const DeprecatedString&); +#if PLATFORM(MAC) + KURL(NSURL*); +#endif +#if PLATFORM(CF) + KURL(CFURLRef); +#endif +#if PLATFORM(QT) + KURL(const QUrl&); +#endif + + bool isNull() const { return urlString.isNull(); } + bool isEmpty() const { return urlString.isEmpty(); } - // The argument is an absolute URL string. The string is assumed to be - // already encoded. - // FIXME: This constructor has a special case for strings that start with - // "/", prepending "file://" to such strings; it would be good to get - // rid of that special case. - explicit KURL(const char*); - - // The argument is an absolute URL string. The string is assumed to be - // already encoded. - // FIXME: For characters with codes other than 0000-00FF will be chopped - // off, so this call is currently not safe to use with arbitrary strings. - // FIXME: This constructor has a special case for strings that start with - // "/", prepending "file://" to such strings; it would be good to get - // rid of that special case. - explicit KURL(const String&); - - // Resolves the relative URL with the given base URL. If provided, the - // TextEncoding is used to encode non-ASCII characers. The base URL can be - // null or empty, in which case the relative URL will be interpreted as - // absolute. - // FIXME: If the base URL is invalid, this always creates an invalid - // URL. Instead I think it would be better to treat all invalid base URLs - // the same way we treate null and empty base URLs. - KURL(const KURL& base, const String& relative); - KURL(const KURL& base, const String& relative, const TextEncoding&); - - // FIXME: The above functions should be harmonized so that passing a - // base of null or the empty string gives the same result as the - // standard String constructor. - - bool isNull() const { return m_string.isNull(); } - bool isEmpty() const { return m_string.isEmpty(); } - - // Returns true if this URL has a path. Note that "http://foo.com/" has a - // path of "/", so this function will return true. Only invalid or - // non-hierarchical (like "javascript:") URLs will have no path. bool hasPath() const; - const String& string() const { return m_string; } - - String protocol() const; - String host() const; - unsigned short port() const; - String user() const; - String pass() const; - String path() const; - String lastPathComponent() const; - String query() const; // Includes the "?". - String ref() const; // Does *not* include the "#". + String string() const { return urlString; } + const DeprecatedString& deprecatedString() const { return urlString; } + + DeprecatedString protocol() const; + DeprecatedString host() const; + unsigned short int port() const; + DeprecatedString user() const; + DeprecatedString pass() const; + DeprecatedString path() const; + DeprecatedString lastPathComponent() const; + DeprecatedString query() const; + DeprecatedString ref() const; bool hasRef() const; - String prettyURL() const; - String fileSystemPath() const; - - // Returns true if the current URL's protocol is the same as the null- - // terminated ASCII argument. The argument must be lower-case. - bool protocolIs(const char*) const; - bool isLocalFile() const; - - void setProtocol(const String&); - void setHost(const String&); - - // Setting the port to 0 will clear any port from the URL. - void setPort(unsigned short); - - // Input is like "foo.com" or "foo.com:8000". - void setHostAndPort(const String&); - - void setUser(const String&); - void setPass(const String&); - - // If you pass an empty path for HTTP or HTTPS URLs, the resulting path - // will be "/". - void setPath(const String&); + DeprecatedString encodedHtmlRef() const { return ref(); } - // The query may begin with a question mark, or, if not, one will be added - // for you. Setting the query to the empty string will leave a "?" in the - // URL (with nothing after it). To clear the query, pass a null string. - void setQuery(const String&); + void setProtocol(const DeprecatedString &); + void setHost(const DeprecatedString &); + void setPort(unsigned short int); + void setHostAndPort(const DeprecatedString&); + void setUser(const DeprecatedString &); + void setPass(const DeprecatedString &); + void setPath(const DeprecatedString &); + void setQuery(const DeprecatedString &); + void setRef(const DeprecatedString &); - void setRef(const String&); - - friend bool equalIgnoringRef(const KURL&, const KURL&); - - operator const String&() const { return m_string; } - operator KJS::UString() const { return m_string; } + DeprecatedString prettyURL() const; #if PLATFORM(CF) - KURL(CFURLRef); CFURLRef createCFURL() const; #endif - #if PLATFORM(MAC) - KURL(NSURL*); - operator NSURL*() const; -#endif -#ifdef __OBJC__ - operator NSString*() const { return m_string; } + NSURL *getNSURL() const; #endif - #if PLATFORM(QT) - KURL(const QUrl&); operator QUrl() const; #endif +#ifdef ANDROID_JAVASCRIPT_SECURITY + // Returns true if the current URL's protocol is the same as the null- + // terminated ASCII argument. The argument must be lower-case. + bool protocolIs(const char*) const; +#endif + bool isLocalFile() const; + String fileSystemPath() const; + + static DeprecatedString decode_string(const DeprecatedString&); + static DeprecatedString decode_string(const DeprecatedString&, const TextEncoding&); + static DeprecatedString encode_string(const DeprecatedString&); + + friend bool operator==(const KURL &, const KURL &); + #ifndef NDEBUG void print() const; #endif private: bool isHierarchical() const; - void init(const KURL&, const String&, const TextEncoding&); + void init(const KURL&, const DeprecatedString&, const TextEncoding&); +#ifdef ANDROID_JAVASCRIPT_SECURITY static bool protocolIs(const String&, const char*); - void copyToBuffer(Vector<char, 512>& buffer) const; - - // Parses the given URL. The originalString parameter allows for an - // optimization: When the source is the same as the fixed-up string, - // it will use the passed-in string instead of allocating a new one. - void parse(const String&); - void parse(const char* url, const String* originalString); +#endif + void parse(const char *url, const DeprecatedString *originalString); - String m_string; + DeprecatedString urlString; bool m_isValid; int schemeEndPos; int userStartPos; @@ -185,63 +152,13 @@ private: int pathEndPos; int queryEndPos; int fragmentEndPos; + + friend bool equalIgnoringRef(const KURL& a, const KURL& b); }; -bool operator==(const KURL&, const KURL&); -bool operator==(const KURL&, const String&); -bool operator==(const String&, const KURL&); -bool operator!=(const KURL&, const KURL&); -bool operator!=(const KURL&, const String&); -bool operator!=(const String&, const KURL&); - -bool equalIgnoringRef(const KURL&, const KURL&); - -const KURL& blankURL(); - -// Functions to do URL operations on strings. -// These are operations that aren't faster on a parsed URL. - +#ifdef ANDROID_JAVASCRIPT_SECURITY bool protocolIs(const String& url, const char* protocol); - -// Unescapes the given string using URL escaping rules, given an optional -// encoding (defaulting to UTF-8 otherwise). DANGER: If the URL has "%00" -// in it, the resulting string will have embedded null characters! -String decodeURLEscapeSequences(const String&); -String decodeURLEscapeSequences(const String&, const TextEncoding&); - -String encodeWithURLEscapeSequences(const String&); - -// Inlines. - -inline bool operator==(const KURL& a, const KURL& b) -{ - return a.string() == b.string(); -} - -inline bool operator==(const KURL& a, const String& b) -{ - return a.string() == b; -} - -inline bool operator==(const String& a, const KURL& b) -{ - return a == b.string(); -} - -inline bool operator!=(const KURL& a, const KURL& b) -{ - return a.string() != b.string(); -} - -inline bool operator!=(const KURL& a, const String& b) -{ - return a.string() != b; -} - -inline bool operator!=(const String& a, const KURL& b) -{ - return a != b.string(); -} +#endif } diff --git a/WebCore/platform/LocalizedStrings.h b/WebCore/platform/LocalizedStrings.h index f640c78..a61f3ce 100644 --- a/WebCore/platform/LocalizedStrings.h +++ b/WebCore/platform/LocalizedStrings.h @@ -29,8 +29,7 @@ namespace WebCore { class String; - class IntSize; - + String inputElementAltText(); String resetButtonDefaultLabel(); String searchableIndexIntroduction(); @@ -106,8 +105,6 @@ namespace WebCore { String uploadFileText(); String allFilesText(); #endif - - String imageTitle(const String& filename, const IntSize& size); } #endif diff --git a/WebCore/platform/MainThread.h b/WebCore/platform/Locker.h index 55758ea..7217bf1 100644 --- a/WebCore/platform/MainThread.h +++ b/WebCore/platform/Locker.h @@ -1,19 +1,18 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * Copyright (C) 2008 Apple 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. + * 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. + * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -26,28 +25,21 @@ * (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 Locker_h +#define Locker_h -#ifndef MainThread_h -#define MainThread_h - -#include <wtf/Threading.h> +#include <wtf/Noncopyable.h> namespace WebCore { -typedef void MainThreadFunction(void*); - -void callOnMainThread(MainThreadFunction*, void* context); +template <typename T> class Locker : Noncopyable { +public: + Locker(T& lockable) : m_lockable(lockable) { m_lockable.lock(); } + ~Locker() { m_lockable.unlock(); } +private: + T& m_lockable; +}; -void initializeThreadingAndMainThread(); - -#if !PLATFORM(WIN) -inline void initializeThreadingAndMainThread() -{ - WTF::initializeThreading(); } -#endif - -} // namespace WebCore - -#endif // MainThread_h +#endif diff --git a/WebCore/platform/MIMETypeRegistry.cpp b/WebCore/platform/MIMETypeRegistry.cpp index 2bca60d..ecd8f54 100644 --- a/WebCore/platform/MIMETypeRegistry.cpp +++ b/WebCore/platform/MIMETypeRegistry.cpp @@ -33,6 +33,9 @@ #if PLATFORM(CG) #include <ApplicationServices/ApplicationServices.h> #endif +#if PLATFORM(MAC) +#include "WebCoreSystemInterface.h" +#endif #if PLATFORM(QT) #include <qimagereader.h> #endif diff --git a/WebCore/platform/MessageQueue.h b/WebCore/platform/MessageQueue.h new file mode 100644 index 0000000..dd35449 --- /dev/null +++ b/WebCore/platform/MessageQueue.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2008 Apple 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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 MessageQueue_h +#define MessageQueue_h + +#include "Threading.h" +#include <wtf/Assertions.h> +#include <wtf/Deque.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { + + template<typename DataType> + class MessageQueue : Noncopyable { + public: + MessageQueue() : m_killed(false) {} + + void append(const DataType&); + void prepend(const DataType&); + bool waitForMessage(DataType&); + bool tryGetMessage(DataType&); + void kill(); + bool killed() const { return m_killed; } + + private: + Mutex m_mutex; + ThreadCondition m_condition; + Deque<DataType> m_queue; + bool m_killed; + }; + + template<typename DataType> + inline void MessageQueue<DataType>::append(const DataType& message) + { + MutexLocker lock(m_mutex); + m_queue.append(message); + m_condition.signal(); + } + + template<typename DataType> + inline void MessageQueue<DataType>::prepend(const DataType& message) + { + MutexLocker lock(m_mutex); + m_queue.prepend(message); + m_condition.signal(); + } + + template<typename DataType> + inline bool MessageQueue<DataType>::waitForMessage(DataType& result) + { + MutexLocker lock(m_mutex); + if (m_killed) + return false; + + if (m_queue.isEmpty()) + m_condition.wait(m_mutex); + if (m_killed) + return false; + + ASSERT(!m_queue.isEmpty()); + result = m_queue.first(); + m_queue.removeFirst(); + return true; + } + + template<typename DataType> + inline bool MessageQueue<DataType>::tryGetMessage(DataType& result) + { + MutexLocker lock(m_mutex); + if (m_killed) + return false; + if (m_queue.isEmpty()) + return false; + + result = m_queue.first(); + m_queue.removeFirst(); + return true; + } + + template<typename DataType> + inline void MessageQueue<DataType>::kill() + { + MutexLocker lock(m_mutex); + m_killed = true; + m_condition.broadcast(); + } +} + +#endif // MessageQueue_h diff --git a/WebCore/platform/NotImplemented.h b/WebCore/platform/NotImplemented.h index 00e29c8..31dd589 100644 --- a/WebCore/platform/NotImplemented.h +++ b/WebCore/platform/NotImplemented.h @@ -43,6 +43,11 @@ if (qgetenv("DISABLE_NI_WARNING").isEmpty()) \ qDebug("FIXME: UNIMPLEMENTED: %s:%d (%s)", __FILE__, __LINE__, WTF_PRETTY_FUNCTION) +#elif defined ANDROID + + #define notImplemented() fprintf(stderr, "%s\n", __PRETTY_FUNCTION__) +// #define notImplemented() LOGV("%s\n", __PRETTY_FUNCTION__) + #elif defined(NDEBUG) #define notImplemented() ((void)0) diff --git a/WebCore/platform/PlatformKeyboardEvent.h b/WebCore/platform/PlatformKeyboardEvent.h index 4346295..c85edc4 100644 --- a/WebCore/platform/PlatformKeyboardEvent.h +++ b/WebCore/platform/PlatformKeyboardEvent.h @@ -1,6 +1,5 @@ /* * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2008 Collabora, Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -119,7 +118,10 @@ namespace WebCore { #if PLATFORM(GTK) PlatformKeyboardEvent(GdkEventKey*); - GdkEventKey* gdkEventKey() const; +#endif + +#ifdef ANDROID_BRIDGE + PlatformKeyboardEvent(int keyCode, int keyValue, bool down, bool forceAutoRepeat, bool cap, bool fn, bool sym); #endif #if PLATFORM(QT) @@ -149,9 +151,6 @@ namespace WebCore { #if PLATFORM(WIN) bool m_isSystemKey; #endif -#if PLATFORM(GTK) - GdkEventKey* m_gdkEventKey; -#endif }; } // namespace WebCore diff --git a/WebCore/platform/PlatformMenuDescription.h b/WebCore/platform/PlatformMenuDescription.h index 7ecc4ce..8d428fc 100644 --- a/WebCore/platform/PlatformMenuDescription.h +++ b/WebCore/platform/PlatformMenuDescription.h @@ -53,6 +53,9 @@ namespace WebCore { typedef const QList<ContextMenuItem>* PlatformMenuDescription; #elif PLATFORM(GTK) typedef GtkMenu* PlatformMenuDescription; +#elif defined ANDROID +// Currently Android is not using this. Until its use is deemed necessary/desirable, typedef to void*. + typedef void* PlatformMenuDescription; #elif PLATFORM(WX) typedef wxMenu* PlatformMenuDescription; #endif diff --git a/WebCore/platform/PlatformMouseEvent.h b/WebCore/platform/PlatformMouseEvent.h index 983a5d1..e2d65a5 100644 --- a/WebCore/platform/PlatformMouseEvent.h +++ b/WebCore/platform/PlatformMouseEvent.h @@ -129,7 +129,6 @@ namespace WebCore { #if PLATFORM(QT) PlatformMouseEvent(QInputEvent*, int clickCount); #endif - #if PLATFORM(WX) PlatformMouseEvent(const wxMouseEvent&, const wxPoint& globalPoint); #endif diff --git a/WebCore/platform/ScrollBar.cpp b/WebCore/platform/ScrollBar.cpp index 0b8e9ab..5618047 100644 --- a/WebCore/platform/ScrollBar.cpp +++ b/WebCore/platform/ScrollBar.cpp @@ -34,8 +34,7 @@ using std::min; namespace WebCore { Scrollbar::Scrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) - : RefCounted<Scrollbar>(0) - , m_client(client) + : m_client(client) , m_orientation(orientation) , m_controlSize(controlSize) , m_visibleSize(0) diff --git a/WebCore/platform/ScrollView.h b/WebCore/platform/ScrollView.h index b78d5bc..761e594 100644 --- a/WebCore/platform/ScrollView.h +++ b/WebCore/platform/ScrollView.h @@ -235,6 +235,16 @@ namespace WebCore { class ScrollViewPrivate; ScrollViewPrivate* m_data; #endif +#ifdef ANDROID + ScrollView(); + ~ScrollView(); + // we call layout() just before creating the picture + // this allows ignoring invals generated by layout() during this phase + void ignoreUpdateContents(bool ); + private: + struct ScrollViewPrivate; + ScrollViewPrivate* m_data; +#endif }; } // namespace WebCore diff --git a/WebCore/platform/SecurityOrigin.h b/WebCore/platform/SecurityOrigin.h index 6f7e6f5..bb9ae12 100644 --- a/WebCore/platform/SecurityOrigin.h +++ b/WebCore/platform/SecurityOrigin.h @@ -31,9 +31,9 @@ #include <wtf/RefCounted.h> #include <wtf/PassRefPtr.h> -#include <wtf/Threading.h> #include "PlatformString.h" +#include "Threading.h" namespace WebCore { diff --git a/WebCore/platform/SecurityOriginHash.h b/WebCore/platform/SecurityOriginHash.h index a5f25e5..7881474 100644 --- a/WebCore/platform/SecurityOriginHash.h +++ b/WebCore/platform/SecurityOriginHash.h @@ -35,7 +35,7 @@ namespace WebCore { struct SecurityOriginHash { - static unsigned hash(const RefPtr<SecurityOrigin>& origin) + static unsigned hash(RefPtr<SecurityOrigin> origin) { unsigned hashCodes[3] = { origin->protocol().impl() ? origin->protocol().impl()->hash() : 0, @@ -45,7 +45,7 @@ struct SecurityOriginHash { return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), 3 * sizeof(unsigned) / sizeof(UChar)); } - static bool equal(const RefPtr<SecurityOrigin>& a, const RefPtr<SecurityOrigin>& b) + static bool equal(RefPtr<SecurityOrigin> a, RefPtr<SecurityOrigin> b) { if (a == 0 || b == 0) return a == b; diff --git a/WebCore/platform/SharedBuffer.cpp b/WebCore/platform/SharedBuffer.cpp index db3183d..449c5a0 100644 --- a/WebCore/platform/SharedBuffer.cpp +++ b/WebCore/platform/SharedBuffer.cpp @@ -29,18 +29,15 @@ namespace WebCore { SharedBuffer::SharedBuffer() - : RefCounted<SharedBuffer>(0) { } SharedBuffer::SharedBuffer(const char* data, int size) - : RefCounted<SharedBuffer>(0) { m_buffer.append(data, size); } SharedBuffer::SharedBuffer(const unsigned char* data, int size) - : RefCounted<SharedBuffer>(0) { m_buffer.append(data, size); } @@ -112,4 +109,13 @@ inline unsigned SharedBuffer::platformDataSize() const #endif +#if !PLATFORM(MAC) && !PLATFORM(WIN) + +PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String& filePath) +{ + return 0; +} + +#endif + } diff --git a/WebCore/platform/SystemTime.h b/WebCore/platform/SystemTime.h index 2b1eed0..367580a 100644 --- a/WebCore/platform/SystemTime.h +++ b/WebCore/platform/SystemTime.h @@ -34,7 +34,10 @@ namespace WebCore { // Return the number of seconds since a user event has been generated float userIdleTime(); - + +#ifdef ANDROID_INSTRUMENT + uint32_t get_thread_msec(); +#endif } #endif diff --git a/WebCore/platform/Threading.h b/WebCore/platform/Threading.h new file mode 100644 index 0000000..f84fd3b --- /dev/null +++ b/WebCore/platform/Threading.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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. + * + * + * Note: The implementations of InterlockedIncrement and InterlockedDecrement are based + * on atomic_increment and atomic_exchange_and_add from the Boost C++ Library. The license + * is virtually identical to the Apple license above but is included here for completeness. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef Threading_h +#define Threading_h + +#include "Locker.h" + +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> + +#if PLATFORM(WIN_OS) +#include <windows.h> +#elif PLATFORM(DARWIN) +#include <libkern/OSAtomic.h> +#elif defined ANDROID +#include "cutils/atomic.h" +#elif COMPILER(GCC) +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) +#include <ext/atomicity.h> +#else +#include <bits/atomicity.h> +#endif +#endif + +#if USE(PTHREADS) +#include <pthread.h> +#endif + +#if PLATFORM(GTK) +typedef struct _GMutex GMutex; +typedef struct _GCond GCond; +#endif + +#if PLATFORM(QT) +class QMutex; +class QWaitCondition; +#endif + +#include <stdint.h> + +namespace WebCore { + +typedef uint32_t ThreadIdentifier; +typedef void* (*ThreadFunction)(void* argument); + +// Returns 0 if thread creation failed +ThreadIdentifier createThread(ThreadFunction, void*); +ThreadIdentifier currentThread(); +int waitForThreadCompletion(ThreadIdentifier, void**); +void detachThread(ThreadIdentifier); + +#if USE(PTHREADS) +typedef pthread_mutex_t PlatformMutex; +typedef pthread_cond_t PlatformCondition; +#elif PLATFORM(GTK) +typedef GMutex* PlatformMutex; +typedef GCond* PlatformCondition; +#elif PLATFORM(QT) +typedef QMutex* PlatformMutex; +typedef QWaitCondition* PlatformCondition; +#elif PLATFORM(WIN_OS) +struct PlatformMutex { + CRITICAL_SECTION m_internalMutex; + size_t m_recursionCount; +}; +struct PlatformCondition { + size_t m_timedOut; + size_t m_blocked; + size_t m_waitingForRemoval; + HANDLE m_gate; + HANDLE m_queue; + HANDLE m_mutex; +}; +#else +typedef void* PlatformMutex; +typedef void* PlatformCondition; +#endif + +class Mutex : Noncopyable { +public: + Mutex(); + ~Mutex(); + + void lock(); + bool tryLock(); + void unlock(); + +public: + PlatformMutex& impl() { return m_mutex; } +private: + PlatformMutex m_mutex; +}; + +typedef Locker<Mutex> MutexLocker; + +class ThreadCondition : Noncopyable { +public: + ThreadCondition(); + ~ThreadCondition(); + + void wait(Mutex& mutex); + void signal(); + void broadcast(); + +private: + PlatformCondition m_condition; +}; + +#if PLATFORM(WIN_OS) +#define WTF_USE_LOCKFREE_THREADSAFESHARED 1 + +inline void atomicIncrement(int volatile* addend) { InterlockedIncrement(reinterpret_cast<long volatile*>(addend)); } +inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast<long volatile*>(addend)); } + +#elif PLATFORM(DARWIN) +#define WTF_USE_LOCKFREE_THREADSAFESHARED 1 + +inline void atomicIncrement(int volatile* addend) { OSAtomicIncrement32Barrier(const_cast<int*>(addend)); } +inline int atomicDecrement(int volatile* addend) { return OSAtomicDecrement32Barrier(const_cast<int*>(addend)); } + +#elif defined ANDROID + +inline void atomicIncrement(int volatile* addend) { android_atomic_inc(addend); } +inline int atomicDecrement(int volatile* addend) { return android_atomic_dec(addend); } + +#elif COMPILER(GCC) +#define WTF_USE_LOCKFREE_THREADSAFESHARED 1 + +inline void atomicIncrement(int volatile* addend) { __gnu_cxx::__atomic_add(addend, 1); } +inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; } + +#endif + +template<class T> class ThreadSafeShared : Noncopyable { +public: + ThreadSafeShared() + : m_refCount(0) + { + } + + void ref() + { +#if USE(LOCKFREE_THREADSAFESHARED) + atomicIncrement(&m_refCount); +#else + MutexLocker locker(m_mutex); + ++m_refCount; +#endif + } + + void deref() + { +#if USE(LOCKFREE_THREADSAFESHARED) + if (atomicDecrement(&m_refCount) <= 0) +#else + { + MutexLocker locker(m_mutex); + --m_refCount; + } + if (m_refCount <= 0) +#endif + delete static_cast<T*>(this); + } + + bool hasOneRef() + { + return refCount() == 1; + } + + int refCount() const + { +#if !USE(LOCKFREE_THREADSAFESHARED) + MutexLocker locker(m_mutex); +#endif + return static_cast<int const volatile &>(m_refCount); + } + +private: + int m_refCount; +#if !USE(LOCKFREE_THREADSAFESHARED) + mutable Mutex m_mutex; +#endif +}; + +typedef void MainThreadFunction(void*); + +void callOnMainThread(MainThreadFunction*, void* context); + +void initializeThreading(); + +#if !PLATFORM(WIN) && !PLATFORM(GTK) +inline void initializeThreading() +{ +} +#endif + +} // namespace WebCore + +#endif // Threading_h diff --git a/WebCore/platform/ThreadingNone.cpp b/WebCore/platform/ThreadingNone.cpp new file mode 100644 index 0000000..10ba8d7 --- /dev/null +++ b/WebCore/platform/ThreadingNone.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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 "Threading.h" + +namespace WebCore { + +ThreadIdentifier createThread(ThreadFunction, void*) { return 0; } +int waitForThreadCompletion(ThreadIdentifier, void**) { return 0; } +void detachThread(ThreadIdentifier) { } +ThreadIdentifier currentThread() { return 0; } + +Mutex::Mutex() {} +Mutex::~Mutex() {} +void Mutex::lock() {} +bool Mutex::tryLock() { return false; } +void Mutex::unlock() {}; + +ThreadCondition::ThreadCondition() {} +ThreadCondition::~ThreadCondition() {} +void ThreadCondition::wait(Mutex& mutex) {} +void ThreadCondition::signal() {} +void ThreadCondition::broadcast() {} + +} // namespace WebCore diff --git a/WebCore/platform/Timer.cpp b/WebCore/platform/Timer.cpp index 94e2af8..bf2eeef 100644 --- a/WebCore/platform/Timer.cpp +++ b/WebCore/platform/Timer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,9 +28,8 @@ #include "SharedTimer.h" #include "SystemTime.h" -#include <limits.h> -#include <limits> #include <math.h> +#include <limits> #include <wtf/HashSet.h> #include <wtf/Vector.h> diff --git a/WebCore/platform/Widget.h b/WebCore/platform/Widget.h index e5c7aac..bd00b9a 100644 --- a/WebCore/platform/Widget.h +++ b/WebCore/platform/Widget.h @@ -36,6 +36,10 @@ class NSView; #endif #endif +#ifdef ANDROID_BRIDGE +class WebCoreViewBridge; +#endif + #if PLATFORM(WIN) typedef struct HWND__* HWND; #endif @@ -206,6 +210,11 @@ protected: IntPoint convertToScreenCoordinate(NSView*, const IntPoint&); #endif +#ifdef ANDROID_BRIDGE + WebCoreViewBridge* getWebCoreViewBridge() const; + void setWebCoreViewBridge(WebCoreViewBridge*); +#endif + #if PLATFORM(WX) Widget(wxWindow*); wxWindow* nativeWindow() const; diff --git a/WebCore/platform/android/AndroidLog.cpp b/WebCore/platform/android/AndroidLog.cpp new file mode 100644 index 0000000..aec7491 --- /dev/null +++ b/WebCore/platform/android/AndroidLog.cpp @@ -0,0 +1,62 @@ +/* + ** + ** Copyright 2008, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "config.h" +#include "AndroidLog.h" +#include <stdio.h> + +#define LOG_TAG "WebCore" +#undef LOG +#include <utils/Log.h> + +namespace WebCore { + +#define BUF_SIZE 1024 + +void ANDROID_LOGD(const char *fmt, ...) { + va_list ap; + char buf[BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUF_SIZE, fmt, ap); + va_end(ap); + + LOGD(buf); +} + +void ANDROID_LOGW(const char *fmt, ...) { + va_list ap; + char buf[BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUF_SIZE, fmt, ap); + va_end(ap); + + LOGW(buf); +} + +void ANDROID_LOGV(const char *fmt, ...) { + va_list ap; + char buf[BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUF_SIZE, fmt, ap); + va_end(ap); + + LOGV(buf); +} +} diff --git a/WebCore/platform/android/AndroidLog.h b/WebCore/platform/android/AndroidLog.h new file mode 100644 index 0000000..f191ebf --- /dev/null +++ b/WebCore/platform/android/AndroidLog.h @@ -0,0 +1,29 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROIDLOG_H_ +#define ANDROIDLOG_H_ + +namespace WebCore { + + void ANDROID_LOGD(const char *fmt, ...); + + void ANDROID_LOGW(const char *fmt, ...); + + void ANDROID_LOGV(const char *fmt, ...); +} +#endif /*ANDROIDLOG_H_*/ diff --git a/WebCore/platform/android/ChromeClientAndroid.cpp b/WebCore/platform/android/ChromeClientAndroid.cpp new file mode 100644 index 0000000..25cbf95 --- /dev/null +++ b/WebCore/platform/android/ChromeClientAndroid.cpp @@ -0,0 +1,213 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" + +#include "ChromeClientAndroid.h" +#include "CString.h" +#include "Document.h" +#include "PlatformString.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameAndroid.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "kjs_proxy.h" +#include "Page.h" +#include "Screen.h" +#include "WebCoreViewBridge.h" +#include "WindowFeatures.h" +#include "Settings.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include <utils/Log.h> + +namespace WebCore { + +void ChromeClientAndroid::chromeDestroyed() +{ + delete this; +} + +void ChromeClientAndroid::setWindowRect(const FloatRect&) { notImplemented(); } + +static WebCoreViewBridge* rootViewForFrame(const Frame* frame) +{ + if (!frame) + return NULL; + FrameView* frameView = frame->view(); + return frameView ? frameView->getWebCoreViewBridge() : NULL; +} + +FloatRect ChromeClientAndroid::windowRect() { + ASSERT(m_frame); + WebCoreViewBridge* view = rootViewForFrame(m_frame); + if (!view) + return FloatRect(); + const WebCore::IntRect& rect = view->getBounds(); + FloatRect fRect(rect.x(), rect.y(), rect.width(), rect.height()); + return fRect; +} + +FloatRect ChromeClientAndroid::pageRect() { notImplemented(); return FloatRect(); } + +float ChromeClientAndroid::scaleFactor() +{ + // only seems to be used for dashboard regions, so just return 1 + return 1; +} + +void ChromeClientAndroid::focus() { + ASSERT(m_frame); + // Ask the application to focus this WebView. + m_frame->bridge()->requestFocus(); +} +void ChromeClientAndroid::unfocus() { notImplemented(); } + +bool ChromeClientAndroid::canTakeFocus(FocusDirection) { notImplemented(); return false; } +void ChromeClientAndroid::takeFocus(FocusDirection) { notImplemented(); } + +Page* ChromeClientAndroid::createWindow(Frame* frame, const FrameLoadRequest&, + const WindowFeatures& features) +{ + ASSERT(frame); + if (!(frame->settings()->supportMultipleWindows())) + // If the client doesn't support multiple windows, just return the current page + return frame->page(); + + FrameAndroid* frameAndroid = (FrameAndroid*) frame; + WebCore::Screen screen(frame); + bool dialog = features.dialog || !features.resizable + || (features.heightSet && features.height < screen.height() + && features.widthSet && features.width < screen.width()) + || (!features.menuBarVisible && !features.statusBarVisible + && !features.toolBarVisible && !features.locationBarVisible + && !features.scrollbarsVisible); + // fullscreen definitely means no dialog + if (features.fullscreen) + dialog = false; + WebCore::Frame* newFrame = frameAndroid->bridge()->createWindow(dialog, + frame->scriptProxy()->processingUserGesture()); + if (newFrame) { + WebCore::Page* page = newFrame->page(); + page->setGroupName(frameAndroid->page()->groupName()); + return page; + } + return NULL; +} + +Page* ChromeClientAndroid::createModalDialog(Frame* , const FrameLoadRequest&) { notImplemented(); return 0; } +void ChromeClientAndroid::show() { notImplemented(); } + +bool ChromeClientAndroid::canRunModal() { notImplemented(); return false; } +void ChromeClientAndroid::runModal() { notImplemented(); } + +void ChromeClientAndroid::setToolbarsVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::toolbarsVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setStatusbarVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::statusbarVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setScrollbarsVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::scrollbarsVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setMenubarVisible(bool) { notImplemented(); } +bool ChromeClientAndroid::menubarVisible() { notImplemented(); return false; } + +void ChromeClientAndroid::setResizable(bool) { notImplemented(); } + +// This function is called by the JavaScript bindings to print usually an error to +// a message console. +void ChromeClientAndroid::addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID) { + notImplemented(); + LOGD("Console: %s line: %d source: %s\n", message.latin1().data(), lineNumber, sourceID.latin1().data()); +} + +bool ChromeClientAndroid::canRunBeforeUnloadConfirmPanel() { return true; } +bool ChromeClientAndroid::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) { + String url = frame->document()->documentURI(); + return frame->view()->getWebCoreViewBridge()->jsUnload(url, message); +} + +void ChromeClientAndroid::closeWindowSoon() +{ + ASSERT(m_frame); + // This will prevent javascript cross-scripting during unload + m_frame->page()->setGroupName(String()); + // Stop loading but do not send the unload event + m_frame->loader()->stopLoading(false); + // Cancel all pending loaders + m_frame->loader()->stopAllLoaders(); + // Remove all event listeners so that no javascript can execute as a result + // of mouse/keyboard events. + m_frame->document()->removeAllEventListenersFromAllNodes(); + // Close the window. + m_frame->bridge()->closeWindow(m_frame->view()->getWebCoreViewBridge()); +} + +void ChromeClientAndroid::runJavaScriptAlert(Frame* frame, const String& message) +{ + String url = frame->document()->documentURI(); + + frame->view()->getWebCoreViewBridge()->jsAlert(url, message); +} + +bool ChromeClientAndroid::runJavaScriptConfirm(Frame* frame, const String& message) +{ + String url = frame->document()->documentURI(); + + return frame->view()->getWebCoreViewBridge()->jsConfirm(url, message); +} + +/* This function is called for the javascript method Window.prompt(). A dialog should be shown on + * the screen with an input put box. First param is the text, the second is the default value for + * the input box, third is return param. If the function returns true, the value set in the third parameter + * is provided to javascript, else null is returned to the script. + */ +bool ChromeClientAndroid::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result) +{ + String url = frame->document()->documentURI(); + + return frame->view()->getWebCoreViewBridge()->jsPrompt(url, message, defaultValue, result); +} +void ChromeClientAndroid::setStatusbarText(const String&) { notImplemented(); } + +// This is called by the JavaScript interpreter when a script has been running for a long +// time. A dialog should be shown to the user asking them if they would like to cancel the +// Javascript. If true is returned, the script is cancelled. +// To make a device more responsive, we default to return true to disallow long running script. +// This implies that some of scripts will not be completed. +bool ChromeClientAndroid::shouldInterruptJavaScript() { return true; } + +// functions new to Jun-07 tip of tree merge: +void ChromeClientAndroid::addToDirtyRegion(IntRect const&) {} +void ChromeClientAndroid::scrollBackingStore(int, int, IntRect const&, IntRect const&) {} +void ChromeClientAndroid::updateBackingStore() {} +bool ChromeClientAndroid::tabsToLinks() const { return false; } +IntRect ChromeClientAndroid::windowResizerRect() const { return IntRect(0, 0, 0, 0); } + +// functions new to the Nov-16-08 tip of tree merge: +void ChromeClientAndroid::mouseDidMoveOverElement(const HitTestResult&, unsigned int) {} +void ChromeClientAndroid::setToolTip(const String&) {} +void ChromeClientAndroid::print(Frame*) {} +bool ChromeClientAndroid::runDatabaseSizeLimitPrompt(Frame*, const String&) { return false; } + +// functions new to Feb-19 tip of tree merge: +void ChromeClientAndroid::exceededDatabaseQuota(Frame*, const String&) {} + +} diff --git a/WebCore/platform/android/ChromeClientAndroid.h b/WebCore/platform/android/ChromeClientAndroid.h new file mode 100644 index 0000000..5f1dd0b --- /dev/null +++ b/WebCore/platform/android/ChromeClientAndroid.h @@ -0,0 +1,101 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ChromeClientAndroid_h +#define ChromeClientAndroid_h + +#include "ChromeClient.h" + +namespace WebCore { + +class FrameAndroid; + class ChromeClientAndroid : public ChromeClient { + public: + ChromeClientAndroid() : m_frame(NULL) {} + virtual void chromeDestroyed(); + + virtual void setWindowRect(const FloatRect&); + virtual FloatRect windowRect(); + + virtual FloatRect pageRect(); + + virtual float scaleFactor(); + + virtual void focus(); + virtual void unfocus(); + + virtual bool canTakeFocus(FocusDirection); + virtual void takeFocus(FocusDirection); + + // The Frame pointer provides the ChromeClient with context about which + // Frame wants to create the new Page. Also, the newly created window + // should not be shown to the user until the ChromeClient of the newly + // created Page has its show method called. + virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&); + virtual Page* createModalDialog(Frame*, const FrameLoadRequest&); + virtual void show(); + + virtual bool canRunModal(); + virtual void runModal(); + + virtual void setToolbarsVisible(bool); + virtual bool toolbarsVisible(); + + virtual void setStatusbarVisible(bool); + virtual bool statusbarVisible(); + + virtual void setScrollbarsVisible(bool); + virtual bool scrollbarsVisible(); + + virtual void setMenubarVisible(bool); + virtual bool menubarVisible(); + + virtual void setResizable(bool); + + virtual void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID); + + virtual bool canRunBeforeUnloadConfirmPanel(); + virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame); + + virtual void closeWindowSoon(); + + virtual void runJavaScriptAlert(Frame*, const String&); + virtual bool runJavaScriptConfirm(Frame*, const String&); + virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result); + + virtual void setStatusbarText(const String&); + virtual bool shouldInterruptJavaScript(); + virtual bool tabsToLinks() const; + + virtual IntRect windowResizerRect() const; + virtual void addToDirtyRegion(const IntRect&); + virtual void scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect); + virtual void updateBackingStore(); + virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned int); + virtual void setToolTip(const String&); + virtual void print(Frame*); + virtual bool runDatabaseSizeLimitPrompt(Frame*, const String&); + virtual void exceededDatabaseQuota(Frame*, const String&); + // Android-specific + void setFrame(FrameAndroid* frame) { m_frame = frame; } + private: + FrameAndroid* m_frame; + }; + +} + +#endif diff --git a/WebCore/platform/android/ClipboardAndroid.cpp b/WebCore/platform/android/ClipboardAndroid.cpp new file mode 100644 index 0000000..d4bf734 --- /dev/null +++ b/WebCore/platform/android/ClipboardAndroid.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2006, 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "ClipboardAndroid.h" + +#include "CachedImage.h" +#include "CSSHelper.h" +#include "CString.h" +#include "DeprecatedString.h" +#include "Document.h" +#include "DragData.h" +#include "Element.h" +#include "EventHandler.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "Image.h" +//#include "MimeTypeRegistry.h" +#include "markup.h" +#include "Page.h" +#include "Pasteboard.h" +#include "PlatformMouseEvent.h" +#include "PlatformString.h" +#include "Range.h" +#include "RenderImage.h" +#include "ResourceResponse.h" +#include "StringHash.h" + +#include <wtf/RefPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +// format string for +static const char szShellDotUrlTemplate[] = "[InternetShortcut]\r\nURL=%s\r\n"; + +// We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft +// see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 + +enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText }; + +static ClipboardDataType clipboardTypeFromMIMEType(const String& type) +{ + String qType = type.stripWhiteSpace().lower(); + + // two special cases for IE compatibility + if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain;")) + return ClipboardDataTypeText; + if (qType == "url" || qType == "text/uri-list") + return ClipboardDataTypeURL; + + return ClipboardDataTypeNone; +} + +ClipboardAndroid::ClipboardAndroid(ClipboardAccessPolicy policy, bool isForDragging) + : Clipboard(policy, isForDragging) +{ +} + +ClipboardAndroid::~ClipboardAndroid() +{ +} + +void ClipboardAndroid::clearData(const String& type) +{ + //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941> + ASSERT(isForDragging()); + if (policy() != ClipboardWritable) + return; + + ClipboardDataType dataType = clipboardTypeFromMIMEType(type); + + if (dataType == ClipboardDataTypeURL) { + } + if (dataType == ClipboardDataTypeText) { + + } + +} + +void ClipboardAndroid::clearAllData() +{ + //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941> + ASSERT(isForDragging()); + if (policy() != ClipboardWritable) + return; + +} + +String ClipboardAndroid::getData(const String& type, bool& success) const +{ + success = false; + if (policy() != ClipboardReadable) { + return ""; + } + + ClipboardDataType dataType = clipboardTypeFromMIMEType(type); + /* if (dataType == ClipboardDataTypeText) + return getPlainText(m_dataObject.get(), success); + else if (dataType == ClipboardDataTypeURL) + return getURL(m_dataObject.get(), success); + */ + return ""; +} + +bool ClipboardAndroid::setData(const String &type, const String &data) +{ + //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941> + ASSERT(isForDragging()); + if (policy() != ClipboardWritable) + return false; + + ClipboardDataType platformType = clipboardTypeFromMIMEType(type); + + if (platformType == ClipboardDataTypeURL) { + KURL url = data.deprecatedString(); +#if 0 && defined ANDROID // FIXME HACK : KURL no longer defines a public method isValid() + if (!url.isValid()) + return false; +#endif + return false; // WebCore::writeURL(m_writableDataObject.get(), url, String(), false, true); + } else if ( platformType == ClipboardDataTypeText) { + return false; + } + return false; +} + + +// extensions beyond IE's API +HashSet<String> ClipboardAndroid::types() const +{ + HashSet<String> results; + if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) + return results; + + return results; +} + +void ClipboardAndroid::setDragImage(CachedImage* image, Node *node, const IntPoint &loc) +{ + if (policy() != ClipboardImageWritable && policy() != ClipboardWritable) + return; + + if (m_dragImage) + m_dragImage->deref(this); + m_dragImage = image; + if (m_dragImage) + m_dragImage->ref(this); + + m_dragLoc = loc; + m_dragImageElement = node; +} + +void ClipboardAndroid::setDragImage(CachedImage* img, const IntPoint &loc) +{ + setDragImage(img, 0, loc); +} + +void ClipboardAndroid::setDragImageElement(Node *node, const IntPoint &loc) +{ + setDragImage(0, node, loc); +} + +DragImageRef ClipboardAndroid::createDragImage(IntPoint& loc) const +{ + void* result = 0; + //FIXME: Need to be able to draw element <rdar://problem/5015942> + if (m_dragImage) { + result = createDragImageFromImage(m_dragImage->image()); + loc = m_dragLoc; + } + return result; +} + + +void ClipboardAndroid::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) +{ + +} + +void ClipboardAndroid::writeURL(const KURL& kurl, const String& titleStr, Frame*) +{ + +} + +void ClipboardAndroid::writeRange(Range* selectedRange, Frame* frame) +{ + ASSERT(selectedRange); +} + +bool ClipboardAndroid::hasData() +{ + + return false; +} + +} // namespace WebCore diff --git a/WebCore/platform/network/mac/ResourceError.h b/WebCore/platform/android/ClipboardAndroid.h index 5e4a553..45ff5f9 100644 --- a/WebCore/platform/network/mac/ResourceError.h +++ b/WebCore/platform/android/ClipboardAndroid.h @@ -1,6 +1,5 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,52 +23,46 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceError_h -#define ResourceError_h +#ifndef ClipboardAndroid_h +#define ClipboardAndroid_h -#include "ResourceErrorBase.h" -#include <wtf/RetainPtr.h> +#include "Clipboard.h" -#ifdef __OBJC__ -@class NSError; -#else -class NSError; -#endif +#include "CachedResourceClient.h" namespace WebCore { - class ResourceError : public ResourceErrorBase { - public: - ResourceError() - : m_dataIsUpToDate(true) - { - } - - ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) - : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) - , m_dataIsUpToDate(true) - { - } + class CachedImage; - ResourceError(NSError* error) - : m_dataIsUpToDate(false) - , m_platformError(error) - { - m_isNull = !error; - } + class ClipboardAndroid : public Clipboard, public CachedResourceClient { + public: + ClipboardAndroid(ClipboardAccessPolicy policy, bool isForDragging); + ~ClipboardAndroid(); + + void clearData(const String& type); + void clearAllData(); + String getData(const String& type, bool& success) const; + bool setData(const String& type, const String& data); + + // extensions beyond IE's API + HashSet<String> types() const; + + void setDragImage(CachedImage*, const IntPoint&); + void setDragImageElement(Node*, const IntPoint&); + + virtual DragImageRef createDragImage(IntPoint& dragLoc) const; + virtual void declareAndWriteDragImage(Element*, const KURL&, const String& title, Frame*); + virtual void writeURL(const KURL&, const String&, Frame*); + virtual void writeRange(Range*, Frame*); - operator NSError*() const; + virtual bool hasData(); private: - friend class ResourceErrorBase; - - void platformLazyInit(); - static bool platformCompare(const ResourceError& a, const ResourceError& b); - - bool m_dataIsUpToDate; - mutable RetainPtr<NSError> m_platformError; -}; + void resetFromClipboard(); + void setDragImage(CachedImage*, Node*, const IntPoint&); + Frame* m_frame; + }; } // namespace WebCore -#endif // ResourceError_h_ +#endif // ClipboardAndroid_h diff --git a/WebCore/platform/android/ContextMenuClientAndroid.cpp b/WebCore/platform/android/ContextMenuClientAndroid.cpp new file mode 100644 index 0000000..cc7252f --- /dev/null +++ b/WebCore/platform/android/ContextMenuClientAndroid.cpp @@ -0,0 +1,38 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "ContextMenuClientAndroid.h" +#include "wtf/Assertions.h" + +#define notImplemented() ASSERT(0) + +namespace WebCore { + +void ContextMenuClientAndroid::contextMenuDestroyed() { delete this; } + +PlatformMenuDescription ContextMenuClientAndroid::getCustomMenuFromDefaultItems(ContextMenu*) { notImplemented(); return 0; } +void ContextMenuClientAndroid::contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) { notImplemented(); } + +void ContextMenuClientAndroid::downloadURL(const KURL& url) { notImplemented(); } +void ContextMenuClientAndroid::copyImageToClipboard(const HitTestResult&) { notImplemented(); } +void ContextMenuClientAndroid::searchWithGoogle(const Frame*) { notImplemented(); } +void ContextMenuClientAndroid::lookUpInDictionary(Frame*) { notImplemented(); } +void ContextMenuClientAndroid::speak(const String&) { notImplemented(); } +void ContextMenuClientAndroid::stopSpeaking() { notImplemented(); } + +} diff --git a/WebCore/platform/android/ContextMenuClientAndroid.h b/WebCore/platform/android/ContextMenuClientAndroid.h new file mode 100644 index 0000000..0d1e888 --- /dev/null +++ b/WebCore/platform/android/ContextMenuClientAndroid.h @@ -0,0 +1,42 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ContextMenuClientAndroid_h +#define ContextMenuClientAndroid_h + +#include "ContextMenuClient.h" + +namespace WebCore { + +class ContextMenuClientAndroid : public ContextMenuClient { +public: + virtual void contextMenuDestroyed(); + + virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*); + virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*); + + virtual void downloadURL(const KURL& url); + virtual void copyImageToClipboard(const HitTestResult&); + virtual void searchWithGoogle(const Frame*); + virtual void lookUpInDictionary(Frame*); + virtual void speak(const String&); + virtual void stopSpeaking(); +}; + +} + +#endif diff --git a/WebCore/platform/android/Cookie.cpp b/WebCore/platform/android/Cookie.cpp new file mode 100644 index 0000000..eeaa4ce --- /dev/null +++ b/WebCore/platform/android/Cookie.cpp @@ -0,0 +1,51 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "JavaSharedClient.h" +#include "CookieClient.h" + +#define LOG_TAG "Cookies" +#undef LOG +#include "utils/Log.h" + +namespace WebCore { + + class Document; + + void setCookies(Document* , const KURL& url, const KURL& policyBaseURL, const String& value) + { + if (JavaSharedClient::GetCookieClient()) + JavaSharedClient::GetCookieClient()->setCookies(url, policyBaseURL, value); + } + + String cookies(const Document* , const KURL& url) + { + if (JavaSharedClient::GetCookieClient()) + return JavaSharedClient::GetCookieClient()->cookies(url); + return String(); + } + + bool cookiesEnabled(const Document* ) + { + if (JavaSharedClient::GetCookieClient()) + return JavaSharedClient::GetCookieClient()->cookiesEnabled(); + return false; + } + +} + diff --git a/WebCore/platform/android/CookieClient.h b/WebCore/platform/android/CookieClient.h new file mode 100644 index 0000000..0b76932 --- /dev/null +++ b/WebCore/platform/android/CookieClient.h @@ -0,0 +1,37 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef COOKIE_CLIENT_H +#define COOKIE_CLIENT_H + +#include "KURL.h" +#include "PlatformString.h" + +namespace WebCore { + + class CookieClient + { + public: + virtual ~CookieClient() {} + virtual void setCookies(const KURL& url, const KURL& docURL, const String& value) = 0; + virtual String cookies(const KURL& url) = 0; + virtual bool cookiesEnabled() = 0; + }; + +} +#endif + diff --git a/WebCore/platform/android/CursorAndroid.cpp b/WebCore/platform/android/CursorAndroid.cpp new file mode 100644 index 0000000..e3a2562 --- /dev/null +++ b/WebCore/platform/android/CursorAndroid.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "Cursor.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include "utils/Log.h" + +namespace WebCore { + +static void notImplemented() { LOGV("Cursor: NotYetImplemented"); } + +Cursor::Cursor(Image* image, const IntPoint& ) +{ + notImplemented(); +} + +Cursor::Cursor(const Cursor& other) +{ + notImplemented(); +} + +Cursor::~Cursor() +{ + notImplemented(); +} + +Cursor& Cursor::operator=(const Cursor& other) +{ + notImplemented(); + return *this; +} + +const Cursor& pointerCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& crossCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& handCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& moveCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& iBeamCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& waitCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& helpCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& eastResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& northResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& northEastResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& northWestResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& southResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& southEastResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& southWestResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& westResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& northSouthResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& eastWestResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& northEastSouthWestResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& northWestSouthEastResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& columnResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& rowResizeCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& verticalTextCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& cellCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& contextMenuCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& noDropCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& copyCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& progressCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& aliasCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +const Cursor& noneCursor() +{ + notImplemented(); + static Cursor c; + return c; +} + +} diff --git a/WebCore/platform/android/DragClientAndroid.cpp b/WebCore/platform/android/DragClientAndroid.cpp new file mode 100644 index 0000000..6a9aef1 --- /dev/null +++ b/WebCore/platform/android/DragClientAndroid.cpp @@ -0,0 +1,43 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "DragClientAndroid.h" +#include "wtf/Assertions.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include <utils/Log.h> + +//#define notImplemented() ASSERT(0) +#define notImplemented() LOGV("%s\n", __PRETTY_FUNCTION__); + +namespace WebCore { + +void DragClientAndroid::dragControllerDestroyed() { notImplemented(); delete this; } + +void DragClientAndroid::willPerformDragDestinationAction(DragDestinationAction, DragData*) { notImplemented(); } + +DragDestinationAction DragClientAndroid::actionMaskForDrag(DragData*) { notImplemented(); return DragDestinationActionNone; } + +DragSourceAction DragClientAndroid::dragSourceActionMaskForPoint(const IntPoint&) { notImplemented(); return DragSourceActionNone; } + +void* DragClientAndroid::createDragImageForLink(KURL&, String const&, Frame*) { return NULL; } +void DragClientAndroid::willPerformDragSourceAction(DragSourceAction, IntPoint const&, Clipboard*) {} +void DragClientAndroid::startDrag(void*, IntPoint const&, IntPoint const&, Clipboard*, Frame*, bool) {} + +} diff --git a/WebCore/platform/android/DragClientAndroid.h b/WebCore/platform/android/DragClientAndroid.h new file mode 100644 index 0000000..fd699e0 --- /dev/null +++ b/WebCore/platform/android/DragClientAndroid.h @@ -0,0 +1,41 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef DragClientAndroid_h +#define DragClientAndroid_h + +#include "DragClient.h" + +namespace WebCore { + + class DragClientAndroid : public WebCore::DragClient { + public: + virtual void willPerformDragDestinationAction(DragDestinationAction, DragData*); + virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard*); + virtual DragDestinationAction actionMaskForDrag(DragData*); + //We work in window rather than view coordinates here + virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint&); + + virtual void startDrag(DragImageRef dragImage, const IntPoint& dragImageOrigin, const IntPoint& eventPos, Clipboard*, Frame*, bool linkDrag = false); + virtual DragImageRef createDragImageForLink(KURL&, const String& label, Frame*); + + virtual void dragControllerDestroyed(); + }; + +} + +#endif diff --git a/WebCore/platform/android/DragDataAndroid.cpp b/WebCore/platform/android/DragDataAndroid.cpp new file mode 100644 index 0000000..cac3c4a --- /dev/null +++ b/WebCore/platform/android/DragDataAndroid.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "DragData.h" + +#include "Document.h" +#include "DocumentFragment.h" + +namespace WebCore { + +bool DragData::canSmartReplace() const +{ + return false; +} + +bool DragData::containsColor() const +{ + return false; +} + +bool DragData::containsPlainText() const +{ + return false; +} + +String DragData::asPlainText() const +{ + return String(); +} + +Color DragData::asColor() const +{ + return Color(); +} + +Clipboard* DragData::createClipboard(ClipboardAccessPolicy) const +{ + return 0; +} + +bool DragData::containsCompatibleContent() const +{ + return false; +} + +bool DragData::containsURL() const +{ + return false; +} + +String DragData::asURL(String* title) const +{ + return String(); +} + + +PassRefPtr<DocumentFragment> DragData::asFragment(Document*) const +{ + return 0; +} + +// functions new to Jun-07 tip of tree merge: +void DragData::asFilenames(Vector<String>&) const {} +bool DragData::containsFiles() const { return false; } + +} diff --git a/WebCore/platform/android/EditorAndroid.cpp b/WebCore/platform/android/EditorAndroid.cpp new file mode 100644 index 0000000..204b23c --- /dev/null +++ b/WebCore/platform/android/EditorAndroid.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006, 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "Editor.h" +#include "EditorClient.h" + +#include "ClipboardAndroid.h" +#include "Document.h" +#include "Element.h" +#include "htmlediting.h" +#include "NotImplemented.h" +#include "TextIterator.h" +#include "visible_units.h" + +namespace WebCore { + +PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy) +{ + return new ClipboardAndroid(policy, false); +} + +} // namespace WebCore diff --git a/WebCore/platform/android/EditorClientAndroid.cpp b/WebCore/platform/android/EditorClientAndroid.cpp new file mode 100644 index 0000000..b38c542 --- /dev/null +++ b/WebCore/platform/android/EditorClientAndroid.cpp @@ -0,0 +1,241 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "Editor.h" +#include "EditorClientAndroid.h" +#include "Event.h" +#include "EventNames.h" +#include "FocusController.h" +#include "Frame.h" +#include "KeyboardCodes.h" +#include "KeyboardEvent.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformString.h" +#include "wtf/Assertions.h" + +#include <assert.h> + +#define LOG_TAG "WebCore" +#undef LOG +#include <utils/Log.h> + +#define notImplemented() LOGV("%s\n", __PRETTY_FUNCTION__) +#define lowPriority_notImplemented() //printf("%s\n", __PRETTY_FUNCTION__) + +namespace WebCore { + +void EditorClientAndroid::pageDestroyed() { + delete this; +} + +bool EditorClientAndroid::shouldDeleteRange(Range*) { return true; } +bool EditorClientAndroid::shouldShowDeleteInterface(HTMLElement*) { notImplemented(); return false; } +bool EditorClientAndroid::smartInsertDeleteEnabled() { notImplemented(); return false; } +bool EditorClientAndroid::isContinuousSpellCheckingEnabled() { notImplemented(); return false; } +void EditorClientAndroid::toggleContinuousSpellChecking() { notImplemented(); } +bool EditorClientAndroid::isGrammarCheckingEnabled() { notImplemented(); return false; } +void EditorClientAndroid::toggleGrammarChecking() { notImplemented(); } +int EditorClientAndroid::spellCheckerDocumentTag() { notImplemented(); return -1; } + +bool EditorClientAndroid::isEditable() { notImplemented(); return false; } + +// Following Qt's implementation. For shouldBeginEditing and shouldEndEditing. +// Returning true for these fixes issue http://b/issue?id=735185 +bool EditorClientAndroid::shouldBeginEditing(Range*) +{ + return true; +} + +bool EditorClientAndroid::shouldEndEditing(Range*) +{ + return true; +} + +bool EditorClientAndroid::shouldInsertNode(Node*, Range*, EditorInsertAction) { notImplemented(); return true; } +bool EditorClientAndroid::shouldInsertText(String, Range*, EditorInsertAction) { return true; } +bool EditorClientAndroid::shouldApplyStyle(CSSStyleDeclaration*, Range*) { notImplemented(); return true; } + +void EditorClientAndroid::didBeginEditing() { notImplemented(); } + +// This function is called so that the platform can handle changes to content. It is called +// after the contents have been edited or unedited (ie undo) +void EditorClientAndroid::respondToChangedContents() { notImplemented(); } + +void EditorClientAndroid::didEndEditing() { notImplemented(); } +void EditorClientAndroid::didWriteSelectionToPasteboard() { notImplemented(); } +void EditorClientAndroid::didSetSelectionTypesForPasteboard() { notImplemented(); } + +// Copied from the Window's port of WebKit. +static const unsigned AltKey = 1 << 0; +static const unsigned ShiftKey = 1 << 1; + +struct KeyDownEntry { + unsigned virtualKey; + unsigned modifiers; + const char* name; +}; + +struct KeyPressEntry { + unsigned charCode; + unsigned modifiers; + const char* name; +}; + +static const KeyDownEntry keyDownEntries[] = { + { VK_LEFT, 0, "MoveLeft" }, + { VK_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, + { VK_LEFT, AltKey, "MoveWordLeft" }, + { VK_LEFT, AltKey | ShiftKey, "MoveWordLeftAndModifySelection" }, + { VK_RIGHT, 0, "MoveRight" }, + { VK_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, + { VK_RIGHT, AltKey, "MoveWordRight" }, + { VK_RIGHT, AltKey | ShiftKey, "MoveWordRightAndModifySelection" }, + { VK_UP, 0, "MoveUp" }, + { VK_UP, ShiftKey, "MoveUpAndModifySelection" }, + { VK_DOWN, 0, "MoveDown" }, + { VK_DOWN, ShiftKey, "MoveDownAndModifySelection" }, + + { VK_BACK, 0, "BackwardDelete" }, + { VK_BACK, ShiftKey, "ForwardDelete" }, + { VK_BACK, AltKey, "DeleteWordBackward" }, + { VK_BACK, AltKey | ShiftKey, "DeleteWordForward" }, + + { VK_ESCAPE, 0, "Cancel" }, + { VK_TAB, 0, "InsertTab" }, + { VK_TAB, ShiftKey, "InsertBacktab" }, + { VK_RETURN, 0, "InsertNewline" }, + { VK_RETURN, AltKey, "InsertNewline" }, + { VK_RETURN, AltKey | ShiftKey, "InsertNewline" } +}; + +static const KeyPressEntry keyPressEntries[] = { + { '\t', 0, "InsertTab" }, + { '\t', ShiftKey, "InsertBackTab" }, + { '\r', 0, "InsertNewline" }, + { '\r', AltKey, "InsertNewline" }, + { '\r', AltKey | ShiftKey, "InsertNewline" } +}; + +static const char* interpretKeyEvent(const KeyboardEvent* evt) +{ + const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); + + static HashMap<int, const char*>* keyDownCommandsMap = 0; + static HashMap<int, const char*>* keyPressCommandsMap = 0; + + if (!keyDownCommandsMap) { + keyDownCommandsMap = new HashMap<int, const char*>; + keyPressCommandsMap = new HashMap<int, const char*>; + + for (unsigned i = 0; i < sizeof(keyDownEntries)/sizeof(KeyDownEntry); i++) + keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name); + + for (unsigned i = 0; i < sizeof(keyPressEntries)/sizeof(KeyPressEntry); i++) + keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name); + } + + unsigned modifiers = 0; + if (keyEvent->shiftKey()) + modifiers |= ShiftKey; + if (keyEvent->altKey()) + modifiers |= AltKey; + + if (evt->type() == EventNames::keydownEvent) { + int mapKey = modifiers << 16 | evt->keyCode(); + return mapKey ? keyDownCommandsMap->get(mapKey) : 0; + } + + int mapKey = modifiers << 16 | evt->charCode(); + return mapKey ? keyPressCommandsMap->get(mapKey) : 0; +} + +void EditorClientAndroid::handleKeyboardEvent(KeyboardEvent* event) { + assert(m_page); + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame) + return; + + const PlatformKeyboardEvent* keyEvent = event->keyEvent(); + // TODO: If the event is not coming from Android Java, e.g. from JavaScript, + // PlatformKeyboardEvent is null. We should support this later. + if (!keyEvent) + return; + + Editor::Command command = frame->editor()->command(interpretKeyEvent(event)); + if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) { + if (!command.isTextInsertion() && command.execute(event)) { + // This function mimics the Windows version. However, calling event->setDefaultHandled() + // prevents the javascript key events for the delete key from happening. + // Update: Safari doesn't send delete key events to javascript so + // we will mimic that behavior. + event->setDefaultHandled(); + } + return; + } + + if (command.execute(event)) { + event->setDefaultHandled(); + return; + } + + // Don't insert null or control characters as they can result in unexpected behaviour + if (event->charCode() < ' ') + return; + + if (frame->editor()->insertText(keyEvent->text(), event)) + event->setDefaultHandled(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// we just don't support Undo/Redo at the moment + +void EditorClientAndroid::registerCommandForUndo(PassRefPtr<EditCommand>) {} +void EditorClientAndroid::registerCommandForRedo(PassRefPtr<EditCommand>) {} +void EditorClientAndroid::clearUndoRedoOperations() {} +bool EditorClientAndroid::canUndo() const { return false; } +bool EditorClientAndroid::canRedo() const { return false; } +void EditorClientAndroid::undo() {} +void EditorClientAndroid::redo() {} + +// functions new to Jun-07 tip of tree merge: +void EditorClientAndroid::showSpellingUI(bool) {} +void EditorClientAndroid::getGuessesForWord(String const&, Vector<String>&) {} +bool EditorClientAndroid::spellingUIIsShowing() { return false; } +void EditorClientAndroid::checkGrammarOfString(unsigned short const*, int, Vector<GrammarDetail>&, int*, int*) {} +void EditorClientAndroid::checkSpellingOfString(unsigned short const*, int, int*, int*) {} +void EditorClientAndroid::textFieldDidEndEditing(Element*) {} +void EditorClientAndroid::textDidChangeInTextArea(Element*) {} +void EditorClientAndroid::textDidChangeInTextField(Element*) {} +void EditorClientAndroid::textFieldDidBeginEditing(Element*) {} +void EditorClientAndroid::ignoreWordInSpellDocument(String const&) {} +void EditorClientAndroid::respondToChangedSelection() {} +bool EditorClientAndroid::shouldChangeSelectedRange(Range*, Range*, EAffinity, bool) { return m_notFromClick; } +bool EditorClientAndroid::doTextFieldCommandFromEvent(Element*, KeyboardEvent*) { return false; } +void EditorClientAndroid::textWillBeDeletedInTextField(Element*) {} +void EditorClientAndroid::updateSpellingUIWithGrammarString(String const&, GrammarDetail const&) {} +void EditorClientAndroid::updateSpellingUIWithMisspelledWord(String const&) {} +void EditorClientAndroid::learnWord(String const&) {} + +// functions new to the Nov-16-08 tip of tree merge: +bool EditorClientAndroid::shouldMoveRangeAfterDelete(Range*, Range*) { return true; } +void EditorClientAndroid::setInputMethodState(bool) {} + +// functions new to Feb-19 tip of tree merge: +void EditorClientAndroid::handleInputMethodKeydown(KeyboardEvent*) {} + +} diff --git a/WebCore/platform/android/EditorClientAndroid.h b/WebCore/platform/android/EditorClientAndroid.h new file mode 100644 index 0000000..1bfd7e6 --- /dev/null +++ b/WebCore/platform/android/EditorClientAndroid.h @@ -0,0 +1,103 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef EditorClientAndroid_h +#define EditorClientAndroid_h + +#include "EditorClient.h" +#include "Page.h" + +namespace WebCore { + +class EditorClientAndroid : public EditorClient { +public: + EditorClientAndroid() { m_notFromClick = true; } + virtual void pageDestroyed(); + + virtual bool shouldDeleteRange(Range*); + virtual bool shouldShowDeleteInterface(HTMLElement*); + virtual bool smartInsertDeleteEnabled(); + virtual bool isContinuousSpellCheckingEnabled(); + virtual void toggleContinuousSpellChecking(); + virtual bool isGrammarCheckingEnabled(); + virtual void toggleGrammarChecking(); + virtual int spellCheckerDocumentTag(); + + virtual bool isEditable(); + + virtual bool shouldBeginEditing(Range*); + virtual bool shouldEndEditing(Range*); + virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction); + virtual bool shouldInsertText(String, Range*, EditorInsertAction); + virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting); + + virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*); +// virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle); +// virtual bool doCommandBySelector(SEL selector); + + virtual void didBeginEditing(); + virtual void respondToChangedContents(); + virtual void respondToChangedSelection(); + virtual void didEndEditing(); + virtual void didWriteSelectionToPasteboard(); + virtual void didSetSelectionTypesForPasteboard(); +// virtual void didChangeTypingStyle:(NSNotification *)notification; +// virtual void didChangeSelection:(NSNotification *)notification; +// virtual NSUndoManager* undoManager:(WebView *)webView; + + virtual void registerCommandForUndo(PassRefPtr<EditCommand>); + virtual void registerCommandForRedo(PassRefPtr<EditCommand>); + virtual void clearUndoRedoOperations(); + + virtual bool canUndo() const; + virtual bool canRedo() const; + + virtual void undo(); + virtual void redo(); + + virtual void textFieldDidBeginEditing(Element*); + virtual void textFieldDidEndEditing(Element*); + virtual void textDidChangeInTextField(Element*); + virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*); + virtual void textWillBeDeletedInTextField(Element*); + virtual void textDidChangeInTextArea(Element*); + + virtual void ignoreWordInSpellDocument(const String&); + virtual void learnWord(const String&); + virtual void checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength); + virtual void checkGrammarOfString(const UChar*, int length, Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength); + virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail& detail); + virtual void updateSpellingUIWithMisspelledWord(const String&); + virtual void showSpellingUI(bool show); + virtual bool spellingUIIsShowing(); + virtual void getGuessesForWord(const String&, Vector<String>& guesses); + virtual bool shouldMoveRangeAfterDelete(Range*, Range*); + virtual void setInputMethodState(bool); + + virtual void handleKeyboardEvent(KeyboardEvent*); + virtual void handleInputMethodKeydown(KeyboardEvent*); + // Android specific: + void setPage(Page* page) { m_page = page; } + void setFromClick(bool fromClick) { m_notFromClick = !fromClick; } +private: + Page* m_page; + bool m_notFromClick; +}; + +} + +#endif diff --git a/WebCore/platform/android/FileChooserAndroid.cpp b/WebCore/platform/android/FileChooserAndroid.cpp new file mode 100644 index 0000000..e613115 --- /dev/null +++ b/WebCore/platform/android/FileChooserAndroid.cpp @@ -0,0 +1,67 @@ +/* libs/WebKitLib/WebKit/WebCore/platform/android/FileChooserAndroid.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "FileChooser.h" +#include "Font.h" +#include "Icon.h" +#include "LocalizedStrings.h" +#include "PlatformString.h" + +namespace WebCore { + +FileChooser::FileChooser(FileChooserClient* client, const String& initialFilename) +{ + m_client = client; + if (initialFilename.length() == 0) + m_filename = fileButtonNoFileSelectedLabel(); + else + m_filename = initialFilename; +} + +FileChooser::~FileChooser() +{ +} + +void FileChooser::openFileChooser(Document* doc) +{ + // FIXME: NEED TO OPEN A FILE CHOOSER OF SOME SORT!! + // When it's chosen, set m_filename, call chooseFile(m_filename) and call chooseIcon(m_filename) +} + +String FileChooser::basenameForWidth(const Font& font, int width) const +{ + // FIXME: This could be a lot faster, but assuming the data will not often be + // much longer than the provided width, this may be fast enough. + String output = m_filename.copy(); + while (font.width(TextRun(output.impl())) > width && output.length() > 4) { + output = output.replace(output.length() - 4, 4, String("...")); + } + return output; + +} + + +// The following two strings are used for File Upload form control, ie +// <input type="file">. The first is the text that appears on the button +// that when pressed, the user can browse for and select a file. The +// second string is rendered on the screen when no file has been selected. +String fileButtonChooseFileLabel() { return String("Uploads Disabled"); } +String fileButtonNoFileSelectedLabel() { return String("No file selected"); } + +} // WebCore + diff --git a/WebCore/platform/android/FileSystemAndroid.cpp b/WebCore/platform/android/FileSystemAndroid.cpp new file mode 100644 index 0000000..1aa8843 --- /dev/null +++ b/WebCore/platform/android/FileSystemAndroid.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007 Holger Hans Peter Freyther + * 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "FileSystem.h" +#include "PlatformString.h" +#include "CString.h" + +namespace WebCore { + +CString fileSystemRepresentation(const String& path) { + return path.utf8(); +} + +} diff --git a/WebCore/platform/android/FrameLoaderClientAndroid.cpp b/WebCore/platform/android/FrameLoaderClientAndroid.cpp new file mode 100644 index 0000000..2319bd8 --- /dev/null +++ b/WebCore/platform/android/FrameLoaderClientAndroid.cpp @@ -0,0 +1,1173 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" + +#include "android_graphics.h" +#include "CString.h" +#include "DocumentLoader.h" +#include "FrameAndroid.h" +#include "FrameLoader.h" +#include "FrameLoaderClientAndroid.h" +#include "FrameTree.h" +#include "GraphicsContext.h" +// HTMLFormElement needed for a bad include +#include "HTMLFormElement.h" +#include "HTMLFrameOwnerElement.h" +#include "IconDatabase.h" +#include "Page.h" +#include "PlatformGraphicsContext.h" +#include "PlatformString.h" +#include "PluginInfoStore.h" +#include "PluginDatabaseAndroid.h" +#include "PluginViewAndroid.h" +#include "ProgressTracker.h" +#include "RenderPart.h" +#include "ResourceError.h" +#include "SelectionController.h" +#include "SkCanvas.h" +#include "SkRect.h" +#include "Document.h" +#include "FrameView.h" +#include "WebCoreViewBridge.h" +#include "HistoryItem.h" +#include "ResourceHandle.h" +#include "ResourceHandleInternal.h" +#include "WebCoreResourceLoader.h" +#include "WebCoreViewBridge.h" +#include "WebHistory.h" +#include "WebIconDatabase.h" +#include "Settings.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include <utils/Log.h> +#include <utils/AssetManager.h> + +//#define notImplemented() LOGV("%s\n", __PRETTY_FUNCTION__) +#define lowPriority_notImplemented() //printf("%s\n", __PRETTY_FUNCTION__) +#define verifiedOk() // not a problem that it's not implemented +static inline void needsBridge(const char name[]) {LOGV("<%s> needs bridge", name);} + +extern android::AssetManager* gGlobalAssetMgr; + +namespace WebCore { + +static const int EXTRA_LAYOUT_DELAY = 1000; + +// FIXME: Need some data for how big this should be. +#define MAX_SESSION_HISTORY 50 +static Vector<KURL> gSessionHistory; + +bool historyContains(const UChar* chars, unsigned len) { + const KURL url(DeprecatedString(reinterpret_cast<const DeprecatedChar*>(chars), len)); + Vector<KURL>::const_iterator end = gSessionHistory.end(); + for (Vector<KURL>::const_iterator i = gSessionHistory.begin(); i != end; ++i) { + if (equalIgnoringRef(url, *i)) + return true; + } + return false; +} + +void FrameLoaderClientAndroid::frameLoaderDestroyed() { + registerForIconNotification(false); + m_frame = 0; + delete this; +} + +bool FrameLoaderClientAndroid::hasWebView() const { + // FIXME, + // there is one web view per page, or top frame. + // as android's view is created from Java side, it is always there. + return true; +} + +bool FrameLoaderClientAndroid::hasFrameView() const { + // FIXME, + // need to revisit for sub-frame case + return true; +} + +bool FrameLoaderClientAndroid::privateBrowsingEnabled() const { + // FIXME, are we going to support private browsing? + notImplemented(); + return false; +} + +void FrameLoaderClientAndroid::makeRepresentation(DocumentLoader*) { + // don't use representation + verifiedOk(); +} + +void FrameLoaderClientAndroid::forceLayout() { + ASSERT(m_frame); + m_frame->forceLayout(); + // FIXME, should we adjust view size here? + m_frame->view()->adjustViewSize(); +} + +void FrameLoaderClientAndroid::forceLayoutForNonHTML() { + notImplemented(); +} + +void FrameLoaderClientAndroid::setCopiesOnScroll() { + // this is a hint about whether we need to force redraws, or can + // just copy the scrolled content. Since we always force a redraw + // anyways, we can ignore this call. + verifiedOk(); +} + +void FrameLoaderClientAndroid::detachedFromParent2() { + // FIXME, ready to detach frame from view +} + +void FrameLoaderClientAndroid::detachedFromParent3() { + // FIXME, ready to release view + notImplemented(); +} + +void FrameLoaderClientAndroid::detachedFromParent4() { + // FIXME, ready to release view + notImplemented(); +} + +void FrameLoaderClientAndroid::loadedFromPageCache() { + // don't support page cache + verifiedOk(); +} + +// This function is responsible for associating the "id" with a given +// subresource load. The following functions that accept an "id" are +// called for each subresource, so they should not be dispatched to the m_frame. +void FrameLoaderClientAndroid::assignIdentifierToInitialRequest(unsigned long id, + DocumentLoader*, const ResourceRequest&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchWillSendRequest(DocumentLoader*, unsigned long id, + ResourceRequest&, const ResourceResponse&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, + unsigned long id, const AuthenticationChallenge&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidCancelAuthenticationChallenge(DocumentLoader*, + unsigned long id, const AuthenticationChallenge&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveResponse(DocumentLoader*, + unsigned long id, const ResourceResponse&) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveContentLength(DocumentLoader*, + unsigned long id, int lengthReceived) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidFinishLoading(DocumentLoader*, + unsigned long id) { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidFailLoading(DocumentLoader* docLoader, + unsigned long id, const ResourceError&) { + lowPriority_notImplemented(); +} + +bool FrameLoaderClientAndroid::dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, + const ResourceRequest&, const ResourceResponse&, int length) { + notImplemented(); + return false; +} + +void FrameLoaderClientAndroid::dispatchDidHandleOnloadEvents() { +} + +void FrameLoaderClientAndroid::dispatchDidReceiveServerRedirectForProvisionalLoad() { + ASSERT(m_frame); + // Tell the load it was a redirect. + m_frame->bridge()->loadStarted(m_frame); +} + +void FrameLoaderClientAndroid::dispatchDidCancelClientRedirect() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchWillPerformClientRedirect(const KURL&, + double interval, double fireDate) { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidChangeLocationWithinPage() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchWillClose() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveIcon() { + ASSERT(m_frame); + if (m_frame->tree() && m_frame->tree()->parent()) + return; + WebCore::String url(m_frame->loader()->url().string()); + WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(url, WebCore::IntSize(16,16)); + // There is a bug in webkit where cancelling an icon load is treated as a + // failure. When this is fixed, we can ASSERT again that we have an icon. + if (icon) { + m_frame->bridge()->didReceiveIcon(icon); + LOGV("Received icon (%p) for %s", icon, + m_frame->loader()->url().deprecatedString().ascii()); + } +} + +void FrameLoaderClientAndroid::dispatchDidStartProvisionalLoad() { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidReceiveTitle(const String& title) { + ASSERT(m_frame); + // Used to check for FrameLoadTypeStandard but we only want to send the title for + // the top frame and not sub-frames. + if (!m_frame->tree() || !m_frame->tree()->parent()) { + m_frame->bridge()->setTitle(title); + } +} + +void FrameLoaderClientAndroid::dispatchDidCommitLoad() { + ASSERT(m_frame); + m_frame->view()->getWebCoreViewBridge()->updateFrameGeneration(m_frame); +} + +void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) { + ASSERT(m_frame); + // Ignore ErrorInterrupted since it is due to a policy interruption. This + // is caused by a decision to download the main resource rather than + // display it. + if (error.errorCode() == InternalErrorInterrupted + || error.errorCode() == InternalErrorCancelled) + return; + + if (!gGlobalAssetMgr) { + gGlobalAssetMgr = new android::AssetManager(); + gGlobalAssetMgr->addDefaultAssets(); + } + + // Check to see if the error code was not generated internally + const char* errorPage = "webkit/nodomain.html"; + if ((error.errorCode() == ErrorFile || + error.errorCode() == ErrorFileNotFound) && + (!error.localizedDescription().isEmpty())) { + errorPage = "webkit/loaderror.html"; + } + + // Grab the error page from the asset manager + android::Asset* a = gGlobalAssetMgr->open(errorPage, + android::Asset::ACCESS_BUFFER); + if (!a) + return; + + // Take the failing url and encode html entities so javascript urls are not + // executed. + CString failingUrl = error.failingURL().utf8(); + Vector<char> url; + int len = failingUrl.length(); + const char* data = failingUrl.data(); + for (int i = 0; i < len; i++) { + char c = data[i]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9')) + url.append(c); + else { + char buf[16]; + int res = sprintf(buf, "&#%d;", c); + buf[res] = 0; + url.append(buf, res); + } + } + + // Replace all occurances of %s with the failing url. + String s = String((const char*)a->getBuffer(false), a->getLength()); + s = s.replace("%s", String(url.data(), url.size())); + + // Replace all occurances of %e with the error text + s = s.replace("%e", error.localizedDescription()); + + // Create the request and the substitute data and tell the FrameLoader to + // load with the replacement data. + ResourceRequest request(error.failingURL()); + CString cstr = s.utf8(); + RefPtr<SharedBuffer> buf = new SharedBuffer(cstr.data(), cstr.length()); + SubstituteData subData(buf, String("text/html"), String("utf-8"), + request.url()); + m_frame->loader()->load(request, subData); + + // Delete the asset. + delete a; +} + +void FrameLoaderClientAndroid::dispatchDidFailLoad(const ResourceError&) { + // called when page is completed with error + lowPriority_notImplemented(); + ASSERT(m_frame); + m_frame->document()->setExtraLayoutDelay(0); +} + +void FrameLoaderClientAndroid::dispatchDidFinishDocumentLoad() { + // called when finishedParsing + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDidFinishLoad() { + ASSERT(m_frame); + m_frame->document()->setExtraLayoutDelay(0); + m_frame->bridge()->didFinishLoad(m_frame); +} + +void FrameLoaderClientAndroid::dispatchDidFirstLayout() { + ASSERT(m_frame); + m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY); + // FIXME: Need to figure out if we need didLayout or didFirstLayout + // see WebViewCore::didLayout + m_frame->view()->getWebCoreViewBridge()->didFirstLayout(); +} + +Frame* FrameLoaderClientAndroid::dispatchCreatePage() { + ASSERT(m_frame); + if (m_frame->settings()->supportMultipleWindows()) + // Always a user gesture since window.open maps to + // ChromeClientAndroid::createWindow + return m_frame->bridge()->createWindow(false, true); + else + // If the client doesn't support multiple windows, just replace the + // current frame's contents. + return m_frame; +} + +void FrameLoaderClientAndroid::dispatchShow() { + ASSERT(m_frame); + m_frame->view()->invalidate(); +} + + +static bool TreatAsAttachment(const String& content_disposition) { + // Some broken sites just send + // Content-Disposition: ; filename="file" + // screen those out here. + if (content_disposition.startsWith(";")) + return false; + + if (content_disposition.startsWith("inline", false)) + return false; + + // Some broken sites just send + // Content-Disposition: filename="file" + // without a disposition token... screen those out. + if (content_disposition.startsWith("filename", false)) + return false; + + // Also in use is Content-Disposition: name="file" + if (content_disposition.startsWith("name", false)) + return false; + + // We have a content-disposition of "attachment" or unknown. + // RFC 2183, section 2.8 says that an unknown disposition + // value should be treated as "attachment" + return true; +} + +void FrameLoaderClientAndroid::dispatchDecidePolicyForMIMEType(FramePolicyFunction func, + const String& MIMEType, const ResourceRequest&) { + ASSERT(m_frame); + ASSERT(func); + // Default to Use (display internally). + PolicyAction action = PolicyUse; + // Check if we should Download instead. + const ResourceResponse& response = m_frame->loader()->activeDocumentLoader()->response(); + const String& content_disposition = response.httpHeaderField("Content-Disposition"); + if (!content_disposition.isEmpty()) { + // Server wants to override our normal policy. + if (TreatAsAttachment(content_disposition)) { + // Check to see if we are a sub frame (main frame has no owner element) + if (m_frame->ownerElement() != 0) + action = PolicyIgnore; + else + action = PolicyDownload; + } + } else { + // Ask if it can be handled internally. + if (!canShowMIMEType(MIMEType)) { + // Check to see if we are a sub frame (main frame has no owner element) + if (m_frame->ownerElement() != 0) + action = PolicyIgnore; + else + action = PolicyDownload; + } + } + // A status code of 204 indicates no content change. Ignore the result. + WebCore::DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader(); + if (docLoader->response().httpStatusCode() == 204) + action = PolicyIgnore; + (m_frame->loader()->*func)(action); +} + +void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func, + const NavigationAction&, const ResourceRequest& req, const String& frameName) { + ASSERT(m_frame); + // If we get to this point it means that a link has a target that was not + // found by the frame tree. Instead of creating a new frame, return the + // current frame in dispatchCreatePage. + if (canHandleRequest(req)) + (m_frame->loader()->*func)(PolicyUse); + else + (m_frame->loader()->*func)(PolicyIgnore); +} + +void FrameLoaderClientAndroid::cancelPolicyCheck() { + lowPriority_notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchUnableToImplementPolicy(const ResourceError&) { + notImplemented(); +} + +void FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction(FramePolicyFunction func, + const NavigationAction& action, const ResourceRequest& request) { + ASSERT(m_frame); + ASSERT(func); + if (action.type() == NavigationTypeFormResubmitted) { + m_frame->bridge()->decidePolicyForFormResubmission(func); + return; + } else { + (m_frame->loader()->*func)(PolicyUse); + } +} + +void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, PassRefPtr<FormState>) { + ASSERT(m_frame); + ASSERT(func); + (m_frame->loader()->*func)(PolicyUse); +} + +void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) { + notImplemented(); +} + +void FrameLoaderClientAndroid::revertToProvisionalState(DocumentLoader*) { + notImplemented(); +} + +void FrameLoaderClientAndroid::setMainDocumentError(DocumentLoader* docLoader, const ResourceError& error) { + ASSERT(m_frame); + if (!error.isNull() && error.errorCode() >= InternalErrorLast) + m_frame->bridge()->reportError(error.errorCode(), + error.localizedDescription(), error.failingURL()); +} + +void FrameLoaderClientAndroid::clearUnarchivingState(DocumentLoader*) { + notImplemented(); +} + +// This function is called right before the progress is updated. +void FrameLoaderClientAndroid::willChangeEstimatedProgress() { + verifiedOk(); +} + +// This function is called after the progress has been updated. The bad part +// about this is that when a page is completed, this function is called after +// the progress has been reset to 0. +void FrameLoaderClientAndroid::didChangeEstimatedProgress() { + verifiedOk(); +} + +// This will give us the initial estimate when the page first starts to load. +void FrameLoaderClientAndroid::postProgressStartedNotification() { + ASSERT(m_frame); + if (m_frame->page()) + m_frame->bridge()->setProgress(m_frame->page()->progress()->estimatedProgress()); +} + +// This will give us any updated progress including the final progress. +void FrameLoaderClientAndroid::postProgressEstimateChangedNotification() { + ASSERT(m_frame); + if (m_frame->page()) + m_frame->bridge()->setProgress(m_frame->page()->progress()->estimatedProgress()); +} + +// This is just a notification that the progress has finished. Don't call +// setProgress(1) because postProgressEstimateChangedNotification will do so. +void FrameLoaderClientAndroid::postProgressFinishedNotification() { + m_frame->view()->getWebCoreViewBridge()->notifyProgressFinished(); +} + +void FrameLoaderClientAndroid::setMainFrameDocumentReady(bool) { + // this is only interesting once we provide an external API for the DOM + notImplemented(); +} + +void FrameLoaderClientAndroid::startDownload(const ResourceRequest&) { + notImplemented(); +} + +void FrameLoaderClientAndroid::willChangeTitle(DocumentLoader*) { + verifiedOk(); +} + +void FrameLoaderClientAndroid::didChangeTitle(DocumentLoader* loader) { + verifiedOk(); +} + +void FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader) { + // Telling the frame we received some data and passing 0 as the data is our + // way to get work done that is normally done when the first bit of data is + // received, even for the case of a document with no data (like about:blank) + committedLoad(docLoader, 0, 0); +} + +void FrameLoaderClientAndroid::finalSetupForReplace(DocumentLoader*) { + notImplemented(); +} + +void FrameLoaderClientAndroid::updateGlobalHistoryForStandardLoad(const KURL& url) { + ASSERT(m_frame); + const DeprecatedString dStr = url.deprecatedString(); + if (!historyContains(reinterpret_cast<const UChar*>(dStr.unicode()), dStr.length())) { + if (gSessionHistory.size() == MAX_SESSION_HISTORY) + gSessionHistory.remove(0); + gSessionHistory.append(url); + } + m_frame->bridge()->updateVisitedHistory(url, false); +} + +void FrameLoaderClientAndroid::updateGlobalHistoryForReload(const KURL& url) { + ASSERT(m_frame); + m_frame->bridge()->updateVisitedHistory(url, true); +} + +bool FrameLoaderClientAndroid::shouldGoToHistoryItem(HistoryItem* item) const { + // hmmm, seems like we might do a more thoughtful check + ASSERT(m_frame); + return item != NULL; +} + +void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) { + ASSERT(m_frame); + String encoding = loader->overrideEncoding(); + bool userChosen = !encoding.isNull(); + if (encoding.isNull()) + encoding = loader->response().textEncodingName(); + loader->frameLoader()->setEncoding(encoding, userChosen); + Document *doc = m_frame->document(); + if (doc) { + loader->frameLoader()->addData(data, length); + } +} + +ResourceError FrameLoaderClientAndroid::cancelledError(const ResourceRequest& request) { + return ResourceError(String(), InternalErrorCancelled, String(), String()); +} + +ResourceError FrameLoaderClientAndroid::cannotShowURLError(const ResourceRequest& request) { + return ResourceError(String(), InternalErrorCannotShowUrl, String(), String()); +} + +ResourceError FrameLoaderClientAndroid::interruptForPolicyChangeError(const ResourceRequest& request) { + return ResourceError(String(), InternalErrorInterrupted, String(), String()); +} + +ResourceError FrameLoaderClientAndroid::cannotShowMIMETypeError(const ResourceResponse& request) { + return ResourceError(String(), InternalErrorCannotShowMimeType, String(), String()); +} + +ResourceError FrameLoaderClientAndroid::fileDoesNotExistError(const ResourceResponse& request) { + return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String()); +} + +bool FrameLoaderClientAndroid::shouldFallBack(const ResourceError&) { + notImplemented(); + return false; +} + +void FrameLoaderClientAndroid::setDefersLoading(bool) { + notImplemented(); +} + +bool FrameLoaderClientAndroid::willUseArchive(ResourceLoader*, const ResourceRequest&, + const KURL& originalURL) const { + lowPriority_notImplemented(); + return false; +} + +bool FrameLoaderClientAndroid::isArchiveLoadPending(ResourceLoader*) const { + lowPriority_notImplemented(); + return false; +} + +void FrameLoaderClientAndroid::cancelPendingArchiveLoad(ResourceLoader*) { + notImplemented(); +} + +void FrameLoaderClientAndroid::clearArchivedResources() { + notImplemented(); +} + +bool FrameLoaderClientAndroid::canHandleRequest(const ResourceRequest& request) const { + ASSERT(m_frame); + // Don't allow hijacking of intrapage navigation + if (WebCore::equalIgnoringRef(request.url(), m_frame->loader()->url())) + return true; + + // Don't allow hijacking of iframe urls that are http or https + if (request.url().protocol().startsWith("http", false) && + m_frame->tree() && m_frame->tree()->parent()) + return true; + + if (m_frame->bridge()->canHandleRequest(request)) { +#ifdef ANDROID_META_SUPPORT + // reset metadata settings for the top frame as they are not preserved cross page + if (!m_frame->tree()->parent()) + m_frame->settings()->resetMetadataSettings(); +#endif + return true; + } + return false; +} + +bool FrameLoaderClientAndroid::canShowMIMEType(const String& mimeType) const { + // FIXME: This looks like it has to do with whether or not a type can be + // shown "internally" (i.e. inside the browser) regardless of whether + // or not the browser is doing the rendering, e.g. a full page plugin. + if (mimeType.startsWith("text/plain", false) || + mimeType.startsWith("image/jpeg", false) || // Be specific with supported image formats + mimeType.startsWith("image/gif", false) || + mimeType.startsWith("image/png", false) || + mimeType.startsWith("image/bmp", false) || + mimeType.startsWith("image/x-icon", false) || + mimeType.startsWith("image/ico", false) || + mimeType.endsWith("/xml", false) || // text/xml & application/xml + mimeType.endsWith("xhtml+xml", false) || // application/xhtml+xml + // Checked Safari impl, it seems that the HTTP stack returns + // multiple responses, the initial response, and then one for + // multipart segment. Each response is sent to the same ResourceLoader + // so for us to support this we would need to do the same. + // mimeType.startsWith("multipart/x-mixed-replace", false) || + mimeType.endsWith("html", false)) + return true; + return false; +} + +bool FrameLoaderClientAndroid::representationExistsForURLScheme(const String&) const { + // don't use representation + verifiedOk(); + return false; +} + +String FrameLoaderClientAndroid::generatedMIMETypeForURLScheme(const String& URLScheme) const { + // FIXME, copy from Apple's port + String mimetype("x-apple-web-kit/"); + mimetype.append(URLScheme.lower()); + return mimetype; +} + +void FrameLoaderClientAndroid::frameLoadCompleted() { + // copied from Apple port, without this back with sub-frame will trigger ASSERT + ASSERT(m_frame); + m_frame->loader()->setPreviousHistoryItem(0); +} + +void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) { +#ifdef ANDROID_HISTORY_CLIENT + ASSERT(m_frame); + ASSERT(item); + // We should have added a bridge when the child item was added to its + // parent. + android::WebHistoryItem* bridge = item->bridge(); + ASSERT(bridge); + // store the current scale + bridge->setScale(m_frame->view()->getWebCoreViewBridge()->scale()); + + // Store the location of the focus, based on how many traversals it + // takes to reach the focus from the document. + WebCore::Document* doc = m_frame->document(); + WebCore::Node* focus = doc->focusedNode(); + if (focus) { + int focusNum = 0; + WebCore::Node* node = doc; + while (node != focus) { + node = node->traverseNextNode(doc); + ASSERT(node); + focusNum++; + } + bridge->setTraversals(focusNum); + DEBUG_NAV_UI_LOGD("%s focus=%p traversals=%d", __FUNCTION__, focus, focusNum); + } else { + bridge->setTraversals(-1); + DEBUG_NAV_UI_LOGD("%s focus=NULL traversals=-1", __FUNCTION__); + } + WebCore::notifyHistoryItemChanged(item); +#endif +} + +void FrameLoaderClientAndroid::restoreViewState() { +#ifdef ANDROID_HISTORY_CLIENT + HistoryItem* item = m_frame->loader()->currentHistoryItem(); + // restore the scale + m_frame->view()->getWebCoreViewBridge()->restoreScale(item->bridge()->scale()); + // restore the focus + if (m_frame->loader()->firstLayoutDone() == false) + return; + int traversals = item->bridge()->traversals(); + if (traversals < 0) + return; + WebCore::Document* doc = m_frame->document(); + WebCore::Node* node = doc; + while (--traversals >= 0) { + if ((node = node->traverseNextNode(doc)) == NULL) + return; + } + if (doc->focusedNode() == node) + return; + DEBUG_NAV_UI_LOGD("%s focus=%p", __FUNCTION__, node); + doc->setFocusedNode(node); + m_frame->view()->getWebCoreViewBridge()->notifyFocusSet(); +#endif +} + +#ifdef ANDROID_HISTORY_CLIENT +void FrameLoaderClientAndroid::dispatchDidAddHistoryItem(HistoryItem* item) const { + ASSERT(m_frame); + m_frame->bridge()->addHistoryItem(item); +} + +void FrameLoaderClientAndroid::dispatchDidRemoveHistoryItem(HistoryItem* item, int index) const { + ASSERT(m_frame); + m_frame->bridge()->removeHistoryItem(index); +} + +void FrameLoaderClientAndroid::dispatchDidChangeHistoryIndex( + BackForwardList* list) const { + ASSERT(m_frame); + m_frame->bridge()->updateHistoryIndex(list->backListCount()); +} +#endif + +void FrameLoaderClientAndroid::provisionalLoadStarted() { + ASSERT(m_frame); + m_frame->bridge()->loadStarted(m_frame); +} + +void FrameLoaderClientAndroid::didFinishLoad() { + ASSERT(m_frame); + m_frame->bridge()->didFinishLoad(m_frame); +} + +void FrameLoaderClientAndroid::prepareForDataSourceReplacement() { + ASSERT(m_frame); + m_frame->loader()->detachChildren(); +} + +PassRefPtr<DocumentLoader> FrameLoaderClientAndroid::createDocumentLoader( + const ResourceRequest& request, const SubstituteData& data) { + RefPtr<DocumentLoader> loader = new DocumentLoader(request, data); + return loader.release(); +} + +void FrameLoaderClientAndroid::setTitle(const String& title, const KURL& url) { + // Not needed. dispatchDidReceiveTitle is called immediately after this. + // url is used to update the Apple port history items. + verifiedOk(); +} + +String FrameLoaderClientAndroid::userAgent(const KURL& u) { + ASSERT(m_frame); + return m_frame->bridge()->userAgentForURL(&u); +} + +bool FrameLoaderClientAndroid::canCachePage() const { + return true; +} + +void FrameLoaderClientAndroid::download(ResourceHandle* handle, const ResourceRequest&, + const ResourceRequest&, const ResourceResponse&) { + // Get the C++ side of the load listener and tell it to handle the download + android::WebCoreResourceLoader* loader = handle->getInternal()->m_loader; + loader->downloadFile(); +} + +class ChildFrameViewBridge : public WebCoreViewBridge +{ +public: + ChildFrameViewBridge(WebCoreViewBridge* parent, WebCore::FrameAndroid* frame) + : mFrame(frame) + { + setParent(parent); + mFrame->ref(); + } + + virtual ~ChildFrameViewBridge() + { + mFrame->deref(); + } + + virtual void draw(WebCore::GraphicsContext* ctx, + const WebCore::IntRect& rect, bool) + { + // Create a new translated rect from the given rectangle. + WebCore::IntRect transRect(rect); + + // Grab the intersection of transRect and the frame's bounds. + transRect.intersect(this->getBounds()); + + // Move the transRect into the frame's local coordinates. + transRect.move(-this->locX(), -this->locY()); + + // If the rect is non-empty, translate the canvas, add a clip and draw. + SkRect r; + android_setrect(&r, transRect); + if (r.isEmpty()) + return; + // In Frame::markAllMatchesForText(), it does a fake paint. So we need + // to handle the case where platformContext() is null. However, we still + // want to call paint, since WebKit must have called the paint for a reason. + SkCanvas* canvas = ctx->platformContext() ? ctx->platformContext()->mCanvas : NULL; + if (canvas) { + canvas->save(); + canvas->translate(SkIntToScalar(this->locX()), SkIntToScalar(this->locY())); + canvas->clipRect(r); + } + mFrame->paint(ctx, transRect); + if (canvas) + canvas->restore(); + } + + // Bubble up Javascript dialogs to the parent. The top level view will + // take care of displaying them to the user. + virtual void jsAlert(const WebCore::String& url, const WebCore::String& text) + { + getParent()->jsAlert(url, text); + } + virtual bool jsConfirm(const WebCore::String& url, const WebCore::String& text) + { + return getParent()->jsConfirm(url, text); + } + virtual bool jsPrompt(const WebCore::String& url, + const WebCore::String& message, + const WebCore::String& defaultValue, + WebCore::String& result) + { + return getParent()->jsPrompt(url, message, defaultValue, result); + } + +private: + WebCore::FrameAndroid* mFrame; + typedef WebCoreViewBridge INHERITED; +}; + +WTF::PassRefPtr<WebCore::Frame> FrameLoaderClientAndroid::createFrame(const KURL& url, const String& name, + HTMLFrameOwnerElement* ownerElement, const String& referrer, + bool allowsScrolling, int marginWidth, int marginHeight) +{ + Frame* parent = ownerElement->document()->frame(); + FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid; + WebCore::FrameAndroid* newFrame = new WebCore::FrameAndroid(parent->page(), ownerElement, loaderC); + loaderC->setFrame(newFrame); + // Append the subframe to the parent and set the name of the subframe. The name must be set after + // appending the child so that the name becomes unique. + parent->tree()->appendChild(newFrame); + newFrame->tree()->setName(name); + newFrame->setBridge(Android(parent)->bridge()); + // Create a new FrameView and bridge for the child frame to draw into. + WebCore::FrameView* frameView = new WebCore::FrameView(newFrame); + ChildFrameViewBridge* view = new ChildFrameViewBridge(parent->view()->getWebCoreViewBridge(), newFrame); + frameView->setWebCoreViewBridge(view); + // Unref the viewBridge so that frameView is the only reference holder. + Release(view); + newFrame->setView(frameView); + // Frames are initiliazed with a ref count of 1 but since our view bridge is holding onto the frame, deref + // it here. setView also refs the frameView so call deref on the frameView as well. + newFrame->deref(); + frameView->deref(); + newFrame->init(); + newFrame->selectionController()->setFocused(true); + LOGV("::WebCore:: createSubFrame returning %p", newFrame); + + HistoryItem* item = parent->loader()->currentHistoryItem(); + KURL childUrl = url; + FrameLoadType loadType = parent->loader()->loadType(); + FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedHistory; + // If we are moving in the back/forward list, we might want to replace the + // content of this child frame with whatever was there at that point. Reload + // will maintain the frame contents, LoadSame will not. + HistoryItem* childItem = NULL; + if (item && item->children().size() && + (isBackForwardLoadType(loadType) || + loadType == FrameLoadTypeReload || + loadType == FrameLoadTypeReloadAllowingStaleData)) { + childItem = item->childItemWithName(newFrame->tree()->name()); + if (childItem) { + // Use the original url to ensure we get all the side-effects, such + // as onLoad handlers or redirects. + childUrl = childItem->originalURL(); + childLoadType = loadType; + if (isBackForwardLoadType(loadType)) + // For back/forward, remember this item so we can traverse any + // child items as child frames load. + newFrame->loader()->setProvisionalHistoryItem(childItem); + else + // For reload, just reinstall the current item, since a new + // child frame was created but we won't be creating a new + // history item. + newFrame->loader()->setCurrentHistoryItem(childItem); + } + } + RefPtr<Frame> autoFrame = newFrame; +#ifdef ANDROID_FIX + if (childItem && childItem->originalFormData()) + autoFrame->loader()->postFromHistory(childUrl, childItem->originalFormData(), + childItem->originalFormContentType(), childItem->originalFormReferrer(), childLoadType); + else +#endif +#ifdef ANDROID_USER_GESTURE + autoFrame->loader()->load(childUrl, referrer, childLoadType, + String(), NULL, NULL, false); +#else + autoFrame->loader()->load(childUrl, referrer, childLoadType, + String(), NULL, NULL); +#endif + + // onLoad may cuase the frame to be removed from the document. Allow the RefPtr to delete the child frame. + if (!autoFrame->tree()->parent()) + return NULL; + + return autoFrame.release(); +} + +// YouTube flash url path starts with /v/ +static const char slash_v_slash[] = { '/', 'v', '/' }; + +static bool isValidYouTubeVideo(const DeprecatedString& path) +{ + if (!path.isAllASCII()) + return false; + unsigned int len = path.length(); + if (len <= sizeof(slash_v_slash)) // check for more than just /v/ + return false; + DeprecatedString str = path.lower(); + const char* data = str.ascii(); + if (memcmp(data, slash_v_slash, sizeof(slash_v_slash)) != 0) + return false; + // Start after /v/ + for (unsigned int i = sizeof(slash_v_slash); i < len; i++) { + char c = data[i]; + // Check for alpha-numeric characters only. + if (WTF::isASCIIAlphanumeric(c)) + continue; + // The url can have more parameters such as &hl=en after the video id. + // Once we start seeing extra parameters we can return true. + return c == '&' && i > sizeof(slash_v_slash); + } + return true; +} + +static bool isYouTubeUrl(const KURL& url, const String& mimeType) +{ + return url.host().endsWith("youtube.com") && isValidYouTubeVideo(url.path()) + && equalIgnoringCase(mimeType, "application/x-shockwave-flash"); +} + +Widget* FrameLoaderClientAndroid::createPlugin( + const IntSize& size, + Element* element, + const KURL& url, + const WTF::Vector<String, 0u>& names, + const WTF::Vector<String, 0u>& values, + const String& mimeType, + bool loadManually) { + // Create an iframe for youtube urls. + if (isYouTubeUrl(url, mimeType)) { + DeprecatedString str("file:///android_asset/webkit/youtube.html?v="); + str.append(url.path().mid(sizeof(slash_v_slash))); // grab everything after /v/ + RefPtr<Frame> frame = createFrame(KURL(str), String(), static_cast<HTMLFrameOwnerElement*>(element), + String(), false, 0, 0); + return frame ? frame->view() : NULL; + } + PluginViewAndroid* v = + PluginDatabaseAndroid::installedPlugins()->createPluginView( + m_frame, + size, + element, + url, + names, + values, + mimeType, + loadManually); + return v; +} + +void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) { + // don't support plugin yet + notImplemented(); +} + +Widget* FrameLoaderClientAndroid::createJavaAppletWidget(const IntSize&, Element*, + const KURL& baseURL, const Vector<String>& paramNames, + const Vector<String>& paramValues) { + // don't support widget yet + notImplemented(); + return 0; +} + +// This function is used by the <OBJECT> element to determine the type of +// the contents and work out if it can render it. +ObjectContentType FrameLoaderClientAndroid::objectContentType(const KURL& url, + const String& mimeType) { + if (mimeType.length() == 0) + { + // Guess the mimeType from the extension + if (url.hasPath()) + { + DeprecatedString path = url.path(); + const char* ascii = path.ascii(); + const char* lastIndex = strrchr(ascii, '.'); + static const String image("image/"); + if (lastIndex) + { + String mime(lastIndex + 1); + mime.insert(image, 0); + if (Image::supportsType(mime)) + return ObjectContentImage; + } + } + return ObjectContentFrame; + } + if (equalIgnoringCase(mimeType, "text/html") || + equalIgnoringCase(mimeType, "text/xml") || + equalIgnoringCase(mimeType, "text/") || + equalIgnoringCase(mimeType, "application/xml") || + equalIgnoringCase(mimeType, "application/xhtml+xml") || + equalIgnoringCase(mimeType, "application/x-javascript")) + return ObjectContentFrame; + if (Image::supportsType(mimeType)) + return ObjectContentImage; + return ObjectContentNone; +} + +// This function allows the application to set the correct CSS media +// style. Android could use it to set the media style 'handheld'. Safari +// may use it to set the media style to 'print' when the user wants to print +// a particular web page. +String FrameLoaderClientAndroid::overrideMediaType() const { + lowPriority_notImplemented(); + return String(); +} + +// This function is used to re-attach Javascript<->native code classes. +void FrameLoaderClientAndroid::windowObjectCleared() { + ASSERT(m_frame); + LOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n", + m_frame, m_frame->loader()->url().string().ascii().data()); + m_frame->bridge()->windowObjectCleared(m_frame); +} + +// functions new to Jun-07 tip of tree merge: +ResourceError FrameLoaderClientAndroid::blockedError(ResourceRequest const& request) { + return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String()); +} + +// functions new to Nov-07 tip of tree merge: +void FrameLoaderClientAndroid::didPerformFirstNavigation() const { + // This seems to be just a notification that the UI can listen to, to + // know if the user has performed first navigation action. + // It is called from + // void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) + // "Navigation" here means a transition from one page to another that + // ends up in the back/forward list. +} + +void FrameLoaderClientAndroid::registerForIconNotification(bool listen) { + if (listen) + android::WebIconDatabase::RegisterForIconNotification(this); + else + android::WebIconDatabase::UnregisterForIconNotification(this); +} + +// This is the WebIconDatabaseClient method for receiving a notification when we +// get the icon for the page. +void FrameLoaderClientAndroid::didAddIconForPageUrl(const String& pageUrl) { + registerForIconNotification(false); + KURL u(pageUrl.deprecatedString()); + if (equalIgnoringRef(u, m_frame->loader()->url())) { + dispatchDidReceiveIcon(); + } +} + +// functions new to Feb-19 tip of tree merge: +// According to the changelog: +// The very Mac-centric "makeDocumentView", "setDocumentViewFromCachedPage", +// and "saveDocumentViewToCachedPage" become "transitionToCommittedForNewPage", +// "transitionToCommittedFromCachedPage", and "savePlatformDataToCachedPage" +// accordingly +void FrameLoaderClientAndroid::savePlatformDataToCachedPage(CachedPage*) { + // don't support page cache + verifiedOk(); +} + +void FrameLoaderClientAndroid::transitionToCommittedFromCachedPage(CachedPage*) { + // don't support page cache + verifiedOk(); +} + +void FrameLoaderClientAndroid::transitionToCommittedForNewPage() { + ASSERT(m_frame); + if (!m_frame->page()->settings()->usesPageCache()) { + m_frame->bridge()->transitionToCommitted(m_frame); + return; + } + + // Remember the old bridge + WebCoreViewBridge* bridge = m_frame->view()->getWebCoreViewBridge(); + Retain(bridge); + + // Remove the old FrameView + m_frame->setView(NULL); + + // Create a new one and set the bridge + FrameView* view = new FrameView(m_frame); + view->setWebCoreViewBridge(bridge); + Release(bridge); + bridge->setView(view); + + // Give the new FrameView to the Frame + m_frame->setView(view); + + // Deref since FrameViews are created with a ref of 1 + view->deref(); + + if (m_frame->ownerRenderer()) + m_frame->ownerRenderer()->setWidget(view); + + m_frame->view()->initScrollbars(); + + m_frame->bridge()->transitionToCommitted(m_frame); +} + +} diff --git a/WebCore/platform/android/FrameLoaderClientAndroid.h b/WebCore/platform/android/FrameLoaderClientAndroid.h new file mode 100644 index 0000000..df4657a --- /dev/null +++ b/WebCore/platform/android/FrameLoaderClientAndroid.h @@ -0,0 +1,216 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef FrameLoaderClientAndroid_h +#define FrameLoaderClientAndroid_h + +#include "FrameLoaderClient.h" +#include "ResourceResponse.h" +#include "WebIconDatabase.h" + +namespace WebCore { + class FrameAndroid; + + class FrameLoaderClientAndroid : public FrameLoaderClient, + android::WebIconDatabaseClient { + public: + FrameLoaderClientAndroid() : m_frame(NULL) {} + void setFrame(FrameAndroid* frame) { m_frame = frame; } + + virtual void frameLoaderDestroyed(); + + virtual bool hasWebView() const; // mainly for assertions + virtual bool hasFrameView() const; // ditto + + virtual bool privateBrowsingEnabled() const; + + virtual void makeRepresentation(DocumentLoader*); + virtual void forceLayout(); + virtual void forceLayoutForNonHTML(); + + virtual void setCopiesOnScroll(); + + virtual void detachedFromParent2(); + virtual void detachedFromParent3(); + virtual void detachedFromParent4(); + + virtual void loadedFromPageCache(); + + virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&); + + virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); + virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&); + virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&); + virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&); + virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived); + virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier); + virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&); + virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length); + + virtual void dispatchDidHandleOnloadEvents(); + virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(); + virtual void dispatchDidCancelClientRedirect(); + virtual void dispatchWillPerformClientRedirect(const KURL&, double interval, double fireDate); + virtual void dispatchDidChangeLocationWithinPage(); + virtual void dispatchWillClose(); + virtual void dispatchDidReceiveIcon(); + virtual void dispatchDidStartProvisionalLoad(); + virtual void dispatchDidReceiveTitle(const String& title); + virtual void dispatchDidCommitLoad(); + virtual void dispatchDidFailProvisionalLoad(const ResourceError&); + virtual void dispatchDidFailLoad(const ResourceError&); + virtual void dispatchDidFinishDocumentLoad(); + virtual void dispatchDidFinishLoad(); + virtual void dispatchDidFirstLayout(); + + virtual Frame* dispatchCreatePage(); + virtual void dispatchShow(); + + virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&); + virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, const String& frameName); + virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&); + virtual void cancelPolicyCheck(); + + virtual void dispatchUnableToImplementPolicy(const ResourceError&); + + virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>); + + virtual void dispatchDidLoadMainResource(DocumentLoader*); + virtual void revertToProvisionalState(DocumentLoader*); + virtual void setMainDocumentError(DocumentLoader*, const ResourceError&); + virtual void clearUnarchivingState(DocumentLoader*); + + virtual void willChangeEstimatedProgress(); + virtual void didChangeEstimatedProgress(); + virtual void postProgressStartedNotification(); + virtual void postProgressEstimateChangedNotification(); + virtual void postProgressFinishedNotification(); + + virtual void setMainFrameDocumentReady(bool); + + virtual void startDownload(const ResourceRequest&); + + virtual void willChangeTitle(DocumentLoader*); + virtual void didChangeTitle(DocumentLoader*); + + virtual void committedLoad(DocumentLoader*, const char*, int); + virtual void finishedLoading(DocumentLoader*); + virtual void finalSetupForReplace(DocumentLoader*); + + virtual void updateGlobalHistoryForStandardLoad(const KURL&); + virtual void updateGlobalHistoryForReload(const KURL&); + virtual bool shouldGoToHistoryItem(HistoryItem*) const; +#ifdef ANDROID_HISTORY_CLIENT + virtual void dispatchDidAddHistoryItem(HistoryItem*) const; + virtual void dispatchDidRemoveHistoryItem(HistoryItem*, int) const; + virtual void dispatchDidChangeHistoryIndex(BackForwardList*) const; +#endif + + virtual ResourceError cancelledError(const ResourceRequest&); + virtual ResourceError blockedError(const ResourceRequest&); + virtual ResourceError cannotShowURLError(const ResourceRequest&); + virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&); + + virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&); + virtual ResourceError fileDoesNotExistError(const ResourceResponse&); + + virtual bool shouldFallBack(const ResourceError&); + + virtual void setDefersLoading(bool); + + virtual bool willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL& originalURL) const; + virtual bool isArchiveLoadPending(ResourceLoader*) const; + virtual void cancelPendingArchiveLoad(ResourceLoader*); + virtual void clearArchivedResources(); + + virtual bool canHandleRequest(const ResourceRequest&) const; + virtual bool canShowMIMEType(const String& MIMEType) const; + virtual bool representationExistsForURLScheme(const String& URLScheme) const; + virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const; + + virtual void frameLoadCompleted(); + virtual void saveViewStateToItem(HistoryItem*); + virtual void restoreViewState(); + virtual void provisionalLoadStarted(); + virtual void didFinishLoad(); + virtual void prepareForDataSourceReplacement(); + + virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest&, const SubstituteData&); + virtual void setTitle(const String& title, const KURL&); + + virtual String userAgent(const KURL&); + + virtual bool canCachePage() const; + virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&); + + virtual WTF::PassRefPtr<WebCore::Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, + const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight); + virtual Widget* createPlugin(const IntSize&, Element*, const KURL&, + const WTF::Vector<WebCore::String, 0u>&, const WTF::Vector<String, 0u>&, + const String&, bool); + virtual void redirectDataToPlugin(Widget* pluginWidget); + + virtual Widget* createJavaAppletWidget(const IntSize&, Element*, const KURL& baseURL, const Vector<String>& paramNames, const Vector<String>& paramValues); + + virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType); + virtual String overrideMediaType() const; + + virtual void windowObjectCleared(); + + virtual void didPerformFirstNavigation() const; + virtual void registerForIconNotification(bool); + + virtual void savePlatformDataToCachedPage(CachedPage*); + virtual void transitionToCommittedFromCachedPage(CachedPage*); + virtual void transitionToCommittedForNewPage(); + + // WebIconDatabaseClient api + virtual void didAddIconForPageUrl(const String& pageUrl); + private: + FrameAndroid* m_frame; + enum ResourceErrors { + InternalErrorCancelled = -99, + InternalErrorCannotShowUrl, + InternalErrorInterrupted, + InternalErrorCannotShowMimeType, + InternalErrorFileDoesNotExist, + InternalErrorLast + }; + + /* XXX: These must match android.net.http.EventHandler */ + enum EventHandlerErrors { + Error = -1, + ErrorLookup = -2, + ErrorUnsupportedAuthScheme = -3, + ErrorAuth = -4, + ErrorProxyAuth = -5, + ErrorConnect = -6, + ErrorIO = -7, + ErrorTimeout = -8, + ErrorRedirectLoop = -9, + ErrorUnsupportedScheme = -10, + ErrorFailedSslHandshake = -11, + ErrorBadUrl = -12, + ErrorFile = -13, + ErrorFileNotFound = -14, + ErrorTooManyRequests = -15 + }; + }; + +} + +#endif diff --git a/WebCore/platform/android/InspectorClientAndroid.h b/WebCore/platform/android/InspectorClientAndroid.h new file mode 100644 index 0000000..fd1493a --- /dev/null +++ b/WebCore/platform/android/InspectorClientAndroid.h @@ -0,0 +1,49 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef InspectorClientAndroid_h +#define InspectorClientAndroid_h + +#include "InspectorClient.h" + +namespace WebCore { + +class InspectorClientAndroid : public InspectorClient { +public: + virtual ~InspectorClientAndroid() { } + + virtual void inspectorDestroyed() {} + + virtual Page* createPage() { return NULL; } + + virtual void showWindow() {} + virtual void closeWindow() {} + + virtual void attachWindow() {} + virtual void detachWindow() {} + + virtual void highlight(Node*) {} + virtual void hideHighlight() {} + + virtual void inspectedURLChanged(const String& newURL) {} +// functions new to Feb-19 tip of tree merge: + virtual String localizedStringsURL() { return String(); } +}; + +} + +#endif diff --git a/WebCore/platform/android/IteratorAndroid.h b/WebCore/platform/android/IteratorAndroid.h new file mode 100644 index 0000000..d30013d --- /dev/null +++ b/WebCore/platform/android/IteratorAndroid.h @@ -0,0 +1,41 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ITERATOR_ANDROID_H +#define ITERATOR_ANDROID_H + +#include "stl_iterator_base.h" +#include "heap.h" + +/* +struct input_iterator_tag {}; +struct forward_iterator_tag : public input_iterator_tag {}; +struct bidirectional_iterator_tag : public forward_iterator_tag {}; +struct random_access_iterator_tag : public bidirectional_iterator_tag {}; + +template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t, + typename _Pointer = _Tp*, typename _Reference = _Tp&> +struct iterator +{ + typedef _Category iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Pointer pointer; + typedef _Reference reference; +}; + */ +#endif diff --git a/WebCore/platform/android/JavaVM/jni.h b/WebCore/platform/android/JavaVM/jni.h new file mode 100644 index 0000000..429dbc3 --- /dev/null +++ b/WebCore/platform/android/JavaVM/jni.h @@ -0,0 +1,24 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef _JNI_COVER_H_ +#define _JNI_COVER_H_ + +#include "nativehelper/jni.h" +#define AttachCurrentThread(a, b) AttachCurrentThread((JNIEnv**) a, b) + +#endif diff --git a/WebCore/platform/android/KeyEventAndroid.cpp b/WebCore/platform/android/KeyEventAndroid.cpp new file mode 100644 index 0000000..ede5ae5 --- /dev/null +++ b/WebCore/platform/android/KeyEventAndroid.cpp @@ -0,0 +1,256 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "DeprecatedString.h" +#include "KeyboardCodes.h" +#include "NotImplemented.h" +#include "PlatformKeyboardEvent.h" +#include <ui/KeycodeLabels.h> + +namespace WebCore { + +// compare to same function in gdk/KeyEventGdk.cpp +static int windowsKeyCodeForKeyEvent(unsigned int keyCode) { +// Does not provide all key codes, and does not handle all keys. + switch(keyCode) { + case kKeyCodeDel: + return VK_BACK; + case kKeyCodeTab: + return VK_TAB; + case kKeyCodeClear: + return VK_CLEAR; + case kKeyCodeDpadCenter: + case kKeyCodeNewline: + return VK_RETURN; + case kKeyCodeShiftLeft: + case kKeyCodeShiftRight: + return VK_SHIFT; + // back will serve as escape, although we probably do not have access to it + case kKeyCodeBack: + return VK_ESCAPE; + case kKeyCodeSpace: + return VK_SPACE; + case kKeyCodeHome: + return VK_HOME; + case kKeyCodeDpadLeft: + return VK_LEFT; + case kKeyCodeDpadUp: + return VK_UP; + case kKeyCodeDpadRight: + return VK_RIGHT; + case kKeyCodeDpadDown: + return VK_DOWN; + case kKeyCode0: + return VK_0; + case kKeyCode1: + return VK_1; + case kKeyCode2: + return VK_2; + case kKeyCode3: + return VK_3; + case kKeyCode4: + return VK_4; + case kKeyCode5: + return VK_5; + case kKeyCode6: + return VK_6; + case kKeyCode7: + return VK_7; + case kKeyCode8: + return VK_8; + case kKeyCode9: + return VK_9; + case kKeyCodeA: + return VK_A; + case kKeyCodeB: + return VK_B; + case kKeyCodeC: + return VK_C; + case kKeyCodeD: + return VK_D; + case kKeyCodeE: + return VK_E; + case kKeyCodeF: + return VK_F; + case kKeyCodeG: + return VK_G; + case kKeyCodeH: + return VK_H; + case kKeyCodeI: + return VK_I; + case kKeyCodeJ: + return VK_J; + case kKeyCodeK: + return VK_K; + case kKeyCodeL: + return VK_L; + case kKeyCodeM: + return VK_M; + case kKeyCodeN: + return VK_N; + case kKeyCodeO: + return VK_O; + case kKeyCodeP: + return VK_P; + case kKeyCodeQ: + return VK_Q; + case kKeyCodeR: + return VK_R; + case kKeyCodeS: + return VK_S; + case kKeyCodeT: + return VK_T; + case kKeyCodeU: + return VK_U; + case kKeyCodeV: + return VK_V; + case kKeyCodeW: + return VK_W; + case kKeyCodeX: + return VK_X; + case kKeyCodeY: + return VK_Y; + case kKeyCodeZ: + return VK_Z; + // colon + case kKeyCodeSemicolon: + return VK_OEM_1; + case kKeyCodeComma: + return VK_OEM_COMMA; + case kKeyCodeMinus: + return VK_OEM_MINUS; + case kKeyCodeEquals: + return VK_OEM_PLUS; + case kKeyCodePeriod: + return VK_OEM_PERIOD; + case kKeyCodeSlash: + return VK_OEM_2; + // maybe not the right choice + case kKeyCodeLeftBracket: + return VK_OEM_4; + case kKeyCodeBackslash: + return VK_OEM_5; + case kKeyCodeRightBracket: + return VK_OEM_6; + default: + return 0; + } +} + +static String keyIdentifierForAndroidKeyCode(int keyCode) +{ +/* Does not return all of the same key identifiers, and + * does not handle all the keys. + */ + switch (keyCode) { + case kKeyCodeClear: + return "Clear"; + case kKeyCodeNewline: + case kKeyCodeDpadCenter: + return "Enter"; + case kKeyCodeHome: + return "Home"; + case kKeyCodeDpadDown: + return "Down"; + case kKeyCodeDpadLeft: + return "Left"; + case kKeyCodeDpadRight: + return "Right"; + case kKeyCodeDpadUp: + return "Up"; + // Standard says that DEL becomes U+00007F. + case kKeyCodeDel: + return "U+00007F"; + default: + char upper[16]; + sprintf(upper, "U+%06X", windowsKeyCodeForKeyEvent(keyCode)); + return String(upper); + } +} + +static inline String singleCharacterString(int c) +{ + if (!c) + return String(); + if (c > 0xffff) { + UChar lead = U16_LEAD(c); + UChar trail = U16_TRAIL(c); + UChar utf16[2] = {lead, trail}; + return String(utf16, 2); + } + UChar n = (UChar)c; + return String(&n, 1); +} + +PlatformKeyboardEvent::PlatformKeyboardEvent(int keyCode, int keyValue, bool down, bool forceAutoRepeat, bool cap, bool fn, bool sym) + : m_type(down ? KeyDown : KeyUp) + , m_text(singleCharacterString(keyValue)) + , m_unmodifiedText(singleCharacterString(keyValue)) + , m_keyIdentifier(keyIdentifierForAndroidKeyCode(keyCode)) + , m_autoRepeat(forceAutoRepeat) + , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(keyCode)) + , m_isKeypad(false) + , m_shiftKey(cap) +// FIXME: Mapping fn to alt and sym to ctrl. Is this the desired behavior? + , m_ctrlKey(sym) + , m_altKey(fn) + , m_metaKey(false) +{ + // Copied from the mac port + if (m_windowsVirtualKeyCode == '\r') { + m_text = "\r"; + m_unmodifiedText = "\r"; + } + + if (m_text == "\x7F") + m_text = "\x8"; + if (m_unmodifiedText == "\x7F") + m_unmodifiedText = "\x8"; + + if (m_windowsVirtualKeyCode == 9) { + m_text = "\x9"; + m_unmodifiedText = "\x9"; + } +} + +bool PlatformKeyboardEvent::currentCapsLockState() +{ + notImplemented(); + return false; +} + +// functions new to Feb-19 tip of tree merge: +void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCompatibilityMode) +{ + // Copied with modification from the mac port. + ASSERT(m_type == KeyDown); + ASSERT(type == RawKeyDown || type == Char); + m_type = type; + if (backwardCompatibilityMode) + return; + + if (type == RawKeyDown) { + m_text = String(); + m_unmodifiedText = String(); + } else { + m_keyIdentifier = String(); + m_windowsVirtualKeyCode = 0; + } +} + +} // WebCore diff --git a/WebCore/platform/android/KeyboardCodes.h b/WebCore/platform/android/KeyboardCodes.h new file mode 100644 index 0000000..d6820d4 --- /dev/null +++ b/WebCore/platform/android/KeyboardCodes.h @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * + * 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 KeyboardCodes_H +#define KeyboardCodes_H + +namespace WebCore { + +// VK_LBUTTON (01) Left mouse button +// VK_RBUTTON (02) Right mouse button +// VK_CANCEL (03) Control-break processing +// VK_MBUTTON (04) Middle mouse button (three-button mouse) +// VK_XBUTTON1 (05) +// VK_XBUTTON2 (06) + +// VK_BACK (08) BACKSPACE key +const int VK_BACK = 0x08; + +// VK_TAB (09) TAB key +const int VK_TAB = 0x09; + +// VK_CLEAR (0C) CLEAR key +const int VK_CLEAR = 0x0C; + +// VK_RETURN (0D) +const int VK_RETURN = 0x0D; + +// VK_SHIFT (10) SHIFT key +const int VK_SHIFT = 0x10; + +// VK_CONTROL (11) CTRL key +const int VK_CONTROL = 0x11; + +// VK_MENU (12) ALT key +const int VK_MENU = 0x12; + +// VK_PAUSE (13) PAUSE key +const int VK_PAUSE = 0x13; + +// VK_CAPITAL (14) CAPS LOCK key +const int VK_CAPITAL = 0x14; + +// VK_KANA (15) Input Method Editor (IME) Kana mode +const int VK_KANA = 0x15; + +// VK_HANGUEL (15) IME Hanguel mode (maintained for compatibility; use VK_HANGUL) +// VK_HANGUL (15) IME Hangul mode +const int VK_HANGUL = 0x15; + +// VK_JUNJA (17) IME Junja mode +const int VK_JUNJA = 0x17; + +// VK_FINAL (18) IME final mode +const int VK_FINAL = 0x18; + +// VK_HANJA (19) IME Hanja mode +const int VK_HANJA = 0x19; + +// VK_KANJI (19) IME Kanji mode +const int VK_KANJI = 0x19; + +// VK_ESCAPE (1B) ESC key +const int VK_ESCAPE = 0x1B; + +// VK_CONVERT (1C) IME convert +const int VK_CONVERT = 0x1C; + +// VK_NONCONVERT (1D) IME nonconvert +const int VK_NONCONVERT = 0x1D; + +// VK_ACCEPT (1E) IME accept +const int VK_ACCEPT = 0x1E; + +// VK_MODECHANGE (1F) IME mode change request +const int VK_MODECHANGE = 0x1F; + +// VK_SPACE (20) SPACEBAR +const int VK_SPACE = 0x20; + +// VK_PRIOR (21) PAGE UP key +const int VK_PRIOR = 0x21; + +// VK_NEXT (22) PAGE DOWN key +const int VK_NEXT = 0x22; + +// VK_END (23) END key +const int VK_END = 0x23; + +// VK_HOME (24) HOME key +const int VK_HOME = 0x24; + +// VK_LEFT (25) LEFT ARROW key +const int VK_LEFT = 0x25; + +// VK_UP (26) UP ARROW key +const int VK_UP = 0x26; + +// VK_RIGHT (27) RIGHT ARROW key +const int VK_RIGHT = 0x27; + +// VK_DOWN (28) DOWN ARROW key +const int VK_DOWN = 0x28; + +// VK_SELECT (29) SELECT key +const int VK_SELECT = 0x29; + +// VK_PRINT (2A) PRINT key +const int VK_PRINT = 0x2A; + +// VK_EXECUTE (2B) EXECUTE key +const int VK_EXECUTE = 0x2B; + +// VK_SNAPSHOT (2C) PRINT SCREEN key +const int VK_SNAPSHOT = 0x2C; + +// VK_INSERT (2D) INS key +const int VK_INSERT = 0x2D; + +// VK_DELETE (2E) DEL key +const int VK_DELETE = 0x2E; + +// VK_HELP (2F) HELP key +const int VK_HELP = 0x2F; + +// (30) 0 key +const int VK_0 = 0x30; + +// (31) 1 key +const int VK_1 = 0x31; + +// (32) 2 key +const int VK_2 = 0x32; + +// (33) 3 key +const int VK_3 = 0x33; + +// (34) 4 key +const int VK_4 = 0x34; + +// (35) 5 key; + +const int VK_5 = 0x35; + +// (36) 6 key +const int VK_6 = 0x36; + +// (37) 7 key +const int VK_7 = 0x37; + +// (38) 8 key +const int VK_8 = 0x38; + +// (39) 9 key +const int VK_9 = 0x39; + +// (41) A key +const int VK_A = 0x41; + +// (42) B key +const int VK_B = 0x42; + +// (43) C key +const int VK_C = 0x43; + +// (44) D key +const int VK_D = 0x44; + +// (45) E key +const int VK_E = 0x45; + +// (46) F key +const int VK_F = 0x46; + +// (47) G key +const int VK_G = 0x47; + +// (48) H key +const int VK_H = 0x48; + +// (49) I key +const int VK_I = 0x49; + +// (4A) J key +const int VK_J = 0x4A; + +// (4B) K key +const int VK_K = 0x4B; + +// (4C) L key +const int VK_L = 0x4C; + +// (4D) M key +const int VK_M = 0x4D; + +// (4E) N key +const int VK_N = 0x4E; + +// (4F) O key +const int VK_O = 0x4F; + +// (50) P key +const int VK_P = 0x50; + +// (51) Q key +const int VK_Q = 0x51; + +// (52) R key +const int VK_R = 0x52; + +// (53) S key +const int VK_S = 0x53; + +// (54) T key +const int VK_T = 0x54; + +// (55) U key +const int VK_U = 0x55; + +// (56) V key +const int VK_V = 0x56; + +// (57) W key +const int VK_W = 0x57; + +// (58) X key +const int VK_X = 0x58; + +// (59) Y key +const int VK_Y = 0x59; + +// (5A) Z key +const int VK_Z = 0x5A; + +// VK_LWIN (5B) Left Windows key (Microsoft Natural keyboard) +const int VK_LWIN = 0x5B; + +// VK_RWIN (5C) Right Windows key (Natural keyboard) +const int VK_RWIN = 0x5C; + +// VK_APPS (5D) Applications key (Natural keyboard) +const int VK_APPS = 0x5D; + +// VK_SLEEP (5F) Computer Sleep key +const int VK_SLEEP = 0x5F; + +// VK_NUMPAD0 (60) Numeric keypad 0 key +const int VK_NUMPAD0 = 0x60; + +// VK_NUMPAD1 (61) Numeric keypad 1 key +const int VK_NUMPAD1 = 0x61; + +// VK_NUMPAD2 (62) Numeric keypad 2 key +const int VK_NUMPAD2 = 0x62; + +// VK_NUMPAD3 (63) Numeric keypad 3 key +const int VK_NUMPAD3 = 0x63; + +// VK_NUMPAD4 (64) Numeric keypad 4 key +const int VK_NUMPAD4 = 0x64; + +// VK_NUMPAD5 (65) Numeric keypad 5 key +const int VK_NUMPAD5 = 0x65; + +// VK_NUMPAD6 (66) Numeric keypad 6 key +const int VK_NUMPAD6 = 0x66; + +// VK_NUMPAD7 (67) Numeric keypad 7 key +const int VK_NUMPAD7 = 0x67; + +// VK_NUMPAD8 (68) Numeric keypad 8 key +const int VK_NUMPAD8 = 0x68; + +// VK_NUMPAD9 (69) Numeric keypad 9 key +const int VK_NUMPAD9 = 0x69; + +// VK_MULTIPLY (6A) Multiply key +const int VK_MULTIPLY = 0x6A; + +// VK_ADD (6B) Add key +const int VK_ADD = 0x6B; + +// VK_SEPARATOR (6C) Separator key +const int VK_SEPARATOR = 0x6C; + +// VK_SUBTRACT (6D) Subtract key +const int VK_SUBTRACT = 0x6D; + +// VK_DECIMAL (6E) Decimal key +const int VK_DECIMAL = 0x6E; + +// VK_DIVIDE (6F) Divide key +const int VK_DIVIDE = 0x6F; + +// VK_F1 (70) F1 key +const int VK_F1 = 0x70; + +// VK_F2 (71) F2 key +const int VK_F2 = 0x71; + +// VK_F3 (72) F3 key +const int VK_F3 = 0x72; + +// VK_F4 (73) F4 key +const int VK_F4 = 0x73; + +// VK_F5 (74) F5 key +const int VK_F5 = 0x74; + +// VK_F6 (75) F6 key +const int VK_F6 = 0x75; + +// VK_F7 (76) F7 key +const int VK_F7 = 0x76; + +// VK_F8 (77) F8 key +const int VK_F8 = 0x77; + +// VK_F9 (78) F9 key +const int VK_F9 = 0x78; + +// VK_F10 (79) F10 key +const int VK_F10 = 0x79; + +// VK_F11 (7A) F11 key +const int VK_F11 = 0x7A; + +// VK_F12 (7B) F12 key +const int VK_F12 = 0x7B; + +// VK_F13 (7C) F13 key +const int VK_F13 = 0x7C; + +// VK_F14 (7D) F14 key +const int VK_F14 = 0x7D; + +// VK_F15 (7E) F15 key +const int VK_F15 = 0x7E; + +// VK_F16 (7F) F16 key +const int VK_F16 = 0x7F; + +// VK_F17 (80H) F17 key +const int VK_F17 = 0x80; + +// VK_F18 (81H) F18 key +const int VK_F18 = 0x81; + +// VK_F19 (82H) F19 key +const int VK_F19 = 0x82; + +// VK_F20 (83H) F20 key +const int VK_F20 = 0x83; + +// VK_F21 (84H) F21 key +const int VK_F21 = 0x84; + +// VK_F22 (85H) F22 key +const int VK_F22 = 0x85; + +// VK_F23 (86H) F23 key +const int VK_F23 = 0x86; + +// VK_F24 (87H) F24 key +const int VK_F24 = 0x87; + +// VK_NUMLOCK (90) NUM LOCK key +const int VK_NUMLOCK = 0x90; + +// VK_SCROLL (91) SCROLL LOCK key +const int VK_SCROLL = 0x91; + +// VK_LSHIFT (A0) Left SHIFT key +const int VK_LSHIFT = 0xA0; + +// VK_RSHIFT (A1) Right SHIFT key +const int VK_RSHIFT = 0xA1; + +// VK_LCONTROL (A2) Left CONTROL key +const int VK_LCONTROL = 0xA2; + +// VK_RCONTROL (A3) Right CONTROL key +const int VK_RCONTROL = 0xA3; + +// VK_LMENU (A4) Left MENU key +const int VK_LMENU = 0xA4; + +// VK_RMENU (A5) Right MENU key +const int VK_RMENU = 0xA5; + +// VK_BROWSER_BACK (A6) Windows 2000/XP: Browser Back key +const int VK_BROWSER_BACK = 0xA6; + +// VK_BROWSER_FORWARD (A7) Windows 2000/XP: Browser Forward key +const int VK_BROWSER_FORWARD = 0xA7; + +// VK_BROWSER_REFRESH (A8) Windows 2000/XP: Browser Refresh key +const int VK_BROWSER_REFRESH = 0xA8; + +// VK_BROWSER_STOP (A9) Windows 2000/XP: Browser Stop key +const int VK_BROWSER_STOP = 0xA9; + +// VK_BROWSER_SEARCH (AA) Windows 2000/XP: Browser Search key +const int VK_BROWSER_SEARCH = 0xAA; + +// VK_BROWSER_FAVORITES (AB) Windows 2000/XP: Browser Favorites key +const int VK_BROWSER_FAVORITES = 0xAB; + +// VK_BROWSER_HOME (AC) Windows 2000/XP: Browser Start and Home key +const int VK_BROWSER_HOME = 0xAC; + +// VK_VOLUME_MUTE (AD) Windows 2000/XP: Volume Mute key +const int VK_VOLUME_MUTE = 0xAD; + +// VK_VOLUME_DOWN (AE) Windows 2000/XP: Volume Down key +const int VK_VOLUME_DOWN = 0xAE; + +// VK_VOLUME_UP (AF) Windows 2000/XP: Volume Up key +const int VK_VOLUME_UP = 0xAF; + +// VK_MEDIA_NEXT_TRACK (B0) Windows 2000/XP: Next Track key +const int VK_MEDIA_NEXT_TRACK = 0xB0; + +// VK_MEDIA_PREV_TRACK (B1) Windows 2000/XP: Previous Track key +const int VK_MEDIA_PREV_TRACK = 0xB1; + +// VK_MEDIA_STOP (B2) Windows 2000/XP: Stop Media key +const int VK_MEDIA_STOP = 0xB2; + +// VK_MEDIA_PLAY_PAUSE (B3) Windows 2000/XP: Play/Pause Media key +const int VK_MEDIA_PLAY_PAUSE = 0xB3; + +// VK_LAUNCH_MAIL (B4) Windows 2000/XP: Start Mail key +const int VK_MEDIA_LAUNCH_MAIL = 0xB4; + +// VK_LAUNCH_MEDIA_SELECT (B5) Windows 2000/XP: Select Media key +const int VK_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5; + +// VK_LAUNCH_APP1 (B6) Windows 2000/XP: Start Application 1 key +const int VK_MEDIA_LAUNCH_APP1 = 0xB6; + +// VK_LAUNCH_APP2 (B7) Windows 2000/XP: Start Application 2 key +const int VK_MEDIA_LAUNCH_APP2 = 0xB7; + +// VK_OEM_1 (BA) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the ';:' key +const int VK_OEM_1 = 0xBA; + +// VK_OEM_PLUS (BB) Windows 2000/XP: For any country/region, the '+' key +const int VK_OEM_PLUS = 0xBB; + +// VK_OEM_COMMA (BC) Windows 2000/XP: For any country/region, the ',' key +const int VK_OEM_COMMA = 0xBC; + +// VK_OEM_MINUS (BD) Windows 2000/XP: For any country/region, the '-' key +const int VK_OEM_MINUS = 0xBD; + +// VK_OEM_PERIOD (BE) Windows 2000/XP: For any country/region, the '.' key +const int VK_OEM_PERIOD = 0xBE; + +// VK_OEM_2 (BF) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '/?' key +const int VK_OEM_2 = 0xBF; + +// VK_OEM_3 (C0) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '`~' key +const int VK_OEM_3 = 0xC0; + +// VK_OEM_4 (DB) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '[{' key +const int VK_OEM_4 = 0xDB; + +// VK_OEM_5 (DC) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the '\|' key +const int VK_OEM_5 = 0xDC; + +// VK_OEM_6 (DD) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the ']}' key +const int VK_OEM_6 = 0xDD; + +// VK_OEM_7 (DE) Used for miscellaneous characters; it can vary by keyboard. Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key +const int VK_OEM_7 = 0xDE; + +// VK_OEM_8 (DF) Used for miscellaneous characters; it can vary by keyboard. +const int VK_OEM_8 = 0xDF; + +// VK_OEM_102 (E2) Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard +const int VK_OEM_102 = 0xE2; + +// VK_PROCESSKEY (E5) Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key +const int VK_PROCESSKEY = 0xE5; + +// VK_PACKET (E7) Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT,SendInput, WM_KEYDOWN, and WM_KEYUP +const int VK_PACKET = 0xE7; + +// VK_ATTN (F6) Attn key +const int VK_ATTN = 0xF6; + +// VK_CRSEL (F7) CrSel key +const int VK_CRSEL = 0xF7; + +// VK_EXSEL (F8) ExSel key +const int VK_EXSEL = 0xF8; + +// VK_EREOF (F9) Erase EOF key +const int VK_EREOF = 0xF9; + +// VK_PLAY (FA) Play key +const int VK_PLAY = 0xFA; + +// VK_ZOOM (FB) Zoom key +const int VK_ZOOM = 0xFB; + +// VK_NONAME (FC) Reserved for future use +const int VK_NONAME = 0xFC; + +// VK_PA1 (FD) PA1 key +const int VK_PA1 = 0xFD; + +// VK_OEM_CLEAR (FE) Clear key +const int VK_OEM_CLEAR = 0xFE; + +const int VK_UNKNOWN = 0; + +} + +#endif diff --git a/WebCore/platform/network/curl/AuthenticationChallenge.h b/WebCore/platform/android/LocalizedStringsAndroid.cpp index 753ac6f..6c0255e 100644 --- a/WebCore/platform/network/curl/AuthenticationChallenge.h +++ b/WebCore/platform/android/LocalizedStringsAndroid.cpp @@ -1,5 +1,8 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Holger Hans Peter Freyther + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,25 +25,22 @@ * (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 AuthenticationChallenge_h -#define AuthenticationChallenge_h -#include "AuthenticationChallengeBase.h" +#include "config.h" -namespace WebCore { +#include "LocalizedStrings.h" +#include "PlatformString.h" -class AuthenticationChallenge : public AuthenticationChallengeBase { -public: - AuthenticationChallenge() - { - } +namespace WebCore { - AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error) - : AuthenticationChallengeBase(protectionSpace, proposedCredential, previousFailureCount, response, error) - { - } -}; +String contextMenuItemTagInspectElement() +{ + return String::fromUTF8("Inspect Element"); +} +String unknownFileSizeText() +{ + return String::fromUTF8("Unknown"); } -#endif +} diff --git a/WebCore/platform/android/PlatformScrollBar.h b/WebCore/platform/android/PlatformScrollBar.h new file mode 100644 index 0000000..e552c8b --- /dev/null +++ b/WebCore/platform/android/PlatformScrollBar.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 PlatformScrollBar_h +#define PlatformScrollBar_h + +#include "Widget.h" +#include "ScrollBar.h" + +namespace WebCore { + +class PlatformScrollbar : public Widget, public Scrollbar { +public: + PlatformScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize); + virtual ~PlatformScrollbar(); + + virtual bool isWidget() const { return true; } + + virtual int width() const; + virtual int height() const; + virtual void setRect(const IntRect&); + virtual void setEnabled(bool); + virtual void paint(GraphicsContext*, const IntRect& damageRect); + + static int horizontalScrollbarHeight() { return 8; } + static int verticalScrollbarWidth() { return 8; } + +protected: + virtual void updateThumbPosition(); + virtual void updateThumbProportion(); + +}; + +} + +#endif // PlatformScrollBar_h diff --git a/WebCore/platform/android/PlatformScrollBarAndroid.cpp b/WebCore/platform/android/PlatformScrollBarAndroid.cpp new file mode 100644 index 0000000..a087aed --- /dev/null +++ b/WebCore/platform/android/PlatformScrollBarAndroid.cpp @@ -0,0 +1,80 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "Color.h" +#include "GraphicsContext.h" +#include "PlatformScrollBar.h" +#include "WebCoreViewBridge.h" + +#define MINIMUM_SCROLL_KNOB_HEIGHT 10 +#define SCROLL_BAR_ROUNDING 5 + +class ScrollBarView { +public: + +private: + int m_height; + int m_width; +}; + +namespace WebCore { + +PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, ScrollbarOrientation o, + ScrollbarControlSize s) : Scrollbar(client, o, s) +{ + +} + +PlatformScrollbar::~PlatformScrollbar() +{ + +} + +int PlatformScrollbar::width() const +{ + return 8; + +} + +int PlatformScrollbar::height() const { + return 8; + +} + +void PlatformScrollbar::setRect(const IntRect& r) +{ +} + +void PlatformScrollbar::setEnabled(bool) +{ + +} + +void PlatformScrollbar::paint(GraphicsContext* gc, const IntRect& damageRect) +{ +} + +void PlatformScrollbar::updateThumbPosition() +{ +} + +void PlatformScrollbar::updateThumbProportion() +{ +} + +} diff --git a/WebCore/platform/android/PopupMenuAndroid.cpp b/WebCore/platform/android/PopupMenuAndroid.cpp new file mode 100644 index 0000000..b8bbd2d --- /dev/null +++ b/WebCore/platform/android/PopupMenuAndroid.cpp @@ -0,0 +1,55 @@ +/* + * This file is part of the popup menu implementation for <select> elements in WebCore. + * + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include "PopupMenu.h" + +namespace WebCore { + +// Now we handle all of this in WebViewCore.cpp +PopupMenu::PopupMenu(PopupMenuClient* menuList) : m_popupClient(menuList) +{ +} + +PopupMenu::~PopupMenu() +{ +} + +void PopupMenu::show(const IntRect& rect, FrameView* view, int index) +{ + +} + +void PopupMenu::hide() +{ +} + +void PopupMenu::updateFromElement() +{ +} + +// functions new to Jun-07 tip of tree merge: +bool PopupMenu::itemWritingDirectionIsNatural() { return false; } + +} + diff --git a/WebCore/platform/android/QLineEditAndroid.cpp b/WebCore/platform/android/QLineEditAndroid.cpp new file mode 100644 index 0000000..b31baf5 --- /dev/null +++ b/WebCore/platform/android/QLineEditAndroid.cpp @@ -0,0 +1,120 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" + +#include "KWQLineEdit.h" +#include "IntPoint.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include <utils/Log.h> + +using namespace WebCore; + +class KWQTextFieldController { +public: + int m_maxLength; + int m_cursorPosition; + bool m_edited; + String m_placeholderString; + String m_text; +}; + +QLineEdit::QLineEdit(Type type) : m_type(type) +{ + printf("============ QLineEdit %d\n", type); + + m_controller = new KWQTextFieldController; + + m_controller->m_maxLength = 32; // ??? + m_controller->m_cursorPosition = 0; + m_controller->m_edited = false; +} + +QLineEdit::~QLineEdit() +{ + delete m_controller; +} + +int QLineEdit::maxLength() const +{ + return m_controller->m_maxLength; +} + +void QLineEdit::setMaxLength(int ml) +{ + m_controller->m_maxLength = ml; +} + +String QLineEdit::text() const +{ + return m_controller->m_text; +} + +void QLineEdit::setText(String const& text) +{ + m_controller->m_text = text; +} + +int QLineEdit::cursorPosition() const +{ + return m_controller->m_cursorPosition; +} + +void QLineEdit::setCursorPosition(int cp) +{ + m_controller->m_cursorPosition = cp; +} + +void QLineEdit::setPlaceholderString(String const& ph) +{ + m_controller->m_placeholderString = ph; +} + +bool QLineEdit::edited() const +{ + return m_controller->m_edited; +} + +void QLineEdit::setEdited(bool edited) +{ + m_controller->m_edited = edited; +} + +void QLineEdit::setFont(Font const&) { } +void QLineEdit::setAlignment(HorizontalAlignment) { } +void QLineEdit::setWritingDirection(TextDirection) { } +void QLineEdit::setReadOnly(bool) { } +void QLineEdit::setColors(Color const&, Color const&) { } +IntSize QLineEdit::sizeForCharacterWidth(int) const { return IntSize(); } +int QLineEdit::baselinePosition(int) const { return 0; } +void QLineEdit::setLiveSearch(bool) { } + +#define notImplemented() { LOGV("%s: Not Implemented", __FUNCTION__); } + +void QLineEdit::selectAll() { notImplemented(); } +void QLineEdit::addSearchResult() { notImplemented(); } +int QLineEdit::selectionStart() const { notImplemented(); return 0; } +bool QLineEdit::hasSelectedText() const { notImplemented(); return 0; } +String QLineEdit::selectedText() const { notImplemented(); return String(); } +void QLineEdit::setAutoSaveName(String const&) { notImplemented(); } +bool QLineEdit::checksDescendantsForFocus() const { notImplemented(); return false; } +void QLineEdit::setSelection(int,int) { notImplemented(); } +void QLineEdit::setMaxResults(int) { notImplemented(); } + +Widget::FocusPolicy QLineEdit::focusPolicy() const { notImplemented(); return NoFocus; } diff --git a/WebCore/platform/android/RenderSkinAndroid.cpp b/WebCore/platform/android/RenderSkinAndroid.cpp new file mode 100644 index 0000000..fc256d2 --- /dev/null +++ b/WebCore/platform/android/RenderSkinAndroid.cpp @@ -0,0 +1,65 @@ +/* libs/WebKitLib/WebKit/WebCore/rendering/RenderSkinAndroid.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "RenderSkinAndroid.h" +#include "RenderSkinButton.h" +#include "RenderSkinCombo.h" +#include "RenderSkinRadio.h" +#include "SkImageDecoder.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include "utils/Log.h" +#include "utils/AssetManager.h" +#include "utils/Asset.h" + +namespace WebCore { + +RenderSkinAndroid::RenderSkinAndroid() + : m_height(0) + , m_width(0) +{} + +void RenderSkinAndroid::Init(android::AssetManager* am) +{ + RenderSkinButton::Init(am); + RenderSkinCombo::Init(am); + RenderSkinRadio::Init(am); +} + +bool RenderSkinAndroid::DecodeBitmap(android::AssetManager* am, const char* fileName, SkBitmap* bitmap) +{ + android::Asset* asset = am->open(fileName, android::Asset::ACCESS_BUFFER); + if (!asset) { + asset = am->openNonAsset(fileName, android::Asset::ACCESS_BUFFER); + if (!asset) { + LOGD("RenderSkinAndroid: File \"%s\" not found.\n", fileName); + return false; + } + } + + bool success = SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), bitmap); + if (!success) { + LOGD("RenderSkinAndroid: Failed to decode %s\n", fileName); + } + + delete asset; + return success; +} + +} // namespace WebCore diff --git a/WebCore/platform/android/RenderSkinAndroid.h b/WebCore/platform/android/RenderSkinAndroid.h new file mode 100644 index 0000000..2226e7f --- /dev/null +++ b/WebCore/platform/android/RenderSkinAndroid.h @@ -0,0 +1,83 @@ +/* libs/WebKitLib/WebKit/WebCore/rendering/RenderSkinAndroid.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef RenderSkinAndroid_h +#define RenderSkinAndroid_h + +namespace android { + class AssetManager; +} + +class SkBitmap; + +namespace WebCore { +class Node; +class PlatformGraphicsContext; + +/* RenderSkinAndroid is the base class for all RenderSkins. Form elements each have a + * subclass for drawing themselves. + */ +class RenderSkinAndroid +{ +public: + RenderSkinAndroid(); + virtual ~RenderSkinAndroid() {} + + enum State { + kDisabled, + kNormal, + kFocused, + + kNumStates + }; + + /** + * Initialize the Android skinning system. The AssetManager may be used to find resources used + * in rendering. + */ + static void Init(android::AssetManager*); + + /* DecodeBitmap determines which file to use, with the given fileName of the form + * "images/bitmap.png", and uses the asset manager to select the exact one. It + * returns true if it successfully decoded the bitmap, false otherwise. + */ + static bool DecodeBitmap(android::AssetManager* am, const char* fileName, SkBitmap* bitmap); + + /* draw() tells the skin to draw itself, and returns true if the skin needs + * a redraw to animations, false otherwise + */ + virtual bool draw(PlatformGraphicsContext*) { return false; } + + /* notifyState() checks to see if the element is checked, focused, and enabled + * it must be implemented in the subclass + */ + virtual void notifyState(Node* element) { } + + /* setDim() tells the skin its width and height + */ + virtual void setDim(int width, int height) { m_width = width; m_height = height; } + +protected: + int m_height; + int m_width; + +}; + +} // WebCore + +#endif + diff --git a/WebCore/platform/android/RenderSkinButton.cpp b/WebCore/platform/android/RenderSkinButton.cpp new file mode 100644 index 0000000..73e6ea0 --- /dev/null +++ b/WebCore/platform/android/RenderSkinButton.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "AndroidLog.h" +#include "android_graphics.h" +#include "Document.h" +#include "IntRect.h" +#include "Node.h" +#include "RenderSkinButton.h" +#include "SkCanvas.h" +#include "SkNinePatch.h" +#include "SkRect.h" +#include "utils/Debug.h" + +struct PatchData { + const char* name; + int8_t outset, margin; +}; + +static const PatchData gFiles[] = + { + { "res/drawable/btn_default_normal_disable.9.png", 2, 7 }, + { "res/drawable/btn_default_normal.9.png", 2, 7 }, + { "res/drawable/btn_default_selected.9.png", 2, 7 } + }; + +static SkBitmap gButton[sizeof(gFiles)/sizeof(gFiles[0])]; +static bool gDecoded; + +namespace WebCore { + +void RenderSkinButton::Init(android::AssetManager* am) +{ + static bool gInited; + if (gInited) + return; + + gInited = true; + gDecoded = true; + for (size_t i = 0; i < sizeof(gFiles)/sizeof(gFiles[0]); i++) { + if (!RenderSkinAndroid::DecodeBitmap(am, gFiles[i].name, &gButton[i])) { + gDecoded = false; + ANDROID_LOGD("RenderSkinButton::Init: button assets failed to decode\n\tBrowser buttons will not draw"); + break; + } + } + + // Ensure our enums properly line up with our arrays. + android::CompileTimeAssert<(RenderSkinAndroid::kDisabled == 0)> a1; + android::CompileTimeAssert<(RenderSkinAndroid::kNormal == 1)> a2; + android::CompileTimeAssert<(RenderSkinAndroid::kFocused == 2)> a3; +} + +void RenderSkinButton::Draw(SkCanvas* canvas, const IntRect& r, RenderSkinAndroid::State newState) +{ + // If we failed to decode, do nothing. This way the browser still works, + // and webkit will still draw the label and layout space for us. + if (!gDecoded) { + return; + } + + // Ensure that the state is within the valid range of our array. + SkASSERT(newState < RenderSkinAndroid::kNumStates && newState >= 0); + + // Set up the ninepatch information for drawing. + SkRect bounds; + android_setrect(&bounds, r); + const PatchData& pd = gFiles[newState]; + int marginValue = pd.margin + pd.outset; + + SkIRect margin; + + margin.set(marginValue, marginValue, marginValue, marginValue); + + // Draw to the canvas. + SkNinePatch::DrawNine(canvas, bounds, gButton[newState], margin); +} + +} //WebCore + diff --git a/WebCore/platform/android/RenderSkinButton.h b/WebCore/platform/android/RenderSkinButton.h new file mode 100644 index 0000000..3089a0b --- /dev/null +++ b/WebCore/platform/android/RenderSkinButton.h @@ -0,0 +1,44 @@ +/* libs/WebKitLib/WebKit/WebCore/platform/android/RenderSkinButton.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef RenderSkinButton_h +#define RenderSkinButton_h + +#include "RenderSkinAndroid.h" + +class SkCanvas; + +namespace WebCore { +class IntRect; +class RenderSkinButton +{ +public: + /** + * Initialize the class before use. Uses the AssetManager to initialize any + * bitmaps the class may use. + */ + static void Init(android::AssetManager*); + /** + * Draw the skin to the canvas, using the rectangle for its bounds and the + * State to determine which skin to use, i.e. focused or not focused. + */ + static void Draw(SkCanvas* , const IntRect& , RenderSkinAndroid::State); +}; + +} // WebCore +#endif + diff --git a/WebCore/platform/android/RenderSkinCombo.cpp b/WebCore/platform/android/RenderSkinCombo.cpp new file mode 100644 index 0000000..527cb10 --- /dev/null +++ b/WebCore/platform/android/RenderSkinCombo.cpp @@ -0,0 +1,84 @@ +/* libs/WebKitLib/WebKit/WebCore/platform/android/RenderSkinCombo.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "Document.h" +#include "Node.h" +#include "PlatformGraphicsContext.h" +#include "RenderSkinCombo.h" +#include "SkCanvas.h" +#include "SkNinePatch.h" + +namespace WebCore { + +static const int margin = 2; +static const SkIRect mar = { margin, margin, RenderSkinCombo::extraWidth(), margin }; + +SkBitmap RenderSkinCombo::m_bitmap[2]; +bool RenderSkinCombo::m_decoded; + +RenderSkinCombo::RenderSkinCombo() +{ + m_height = 20; + m_width = 75; + m_state = kNormal; + m_bounds.set(0, 0, SkIntToScalar(m_width), SkIntToScalar(m_height)); +} + +void RenderSkinCombo::Init(android::AssetManager* am) +{ + if (m_decoded) + return; + // Maybe short circuiting is fine, since I don't even draw if one state is not decoded properly + // but is that necessary in the final version? + m_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-noHighlight.png", &m_bitmap[kNormal]); + m_decoded = RenderSkinAndroid::DecodeBitmap(am, "images/combobox-disabled.png", &m_bitmap[kDisabled]) && m_decoded; +} + + +bool RenderSkinCombo::draw(PlatformGraphicsContext *p) +{ + // The text is drawn right next to the left side - should I draw the box slightly to the left? + if (!m_decoded) + return false; + SkCanvas* canvas = p->mCanvas; + SkNinePatch::DrawNine(canvas, m_bounds, m_bitmap[m_state], mar); + return false; +} + +void RenderSkinCombo::notifyState(Node* element) +{ + m_state = kDisabled; + if (!element) + return; + if (element->isEnabled()) + m_state = kNormal; +} + +void RenderSkinCombo::setDim(int width, int height) +{ + if (width != m_width || height != m_height) { + m_width = width; + if (height < (margin<<1) + 1) + height = (margin<<1) + 1; + else + m_height = height; + m_bounds.set(0, 0, SkIntToScalar(m_width), SkIntToScalar(m_height)); + } +} + +} //WebCore diff --git a/WebCore/platform/android/RenderSkinCombo.h b/WebCore/platform/android/RenderSkinCombo.h new file mode 100644 index 0000000..f962fcc --- /dev/null +++ b/WebCore/platform/android/RenderSkinCombo.h @@ -0,0 +1,61 @@ +/* libs/WebKitLib/WebKit/WebCore/platform/android/RenderSkinCombo.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef RenderSkinCombo_h +#define RenderSkinCombo_h + +#include "RenderSkinAndroid.h" +#include "SkBitmap.h" +#include "SkPaint.h" +#include "SkRect.h" + +namespace WebCore { + +// This is very similar to RenderSkinButton - maybe they should be the same class? +class RenderSkinCombo : public RenderSkinAndroid +{ +public: + RenderSkinCombo(); + virtual ~RenderSkinCombo() {} + + /** + * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use. + */ + static void Init(android::AssetManager*); + + virtual bool draw(PlatformGraphicsContext*); + virtual void notifyState(Node* element); + virtual void setDim(int width, int height); + + // The image is an extra 30 pixels wider than the RenderObject, so this accounts for that. + static int extraWidth() { return arrowMargin; } + +private: + SkRect m_bounds; // Maybe this should become a protected member of RenderSkinAndroid... + static SkBitmap m_bitmap[2]; // Collection of assets for a combo box + static bool m_decoded; // True if all assets were decoded + SkPaint m_paint; + // Could probably move m_state into RenderSkinAndroid... + // Although notice that the state for RenderSkinRadio is just an integer, and it behaves differently + State m_state; + + static const int arrowMargin = 30; +}; + +} // WebCore + +#endif diff --git a/WebCore/platform/android/RenderSkinRadio.cpp b/WebCore/platform/android/RenderSkinRadio.cpp new file mode 100644 index 0000000..d610fa7 --- /dev/null +++ b/WebCore/platform/android/RenderSkinRadio.cpp @@ -0,0 +1,83 @@ +/* libs/WebKitLib/WebKit/WebCore/platform/android/RenderSkinRadio.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "Document.h" +#include "Node.h" +#include "PlatformGraphicsContext.h" +#include "RenderSkinRadio.h" +#include "SkCanvas.h" + +static const char* checks[] = { "res/drawable/checkbox_off_background.png", "res/drawable/checkbox_on_background.png", + "res/drawable/radiobutton_off_background.png", "res/drawable/radiobutton_on_background.png"}; +static const SkScalar SIZE = SkIntToScalar(19); // Default height and width - corresponds with the bitmap - perhaps we should query the bitmap. + +namespace WebCore { + +SkBitmap RenderSkinRadio::m_bitmap[4]; +bool RenderSkinRadio::m_decoded; + +RenderSkinRadio::RenderSkinRadio(bool isCheckBox) +{ + m_checked = false; + m_enabled = true; + m_isCheckBox = isCheckBox; +} + +void RenderSkinRadio::Init(android::AssetManager* am) +{ + if (m_decoded) + return; + m_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[0], &m_bitmap[0]); + m_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[1], &m_bitmap[1]) && m_decoded; + m_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[2], &m_bitmap[2]) && m_decoded; + m_decoded = RenderSkinAndroid::DecodeBitmap(am, checks[3], &m_bitmap[3]) && m_decoded; +} + + +bool RenderSkinRadio::draw(PlatformGraphicsContext* pgc) +{ + if (!m_decoded) // Seems like an unnecessary slowdown, since it should always decode + return false; + SkCanvas* canvas = pgc->mCanvas; + if (!m_enabled) { + SkRect r; + r.set(0, 0, m_size, m_size); + canvas->saveLayerAlpha(&r, 0x80); + } else { + canvas->save(); + } + if (SIZE != m_size) { + SkScalar scale = SkScalarDiv(m_size, SIZE); + canvas->scale(scale, scale); + } + canvas->drawBitmap(m_bitmap[m_checked + 2*(!m_isCheckBox)], 0, 0, &m_paint); + canvas->restore(); + return false; // True if we need to redraw +} + +void RenderSkinRadio::notifyState(Node* element) +{ + if (!element) { + return; + } + m_checked = element->isChecked(); + m_enabled = element->isEnabled(); +} + +} //WebCore + diff --git a/WebCore/platform/android/RenderSkinRadio.h b/WebCore/platform/android/RenderSkinRadio.h new file mode 100644 index 0000000..918cdf5 --- /dev/null +++ b/WebCore/platform/android/RenderSkinRadio.h @@ -0,0 +1,59 @@ +/* libs/WebKitLib/WebKit/WebCore/platform/android/RenderSkinRadio.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef RenderSkinRadio_h +#define RenderSkinRadio_h + +#include "RenderSkinAndroid.h" +#include "SkBitmap.h" +#include "SkPaint.h" +#include "SkRect.h" + +namespace WebCore { + +class Node; + +/* RenderSkin for a radio button or a checkbox + */ +class RenderSkinRadio : public RenderSkinAndroid +{ +public: + /* This skin represents a checkbox if isCheckBox is true, otherwise it is a radio button */ + RenderSkinRadio(bool isCheckBox); + virtual ~RenderSkinRadio() {} + + /** + * Initialize the class before use. Uses the AssetManager to initialize any bitmaps the class may use. + */ + static void Init(android::AssetManager*); + + virtual bool draw(PlatformGraphicsContext*); + virtual void notifyState(Node* element); + virtual void setDim(int width, int height) { RenderSkinAndroid::setDim(width, height); m_size = SkIntToScalar(height); } + +protected: + static SkBitmap m_bitmap[4]; // Bitmaps representing all states + static bool m_decoded; // True if all assets were decoded. + bool m_isCheckBox; + bool m_checked; + bool m_enabled; + SkPaint m_paint; + SkScalar m_size; +}; + +} // WebCore +#endif diff --git a/WebCore/platform/android/RenderThemeAndroid.cpp b/WebCore/platform/android/RenderThemeAndroid.cpp new file mode 100644 index 0000000..2e8cb86 --- /dev/null +++ b/WebCore/platform/android/RenderThemeAndroid.cpp @@ -0,0 +1,313 @@ +/* libs/WebKitLib/WebKit/WebCore/rendering/RenderThemeAndroid.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "RenderThemeAndroid.h" +#include "PopupMenu.h" +#include "RenderSkinAndroid.h" +#include "RenderSkinButton.h" +#include "RenderSkinCombo.h" +#include "RenderSkinRadio.h" + +#include "GraphicsContext.h" +#include "PlatformGraphicsContext.h" + +#include "SkCanvas.h" + +#define MAX_COMBO_HEIGHT 20 + +// Add a constant amount of padding to the textsize to get the final height of buttons, +// so that our button images are large enough to properly fit the text. +#define BUTTON_PADDING 18 + +namespace WebCore { +static SkCanvas* getCanvasFromInfo(const RenderObject::PaintInfo& info) +{ + return info.context->platformContext()->mCanvas; +} + +/* Helper function that paints the RenderObject + * paramters: + * the skin to use, + * the object to be painted, the PaintInfo, from which we get the canvas, the bounding rectangle, + * returns false, meaning no one else has to paint it +*/ + +static bool paintBrush(RenderSkinAndroid* rSkin, RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + Node* element = o->element(); + SkCanvas* canvas = getCanvasFromInfo(i); + canvas->save(); + canvas->translate(SkIntToScalar(ir.x()), SkIntToScalar(ir.y())); + rSkin->setDim(ir.width(), ir.height()); + rSkin->notifyState(element); + rSkin->draw(i.context->platformContext()); + canvas->restore(); + return false; +} + + +RenderTheme* theme() +{ + static RenderThemeAndroid androidTheme; + return &androidTheme; +} + +RenderThemeAndroid::RenderThemeAndroid() +{ + m_radio = new RenderSkinRadio(false); + m_checkbox = new RenderSkinRadio(true); + m_combo = new RenderSkinCombo(); +} + +RenderThemeAndroid::~RenderThemeAndroid() +{ + delete m_radio; + delete m_checkbox; + delete m_combo; +} + +void RenderThemeAndroid::close() +{ + +} + +bool RenderThemeAndroid::stateChanged(RenderObject* o, ControlState state) const +{ + if (CheckedState == state) { + o->repaint(); + return true; + } + return false; +} + +Color RenderThemeAndroid::platformActiveSelectionBackgroundColor() const +{ + return Color(46, 251, 0); +} + +Color RenderThemeAndroid::platformInactiveSelectionBackgroundColor() const +{ + return Color(255, 255, 0, 255); +} + +Color RenderThemeAndroid::platformActiveSelectionForegroundColor() const +{ + return Color::black; +} + +Color RenderThemeAndroid::platformInactiveSelectionForegroundColor() const +{ + return Color::black; +} + +Color RenderThemeAndroid::platformTextSearchHighlightColor() const +{ + return Color(192, 192, 192); +} + +short RenderThemeAndroid::baselinePosition(const RenderObject* obj) const +{ + // From the description of this function in RenderTheme.h: + // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline + // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of + // controls that need to do this. + // + // Our checkboxes and radio buttons need to be offset to line up properly. + return RenderTheme::baselinePosition(obj) - 5; +} + +void RenderThemeAndroid::addIntrinsicMargins(RenderStyle* style) const +{ + // Cut out the intrinsic margins completely if we end up using a small font size + if (style->fontSize() < 11) + return; + + // Intrinsic margin value. + const int m = 2; + + // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed. + if (style->width().isIntrinsicOrAuto()) { + if (style->marginLeft().quirk()) + style->setMarginLeft(Length(m, Fixed)); + if (style->marginRight().quirk()) + style->setMarginRight(Length(m, Fixed)); + } + + if (style->height().isAuto()) { + if (style->marginTop().quirk()) + style->setMarginTop(Length(m, Fixed)); + if (style->marginBottom().quirk()) + style->setMarginBottom(Length(m, Fixed)); + } +} + +bool RenderThemeAndroid::supportsFocus(EAppearance appearance) +{ + switch (appearance) { + case PushButtonAppearance: + case ButtonAppearance: + case TextFieldAppearance: + return true; + default: + return false; + } + + return false; +} + +void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +{ + // Padding code is taken from RenderThemeSafari.cpp + // It makes sure we have enough space for the button text. + const int padding = 12; + style->setPaddingLeft(Length(padding, Fixed)); + style->setPaddingRight(Length(padding, Fixed)); + style->setMinHeight(Length(style->fontSize() + BUTTON_PADDING, Fixed)); +} + +bool RenderThemeAndroid::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + return paintBrush(m_checkbox, o, i, ir); +} + +bool RenderThemeAndroid::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + // If it is a disabled button, simply paint it to the master picture. + Node* element = o->element(); + if (!element->isEnabled()) { + RenderSkinButton::Draw(getCanvasFromInfo(i), ir, RenderSkinAndroid::kDisabled); + } else { + // Store all the important information in the platform context. + i.context->platformContext()->storeButtonInfo(element, ir); + } + // We always return false so we do not request to be redrawn. + return false; +} + +bool RenderThemeAndroid::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + return paintBrush(m_radio, o, i, ir); +} + +void RenderThemeAndroid::setCheckboxSize(RenderStyle* style) const +{ + style->setWidth(Length(19, Fixed)); + style->setHeight(Length(19, Fixed)); +} + +void RenderThemeAndroid::setRadioSize(RenderStyle* style) const +{ + // This is the same as checkboxes. + setCheckboxSize(style); +} + +void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +{ + addIntrinsicMargins(style); +} + +bool RenderThemeAndroid::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + return true; +} + +void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +{ + addIntrinsicMargins(style); +} + +bool RenderThemeAndroid::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + if (o->isMenuList()) { + return paintCombo(o, i, ir); + } + return true; +} + +void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + addIntrinsicMargins(style); +} + +bool RenderThemeAndroid::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + return true; +} + +void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed)); + addIntrinsicMargins(style); +} + +bool RenderThemeAndroid::paintCombo(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + if (o->style() && o->style()->backgroundColor().alpha() == 0) + return true; + Node* element = o->element(); + SkCanvas* canvas = getCanvasFromInfo(i); + m_combo->notifyState(element); + canvas->save(); + int height = ir.height(); + int y = ir.y(); + // If the combo box is too large, leave it at its max height, and center it. + if (height > MAX_COMBO_HEIGHT) { + y += (height - MAX_COMBO_HEIGHT) >> 1; + height = MAX_COMBO_HEIGHT; + } + canvas->translate(SkIntToScalar(ir.x()), SkIntToScalar(y)); + m_combo->setDim(ir.width(), height); + m_combo->draw(i.context->platformContext()); + canvas->restore(); + return false; +} + +bool RenderThemeAndroid::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + return paintCombo(o, i, ir); +} + +void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed)); + addIntrinsicMargins(style); +} + +bool RenderThemeAndroid::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +{ + return paintCombo(o, i, ir); +} + +bool RenderThemeAndroid::supportsFocusRing(const RenderStyle* style) const +{ + return (style->opacity() > 0 && style->hasAppearance() + && style->appearance() != TextFieldAppearance + && style->appearance() != SearchFieldAppearance + && style->appearance() != TextAreaAppearance + && style->appearance() != CheckboxAppearance + && style->appearance() != RadioAppearance + && style->appearance() != PushButtonAppearance + && style->appearance() != SquareButtonAppearance + && style->appearance() != ButtonAppearance + && style->appearance() != ButtonBevelAppearance + && style->appearance() != MenulistAppearance + && style->appearance() != MenulistButtonAppearance + ); +} + +} diff --git a/WebCore/platform/android/RenderThemeAndroid.h b/WebCore/platform/android/RenderThemeAndroid.h new file mode 100644 index 0000000..4233f6f --- /dev/null +++ b/WebCore/platform/android/RenderThemeAndroid.h @@ -0,0 +1,102 @@ +/* libs/WebKitLib/WebKit/WebCore/rendering/RenderThemeAndroid.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef RenderThemeAndroid_h +#define RenderThemeAndroid_h + +#include "RenderTheme.h" + +namespace WebCore { + +class PopupMenu; +class RenderSkinButton; +class RenderSkinRadio; +class RenderSkinCombo; + +struct ThemeData { + ThemeData() :m_part(0), m_state(0) {} + + unsigned m_part; + unsigned m_state; +}; + +class RenderThemeAndroid : public RenderTheme { +public: + RenderThemeAndroid(); + ~RenderThemeAndroid(); + + virtual bool stateChanged(RenderObject*, ControlState) const; + + virtual bool supportsFocusRing(const RenderStyle* style) const; + // A method asking if the theme's controls actually care about redrawing when hovered. + virtual bool supportsHover(const RenderStyle* style) const { return style->affectedByHoverRules(); } + + virtual short baselinePosition(const RenderObject*) const; + + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveSelectionForegroundColor() const; + virtual Color platformInactiveSelectionForegroundColor() const; + virtual Color platformTextSearchHighlightColor() const; + + virtual void systemFont(int, WebCore::FontDescription&) const {} + + virtual int minimumMenuListSize(RenderStyle*) const { return 0; } + +protected: + virtual bool paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void setCheckboxSize(RenderStyle* style) const; + + virtual bool paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void setRadioSize(RenderStyle* style) const; + + virtual void adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const; + virtual bool paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + + virtual void adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const; + virtual bool paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + + virtual void adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const; + virtual bool paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + + bool paintCombo(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir); + + virtual void adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const; + virtual bool paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + + virtual void adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const; + virtual bool paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + +private: + void addIntrinsicMargins(RenderStyle* style) const; + void close(); + + bool supportsFocus(EAppearance appearance); + // FIXME: There should be a way to use one RenderSkinRadio for both radio and checkbox + RenderSkinRadio* m_radio; + RenderSkinRadio* m_checkbox; + RenderSkinCombo* m_combo; +}; + +}; + +#endif + diff --git a/WebCore/platform/android/ScreenAndroid.cpp b/WebCore/platform/android/ScreenAndroid.cpp new file mode 100644 index 0000000..6cf8bc9 --- /dev/null +++ b/WebCore/platform/android/ScreenAndroid.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "Screen.h" + +#include "FloatRect.h" +#include "Widget.h" + +#define LOG_TAG "WebCore" +#include "utils/Log.h" + +#include "ui/SurfaceComposerClient.h" +#include "ui/PixelFormat.h" +#include "ui/DisplayInfo.h" + +namespace WebCore { + +int screenDepth(Widget* page) +{ + android::DisplayInfo info; + android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info); + return info.pixelFormatInfo.bitsPerPixel; +} + +int screenDepthPerComponent(Widget* page) +{ + android::DisplayInfo info; + android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info); + return info.pixelFormatInfo.bitsPerPixel; +} + +bool screenIsMonochrome(Widget* page) +{ + return false; +} + +#ifdef ANDROID_ORIENTATION_SUPPORT +int Screen::orientation() const +{ + android::DisplayInfo info; + android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info); + return info.orientation; +} +#endif + +// The only place I have seen these values used is +// positioning popup windows. If we support multiple windows +// they will be most likely full screen. Therefore, +// the accuracy of these number are not too important. +FloatRect screenRect(Widget* page) +{ + android::DisplayInfo info; + android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info); + return FloatRect(0.0, 0.0, info.w, info.h); +} + +/* +Scale functions don't seem to be needed. There is no code that +call them, and they seem to only exist in ports and not in the core +WebKit code - davidc +FloatRect usableScreenRect(Widget* page) +{ + return FloatRect();; +} + +FloatRect scaleScreenRectToPageCoordinates(const FloatRect&, Widget*) +{ + return FloatRect();; +} + +FloatRect scalePageRectToScreenCoordinates(const FloatRect&, Widget*) +{ + return FloatRect();; +} + +float scaleFactor(Widget*) +{ + return 1.0f; +} +*/ + +// Similar screenRect, this function is commonly used by javascripts +// to position and resize windows (usually to full screen). +FloatRect screenAvailableRect(Widget*) +{ + android::DisplayInfo info; + android::SurfaceComposerClient::getDisplayInfo(android::DisplayID(0), &info); + return FloatRect(0.0, 0.0, info.w, info.h); +} + +} diff --git a/WebCore/platform/android/ScrollViewAndroid.cpp b/WebCore/platform/android/ScrollViewAndroid.cpp new file mode 100644 index 0000000..1ac819d --- /dev/null +++ b/WebCore/platform/android/ScrollViewAndroid.cpp @@ -0,0 +1,334 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "ScrollView.h" + +#include "FloatRect.h" +#include "IntRect.h" + +#include "WebCoreViewBridge.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include "utils/Log.h" + +/* hack to allow the DOM to communicate how to interpret inval requests, since + it doesn't have the notion of distinguishing between the screen and the DOM + but we do, since we have a copy of the display in our picture, and we don't + want to rebuild the picture unless the DOM has actually been changed. +*/ +bool gAndroid_treatInvalForScreen; + +#define verifiedOk() // no need to do anything in this function + +/* + This class implementation does NOT actually emulate the Qt ScrollView. + It does provide an implementation that khtml will use to interact with + WebKit's WebFrameView documentView and our NSScrollView subclass. + + ScrollView's view is a NSScrollView (or subclass of NSScrollView) + in most cases. That scrollview is a subview of an + WebCoreFrameView. The WebCoreFrameView's documentView will also be + the scroll view's documentView. + + The WebCoreFrameView's size is the frame size. The WebCoreFrameView's documentView + corresponds to the frame content size. The scrollview itself is autosized to the + WebCoreFrameView's size (see Widget::resize). +*/ + +namespace WebCore { + +struct ScrollView::ScrollViewPrivate +{ +public: + ScrollViewPrivate() : + hasStaticBackground(false), ignoreUpdateContents(false), + vScrollbarMode(ScrollbarAuto), + hScrollbarMode(ScrollbarAuto) {} + IntSize contentsSize; + bool hasStaticBackground; + bool ignoreUpdateContents; + ScrollbarMode vScrollbarMode; + ScrollbarMode hScrollbarMode; +}; + +ScrollView::ScrollView() +{ + m_data = new ScrollViewPrivate; +} + +ScrollView::~ScrollView() +{ + delete m_data; +} + +int ScrollView::visibleWidth() const +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + return this->getWebCoreViewBridge()->width(); +} + +int ScrollView::visibleHeight() const +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + return this->getWebCoreViewBridge()->height(); +} + +FloatRect ScrollView::visibleContentRect() const +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + IntRect rect = this->getWebCoreViewBridge()->getBounds(); + // FIXME: This is a hack to get subframes drawing correctly. Since subframes cannot + // scroll, we know that if this view has a parent, the visible rect is (0, 0, w, h) + if (this->getWebCoreViewBridge()->getParent()) + return FloatRect(0, 0, rect.width(), rect.height()); + return FloatRect(rect.x(), rect.y(), rect.width(), rect.height()); +} + +int ScrollView::contentsWidth() const +{ + return m_data->contentsSize.width(); +} + +int ScrollView::contentsHeight() const +{ + return m_data->contentsSize.height(); +} + +int ScrollView::contentsX() const +{ + return scrollOffset().width(); +} + +int ScrollView::contentsY() const +{ + return scrollOffset().height(); +} + +IntSize ScrollView::scrollOffset() const +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + WebCoreViewBridge* bridge = this->getWebCoreViewBridge(); + // FIXME: This is a hack to get subframes drawing correctly. Since subframes cannot + // scroll, we know that if this view has a parent, the scroll offset is always (0, 0) + if (bridge->getParent()) + return IntSize(0, 0); + return IntSize(bridge->locX(), bridge->locY()); +} + +void ScrollView::scrollBy(int dx, int dy) +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + this->getWebCoreViewBridge()->scrollBy(dx, dy); +} + +void WebCore::ScrollView::update() +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + this->getWebCoreViewBridge()->contentInvalidate(); +} + +void WebCore::ScrollView::scrollRectIntoViewRecursively(WebCore::IntRect const& r) +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + int x = r.x(); + int y = r.y(); + IntPoint p(x > 0 ? x : 0, y > 0 ? y : 0); + ScrollView* view = this; + while (view) { + view->setContentsPos(p.x(), p.y()); + p.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height()); + if (view->getWebCoreViewBridge()->getParent()) + view = static_cast<ScrollView*>(view->getWebCoreViewBridge()->getParent()->widget()); + else + view = NULL; + } +} + +WebCore::FloatRect WebCore::ScrollView::visibleContentRectConsideringExternalScrollers() const { + return FloatRect(contentsX(), contentsY(), contentsWidth(), contentsHeight()); +} + +void ScrollView::setContentsPos(int x, int y) +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + this->getWebCoreViewBridge()->scrollTo(x, y); +} + +//--------------------------------------------------------------------- +// Scroll bar methods +// +// These methods are largely unimplemented meaning that the +// state of the scroll bars in the only data maintained. If +// a scroll bar is needed, the set(V|H)ScrollbarMode methods +// need to update the scroll bar widget through the java +// layer and display the new scroll bars. +// +//--------------------------------------------------------------------- + +void ScrollView::setVScrollbarMode(ScrollbarMode vMode) +{ + m_data->vScrollbarMode = vMode; +} + +void ScrollView::setHScrollbarMode(ScrollbarMode hMode) +{ + m_data->hScrollbarMode = hMode; +} + +void ScrollView::setScrollbarsMode(ScrollbarMode mode) +{ + m_data->hScrollbarMode = mode; + m_data->vScrollbarMode = mode; +} + +ScrollbarMode ScrollView::vScrollbarMode() const +{ + return m_data->vScrollbarMode; +} + +ScrollbarMode ScrollView::hScrollbarMode() const +{ + return m_data->hScrollbarMode; +} + +void ScrollView::suppressScrollbars(bool suppressed, bool repaintOnUnsuppress) +{ + verifiedOk(); +} + +//--------------------------------------------------------------------- +// End Scroll bar methods +//--------------------------------------------------------------------- + +void ScrollView::addChild(Widget* child) +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + WebCoreViewBridge* childBridge = child->getWebCoreViewBridge(); + // FIXME: For now just check this, it should be an assert down the road. + if (!childBridge) + { + LOGV("childBridge is not set"); + return; + } + LOG_ASSERT(child != this, "Child has no view bridge or child == this!"); + WebCoreViewBridge* thisBridge = this->getWebCoreViewBridge(); + LOG_ASSERT(thisBridge && thisBridge != childBridge, "Our bridge is not set or thisBridge == childBridge!"); + LOGV("Adding parent"); + childBridge->setParent(thisBridge); +} + +void ScrollView::ignoreUpdateContents(bool ignore) +{ + m_data->ignoreUpdateContents = ignore; +} + +void ScrollView::removeChild(Widget* child) +{ + // FIXME: Make this only an assert once all widgets have views + if (!child->getWebCoreViewBridge()) + { + LOGV("child has no bridge"); + return; + } + LOG_ASSERT(child->getWebCoreViewBridge(), "Child has no view bridge"); + child->getWebCoreViewBridge()->setParent(NULL); +} + +void ScrollView::resizeContents(int w, int h) +{ + LOG_ASSERT(this->getWebCoreViewBridge(), "ScrollView does not have a WebCoreViewBridge"); + if (w < 0) + w = 0; + if (h < 0) + h = 0; + + IntSize newSize(w, h); + m_data->contentsSize = newSize; +} + +void ScrollView::updateContents(const IntRect &rect, bool now) +{ + LOG_ASSERT(this->getWebCoreViewBridge(), + "ScrollView does not have a WebCoreViewBridge"); + + WebCoreViewBridge* bridge = this->getWebCoreViewBridge(); + + if (gAndroid_treatInvalForScreen) { +// SkDebugf("------ contentInvalidate sent to viewInvalidate [%d %d]\n", rect.width(), rect.height()); + bridge->viewInvalidate(); + } else if (m_data->ignoreUpdateContents == false) { + bridge->contentInvalidate(rect); + } +} + +IntPoint ScrollView::windowToContents(const IntPoint& contentsPoint) const +{ + WebCoreViewBridge* bridge = this->getWebCoreViewBridge(); + WebCoreViewBridge* parent = bridge->getParent(); + int x = 0, y= 0; + while (parent) { + x += bridge->locX(); + y += bridge->locY(); + bridge = parent; + parent = bridge->getParent(); + } + return IntPoint(contentsPoint.x() - x, contentsPoint.y() - y); +} + +IntPoint ScrollView::contentsToWindow(const IntPoint& viewportPoint) const +{ + WebCoreViewBridge* bridge = this->getWebCoreViewBridge(); + WebCoreViewBridge* parent = bridge->getParent(); + int x = 0, y= 0; + while (parent) { + x += bridge->locX(); + y += bridge->locY(); + bridge = parent; + parent = bridge->getParent(); + } + return IntPoint(viewportPoint.x() + x, viewportPoint.y() + y); +} + +void ScrollView::setStaticBackground(bool) +{ + // we don't have any optimizations for this + verifiedOk(); +} + +bool ScrollView::inWindow() const +{ + LOGV("inWindow Unimplemented"); +#if 0 + return [getView() window]; +#endif + return true; +} + +void ScrollView::wheelEvent(PlatformWheelEvent&) +{ + verifiedOk(); +} + +PlatformScrollbar* ScrollView::scrollbarUnderMouse(const PlatformMouseEvent& mouseEvent) +{ + verifiedOk(); + return NULL; +} + +} diff --git a/WebCore/platform/android/SearchPopupMenuAndroid.cpp b/WebCore/platform/android/SearchPopupMenuAndroid.cpp new file mode 100644 index 0000000..0c9075f --- /dev/null +++ b/WebCore/platform/android/SearchPopupMenuAndroid.cpp @@ -0,0 +1,57 @@ +/* +** libs/WebKitLib/WebKit/WebCore/platform/android/SearchPopupMenuAndroid.cpp +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SearchPopupMenuAndroid_h +#define SearchPopupMenuAndroid_h + +#include "config.h" +#include "SearchPopupMenu.h" + +namespace WebCore { + +// Save the past searches stored in 'searchItems' to a database associated with 'name' +void SearchPopupMenu::saveRecentSearches(const AtomicString& name, const Vector<String>& searchItems) +{ + + //ASSERT(0); //notImplemented(); + +} + +// Load past searches associated with 'name' from the database to 'searchItems' +void SearchPopupMenu::loadRecentSearches(const AtomicString& name, Vector<String>& searchItems) +{ + + //ASSERT(0); //notImplemented(); + +} + +// Create a search popup menu - not sure what else we have to do here +SearchPopupMenu::SearchPopupMenu(PopupMenuClient* client) : PopupMenu(client) +{ + + //ASSERT(0); //notImplemented(); + +} + +// functions new to Jun-07 tip of tree merge: +bool SearchPopupMenu::enabled() { return false; } + +} // WebCore + +#endif + diff --git a/WebCore/platform/android/SharedTimerAndroid.cpp b/WebCore/platform/android/SharedTimerAndroid.cpp new file mode 100644 index 0000000..756d636 --- /dev/null +++ b/WebCore/platform/android/SharedTimerAndroid.cpp @@ -0,0 +1,57 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "SharedTimer.h" +#include "SystemTime.h" +#include "JavaSharedClient.h" +#include "TimerClient.h" +#define LOG_TAG "Timers" +#undef LOG +#include "utils/Log.h" + +namespace WebCore { + + // Single timer, shared to implement all the timers managed by the Timer class. + // Not intended to be used directly; use the Timer class instead. + + void setSharedTimerFiredFunction(void (*f)()) + { + if (JavaSharedClient::GetTimerClient()) + JavaSharedClient::GetTimerClient()->setSharedTimerCallback(f); + } + + // The fire time is relative to the classic POSIX epoch of January 1, 1970, + // as the result of currentTime() is. + + void setSharedTimerFireTime(double fireTime) + { + long long timeInMS = (long long)((fireTime - currentTime()) * 1000); + + LOGV("setSharedTimerFireTime: in %ld millisec", timeInMS); + if (JavaSharedClient::GetTimerClient()) + JavaSharedClient::GetTimerClient()->setSharedTimer(timeInMS); + } + + void stopSharedTimer() + { + if (JavaSharedClient::GetTimerClient()) + JavaSharedClient::GetTimerClient()->stopSharedTimer(); + } + +} + diff --git a/WebCore/platform/android/SystemTimeAndroid.cpp b/WebCore/platform/android/SystemTimeAndroid.cpp new file mode 100644 index 0000000..3dfc433 --- /dev/null +++ b/WebCore/platform/android/SystemTimeAndroid.cpp @@ -0,0 +1,52 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "SystemTime.h" +#include <sys/time.h> + +namespace WebCore { + +double currentTime() +{ + struct timeval tv; + + gettimeofday(&tv, (struct timezone *) NULL); + double now = tv.tv_sec + (tv.tv_usec / 1000000.0); + + return now; +} + +#ifdef ANDROID_INSTRUMENT +uint32_t get_thread_msec() +{ +#if defined(HAVE_POSIX_CLOCKS) + struct timespec tm; + + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm); + + return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000; +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000LL + tv.tv_usec / 1000; +#endif +} +#endif + +} diff --git a/WebCore/platform/android/TemporaryLinkStubs.cpp b/WebCore/platform/android/TemporaryLinkStubs.cpp new file mode 100644 index 0000000..74712f5 --- /dev/null +++ b/WebCore/platform/android/TemporaryLinkStubs.cpp @@ -0,0 +1,290 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" + +#define ANDROID_COMPILE_HACK + +#include <stdio.h> +#include <stdlib.h> +#include "AXObjectCache.h" +#include "CachedPage.h" +#include "CachedResource.h" +#include "CookieJar.h" +#include "ContextMenu.h" +#include "ContextMenuItem.h" +#include "Clipboard.h" +#include "CString.h" +#include "Cursor.h" +#include "DocumentFragment.h" +#include "DocumentLoader.h" +#include "EditCommand.h" +#include "Editor.h" +#include "Font.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLKeygenElement.h" +#include "History.h" +#include "Icon.h" +#include "IconDatabase.h" +#include "IconLoader.h" +#include "IntPoint.h" +#include "KURL.h" +#include "Language.h" +#include "loader.h" +#include "LocalizedStrings.h" +#include "MainResourceLoader.h" +#include "MIMETypeRegistry.h" +#include "Node.h" +#include "PageCache.h" +#include "Pasteboard.h" +#include "Path.h" +#include "PlatformScrollBar.h" +#include "PluginInfoStore.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceLoader.h" +#include "Screen.h" +#include "ScrollBar.h" +#include "SmartReplace.h" +#include "Widget.h" + +#undef LOG +#define LOG_TAG "WebCore" +#include "utils/Log.h" + +using namespace WebCore; + +//static inline void notImplemented() { LOGV("LinkStubs: Not Implemented %s\n", __PRETTY_FUNCTION__); } +//static inline void notImplemented(const char name[]) { LOGV("LinkStubs: Not Implemented %s\n", name); } +static inline void verifiedOk() { } // not a problem that it's not implemented + +// This function is called when the frame view has changed the state of it's border. +// iFrames, which are have a FrameView, are drawn with a 1px left/right border and 2px top/bottom border +// Check function _shouldDrawBorder in WebFrameView.mm +// We don't draw borders unless css draws them. +// void FrameView::updateBorder() { verifiedOk(); } + +//int WebCore::screenDepthPerComponent(Widget*) { ASSERT(0); notImplemented(); return 0; } +//bool WebCore::screenIsMonochrome(Widget*) { ASSERT(0); notImplemented(); return false; } + +/********************************************************/ +/* Completely empty stubs (mostly to allow DRT to run): */ +/********************************************************/ +// This value turns on or off the Mac specific Accessibility support. +// bool AXObjectCache::gAccessibilityEnabled = false; + +// This function is used by Javascript to find out what the default language +// the user has selected. It is used by the JS object Navigator.language +// I guess this information should be mapped with the Accept-Language: HTTP header. +String WebCore::defaultLanguage() { verifiedOk(); return "en"; } + +namespace WebCore { + +#if !defined(ANDROID_PLUGINS) +// If plugins support is turned on, don't use these stubs. + +// Except for supportsMIMEType(), these Plugin functions are used by javascript's +// navigator.plugins[] object to provide the list of available plugins. This is most +// often used with to check to see if the browser supports Flash or which video +// codec to use. +// The supportsMIMEType() is used by the Frame to determine if a full screen instance +// of a plugin can be used to render a mimetype that is not native to the browser. +PluginInfo* PluginInfoStore::createPluginInfoForPluginAtIndex(unsigned) { ASSERT(0); return 0;} +unsigned PluginInfoStore::pluginCount() const { verifiedOk(); return 0; } +// FIXME, return false for now. +String PluginInfoStore::pluginNameForMIMEType(const String& ) { notImplemented(); return String(); } +bool PluginInfoStore::supportsMIMEType(const String& ) { verifiedOk(); return false; } +void refreshPlugins(bool) { verifiedOk(); } +#endif // !defined(ANDROID_PLUGINS) + +// This function tells the bridge that a resource was loaded from the cache and thus +// the app may update progress with the amount of data loaded. +void CheckCacheObjectStatus(DocLoader*, CachedResource*) { ASSERT(0); notImplemented();} + +// This class is used in conjunction with the File Upload form element, and +// therefore relates to the above. When a file has been selected, an icon +// representing the file type can be rendered next to the filename on the +// web page. The icon for the file is encapsulated within this class. +Icon::Icon() { notImplemented(); } +Icon::~Icon() { } +PassRefPtr<Icon> Icon::newIconForFile(const String& filename){ notImplemented(); return PassRefPtr<Icon>(new Icon()); } +void Icon::paint(GraphicsContext*, const IntRect&) { } + +// *** The following strings should be localized *** // + +// The following functions are used to fetch localized text for HTML form +// elements submit and reset. These strings are used when the page author +// has not specified any text for these buttons. +String submitButtonDefaultLabel() { verifiedOk(); return "Submit"; } +String resetButtonDefaultLabel() { verifiedOk(); return "Reset"; } + +// The alt text for an input element is not used visually, but rather is +// used for accessability - eg reading the web page. See +// HTMLInputElement::altText() for more information. +String inputElementAltText() { notImplemented(); return String(); } + +// This is the string that appears before an input box when the HTML element +// <ISINDEX> is used. The returned string is used if no PROMPT attribute is +// provided. +// note: Safari and FireFox use (too long for us imho) "This is a searchable index. Enter search keywords:" +String searchableIndexIntroduction() { verifiedOk(); return String("Enter search:"); } + +} + +// This function provides the default value for the CSS property: +// -webkit-focus-ring-color +// It is also related to the CSS property outline-color: +Color WebCore::focusRingColor() { verifiedOk(); return 0xFF0000FF; } + +// The following functions are used on the Mac to register for and handle +// platform colour changes. The later function simply tells the view to +// reapply style and relayout. +void WebCore::setFocusRingColorChangeFunction(void (*)()) { verifiedOk(); } + +// LocalizedStrings +String WebCore::contextMenuItemTagOpenLinkInNewWindow() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagDownloadLinkToDisk() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagCopyLinkToClipboard() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagOpenImageInNewWindow() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagDownloadImageToDisk() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagCopyImageToClipboard() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagOpenFrameInNewWindow() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagCopy() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagGoBack() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagGoForward() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagStop() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagReload() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagCut() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagPaste() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagNoGuessesFound() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagIgnoreSpelling() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagLearnSpelling() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagSearchWeb() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagLookUpInDictionary() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagOpenLink() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagIgnoreGrammar() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagSpellingMenu() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagShowSpellingPanel(bool show) { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagCheckSpelling() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagCheckSpellingWhileTyping() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagCheckGrammarWithSpelling() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagFontMenu() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagBold() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagItalic() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagUnderline() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagOutline() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagWritingDirectionMenu() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagDefaultDirection() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagLeftToRight() { ASSERT(0); return String(); } +String WebCore::contextMenuItemTagRightToLeft() { ASSERT(0); return String(); } + +// FIXME, no support for spelling yet. +Pasteboard* Pasteboard::generalPasteboard() { return new Pasteboard(); } +void Pasteboard::writeSelection(Range*, bool canSmartCopyOrDelete, Frame*) { notImplemented(); } +void Pasteboard::writeURL(const KURL&, const String&, Frame*) { notImplemented(); } +void Pasteboard::clear() { notImplemented(); } +bool Pasteboard::canSmartReplace() { notImplemented(); return false; } +PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame*, PassRefPtr<Range>, bool allowPlainText, bool& chosePlainText) { notImplemented(); return 0; } +String Pasteboard::plainText(Frame* frame) { notImplemented(); return String(); } +Pasteboard::Pasteboard() { notImplemented(); } +Pasteboard::~Pasteboard() { notImplemented(); } + +ContextMenu::ContextMenu(const HitTestResult& result) : m_hitTestResult(result) { ASSERT(0); notImplemented(); } +ContextMenu::~ContextMenu() { ASSERT(0); notImplemented(); } +void ContextMenu::appendItem(ContextMenuItem&) { ASSERT(0); notImplemented(); } +void ContextMenu::setPlatformDescription(PlatformMenuDescription menu) { ASSERT(0); m_platformDescription = menu; } +PlatformMenuDescription ContextMenu::platformDescription() const { ASSERT(0); return m_platformDescription; } + +ContextMenuItem::ContextMenuItem(PlatformMenuItemDescription) { ASSERT(0); notImplemented(); } +ContextMenuItem::ContextMenuItem(ContextMenu*) { ASSERT(0); notImplemented(); } +ContextMenuItem::ContextMenuItem(ContextMenuItemType type, ContextMenuAction action, const String& title, ContextMenu* subMenu) { ASSERT(0); notImplemented(); } +ContextMenuItem::~ContextMenuItem() { ASSERT(0); notImplemented(); } +PlatformMenuItemDescription ContextMenuItem::releasePlatformDescription() { ASSERT(0); notImplemented(); return m_platformDescription; } +ContextMenuItemType ContextMenuItem::type() const { ASSERT(0); notImplemented(); return ActionType; } +void ContextMenuItem::setType(ContextMenuItemType) { ASSERT(0); notImplemented(); } +ContextMenuAction ContextMenuItem::action() const { ASSERT(0); notImplemented(); return ContextMenuItemTagNoAction; } +void ContextMenuItem::setAction(ContextMenuAction) { ASSERT(0); notImplemented(); } +String ContextMenuItem::title() const { ASSERT(0); notImplemented(); return String(); } +void ContextMenuItem::setTitle(const String&) { ASSERT(0); notImplemented(); } +PlatformMenuDescription ContextMenuItem::platformSubMenu() const { ASSERT(0); notImplemented(); return 0; } +void ContextMenuItem::setSubMenu(ContextMenu*) { ASSERT(0); notImplemented(); } +void ContextMenuItem::setChecked(bool) { ASSERT(0); notImplemented(); } +void ContextMenuItem::setEnabled(bool) { ASSERT(0); notImplemented(); } + +namespace WebCore { +float userIdleTime() { notImplemented(); return 0; } +// systemBeep() is called by the Editor to indicate that there was nothing to copy, and may be called from +// other places too. +void systemBeep() { notImplemented(); } +} + +// functions new to Jun-07 tip of tree merge: + +// void WebCore::CachedPage::close() {} + +//void WebCore::Frame::print() {} +// void WebCore::Frame::issueTransposeCommand() {} +//void WebCore::Frame::cleanupPlatformScriptObjects() {} +void WebCore::Frame::dashboardRegionsChanged() {} +//bool WebCore::Frame::isCharacterSmartReplaceExempt(unsigned short, bool) { return false; } +void* WebCore::Frame::dragImageForSelection() { return NULL; } + +WebCore::String WebCore::MIMETypeRegistry::getMIMETypeForExtension(WebCore::String const&) { + return WebCore::String(); +} + +void WebCore::Pasteboard::writeImage(WebCore::Node*, WebCore::KURL const&, WebCore::String const&) {} + +namespace WebCore { +IntSize dragImageSize(void*) { return IntSize(0, 0); } +void deleteDragImage(void*) {} +void* createDragImageFromImage(Image*) { return NULL; } +void* dissolveDragImageToFraction(void*, float) { return NULL; } +void* createDragImageIconForCachedImage(CachedImage*) { return NULL; } +Cursor dummyCursor; +const Cursor& zoomInCursor() { return dummyCursor; } +const Cursor& zoomOutCursor() { return dummyCursor; } +const Cursor& notAllowedCursor() { return dummyCursor; } +void* scaleDragImage(void*, FloatSize) { return NULL; } +String searchMenuRecentSearchesText() { return String(); } +String searchMenuNoRecentSearchesText() { return String(); } +String searchMenuClearRecentSearchesText() { return String(); } +Vector<String> supportedKeySizes() { notImplemented(); return Vector<String>(); } +String signedPublicKeyAndChallengeString(unsigned int, String const&, WebCore::KURL const&) { return String(); } + +} + +// added for Nov-16-07 ToT integration +namespace WebCore { +void Frame::clearPlatformScriptObjects() { notImplemented(); } +} + +// functions new to Feb-19 tip of tree merge: +namespace WebCore { +// isCharacterSmartReplaceExempt is defined in SmartReplaceICU.cpp; in theory, we could use that one +// but we don't support all of the required icu functions +bool isCharacterSmartReplaceExempt(UChar32 , bool ) { notImplemented(); return false; } +} + +int MakeDataExecutable; + +// functions new to Mar-2 tip of tree merge: +String KURL::fileSystemPath() const { notImplemented(); return String(); } diff --git a/WebCore/platform/android/TextBoundaries.cpp b/WebCore/platform/android/TextBoundaries.cpp new file mode 100644 index 0000000..e4f8c58 --- /dev/null +++ b/WebCore/platform/android/TextBoundaries.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006, 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "TextBoundaries.h" + +#include <unicode/ubrk.h> + +#include "TextBreakIterator.h" + +namespace WebCore { + +int findNextWordFromIndex(const UChar* chars, int len, int position, bool forward) +{ + UBreakIterator* it = wordBreakIterator(chars, len); + + int newPosition = position; + + if (forward) { + position = ubrk_following(it, position); + while (position != UBRK_DONE) { + // We stop searching when the character preceeding the break + // is alphanumeric. + if (position < len && u_isalnum(chars[position - 1])) + return position; + + position = ubrk_following(it, position); + } + + return len; + } else { + position = ubrk_preceding(it, position); + while (position != UBRK_DONE) { + // We stop searching when the character following the break + // is alphanumeric. + if (position > 0 && u_isalnum(chars[position])) + return position; + + position = ubrk_preceding(it, position); + } + + return 0; + } +} + +void findWordBoundary(const UChar* chars, int len, int position, int* start, int* end) +{ + UBreakIterator* it = wordBreakIterator(chars, len); + *end = ubrk_following(it, position); + if (*end < 0) + *end = ubrk_last(it); + *start = ubrk_previous(it); +} + +} // namespace WebCore diff --git a/WebCore/platform/android/TextBreakIteratorInternalICU.cpp b/WebCore/platform/android/TextBreakIteratorInternalICU.cpp new file mode 100644 index 0000000..19f33e5 --- /dev/null +++ b/WebCore/platform/android/TextBreakIteratorInternalICU.cpp @@ -0,0 +1,28 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "TextBreakIteratorInternalICU.h" + +namespace WebCore { + +const char* currentTextBreakLocaleID() +{ + return "en_us"; +} + +} diff --git a/WebCore/platform/android/TextEditAndroid.cpp b/WebCore/platform/android/TextEditAndroid.cpp new file mode 100644 index 0000000..a946465 --- /dev/null +++ b/WebCore/platform/android/TextEditAndroid.cpp @@ -0,0 +1,377 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "KWQTextEdit.h" +#include "Font.h" +#include "IntSize.h" +#include "WebCoreViewBridge.h" +#include "GraphicsContext.h" +#include "PlatformGraphicsContext.h" + +#include "SkCanvas.h" +#include "SkString.h" +#include "SkUtils.h" +#include "SkPaint.h" + +#undef LOG +#define LOG_TAG "WebCore" +#undef LOGI +#undef LOG +#include "utils/Log.h" + +static void setstr(SkString* dst, const WebCore::String& src) +{ + if (src.length()) { + const uint16_t* uni = (const uint16_t*)src.characters(); + size_t size = SkUTF16_ToUTF8(uni, src.length(), 0); + + dst->resize(size); + SkUTF16_ToUTF8(uni, src.length(), dst->writable_str()); + } +} + +class TextEditViewBridge : public WebCoreViewBridge { +public: + TextEditViewBridge(QTextEdit* te); + + virtual void draw(WebCore::GraphicsContext* ctx, const IntRect& rect); + virtual bool isEnabled() const; + virtual void setEnabled(bool); + virtual bool hasFocus() const; + virtual void setFocus(bool); + +private: + QTextEdit* m_te; + bool m_enabled; + bool m_focused; +}; + +TextEditViewBridge::TextEditViewBridge(QTextEdit* te) +{ + m_te = te; + this->setBounds(0,0,0,0); + m_enabled = true; + m_focused = false; +} + +void TextEditViewBridge::draw(WebCore::GraphicsContext* ctx, const IntRect& rect) +{ + SkCanvas* canvas = ctx->platformContext()->mCanvas; + SkPaint paint; + SkRect r; + + paint.setColor(m_focused ? SK_ColorGREEN : SK_ColorBLUE); + r.set( SkIntToScalar(rect.x()), SkIntToScalar(rect.y()), + SkIntToScalar(rect.right()), SkIntToScalar(rect.bottom())); + canvas->drawRect(r, paint); +} + +bool TextEditViewBridge::isEnabled() const { return m_enabled; } +void TextEditViewBridge::setEnabled(bool e) { m_enabled = e; } + +bool TextEditViewBridge::hasFocus() const { return m_focused; } +void TextEditViewBridge::setFocus(bool f) { m_focused = f; } + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +using namespace WebCore; + +QTextEdit::QTextEdit(Widget *parent) +{ + this->setWebCoreViewBridge(new TextEditViewBridge(this)); + +#if 0 + BEGIN_BLOCK_OBJC_EXCEPTIONS; + WebCoreTextArea *textView = [[WebCoreTextArea alloc] initWithQTextEdit:this]; + setView(textView); + [textView release]; + END_BLOCK_OBJC_EXCEPTIONS; +#endif +} + +QTextEdit::~QTextEdit() +{ + delete this->getWebCoreViewBridge(); + +#if 0 + WebCoreTextArea *textArea = (WebCoreTextArea *)getView(); + [textArea detachQTextEdit]; +#endif +} + +void QTextEdit::setText(const String& string) +{ + SkString str; + + setstr(&str, string); + LOGI("%p QTextEdit::setText(%s)\n", this, str.c_str()); +} + +String QTextEdit::text() const +{ + return String("ATextEdit::text"); +} + +String QTextEdit::textWithHardLineBreaks() const +{ + return String("ATextEdit::textWithHardLineBreaks"); +} + +void QTextEdit::getCursorPosition(int *paragraph, int *index) const +{ +// WebCoreTextArea *textView = (WebCoreTextArea *)getView(); + if (index) + *index = 0; + if (paragraph) + *paragraph = 0; +} + +void QTextEdit::setCursorPosition(int paragraph, int index) +{ +LOGI("QTextEdit::setCursorPosition(%d, %d)\n", paragraph, index); +} + +QTextEdit::WrapStyle QTextEdit::wordWrap() const +{ +// return [textView wordWrap] ? WidgetWidth : NoWrap; + return NoWrap; +} + +void QTextEdit::setWordWrap(WrapStyle style) +{ +} + +void QTextEdit::setScrollBarModes(ScrollBarMode hMode, ScrollBarMode vMode) +{ +#if 0 + WebCoreTextArea *textView = (WebCoreTextArea *)getView(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + // this declaration must be inside the BEGIN_BLOCK_OBJC_EXCEPTIONS block or the deployment build fails + bool autohides = hMode == ScrollBarAuto || vMode == ScrollBarAuto; + + ASSERT(!autohides || hMode != ScrollBarAlwaysOn); + ASSERT(!autohides || vMode != ScrollBarAlwaysOn); + + [textView setHasHorizontalScroller:hMode != ScrollBarAlwaysOff]; + [textView setHasVerticalScroller:vMode != ScrollBarAlwaysOff]; + [textView setAutohidesScrollers:autohides]; + + END_BLOCK_OBJC_EXCEPTIONS; +#endif +} + +bool QTextEdit::isReadOnly() const +{ + return false; +} + +void QTextEdit::setReadOnly(bool flag) +{ +} + +bool QTextEdit::isDisabled() const +{ + return false; +} + +void QTextEdit::setDisabled(bool flag) +{ +} + +int QTextEdit::selectionStart() +{ + return 0; +} + +int QTextEdit::selectionEnd() +{ + return 0; +} + +void QTextEdit::setSelectionStart(int start) +{ +#if 0 + WebCoreTextArea *textView = (WebCoreTextArea *)getView(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + NSRange range = [textView selectedRange]; + if (range.location == NSNotFound) { + range.location = 0; + range.length = 0; + } + + // coerce start to a valid value + int maxLength = [[textView text] length]; + int newStart = start; + if (newStart < 0) + newStart = 0; + if (newStart > maxLength) + newStart = maxLength; + + if ((unsigned)newStart < range.location + range.length) { + // If we're expanding or contracting, but not collapsing the selection + range.length += range.location - newStart; + range.location = newStart; + } else { + // ok, we're collapsing the selection + range.location = newStart; + range.length = 0; + } + + [textView setSelectedRange:range]; + END_BLOCK_OBJC_EXCEPTIONS; +#endif +} + +void QTextEdit::setSelectionEnd(int end) +{ +#if 0 + WebCoreTextArea *textView = (WebCoreTextArea *)getView(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + NSRange range = [textView selectedRange]; + if (range.location == NSNotFound) { + range.location = 0; + range.length = 0; + } + + // coerce end to a valid value + int maxLength = [[textView text] length]; + int newEnd = end; + if (newEnd < 0) + newEnd = 0; + if (newEnd > maxLength) + newEnd = maxLength; + + if ((unsigned)newEnd >= range.location) { + // If we're just changing the selection length, but not location.. + range.length = newEnd - range.location; + } else { + // ok, we've collapsed the selection and are moving it + range.location = newEnd; + range.length = 0; + } + + [textView setSelectedRange:range]; + END_BLOCK_OBJC_EXCEPTIONS; +#endif +} + +bool QTextEdit::hasSelectedText() const +{ + return false; +} + +void QTextEdit::selectAll() +{ +} + +void QTextEdit::setSelectionRange(int start, int length) +{ +#if 0 + WebCoreTextArea *textView = (WebCoreTextArea *)getView(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + int newStart = start; + int newLength = length; + if (newStart < 0) { + // truncate the length by the negative start + newLength = length + newStart; + newStart = 0; + } + if (newLength < 0) { + newLength = 0; + } + int maxlen = [[textView text] length]; + if (newStart > maxlen) { + newStart = maxlen; + } + if (newStart + newLength > maxlen) { + newLength = maxlen - newStart; + } + NSRange tempRange = {newStart, newLength}; // 4213314 + [textView setSelectedRange:tempRange]; + END_BLOCK_OBJC_EXCEPTIONS; +#endif +} + +void QTextEdit::setFont(const Font& font) +{ + Widget::setFont(font); +#if 0 + WebCoreTextArea *textView = (WebCoreTextArea *)getView(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + [textView setFont:font.getNSFont()]; + END_BLOCK_OBJC_EXCEPTIONS; +#endif +} + +void QTextEdit::setAlignment(HorizontalAlignment alignment) +{ +} + +void QTextEdit::setLineHeight(int lineHeight) +{ +} + +void QTextEdit::setWritingDirection(TextDirection direction) +{ +#if 0 + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + WebCoreTextArea *textArea = static_cast<WebCoreTextArea *>(getView()); + [textArea setBaseWritingDirection:(direction == RTL ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight)]; + + END_BLOCK_OBJC_EXCEPTIONS; +#endif +} + +IntSize QTextEdit::sizeWithColumnsAndRows(int numColumns, int numRows) const +{ +#if 0 + WebCoreTextArea *textArea = static_cast<WebCoreTextArea *>(getView()); + NSSize size = {0,0}; + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + size = [textArea sizeWithColumns:numColumns rows:numRows]; + END_BLOCK_OBJC_EXCEPTIONS; + + return IntSize((int)ceil(size.width), (int)ceil(size.height)); +#else + return IntSize(0, 0); +#endif +} + +Widget::FocusPolicy QTextEdit::focusPolicy() const +{ + FocusPolicy policy = ScrollView::focusPolicy(); + return policy == TabFocus ? StrongFocus : policy; +} + +bool QTextEdit::checksDescendantsForFocus() const +{ + return true; +} + +void QTextEdit::setColors(const Color& background, const Color& foreground) +{ +} + diff --git a/WebCore/platform/gtk/MainThreadGtk.cpp b/WebCore/platform/android/ThreadingAndroid.cpp index db3a583..6317dab 100644 --- a/WebCore/platform/gtk/MainThreadGtk.cpp +++ b/WebCore/platform/android/ThreadingAndroid.cpp @@ -1,19 +1,18 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * Copyright (C) 2007 Apple 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. + * 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. + * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -28,33 +27,20 @@ */ #include "config.h" -#include "MainThread.h" - -#include <glib.h> +#include "Threading.h" +#include "JavaSharedClient.h" +#include "TimerClient.h" namespace WebCore { -struct FunctionWithContext { - MainThreadFunction* function; - void* context; -}; - -static gboolean callFunctionOnMainThread(gpointer data) -{ - FunctionWithContext* functionWithContext = static_cast<FunctionWithContext*>(data); - functionWithContext->function(functionWithContext->context); - delete functionWithContext; - return FALSE; -} - -void callOnMainThread(MainThreadFunction* function, void* context) +void callOnMainThread(void (*function)(void* ), void* context) { - ASSERT(function); - FunctionWithContext* functionWithContext = new FunctionWithContext; - functionWithContext->function = function; - functionWithContext->context = context; - g_timeout_add(0, callFunctionOnMainThread, functionWithContext); + // Through some investigation, this method is used to notify the frame + // loader that the favicon load decision has been made. This is not + // necessary as the frame loader will always attempt to the load the favicon + // at the end of the load. + // The other use for this function is to call a DatabaseCallback + // implementation which we do not implement. } - } diff --git a/WebCore/platform/android/TimerClient.h b/WebCore/platform/android/TimerClient.h new file mode 100644 index 0000000..e4f36a0 --- /dev/null +++ b/WebCore/platform/android/TimerClient.h @@ -0,0 +1,34 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef TIMER_CLIENT_H +#define TIMER_CLIENT_H + +namespace WebCore { + + class TimerClient + { + public: + virtual ~TimerClient() {} + virtual void setSharedTimerCallback(void(*f)()) = 0; + virtual void setSharedTimer(long long timemillis) = 0; + virtual void stopSharedTimer() = 0; + }; + +} +#endif + diff --git a/WebCore/platform/android/WidgetAndroid.cpp b/WebCore/platform/android/WidgetAndroid.cpp new file mode 100644 index 0000000..3ed3abc --- /dev/null +++ b/WebCore/platform/android/WidgetAndroid.cpp @@ -0,0 +1,161 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "Widget.h" +#include "WebCoreViewBridge.h" +#include "WidgetClient.h" +#include "Font.h" +#include "IntRect.h" +#include "GraphicsContext.h" +#include "FrameAndroid.h" +#include "WebCoreFrameBridge.h" +#include "Document.h" +#include "Element.h" + +#define LOG_TAG "WebCore" +#undef LOG +#include "utils/Log.h" + +namespace WebCore { + +#define notImplemented() LOGV("WidgetAndroid: NotYetImplemented") + + class WidgetPrivate + { + public: + Font m_font; + WebCoreViewBridge* m_viewbridge; // we point to this, but don't delete it (unless we had refcounting...) + WidgetClient* m_client; + bool m_visible; + }; + + Widget::Widget() : data(new WidgetPrivate) + { + data->m_viewbridge = NULL; + data->m_client = NULL; + data->m_visible = false; + } + + Widget::~Widget() + { + Release(data->m_viewbridge); + delete data; + } + + void Widget::setEnabled(bool enabled) + { + WebCoreViewBridge* view = data->m_viewbridge; + ASSERT(data->m_viewbridge); + if (view) + view->setEnabled(enabled); + } + + bool Widget::isEnabled() const + { + ASSERT(data->m_viewbridge); + return data->m_viewbridge->isEnabled(); + } + + IntRect Widget::frameGeometry() const + { + ASSERT(data->m_viewbridge); + return data->m_viewbridge->getBounds(); + } + + void Widget::setFocus() + { + ASSERT(data->m_viewbridge); + data->m_viewbridge->setFocus(true); + } + + void Widget::paint(GraphicsContext* ctx, const IntRect& r) + { + WebCoreViewBridge* viewBridge = data->m_viewbridge; + ASSERT(viewBridge); + viewBridge->layout(); + viewBridge->draw(ctx, r, true); + } + + void Widget::setCursor(const Cursor& cursor) + { + notImplemented(); + } + + void Widget::show() + { + if (!data || data->m_visible) + return; + + data->m_visible = true; + } + + void Widget::hide() + { + notImplemented(); + } + + void Widget::setFrameGeometry(const IntRect& rect) + { + ASSERT(data->m_viewbridge); + data->m_viewbridge->setBounds(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); + } + + void Widget::setIsSelected(bool isSelected) + { + notImplemented(); + } + + void Widget::invalidate() + { + notImplemented(); + } + + void Widget::invalidateRect(const IntRect&) + { + notImplemented(); + } + + void Widget::removeFromParent() + { + notImplemented(); + } + + void Widget::setClient(WidgetClient* c) + { + data->m_client = c; + } + + WidgetClient* Widget::client() const + { + return data->m_client; + } + + WebCoreViewBridge* Widget::getWebCoreViewBridge() const + { + return data->m_viewbridge; + } + + void Widget::setWebCoreViewBridge(WebCoreViewBridge* view) + { + Release(data->m_viewbridge); + data->m_viewbridge = view; + view->setWidget(this); + Retain(data->m_viewbridge); + } + +} // WebCore namepsace diff --git a/WebCore/platform/android/android-encodings.txt b/WebCore/platform/android/android-encodings.txt new file mode 100644 index 0000000..0cc346d --- /dev/null +++ b/WebCore/platform/android/android-encodings.txt @@ -0,0 +1,44 @@ +## +## Copyright 2007, The Android Open Source Project +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +# + +# The items on the left are names of TextEncodingID values +# The items on the right are IANA character set names. Names listed in character-sets.txt are not +# repeated here; mentioning any one character set from a group in there pulls in all the aliases in +# that group. + +# XXX: This needs to be kept in sync with AndroidUnicode.h in utils. +# XXX: These are not the supported encodings, they are just the available encodings +# that WebCore will recognize. Support is determined by carriers. + +ASCII: US-ASCII, isoir6us +ISO_8859_1: ISO-8859-1, 88591 +ISO_8859_5: ISO-8859-5 +ISO_8859_8: ISO-8859-8-I, iso88598 +UTF8: UTF-8 +UTF16: UTF-16 +SHIFT_JIS: Shift_JIS, sjis, xsjis +BIG5: Big5, xxbig5 +EUC_JP: EUC-JP, xeuc, xeucjp +EUC_CN: EUC-CN, gb2312, gb231280, gbk, xeuccn, xgbk +WINDOWS_1251: windows-1251, cp1251 +WINDOWS_1252: windows-1252 +WINDOWS_1255: windows-1255 +KOI8_R: KOI8-R +KOI8_U: KOI8-U +NEXT_STEP: x-nextstep +MACINTOSH: macintosh +THAI: TIS-620, cp874, iso885911, windows874 +INVALID: cp864, iso2022jp, windows1250, windows1253, windows1254, windows1256, windows1257, windows1258 diff --git a/WebCore/platform/android/jni/JavaBridge.cpp b/WebCore/platform/android/jni/JavaBridge.cpp new file mode 100644 index 0000000..27b2bb2 --- /dev/null +++ b/WebCore/platform/android/jni/JavaBridge.cpp @@ -0,0 +1,332 @@ +/* +** Copyright 2006-2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "webcoreglue" + +#include <config.h> +#include <wtf/Platform.h> + +#include "Cache.h" +#include "CookieClient.h" +#include "JavaSharedClient.h" +#include "KURL.h" +#include "Timer.h" +#include "TimerClient.h" + +#ifdef ANDROID_INSTRUMENT +#include "Frame.h" +#include "SystemTime.h" +#endif + +#undef LOG +#include <jni.h> +#include <JNIHelp.h> +#include <SkImageRef_GlobalPool.h> +#include <SkUtils.h> +#include <utils/Log.h> +#include <utils/misc.h> + +// maximum bytes used to cache decoded images +// (not including big images using ashmem) +#define IMAGE_POOL_BUDGET (512 * 1024) + +#ifdef ANDROID_INSTRUMENT +static uint32_t sTotalTimeUsed = 0; + +namespace WebCore { +void Frame::resetSharedTimerTimeCounter() +{ + sTotalTimeUsed = 0; +} + +void Frame::reportSharedTimerTimeCounter() +{ + LOG(LOG_DEBUG, "WebCore", "*-* Total native 2 (shared timer) time: %d ms\n", + sTotalTimeUsed); +} +} +#endif + +namespace android { + +// ---------------------------------------------------------------------------- + +static jfieldID gJavaBridge_ObjectID; + +static bool checkException(JNIEnv* env) +{ + if (env->ExceptionCheck() != 0) + { + LOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +// ---------------------------------------------------------------------------- + +extern JavaVM* jnienv_to_javavm(JNIEnv* env); +extern JNIEnv* javavm_to_jnienv(JavaVM* vm); + +// ---------------------------------------------------------------------------- + +class JavaBridge : public WebCore::TimerClient, public WebCore::CookieClient +{ +public: + JavaBridge(JNIEnv* env, jobject obj); + virtual ~JavaBridge(); + + /* + * WebCore -> Java API + */ + virtual void setSharedTimer(long long timemillis); + virtual void stopSharedTimer(); + + virtual void setCookies(WebCore::KURL const& url, WebCore::KURL const& docURL, WebCore::String const& value); + virtual WebCore::String cookies(WebCore::KURL const& url); + virtual bool cookiesEnabled(); + + //////////////////////////////////////////// + + virtual void setSharedTimerCallback(void (*f)()); + + //////////////////////////////////////////// + + void signalServiceFuncPtrQueue(); + + // jni functions + static void Constructor(JNIEnv* env, jobject obj); + static void Finalize(JNIEnv* env, jobject obj); + static void SharedTimerFired(JNIEnv* env, jobject); + static void SetCacheSize(JNIEnv* env, jobject obj, jint bytes); + static void SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer); + static void ServiceFuncPtrQueue(JNIEnv*); + +private: + JavaVM* mJvm; + jobject mJavaObject; + jmethodID mSetSharedTimer; + jmethodID mStopSharedTimer; + jmethodID mSetCookies; + jmethodID mCookies; + jmethodID mCookiesEnabled; + jmethodID mSignalFuncPtrQueue; +}; + +static void (*sSharedTimerFiredCallback)(); +static JavaBridge* gJavaBridge; + +JavaBridge::JavaBridge(JNIEnv* env, jobject obj) +{ + mJvm = jnienv_to_javavm(env); + mJavaObject = env->NewGlobalRef(obj); + jclass clazz = env->GetObjectClass(obj); + + mSetSharedTimer = env->GetMethodID(clazz, "setSharedTimer", "(J)V"); + mStopSharedTimer = env->GetMethodID(clazz, "stopSharedTimer", "()V"); + mSetCookies = env->GetMethodID(clazz, "setCookies", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + mCookies = env->GetMethodID(clazz, "cookies", "(Ljava/lang/String;)Ljava/lang/String;"); + mCookiesEnabled = env->GetMethodID(clazz, "cookiesEnabled", "()Z"); + mSignalFuncPtrQueue = env->GetMethodID(clazz, "signalServiceFuncPtrQueue", "()V"); + + LOG_ASSERT(mSetSharedTimer, "Could not find method setSharedTimer"); + LOG_ASSERT(mStopSharedTimer, "Could not find method stopSharedTimer"); + LOG_ASSERT(mSetCookies, "Could not find method setCookies"); + LOG_ASSERT(mCookies, "Could not find method cookies"); + LOG_ASSERT(mCookiesEnabled, "Could not find method cookiesEnabled"); + + WebCore::JavaSharedClient::SetTimerClient(this); + WebCore::JavaSharedClient::SetCookieClient(this); + gJavaBridge = this; +} + +JavaBridge::~JavaBridge() +{ + if (mJavaObject) { + JNIEnv* env = javavm_to_jnienv(mJvm); + env->DeleteGlobalRef(mJavaObject); + mJavaObject = 0; + } + + WebCore::JavaSharedClient::SetTimerClient(NULL); + WebCore::JavaSharedClient::SetCookieClient(NULL); +} + +void +JavaBridge::setSharedTimer(long long timemillis) +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + env->CallVoidMethod(mJavaObject, mSetSharedTimer, timemillis); +} + +void +JavaBridge::stopSharedTimer() +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + env->CallVoidMethod(mJavaObject, mStopSharedTimer); +} + +void +JavaBridge::setCookies(WebCore::KURL const& url, WebCore::KURL const& docUrl, WebCore::String const& value) +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + const WebCore::DeprecatedString& urlStr = url.deprecatedString(); + jstring jUrlStr = env->NewString((unsigned short *)urlStr.unicode(), urlStr.length()); + const WebCore::DeprecatedString& docUrlStr = docUrl.deprecatedString(); + jstring jDocUrlStr = env->NewString((unsigned short *)docUrlStr.unicode(), docUrlStr.length()); + jstring jValueStr = env->NewString((unsigned short *)value.characters(), value.length()); + + env->CallVoidMethod(mJavaObject, mSetCookies, jUrlStr, jDocUrlStr, jValueStr); + env->DeleteLocalRef(jUrlStr); + env->DeleteLocalRef(jDocUrlStr); + env->DeleteLocalRef(jValueStr); +} + +WebCore::String +JavaBridge::cookies(WebCore::KURL const& url) +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + const WebCore::DeprecatedString& urlStr = url.deprecatedString(); + jstring jUrlStr = env->NewString((unsigned short *)urlStr.unicode(), urlStr.length()); + + jstring string = (jstring)(env->CallObjectMethod(mJavaObject, mCookies, jUrlStr)); + + if (string == NULL || checkException(env)) + return WebCore::String(); + + const jchar* str = env->GetStringChars(string, NULL); + WebCore::String ret = WebCore::String((const UChar*)str, env->GetStringLength(string)); + env->ReleaseStringChars(string, str); + env->DeleteLocalRef(jUrlStr); + env->DeleteLocalRef(string); + return ret; +} + +bool +JavaBridge::cookiesEnabled() +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + jboolean ret = env->CallBooleanMethod(mJavaObject, mCookiesEnabled); + return (ret != 0); +} + +void +JavaBridge::setSharedTimerCallback(void (*f)()) +{ + LOG_ASSERT(!sSharedTimerFiredCallback || sSharedTimerFiredCallback==f, + "Shared timer callback may already be set or null!"); + + sSharedTimerFiredCallback = f; +} + +void JavaBridge::signalServiceFuncPtrQueue() +{ + javavm_to_jnienv(mJvm)->CallVoidMethod(mJavaObject, mSignalFuncPtrQueue); +} + +// ---------------------------------------------------------------------------- + +// visible to Shared +void AndroidSignalServiceFuncPtrQueue() +{ + gJavaBridge->signalServiceFuncPtrQueue(); +} + +// ---------------------------------------------------------------------------- + +void JavaBridge::Constructor(JNIEnv* env, jobject obj) +{ + JavaBridge* javaBridge = new JavaBridge(env, obj); + env->SetIntField(obj, gJavaBridge_ObjectID, (jint)javaBridge); +} + +void JavaBridge::Finalize(JNIEnv* env, jobject obj) +{ + JavaBridge* javaBridge = (JavaBridge*) + (env->GetIntField(obj, gJavaBridge_ObjectID)); + LOG_ASSERT(javaBridge, "Finalize should not be called twice for the same java bridge!"); + LOGV("webcore_javabridge::nativeFinalize(%p)\n", javaBridge); + delete javaBridge; + env->SetIntField(obj, gJavaBridge_ObjectID, 0); +} + +// we don't use the java bridge object, as we're just looking at a global +void JavaBridge::SharedTimerFired(JNIEnv* env, jobject) +{ + if (sSharedTimerFiredCallback) + { +#ifdef ANDROID_INSTRUMENT + uint32_t startTime = WebCore::get_thread_msec(); +#endif + SkAutoMemoryUsageProbe mup("JavaBridge::sharedTimerFired"); + sSharedTimerFiredCallback(); +#ifdef ANDROID_INSTRUMENT + sTotalTimeUsed += WebCore::get_thread_msec() - startTime; +#endif + } +} + +void JavaBridge::SetCacheSize(JNIEnv* env, jobject obj, jint bytes) +{ + WebCore::cache()->setCapacities(0, bytes/2, bytes); + SkImageRef_GlobalPool::SetRAMBudget(IMAGE_POOL_BUDGET); + LOGV("--- set ImageRef budget %d\n", SkImageRef_GlobalPool::GetRAMBudget()); +} + +void JavaBridge::SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer) +{ + WebCore::setDeferringTimers(defer); +} + +void JavaBridge::ServiceFuncPtrQueue(JNIEnv*) +{ + WebCore::JavaSharedClient::ServiceFunctionPtrQueue(); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gWebCoreJavaBridgeMethods[] = { + /* name, signature, funcPtr */ + { "nativeConstructor", "()V", + (void*) JavaBridge::Constructor }, + { "nativeFinalize", "()V", + (void*) JavaBridge::Finalize }, + { "sharedTimerFired", "()V", + (void*) JavaBridge::SharedTimerFired }, + { "setCacheSize", "(I)V", + (void*) JavaBridge::SetCacheSize }, + { "setDeferringTimers", "(Z)V", + (void*) JavaBridge::SetDeferringTimers }, + { "nativeServiceFuncPtrQueue", "()V", + (void*) JavaBridge::ServiceFuncPtrQueue }, +}; + +int register_javabridge(JNIEnv* env) +{ + jclass javaBridge = env->FindClass("android/webkit/JWebCoreJavaBridge"); + LOG_FATAL_IF(javaBridge == NULL, "Unable to find class android/webkit/JWebCoreJavaBridge"); + gJavaBridge_ObjectID = env->GetFieldID(javaBridge, "mNativeBridge", "I"); + LOG_FATAL_IF(gJavaBridge_ObjectID == NULL, "Unable to find android/webkit/JWebCoreJavaBridge.mNativeBridge"); + + return jniRegisterNativeMethods(env, "android/webkit/JWebCoreJavaBridge", + gWebCoreJavaBridgeMethods, NELEM(gWebCoreJavaBridgeMethods)); +} + +} /* namespace android */ diff --git a/WebCore/platform/android/jni/JavaSharedClient.cpp b/WebCore/platform/android/jni/JavaSharedClient.cpp new file mode 100644 index 0000000..aaefd72 --- /dev/null +++ b/WebCore/platform/android/jni/JavaSharedClient.cpp @@ -0,0 +1,106 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "JavaSharedClient.h" +#define LOG_TAG "JavaSharedClient" +#include "utils/Log.h" +#include "SkDeque.h" +#include "SkThread.h" + +namespace android { + void AndroidSignalServiceFuncPtrQueue(); +} + +namespace WebCore { + + TimerClient* JavaSharedClient::GetTimerClient() + { + //LOG_ASSERT(gTimerClient != NULL, "gTimerClient not initialized!!!"); + return gTimerClient; + } + + CookieClient* JavaSharedClient::GetCookieClient() + { + //LOG_ASSERT(gCookieClient != NULL, "gCookieClient not initialized!!!"); + return gCookieClient; + } + + void JavaSharedClient::SetTimerClient(TimerClient* client) + { + //LOG_ASSERT(gTimerClient == NULL || client == NULL, "gTimerClient already set, aborting..."); + gTimerClient = client; + } + + void JavaSharedClient::SetCookieClient(CookieClient* client) + { + //LOG_ASSERT(gCookieClient == NULL || client == NULL, "gCookieClient already set, aborting..."); + gCookieClient = client; + } + + TimerClient* JavaSharedClient::gTimerClient = NULL; + CookieClient* JavaSharedClient::gCookieClient = NULL; + + /////////////////////////////////////////////////////////////////////////// + + struct FuncPtrRec { + void (*fProc)(void* payload); + void* fPayload; + }; + + static SkMutex gFuncPtrQMutex; + static SkDeque gFuncPtrQ(sizeof(FuncPtrRec)); + + void JavaSharedClient::EnqueueFunctionPtr(void (*proc)(void* payload), + void* payload) + { + gFuncPtrQMutex.acquire(); + + FuncPtrRec* rec = (FuncPtrRec*)gFuncPtrQ.push_back(); + rec->fProc = proc; + rec->fPayload = payload; + + gFuncPtrQMutex.release(); + + android::AndroidSignalServiceFuncPtrQueue(); + } + + void JavaSharedClient::ServiceFunctionPtrQueue() + { + for (;;) { + void (*proc)(void*); + void* payload; + const FuncPtrRec* rec; + + // we have to copy the proc/payload (if present). we do this so we + // don't call the proc inside the mutex (possible deadlock!) + gFuncPtrQMutex.acquire(); + rec = (const FuncPtrRec*)gFuncPtrQ.front(); + if (NULL != rec) { + proc = rec->fProc; + payload = rec->fPayload; + gFuncPtrQ.pop_front(); + } + gFuncPtrQMutex.release(); + + if (NULL == rec) { + break; + } + proc(payload); + } + } +} diff --git a/WebCore/platform/android/jni/JavaSharedClient.h b/WebCore/platform/android/jni/JavaSharedClient.h new file mode 100644 index 0000000..470c4dd --- /dev/null +++ b/WebCore/platform/android/jni/JavaSharedClient.h @@ -0,0 +1,45 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef JAVA_SHARED_CLIENT_H +#define JAVA_SHARED_CLIENT_H + +namespace WebCore { + + class TimerClient; + class CookieClient; + + class JavaSharedClient + { + public: + static TimerClient* GetTimerClient(); + static CookieClient* GetCookieClient(); + + static void SetTimerClient(TimerClient* client); + static void SetCookieClient(CookieClient* client); + + // can be called from any thread, to be executed in webkit thread + static void EnqueueFunctionPtr(void (*proc)(void*), void* payload); + // only call this from webkit thread + static void ServiceFunctionPtrQueue(); + + private: + static TimerClient* gTimerClient; + static CookieClient* gCookieClient; + }; +} +#endif diff --git a/WebCore/platform/android/jni/WebCoreFrameBridge.cpp b/WebCore/platform/android/jni/WebCoreFrameBridge.cpp new file mode 100644 index 0000000..55c9c88 --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreFrameBridge.cpp @@ -0,0 +1,1249 @@ +/* +** Copyright 2006-2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "webcoreglue" + +#include <config.h> +#include <wtf/Platform.h> + +#include "android_graphics.h" +#include "Arena.h" +#include "AtomicString.h" +#include "Cache.h" +#include "ChromeClientAndroid.h" +#include "ContextMenuClientAndroid.h" +#include "DeprecatedString.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "DragClientAndroid.h" +#include "EditorClientAndroid.h" +#include "Element.h" +#include "Font.h" +#include "FrameAndroid.h" +#include "FrameLoader.h" +#include "FrameLoaderClientAndroid.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GCController.h" +#include "GraphicsContext.h" +#include "HistoryItem.h" +#include "HTMLElement.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "IconDatabase.h" +#include "Image.h" +#include "InspectorClientAndroid.h" +#include "KURL.h" +#include "Page.h" +#include "PageCache.h" +#include "PlatformString.h" +#include "RenderPart.h" +#include "RenderSkinAndroid.h" +#include "RenderTreeAsText.h" +#include "ResourceHandle.h" +#include "SelectionController.h" +#include "Settings.h" +#include "SubstituteData.h" +#include "WebCoreFrameBridge.h" +#include "WebCoreResourceLoader.h" +#include "WebHistory.h" +#include "WebIconDatabase.h" +#include "WebViewCore.h" + +#ifdef LOG +#undef LOG +#endif + +#include <JNIHelp.h> +#include <SkGraphics.h> +#include <SkImageRef_GlobalPool.h> +#include <utils/Log.h> +#include <utils/misc.h> +#include <utils/AssetManager.h> +#include <android_runtime/android_util_AssetManager.h> + +#ifdef ANDROID_INSTRUMENT +#include "SystemTime.h" + +static uint32_t sTotalJavaTimeUsed = 0; +static uint32_t sTotalNativeTimeUsed = 0; + +namespace WebCore { +void Frame::resetFramebridgeTimeCounter() +{ + sTotalJavaTimeUsed = 0; + sTotalNativeTimeUsed = 0; +} + +void Frame::reportFramebridgeTimeCounter() +{ + LOG(LOG_DEBUG, "WebCore", "*-* Total Java callback (frame bridge) time: %d ms\n", + sTotalJavaTimeUsed); + LOG(LOG_DEBUG, "WebCore", "*-* Total native 1 (frame bridge) time: %d ms\n", + sTotalNativeTimeUsed); +} +} +#endif + +namespace android { + +#ifdef ANDROID_INSTRUMENT +class TimeCounterFB { +public: + TimeCounterFB(bool native = false) { + mNative = native; + mStartTime = WebCore::get_thread_msec(); + } + + ~TimeCounterFB() { + if (mNative) + sTotalNativeTimeUsed += WebCore::get_thread_msec() - mStartTime; + else + sTotalJavaTimeUsed += WebCore::get_thread_msec() - mStartTime; + } +private: + bool mNative; + uint32_t mStartTime; +}; +#endif + +// ---------------------------------------------------------------------------- + +#define WEBCORE_MEMORY_CAP 15 * 1024 * 1024 + +// ---------------------------------------------------------------------------- + +JavaVM* jnienv_to_javavm(JNIEnv* env) +{ + JavaVM* vm; + return env->GetJavaVM(&vm) >= 0 ? vm : NULL; +} + +JNIEnv* javavm_to_jnienv(JavaVM* vm) +{ + JNIEnv* env; + return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL; +} + +// ---------------------------------------------------------------------------- + +struct WebCoreFrameBridge::JavaBrowserFrame +{ + JavaVM* mJVM; + jobject mObj; + jobject mHistoryList; // WebBackForwardList object + jmethodID mStartLoadingResource; + jmethodID mLoadStarted; + jmethodID mTransitionToCommitted; + jmethodID mLoadFinished; + jmethodID mReportError; + jmethodID mSetTitle; + jmethodID mWindowObjectCleared; + jmethodID mSetProgress; + jmethodID mDidReceiveIcon; + jmethodID mUpdateVisitedHistory; + jmethodID mHandleUrl; + jmethodID mCreateWindow; + jmethodID mCloseWindow; + jmethodID mDecidePolicyForFormResubmission; + jmethodID mRequestFocus; +}; + +static jfieldID gFrameAndroidField; +#define GET_NATIVE_FRAME(env, obj) ((WebCore::FrameAndroid*)env->GetIntField(obj, gFrameAndroidField)) +#define SET_NATIVE_FRAME(env, obj, frame) (env->SetIntField(obj, gFrameAndroidField, frame)) + +// ---------------------------------------------------------------------------- + +/** + * Helper method for checking java exceptions + * @return true if an exception occurred. + */ +static bool checkException(JNIEnv* env) +{ + if (env->ExceptionCheck() != 0) + { + LOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +// ---------------------------------------------------------------------------- + +WebCoreFrameBridge::WebCoreFrameBridge(JNIEnv* env, jobject obj, jobject historyList) +{ + jclass clazz = env->GetObjectClass(obj); + mJavaFrame = new JavaBrowserFrame; + mJavaFrame->mJVM = jnienv_to_javavm(env); + mJavaFrame->mObj = env->NewGlobalRef(obj); + mJavaFrame->mHistoryList = env->NewGlobalRef(historyList); + mJavaFrame->mStartLoadingResource = env->GetMethodID(clazz, "startLoadingResource", + "(ILjava/lang/String;Ljava/lang/String;Ljava/util/HashMap;Ljava/lang/String;IZZ)Landroid/webkit/LoadListener;"); + mJavaFrame->mLoadStarted = env->GetMethodID(clazz, "loadStarted", + "(Ljava/lang/String;Landroid/graphics/Bitmap;IZ)V"); + mJavaFrame->mTransitionToCommitted = env->GetMethodID(clazz, "transitionToCommitted", + "(IZ)V"); + mJavaFrame->mLoadFinished = env->GetMethodID(clazz, "loadFinished", + "(Ljava/lang/String;IZ)V"); + mJavaFrame->mReportError = env->GetMethodID(clazz, "reportError", + "(ILjava/lang/String;Ljava/lang/String;)V"); + mJavaFrame->mSetTitle = env->GetMethodID(clazz, "setTitle", + "(Ljava/lang/String;)V"); + mJavaFrame->mWindowObjectCleared = env->GetMethodID(clazz, "windowObjectCleared", + "(I)V"); + mJavaFrame->mSetProgress = env->GetMethodID(clazz, "setProgress", + "(I)V"); + mJavaFrame->mDidReceiveIcon = env->GetMethodID(clazz, "didReceiveIcon", + "(Landroid/graphics/Bitmap;)V"); + mJavaFrame->mUpdateVisitedHistory = env->GetMethodID(clazz, "updateVisitedHistory", + "(Ljava/lang/String;Z)V"); + mJavaFrame->mHandleUrl = env->GetMethodID(clazz, "handleUrl", + "(Ljava/lang/String;)Z"); + mJavaFrame->mCreateWindow = env->GetMethodID(clazz, "createWindow", + "(ZZ)Landroid/webkit/BrowserFrame;"); + mJavaFrame->mCloseWindow = env->GetMethodID(clazz, "closeWindow", + "(Landroid/webkit/WebViewCore;)V"); + mJavaFrame->mDecidePolicyForFormResubmission = env->GetMethodID(clazz, + "decidePolicyForFormResubmission", "(I)V"); + mJavaFrame->mRequestFocus = env->GetMethodID(clazz, "requestFocus", + "()V"); + + LOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource"); + LOG_ASSERT(mJavaFrame->mLoadStarted, "Could not find method loadStarted"); + LOG_ASSERT(mJavaFrame->mTransitionToCommitted, "Could not find method transitionToCommitted"); + LOG_ASSERT(mJavaFrame->mLoadFinished, "Could not find method loadFinished"); + LOG_ASSERT(mJavaFrame->mReportError, "Could not find method reportError"); + LOG_ASSERT(mJavaFrame->mSetTitle, "Could not find method setTitle"); + LOG_ASSERT(mJavaFrame->mWindowObjectCleared, "Could not find method windowObjectCleared"); + LOG_ASSERT(mJavaFrame->mSetProgress, "Could not find method setProgress"); + LOG_ASSERT(mJavaFrame->mDidReceiveIcon, "Could not find method didReceiveIcon"); + LOG_ASSERT(mJavaFrame->mUpdateVisitedHistory, "Could not find method updateVisitedHistory"); + LOG_ASSERT(mJavaFrame->mHandleUrl, "Could not find method handleUrl"); + LOG_ASSERT(mJavaFrame->mCreateWindow, "Could not find method createWindow"); + LOG_ASSERT(mJavaFrame->mCloseWindow, "Could not find method closeWindow"); + LOG_ASSERT(mJavaFrame->mDecidePolicyForFormResubmission, "Could not find method decidePolicyForFormResubmission"); + LOG_ASSERT(mJavaFrame->mRequestFocus, "Could not find method requestFocus"); + + mUserAgent = WebCore::String(); + mInKeyHandler = false; +} + +WebCoreFrameBridge::~WebCoreFrameBridge() +{ + if (mJavaFrame->mObj) { + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + env->DeleteGlobalRef(mJavaFrame->mObj); + env->DeleteGlobalRef(mJavaFrame->mHistoryList); + mJavaFrame->mObj = 0; + } + delete mJavaFrame; +} + +static jobject createJavaMapFromHTTPHeaders(JNIEnv* env, const WebCore::HTTPHeaderMap& map) +{ + jclass mapClass = env->FindClass("java/util/HashMap"); + LOG_ASSERT(mapClass, "Could not find HashMap class!"); + jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V"); + LOG_ASSERT(init, "Could not find constructor for HashMap"); + jobject hashMap = env->NewObject(mapClass, init, map.size()); + LOG_ASSERT(hashMap, "Could not create a new HashMap"); + jmethodID put = env->GetMethodID(mapClass, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + LOG_ASSERT(put, "Could not find put method on HashMap"); + + WebCore::HTTPHeaderMap::const_iterator end = map.end(); + for (WebCore::HTTPHeaderMap::const_iterator i = map.begin(); i != end; ++i) { + jstring key = env->NewString((unsigned short *)i->first.characters(), i->first.length()); + jstring val = env->NewString((unsigned short *)i->second.characters(), i->second.length()); + if (key && val) { + env->CallObjectMethod(hashMap, put, key, val); + env->DeleteLocalRef(key); + env->DeleteLocalRef(val); + } + } + env->DeleteLocalRef(mapClass); + + return hashMap; +} + +WebCoreResourceLoader* +WebCoreFrameBridge::startLoadingResource(WebCore::ResourceHandle* loader, + const WebCore::ResourceRequest& request, + bool isHighPriority, bool synchronous) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + LOGV("::WebCore:: startLoadingResource(%p, %s)", + loader, request.url().string().ascii().data()); + + WebCore::String method = request.httpMethod(); + WebCore::String postData; + if (request.httpBody()) + postData = request.httpBody()->flattenToString(); + WebCore::HTTPHeaderMap headers = request.httpHeaderFields(); + + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + WebCore::DeprecatedString urlStr = request.url().deprecatedString(); + jstring jUrlStr = env->NewString((unsigned short *)urlStr.unicode(), urlStr.length()); + jstring jMethodStr = NULL; + if (!method.isEmpty()) + jMethodStr = env->NewString((unsigned short *)method.characters(), method.length()); + jstring jPostDataStr = NULL; + if (!postData.isEmpty()) + jPostDataStr = + env->NewString((unsigned short *)postData.characters(), postData.length()); + + jobject jHeaderMap = createJavaMapFromHTTPHeaders(env, headers); + + // Convert the WebCore Cache Policy to a WebView Cache Policy. + int cacheMode = 0; // WebView.LOAD_NORMAL + switch (request.cachePolicy()) { + case WebCore::ReloadIgnoringCacheData: + cacheMode = 2; // WebView.LOAD_NO_CACHE + break; + case WebCore::ReturnCacheDataDontLoad: + cacheMode = 3; // WebView.LOAD_CACHE_ONLY + break; + case WebCore::ReturnCacheDataElseLoad: + cacheMode = 1; // WebView.LOAD_CACHE_ELSE_NETWORK + break; + case WebCore::UseProtocolCachePolicy: + default: + break; + } + + LOGV("::WebCore:: startLoadingResource %s with cacheMode %d", urlStr.ascii(), cacheMode); + + + jobject jLoadListener = + env->CallObjectMethod(mJavaFrame->mObj, mJavaFrame->mStartLoadingResource, + (int)loader, jUrlStr, jMethodStr, jHeaderMap, + jPostDataStr, cacheMode, isHighPriority, synchronous); + + env->DeleteLocalRef(jUrlStr); + env->DeleteLocalRef(jMethodStr); + env->DeleteLocalRef(jPostDataStr); + env->DeleteLocalRef(jHeaderMap); + if (checkException(env)) + return NULL; + + WebCoreResourceLoader* h = NULL; + if (jLoadListener) + h = new WebCoreResourceLoader(env, jLoadListener); + env->DeleteLocalRef(jLoadListener); + return h; +} + +void +WebCoreFrameBridge::reportError(int errorCode, const WebCore::String& description, + const WebCore::String& failingUrl) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + LOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data()); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + + jstring descStr = env->NewString((unsigned short*)description.characters(), description.length()); + jstring failUrl = env->NewString((unsigned short*)failingUrl.characters(), failingUrl.length()); + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mReportError, errorCode, descStr, failUrl); + env->DeleteLocalRef(descStr); + env->DeleteLocalRef(failUrl); +} + +void +WebCoreFrameBridge::loadStarted(WebCore::FrameAndroid* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + const WebCore::KURL& url = frame->loader()->activeDocumentLoader()->url(); + if (url.isEmpty()) + return; + LOGV("::WebCore:: loadStarted %s", url.string().ascii().data()); + + bool isMainFrame = (!frame->tree() || !frame->tree()->parent()); + WebCore::FrameLoadType loadType = frame->loader()->loadType(); + + if (loadType == WebCore::FrameLoadTypeReplace || + loadType == WebCore::FrameLoadTypeSame || + (loadType == WebCore::FrameLoadTypeRedirectWithLockedHistory && + !isMainFrame)) + return; + + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + WebCore::String urlString(url.string()); + // If this is the main frame and we already have a favicon in the database, + // send it along with the page started notification. + jobject favicon = NULL; + if (isMainFrame) { + WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(urlString, WebCore::IntSize(16, 16)); + if (icon) + favicon = webcoreImageToJavaBitmap(env, icon); + LOGV("favicons", "Starting load with icon %p for %s", icon, url.deprecatedString().ascii()); + } + jstring urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length()); + + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mLoadStarted, urlStr, favicon, + (int)loadType, isMainFrame); + checkException(env); + env->DeleteLocalRef(urlStr); + if (favicon) + env->DeleteLocalRef(favicon); +} + +void +WebCoreFrameBridge::transitionToCommitted(WebCore::Frame* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + WebCore::FrameLoadType loadType = frame->loader()->loadType(); + bool isMainFrame = (!frame->tree() || !frame->tree()->parent()); + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mTransitionToCommitted, + (int)loadType, isMainFrame); + checkException(env); +} + +void +WebCoreFrameBridge::didFinishLoad(WebCore::Frame* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + WebCore::FrameLoader* loader = frame->loader(); + const WebCore::KURL& url = loader->activeDocumentLoader()->url(); + if (url.isEmpty()) + return; + LOGV("::WebCore:: didFinishLoad %s", url.string().ascii().data()); + + bool isMainFrame = (!frame->tree() || !frame->tree()->parent()); + WebCore::FrameLoadType loadType = loader->loadType(); + WebCore::String urlString(url.string()); + jstring urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length()); + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mLoadFinished, urlStr, + (int)loadType, isMainFrame); + checkException(env); + env->DeleteLocalRef(urlStr); +} + +void +WebCoreFrameBridge::addHistoryItem(WebCore::HistoryItem* item) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + LOGV("::WebCore:: addHistoryItem"); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + WebHistory::AddItem(env, mJavaFrame->mHistoryList, item); +} + +void +WebCoreFrameBridge::removeHistoryItem(int index) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + LOGV("::WebCore:: removeHistoryItem at %d", index); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + WebHistory::RemoveItem(env, mJavaFrame->mHistoryList, index); +} + +void +WebCoreFrameBridge::updateHistoryIndex(int newIndex) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + LOGV("::WebCore:: updateHistoryIndex to %d", newIndex); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + WebHistory::UpdateHistoryIndex(env, mJavaFrame->mHistoryList, newIndex); +} + +void +WebCoreFrameBridge::setTitle(const WebCore::String& title) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif +#ifndef NDEBUG + LOGV("setTitle(%s)", title.ascii().data()); +#endif + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + jstring jTitleStr = env->NewString((unsigned short *)title.characters(), title.length()); + + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mSetTitle, + jTitleStr); + checkException(env); + env->DeleteLocalRef(jTitleStr); +} + +void +WebCoreFrameBridge::windowObjectCleared(WebCore::FrameAndroid* frame) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + LOGV("::WebCore:: windowObjectCleared"); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mWindowObjectCleared, (int)frame); + checkException(env); +} + +void +WebCoreFrameBridge::setProgress(float newProgress) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + int progress = (int) (100 * newProgress); + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mSetProgress, progress); + checkException(env); +} + +const WebCore::String +WebCoreFrameBridge::userAgentForURL(const WebCore::KURL* url) +{ + return mUserAgent; +} + +void +WebCoreFrameBridge::didReceiveIcon(WebCore::Image* icon) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + LOG_ASSERT(icon, "DidReceiveIcon called without an image!"); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + jobject bitmap = webcoreImageToJavaBitmap(env, icon); + if (!bitmap) + return; + + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mDidReceiveIcon, bitmap); + env->DeleteLocalRef(bitmap); + checkException(env); +} + +void +WebCoreFrameBridge::updateVisitedHistory(const WebCore::KURL& url, bool reload) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + WebCore::String urlStr(url.string()); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + jstring jUrlStr = env->NewString((unsigned short*)urlStr.characters(), urlStr.length()); + + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload); + checkException(env); +} + +bool +WebCoreFrameBridge::canHandleRequest(const WebCore::ResourceRequest& request) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + // Internal loads are ok but any request that is due to a user hitting a key + // should be checked. + bool userGesture = false; +#ifdef ANDROID_USER_GESTURE + userGesture = request.userGesture(); +#endif + WebCore::KURL requestUrl = request.url(); + if (!mInKeyHandler && !userGesture && + (requestUrl.protocolIs("http") || requestUrl.protocolIs("https") || + requestUrl.protocolIs("file") || requestUrl.protocolIs("about") || + requestUrl.protocolIs("javascript"))) + return true; + WebCore::String url(request.url().string()); + // Empty urls should not be sent to java + if (url.isEmpty()) + return true; + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length()); + + // check to see whether browser app wants to hijack url loading. + // if browser app handles the url, we will return false to bail out WebCore loading + jboolean ret = env->CallBooleanMethod(mJavaFrame->mObj, mJavaFrame->mHandleUrl, jUrlStr); + checkException(env); + return (ret == 0); +} + +WebCore::Frame* +WebCoreFrameBridge::createWindow(bool dialog, bool userGesture) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + jobject obj = env->CallObjectMethod(mJavaFrame->mObj, + mJavaFrame->mCreateWindow, dialog, userGesture); + if (obj) { + WebCore::FrameAndroid* frame = GET_NATIVE_FRAME(env, obj); + return frame; + } + return NULL; +} + +void +WebCoreFrameBridge::requestFocus() const +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mRequestFocus); + checkException(env); +} + +void +WebCoreFrameBridge::closeWindow(WebCoreViewBridge* viewBridge) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + assert(viewBridge); + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + // TODO: This is a hack until WebCoreViewBridge is no longer an interface. + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mCloseWindow, + ((WebViewCore*)viewBridge)->getJavaObject()); +} + +struct PolicyFunctionWrapper { + WebCore::FramePolicyFunction func; +}; + +void +WebCoreFrameBridge::decidePolicyForFormResubmission(WebCore::FramePolicyFunction func) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter; +#endif + JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM); + PolicyFunctionWrapper* p = new PolicyFunctionWrapper; + p->func = func; + env->CallVoidMethod(mJavaFrame->mObj, mJavaFrame->mDecidePolicyForFormResubmission, p); +} + +// ---------------------------------------------------------------------------- +static void CallPolicyFunction(JNIEnv* env, jobject obj, jint func, jint decision) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeCallPolicyFunction must take a valid frame pointer!"); + PolicyFunctionWrapper* pFunc = (PolicyFunctionWrapper*)func; + LOG_ASSERT(pFunc, "nativeCallPolicyFunction must take a valid function pointer!"); + + (pFrame->loader()->*(pFunc->func))((WebCore::PolicyAction)decision); +} + +static void CreateFrame(JNIEnv* env, jobject obj, jobject jAssetManager, jobject historyList) +{ + // Register this thread as the main thread. + KJS::Collector::registerAsMainThread(); +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::ChromeClientAndroid* chromeC = new WebCore::ChromeClientAndroid; + WebCore::EditorClientAndroid* editorC = new WebCore::EditorClientAndroid; + WebCore::ContextMenuClient* contextMenuC = new WebCore::ContextMenuClientAndroid; + WebCore::DragClient* dragC = new WebCore::DragClientAndroid; + WebCore::FrameLoaderClientAndroid* loaderC = new WebCore::FrameLoaderClientAndroid; + WebCore::InspectorClientAndroid* inspectorC = new WebCore::InspectorClientAndroid; + // Create a new page + WebCore::Page* page = new WebCore::Page(chromeC, contextMenuC, editorC, dragC, inspectorC); + /* TODO: Don't turn on PageCache until we can restore the ScrollView State. + * This caused bug http://b/issue?id=1202983 + page->settings()->setUsesPageCache(true); + // 10 is a random number chosen because it is small enough to give the user + // a good back/forward page cache without allowing the page cache to get too + // big. + WebCore::pageCache()->setCapacity(10); + */ + editorC->setPage(page); + page->setGroupName("com.android.browser"); + // Frames are automatically refed to 1, keep this ref because BrowserFrame will + // maintain a native frame pointer. + WebCore::FrameAndroid* frame = new WebCore::FrameAndroid(page, NULL, loaderC); + chromeC->setFrame(frame); + loaderC->setFrame(frame); + + // Create one instance of WebCoreFrameBridge for calling into Java from WebCore + WebCoreFrameBridge* frameBridge = new WebCoreFrameBridge(env, obj, historyList); + + // Pass the bridge to the frame and release our ownership. + frame->setBridge(frameBridge); + Release(frameBridge); + + LOGV("::WebCore:: createFrame %p", frame); + + // Set the mNativeFrame field in Frame + SET_NATIVE_FRAME(env, obj, (int)frame); + + // Setup the asset manager. + AssetManager* am = assetManagerForJavaObject(env, jAssetManager); + // Initialize our skinning classes + WebCore::RenderSkinAndroid::Init(am); +} + +static void DestroyFrame(JNIEnv* env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeDestroyFrame must take a valid frame pointer!"); + + LOGV("::WebCore:: deleting frame %p", pFrame); + + WebCore::FrameView* view = pFrame->view(); + // detachFromParent will cause the page to be closed. + WebCore::FrameLoader* fl = pFrame->loader(); + // retain a pointer because detachFromParent will set the page to null. + WebCore::Page* page = pFrame->page(); + if (fl) + fl->detachFromParent(); + delete page; + view->deref(); + + SET_NATIVE_FRAME(env, obj, 0); +} + +static void CreateView(JNIEnv *env, jobject obj, jobject widget) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeCreateView must take a valid frame pointer!"); + + // Create a new FrameView and attach an initial WebViewCore. FrameView begins with an initial ref + // of 1 and that ref is in WebViewCore. + WebCore::FrameView* view = new WebCore::FrameView(pFrame); + // The viewBridge will make a java call to maintain a pointer to the view. + WebViewCore* viewBridge = new WebViewCore(env, widget, view); + view->setWebCoreViewBridge(viewBridge); + view->deref(); + Release(viewBridge); + + // Attach the view to the frame. + pFrame->setView(view); + + // Set the frame to active to turn on keyboard focus. + pFrame->init(); + pFrame->selectionController()->setFocused(true); + + LOGV("::WebCore:: created view %p with bridge %p", view, viewBridge); +} + +static void DetachView(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeDetachView must take a valid frame pointer!"); + LOGV("::WebCore:: detaching view from frame %p", pFrame); + + WebCore::FrameView* view = pFrame->view(); + LOG_ASSERT(view, "cannot detach a null view!"); + + // Remove keyboard focus + pFrame->selectionController()->setFocused(false); + + // Remove the FrameView from the frame. + pFrame->setView(NULL); + +} + +static jboolean LoadUrl(JNIEnv *env, jobject obj, jstring url) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!"); + + const char* urlStr = env->GetStringUTFChars(url, NULL); + WebCore::String webcoreUrl(urlStr); + WebCore::ResourceRequest request(webcoreUrl); + WebCore::DeprecatedString protocol = request.url().protocol(); + // If the url is http(s) and doesn't have a host, it is a bad url. + if ((WebCore::equalIgnoringCase(protocol, "http") || + WebCore::equalIgnoringCase(protocol, "https")) && + request.url().host().isEmpty()) { + env->ReleaseStringUTFChars(url, urlStr); + return false; + } + + pFrame->loader()->load(request); + env->ReleaseStringUTFChars(url, urlStr); + return true; +} + + +static void LoadData(JNIEnv *env, jobject obj, jstring baseUrl, jstring data, + jstring mimeType, jstring encoding, jstring failUrl) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeLoadData must take a valid frame pointer!"); + + // Setup the resource request + const char* baseUrlStr = env->GetStringUTFChars(baseUrl, NULL); + WebCore::String baseUrlString(baseUrlStr); + WebCore::ResourceRequest request(baseUrlString); + + // Setup the substituteData + const char* dataStr = env->GetStringUTFChars(data, NULL); + WTF::RefPtr<WebCore::SharedBuffer> sharedBuffer = + new WebCore::SharedBuffer(); + LOG_ASSERT(dataStr, "nativeLoadData has a null data string."); + sharedBuffer->append(dataStr, strlen(dataStr)); + const char* mimeTypeStr = env->GetStringUTFChars(mimeType, NULL); + WebCore::String mimeTypeString(mimeTypeStr); + const char* encodingStr = env->GetStringUTFChars(encoding, NULL); + WebCore::String encodingString(encodingStr); + const char* failUrlStr = env->GetStringUTFChars(failUrl, NULL); + WebCore::KURL failURL(failUrlStr); + WebCore::SubstituteData substituteData(sharedBuffer, mimeTypeString, + encodingString, failURL); + + // Perform the load + pFrame->loader()->load(request, substituteData); + + // Release the Java strings + env->ReleaseStringUTFChars(baseUrl, baseUrlStr); + env->ReleaseStringUTFChars(data, dataStr); + env->ReleaseStringUTFChars(mimeType, mimeTypeStr); + env->ReleaseStringUTFChars(encoding, encodingStr); + env->ReleaseStringUTFChars(failUrl, failUrlStr); +} + +static void StopLoading(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeStopLoading must take a valid frame pointer!"); + LOGV("::WebCore:: stopLoading %p", pFrame); + + // Stop loading the page and do not send an unload event + pFrame->loader()->stopForUserCancel(); +} + +static jstring ExternalRepresentation(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "android_webcore_nativeExternalRepresentation must take a valid frame pointer!"); + + // Request external representation of the render tree + WebCore::DeprecatedString renderDump = externalRepresentation(pFrame->renderer()); + unsigned len = renderDump.length(); + if (!len) + return NULL; + return env->NewString((unsigned short*)renderDump.unicode(), len); +} + +static jstring DocumentAsText(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!"); + + WebCore::Element *documentElement = pFrame->document()->documentElement(); + WebCore::String renderDump = ((WebCore::HTMLElement*)documentElement)->innerText(); + renderDump.append("\n"); + unsigned len = renderDump.length(); + if (!len) + return NULL; + return env->NewString((unsigned short*)renderDump.characters(), len); +} + +static void Reload(JNIEnv *env, jobject obj, jboolean allowStale) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeReload must take a valid frame pointer!"); + + WebCore::FrameLoader* loader = pFrame->loader(); + if (allowStale) + loader->reloadAllowingStaleData(loader->documentLoader()->overrideEncoding()); + else + loader->reload(); +} + +static void GoBackOrForward(JNIEnv *env, jobject obj, jint pos) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "nativeGoBackOrForward must take a valid frame pointer!"); + + if (pos == 1) + pFrame->page()->goForward(); + else if (pos == -1) + pFrame->page()->goBack(); + else + pFrame->loader()->goBackOrForward(pos); +} + +static jobject StringByEvaluatingJavaScriptFromString(JNIEnv *env, jobject obj, jstring script) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "stringByEvaluatingJavaScriptFromString must take a valid frame pointer!"); + + const char* scriptStr = env->GetStringUTFChars(script, NULL); + WebCore::String result = pFrame->stringByEvaluatingJavaScriptFromString(scriptStr); + env->ReleaseStringUTFChars(script, scriptStr); + + unsigned len = result.length(); + if (len == 0) + return NULL; + return env->NewString((unsigned short*)result.characters(), len); +} + +static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer, + jobject javascriptObj, jstring interfaceName) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = (WebCore::FrameAndroid*)nativeFramePointer; + LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!"); + + const char* interfaceNameStr = env->GetStringUTFChars(interfaceName, NULL); + JavaVM* vm; + env->GetJavaVM(&vm); + LOGV("::WebCore:: addJSInterface: %p", pFrame); + pFrame->addJavascriptInterface((void*)(vm), javascriptObj, interfaceNameStr); + + env->ReleaseStringUTFChars(interfaceName, interfaceNameStr); +} + +static void SetCacheDisabled(JNIEnv *env, jobject obj, jboolean disabled) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::cache()->setDisabled(disabled); +} + +static jboolean CacheDisabled(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + return WebCore::cache()->disabled(); +} + +static void ClearCache(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + if (!WebCore::cache()->disabled()) { + // Disabling the cache will remove all resources from the cache. They may + // still live on if they are referenced by some Web page though. + WebCore::cache()->setDisabled(true); + WebCore::cache()->setDisabled(false); + } + // force JavaScript to GC when clear cache + WebCore::gcController().garbageCollectSoon(); + // clear image cache + SkImageRef_GlobalPool::SetRAMUsed(0); +} + +static jboolean DocumentHasImages(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "DocumentHasImages must take a valid frame pointer!"); + + return pFrame->document()->images()->length() > 0; +} + +static jboolean HasPasswordField(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "HasPasswordField must take a valid frame pointer!"); + + bool found = false; + WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms(); + WebCore::Node* node = form->firstItem(); + while (node && !found) { + WTF::Vector<WebCore::HTMLGenericFormElement*> elements = + ((WebCore::HTMLFormElement*)node)->formElements; + size_t size = elements.size(); + for (size_t i = 0; i< size && !found; i++) { + WebCore::HTMLGenericFormElement* e = elements[i]; + if (e->hasLocalName(WebCore::HTMLNames::inputTag)) { + if (((WebCore::HTMLInputElement*)e)->inputType() == + WebCore::HTMLInputElement::PASSWORD) + found = true; + } + } + node = form->nextItem(); + } + return found; +} + +static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "GetUsernamePassword must take a valid frame pointer!"); + jobjectArray strArray = NULL; + + WebCore::String username, password; + bool found = false; + WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms(); + WebCore::Node* node = form->firstItem(); + while (node && !found) { + WTF::Vector<WebCore::HTMLGenericFormElement*> elements = + ((WebCore::HTMLFormElement*)node)->formElements; + size_t size = elements.size(); + for (size_t i = 0; i< size && !found; i++) { + WebCore::HTMLGenericFormElement* e = elements[i]; + if (e->hasLocalName(WebCore::HTMLNames::inputTag)) { + WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e; + if (input->autoComplete() == false) + continue; + if (input->inputType() == WebCore::HTMLInputElement::PASSWORD) + password = input->value(); + else if (input->inputType() == WebCore::HTMLInputElement::TEXT) + username = input->value(); + if (!username.isNull() && !password.isNull()) + found = true; + } + } + node = form->nextItem(); + } + if (found) { + jclass stringClass = env->FindClass("java/lang/String"); + strArray = env->NewObjectArray(2, stringClass, NULL); + env->SetObjectArrayElement(strArray, 0, env->NewString((unsigned short *) + username.characters(), username.length())); + env->SetObjectArrayElement(strArray, 1, env->NewString((unsigned short *) + password.characters(), password.length())); + } + return strArray; +} + +static void SetUsernamePassword(JNIEnv *env, jobject obj, + jstring username, jstring password) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "SetUsernamePassword must take a valid frame pointer!"); + + WebCore::HTMLInputElement* usernameEle = NULL; + WebCore::HTMLInputElement* passwordEle = NULL; + bool found = false; + WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms(); + WebCore::Node* node = form->firstItem(); + while (node && !found) { + WTF::Vector<WebCore::HTMLGenericFormElement*> elements = + ((WebCore::HTMLFormElement*)node)->formElements; + size_t size = elements.size(); + for (size_t i = 0; i< size && !found; i++) { + WebCore::HTMLGenericFormElement* e = elements[i]; + if (e->hasLocalName(WebCore::HTMLNames::inputTag)) { + WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e; + if (input->autoComplete() == false) + continue; + if (input->inputType() == WebCore::HTMLInputElement::PASSWORD) + passwordEle = input; + else if (input->inputType() == WebCore::HTMLInputElement::TEXT) + usernameEle = input; + if (usernameEle != NULL && passwordEle != NULL) + found = true; + } + } + node = form->nextItem(); + } + if (found) { + const char* usernameStr = env->GetStringUTFChars(username, NULL); + const char* passwordStr = env->GetStringUTFChars(password, NULL); + usernameEle->setValue(usernameStr); + passwordEle->setValue(passwordStr); + env->ReleaseStringUTFChars(username, usernameStr); + env->ReleaseStringUTFChars(password, passwordStr); + } +} + +static jobject GetFormTextData(JNIEnv *env, jobject obj) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterFB counter(true); +#endif + WebCore::FrameAndroid* pFrame = GET_NATIVE_FRAME(env, obj); + LOG_ASSERT(pFrame, "GetFormTextData must take a valid frame pointer!"); + jobject hashMap = NULL; + + WTF::PassRefPtr<WebCore::HTMLCollection> collection = pFrame->document()->forms(); + if (collection->length() > 0) { + jclass mapClass = env->FindClass("java/util/HashMap"); + LOG_ASSERT(mapClass, "Could not find HashMap class!"); + jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V"); + LOG_ASSERT(init, "Could not find constructor for HashMap"); + hashMap = env->NewObject(mapClass, init, 1); + LOG_ASSERT(hashMap, "Could not create a new HashMap"); + jmethodID put = env->GetMethodID(mapClass, "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + LOG_ASSERT(put, "Could not find put method on HashMap"); + + static const WebCore::AtomicString text("text"); + static const WebCore::AtomicString off("off"); + + WebCore::HTMLFormElement* form; + WebCore::HTMLInputElement* input; + for (WebCore::Node* node = collection->firstItem(); node; node = collection->nextItem()) { + form = static_cast<WebCore::HTMLFormElement*>(node); + if (form->autoComplete()) { + WTF::Vector<WebCore::HTMLGenericFormElement*> elements = form->formElements; + size_t size = elements.size(); + for (size_t i = 0; i < size; i++) { + WebCore::HTMLGenericFormElement* e = elements[i]; + if (e->type() == text) { + if (e->hasAttribute(WebCore::HTMLNames::autocompleteAttr)) { + const WebCore::AtomicString& attr = e->getAttribute(WebCore::HTMLNames::autocompleteAttr); + if (attr == off) + continue; + } + input = (WebCore::HTMLInputElement*) e; + WebCore::String value = input->value(); + int len = value.length(); + if (len) { + const WebCore::AtomicString& name = input->name(); + jstring key = env->NewString((jchar *)name.characters(), name.length()); + jstring val = env->NewString((jchar *)value.characters(), len); + LOG_ASSERT(key && val, "name or value not set"); + env->CallObjectMethod(hashMap, put, key, val); + env->DeleteLocalRef(key); + env->DeleteLocalRef(val); + } + } + } + } + } + env->DeleteLocalRef(mapClass); + + } + return hashMap; +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gBrowserFrameNativeMethods[] = { + /* name, signature, funcPtr */ + { "nativeCallPolicyFunction", "(II)V", + (void*) CallPolicyFunction }, + { "nativeCreateFrame", "(Landroid/content/res/AssetManager;Landroid/webkit/WebBackForwardList;)V", + (void*) CreateFrame }, + { "nativeCreateView", "(Landroid/webkit/WebViewCore;)V", + (void*) CreateView }, + { "nativeDestroyFrame", "()V", + (void*) DestroyFrame }, + { "nativeDetachView", "()V", + (void*) DetachView }, + { "stopLoading", "()V", + (void*) StopLoading }, + { "nativeLoadUrl", "(Ljava/lang/String;)Z", + (void*) LoadUrl }, + { "nativeLoadData", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + (void*) LoadData }, + { "externalRepresentation", "()Ljava/lang/String;", + (void*) ExternalRepresentation }, + { "documentAsText", "()Ljava/lang/String;", + (void*) DocumentAsText }, + { "reload", "(Z)V", + (void*) Reload }, + { "goBackOrForward", "(I)V", + (void*) GoBackOrForward }, + { "nativeAddJavascriptInterface", "(ILjava/lang/Object;Ljava/lang/String;)V", + (void*) AddJavascriptInterface }, + { "stringByEvaluatingJavaScriptFromString", + "(Ljava/lang/String;)Ljava/lang/String;", + (void*) StringByEvaluatingJavaScriptFromString }, + { "setCacheDisabled", "(Z)V", + (void*) SetCacheDisabled }, + { "cacheDisabled", "()Z", + (void*) CacheDisabled }, + { "clearCache", "()V", + (void*) ClearCache }, + { "documentHasImages", "()Z", + (void*) DocumentHasImages }, + { "hasPasswordField", "()Z", + (void*) HasPasswordField }, + { "getUsernamePassword", "()[Ljava/lang/String;", + (void*) GetUsernamePassword }, + { "setUsernamePassword", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*) SetUsernamePassword }, + { "getFormTextData", "()Ljava/util/HashMap;", + (void*) GetFormTextData } +}; + +int register_webcoreframebridge(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/webkit/BrowserFrame"); + LOG_ASSERT(clazz, "Cannot find BrowserFrame"); + gFrameAndroidField = env->GetFieldID(clazz, "mNativeFrame", "I"); + LOG_ASSERT(gFrameAndroidField, "Cannot find mNativeFrame on BrowserFrame"); + + return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame", + gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods)); +} + +} /* namespace android */ + diff --git a/WebCore/platform/android/jni/WebCoreFrameBridge.h b/WebCore/platform/android/jni/WebCoreFrameBridge.h new file mode 100644 index 0000000..ba89f19 --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreFrameBridge.h @@ -0,0 +1,106 @@ +/* +** Copyright 2006-2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef WEBCOREFRAMEBRIDGE_H +#define WEBCOREFRAMEBRIDGE_H + +#include "FrameLoaderClient.h" +#include "PlatformString.h" +#include "WebCoreRefObject.h" +#include <jni.h> + +namespace WebCore { + class FrameAndroid; + class HistoryItem; + class Image; + class RenderPart; + class ResourceHandle; + class ResourceRequest; +} + +class WebCoreViewBridge; + +namespace android { + +class WebCoreResourceLoader; + +class WebCoreFrameBridge : public WebCoreRefObject { + public: + WebCoreFrameBridge(JNIEnv* env, jobject obj, jobject historyList); + ~WebCoreFrameBridge(); + + WebCoreResourceLoader* startLoadingResource(WebCore::ResourceHandle*, + const WebCore::ResourceRequest& request, + bool isHighPriority, + bool synchronous); + + void reportError(int errorCode, const WebCore::String& description, + const WebCore::String& failingUrl); + + void loadStarted(WebCore::FrameAndroid* frame); + + void transitionToCommitted(WebCore::Frame* frame); + + void didFinishLoad(WebCore::Frame* frame); + + void addHistoryItem(WebCore::HistoryItem* item); + + void removeHistoryItem(int index); + + void updateHistoryIndex(int newIndex); + + void setTitle(const WebCore::String& title); + + void windowObjectCleared(WebCore::FrameAndroid* frame); + + void setProgress(float newProgress); + + const WebCore::String userAgentForURL(const WebCore::KURL* url); + + void didReceiveIcon(WebCore::Image* icon); + + void updateVisitedHistory(const WebCore::KURL& url, bool reload); + + bool canHandleRequest(const WebCore::ResourceRequest& request); + + WebCore::Frame* createWindow(bool dialog, bool userGesture); + + void requestFocus() const; + + void closeWindow(WebCoreViewBridge* viewBridge); + + void decidePolicyForFormResubmission(WebCore::FramePolicyFunction func); + + void setUserAgent(WebCore::String userAgent) { mUserAgent = userAgent; } + + /** + * This function is called during a key event so that canHandleRequest can + * avoid asking the application to override the url loading. If a load is + * due to a key event, then we ask the application if it wants to override + * the load. Otherwise, we attempt to load the resource internally. + */ + void setInKeyHandler(bool inKeyHandler) { mInKeyHandler = inKeyHandler; } + + private: + struct JavaBrowserFrame; + JavaBrowserFrame* mJavaFrame; + WebCore::String mUserAgent; + bool mInKeyHandler; +}; + +} // namespace android + +#endif // WEBCOREFRAMEBRIDGE_H diff --git a/WebCore/platform/android/jni/WebCoreJni.cpp b/WebCore/platform/android/jni/WebCoreJni.cpp new file mode 100644 index 0000000..8ba3d0b --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreJni.cpp @@ -0,0 +1,83 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "WebCoreJni.h" +#include <jni.h> + +#ifdef LOG +#undef LOG +#endif +#define LOG_TAG "webcoreglue" +#include <utils/Log.h> + +namespace android { + +JavaVM* WebCoreJni::mJavaVM; + +extern int register_webcoreframebridge(JNIEnv*); +extern int register_javabridge(JNIEnv*); +extern int register_resource_loader(JNIEnv*); +extern int register_webviewcore(JNIEnv*); +extern int register_webhistory(JNIEnv*); +extern int register_webicondatabase(JNIEnv*); +extern int register_websettings(JNIEnv*); +extern int register_webview(JNIEnv*); + +} + +struct RegistrationMethod { + const char* name; + int (*func)(JNIEnv*); +}; + +static RegistrationMethod gWebCoreRegMethods[] = { + { "JavaBridge", android::register_javabridge }, + { "WebCoreFrameBridge", android::register_webcoreframebridge }, + { "WebCoreResourceLoader", android::register_resource_loader }, + { "WebViewCore", android::register_webviewcore }, + { "WebHistory", android::register_webhistory }, + { "WebIconDatabase", android::register_webicondatabase }, + { "WebSettings", android::register_websettings }, + { "WebView", android::register_webview } +}; + +EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + // Save the JavaVM pointer for use globally. + android::WebCoreJni::setJavaVM(vm); + + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + LOGE("GetEnv failed!"); + return result; + } + LOG_ASSERT(env, "Could not retrieve the env!"); + + const RegistrationMethod* method = gWebCoreRegMethods; + const RegistrationMethod* end = method + sizeof(gWebCoreRegMethods)/sizeof(RegistrationMethod); + while (method != end) { + if (method->func(env) < 0) { + LOGE("%s registration failed!", method->name); + return result; + } + method++; + } + + return JNI_VERSION_1_4; +} diff --git a/WebCore/platform/android/jni/WebCoreJni.h b/WebCore/platform/android/jni/WebCoreJni.h new file mode 100644 index 0000000..4031d43 --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreJni.h @@ -0,0 +1,32 @@ +/* Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef WEBCOREJNI_H +#define WEBCOREJNI_H + +#include <jni.h> + +namespace android { + +class WebCoreJni { + static JavaVM *mJavaVM; + public: + static void setJavaVM(JavaVM *vm) { mJavaVM = vm; } + static JavaVM *getJavaVM() { return mJavaVM; } +}; + +} // namespace android + +#endif // WEBCOREJNI_H diff --git a/WebCore/platform/android/jni/WebCoreRefObject.h b/WebCore/platform/android/jni/WebCoreRefObject.h new file mode 100644 index 0000000..7e96191 --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreRefObject.h @@ -0,0 +1,38 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef WEBCORE_FOUNDATION_h +#define WEBCORE_FOUNDATION_h + +#include "SkRefCnt.h" + +typedef SkRefCnt WebCoreRefObject; + +static inline WebCoreRefObject* Retain(WebCoreRefObject* obj) +{ + if (obj) + obj->ref(); + return obj; +} + +static inline void Release(WebCoreRefObject* obj) +{ + if (obj) + obj->unref(); +} + +#endif // WEBCORE_FOUNDATION_h diff --git a/WebCore/platform/android/jni/WebCoreResourceLoader.cpp b/WebCore/platform/android/jni/WebCoreResourceLoader.cpp new file mode 100644 index 0000000..3807a2a --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreResourceLoader.cpp @@ -0,0 +1,395 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "webcoreglue" + +#include <config.h> +#include <wtf/Platform.h> + +#include "WebCoreResourceLoader.h" +#include "SkUtils.h" +#include "WebCoreJni.h" + +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleInternal.h" +#include "ResourceResponse.h" + +#ifdef ANDROID_INSTRUMENT +#include "Frame.h" +#include "SystemTime.h" +#endif + +#undef LOG +#include <utils/Log.h> +#include <utils/misc.h> +#include <JNIHelp.h> +#include <SkTypes.h> +#include <stdlib.h> + +#ifdef ANDROID_INSTRUMENT +static uint32_t sTotalTimeUsed = 0; + +namespace WebCore { +void Frame::resetResourceLoadTimeCounter() +{ + sTotalTimeUsed = 0; +} + +void Frame::reportResourceLoadTimeCounter() +{ + LOG(LOG_DEBUG, "WebCore", "*-* Total native 3 (resource load) time: %d ms\n", + sTotalTimeUsed); +} +} +#endif + +namespace android { + +#ifdef ANDROID_INSTRUMENT +class TimeCounterRC { +public: + TimeCounterRC() { + mStartTime = WebCore::get_thread_msec(); + } + + ~TimeCounterRC() { + sTotalTimeUsed += WebCore::get_thread_msec() - mStartTime; + } + +private: + uint32_t mStartTime; +}; +#endif + +// ---------------------------------------------------------------------------- + +static struct resourceloader_t { + jfieldID mObject; + jmethodID mCancelMethodID; + jmethodID mDownloadFileMethodID; + jmethodID mWillLoadFromCacheMethodID; +} gResourceLoader; + +// ---------------------------------------------------------------------------- + +/** + * Helper method for checking java exceptions + * @return true if an exception occurred. + */ +static bool checkException(JNIEnv* env) +{ + if (env->ExceptionCheck() != 0) + { + LOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +// ---------------------------------------------------------------------------- + +extern JavaVM* jnienv_to_javavm(JNIEnv* env); +extern JNIEnv* javavm_to_jnienv(JavaVM* vm); + +//----------------------------------------------------------------------------- + +#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 + +WebCoreResourceLoader::WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener) +{ + mJvm = jnienv_to_javavm(env); + mJLoader = env->NewGlobalRef(jLoadListener); +} + +WebCoreResourceLoader::~WebCoreResourceLoader() +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + SET_NATIVE_HANDLE(env, mJLoader, 0); + env->DeleteGlobalRef(mJLoader); + mJLoader = 0; +} + +void WebCoreResourceLoader::cancel() +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + env->CallVoidMethod(mJLoader, gResourceLoader.mCancelMethodID); + SET_NATIVE_HANDLE(env, mJLoader, 0); + checkException(env); +} + +void WebCoreResourceLoader::downloadFile() +{ + JNIEnv* env = javavm_to_jnienv(mJvm); + env->CallVoidMethod(mJLoader, gResourceLoader.mDownloadFileMethodID); + 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) +{ + JNIEnv* env = javavm_to_jnienv(android::WebCoreJni::getJavaVM()); + WebCore::DeprecatedString urlStr = url.deprecatedString(); + jstring jUrlStr = env->NewString((unsigned short *)urlStr.unicode(), urlStr.length()); + jclass resourceLoader = env->FindClass("android/webkit/LoadListener"); + bool val = env->CallStaticBooleanMethod(resourceLoader, + gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr); + checkException(env); + env->DeleteLocalRef(jUrlStr); + + return val; +} + +// ---------------------------------------------------------------------------- +void WebCoreResourceLoader::SetResponseHeader(JNIEnv* env, jobject obj, jint nativeResponse, jstring key, jstring val) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterRC counter; +#endif + + WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse; + LOG_ASSERT(response, "nativeSetResponseHeader must take a valid response pointer!"); + + LOG_ASSERT(key, "How did a null value become a key?"); + if (val) { + const char* keyStr = env->GetStringUTFChars(key, NULL); + const char* valStr = env->GetStringUTFChars(val, NULL); + if (valStr) + response->setHTTPHeaderField(keyStr, valStr); + + env->ReleaseStringUTFChars(key, keyStr); + env->ReleaseStringUTFChars(val, valStr); + } +} + +jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode, + jstring statusText, jstring mimeType, jlong expectedLength, + jstring encoding, jlong expireTime) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterRC counter; +#endif + LOG_ASSERT(url, "Must have a url in the response!"); + const char* urlStr = env->GetStringUTFChars(url, NULL); + const char* encodingStr = NULL; + const char* mimeTypeStr = NULL; + if (mimeType) { + mimeTypeStr = env->GetStringUTFChars(mimeType, NULL); + LOGV("Response setMIMEType: %s", mimeTypeStr); + } + if (encoding) { + encodingStr = env->GetStringUTFChars(encoding, NULL); + LOGV("Response setTextEncodingName: %s", encodingStr); + } + WebCore::ResourceResponse* response = new WebCore::ResourceResponse(WebCore::KURL(urlStr), + mimeTypeStr, (long long)expectedLength, encodingStr, WebCore::String()); + response->setHTTPStatusCode(statusCode); + if (statusText) { + const char* statusStr = env->GetStringUTFChars(statusText, NULL); + response->setHTTPStatusText(statusStr); + LOGV("Response setStatusText: %s", statusStr); + env->ReleaseStringUTFChars(statusText, statusStr); + } + // FIXME: This assumes that time_t is a long and that long is the same size as int. + if ((unsigned long)expireTime > INT_MAX) + expireTime = INT_MAX; + response->setExpirationDate((time_t)expireTime); + if (encoding) + env->ReleaseStringUTFChars(encoding, encodingStr); + if (mimeType) + env->ReleaseStringUTFChars(mimeType, mimeTypeStr); + env->ReleaseStringUTFChars(url, urlStr); + return (int)response; +} + +void WebCoreResourceLoader::ReceivedResponse(JNIEnv* env, jobject obj, jint nativeResponse) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterRC counter; +#endif + WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); + LOG_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; + LOG_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) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterRC counter; +#endif + LOGV("webcore_resourceloader data(%d)", length); + + WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); + LOG_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); + + LOG_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) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterRC counter; +#endif + LOGV("webcore_resourceloader finished"); + WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); + LOG_ASSERT(handle, "nativeFinished must take a valid handle!"); + // ResourceLoader::didFail() can set handle to be NULL, we need to check + if (!handle) + return; + + LOG_ASSERT(handle->client(), "Why do we not have a client?"); + handle->client()->didFinishLoading(handle); +} + +jstring WebCoreResourceLoader::RedirectedToUrl(JNIEnv* env, jobject obj, + jstring baseUrl, jstring redirectTo, jint nativeResponse) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterRC counter; +#endif + LOGV("webcore_resourceloader redirectedToUrl"); + WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); + LOG_ASSERT(handle, "nativeRedirectedToUrl must take a valid handle!"); + // ResourceLoader::didFail() can set handle to be NULL, we need to check + if (!handle) + return NULL; + + const char* baseStr = env->GetStringUTFChars(baseUrl, NULL); + const char* redirectStr = env->GetStringUTFChars(redirectTo, NULL); + LOG_ASSERT(handle->client(), "Why do we not have a client?"); + WebCore::ResourceRequest r = handle->request(); + WebCore::KURL url(baseStr, redirectStr); + r.setURL(url); + if (r.httpMethod() == "POST") + r.setHTTPMethod("GET"); + env->ReleaseStringUTFChars(baseUrl, baseStr); + env->ReleaseStringUTFChars(redirectTo, redirectStr); + 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; + } + handle->client()->willSendRequest(handle, r, *response); + delete response; + WebCore::String s = url.string(); + return env->NewString((unsigned short*)s.characters(), s.length()); +} + +void WebCoreResourceLoader::Error(JNIEnv* env, jobject obj, jint id, jstring description, + jstring failingUrl) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterRC counter; +#endif + LOGV("webcore_resourceloader error"); + WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj); + LOG_ASSERT(handle, "nativeError must take a valid handle!"); + // ResourceLoader::didFail() can set handle to be NULL, we need to check + if (!handle) + return; + + const char* descStr = env->GetStringUTFChars(description, NULL); + const char* failUrl = env->GetStringUTFChars(failingUrl, NULL); + handle->client()->didFail(handle, WebCore::ResourceError("", id, + WebCore::String(failUrl), WebCore::String(descStr))); + env->ReleaseStringUTFChars(failingUrl, failUrl); + env->ReleaseStringUTFChars(description, descStr); +} + +// ---------------------------------------------------------------------------- + +/* + * 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;J)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 register_resource_loader(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.mWillLoadFromCacheMethodID = + env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;)Z"); + LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL, + "Could not find static method willLoadFromCache on LoadListener"); + + return jniRegisterNativeMethods(env, "android/webkit/LoadListener", + gResourceloaderMethods, NELEM(gResourceloaderMethods)); +} + +} /* namespace android */ + diff --git a/WebCore/platform/android/jni/WebCoreResourceLoader.h b/WebCore/platform/android/jni/WebCoreResourceLoader.h new file mode 100644 index 0000000..e3b3cc7 --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreResourceLoader.h @@ -0,0 +1,67 @@ +/* //device/libs/android_runtime/android_webcore_resource_loader.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_WEBKIT_RESOURCELOADLISTENER_H +#define ANDROID_WEBKIT_RESOURCELOADLISTENER_H + +#include "KURL.h" + +#include "WebCoreRefObject.h" +#include <jni.h> + +namespace android { + +class WebCoreResourceLoader : public WebCoreRefObject +{ +public: + WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener); + virtual ~WebCoreResourceLoader(); + + /** + * Call to java to cancel the current load. + */ + void cancel(); + + /** + * Call to java to download the current load rather than feed it + * back to WebCore + */ + void downloadFile(); + + /** + * Call to java to find out if this URL is in the cache + */ + static bool willLoadFromCache(const WebCore::KURL& url); + + // Native jni functions + static void SetResponseHeader(JNIEnv*, jobject, jint, jstring, jstring); + static jint CreateResponse(JNIEnv*, jobject, jstring, jint, jstring, + jstring, jlong, jstring, jlong); + 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); + +private: + JavaVM* mJvm; + jobject mJLoader; +}; + +} // end namespace android + +#endif diff --git a/WebCore/platform/android/jni/WebCoreViewBridge.h b/WebCore/platform/android/jni/WebCoreViewBridge.h new file mode 100644 index 0000000..58aa953 --- /dev/null +++ b/WebCore/platform/android/jni/WebCoreViewBridge.h @@ -0,0 +1,191 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef WEBCORE_VIEW_BRIDGE_H +#define WEBCORE_VIEW_BRIDGE_H + +#include "IntRect.h" +#include "SkTDArray.h" +#include "WebCoreRefObject.h" +#include "Widget.h" +#include <ui/KeycodeLabels.h> +#include <stdlib.h> +namespace WebCore +{ + class GraphicsContext; + class Node; + class StringImpl; + class String; + class RenderText; + class Frame; + class FrameView; +} + +class WebCoreReply : public WebCoreRefObject { +public: + virtual ~WebCoreReply() {} + + virtual void replyInt(int value) { + SkDEBUGF(("WebCoreReply::replyInt(%d) not handled\n", value)); + } + + virtual void replyIntArray(SkTDArray<int> array) { + SkDEBUGF(("WebCoreReply::replyIntArray() not handled\n")); + } + // add more replyFoo signatures as needed +}; + +class WebCoreViewBridge : public WebCoreRefObject { +public: + WebCoreViewBridge(): mBounds(0,0,0,0), mScreenWidth(0), mScale(100), + mWidget(NULL), mParent(NULL) {} + virtual ~WebCoreViewBridge() { Release(mParent); } + + virtual void setParent(WebCoreViewBridge* parent) {Release(mParent); mParent = parent; Retain(mParent); } + virtual WebCoreViewBridge* getParent() { return mParent; } + + // these are needed for WidgetAndroid.cpp + virtual void draw(WebCore::GraphicsContext* ctx, + const WebCore::IntRect& rect, bool invalCache) = 0; + virtual void layout() {} + virtual bool isEnabled() const { return true; } + virtual void setEnabled(bool) {} + virtual bool hasFocus() const { return true; } + virtual void setFocus(bool) {} + virtual void didFirstLayout() {} + virtual void restoreScale(int) {} + + // Used by the page cache + virtual void setView(WebCore::FrameView* view) {} + + const WebCore::IntRect& getBounds() const + { + return mBounds; + } + virtual void setBounds(int left, int top, int right, int bottom) + { + this->setLocation(left, top); + this->setSize(right - left, bottom - top); + } + virtual int getMaxXScroll() const { return width() >> 2; } + virtual int getMaxYScroll() const { return height() >> 2; } + virtual void notifyFocusSet() {} + virtual void notifyProgressFinished() {} + // Subclasses should implement this if they want to do something after being resized + virtual void onResize() {} + + // These are referenced by Scrollview (and others) + virtual bool scrollIntoView(WebCore::IntRect rect, bool force) { return false; } + virtual void scrollTo(int x, int y, bool animate=false) {} + virtual void scrollBy(int x, int y) {} + virtual void contentInvalidate(const WebCore::IntRect &rect) + { + if (mParent) + mParent->contentInvalidate(rect); + } + virtual void contentInvalidate() + { + if (mParent) + mParent->contentInvalidate(); + } + // invalidate the view/display, NOT the content/DOM + virtual void viewInvalidate() + { + if (mParent) + mParent->viewInvalidate(); + } + + // these need not be virtual + // + void setSize(int w, int h) + { + int ow = width(); + int oh = height(); + mBounds.setWidth(w); + mBounds.setHeight(h); + // Only call onResize if the new size is different. + if (w != ow || h != oh) + onResize(); + } + + // used by layout when it needs to wrap content column around screen + void setSizeScreenWidthAndScale(int w, int h, int screenWidth, int scale) + { + int ow = width(); + int oh = height(); + int osw = mScreenWidth; + mBounds.setWidth(w); + mBounds.setHeight(h); + mScreenWidth = screenWidth; + mScale = scale; + // Only call onResize if the new size is different. + if (w != ow || h != oh || screenWidth != osw) + onResize(); + } + + void setLocation(int x, int y) + { + mBounds.setX(x); + mBounds.setY(y); + } + + int width() const { return mBounds.width(); } + int height() const { return mBounds.height(); } + int locX() const { return mBounds.x(); } + int locY() const { return mBounds.y(); } + int screenWidth() const { return mParent ? mParent->screenWidth() : mScreenWidth; } + int scale() const { return mParent ? mParent->scale() : mScale; } + + // called by RenderPopupMenuAndroid + virtual void popupRequest(WebCoreReply* reply, + int currIndex, + const uint16_t** labels, // the first short is the length (number of following shorts) + size_t labelCount, // the number of label strings + const int enabled[], // bools telling which corresponding labels are selectable + size_t enabledCount) // length of the enabled array, which should equal labelCount + { + if (mParent) + mParent->popupRequest(reply, currIndex, labels, labelCount, enabled, enabledCount); + } + + //implemented in android_widget_htmlwidget + virtual void removeFrameGeneration(WebCore::Frame* ) {} + virtual void updateFrameGeneration(WebCore::Frame* ) {} + virtual void jsAlert(const WebCore::String& url, const WebCore::String& text) { } + virtual bool jsConfirm(const WebCore::String& url, const WebCore::String& text) { return false; } + virtual bool jsPrompt(const WebCore::String& url, const WebCore::String& message, const WebCore::String& defaultValue, WebCore::String& result) { return false;} + virtual bool jsUnload(const WebCore::String& url, const WebCore::String& message) { return false; } + + virtual void updateTextfield(WebCore::Node* pointer, bool changeToPassword, const WebCore::String& text) + { + if (mParent) + mParent->updateTextfield(pointer, changeToPassword, text); + } + + void setWidget(WebCore::Widget* w) { mWidget = w; } + WebCore::Widget* widget() { return mWidget; } + +private: + WebCore::IntRect mBounds; + int mScreenWidth; + int mScale; + WebCore::Widget* mWidget; +protected: + WebCoreViewBridge* mParent; +}; + +#endif // WEBCORE_VIEW_BRIDGE_H diff --git a/WebCore/platform/android/jni/WebHistory.cpp b/WebCore/platform/android/jni/WebHistory.cpp new file mode 100644 index 0000000..4248c68 --- /dev/null +++ b/WebCore/platform/android/jni/WebHistory.cpp @@ -0,0 +1,969 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "webhistory" + +#include <config.h> +#include <wtf/OwnPtr.h> +#include <wtf/Platform.h> + +#include "WebHistory.h" + +#include "BackForwardList.h" +#include "CString.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClientAndroid.h" +#include "FrameTree.h" +#include "HistoryItem.h" +#include "Page.h" +#include "TextEncoding.h" +#include "WebCoreFrameBridge.h" + +#undef LOG +#include <JNIHelp.h> +#include <SkUtils.h> +#include <utils/Log.h> +#include <utils/misc.h> + +namespace android { + +// Forward declarations +static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item); +static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent); +static bool read_item_recursive(WebCore::HistoryItem* child, const char** pData, int length); + +// Field ids for WebHistoryItems +struct WebHistoryItemFields { + jmethodID mInit; + jmethodID mUpdate; + jfieldID mTitle; + jfieldID mUrl; +} gWebHistoryItem; + +struct WebBackForwardListFields { + jmethodID mAddHistoryItem; + jmethodID mRemoveHistoryItem; + jfieldID mCurrentIndex; +} gWebBackForwardList; + +//-------------------------------------------------------------------------- +// WebBackForwardList native methods. +//-------------------------------------------------------------------------- + +static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame) +{ + LOG_ASSERT(frame, "Close needs a valid Frame pointer!"); + WebCore::Frame* pFrame = (WebCore::Frame*)frame; + + WebCore::BackForwardList* list = pFrame->page()->backForwardList(); + RefPtr<WebCore::HistoryItem> current = list->currentItem(); + // Remove each item instead of using close(). close() is intended to be used + // right before the list is deleted. + WebCore::HistoryItemVector& entries = list->entries(); + int size = entries.size(); + for (int i = size - 1; i >= 0; --i) + list->removeItem(entries[i].get()); + // Add the current item back to the list. + if (current) { + current->setBridge(NULL); + // addItem will update the children to match the newly created bridge + list->addItem(current); + + /* + * The Grand Prix site uses anchor navigations to change the display. + * WebKit tries to be smart and not load child frames that have the + * same history urls during an anchor navigation. This means that the + * current history item stored in the child frame's loader does not + * match the item found in the history tree. If we remove all the + * entries in the back/foward list, we have to restore the entire tree + * or else a HistoryItem might have a deleted parent. + * + * In order to restore the history tree correctly, we have to look up + * all the frames first and then look up the history item. We do this + * because the history item in the tree may be null at this point. + * Unfortunately, a HistoryItem can only search its immediately + * children so we do a breadth-first rebuild of the tree. + */ + + // Keep a small list of child frames to traverse. + WTF::Vector<WebCore::Frame*> frameQueue; + // Fix the top-level item. + pFrame->loader()->setCurrentHistoryItem(current); + WebCore::Frame* child = pFrame->tree()->firstChild(); + // Remember the parent history item so we can search for a child item. + RefPtr<WebCore::HistoryItem> parent = current; + while (child) { + // Use the old history item since the current one may have a + // deleted parent. + WebCore::HistoryItem* item = parent->childItemWithName(child->tree()->name()); + child->loader()->setCurrentHistoryItem(item); + // Append the first child to the queue if it exists. + if (WebCore::Frame* f = child->tree()->firstChild()) + frameQueue.append(f); + child = child->tree()->nextSibling(); + // If we don't have a sibling for this frame and the queue isn't + // empty, use the next entry in the queue. + if (!child && !frameQueue.isEmpty()) { + child = frameQueue.at(0); + frameQueue.remove(0); + // Figure out the parent history item used when searching for + // the history item to use. + parent = child->tree()->parent()->loader()->currentHistoryItem(); + } + } + } +} + +static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index) +{ + LOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!"); + WebCore::Frame* pFrame = (WebCore::Frame*)frame; + + // Set the current index in the list. + WebCore::BackForwardList* list = pFrame->page()->backForwardList(); + WebCore::HistoryItem* currentItem = list->entries()[index].get(); + list->goToItem(currentItem); + + // Update the current and previous history item. + WebCore::FrameLoader* loader = pFrame->loader(); + loader->setCurrentHistoryItem(currentItem); + loader->setPreviousHistoryItem(list->backItem()); + + // Update the request with the current item's info. + WebCore::ResourceRequest& request = loader->documentLoader()->request(); + request.setURL(currentItem->url()); + request.setMainDocumentURL(currentItem->url()); + if (currentItem->originalFormData()) { + request.setHTTPMethod("POST"); + request.setHTTPContentType(currentItem->formContentType()); + request.setHTTPReferrer(currentItem->formReferrer()); + request.setHTTPBody(currentItem->formData()); + } + + // Reload the current page + loader->reloadAllowingStaleData(loader->documentLoader()->overrideEncoding()); +} + +static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data) +{ + LOG_ASSERT(frame, "Inflate needs a valid frame pointer!"); + LOG_ASSERT(data, "Inflate needs a valid data pointer!"); + + // Get the actual bytes and the length from the java array. + jbyte* bytes = env->GetByteArrayElements(data, NULL); + jsize size = env->GetArrayLength(data); + + // Inflate the history tree into one HistoryItem or null if the inflation + // failed. + WebCore::HistoryItem* newItem = new WebCore::HistoryItem(); +#ifdef ANDROID_HISTORY_CLIENT + RefPtr<WebHistoryItem> bridge = new WebHistoryItem(env, obj, newItem); + newItem->setBridge(bridge.get()); +#endif + // Inflate the item recursively. If it fails, that is ok. We'll have an + // incomplete HistoryItem but that is better than crashing due to a null + // item. + read_item_recursive(newItem, (const char**)&bytes, (int)size); +#ifdef ANDROID_HISTORY_CLIENT + bridge->setActive(); +#endif + + // Add the new item to the back/forward list. + WebCore::Frame* pFrame = (WebCore::Frame*)frame; + pFrame->page()->backForwardList()->addItem(newItem); + +#ifdef ANDROID_HISTORY_CLIENT + // Update the item. + bridge->updateHistoryItem(newItem); +#endif +} + +// 7 empty strings + no document state + children count = 9 unsigned values +// 1 char for isTargetItem +// ANDROID_HISTORY_CLIENT adds 2 ints for scale and traversals. +#ifdef ANDROID_HISTORY_CLIENT +#ifdef ANDROID_FIX +#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 14 + sizeof(char))) +#else +#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 11 + sizeof(char))) +#endif +#else +#ifdef ANDROID_FIX +#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 12 + sizeof(char))) +#else +#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 9 + sizeof(char))) +#endif +#endif + +jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item) +{ + if (!item) + return NULL; + + // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE. + v.reserveCapacity(HISTORY_MIN_SIZE); + + // Write the top-level history item and then write all the children + // recursively. +#ifdef ANDROID_HISTORY_CLIENT + LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?"); +#endif + write_item(v, item); + write_children_recursive(v, item); + + // Try to create a new java byte array. + jbyteArray b = env->NewByteArray(v.size()); + if (!b) + return NULL; + + // Write our flattened data to the java array. + jbyte* bytes = env->GetByteArrayElements(b, NULL); + memcpy(bytes, v.data(), v.size()); + env->ReleaseByteArrayElements(b, bytes, 0); + return b; +} + +WebHistoryItem::WebHistoryItem(JNIEnv* env, jobject obj, + WebCore::HistoryItem* item) { + JavaVM* vm; + mJVM = env->GetJavaVM(&vm) >= 0 ? vm : NULL; + mObject = env->NewGlobalRef(obj); + mScale = 100; + mTraversals = -1; + mActive = false; + mParent = NULL; + mHistoryItem = item; +} + +WebHistoryItem::~WebHistoryItem() { + if (mObject) { + JNIEnv* env; + env = mJVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL; + if (!env) + return; + env->DeleteGlobalRef(mObject); + } +} + +void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) { +#ifdef ANDROID_HISTORY_CLIENT + // Do not want to update during inflation. + if (!mActive) + return; + WebHistoryItem* webItem = this; + // Now we need to update the top-most WebHistoryItem based on the top-most + // HistoryItem. + if (mParent) { + webItem = mParent.get(); + if (webItem->hasOneRef()) { + // if the parent only has one ref, it is from this WebHistoryItem. + // This means that the matching WebCore::HistoryItem has been freed. + // This can happen during clear(). + LOGW("Can't updateHistoryItem as the top HistoryItem is gone"); + return; + } + while (webItem->parent()) + webItem = webItem->parent(); + item = webItem->historyItem(); + } + JNIEnv* env; + env = webItem->mJVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL; + if (!env) + return; + const WebCore::String& urlString = item->urlString(); + jstring urlStr = NULL; + if (!urlString.isNull()) + urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length()); + const WebCore::String& titleString = item->title(); + jstring titleStr = NULL; + if (!titleString.isNull()) + titleStr = env->NewString((unsigned short*)titleString.characters(), titleString.length()); + + // Try to get the favicon from the history item. For some pages like Grand + // Prix, there are history items with anchors. If the icon fails for the + // item, try to get the icon using the url without the ref. + jobject favicon = NULL; + WebCore::String url = item->urlString(); + if (item->url().hasRef()) { + int refIndex = url.reverseFind('#'); + url = url.substring(0, refIndex); + } + WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(url, + WebCore::IntSize(16, 16)); + + if (icon) + favicon = webcoreImageToJavaBitmap(env, icon); + + WTF::Vector<char> data; + jbyteArray array = WebHistory::Flatten(env, data, item); + env->CallVoidMethod(webItem->mObject, gWebHistoryItem.mUpdate, urlStr, titleStr, favicon, array); + env->DeleteLocalRef(urlStr); + env->DeleteLocalRef(titleStr); + if (favicon) + env->DeleteLocalRef(favicon); + env->DeleteLocalRef(array); +#endif +} + +static void historyItemChanged(WebCore::HistoryItem* item) { +#ifdef ANDROID_HISTORY_CLIENT + LOG_ASSERT(item, + "historyItemChanged called with a null item"); + if (item->bridge()) + item->bridge()->updateHistoryItem(item); +#endif +} + +void WebHistory::AddItem(JNIEnv* env, jobject list, WebCore::HistoryItem* item) +{ +#ifdef ANDROID_HISTORY_CLIENT + LOG_ASSERT(item, "newItem must take a valid HistoryItem!"); + // Item already added. Should only happen when we are inflating the list. + if (item->bridge()) + return; + + // Allocate a blank WebHistoryItem + jclass clazz = env->FindClass("android/webkit/WebHistoryItem"); + jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit); + + // Create the bridge, make it active, and attach it to the item. + WebHistoryItem* bridge = new WebHistoryItem(env, newItem, item); + bridge->setActive(); + item->setBridge(bridge); + + // Update the history item which will flatten the data and call update on + // the java item. + bridge->updateHistoryItem(item); + + // Add it to the list. + env->CallVoidMethod(list, gWebBackForwardList.mAddHistoryItem, newItem); + + // Delete our local reference. + env->DeleteLocalRef(newItem); +#endif +} + +void WebHistory::RemoveItem(JNIEnv* env, jobject list, int index) +{ + env->CallVoidMethod(list, gWebBackForwardList.mRemoveHistoryItem, index); +} + +void WebHistory::UpdateHistoryIndex(JNIEnv* env, jobject list, int newIndex) +{ + env->SetIntField(list, gWebBackForwardList.mCurrentIndex, newIndex); +} + +static void write_string(WTF::Vector<char>& v, const WebCore::String& str) +{ + unsigned strLen = str.length(); + // Only do work if the string has data. + if (strLen) { + // Determine how much to grow the vector. Use the worst case for utf8 to + // avoid reading the string twice. Add sizeof(unsigned) to hold the + // string length in utf8. + unsigned vectorLen = v.size() + sizeof(unsigned); + unsigned length = (strLen << 2) + vectorLen; + // Grow the vector. This will change the value of v.size() but we + // remember the original size above. + v.grow(length); + // Grab the position to write to. + char* data = v.begin() + vectorLen; + // Write the actual string + int l = SkUTF16_ToUTF8(str.characters(), strLen, data); + LOGV("Writing string %d %.*s", l, l, data); + // Go back and write the utf8 length. Subtract sizeof(unsigned) from + // data to get the position to write the length. + memcpy(data - sizeof(unsigned), (char*)&l, sizeof(unsigned)); + // Shrink the internal state of the vector so we match what was + // actually written. + v.shrink(vectorLen + l); + } else + v.append((char*)&strLen, sizeof(unsigned)); +} + +static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item) +{ + // Original url + write_string(v, item->originalURLString()); + + // Url + write_string(v, item->urlString()); + + // Title + write_string(v, item->title()); + + // Form content type + write_string(v, item->formContentType()); + + // Form referrer + write_string(v, item->formReferrer()); + + // Form data + const WebCore::FormData* formData = item->formData(); + if (formData) + write_string(v, formData->flattenToString()); + else + write_string(v, WebCore::String()); // Empty constructor does not allocate a buffer. + +#ifdef ANDROID_FIX + // original form content type + write_string(v, item->originalFormContentType()); + + // original form referrer + write_string(v, item->originalFormReferrer()); + + // original form data + const WebCore::FormData* origformData = item->originalFormData(); + if (origformData) + write_string(v, origformData->flattenToString()); + else + write_string(v, WebCore::String()); // Empty constructor does not allocate a buffer. +#endif + + // Target + write_string(v, item->target()); + +#ifdef ANDROID_HISTORY_CLIENT + WebHistoryItem* bridge = item->bridge(); + LOG_ASSERT(bridge, "We should have a bridge here!"); + // Screen scale + int scale = bridge->scale(); + LOGV("Writing scale %d", scale); + v.append((char*)&scale, sizeof(int)); + + // Focus position + int traversals = bridge->traversals(); + LOGV("Writing traversals %d", traversals); + v.append((char*)&traversals, sizeof(int)); +#endif + + // Document state + const WTF::Vector<WebCore::String>& docState = item->documentState(); + WTF::Vector<WebCore::String>::const_iterator end = docState.end(); + unsigned stateSize = docState.size(); + LOGV("Writing docState %d", stateSize); + v.append((char*)&stateSize, sizeof(unsigned)); + for (WTF::Vector<WebCore::String>::const_iterator i = docState.begin(); i != end; ++i) { + write_string(v, *i); + } + + // Is target item + LOGV("Writing isTargetItem %d", item->isTargetItem()); + v.append((char)item->isTargetItem()); + + // Children count + unsigned childCount = item->children().size(); + LOGV("Writing childCount %d", childCount); + v.append((char*)&childCount, sizeof(unsigned)); +} + +static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent) +{ + const WebCore::HistoryItemVector& children = parent->children(); + WebCore::HistoryItemVector::const_iterator end = children.end(); + for (WebCore::HistoryItemVector::const_iterator i = children.begin(); i != end; ++i) { + WebCore::HistoryItem* item = (*i).get(); +#ifdef ANDROID_HISTORY_CLIENT + LOG_ASSERT(parent->bridge(), + "The parent item should have a bridge object!"); + if (!item->bridge()) { + WebHistoryItem* bridge = new WebHistoryItem(parent->bridge()); + item->setBridge(bridge); + bridge->setActive(); + } else { + // The only time this item's parent may not be the same as the + // parent's bridge is during history close. In that case, the + // parent must not have a parent bridge. + LOG_ASSERT(parent->bridge()->parent() == NULL || + item->bridge()->parent() == parent->bridge(), + "Somehow this item has an incorrect parent"); + item->bridge()->setParent(parent->bridge()); + } +#endif + write_item(v, item); + write_children_recursive(v, item); + } +} + +static bool read_item_recursive(WebCore::HistoryItem* newItem, + const char** pData, int length) +{ + if (!pData || length < HISTORY_MIN_SIZE) + return false; + + const WebCore::TextEncoding& e = WebCore::UTF8Encoding(); + const char* data = *pData; + const char* end = data + length; + int sizeofUnsigned = (int)sizeof(unsigned); + + // Read the original url + // Read the expected length of the string. + int l; + memcpy(&l, data, sizeofUnsigned); + // Increment data pointer by the size of an unsigned int. + data += sizeofUnsigned; + if (l) { + LOGV("Original url %d %.*s", l, l, data); + // If we have a length, check if that length exceeds the data length + // and return null if there is not enough data. + if (data + l < end) + newItem->setOriginalURLString(e.decode(data, l)); + else + return false; + // Increment the data pointer by the length of the string. + data += l; + } + // Check if we have enough data left to continue. + if (end - data < sizeofUnsigned) + return false; + + // Read the url + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Url %d %.*s", l, l, data); + if (data + l < end) + newItem->setURLString(e.decode(data, l)); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + // Read the title + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Title %d %.*s", l, l, data); + if (data + l < end) + newItem->setTitle(e.decode(data, l)); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + // Generate a new ResourceRequest object for populating form information. + WebCore::String formContentType; + WebCore::String formReferrer; + WebCore::FormData* formData = NULL; + + // Read the form content type + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Content type %d %.*s", l, l, data); + if (data + l < end) + formContentType = e.decode(data, l); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + // Read the form referrer + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Referrer %d %.*s", l, l, data); + if (data + l < end) + formReferrer = e.decode(data, l); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + // Read the form data + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Form data %d %.*s", l, l, data); + if (data + l < end) + formData = new WebCore::FormData(data, l); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + // Set up the form info + if (formData != NULL) { + WebCore::ResourceRequest r; + r.setHTTPMethod("POST"); + r.setHTTPContentType(formContentType); + r.setHTTPReferrer(formReferrer); + r.setHTTPBody(formData); + newItem->setFormInfoFromRequest(r); + } + +#ifdef ANDROID_FIX + WebCore::String origformContentType; + WebCore::String origformReferrer; + WebCore::FormData* origformData = NULL; + + // Read the original form content type + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Original content type %d %.*s", l, l, data); + if (data + l < end) + origformContentType = e.decode(data, l); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + // Read the original form referrer + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Original referrer %d %.*s", l, l, data); + if (data + l < end) + origformReferrer = e.decode(data, l); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + // Read the original form data + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Original form data %d %.*s", l, l, data); + if (data + l < end) + origformData = new WebCore::FormData(data, l); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + + if (origformData) + newItem->setOriginalFormInfo(origformData, origformContentType, origformReferrer); +#endif + + // Read the target + memcpy(&l, data, sizeofUnsigned); + data += sizeofUnsigned; + if (l) { + LOGV("Target %d %.*s", l, l, data); + if (data + l < end) + newItem->setTarget(e.decode(data, l)); + else + return false; + data += l; + } + if (end - data < sizeofUnsigned) + return false; + +#ifdef ANDROID_HISTORY_CLIENT + WebHistoryItem* bridge = newItem->bridge(); + LOG_ASSERT(bridge, "There should be a bridge object during inflate"); + // Read the screen scale + memcpy(&l, data, sizeofUnsigned); + LOGV("Screen scale %d", l); + bridge->setScale(l); + data += sizeofUnsigned; + if (end - data < (int)sizeof(int)) + return false; + + // Read the focus index + memcpy(&l, data, sizeof(int)); + LOGV("Traversals %d", l); + bridge->setTraversals(l); + data += sizeof(int); + if (end - data < sizeofUnsigned) + return false; +#endif + + // Read the document state + memcpy(&l, data, sizeofUnsigned); + LOGV("Document state %d", l); + data += sizeofUnsigned; + if (l) { + // Check if we have enough data to at least parse the sizes of each + // document state string. + if (data + l * sizeofUnsigned >= end) + return false; + // Create a new vector and reserve enough space for the document state. + WTF::Vector<WebCore::String> docState; + docState.reserveCapacity(l); + while (l--) { + // Check each time if we have enough to parse the length of the next + // string. + if (end - data < sizeofUnsigned) + return false; + int strLen; + memcpy(&strLen, data, sizeofUnsigned); + data += sizeofUnsigned; + if (data + strLen < end) + docState.append(e.decode(data, strLen)); + else + return false; + LOGV("\t\t%d %.*s", strLen, strLen, data); + data += strLen; + } + newItem->setDocumentState(docState); + } + // Check if we have enough to read the next byte + if (data >= end) + return false; + + // Read is target item + // Cast the value to unsigned char in order to make a negative value larger + // than 1. A value that is not 0 or 1 is a failure. + unsigned char c = (unsigned char)data[0]; + if (c > 1) + return false; + LOGV("Target item %d", c); + newItem->setIsTargetItem((bool)c); + data++; + if (end - data < sizeofUnsigned) + return false; + + // Read the child count + memcpy(&l, data, sizeofUnsigned); + LOGV("Child count %d", l); + data += sizeofUnsigned; + *pData = data; + if (l) { + // Check if we have the minimum amount need to parse l children. + if (data + l * HISTORY_MIN_SIZE >= end) + return false; + while (l--) { + // No need to check the length each time because read_item_recursive + // will return null if there isn't enough data left to parse. + WebCore::HistoryItem* child = new WebCore::HistoryItem(); +#ifdef ANDROID_HISTORY_CLIENT + // Set a bridge that will not call into java. + child->setBridge(new WebHistoryItem(bridge)); +#endif + // Read the child item. + if (!read_item_recursive(child, pData, end - data)) { + delete child; + return false; + } +#ifdef ANDROID_HISTORY_CLIENT + child->bridge()->setActive(); +#endif + newItem->addChildItem(child); + } + } + return true; +} + +#ifndef NDEBUG +static void unit_test() +{ + LOGD("Entering history unit test!"); + const char* test1 = new char[0]; + WebCore::HistoryItem* testItem = new WebCore::HistoryItem(); +#ifdef ANDROID_HISTORY_CLIENT + testItem->setBridge(new WebHistoryItem(NULL)); +#endif + LOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!"); + delete[] test1; + const char* test2 = new char[2]; + LOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!"); + delete[] test2; + LOG_ASSERT(!read_item_recursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!"); + // Original Url + char* test3 = new char[HISTORY_MIN_SIZE]; + const char* ptr = (const char*)test3; + memset(test3, 0, HISTORY_MIN_SIZE); + *(int*)test3 = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!"); + // Url + int offset = 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!"); + // Title + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!"); + // Form content type + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!"); + // Form referrer + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length referrer should fail!"); + // Form data + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!"); +#ifdef ANDROID_FIX + // Original form content type + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!"); + // Original form referrer + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length referrer should fail!"); + // Original form data + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!"); +#endif + // Target + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!"); +#ifdef ANDROID_HISTORY_CLIENT + offset += 4; // Scale + offset += 4; // traversals +#endif + // Document state + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!"); + // Is target item + offset += 1; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(char*)(test3 + offset) = '!'; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!"); + // Child count + offset += 4; + memset(test3, 0, HISTORY_MIN_SIZE); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 4000; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!"); + +#ifdef ANDROID_HISTORY_CLIENT + offset = 36; +#else + offset = 28; +#endif +#ifdef ANDROID_FIX + offset += 12; +#endif + // Test document state + delete[] test3; + test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)]; + memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned)); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 1; + *(int*)(test3 + offset + 4) = 20; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!"); + delete[] test3; + test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)]; + memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned)); + ptr = (const char*)test3; + *(int*)(test3 + offset) = 2; + *(int*)(test3 + offset + 4) = 0; + *(int*)(test3 + offset + 8) = 20; + LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!"); + delete[] test3; + delete testItem; +} +#endif + +//--------------------------------------------------------- +// JNI registration +//--------------------------------------------------------- +static JNINativeMethod gWebBackForwardListMethods[] = { + { "nativeClose", "(I)V", + (void*) WebHistoryClose }, + { "restoreIndex", "(II)V", + (void*) WebHistoryRestoreIndex } +}; + +static JNINativeMethod gWebHistoryItemMethods[] = { + { "inflate", "(I[B)V", + (void*) WebHistoryInflate } +}; + +int register_webhistory(JNIEnv* env) +{ +#ifdef ANDROID_HISTORY_CLIENT + // Get notified of all changes to history items. + WebCore::notifyHistoryItemChanged = historyItemChanged; +#endif +#ifndef NDEBUG + unit_test(); +#endif + // Find WebHistoryItem, its constructor, and the update method. + jclass clazz = env->FindClass("android/webkit/WebHistoryItem"); + LOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem"); + gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "()V"); + LOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor"); + gWebHistoryItem.mUpdate = env->GetMethodID(clazz, "update", + "(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Bitmap;[B)V"); + LOG_ASSERT(gWebHistoryItem.mUpdate, "Could not find method update in WebHistoryItem"); + + // Find the field ids for mTitle and mUrl. + gWebHistoryItem.mTitle = env->GetFieldID(clazz, "mTitle", "Ljava/lang/String;"); + LOG_ASSERT(gWebHistoryItem.mTitle, "Could not find field mTitle in WebHistoryItem"); + gWebHistoryItem.mUrl = env->GetFieldID(clazz, "mUrl", "Ljava/lang/String;"); + LOG_ASSERT(gWebHistoryItem.mUrl, "Could not find field mUrl in WebHistoryItem"); + + // Find the WebBackForwardList object, the addHistoryItem and + // removeHistoryItem methods and the mCurrentIndex field. + clazz = env->FindClass("android/webkit/WebBackForwardList"); + LOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList"); + gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem", + "(Landroid/webkit/WebHistoryItem;)V"); + LOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem"); + gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem", + "(I)V"); + LOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem"); + gWebBackForwardList.mCurrentIndex = env->GetFieldID(clazz, "mCurrentIndex", "I"); + LOG_ASSERT(gWebBackForwardList.mCurrentIndex, "Could not find field mCurrentIndex"); + + int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList", + gWebBackForwardListMethods, NELEM(gWebBackForwardListMethods)); + return (result < 0) ? result : jniRegisterNativeMethods(env, "android/webkit/WebHistoryItem", + gWebHistoryItemMethods, NELEM(gWebHistoryItemMethods)); +} + +} /* namespace android */ diff --git a/WebCore/platform/android/jni/WebHistory.h b/WebCore/platform/android/jni/WebHistory.h new file mode 100644 index 0000000..62db099 --- /dev/null +++ b/WebCore/platform/android/jni/WebHistory.h @@ -0,0 +1,72 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_WEBKIT_WEBHISTORY_H +#define ANDROID_WEBKIT_WEBHISTORY_H + +#include <jni.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + class HistoryItem; +} + +namespace android { + +class WebHistory { +public: + static jbyteArray Flatten(JNIEnv*, WTF::Vector<char>&, WebCore::HistoryItem*); + static void AddItem(JNIEnv*, jobject, WebCore::HistoryItem*); + static void RemoveItem(JNIEnv*, jobject, int); + static void UpdateHistoryIndex(JNIEnv*, jobject, int); +}; + +class WebHistoryItem : public WTF::RefCounted<WebHistoryItem> { +public: + WebHistoryItem(WebHistoryItem* parent) + : mParent(parent) + , mObject(NULL) + , mJVM(NULL) + , mScale(100) + , mTraversals(-1) + , mActive(false) + , mHistoryItem(NULL) {} + WebHistoryItem(JNIEnv*, jobject, WebCore::HistoryItem*); + ~WebHistoryItem(); + void updateHistoryItem(WebCore::HistoryItem* item); + void setScale(int s) { mScale = s; } + void setTraversals(int t) { mTraversals = t; } + void setActive() { mActive = true; } + void setParent(WebHistoryItem* parent) { mParent = parent; } + WebHistoryItem* parent() { return mParent.get(); } + int scale() { return mScale; } + int traversals() { return mTraversals; } + jobject object() { return mObject; } + WebCore::HistoryItem* historyItem() { return mHistoryItem; } +private: + RefPtr<WebHistoryItem> mParent; + jobject mObject; + JavaVM* mJVM; + int mScale; + int mTraversals; + bool mActive; + WebCore::HistoryItem* mHistoryItem; +}; + +}; + +#endif diff --git a/WebCore/platform/android/jni/WebIconDatabase.cpp b/WebCore/platform/android/jni/WebIconDatabase.cpp new file mode 100644 index 0000000..2d5c39c --- /dev/null +++ b/WebCore/platform/android/jni/WebIconDatabase.cpp @@ -0,0 +1,232 @@ +/* +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "favicons" + +#include <config.h> +#include <wtf/Platform.h> + +#include "WebCoreJni.h" +#include "WebIconDatabase.h" + +#include "IconDatabase.h" +#include "Image.h" +#include "IntRect.h" +#include "JavaSharedClient.h" +#include "KURL.h" + +#include <pthread.h> +#include "GraphicsJNI.h" +#include <SkBitmap.h> +#include <SkImageDecoder.h> +#include <SkTemplates.h> +#undef LOG +#include <utils/Log.h> +#include <utils/misc.h> +#include <JNIHelp.h> + +namespace android { + +jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon) +{ + if (!icon) + return NULL; + SkBitmap bm; + WebCore::SharedBuffer* buffer = icon->data(); + if (!buffer || !SkImageDecoder::DecodeMemory(buffer->data(), buffer->size(), + &bm, SkBitmap::kNo_Config, + SkImageDecoder::kDecodePixels_Mode)) + return NULL; + + return GraphicsJNI::createBitmap(env, new SkBitmap(bm), false, NULL); +} + +static WebIconDatabase* gIconDatabaseClient = new WebIconDatabase(); + +// XXX: Called by the IconDatabase thread +void WebIconDatabase::dispatchDidAddIconForPageURL(const WebCore::String& pageURL) +{ + // Attempt to attach to the current vm. + JavaVM* vm = WebCoreJni::getJavaVM(); + JavaVMAttachArgs args; + + args.version = JNI_VERSION_1_4; + args.name = "IconDatabase"; + args.group = NULL; + + JNIEnv* env; + bool threadIsAttached = true; + if (vm->AttachCurrentThread(&env, (void*) &args) != JNI_OK) { + LOGE("Could not attach IconDatabase thread to the VM"); + threadIsAttached = false; + } + + mNotificationsMutex.lock(); + mNotifications.append(pageURL); + if (!mDeliveryRequested) { + if (threadIsAttached) { + mDeliveryRequested = true; + WebCore::JavaSharedClient::EnqueueFunctionPtr(DeliverNotifications, this); + } + } + mNotificationsMutex.unlock(); +} + +// Called in the WebCore thread +void WebIconDatabase::RegisterForIconNotification(WebIconDatabaseClient* client) +{ + gIconDatabaseClient->mClientsMutex.lock(); + gIconDatabaseClient->mClients.append(client); + gIconDatabaseClient->mClientsMutex.unlock(); +} + +// Called in the WebCore thread +void WebIconDatabase::UnregisterForIconNotification(WebIconDatabaseClient* client) +{ + WebIconDatabase* db = gIconDatabaseClient; + db->mClientsMutex.lock(); + for (unsigned i = 0; i < db->mClients.size(); ++i) { + if (db->mClients[i] == client) { + db->mClients.remove(i); + break; + } + } + db->mClientsMutex.unlock(); +} + +// Called in the WebCore thread +void WebIconDatabase::DeliverNotifications(void* v) +{ + ASSERT(v); + ((WebIconDatabase*)v)->deliverNotifications(); +} + +// Called in the WebCore thread +void WebIconDatabase::deliverNotifications() +{ + ASSERT(mDeliveryRequested); + + // Swap the notifications queue + Vector<WebCore::String> queue; + mNotificationsMutex.lock(); + queue.swap(mNotifications); + mDeliveryRequested = false; + mNotificationsMutex.unlock(); + + // Swap the clients queue + Vector<WebIconDatabaseClient*> clients; + mClientsMutex.lock(); + clients.swap(mClients); + mClientsMutex.unlock(); + + for (unsigned i = 0; i < queue.size(); ++i) { + for (unsigned j = 0; j < clients.size(); ++j) { + clients[j]->didAddIconForPageUrl(queue[i]); + } + } +} + +static void Open(JNIEnv* env, jobject obj, jstring path) +{ + WebCore::IconDatabase* iconDb = WebCore::iconDatabase(); + if (iconDb->isOpen()) + return; + iconDb->setEnabled(true); + iconDb->setClient(gIconDatabaseClient); + LOG_ASSERT(path, "No path given to nativeOpen"); + const char* pathStr = env->GetStringUTFChars(path, NULL); + LOG_ASSERT(pathStr, "GetStringUTFChars failed for the path"); + LOGV("Opening WebIconDatabase file '%s'", pathStr); + bool res = iconDb->open(pathStr); + if (!res) + LOGE("Open failed!"); + env->ReleaseStringUTFChars(path, pathStr); +} + +static void Close(JNIEnv* env, jobject obj) +{ + WebCore::iconDatabase()->close(); +} + +static void RemoveAllIcons(JNIEnv* env, jobject obj) +{ + LOGV("Removing all icons"); + WebCore::iconDatabase()->removeAllIcons(); +} + +static jobject IconForPageUrl(JNIEnv* env, jobject obj, jstring url) +{ + LOG_ASSERT(url, "No url given to iconForPageUrl"); + const char* urlStr = env->GetStringUTFChars(url, NULL); + LOG_ASSERT(urlStr, "GetStringUTFChars failed for url"); + + WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(urlStr, + WebCore::IntSize(16, 16)); + LOGV("Retrieving icon for '%s' %p", urlStr, icon); + env->ReleaseStringUTFChars(url, urlStr); + return webcoreImageToJavaBitmap(env, icon); +} + +static void RetainIconForPageUrl(JNIEnv* env, jobject obj, jstring url) +{ + LOG_ASSERT(url, "No url given to retainIconForPageUrl"); + const char* urlStr = env->GetStringUTFChars(url, NULL); + LOG_ASSERT(urlStr, "GetStringUTFChars failed for url"); + + LOGV("Retaining icon for '%s'", urlStr); + WebCore::iconDatabase()->retainIconForPageURL(urlStr); + env->ReleaseStringUTFChars(url, urlStr); +} + +static void ReleaseIconForPageUrl(JNIEnv* env, jobject obj, jstring url) +{ + LOG_ASSERT(url, "No url given to releaseIconForPageUrl"); + const char* urlStr = env->GetStringUTFChars(url, NULL); + LOG_ASSERT(urlStr, "GetStringUTFChars failed for url"); + + LOGV("Releasing icon for '%s'", urlStr); + WebCore::iconDatabase()->releaseIconForPageURL(urlStr); + env->ReleaseStringUTFChars(url, urlStr); +} + +/* + * JNI registration + */ +static JNINativeMethod gWebIconDatabaseMethods[] = { + { "nativeOpen", "(Ljava/lang/String;)V", + (void*) Open }, + { "nativeClose", "()V", + (void*) Close }, + { "nativeRemoveAllIcons", "()V", + (void*) RemoveAllIcons }, + { "nativeIconForPageUrl", "(Ljava/lang/String;)Landroid/graphics/Bitmap;", + (void*) IconForPageUrl }, + { "nativeRetainIconForPageUrl", "(Ljava/lang/String;)V", + (void*) RetainIconForPageUrl }, + { "nativeReleaseIconForPageUrl", "(Ljava/lang/String;)V", + (void*) ReleaseIconForPageUrl } +}; + +int register_webicondatabase(JNIEnv* env) +{ + jclass webIconDB = env->FindClass("android/webkit/WebIconDatabase"); + LOG_ASSERT(webIconDB, "Unable to find class android.webkit.WebIconDatabase"); + + return jniRegisterNativeMethods(env, "android/webkit/WebIconDatabase", + gWebIconDatabaseMethods, NELEM(gWebIconDatabaseMethods)); +} + +} diff --git a/WebCore/platform/android/jni/WebIconDatabase.h b/WebCore/platform/android/jni/WebIconDatabase.h new file mode 100644 index 0000000..eefe1f6 --- /dev/null +++ b/WebCore/platform/android/jni/WebIconDatabase.h @@ -0,0 +1,69 @@ +/* //device/libs/WebKitLib/WebKit/WebCore/platform/android/jni/android_webkit_webicondatabase.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_WEBKIT_WEBICONDATABASE_H +#define ANDROID_WEBKIT_WEBICONDATABASE_H + +#include "IconDatabaseClient.h" +#include "utils/threads.h" +#include "wtf/Vector.h" + +#include <jni.h> + +namespace WebCore { + class Image; + class String; +} + +namespace android { + + class WebIconDatabaseClient { + public: + virtual ~WebIconDatabaseClient() {} + virtual void didAddIconForPageUrl(const WebCore::String& pageUrl) = 0; + }; + + class WebIconDatabase : public WebCore::IconDatabaseClient { + public: + WebIconDatabase() : mDeliveryRequested(false) {} + // IconDatabaseClient method + virtual void dispatchDidAddIconForPageURL(const WebCore::String& pageURL); + + static void RegisterForIconNotification(WebIconDatabaseClient* client); + static void UnregisterForIconNotification(WebIconDatabaseClient* client); + static void DeliverNotifications(void*); + + private: + // Deliver all the icon notifications + void deliverNotifications(); + + // List of clients and a mutex to protect it. + Vector<WebIconDatabaseClient*> mClients; + android::Mutex mClientsMutex; + + // Queue of page urls that have received an icon. + Vector<WebCore::String> mNotifications; + android::Mutex mNotificationsMutex; + // Flag to indicate that we have requested a delivery of notifications. + bool mDeliveryRequested; + }; + + jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon); + +}; + +#endif diff --git a/WebCore/platform/android/jni/WebSettings.cpp b/WebCore/platform/android/jni/WebSettings.cpp new file mode 100644 index 0000000..8d9efc0 --- /dev/null +++ b/WebCore/platform/android/jni/WebSettings.cpp @@ -0,0 +1,309 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "websettings" + +#include <config.h> +#include <wtf/Platform.h> + +#include "Document.h" +#include "Frame.h" +#include "FrameAndroid.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "DocLoader.h" +#include "Page.h" +#ifdef ANDROID_PLUGINS +#include "PluginDatabaseAndroid.h" +#endif +#include "Settings.h" +#include "WebCoreFrameBridge.h" + +#undef LOG +#include <JNIHelp.h> +#include <utils/Log.h> +#include <utils/misc.h> + +namespace android { + +struct FieldIds { + FieldIds(JNIEnv* env, jclass clazz) { + mLayoutAlgorithm = env->GetFieldID(clazz, "mLayoutAlgorithm", + "Landroid/webkit/WebSettings$LayoutAlgorithm;"); + mTextSize = env->GetFieldID(clazz, "mTextSize", + "Landroid/webkit/WebSettings$TextSize;"); + mStandardFontFamily = env->GetFieldID(clazz, "mStandardFontFamily", + "Ljava/lang/String;"); + mFixedFontFamily = env->GetFieldID(clazz, "mFixedFontFamily", + "Ljava/lang/String;"); + mSansSerifFontFamily = env->GetFieldID(clazz, "mSansSerifFontFamily", + "Ljava/lang/String;"); + mSerifFontFamily = env->GetFieldID(clazz, "mSerifFontFamily", + "Ljava/lang/String;"); + mCursiveFontFamily = env->GetFieldID(clazz, "mCursiveFontFamily", + "Ljava/lang/String;"); + mFantasyFontFamily = env->GetFieldID(clazz, "mFantasyFontFamily", + "Ljava/lang/String;"); + mDefaultTextEncoding = env->GetFieldID(clazz, "mDefaultTextEncoding", + "Ljava/lang/String;"); + mUserAgent = env->GetFieldID(clazz, "mUserAgent", + "Ljava/lang/String;"); + mMinimumFontSize = env->GetFieldID(clazz, "mMinimumFontSize", "I"); + mMinimumLogicalFontSize = env->GetFieldID(clazz, "mMinimumLogicalFontSize", "I"); + mDefaultFontSize = env->GetFieldID(clazz, "mDefaultFontSize", "I"); + mDefaultFixedFontSize = env->GetFieldID(clazz, "mDefaultFixedFontSize", "I"); + mLoadsImagesAutomatically = env->GetFieldID(clazz, "mLoadsImagesAutomatically", "Z"); +#ifdef ANDROID_BLOCK_NETWORK_IMAGE + mBlockNetworkImage = env->GetFieldID(clazz, "mBlockNetworkImage", "Z"); +#endif + mJavaScriptEnabled = env->GetFieldID(clazz, "mJavaScriptEnabled", "Z"); + mPluginsEnabled = env->GetFieldID(clazz, "mPluginsEnabled", "Z"); +#ifdef ANDROID_PLUGINS + mPluginsPath = env->GetFieldID(clazz, "mPluginsPath", "Ljava/lang/String;"); +#endif + mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz, + "mJavaScriptCanOpenWindowsAutomatically", "Z"); + mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z"); + mSupportMultipleWindows = env->GetFieldID(clazz, "mSupportMultipleWindows", "Z"); + mUseDoubleTree = env->GetFieldID(clazz, "mUseDoubleTree", "Z"); + + LOG_ASSERT(mLayoutAlgorithm, "Could not find field mLayoutAlgorithm"); + LOG_ASSERT(mTextSize, "Could not find field mTextSize"); + LOG_ASSERT(mStandardFontFamily, "Could not find field mStandardFontFamily"); + LOG_ASSERT(mFixedFontFamily, "Could not find field mFixedFontFamily"); + LOG_ASSERT(mSansSerifFontFamily, "Could not find field mSansSerifFontFamily"); + LOG_ASSERT(mSerifFontFamily, "Could not find field mSerifFontFamily"); + LOG_ASSERT(mCursiveFontFamily, "Could not find field mCursiveFontFamily"); + LOG_ASSERT(mFantasyFontFamily, "Could not find field mFantasyFontFamily"); + LOG_ASSERT(mDefaultTextEncoding, "Could not find field mDefaultTextEncoding"); + LOG_ASSERT(mUserAgent, "Could not find field mUserAgent"); + LOG_ASSERT(mMinimumFontSize, "Could not find field mMinimumFontSize"); + LOG_ASSERT(mMinimumLogicalFontSize, "Could not find field mMinimumLogicalFontSize"); + LOG_ASSERT(mDefaultFontSize, "Could not find field mDefaultFontSize"); + LOG_ASSERT(mDefaultFixedFontSize, "Could not find field mDefaultFixedFontSize"); + LOG_ASSERT(mLoadsImagesAutomatically, "Could not find field mLoadsImagesAutomatically"); +#ifdef ANDROID_BLOCK_NETWORK_IMAGE + LOG_ASSERT(mBlockNetworkImage, "Could not find field mBlockNetworkImage"); +#endif + LOG_ASSERT(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled"); + LOG_ASSERT(mPluginsEnabled, "Could not find field mPluginsEnabled"); +#ifdef ANDROID_PLUGINS + LOG_ASSERT(mPluginsPath, "Could not find field mPluginsPath"); +#endif + LOG_ASSERT(mJavaScriptCanOpenWindowsAutomatically, + "Could not find field mJavaScriptCanOpenWindowsAutomatically"); + LOG_ASSERT(mUseWideViewport, "Could not find field mUseWideViewport"); + LOG_ASSERT(mSupportMultipleWindows, "Could not find field mSupportMultipleWindows"); + LOG_ASSERT(mUseDoubleTree, "Could not find field mUseDoubleTree"); + + jclass c = env->FindClass("java/lang/Enum"); + LOG_ASSERT(c, "Could not find Enum class!"); + mOrdinal = env->GetMethodID(c, "ordinal", "()I"); + LOG_ASSERT(mOrdinal, "Could not find method ordinal"); + c = env->FindClass("android/webkit/WebSettings$TextSize"); + LOG_ASSERT(c, "Could not find TextSize enum"); + mTextSizeValue = env->GetFieldID(c, "value", "I"); + } + + // Field ids + jfieldID mLayoutAlgorithm; + jfieldID mTextSize; + jfieldID mStandardFontFamily; + jfieldID mFixedFontFamily; + jfieldID mSansSerifFontFamily; + jfieldID mSerifFontFamily; + jfieldID mCursiveFontFamily; + jfieldID mFantasyFontFamily; + jfieldID mDefaultTextEncoding; + jfieldID mUserAgent; + jfieldID mMinimumFontSize; + jfieldID mMinimumLogicalFontSize; + jfieldID mDefaultFontSize; + jfieldID mDefaultFixedFontSize; + jfieldID mLoadsImagesAutomatically; +#ifdef ANDROID_BLOCK_NETWORK_IMAGE + jfieldID mBlockNetworkImage; +#endif + jfieldID mJavaScriptEnabled; + jfieldID mPluginsEnabled; +#ifdef ANDROID_PLUGINS + jfieldID mPluginsPath; +#endif + jfieldID mJavaScriptCanOpenWindowsAutomatically; + jfieldID mUseWideViewport; + jfieldID mSupportMultipleWindows; + jfieldID mUseDoubleTree; + + // Ordinal() method and value field for enums + jmethodID mOrdinal; + jfieldID mTextSizeValue; +}; + +static struct FieldIds* gFieldIds; + +class WebSettings { +public: + static void Sync(JNIEnv* env, jobject obj, jint frame) + { + WebCore::FrameAndroid* pFrame = (WebCore::FrameAndroid*)frame; + LOG_ASSERT(pFrame, "%s must take a valid frame pointer!", __FUNCTION__); + WebCore::Settings* s = pFrame->page()->settings(); + WebCore::DocLoader* docLoader = pFrame->document()->docLoader(); + +#ifdef ANDROID_LAYOUT + jobject layout = env->GetObjectField(obj, gFieldIds->mLayoutAlgorithm); + WebCore::Settings::LayoutAlgorithm l = (WebCore::Settings::LayoutAlgorithm) + env->CallIntMethod(layout, gFieldIds->mOrdinal); + if (s->layoutAlgorithm() != l) { + s->setLayoutAlgorithm(l); + if (pFrame->document()) { + pFrame->document()->updateStyleSelector(); + if (pFrame->document()->renderer()) { + pFrame->cleanupForFullLayout(pFrame->document()->renderer()); + LOG_ASSERT(pFrame->view(), "No view for this frame when trying to relayout"); + pFrame->view()->layout(); + // FIXME: This call used to scroll the page to put the focus into view. + // It worked on the WebViewCore, but now scrolling is done outside of the + // WebViewCore, on the UI side, so there needs to be a new way to do this. + //pFrame->makeFocusVisible(); + } + } + } +#endif + jobject textSize = env->GetObjectField(obj, gFieldIds->mTextSize); + jint zoomFactor = env->GetIntField(textSize, gFieldIds->mTextSizeValue); + if (pFrame->zoomFactor() != zoomFactor) + pFrame->setZoomFactor(zoomFactor); + + jstring str = (jstring)env->GetObjectField(obj, gFieldIds->mStandardFontFamily); + const char* font = env->GetStringUTFChars(str, NULL); + s->setStandardFontFamily(font); + env->ReleaseStringUTFChars(str, font); + + str = (jstring)env->GetObjectField(obj, gFieldIds->mFixedFontFamily); + font = env->GetStringUTFChars(str, NULL); + s->setFixedFontFamily(font); + env->ReleaseStringUTFChars(str, font); + + str = (jstring)env->GetObjectField(obj, gFieldIds->mSansSerifFontFamily); + font = env->GetStringUTFChars(str, NULL); + s->setSansSerifFontFamily(font); + env->ReleaseStringUTFChars(str, font); + + str = (jstring)env->GetObjectField(obj, gFieldIds->mSerifFontFamily); + font = env->GetStringUTFChars(str, NULL); + s->setSerifFontFamily(font); + env->ReleaseStringUTFChars(str, font); + + str = (jstring)env->GetObjectField(obj, gFieldIds->mCursiveFontFamily); + font = env->GetStringUTFChars(str, NULL); + s->setCursiveFontFamily(font); + env->ReleaseStringUTFChars(str, font); + + str = (jstring)env->GetObjectField(obj, gFieldIds->mFantasyFontFamily); + font = env->GetStringUTFChars(str, NULL); + s->setFantasyFontFamily(font); + env->ReleaseStringUTFChars(str, font); + + str = (jstring)env->GetObjectField(obj, gFieldIds->mDefaultTextEncoding); + const char* encoding = env->GetStringUTFChars(str, NULL); + s->setDefaultTextEncodingName(encoding); + env->ReleaseStringUTFChars(str, encoding); + + str = (jstring)env->GetObjectField(obj, gFieldIds->mUserAgent); + const char* userAgentStr = env->GetStringUTFChars(str, NULL); + pFrame->bridge()->setUserAgent(userAgentStr); + env->ReleaseStringUTFChars(str, userAgentStr); + + jint size = env->GetIntField(obj, gFieldIds->mMinimumFontSize); + s->setMinimumFontSize(size); + + size = env->GetIntField(obj, gFieldIds->mMinimumLogicalFontSize); + s->setMinimumLogicalFontSize(size); + + size = env->GetIntField(obj, gFieldIds->mDefaultFontSize); + s->setDefaultFontSize(size); + + size = env->GetIntField(obj, gFieldIds->mDefaultFixedFontSize); + s->setDefaultFixedFontSize(size); + + jboolean flag = env->GetBooleanField(obj, gFieldIds->mLoadsImagesAutomatically); + s->setLoadsImagesAutomatically(flag); + if (flag) + docLoader->setAutoLoadImages(true); + +#ifdef ANDROID_BLOCK_NETWORK_IMAGE + flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkImage); + s->setBlockNetworkImage(flag); + if(!flag) + docLoader->setBlockNetworkImage(false); +#endif + + flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptEnabled); + s->setJavaScriptEnabled(flag); + + flag = env->GetBooleanField(obj, gFieldIds->mPluginsEnabled); + s->setPluginsEnabled(flag); + +#ifdef ANDROID_PLUGINS + str = (jstring)env->GetObjectField(obj, gFieldIds->mPluginsPath); + if (str) { + const char* pluginsPathStr = env->GetStringUTFChars(str, NULL); + s->setPluginsPath(pluginsPathStr); + // Also sync PluginDatabaseAndroid with the new default path. + ::WebCore::PluginDatabaseAndroid::setDefaultPluginsPath(pluginsPathStr); + env->ReleaseStringUTFChars(str, pluginsPathStr); + } +#endif + + flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptCanOpenWindowsAutomatically); + s->setJavaScriptCanOpenWindowsAutomatically(flag); + + flag = env->GetBooleanField(obj, gFieldIds->mUseWideViewport); + s->setUseWideViewport(flag); + +#ifdef ANDROID_MULTIPLE_WINDOWS + flag = env->GetBooleanField(obj, gFieldIds->mSupportMultipleWindows); + s->setSupportMultipleWindows(flag); +#endif + +#if USE(LOW_BANDWIDTH_DISPLAY) + flag = env->GetBooleanField(obj, gFieldIds->mUseDoubleTree); + pFrame->loader()->setUseLowBandwidthDisplay(flag); +#endif + } +}; + +//------------------------------------------------------------- +// JNI registration +//------------------------------------------------------------- + +static JNINativeMethod gWebSettingsMethods[] = { + { "nativeSync", "(I)V", + (void*) WebSettings::Sync } +}; + +int register_websettings(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/webkit/WebSettings"); + LOG_ASSERT(clazz, "Unable to find class WebSettings!"); + gFieldIds = new FieldIds(env, clazz); + return jniRegisterNativeMethods(env, "android/webkit/WebSettings", + gWebSettingsMethods, NELEM(gWebSettingsMethods)); +} + +} diff --git a/WebCore/platform/android/jni/WebViewCore.cpp b/WebCore/platform/android/jni/WebViewCore.cpp new file mode 100644 index 0000000..dc2cd48 --- /dev/null +++ b/WebCore/platform/android/jni/WebViewCore.cpp @@ -0,0 +1,2211 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "webcoreglue" + +#include <config.h> +#include <wtf/Platform.h> + +#include "android_graphics.h" +#include "GraphicsJNI.h" +#include "SkPicture.h" + +#include "AtomicString.h" +#include "CacheBuilder.h" +#include "CachedNode.h" +#include "CachedRoot.h" +#include "Color.h" +#include "Document.h" +#include "Element.h" +#include "Editor.h" +#include "EditorClientAndroid.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "Font.h" +#include "FrameAndroid.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "HTMLAnchorElement.h" +#include "HTMLAreaElement.h" +#include "HTMLElement.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLMapElement.h" +#include "HTMLNames.h" +#include "HTMLOptGroupElement.h" +#include "HTMLOptionElement.h" +#include "HTMLSelectElement.h" +#include "HTMLTextAreaElement.h" +#include "HTMLTextFieldInnerElement.h" +#include "InlineTextBox.h" +#include "KeyboardCodes.h" +#include "Node.h" +#include "Page.h" +#include "PlatformGraphicsContext.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformString.h" +#include "PluginInfoStore.h" +#include "Position.h" +#include "RenderLayer.h" +#include "RenderText.h" +#include "RenderTextControl.h" +#include "RenderThemeAndroid.h" +#include "RenderView.h" +#include "ResourceRequest.h" +#include "SelectionController.h" +#include "Settings.h" +#include "StringImpl.h" +#include "Text.h" +#include "TypingCommand.h" +#include "WebCoreFrameBridge.h" +#include "WebViewCore.h" +#include "HistoryItem.h" + +#ifdef LOG +#undef LOG +#endif + +#include <ui/KeycodeLabels.h> +#include <utils/Log.h> +#include "SkTDArray.h" +#include "SkTime.h" +#include <SkTypes.h> +#include <SkCanvas.h> +#include <SkUtils.h> +#include <JNIHelp.h> + +#include "SystemTime.h" + +#ifdef ANDROID_INSTRUMENT +static uint32_t sTotalTimeUsed = 0; +static uint32_t sTotalPaintTimeUsed = 0; +static uint32_t sCounter = 0; + +namespace WebCore { +void Frame::resetWebViewCoreTimeCounter() +{ + sTotalTimeUsed = 0; +} + +void Frame::reportWebViewCoreTimeCounter() +{ + LOG(LOG_DEBUG, "WebCore", "*-* Total native 4 (webview core) time: %d ms\n", + sTotalTimeUsed); +} +// This should be in Frame.cpp, but android LOG is conflict with webcore LOG +void Frame::resetPaintTimeCounter() +{ + sTotalPaintTimeUsed = 0; + sCounter = 0; +} + +void Frame::reportPaintTimeCounter() +{ + LOG(LOG_DEBUG, "WebCore", "*-* Total draw time: %d ms called %d times\n", + sTotalPaintTimeUsed, sCounter); +} +} +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////// + +namespace android { + +#ifdef ANDROID_INSTRUMENT +class TimeCounterWV { +public: + TimeCounterWV() { + mStartTime = WebCore::get_thread_msec(); + } + + ~TimeCounterWV() { + sTotalTimeUsed += WebCore::get_thread_msec() - mStartTime; + } + +private: + uint32_t mStartTime; +}; +#endif + +// ---------------------------------------------------------------------------- + +#define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.mNativeClass)) + +// Field ids for WebViewCore +struct WebViewCoreFields { + jfieldID mNativeClass; + jfieldID mViewportWidth; + jfieldID mViewportHeight; + jfieldID mViewportInitialScale; + jfieldID mViewportMinimumScale; + jfieldID mViewportMaximumScale; + jfieldID mViewportUserScalable; + jfieldID mWebView; +} gWebViewCoreFields; + +// ---------------------------------------------------------------------------- + +extern JavaVM* jnienv_to_javavm(JNIEnv* env); +extern JNIEnv* javavm_to_jnienv(JavaVM* vm); + +// ---------------------------------------------------------------------------- + +/** + * Helper method for checking java exceptions + * @return true if an exception occurred. + */ +static bool checkException(JNIEnv *env) +{ + if (env->ExceptionCheck() != 0) + { + LOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +// ---------------------------------------------------------------------------- + +struct WebViewCore::JavaGlue +{ + JavaVM* mJVM; + jobject mObj; + jmethodID mSpawnScrollTo; + jmethodID mScrollTo; + jmethodID mScrollBy; + jmethodID mContentInvalidate; + jmethodID mRequestListBox; + jmethodID mRequestSingleListBox; + jmethodID mJsAlert; + jmethodID mJsConfirm; + jmethodID mJsPrompt; + jmethodID mJsUnload; + jmethodID mDidFirstLayout; + jmethodID mSendMarkNodeInvalid; + jmethodID mSendNotifyFocusSet; + jmethodID mSendNotifyProgressFinished; + jmethodID mSendRecomputeFocus; + jmethodID mSendViewInvalidate; + jmethodID mUpdateTextfield; + jmethodID mRestoreScale; +}; + +/* + * WebViewCore Implementation + */ + +static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[]) +{ + jmethodID m = env->GetMethodID(clazz, name, signature); + LOG_ASSERT(m, "Could not find method %s", name); + return m; +} + +Mutex WebViewCore::gFrameCacheMutex; +Mutex WebViewCore::gFrameGenerationMutex; +Mutex WebViewCore::gRecomputeFocusMutex; +Mutex WebViewCore::gButtonMutex; + +WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::FrameView* view) + : mView(view) +{ + mFrame = (WebCore::FrameAndroid*)mView->frame(); + // This will be derefed in WebCoreFrameBridge during destroy. + mView->ref(); + + mPopupReply = NULL; + mBuildGeneration = 0; + mMoveGeneration = 0; + mGeneration = 0; + mLastGeneration = 0; + mTouchGeneration = 0; + mBlockTextfieldUpdates = false; + // just initial values. These should be set by client + mMaxXScroll = 320/4; + mMaxYScroll = 240/4; + mButtons = NULL; + mTextGeneration = 0; + + LOG_ASSERT(mFrame, "Uh oh, somehow a frameview was made without an initial frame!"); + + jclass clazz = env->GetObjectClass(javaWebViewCore); + mJavaGlue = new JavaGlue; + mJavaGlue->mJVM = jnienv_to_javavm(env); + mJavaGlue->mObj = env->NewGlobalRef(javaWebViewCore); + mJavaGlue->mSpawnScrollTo = GetJMethod(env, clazz, "contentSpawnScrollTo", "(II)V"); + mJavaGlue->mScrollTo = GetJMethod(env, clazz, "contentScrollTo", "(II)V"); + mJavaGlue->mScrollBy = GetJMethod(env, clazz, "contentScrollBy", "(II)V"); + mJavaGlue->mContentInvalidate = GetJMethod(env, clazz, "contentInvalidate", "()V"); + mJavaGlue->mRequestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[Z[I)V"); + mJavaGlue->mRequestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[ZI)V"); + mJavaGlue->mJsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V"); + mJavaGlue->mJsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z"); + mJavaGlue->mJsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + mJavaGlue->mJsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z"); + mJavaGlue->mDidFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Ljava/lang/String;)V"); + mJavaGlue->mSendMarkNodeInvalid = GetJMethod(env, clazz, "sendMarkNodeInvalid", "(I)V"); + mJavaGlue->mSendNotifyFocusSet = GetJMethod(env, clazz, "sendNotifyFocusSet", "()V"); + mJavaGlue->mSendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V"); + mJavaGlue->mSendRecomputeFocus = GetJMethod(env, clazz, "sendRecomputeFocus", "()V"); + mJavaGlue->mSendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "()V"); + mJavaGlue->mUpdateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V"); + mJavaGlue->mRestoreScale = GetJMethod(env, clazz, "restoreScale", "(I)V"); + + env->SetIntField(javaWebViewCore, gWebViewCoreFields.mNativeClass, (jint)this); + + mVisibleRect.setWidth(0); + mVisibleRect.setHeight(0); + + reset(true); +} + +WebViewCore::~WebViewCore() +{ + // Release the focused view + Release(mPopupReply); + + if (mJavaGlue->mObj) { + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->DeleteGlobalRef(mJavaGlue->mObj); + mJavaGlue->mObj = 0; + } + delete mJavaGlue; + delete mFrameCacheKit; + delete mNavPictureKit; + if (mButtons != NULL) { + mButtons->deleteAll(); + delete mButtons; + } +} + +void WebViewCore::reset(bool fromConstructor) +{ + if (fromConstructor) { + mFrameCacheKit = NULL; + mNavPictureKit = NULL; + } else { + gFrameCacheMutex.lock(); + delete mFrameCacheKit; + delete mNavPictureKit; + mFrameCacheKit = NULL; + mNavPictureKit = NULL; + gFrameCacheMutex.unlock(); + } + + mLastFocused = NULL; + mLastFocusedBounds = WebCore::IntRect(0,0,0,0); + mUpdatedFrameCache = true; + mFrameCacheOutOfDate = true; + mBlockFocusChange = false; + mSnapAnchorNode = NULL; + mUseReplay = false; +} + +void WebViewCore::draw(WebCore::GraphicsContext* ctx, + const WebCore::IntRect& r, bool invalCache) +{ + LOG_ASSERT(mFrame, "%s has no frame for this view!", __FUNCTION__); + LOG_ASSERT(mFrame == mView->frame(), "Our frames do not match!"); +#ifdef ANDROID_INSTRUMENT + uint32_t startTime = WebCore::get_thread_msec(); +#endif + if (NULL == mFrame->renderer()) { + // We only do this if there is nothing else to draw. + // If there is a renderer, it will fill the bg itself, so we don't want to + // double-draw (slow) + SkCanvas* canvas = ctx->platformContext()->mCanvas; + canvas->drawColor(SK_ColorWHITE); + } + else { + mFrame->paint(ctx, r); + } + if (invalCache) { + WebCore::CacheBuilder& builder = mFrame->getCacheBuilder(); + WebCore::Node* oldFocusNode = builder.currentFocus(); + mFrameCacheOutOfDate = true; + WebCore::IntRect oldBounds = oldFocusNode ? + oldFocusNode->getRect() : WebCore::IntRect(0,0,0,0); + DBG_NAV_LOGD_ONCE("mLastFocused=%p oldFocusNode=%p" + " mLastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}", + mLastFocused, oldFocusNode, + mLastFocusedBounds.x(), mLastFocusedBounds.y(), mLastFocusedBounds.width(), mLastFocusedBounds.height(), + oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height()); + if (mLastFocused != oldFocusNode || mLastFocusedBounds != oldBounds) { + mLastFocused = oldFocusNode; + mLastFocusedBounds = oldBounds; + DBG_NAV_LOG("call updateFrameCache"); + updateFrameCache(); + } + } +#ifdef ANDROID_INSTRUMENT + sTotalPaintTimeUsed += WebCore::get_thread_msec() - startTime; + sCounter++; +#endif +} + +void WebViewCore::recordPicture(SkPicture* picture, bool invalCache) +{ + // if there is no document yet, just return + if (!mFrame->document()) + return; + + // Call layout to ensure that the contentWidth and contentHeight are correct + WebCore::ScrollView* view = mFrame->view(); + view->ignoreUpdateContents(true); + layout(); + view->ignoreUpdateContents(false); + +#if USE(DOUBLE_TREE) + // if haveStylesheetsLoaded() is false, render is not created, so don't call draw. + // It can happen when we switch from fast display mode, new style sheets are requested, + // if we call draw, we will have blank screen. + if (mFrame->document() && !mFrame->loader()->isComplete() && + !mFrame->document()->haveStylesheetsLoaded()) { + LOGV("skip %s", __FUNCTION__); + return; + } +#endif + + // draw into the picture's recording canvas + SkAutoPictureRecord arp(picture, contentWidth(), contentHeight()); + SkAutoMemoryUsageProbe mup(__FUNCTION__); + + WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas()); + WebCore::GraphicsContext gc(&pgc); + WebCore::IntRect r = WebCore::IntRect(0, 0, INT_MAX, INT_MAX); + draw(&gc, r, invalCache); + + // If this was done for the Java Picture that is being displayed, grab the + // info for the buttons, so the UI thread can grab them and update them + // according to their state. + if (invalCache) { + gButtonMutex.lock(); + if (mButtons != NULL) { + mButtons->deleteAll(); + delete mButtons; + } + mButtons = pgc.getAndClearButtonInfo(); + gButtonMutex.unlock(); + } +} + +void WebViewCore::onResize() +{ + LOGV("view is resized, notifying the frame to relayout..."); + LOG_ASSERT(mFrame == mView->frame(), "Our frames do not match!"); + // r could be NULL because our frame has not processed any data. This happens + // when an HTMLWidget is first created, attached and sizeChanged is called. + WebCore::RenderObject *r = mFrame->renderer(); + if (r) { + r->setNeedsLayoutAndPrefWidthsRecalc(); + mFrame->forceLayout(true); + } +} + +WebCore::RenderLayer* WebViewCore::getRenderLayer() +{ + LOG_ASSERT(mView, "A view was not associated with this view bridge!"); + LOG_ASSERT(mFrame, "A frame was not associated with our frame view!"); + LOG_ASSERT(mFrame == mView->frame(), "Our frames do not match!"); + WebCore::Document* doc = mFrame->document(); + if (doc == NULL) + return NULL; + WebCore::RenderObject* renderer = doc->renderer(); + return renderer->enclosingLayer(); +} + +bool WebViewCore::pinXToDocument(int* xPtr) +{ + WebCore::RenderLayer* layer = getRenderLayer(); + if (layer == NULL) + return false; + int docWidth = layer->width(); + int x = *xPtr; + if (x < 0 || docWidth <= this->width()) + x = 0; + else if (x + this->width() > docWidth) + x = docWidth - this->width(); + *xPtr = x; + return true; +} + +bool WebViewCore::pinYToDocument(int* yPtr) +{ + WebCore::RenderLayer* layer = getRenderLayer(); + if (layer == NULL) + return false; + int docHeight = layer->height(); + int y = *yPtr; + if (y < 0 || docHeight <= this->height()) + y = 0; + else if (y + this->height() > docHeight) + y = docHeight - this->height(); + *yPtr = y; + return true; +} + +void WebViewCore::scrollTo(int x, int y, bool animate) +{ + LOG_ASSERT(mParent == NULL, "Why do we have a parent"); + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + +// LOGD("WebViewCore::scrollTo(%d %d)\n", x, y); + + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, animate ? mJavaGlue->mSpawnScrollTo : mJavaGlue->mScrollTo, x, y); + checkException(env); +} + +void WebViewCore::sendMarkNodeInvalid(WebCore::Node* node) +{ + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mSendMarkNodeInvalid, (int) node); + checkException(env); +} + +void WebViewCore::sendNotifyFocusSet() +{ + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mSendNotifyFocusSet); + checkException(env); +} + +void WebViewCore::sendNotifyProgressFinished() +{ + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mSendNotifyProgressFinished); + checkException(env); +} + +void WebViewCore::sendRecomputeFocus() +{ + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mSendRecomputeFocus); + checkException(env); +} + +void WebViewCore::sendViewInvalidate() +{ + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mSendViewInvalidate); + checkException(env); +} + +void WebViewCore::scrollBy(int dx, int dy) +{ + LOG_ASSERT(mParent == NULL, "Why do we have a parent"); + if ((dx | dy) == 0) + return; + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mScrollBy, dx, dy); + checkException(env); +} + +void WebViewCore::contentInvalidate(const WebCore::IntRect &rect) +{ +// DBG_NAV_LOGD("rect={%d,%d,%d,%d}", rect.x(), rect.y(), rect.width(), rect.height()); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mContentInvalidate); + checkException(env); +} + +void WebViewCore::contentInvalidate() +{ +// DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mContentInvalidate); + checkException(env); +} + +static int pin_pos(int x, int width, int targetWidth) +{ + if (x + width > targetWidth) + x = targetWidth - width; + if (x < 0) + x = 0; + return x; +} + +void WebViewCore::didFirstLayout() +{ + LOG_ASSERT(mParent == NULL, "Why do we have a parent"); + + DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + + const WebCore::KURL& url = mFrame->loader()->url(); + if (url.isEmpty()) + return; + LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data()); + + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + WebCore::String urlString(url.string()); + jstring urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length()); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mDidFirstLayout, urlStr); + checkException(env); + env->DeleteLocalRef(urlStr); + + DBG_NAV_LOG("call updateFrameCache"); + updateFrameCache(); + mHistory.setDidFirstLayout(true); +} + +void WebViewCore::restoreScale(int scale) +{ + LOG_ASSERT(mParent == NULL, "Why do we have a parent"); + + DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); + LOG_ASSERT(mJavaGlue->mObj, "A Java widget was not associated with this view bridge!"); + + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mRestoreScale, scale); + checkException(env); +} + +static void layoutIfNeededRecursive(WebCore::Frame* f) +{ + if (!f) + return; + + WebCore::FrameView* v = f->view(); + if (!v) + return; + + if (v->needsLayout()) + v->layout(); + + if (!f->tree()) + return; + + WebCore::Frame* child = f->tree()->firstChild(); + while (child) { + WebCore::FrameView* childView = child->view(); + if (childView) + layoutIfNeededRecursive(child); + if (!child->tree()) + break; + child = child->tree()->nextSibling(); + } +} + +void WebViewCore::layout() +{ + LOG_ASSERT(mParent == NULL, "Why do we have a parent"); + + SkASSERT(mView); + layoutIfNeededRecursive(mFrame); +} + +void WebViewCore::notifyFocusSet() +{ + sendNotifyFocusSet(); +} + +void WebViewCore::notifyProgressFinished() +{ + DBG_NAV_LOG("call updateFrameCache"); + updateFrameCache(); + sendNotifyProgressFinished(); +} + +void WebViewCore::doMaxScroll(WebCore::CacheBuilder::Direction dir) +{ + int dx = 0, dy = 0; + + switch (dir) { + case WebCore::CacheBuilder::LEFT: + dx = -this->getMaxXScroll(); + break; + case WebCore::CacheBuilder::UP: + dy = -this->getMaxXScroll(); + break; + case WebCore::CacheBuilder::RIGHT: + dx = this->getMaxYScroll(); + break; + case WebCore::CacheBuilder::DOWN: + dy = this->getMaxYScroll(); + break; + case WebCore::CacheBuilder::UNINITIALIZED: + default: + LOG_ASSERT(0, "unexpected focus selector"); + } + this->scrollBy(dx, dy); +} + +void WebViewCore::getVisibleRect(WebCore::IntRect* rect) const +{ + SkASSERT(rect); + *rect = mVisibleRect; +} + +void WebViewCore::setVisibleRect(const WebCore::IntRect& r) +{ + DBG_NAV_LOGD("{%d,%d,%d,%d}", r.x(), r.y(), r.width(), r.height()); + if (mVisibleRect != r) { + if (mVisibleRect.x() != r.x() || mVisibleRect.y() != r.y()) + mFrame->sendScrollEvent(); + mVisibleRect = r; + // The visible rect is located within our coordinate space so it + // contains the actual scroll position. Setting the location makes hit + // testing work correctly. + setLocation(r.x(), r.y()); + } +} + +void WebViewCore::dump() +{ +#if DUMP_NAV_CACHE + if (mFrame != NULL) + mFrame->getCacheBuilder().mDebug.print(); +#endif +} + +WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node) +{ + WebCore::CacheBuilder& builder = mFrame->getCacheBuilder(); + if (builder.validNode(frame, node) == false) + return WebCore::String(); + if (node->hasTagName(WebCore::HTMLNames::aTag) == false) + return WebCore::String(); + WebCore::HTMLAnchorElement* anchor = static_cast<WebCore::HTMLAnchorElement*>(node); + return anchor->href(); +} + +WebCore::String WebViewCore::retrieveImageRef(int x, int y) +{ + WebCore::IntPoint point(x,y); + WebCore::HitTestResult result = mFrame->eventHandler()->hitTestResultAtPoint(point, false); + WebCore::Node* node = result.innerNode(); + if (node == NULL || node->hasTagName(WebCore::HTMLNames::imgTag) == false) + return WebCore::String(); + WebCore::HTMLImageElement* img = static_cast<WebCore::HTMLImageElement*>(node); + return img->src(); +} + +bool WebViewCore::prepareFrameCache() +{ + if (mFrameCacheOutOfDate == false) { + DBG_NAV_LOG("mFrameCacheOutOfDate == false"); + return false; + } + mFrameCacheOutOfDate = false; +#if DEBUG_NAV_UI + DBG_NAV_LOG("mFrameCacheOutOfDate was true"); + mNow = SkTime::GetMSecs(); +#endif + mTemp = new CachedRoot(); + mTemp->init(mFrame, &mHistory); + mTemp->setGeneration(++mBuildGeneration); + WebCore::CacheBuilder& builder = mFrame->getCacheBuilder(); + WebCore::Settings* settings = frame()->page()->settings(); + builder.allowAllTextDetection(); + if (settings->formatDetectionAddress() == false) + builder.disallowAddressDetection(); + if (settings->formatDetectionEmail() == false) + builder.disallowEmailDetection(); + if (settings->formatDetectionTelephone() == false) + builder.disallowPhoneDetection(); + builder.buildCache(mTemp); + mTempPict = new SkPicture(); + recordPicture(mTempPict, false); + mTemp->setPicture(mTempPict); + mTemp->setTextGeneration(mTextGeneration); + return true; +} + +void WebViewCore::releaseFrameCache(bool newCache) +{ + if (newCache == false) { + DBG_NAV_LOG("newCache == false"); + return; + } + gFrameCacheMutex.lock(); + delete mFrameCacheKit; + delete mNavPictureKit; + mFrameCacheKit = mTemp; + mNavPictureKit = mTempPict; + mUpdatedFrameCache = true; +#if DEBUG_NAV_UI + const CachedNode* cachedFocusNode = mFrameCacheKit->currentFocus(); + DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)", + cachedFocusNode ? cachedFocusNode->index() : 0, + cachedFocusNode ? cachedFocusNode->nodePointer() : NULL); +#endif + gFrameCacheMutex.unlock(); + notifyFocusSet(); + // it's tempting to send an invalidate here, but it's a bad idea + // the cache is now up to date, but the focus is not -- the event + // may need to be recomputed from the prior history. An invalidate + // will draw the stale location causing the ring to flash at the wrong place. +} + +void WebViewCore::updateFrameCache() +{ + mUseReplay = false; + releaseFrameCache(prepareFrameCache()); +} + +void WebViewCore::removeFrameGeneration(WebCore::Frame* frame) +{ + DBG_NAV_LOGD("frame=%p mGeneration=%d", frame, mGeneration); + gFrameGenerationMutex.lock(); + int last = mFrameGenerations.size() - 1; + for (int index = 0; index <= last; index++) { + if (mFrameGenerations[index].mFrame == frame) { + DBG_NAV_LOGD("index=%d last=%d", index, last); + if (index != last) + mFrameGenerations[index] = mFrameGenerations[last]; + mFrameGenerations.removeLast(); + break; + } + } + gFrameGenerationMutex.unlock(); +} + +void WebViewCore::updateFrameGeneration(WebCore::Frame* frame) +{ + DBG_NAV_LOGD("frame=%p mGeneration=%d", frame, mGeneration); + gFrameGenerationMutex.lock(); + ++mBuildGeneration; + for (size_t index = 0; index < mFrameGenerations.size(); index++) { + if (mFrameGenerations[index].mFrame == frame) { + DBG_NAV_LOG("replace"); + mFrameGenerations[index].mGeneration = mBuildGeneration; + goto done; + } + } + { + FrameGen frameGen = {frame, mBuildGeneration}; + mFrameGenerations.append(frameGen); + DBG_NAV_LOG("append"); + } +done: + gFrameGenerationMutex.unlock(); +} + +int WebViewCore::retrieveFrameGeneration(WebCore::Frame* frame) +{ + int result = INT_MAX; + gFrameGenerationMutex.lock(); + for (size_t index = 0; index < mFrameGenerations.size(); index++) { + if (mFrameGenerations[index].mFrame == frame) { + result = mFrameGenerations[index].mGeneration; + break; + } + } + gFrameGenerationMutex.unlock(); + DBG_NAV_LOGD("frame=%p mGeneration=%d result=%d", frame, mGeneration, result); + return result; +} + +void WebViewCore::setFinalFocus(WebCore::Frame* frame, WebCore::Node* node, + int x, int y, bool block) +{ + DBG_NAV_LOGD("frame=%p node=%p x=%d y=%d", frame, node, x, y); + bool result = finalKitFocus(frame, node, x, y); + if (block) { + mBlockFocusChange = true; + if (result == false && node != NULL) + touchUp(mTouchGeneration, 0, NULL, NULL, x, y, 0, true, true); + } +} + +void WebViewCore::setKitFocus(int moveGeneration, int buildGeneration, + WebCore::Frame* frame, WebCore::Node* node, int x, int y, + bool ignoreNullFocus) +{ + DBG_NAV_LOGD("mMoveGeneration=%d moveGeneration=%d" + " buildGeneration=%d frame=%p node=%p x=%d y=%d", + mMoveGeneration, moveGeneration, buildGeneration, frame, node, x, y); + if (mBlockFocusChange) { + DBG_NAV_LOG("mBlockFocusChange"); + return; + } + if (mMoveGeneration > moveGeneration) { + DBG_NAV_LOGD("mMoveGeneration=%d > moveGeneration=%d", + mMoveGeneration, moveGeneration); + return; // short-circuit if a newer move has already been generated + } + if (commonKitFocus(moveGeneration, buildGeneration, frame, node, x, y, + ignoreNullFocus) == false) + return; + mLastGeneration = moveGeneration; +} + +bool WebViewCore::commonKitFocus(int generation, int buildGeneration, + WebCore::Frame* frame, WebCore::Node* node, int x, int y, + bool ignoreNullFocus) +{ + DBG_NAV_LOGD("generation=%d buildGeneration=%d frame=%p" + " node=%p x=%d y=%d", generation, buildGeneration, frame, node, x, y); + mUseReplay = true; + bool newCache = prepareFrameCache(); // must wait for possible recompute before using + if (mMoveGeneration > generation) { + DBG_NAV_LOGD("mMoveGeneration=%d > generation=%d", + mMoveGeneration, generation); + releaseFrameCache(newCache); + return false; // short-circuit if a newer move has already been generated + } + // if the nav cache has been rebuilt since this focus request was generated, + // send a request back to the UI side to recompute the kit-side focus + if (mBuildGeneration > buildGeneration || (node != NULL && mFrame->getCacheBuilder().validNode(frame, node) == false)) { + DBG_NAV_LOGD("mBuildGeneration=%d > buildGeneration=%d", + mBuildGeneration, buildGeneration); + gRecomputeFocusMutex.lock(); + bool first = mRecomputeEvents.size() == 0; + mRecomputeEvents.append(generation); + gRecomputeFocusMutex.unlock(); + releaseFrameCache(newCache); + if (first) + sendRecomputeFocus(); + return false; + } + releaseFrameCache(newCache); + if (node == NULL && ignoreNullFocus) + return true; + finalKitFocus(frame, node, x, y); + return true; +} + +bool WebViewCore::finalKitFocus(WebCore::Frame* frame, WebCore::Node* node, + int x, int y) +{ + if (NULL == frame) + frame = mFrame; + WebCore::CacheBuilder& builder = mFrame->getCacheBuilder(); + WebCore::Node* oldFocusNode = builder.currentFocus(); + mMousePos = WebCore::IntPoint(x, y); + // validNode will still return true if the node is null, as long as we have + // a valid frame. Do not want to make a call on frame unless it is valid. + bool valid = builder.validNode(frame, node); + if (valid) { + WebCore::PlatformMouseEvent mouseEvent(mMousePos, mMousePos, WebCore::NoButton, + WebCore::MouseEventMoved, 1, false, false, false, false, WebCore::currentTime()); + frame->eventHandler()->handleMouseMoveEvent(mouseEvent); + } + WebCore::Document* oldDoc = oldFocusNode ? oldFocusNode->document() : NULL; + if (node == NULL) { + if (oldFocusNode != NULL) + oldDoc->setFocusedNode(NULL); + return false; + } else if (valid == false) { + DBG_NAV_LOGD("sendMarkNodeInvalid node=%p", node); + sendMarkNodeInvalid(node); + if (oldFocusNode != NULL) + oldDoc->setFocusedNode(NULL); + return false; + } + // If we jump frames (docs), kill the focus on the old doc + builder.setLastFocus(node); + if (oldFocusNode != NULL && node->document() != oldDoc) { + oldDoc->setFocusedNode(NULL); + } + WebCore::RenderObject* renderer = node->renderer(); + /* <area> element doesn't have a renderer, but it can accept focus */ + LOG_ASSERT(!(renderer && renderer->isWidget()), "widgets unsupported"); + if (node->isTextNode() == false) + static_cast<WebCore::Element*>(node)->focus(false); + //setFocus on things that WebCore doesn't recognize as supporting focus + //for instance, if there is an onclick element that does not support focus + DBG_NAV_LOGD("setFocusedNode node=%p", node); + node->document()->setFocusedNode(node); + mLastFocused = node; + mLastFocusedBounds = node->getRect(); + return true; +} + +// helper function to find the frame that has focus +static WebCore::Frame* FocusedFrame(WebCore::FrameAndroid* frame) +{ + if (!frame) + return NULL; + WebCore::Node* focusNode = frame->getCacheBuilder().currentFocus(); + if (!focusNode) + return NULL; + WebCore::Document* doc = focusNode->document(); + if (!doc) + return NULL; + return doc->frame(); +} + +static WebCore::RenderTextControl* FocusedTextControl(WebCore::FrameAndroid* frame) +{ + WebCore::Node* focusNode = frame->getCacheBuilder().currentFocus(); + WebCore::RenderObject* renderer = focusNode->renderer(); + if (renderer && (renderer->isTextField() || renderer->isTextArea())) { + return static_cast<WebCore::RenderTextControl*>(renderer); + } + return NULL; +} + +WebCore::Frame* WebViewCore::changedKitFocus(WebCore::Frame* frame, + WebCore::Node* node, int x, int y) +{ + if (frame == NULL || node == NULL) + return mFrame; + WebCore::Node* current = mFrame->getCacheBuilder().currentFocus(); + if (current == node) + return frame; + return finalKitFocus(frame, node, x, y) ? frame : mFrame; +} + +static int findTextBoxIndex(WebCore::Node* node, const WebCore::IntPoint& pt) +{ + if (node->isTextNode() == false) { + DBG_NAV_LOGD("node=%p pt=(%d,%d) isText=false", node, pt.x(), pt.y()); + return -2; // error + } + WebCore::RenderText* renderText = (WebCore::RenderText*) node->renderer(); + if (renderText == NULL) { + DBG_NAV_LOGD("node=%p pt=(%d,%d) renderText=NULL", node, pt.x(), pt.y()); + return -3; // error + } + int renderX, renderY; + renderText->absolutePosition(renderX, renderY); + WebCore::InlineTextBox *textBox = renderText->firstTextBox(); + int globalX, globalY; + WebCore::CacheBuilder::GetGlobalOffset(node, &globalX, &globalY); + int x = pt.x() - globalX; + int y = pt.y() - globalY; + do { + int textBoxStart = textBox->start(); + int textBoxEnd = textBoxStart + textBox->len(); + if (textBoxEnd <= textBoxStart) + continue; + WebCore::IntRect bounds = textBox->selectionRect(renderX, renderY, + textBoxStart, textBoxEnd); + if (bounds.contains(x, y) == false) + continue; + int offset = textBox->offsetForPosition(x - renderX); +#if DEBUG_NAV_UI + int prior = offset > 0 ? textBox->positionForOffset(offset - 1) : -1; + int current = textBox->positionForOffset(offset); + int next = textBox->positionForOffset(offset + 1); + DBG_NAV_LOGD( + "offset=%d pt.x=%d globalX=%d renderX=%d x=%d " + "textBox->x()=%d textBox->start()=%d prior=%d current=%d next=%d", + offset, pt.x(), globalX, renderX, x, + textBox->xPos(), textBox->start(), prior, current, next + ); +#endif + return textBox->start() + offset; + } while ((textBox = textBox->nextTextBox()) != NULL); + return -1; // couldn't find point, may have walked off end +} + +static inline bool isSpace(UChar c) +{ + return c <= ' ' || WTF::Unicode::direction(c) == WTF::Unicode::WhiteSpaceNeutral; +} + +static int centerX(const SkIRect& rect) { return (rect.fLeft + rect.fRight) >> 1; } +static int centerY(const SkIRect& rect) { return (rect.fTop + rect.fBottom) >> 1; } + +WebCore::String WebViewCore::getSelection(SkRegion* selRgn) +{ + SkRegion::Iterator iter(*selRgn); + WebCore::String result; + for (; iter.done() == false; iter.next()) { + const SkIRect& rect = iter.rect(); + DBG_NAV_LOGD("rect=(%d, %d, %d, %d)", rect.fLeft, rect.fTop, + rect.fRight, rect.fBottom); + int cy = centerY(rect); + WebCore::IntPoint startPt = WebCore::IntPoint(rect.fLeft + 1, cy); + WebCore::HitTestResult hitTestResult = mFrame->eventHandler()-> + hitTestResultAtPoint(startPt, false); + WebCore::Node* node = hitTestResult.innerNode(); + if (node == NULL) { + DBG_NAV_LOG("node == NULL"); + return result; + } + WebCore::IntPoint endPt = WebCore::IntPoint(rect.fRight - 1, cy); + hitTestResult = mFrame->eventHandler()->hitTestResultAtPoint(endPt, false); + WebCore::Node* endNode = hitTestResult.innerNode(); + if (endNode == NULL) { + DBG_NAV_LOG("endNode == NULL"); + return result; + } + int start = findTextBoxIndex(node, startPt); + if (start < 0) + continue; + int end = findTextBoxIndex(endNode, endPt); + if (end < -1) // use node if endNode is not valid + endNode = node; + if (end <= 0) + end = static_cast<WebCore::Text*>(endNode)->string()->length(); + DBG_NAV_LOGD("node=%p start=%d endNode=%p end=%d", node, start, endNode, end); + do { + if (node->isTextNode() == false) + continue; + if (node->getRect().isEmpty()) + continue; + WebCore::Text* textNode = static_cast<WebCore::Text*>(node); + WebCore::StringImpl* string = textNode->string(); + if (string->length() == 0) + continue; + const UChar* chars = string->characters(); + int newLen = node == endNode ? end : string->length(); + int oldLen = result.length(); + if (oldLen == 0) { + if (newLen < start) { + DBG_NAV_LOGD("newLen=%d < start=%d", newLen, start); + return result; + } + result.append(chars + start, newLen - start); + continue; + } + if (isSpace(result.characters()[oldLen - 1]) == false && + isSpace(chars[0]) == false) { + result.append(" "); + } + result.append(chars, newLen); + } while (node != endNode && (node = node->traverseNextNode()) != NULL); + } +#if DUMP_NAV_CACHE + { + char buffer[256]; + WebCore::CacheBuilder::Debug debug; + debug.init(buffer, sizeof(buffer)); + debug.print("copy: "); + debug.wideString(result); + DUMP_NAV_LOGD("%s", buffer); + } +#endif + return result; +} + +WebCore::Frame* WebViewCore::setSelection(WebCore::Frame* frame, WebCore::Node* node, + int x, int y, int start, int end) +{ + // FIXME: Consider using a generation number to avoid doing this many more times than necessary. + frame = changedKitFocus(frame, node, x, y); + ((WebCore::FrameAndroid*) frame)->select(start, end); + return frame; +} + +WebCore::Frame* WebViewCore::deleteSelection(WebCore::Frame* frame, WebCore::Node* node, + int x, int y, int start, int end) +{ + frame = setSelection(frame, node, x, y, start, end); + if (start != end) { + WebCore::PlatformKeyboardEvent downEvent(kKeyCodeDel, WebCore::VK_BACK, true, false, false, false, false); + frame->eventHandler()->keyEvent(downEvent); + WebCore::PlatformKeyboardEvent upEvent(kKeyCodeDel, WebCore::VK_BACK, false, false, false, false, false); + frame->eventHandler()->keyEvent(upEvent); + } + return frame; +} + +void WebViewCore::replaceTextfieldText(WebCore::Frame* frame, WebCore::Node* node, int x, int y, + int oldStart, int oldEnd, jstring replace, int start, int end) +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + const jchar* outputChars = NULL == replace ? NULL : env->GetStringChars(replace, NULL); + + WebCore::String webcoreString; + if (outputChars == NULL) { + webcoreString = WebCore::String(); + } + else { + webcoreString = WebCore::String((const UChar*) outputChars, env->GetStringLength(replace)); + } + frame = setSelection(frame, node, x, y, oldStart, oldEnd); + WebCore::TypingCommand::insertText(frame->document(), webcoreString, false); + ((WebCore::FrameAndroid*) frame)->select(start, end); + if (replace && outputChars) + env->ReleaseStringChars(replace, outputChars); + checkException(env); +} + +void WebViewCore::passToJs(WebCore::Frame* frame, WebCore::Node* node, int x, int y, int generation, + jstring currentText, int keyCode, int keyValue, bool down, bool cap, bool fn, bool sym) +{ + frame = changedKitFocus(frame, node, x, y); + WebCore::PlatformKeyboardEvent event(keyCode, keyValue, down, false, cap, fn, sym); + // Block text field updates during a key press. + mBlockTextfieldUpdates = true; + frame->eventHandler()->keyEvent(event); + mBlockTextfieldUpdates = false; + mTextGeneration = generation; + + WebCore::Node* currentFocus = mFrame->getCacheBuilder().currentFocus(); + // Make sure we have the same focus and it is a text field. + if (node == currentFocus && currentFocus) { + WebCore::RenderObject* renderer = currentFocus->renderer(); + if (renderer != NULL && (renderer->isTextField() || renderer->isTextArea())) { + WebCore::RenderTextControl* renderText = static_cast<WebCore::RenderTextControl*>(renderer); + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + const jchar* str = env->GetStringChars(currentText, NULL); + WebCore::String current = WebCore::String(str, env->GetStringLength(currentText)); + env->ReleaseStringChars(currentText, str); + WebCore::String test = renderText->text(); + // If the text changed during the key event, update the UI text field. + if (test != current) + updateTextfield(currentFocus, false, test); + } + } +} + +void WebViewCore::saveDocumentState(WebCore::Frame* frame, WebCore::Node* node, int x, int y) +{ + frame = changedKitFocus(frame, node, x, y); + WebCore::HistoryItem *item = frame->loader()->currentHistoryItem(); + + // item can be null when there is no offical URL for the current page. This happens + // when the content is loaded using with WebCoreFrameBridge::LoadData() and there + // is no failing URL (common case is when content is loaded using data: scheme) + if (item != 0) { + item->setDocumentState(frame->document()->formElementsState()); + } +} + +// Convert a WebCore::String into an array of characters where the first +// character represents the length, for easy conversion to java. +static uint16_t* stringConverter(const WebCore::String& text) +{ + size_t length = text.length(); + uint16_t* itemName = new uint16_t[length+1]; + itemName[0] = (uint16_t)length; + uint16_t* firstChar = &(itemName[1]); + memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length); + return itemName; +} + +// Response to dropdown created for a listbox. +class ListBoxReply : public WebCoreReply { +public: + ListBoxReply(WebCore::HTMLSelectElement* select, WebCore::Frame* frame, WebViewCore* view) + : m_select(select) + , m_frame(frame) + , m_viewImpl(view) + {} + + // Response used if the listbox only allows single selection. + // index is listIndex of the selected item, or -1 if nothing is selected. + virtual void replyInt(int index) { + // If the select element no longer exists, do to a page change, etc, silently return. + if (!m_select || m_viewImpl->mFrame->getCacheBuilder().validNode(m_frame, m_select) == false) + return; + if (-1 == index) { + if (m_select->selectedIndex() != -1) { + m_select->deselectItems(); + m_select->onChange(); + m_viewImpl->contentInvalidate(); + } + return; + } + WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>( + m_select->item(m_select->listToOptionIndex(index))); + if (!option->selected()) { + option->setSelected(true); + m_select->onChange(); + m_viewImpl->contentInvalidate(); + } + } + + // Response if the listbox allows multiple selection. array stores the listIndices + // of selected positions. + virtual void replyIntArray(SkTDArray<int> array) { + // If the select element no longer exists, do to a page change, etc, silently return. + if (!m_select || m_viewImpl->mFrame->getCacheBuilder().validNode(m_frame, m_select) == false) + return; + m_select->deselectItems(); + int count = array.count(); + WebCore::HTMLOptionElement* option; + for (int i = 0; i < count; i++) { + option = static_cast<WebCore::HTMLOptionElement*>( + m_select->item(array[m_select->listToOptionIndex(i)])); + option->setSelected(true); + } + m_viewImpl->contentInvalidate(); + } +private: + // The select element associated with this listbox. + WebCore::HTMLSelectElement* m_select; + // The frame of this select element, to verify that it is valid. + WebCore::Frame* m_frame; + // For calling invalidate. + WebViewCore* m_viewImpl; +}; + +// Create an array of java Strings. +static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count) +{ + jclass stringClass = env->FindClass("java/lang/String"); + LOG_ASSERT(stringClass, "Could not find java/lang/String"); + jobjectArray array = env->NewObjectArray(count, stringClass, NULL); + LOG_ASSERT(array, "Could not create new string array"); + + for (size_t i = 0; i < count; i++) { + jobject newString = env->NewString(&labels[i][1], labels[i][0]); + env->SetObjectArrayElement(array, i, newString); + env->DeleteLocalRef(newString); + checkException(env); + } + env->DeleteLocalRef(stringClass); + return array; +} + +void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount, + bool multiple, const int selected[], size_t selectedCountOrSelection) { + // Reuse mPopupReply + Release(mPopupReply); + mPopupReply = NULL; + + LOG_ASSERT(mJavaGlue->mObj, "No java widget associated with this view!"); + + // Create an array of java Strings for the drop down. + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + jobjectArray labelArray = makeLabelArray(env, labels, count); + + // Create an array determining whether each item is enabled. + jbooleanArray enabledArray = env->NewBooleanArray(enabledCount); + checkException(env); + jboolean* ptrArray = env->GetBooleanArrayElements(enabledArray, NULL); + checkException(env); + for (size_t i = 0; i < enabledCount; i++) { + ptrArray[i] = enabled[i]; + } + env->ReleaseBooleanArrayElements(enabledArray, NULL, enabledCount); + checkException(env); + + if (multiple) { + // Pass up an array representing which items are selected. + jintArray selectedArray = env->NewIntArray(selectedCountOrSelection); + checkException(env); + jint* selArray = env->GetIntArrayElements(selectedArray, NULL); + checkException(env); + for (size_t i = 0; i < selectedCountOrSelection; i++) { + selArray[i] = selected[i]; + } + env->ReleaseIntArrayElements(selectedArray, NULL, selectedCountOrSelection); + + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mRequestListBox, labelArray, enabledArray, selectedArray); + env->DeleteLocalRef(selectedArray); + } else { + // Pass up the single selection. + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mRequestSingleListBox, labelArray, enabledArray, selectedCountOrSelection); + } + + env->DeleteLocalRef(labelArray); + env->DeleteLocalRef(enabledArray); + checkException(env); + + Retain(reply); + mPopupReply = reply; +} + +bool WebViewCore::keyUp(KeyCode keyCode, int keyVal) +{ + DBG_NAV_LOGD("keyCode=%d", keyCode); + bool keyHandled = false; + WebCore::Node* focusNode = mFrame->getCacheBuilder().currentFocus(); + if (focusNode != NULL) { + WebCore::FrameAndroid* focusFrame = Android(focusNode->document()->frame()); + + WebCoreFrameBridge* bridge = mFrame->bridge(); + switch (keyCode) { + case kKeyCodeNewline: + case kKeyCodeDpadCenter: { + focusFrame->loader()->resetMultipleFormSubmissionProtection(); + bridge->setInKeyHandler(true); + if ((focusNode->hasTagName(WebCore::HTMLNames::inputTag) && + ((WebCore::HTMLInputElement*)focusNode)->isTextField()) || + focusNode->hasTagName(WebCore::HTMLNames::textareaTag)) { + // Create the key down event. + WebCore::PlatformKeyboardEvent keydown(keyCode, keyVal, true, false, false, false, false); + // Create the key up event. + WebCore::PlatformKeyboardEvent keyup(keyCode, keyVal, false, false, false, false, false); + // Send both events. + keyHandled = focusFrame->eventHandler()->keyEvent(keydown); + keyHandled |= focusFrame->eventHandler()->keyEvent(keyup); + } else { + keyHandled = handleMouseClick(focusFrame, focusNode); + } + bridge->setInKeyHandler(false); + break; + } + default: + keyHandled = false; + } + } + mBlockFocusChange = false; + return keyHandled; +} + +void WebViewCore::touchUp(int touchGeneration, int buildGeneration, + WebCore::Frame* frame, WebCore::Node* node, int x, int y, int size, + bool isClick, bool retry) +{ + if (mTouchGeneration > touchGeneration) { + DBG_NAV_LOGD("mTouchGeneration=%d > touchGeneration=%d" + " x=%d y=%d", mTouchGeneration, touchGeneration, x, y); + return; // short circuit if a newer touch has been generated + } + if (retry) + finalKitFocus(frame, node, x, y); + else if (commonKitFocus(touchGeneration, buildGeneration, + frame, node, x, y, false) == false) { + return; + } + mLastGeneration = touchGeneration; + // If this is just a touch and not a click, we have already done the change in focus, + // so just leave the function now. + if (!isClick) + return; + WebCore::EditorClientAndroid* client = static_cast<WebCore::EditorClientAndroid*>(mFrame->editor()->client()); + client->setFromClick(true); + DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p" + " x=%d y=%d", touchGeneration, frame, node, x, y); + handleMouseClick(frame, node); + client->setFromClick(false); +} + +bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr) { + if (framePtr != NULL && mFrame->getCacheBuilder().validNode(framePtr, nodePtr) == false) + return false; + WebCoreFrameBridge* bridge = mFrame->bridge(); + // Need to special case area tags because an image map could have an area element in the middle + // so when attempting to get the default, the point chosen would be follow the wrong link. + if (nodePtr != NULL && nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) { + bridge->setInKeyHandler(true); + WebCore::EventTargetNodeCast(nodePtr)->dispatchSimulatedClick(0, true, true); + bridge->setInKeyHandler(false); + return true; + } + WebCore::RenderObject* renderer = nodePtr != NULL ? nodePtr->renderer() : NULL; + if (renderer) { + if (renderer->isMenuList()) { + WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr); + const WTF::Vector<WebCore::HTMLElement*>& listItems = select->listItems(); + SkTDArray<const uint16_t*> names; + SkTDArray<int> enabledArray; + SkTDArray<int> selectedArray; + int size = listItems.size(); + bool multiple = select->multiple(); + for (int i = 0; i < size; i++) { + if (listItems[i]->hasTagName(WebCore::HTMLNames::optionTag)) { + WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(listItems[i]); + *names.append() = stringConverter(option->optionText()); + *enabledArray.append() = option->disabled() ? 0 : 1; + if (multiple && option->selected()) + *selectedArray.append() = i; + } else if (listItems[i]->hasTagName(WebCore::HTMLNames::optgroupTag)) { + WebCore::HTMLOptGroupElement* optGroup = static_cast<WebCore::HTMLOptGroupElement*>(listItems[i]); + *names.append() = stringConverter(optGroup->groupLabelText()); + *enabledArray.append() = 0; + if (multiple) + *selectedArray.append() = 0; + } + } + WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this); + listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(), + multiple, selectedArray.begin(), multiple ? selectedArray.count() : + select->optionToListIndex(select->selectedIndex())); + return true; + } + } + if (NULL == framePtr) + framePtr = mFrame; + bridge->setInKeyHandler(true); + DBG_NAV_LOGD("mMousePos={%d,%d}", mMousePos.x(), mMousePos.y()); + WebCore::PlatformMouseEvent mouseDown(mMousePos, mMousePos, WebCore::LeftButton, + WebCore::MouseEventPressed, 1, false, false, false, false, + WebCore::currentTime()); + // ignore the return from as it will return true if the hit point can trigger selection change + framePtr->eventHandler()->handleMousePressEvent(mouseDown); + WebCore::PlatformMouseEvent mouseUp(mMousePos, mMousePos, WebCore::LeftButton, + WebCore::MouseEventReleased, 1, false, false, false, false, + WebCore::currentTime()); + bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp); + bridge->setInKeyHandler(false); + return handled; +} + +void WebViewCore::popupReply(int index) +{ + if (mPopupReply) { + mPopupReply->replyInt(index); + Release(mPopupReply); + mPopupReply = NULL; + } +} + +void WebViewCore::popupReply(SkTDArray<int> array) { + if (mPopupReply) { + mPopupReply->replyIntArray(array); + Release(mPopupReply); + mPopupReply = NULL; + } +} + +void WebViewCore::jsAlert(const WebCore::String& url, const WebCore::String& text) +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length()); + jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length()); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mJsAlert, jUrlStr, jInputStr); + env->DeleteLocalRef(jInputStr); + env->DeleteLocalRef(jUrlStr); + checkException(env); +} + +bool WebViewCore::jsConfirm(const WebCore::String& url, const WebCore::String& text) +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length()); + jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length()); + jboolean result = env->CallBooleanMethod(mJavaGlue->mObj, mJavaGlue->mJsConfirm, jUrlStr, jInputStr); + env->DeleteLocalRef(jInputStr); + env->DeleteLocalRef(jUrlStr); + checkException(env); + return result; +} + +bool WebViewCore::jsPrompt(const WebCore::String& url, const WebCore::String& text, const WebCore::String& defaultValue, WebCore::String& result) +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length()); + jstring jDefaultStr = env->NewString((unsigned short *)defaultValue.characters(), defaultValue.length()); + jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length()); + jstring returnVal = (jstring) env->CallObjectMethod(mJavaGlue->mObj, mJavaGlue->mJsPrompt, jUrlStr, jInputStr, jDefaultStr); + // If returnVal is null, it means that the user cancelled the dialog. + if (!returnVal) + return false; + + const jchar* outputChars = env->GetStringChars(returnVal, NULL); + if (outputChars == NULL) { + result = WebCore::String(); + } else { + result = WebCore::String((const UChar*) outputChars, env->GetStringLength(returnVal)); + } + env->ReleaseStringChars(returnVal, outputChars); + env->DeleteLocalRef(jInputStr); + env->DeleteLocalRef(jDefaultStr); + env->DeleteLocalRef(jUrlStr); + checkException(env); + return true; +} + +bool WebViewCore::jsUnload(const WebCore::String& url, const WebCore::String& message) +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + jstring jInputStr = env->NewString((unsigned short *)message.characters(), message.length()); + jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length()); + jboolean result = env->CallBooleanMethod(mJavaGlue->mObj, mJavaGlue->mJsUnload, jUrlStr, jInputStr); + env->DeleteLocalRef(jInputStr); + env->DeleteLocalRef(jUrlStr); + checkException(env); + return result; +} + +WebCoreViewBridge* +WebViewCore::createBridgeForView(WebCore::FrameView* view) +{ + SkASSERT(view); + WebViewCore* viewBridge = new WebViewCore(javavm_to_jnienv(mJavaGlue->mJVM), mJavaGlue->mObj, view); + WebCore::IntRect r = getBounds(); + viewBridge->setBounds(r.x(), r.y(), r.right(), r.bottom()); + return viewBridge; +} + +jobject +WebViewCore::getJavaObject() +{ + return mJavaGlue->mObj; +} + +jobject +WebViewCore::getWebViewJavaObject() +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + return env->GetObjectField(mJavaGlue->mObj, gWebViewCoreFields.mWebView); +} + +void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword, + const WebCore::String& text) +{ + if (mBlockTextfieldUpdates) + return; + JNIEnv* env = javavm_to_jnienv(mJavaGlue->mJVM); + if (changeToPassword) { + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mUpdateTextfield, + (int) ptr, true, NULL, mTextGeneration); + checkException(env); + return; + } + int length = text.length(); + jstring string = env->NewString((unsigned short *) text.characters(), length); + env->CallVoidMethod(mJavaGlue->mObj, mJavaGlue->mUpdateTextfield, + (int) ptr, false, string, mTextGeneration); + env->DeleteLocalRef(string); + checkException(env); +} + +void WebViewCore::setSnapAnchor(int x, int y) +{ + mSnapAnchorNode = NULL; + if (x == 0 && y == 0) { + return; + } + + WebCore::IntPoint point = WebCore::IntPoint(x, y); + WebCore::Node* node = mFrame->eventHandler()->hitTestResultAtPoint(point, false).innerNode(); + if (node) { +// LOGD("found focus node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType()); + while (node) { + if (node->hasTagName(WebCore::HTMLNames::divTag) || + node->hasTagName(WebCore::HTMLNames::tableTag)) { + mSnapAnchorNode = node; + return; + } +// LOGD("parent node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType()); + node = node->parentNode(); + } + } +} + +void WebViewCore::snapToAnchor() +{ + if (mSnapAnchorNode) { + if (mSnapAnchorNode->inDocument()) { + int rx, ry; + mSnapAnchorNode->renderer()->absolutePosition(rx, ry); + scrollTo(rx, ry); + } else { + mSnapAnchorNode = NULL; + } + } +} + +void WebViewCore::setBackgroundColor(SkColor c) +{ + WebCore::FrameView* view = mFrame->view(); + if (view == NULL) + return; + WebCore::Color bcolor(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), + SkColorGetA(c)); + view->setBaseBackgroundColor(bcolor); +} + +//---------------------------------------------------------------------- +// Native JNI methods +//---------------------------------------------------------------------- +static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string) +{ + int length = string.length(); + if (0 == length) + return 0; + jstring ret = env->NewString((jchar *)string.characters(), length); + env->DeleteLocalRef(ret); + return ret; +} + +void WebViewCore::SetSize(JNIEnv *env, jobject obj, jint width, jint height, + jint screenWidth, jfloat scale) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize"); + // convert the scale to an int + int s = (int) (scale * 100); + // a negative value indicates that we should not change the scale + if (scale < 0) + s = viewImpl->scale(); + + viewImpl->setSizeScreenWidthAndScale(width, height, screenWidth, s); +} + +void WebViewCore::SetVisibleRect(JNIEnv *env, jobject obj, + jint x, jint y, jint width, jint height) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "need viewImpl"); + + viewImpl->setVisibleRect(WebCore::IntRect(x, y, width, height)); +} + +jboolean WebViewCore::KeyUp(JNIEnv *env, jobject obj, jint key, jint val) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif +// LOGV("webviewcore::nativeKeyUp(%u)\n", (unsigned)key); + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeKeyUp"); + return viewImpl->keyUp((KeyCode)key, val); +} + +void WebViewCore::DeleteSelection(JNIEnv *env, jobject obj, + jint frame, jint node, jint x, jint y, jint start, jint end) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + LOGV("webviewcore::nativeDeleteSelection()\n"); + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection"); + viewImpl->deleteSelection((WebCore::Frame*) frame, (WebCore::Node*) node, + x, y, start, end); +} + +void WebViewCore::SetSelection(JNIEnv *env, jobject obj, + jint frame, jint node, jint x, jint y, jint start, jint end) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + LOGV("webviewcore::nativeSetSelection()\n"); + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection"); + viewImpl->setSelection((WebCore::Frame*) frame, (WebCore::Node*) node, + x, y, start, end); +} + + +void WebViewCore::ReplaceTextfieldText(JNIEnv *env, jobject obj, + jint framePtr, jint nodePtr, jint x, jint y, jint oldStart, jint oldEnd, + jstring replace, jint start, jint end) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + LOGV("webviewcore::nativeReplaceTextfieldText()\n"); + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeReplaceTextfieldText"); + viewImpl->replaceTextfieldText((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr, x, y, oldStart, + oldEnd, replace, start, end); +} + +void WebViewCore::PassToJs(JNIEnv *env, jobject obj, jint frame, jint node, + jint x, jint y, jint generation, jstring currentText, jint keyCode, + jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + LOGV("webviewcore::nativePassToJs()\n"); + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativePassToJs"); + viewImpl->passToJs((WebCore::Frame*) frame, (WebCore::Node*) node, + x, y, generation, currentText, keyCode, keyValue, down, cap, fn, sym); +} + +void WebViewCore::SaveDocumentState(JNIEnv *env, jobject obj, jint frame, jint node, jint x, jint y) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + LOGV("webviewcore::nativeSaveDocumentState()\n"); + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState"); + viewImpl->saveDocumentState((WebCore::Frame*) frame, (WebCore::Node*) node, x, y); +} + +void WebViewCore::Draw(JNIEnv *env, jobject obj, jobject contentPict) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeDraw"); + LOG_ASSERT(contentPict, "need a valid java picture for nativeDraw"); + SkPicture* picture = GraphicsJNI::getNativePicture(env, contentPict); + LOG_ASSERT(picture, "need native picture in nativeDraw"); + viewImpl->recordPicture(picture, true); +} + +void WebViewCore::SendListBoxChoice(JNIEnv* env, jobject obj, jint choice) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice"); + viewImpl->popupReply(choice); +} + +void WebViewCore::SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray, jint size) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices"); + jboolean* ptrArray = env->GetBooleanArrayElements(jArray, NULL); + SkTDArray<int> array; + for (int i = 0; i < size; i++) { + if (ptrArray[i]) { + *array.append() = i; + } + } + viewImpl->popupReply(array); +} + +void WebViewCore::ClearMatches(JNIEnv *env, jobject obj) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeClearMatches"); + + WebCore::Frame* frame = viewImpl->frame(); + while (frame) { + if (frame->document()) + frame->document()->removeMarkers(); + WebCore::SelectionController* controller = frame->selectionController(); + if (controller->isRange()) { + WebCore::Position pos = controller->start(); + WebCore::Position end = controller->end(); + do { + if (pos.inRenderedText()) { + WebCore::RenderObject* renderer = pos.node()->renderer(); + if (renderer != NULL) { + renderer->setSelectionState(WebCore::RenderObject::SelectionNone); + } + } + pos = pos.next(); + } while (pos != end); + controller->clear(); + } + frame = frame->tree()->traverseNext(); + } +} + +jboolean WebViewCore::Find(JNIEnv *env, jobject obj, jstring find, + jboolean forward, jboolean fromSelection) { + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeFind"); + + if (NULL == find) + return false; + int length = env->GetStringLength(find); + if (0 == length) + return false; // perhaps we want to return true, in order to show the correct dialog + const jchar* findChars = env->GetStringChars(find, NULL); + // Will this ever return NULL if find is not NULL and its length is not zero? + if (NULL == findChars) + return false; + WebCore::String findString = WebCore::String((const UChar*) findChars, length); + env->ReleaseStringChars(find, findChars); + WebCore::Frame* mainFrame = viewImpl->frame(); + WebCore::Frame* frame = mainFrame; + bool found = false; + if (fromSelection) { + // Need to figure out which frame has a selection, then start from there + while (frame && frame->selectionController()->isNone()) { + frame = frame->tree()->traverseNext(); + } + if (!frame) { + // No selection, so start from the beginning. + frame = mainFrame; + } + // Now we have the frame with the selection. + WebCore::Frame* starterFrame = frame; + bool firstTimeThroughLoop = true; + if (forward) { + while (!(found = frame->findString(findString, true, false, false, true))) { + if (firstTimeThroughLoop) { + firstTimeThroughLoop = false; + // FIXME: Clearing the selection is necessary for the search to + // work properly. However, the invalidation does not remove + // the drawn rectangle from old frames. + frame->selectionController()->clear(); + } + frame = frame->tree()->traverseNextWithWrap(true); + if (frame == starterFrame) + break; + } + } else { + // Search backwards, searching the frames in reverse order as well. + while (!(found = frame->findString(findString, false, false, false, true))) { + if (firstTimeThroughLoop) { + firstTimeThroughLoop = false; + frame->selectionController()->clear(); + } + frame = frame->tree()->traversePreviousWithWrap(true); + if (frame == starterFrame) + break; + } + } + // In order to wrap to the beginning of the initial frame. + if (!found) { + found = frame->findString(findString, forward, false, false, true); + } + } else { + // Clear any old selections so find will work properly. + while (frame) { + frame->selectionController()->clear(); + frame->invalidateSelection(); + frame = frame->tree()->traverseNext(); + } + frame = mainFrame; + do { + found = frame->findString(findString, forward, false, false, false); + } while (!found && (frame = frame->tree()->traverseNext()) != NULL); + } + if (found) { + frame->view()->scrollRectIntoViewRecursively(frame->selectionController()->caretRect()); + return true; + } + return false; +} + +jstring WebViewCore::FindAddress(JNIEnv *env, jobject obj, jstring addr) +{ + if (NULL == addr) + return NULL; + int length = env->GetStringLength(addr); + if (0 == length) + return NULL; + const jchar* addrChars = env->GetStringChars(addr, NULL); + int start, end; + bool success = WebCore::CacheBuilder::FindAddress(addrChars, length, + &start, &end) == WebCore::CacheBuilder::FOUND_COMPLETE; + jstring ret = NULL; + if (success) { + ret = env->NewString((jchar*) addrChars + start, end - start); + env->DeleteLocalRef(ret); + } + env->ReleaseStringChars(addr, addrChars); + return ret; +} + +jint WebViewCore::FindAll(JNIEnv *env, jobject obj, jstring find) { + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in nativeFindAll"); + if (NULL == find) + return 0; + int length = env->GetStringLength(find); + if (0 == length) + return 0; + const jchar* findChars = env->GetStringChars(find, NULL); + // Will this ever return NULL if find is not NULL and its length is not zero? + if (NULL == findChars) + return 0; + WebCore::String findString = WebCore::String((const UChar*) findChars, length); + env->ReleaseStringChars(find, findChars); + WebCore::Frame* frame = viewImpl->frame(); + int numFound = 0; + ClearMatches(env, obj); + while (frame) { + frame->selectionController()->clear(); + frame->invalidateSelection(); + if (frame->document()) + frame->document()->removeMarkers(); + frame->setMarkedTextMatchesAreHighlighted(true); + numFound += frame->markAllMatchesForText(findString, false, 0); + frame = frame->tree()->traverseNext(); + } + if (numFound > 0) + Find(env, obj, find, true, false); + return numFound; +} + +void WebViewCore::TouchUp(JNIEnv *env, jobject obj, jint touchGeneration, + jint buildGeneration, jint frame, jint node, jint x, jint y, jint size, + jboolean isClick, jboolean retry) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + viewImpl->touchUp(touchGeneration, buildGeneration, + (WebCore::Frame*) frame, (WebCore::Node*) node, x, y, size, isClick, retry); +} + +jstring WebViewCore::RetrieveHref(JNIEnv *env, jobject obj, jint frame, + jint node) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + WebCore::String result = viewImpl->retrieveHref((WebCore::Frame*) frame, + (WebCore::Node*) node); + if (!result.isEmpty()) + return WebCoreStringToJString(env, result); + return NULL; +} + +jstring WebViewCore::RetrieveImageRef(JNIEnv *env, jobject obj, jint x, jint y) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + WebCore::String result = viewImpl->retrieveImageRef(x, y); + if (!result.isEmpty()) + return WebCoreStringToJString(env, result); + return NULL; +} + +void WebViewCore::SetFinalFocus(JNIEnv *env, jobject obj, jint frame, jint node, + jint x, jint y, jboolean block) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + viewImpl->setFinalFocus((WebCore::Frame*) frame, (WebCore::Node*) node, x, + y, block); +} + +void WebViewCore::SetKitFocus(JNIEnv *env, jobject obj, jint moveGeneration, + jint buildGeneration, jint frame, jint node, jint x, jint y, + jboolean ignoreNullFocus) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterWV counter; +#endif + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + viewImpl->setKitFocus(moveGeneration, buildGeneration, + (WebCore::Frame*) frame, (WebCore::Node*) node, x, y, + ignoreNullFocus); +} + +void WebViewCore::UnblockFocus(JNIEnv *env, jobject obj) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + viewImpl->unblockFocus(); +} + +void WebViewCore::UpdateFrameCache(JNIEnv *env, jobject obj) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + viewImpl->updateFrameCache(); +} + +jint WebViewCore::GetContentMinPrefWidth(JNIEnv *env, jobject obj) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + + WebCore::Frame* frame = viewImpl->frame(); + if (frame) { + WebCore::Document* document = frame->document(); + if (document) { + WebCore::RenderObject* renderer = document->renderer(); + if (renderer && renderer->isRenderView()) { + return renderer->minPrefWidth(); + } + } + } + return 0; +} + +void WebViewCore::SetViewportSettingsFromNative(JNIEnv *env, jobject obj) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + + WebCore::Settings* s = viewImpl->frame()->page()->settings(); + if (s == NULL) + return; + + env->SetIntField(obj, gWebViewCoreFields.mViewportWidth, s->viewportWidth()); + env->SetIntField(obj, gWebViewCoreFields.mViewportHeight, s->viewportHeight()); + env->SetIntField(obj, gWebViewCoreFields.mViewportInitialScale, s->viewportInitialScale()); + env->SetIntField(obj, gWebViewCoreFields.mViewportMinimumScale, s->viewportMinimumScale()); + env->SetIntField(obj, gWebViewCoreFields.mViewportMaximumScale, s->viewportMaximumScale()); + env->SetBooleanField(obj, gWebViewCoreFields.mViewportUserScalable, s->viewportUserScalable()); +} + +void WebViewCore::SetSnapAnchor(JNIEnv *env, jobject obj, jint x, jint y) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + + viewImpl->setSnapAnchor(x, y); +} + +void WebViewCore::SnapToAnchor(JNIEnv *env, jobject obj) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + + viewImpl->snapToAnchor(); +} + +void WebViewCore::SetBackgroundColor(JNIEnv *env, jobject obj, jint color) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + + viewImpl->setBackgroundColor((SkColor) color); +} + +static void jrect_to_skirect(JNIEnv* env, jobject obj, SkIRect* result) +{ + int L, T, R, B; + GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B); + result->set(L, T, R, B); +} + +jstring WebViewCore::GetSelection(JNIEnv *env, jobject obj, jobject selRgn) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + SkRegion* selectionRegion = GraphicsJNI::getNativeRegion(env, selRgn); + WebCore::String result = viewImpl->getSelection(selectionRegion); + if (!result.isEmpty()) + return WebCoreStringToJString(env, result); + return NULL; +} + +void WebViewCore::Dump(JNIEnv *env, jobject obj) +{ + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__); + + viewImpl->dump(); +} + +void WebViewCore::RefreshPlugins(JNIEnv *env, + jobject obj, + jboolean reloadOpenPages) +{ + // Refresh the list of plugins, optionally reloading all open + // pages. + WebCore::refreshPlugins(reloadOpenPages); +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gJavaWebViewCoreMethods[] = { + { "nativeClearMatches", "()V", + (void*) WebViewCore::ClearMatches } , + { "nativeDraw", "(Landroid/graphics/Picture;)V", + (void*) WebViewCore::Draw }, + { "nativeKeyUp", "(II)Z", + (void*) WebViewCore::KeyUp }, + { "nativeSendListBoxChoices", "([ZI)V", + (void*) WebViewCore::SendListBoxChoices }, + { "nativeSendListBoxChoice", "(I)V", + (void*) WebViewCore::SendListBoxChoice }, + { "nativeSetSize", "(IIIF)V", + (void*) WebViewCore::SetSize }, + { "nativeSetVisibleRect", "(IIII)V", + (void*) WebViewCore::SetVisibleRect }, + { "nativeSetSelection", "(IIIIII)V", + (void*) WebViewCore::SetSelection } , + { "nativeDeleteSelection", "(IIIIII)V", + (void*) WebViewCore::DeleteSelection } , + { "nativeReplaceTextfieldText", "(IIIIIILjava/lang/String;II)V", + (void*) WebViewCore::ReplaceTextfieldText } , + { "passToJs", "(IIIIILjava/lang/String;IIZZZZ)V", + (void*) WebViewCore::PassToJs } , + { "nativeSaveDocumentState", "(IIII)V", + (void*) WebViewCore::SaveDocumentState }, + { "nativeFind", "(Ljava/lang/String;ZZ)Z", + (void*) WebViewCore::Find } , + { "nativeFindAddress", "(Ljava/lang/String;)Ljava/lang/String;", + (void*) WebViewCore::FindAddress }, + { "nativeFindAll", "(Ljava/lang/String;)I", + (void*) WebViewCore::FindAll } , + { "nativeTouchUp", "(IIIIIIIZZ)V", + (void*) WebViewCore::TouchUp }, + { "nativeRetrieveImageRef", "(II)Ljava/lang/String;", + (void*) WebViewCore::RetrieveImageRef }, + { "nativeRetrieveHref", "(II)Ljava/lang/String;", + (void*) WebViewCore::RetrieveHref }, + { "nativeSetFinalFocus", "(IIIIZ)V", + (void*) WebViewCore::SetFinalFocus }, + { "nativeSetKitFocus", "(IIIIIIZ)V", + (void*) WebViewCore::SetKitFocus }, + { "nativeUnblockFocus", "()V", + (void*) WebViewCore::UnblockFocus }, + { "nativeUpdateFrameCache", "()V", + (void*) WebViewCore::UpdateFrameCache }, + { "nativeGetContentMinPrefWidth", "()I", + (void*) WebViewCore::GetContentMinPrefWidth }, + { "setViewportSettingsFromNative", "()V", + (void*) WebViewCore::SetViewportSettingsFromNative }, + { "nativeSetSnapAnchor", "(II)V", + (void*) WebViewCore::SetSnapAnchor }, + { "nativeSnapToAnchor", "()V", + (void*) WebViewCore::SnapToAnchor }, + { "nativeSetBackgroundColor", "(I)V", + (void*) WebViewCore::SetBackgroundColor }, + { "nativeGetSelection", + "(Landroid/graphics/Region;)Ljava/lang/String;", + (void*) WebViewCore::GetSelection }, + { "nativeRefreshPlugins", "(Z)V", + (void*) WebViewCore::RefreshPlugins }, + { "nativeDump", "()V", + (void*) WebViewCore::Dump } +}; + +int register_webviewcore(JNIEnv* env) +{ + jclass widget = env->FindClass("android/webkit/WebViewCore"); + LOG_ASSERT(widget != NULL, + "Unable to find class android/webkit/WebViewCore"); + gWebViewCoreFields.mNativeClass = env->GetFieldID(widget, "mNativeClass", + "I"); + LOG_ASSERT(gWebViewCoreFields.mNativeClass != NULL, + "Unable to find android/webkit/WebViewCore.mNativeClass"); + gWebViewCoreFields.mViewportWidth = env->GetFieldID(widget, + "mViewportWidth", "I"); + LOG_ASSERT(gWebViewCoreFields.mViewportWidth != NULL, + "Unable to find android/webkit/WebViewCore.mViewportWidth"); + gWebViewCoreFields.mViewportHeight = env->GetFieldID(widget, + "mViewportHeight", "I"); + LOG_ASSERT(gWebViewCoreFields.mViewportHeight != NULL, + "Unable to find android/webkit/WebViewCore.mViewportHeight"); + gWebViewCoreFields.mViewportInitialScale = env->GetFieldID(widget, + "mViewportInitialScale", "I"); + LOG_ASSERT(gWebViewCoreFields.mViewportInitialScale != NULL, + "Unable to find android/webkit/WebViewCore.mViewportInitialScale"); + gWebViewCoreFields.mViewportMinimumScale = env->GetFieldID(widget, + "mViewportMinimumScale", "I"); + LOG_ASSERT(gWebViewCoreFields.mViewportMinimumScale != NULL, + "Unable to find android/webkit/WebViewCore.mViewportMinimumScale"); + gWebViewCoreFields.mViewportMaximumScale = env->GetFieldID(widget, + "mViewportMaximumScale", "I"); + LOG_ASSERT(gWebViewCoreFields.mViewportMaximumScale != NULL, + "Unable to find android/webkit/WebViewCore.mViewportMaximumScale"); + gWebViewCoreFields.mViewportUserScalable = env->GetFieldID(widget, + "mViewportUserScalable", "Z"); + LOG_ASSERT(gWebViewCoreFields.mViewportUserScalable != NULL, + "Unable to find android/webkit/WebViewCore.mViewportUserScalable"); + gWebViewCoreFields.mWebView = env->GetFieldID(widget, + "mWebView", "Landroid/webkit/WebView;"); + LOG_ASSERT(gWebViewCoreFields.mWebView != NULL, + "Unable to find android/webkit/WebViewCore.mWebView"); + + return jniRegisterNativeMethods(env, "android/webkit/WebViewCore", + gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods)); +} + +} /* namespace android */ diff --git a/WebCore/platform/android/jni/WebViewCore.h b/WebCore/platform/android/jni/WebViewCore.h new file mode 100644 index 0000000..1cb51b2 --- /dev/null +++ b/WebCore/platform/android/jni/WebViewCore.h @@ -0,0 +1,400 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_WIDGET_HTMLWIDGET_H +#define ANDROID_WIDGET_HTMLWIDGET_H + +#include "WebCoreViewBridge.h" +#include "CacheBuilder.h" +#include "CachedHistory.h" +#include "FrameView.h" +#include "SkColor.h" +#include "SkScalar.h" +#include "SkRegion.h" +#include <ui/Rect.h> +#include <jni.h> + +namespace WebCore { + class AtomicString; + class Color; + class GraphicsContext; + class HTMLSelectElement; + class RenderPart; + class RenderText; + class FrameAndroid; + class Node; + class RenderTextControl; +} + +class SkPicture; + +namespace android { + + class CachedRoot; + + class ListBoxReply; + + class WebViewCore : public WebCoreViewBridge + { + public: + /** + * Initialize the ViewBridge with a JNI environment, a native HTMLWidget object + * and an associated frame to perform actions on. + */ + WebViewCore(JNIEnv* env, jobject javaView, WebCore::FrameView* view); + virtual ~WebViewCore(); + + /** + * Set the scroll offset. + * @param x The x position of the new scroll offset. + * @param y The y position of the new scroll offset. + */ +// void setScrollOffset(int x, int y); + + // Inherited from WebCoreViewBridge + virtual void draw(WebCore::GraphicsContext* ctx, + const WebCore::IntRect& rect, bool invalCache); + + /** + * Layout our Frame if needed and recursively layout all child frames. + */ + virtual void layout(); + + /** + * Scroll to an absolute position. + * @param x The x coordinate. + * @param y The y coordinate. + * @param animate If it is true, animate to the new scroll position + * + * This method calls Java to trigger a gradual scroll event. + */ + virtual void scrollTo(int x, int y, bool animate = false); + + /** + * Scroll to the point x,y relative to the current position. + * @param x The relative x position. + * @param y The relative y position. + */ + virtual void scrollBy(int x, int y); + + /** + * Mark the display list as invalid, and post an event (once) to + * rebuild the display list by calling webcore to draw the dom + */ + virtual void contentInvalidate(); + virtual void contentInvalidate(const WebCore::IntRect &rect); + + // invalidate the view/display, NOT the content/DOM + virtual void viewInvalidate() { sendViewInvalidate(); } + + /** + * Called by webcore when the focus was set after returning to prior page + * used to rebuild and display any changes in focus + */ + virtual void notifyFocusSet(); + /** + * Called by webcore when the progress indicator is done + * used to rebuild and display any changes in focus + */ + virtual void notifyProgressFinished(); + + /** + * On resize is called after a setSize event on WebCoreViewBridge. onResize + * then tells the frame to relayout the contents due to the size change + */ + virtual void onResize(); + + /** + * Notify the view that WebCore did its first layout. + */ + virtual void didFirstLayout(); + + /** + * Notify the view to restore the screen width, which in turn restores + * the scale. + */ + virtual void restoreScale(int); + + /* Set the view and frame */ + virtual void setView(WebCore::FrameView* view) { + if (mView) + mView->deref(); + mView = view; + if (mView) { + mView->ref(); + mFrame = (WebCore::FrameAndroid*)mView->frame(); + reset(false); + } else + mFrame = NULL; + } + + // new methods for this subclass + + void reset(bool fromConstructor); + + WebCore::FrameAndroid* frame() const { return mFrame; } + WebCore::String retrieveHref(WebCore::Frame* frame, WebCore::Node* node); + + /** + * Return the url of the image located at (x,y) in content coordinates, or + * null if there is no image at that point. + * + * @param x x content ordinate + * @param y y content ordinate + * @return WebCore::String url of the image located at (x,y), or null if there is + * no image there. + */ + WebCore::String retrieveImageRef(int x, int y); + + WebCore::String getSelection(SkRegion* ); + void recordPicture(SkPicture* picture, bool invalCache); + void setFrameCacheOutOfDate(); + void setFinalFocus(WebCore::Frame* frame, WebCore::Node* node, + int x, int y, bool block); + void setKitFocus(int moveGeneration, int buildGeneration, + WebCore::Frame* frame, WebCore::Node* node, int x, int y, + bool ignoreNullFocus); + int getMaxXScroll() const { return mMaxXScroll; } + int getMaxYScroll() const { return mMaxYScroll; } + void setMaxXScroll(int maxx) { mMaxXScroll = maxx; } + void setMaxYScroll(int maxy) { mMaxYScroll = maxy; } + + int contentWidth() const { return mView->contentsWidth(); } + int contentHeight() const { return mView->contentsHeight(); } + + // the visible rect is in document coordinates, and describes the + // intersection of the document with the "window" in the UI. + void getVisibleRect(WebCore::IntRect* rect) const; + void setVisibleRect(const WebCore::IntRect& rect); + + WebCore::FrameView* getFrameView() { return mView; } + void listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount, + bool multiple, const int selected[], size_t selectedCountOrSelection); + + /** + * Handle keyDown events from Java. + * @param keyCode The key pressed. + * @return Whether keyCode was handled by this class. + */ + bool keyUp(KeyCode keyCode, int keyValue); + + /** + * Handle motionUp event from the UI thread (called touchUp in the + * WebCore thread). + */ + void touchUp(int touchGeneration, int buildGeneration, + WebCore::Frame* frame, WebCore::Node* node, int x, int y, + int size, bool isClick, bool retry); + + /** + * Return a new WebCoreViewBridge to interface with the passed in view. + */ + virtual WebCoreViewBridge* createBridgeForView(WebCore::FrameView* view); + + /** + * Sets the index of the label from a popup + */ + void popupReply(int index); + void popupReply(SkTDArray<int>array); + + virtual void jsAlert(const WebCore::String& url, const WebCore::String& text); + virtual bool jsConfirm(const WebCore::String& url, const WebCore::String& text); + virtual bool jsPrompt(const WebCore::String& url, const WebCore::String& message, const WebCore::String& defaultValue, WebCore::String& result); + virtual bool jsUnload(const WebCore::String& url, const WebCore::String& message); + + /** + * Delete text from start to end in the focused textfield. If there is no + * focus, or if start == end, silently fail, but set selection to that value. + * If start and end are out of order, swap them. + * Use the frame, node, x, and y to ensure that the correct node is focused. + * Return a frame. Convenience so replaceTextfieldText can use this function. + */ + WebCore::Frame* deleteSelection(WebCore::Frame* frame, WebCore::Node* node, int x, + int y,int start, int end); + + /** + * Set the selection of the currently focused textfield to (start, end). + * If start and end are out of order, swap them. + * Use the frame, node, x, and y to ensure that the correct node is focused. + * Return a frame. Convenience so deleteSelection can use this function. + */ + WebCore::Frame* setSelection(WebCore::Frame* frame, WebCore::Node* node, int x, + int y,int start, int end); + /** + * In the currenlty focused textfield, represented by frame, node, x, and y (which + * are used to ensure it has focus), replace the characters from oldStart to oldEnd + * (if oldStart == oldEnd, this will be an insert at that position) with replace, + * and set the selection to (start, end). + */ + void replaceTextfieldText(WebCore::Frame* frame, WebCore::Node* node, int x, int y, + int oldStart, int oldEnd, jstring replace, int start, int end); + void passToJs(WebCore::Frame* frame, WebCore::Node* node, int x, int y, int generation, + jstring currentText, int jKeyCode, int keyVal, bool down, bool cap, bool fn, bool sym); + + void saveDocumentState(WebCore::Frame* frame, WebCore::Node* node, int x, int y); + + // TODO: I don't like this hack but I need to access the java object in + // order to send it as a parameter to java + jobject getJavaObject(); + + // Return the parent WebView Java object associated with this + // WebViewCore. + jobject getWebViewJavaObject(); + + bool pinXToDocument(int* yPtr); + bool pinYToDocument(int* yPtr); + + WebCore::RenderLayer* getRenderLayer(); + + /** + * Tell the java side to update the focused textfield + * @param pointer Pointer to the node for the input field. + * @param changeToPassword If true, we are changing the textfield to + * a password field, and ignore the String + * @param text If changeToPassword is false, this is the new text that + * should go into the textfield. + */ + virtual void updateTextfield(WebCore::Node* pointer, + bool changeToPassword, const WebCore::String& text); + + virtual void removeFrameGeneration(WebCore::Frame* ); + virtual void updateFrameGeneration(WebCore::Frame* ); + + void setBackgroundColor(SkColor c); + void setSnapAnchor(int x, int y); + void snapToAnchor(); + void unblockFocus() { mBlockFocusChange = false; } + void updateFrameCache(); + void dump(); + + // jni methods + static void Destroy(JNIEnv*, jobject); + static void Dump(JNIEnv*, jobject); + static void RefreshPlugins(JNIEnv*, jobject, jboolean); + static void SetSize(JNIEnv*, jobject, jint, jint, jint, jfloat); + static void SetVisibleRect(JNIEnv*, jobject, jint, jint, jint, jint); + static jboolean KeyUp(JNIEnv*, jobject, jint, jint); + static void DeleteSelection(JNIEnv*, jobject, jint, jint, jint, jint, + jint, jint); + static void SetSelection(JNIEnv*, jobject, jint, jint, jint, jint, + jint, jint); + static void ReplaceTextfieldText(JNIEnv*, jobject, jint, jint, jint, + jint, jint, jint, jstring, jint, jint); + static void PassToJs(JNIEnv*, jobject, jint, jint, jint, jint, jint, + jstring, jint, jint, jboolean, jboolean, jboolean, jboolean); + static void SaveDocumentState(JNIEnv*, jobject, jint, jint, jint, jint); + static void Draw(JNIEnv*, jobject, jobject); + static void SendListBoxChoices(JNIEnv*, jobject, jbooleanArray, jint); + static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice); + static void ClearMatches(JNIEnv*, jobject); + static jboolean Find(JNIEnv*, jobject, jstring, jboolean, jboolean); + static jstring FindAddress(JNIEnv*, jobject, jstring); + static jint FindAll(JNIEnv*, jobject, jstring); + static void TouchUp(JNIEnv*, jobject, jint, jint, jint, jint, jint, + jint, jint, jboolean, jboolean); + static jstring RetrieveHref(JNIEnv*, jobject, jint, jint); + static jstring RetrieveImageRef(JNIEnv*, jobject, jint, jint); + static void SetFinalFocus(JNIEnv*, jobject, jint, jint, jint, jint, + jboolean); + static void SetKitFocus(JNIEnv*, jobject, jint, jint, jint, jint, jint, + jint, jboolean); + static void UnblockFocus(JNIEnv*, jobject); + static void UpdateFrameCache(JNIEnv*, jobject); + static jint GetContentMinPrefWidth(JNIEnv*, jobject); + static void SetViewportSettingsFromNative(JNIEnv*, jobject); + static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color); + static void SetSnapAnchor(JNIEnv*, jobject, jint, jint); + static void SnapToAnchor(JNIEnv*, jobject); + static jstring GetSelection(JNIEnv*, jobject, jobject); + // end jni methods + + // these members are shared with webview.cpp + int retrieveFrameGeneration(WebCore::Frame* ); + static Mutex gFrameCacheMutex; + CachedRoot* mFrameCacheKit; // nav data being built by webcore + SkPicture* mNavPictureKit; + int mGeneration; // copy of the number bumped by WebViewNative + int mMoveGeneration; // copy of state in WebViewNative triggered by move + int mTouchGeneration; // copy of state in WebViewNative triggered by touch + int mLastGeneration; // last action using up to date cache + bool mUpdatedFrameCache; + bool mUseReplay; + static Mutex gRecomputeFocusMutex; + WTF::Vector<int> mRecomputeEvents; + // These two fields go together: we use the mutex to protect access to + // mButtons, so that we, and webview.cpp can look/modify the mButtons + // field safely from our respective threads + static Mutex gButtonMutex; + SkTDArray<Container*>* mButtons; + // end of shared members + private: + friend class ListBoxReply; + struct FrameGen { + const WebCore::Frame* mFrame; + int mGeneration; + }; + WTF::Vector<FrameGen> mFrameGenerations; + static Mutex gFrameGenerationMutex; + struct JavaGlue; + struct JavaGlue* mJavaGlue; + WebCore::FrameView* mView; + WebCore::FrameAndroid* mFrame; + WebCoreReply* mPopupReply; + WebCore::Node* mLastFocused; + WebCore::IntRect mLastFocusedBounds; + // Used in passToJS to avoid updating the UI text field until after the + // key event has been processed. + bool mBlockTextfieldUpdates; + // Passed in with key events to know when they were generated. Store it + // with the cache so that we can ignore stale text changes. + int mTextGeneration; + CachedRoot* mTemp; + SkPicture* mTempPict; + int mBuildGeneration; + int mMaxXScroll; + int mMaxYScroll; + WebCore::IntRect mVisibleRect; + WebCore::IntPoint mMousePos; + bool mFrameCacheOutOfDate; + bool mBlockFocusChange; + int mLastPassed; + int mLastVelocity; + CachedHistory mHistory; + WebCore::Node* mSnapAnchorNode; + WebCore::Frame* changedKitFocus(WebCore::Frame* frame, + WebCore::Node* node, int x, int y); + bool commonKitFocus(int generation, int buildGeneration, + WebCore::Frame* frame, WebCore::Node* node, int x, int y, + bool ignoreNullFocus); + bool finalKitFocus(WebCore::Frame* frame, WebCore::Node* node, int x, int y); + void doMaxScroll(WebCore::CacheBuilder::Direction dir); + void sendMarkNodeInvalid(WebCore::Node* ); + void sendNotifyFocusSet(); + void sendNotifyProgressFinished(); + void sendRecomputeFocus(); + void sendViewInvalidate(); + bool handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr); + bool prepareFrameCache(); + void releaseFrameCache(bool newCache); +#if DEBUG_NAV_UI + uint32_t mNow; +#endif + }; + +} // namespace android + +#endif // ANDROID_WIDGET_HTMLWIDGET_H diff --git a/WebCore/platform/android/nav/CacheBuilder.cpp b/WebCore/platform/android/nav/CacheBuilder.cpp new file mode 100644 index 0000000..cee6ca7 --- /dev/null +++ b/WebCore/platform/android/nav/CacheBuilder.cpp @@ -0,0 +1,2974 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License.max + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CachedPrefix.h" +#include "CachedNode.h" +#include "CachedRoot.h" +#include "Document.h" +#include "EventNames.h" +#include "EventTargetNode.h" +#include "Frame.h" +#include "FrameAndroid.h" +#include "FrameTree.h" +#include "FrameView.h" +//#include "GraphicsContext.h" +#include "HTMLAreaElement.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLOptionElement.h" +#include "HTMLSelectElement.h" +#include "InlineTextBox.h" +#include "KURL.h" +#include "RenderImage.h" +#include "RenderInline.h" +#include "RenderListBox.h" +#include "RenderSkinCombo.h" +#include "RenderTextControl.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 WebCore { + +CacheBuilder* CacheBuilder::Builder(Frame* frame) { + return &Android(frame)->m_cacheBuilder; +} + +FrameAndroid* CacheBuilder::FrameAnd(CacheBuilder* cacheBuilder) { + return (FrameAndroid*) ((char*) cacheBuilder - OFFSETOF(FrameAndroid, m_cacheBuilder)); +} + +FrameAndroid* CacheBuilder::FrameAnd(const CacheBuilder* cacheBuilder) { + return (FrameAndroid*) ((char*) cacheBuilder - OFFSETOF(FrameAndroid, m_cacheBuilder)); +} + +#if DUMP_NAV_CACHE + +#define DEBUG_BUFFER_SIZE 256 +#define DEBUG_WRAP_SIZE 150 +#define DEBUG_WRAP_MAX 170 + +FrameAndroid* 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(", "); +} + +int CacheBuilder::Debug::flowBoxes(RenderFlow* flow, int ifIndex, int indent) { + char scratch[256]; + const InlineFlowBox* box = flow->firstLineBox(); + if (box == NULL) + return ifIndex; + do { + newLine(); + int i = snprintf(scratch, sizeof(scratch), "// render flow:%p" + " box:%p%.*s", flow, box, indent, " "); + for (; box; box = box->nextFlowBox()) { + i += snprintf(&scratch[i], sizeof(scratch) - i, + " [%d]:{%d, %d, %d, %d}", ++ifIndex, + box->xPos(), box->yPos(), box->width(), box->height()); + if (ifIndex % 4 == 0) + break; + } + print(scratch); + } while (box); + RenderObject const * const end = flow->lastChild(); + if (end == NULL) + return ifIndex; + indent += 2; + if (indent > 8) + indent = 8; + for (const RenderObject* renderer = flow->firstChild(); renderer != end; + renderer = renderer->nextSibling()) + { + if (renderer->isInlineFlow()) + ifIndex = flowBoxes((RenderFlow*) renderer, ifIndex, indent); + } + return ifIndex; +} + +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 (mBuffer[len] == '\\' || 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() { + FrameAndroid* 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() { + FrameAndroid* 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("#define TEST%s_RECTS NULL\n", name); + DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = 0; // 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; + EventTargetNode* elementTarget = node->isEventTargetNode() ? + (EventTargetNode*) node : NULL; + if (elementTarget) { + if (elementTarget->getEventListener(EventNames::clickEvent)) + properties.append("ONCLICK | "); + if (elementTarget->getEventListener(EventNames::mousedownEvent)) + properties.append("MOUSEDOWN | "); + if (elementTarget->getEventListener(EventNames::mouseupEvent)) + properties.append("MOUSEUP | "); + if (elementTarget->getEventListener(EventNames::mouseoverEvent)) + properties.append("MOUSEOVER | "); + if (elementTarget->getEventListener(EventNames::mouseoutEvent)) + properties.append("MOUSEOUT | "); + if (elementTarget->getEventListener(EventNames::keydownEvent)) + properties.append("KEYDOWN | "); + if (elementTarget->getEventListener(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 = static_cast<HTMLAreaElement*>(node)->getAreaRect(); + 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(); + if (renderer) { + const IntRect& absB = renderer->absoluteBoundingBoxRect(); + snprintf(scratch, sizeof(scratch), ", {%d, %d, %d, %d}, %s},", + absB.x(), absB.y(), absB.width(), absB.height(), + renderer->hasOverflowClip() ? "true" : "false"); + print(scratch); + } else + print(", {0, 0, 0, 0}, false},"); + 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); + NamedAttrMap* 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) + renderTree(renderer, 0, node, count); + 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; + 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(); + if (CacheBuilder::ConstructPartRects(node, nodeBounds, rectPtr, + globalOffsetX, globalOffsetY, &rects) == false) + 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) { + int renderX, renderY; + renderText->absolutePosition(renderX, renderY); + IntRect rect = textBox->selectionRect(renderX, renderY, 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(), textBox->selectionHeight(), textBox->selectionTop()); + mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d", + textBox->spaceAdd(), textBox->start(), textBox->textPos()); + mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d, %d", + textBox->xPos(), textBox->yPos(), textBox->width(), textBox->height()); + mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d }, // %d ", + textBox->baseline(), ++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", 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 (node->isEventTargetNode()) + 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::renderTree(RenderObject* renderer, int indent, + Node* child, int count) +{ + char scratch[256]; + Node* node = renderer->node(); + if (node != child) { + count = ParentIndex(child, count, node); + if (renderer->isRenderBlock() == false) + goto tryParent; + RenderBlock* renderBlock = (RenderBlock*) renderer; + if (renderBlock->hasColumns() == false) + goto tryParent; + Vector<IntRect>* rects = renderBlock->columnRects(); + newLine(indent); + snprintf(scratch, sizeof(scratch), "// render parent=%d", count); + print(scratch); + for (size_t x = 0; x < rects->size(); x++) { + const IntRect& rect = rects->at(x); + snprintf(scratch, sizeof(scratch), "(%d,%d,%d,%d) ", rect.x(), + rect.y(), rect.width(), rect.height()); + print(scratch); + } + } + { + newLine(indent); + RenderStyle* style = renderer->style(); + EVisibility vis = style->visibility(); + ASSERT(vis == VISIBLE || vis == HIDDEN || vis == COLLAPSE); + snprintf(scratch, sizeof(scratch), + "// render style visible:%s opacity:%g width:%d height:%d" + " hasBackground:%s isInlineFlow:%s isBlockFlow:%s" + " textOverflow:%s", + vis == VISIBLE ? "visible" : vis == HIDDEN ? "hidden" : "collapse", + style->opacity(), renderer->width(), renderer->height(), + style->hasBackground() ? "true" : "false", + renderer->isInlineFlow() ? "true" : "false", + renderer->isBlockFlow() ? "true" : "false", + style->textOverflow() ? "true" : "false" + ); + print(scratch); + newLine(indent); + const IntRect& oRect = renderer->overflowRect(true); + const IntRect& cRect = renderer->getOverflowClipRect(0,0); + snprintf(scratch, sizeof(scratch), + "// render xPos:%d yPos:%d overflowRect:{%d, %d, %d, %d} " + " getOverflowClipRect:{%d, %d, %d, %d} ", + renderer->xPos(), renderer->yPos(), + oRect.x(), oRect.y(), oRect.width(), oRect.height(), + cRect.x(), cRect.y(), cRect.width(), cRect.height() + ); + print(scratch); + if (renderer->isInlineFlow()) { + RenderFlow* renderFlow = (RenderFlow*) renderer; + int ifIndex = 0; + flowBoxes(renderFlow, ifIndex, 0); + } + } +tryParent: + RenderObject* parent = renderer->parent(); + if (parent) + renderTree(parent, indent + 2, node, count); +} + +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() { + FrameAndroid* frame = frameAnd(); + android::WebCoreFrameBridge* bridge = frame->bridge(); + ASSERT(bridge); + WebCoreViewBridge* vbridge = frame->view()->getWebCoreViewBridge(); + ASSERT(vbridge); + 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() +{ + mLastKnownFocus = NULL; + mAllowableTypes = android::ALL_CACHEDNODETYPES; +#ifdef DUMP_NAV_CACHE_USING_PRINTF + gNavCacheLogFile = NULL; +#endif +} + +void CacheBuilder::adjustForColumns(const ClipColumnTracker& track, + android::CachedNode* node, IntRect* bounds) +{ + int x = 0; + int y = 0; + int tx = track.mBounds.x(); + int ty = track.mBounds.y(); + int columnGap = track.mColumnGap; + size_t limit = track.mColumns->size(); + for (size_t index = 0; index < limit; index++) { + IntRect column = track.mColumns->at(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(); + } +} + +bool CacheBuilder::AnyChildIsClick(Node* node) +{ + Node* child = node->firstChild(); + while (child != NULL) { + if (child->isEventTargetNode()) { + EventTargetNode* target = (EventTargetNode*) child; + if (target->isFocusable() || + target->getEventListener(EventNames::clickEvent) || + target->getEventListener(EventNames::mousedownEvent) || + target->getEventListener(EventNames::mouseupEvent) || + target->getEventListener(EventNames::keydownEvent) || + target->getEventListener(EventNames::keyupEvent)) + 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); + EventTargetNode* target = (EventTargetNode*) node; + if (target->getEventListener(EventNames::mouseoverEvent) == NULL && + target->getEventListener(EventNames::mouseoutEvent) == NULL && + target->getEventListener(EventNames::keydownEvent) == NULL && + target->getEventListener(EventNames::keyupEvent) == NULL) + return false; + if (target->getEventListener(EventNames::clickEvent)) + return false; + if (target->getEventListener(EventNames::mousedownEvent)) + return false; + if (target->getEventListener(EventNames::mouseupEvent)) + return false; + return AnyChildIsClick(node); +} + +void CacheBuilder::buildCache(android::CachedRoot* root) +{ + FrameAndroid* frame = FrameAnd(this); + mLastKnownFocus = NULL; + BuildFrame(frame, frame, root, (android::CachedFrame*) root); + root->finishInit(); // set up frame parent pointers, child pointers + setData((android::CachedFrame*) root); +} + +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; +} + +// 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(FrameAndroid* root, FrameAndroid* frame, + android::CachedRoot* cachedRoot, android::CachedFrame* cachedFrame) +{ + WTF::Vector<Tracker> tracker(1); + { + Tracker* baseTracker = tracker.data(); // sentinel + bzero(baseTracker, sizeof(Tracker)); + baseTracker->mCachedNodeIndex = -1; + } + WTF::Vector<ClipColumnTracker> clipTracker(1); + { + ClipColumnTracker* baseTracker = clipTracker.data(); // sentinel + bzero(baseTracker, sizeof(ClipColumnTracker)); + } +#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; + android::CachedNode cachedParentNode; + cachedParentNode.init(cachedFrame, parent); +#if DUMP_NAV_CACHE + cachedParentNode.mDebug.mNodeIndex = nodeIndex; +#endif + cachedFrame->add(cachedParentNode); + Node* node = parent; + int cacheIndex = 1; + Node* focused = doc->focusedNode(); + if (focused) + setLastFocus(focused); + int globalOffsetX, globalOffsetY; + GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY); + while (walk.mMore || (node = node->traverseNextNode()) != NULL) { +#if DUMP_NAV_CACHE + nodeIndex++; +#endif + Tracker* last = &tracker.last(); + int lastChildIndex = cachedFrame->size() - 1; + while (node == last->mLastChild) { + if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex)) + cacheIndex--; + tracker.removeLast(); + lastChildIndex = last->mCachedNodeIndex; + last = &tracker.last(); + } + if (node == last->mParentLastChild) + last->mParentLastChild = NULL; + do { + const ClipColumnTracker* lastClip = &clipTracker.last(); + if (node != lastClip->mLastChild) + break; + clipTracker.removeLast(); + } while (true); + Frame* child = HasFrame(node); + android::CachedNode cachedNode; + if (child != NULL) { + if (child->document() == NULL) + continue; + RenderObject* nodeRenderer = node->renderer(); + if (nodeRenderer != NULL && nodeRenderer->style()->visibility() == HIDDEN) + continue; + android::CachedFrame cachedChild; + cachedChild.init(cachedRoot, cacheIndex, Android(child)); + int childFrameIndex = cachedFrame->childCount(); + cachedFrame->addFrame(cachedChild); + cachedNode.init(cachedFrame, node); + cachedNode.setIndex(cacheIndex++); + cachedNode.setChildFrameIndex(childFrameIndex); +#if DUMP_NAV_CACHE + cachedNode.mDebug.mNodeIndex = nodeIndex; + cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex( + node, nodeIndex, NULL); +#endif + cachedFrame->add(cachedNode); + android::CachedFrame* childPtr = cachedFrame->lastChild(); + BuildFrame(root, Android(child), cachedRoot, childPtr); + continue; + } + RenderObject* nodeRenderer = node->renderer(); + bool isTransparent = false; + bool hasFocusRing = true; + if (nodeRenderer != NULL) { + RenderStyle* style = nodeRenderer->style(); + if (style->visibility() == HIDDEN) + continue; + if (nodeRenderer->isImage()) { // set all the area elements to have a link to their images + RenderImage* image = static_cast<RenderImage*>(nodeRenderer); + image->setImageForAreaElements(); + } + isTransparent = style->hasBackground() == false; + hasFocusRing = style->tapHighlightColor().alpha() > 0; + } + bool more = walk.mMore; + walk.reset(); + // GetGlobalBounds(node, &bounds, false); + bool anchorHasSrcUrl = false; + bool computeFocusRings = false; + bool hasClip = false; + bool hasMouseOver = false; + bool isAnchor = false; + bool isArea = node->hasTagName(HTMLNames::areaTag); + bool isInput = false; + bool isPassword = false; + bool isTextArea = false; + bool isTextField = false; + bool isRtlText = false; + bool isUnclipped = false; + bool isFocus = node == focused; + bool takesFocus = false; + int maxLength = -1; + int textSize = 12; + int columnGap = 0; + TextDirection direction = LTR; + String name; + String exported; + android::CachedNodeType type = android::NORMAL_CACHEDNODETYPE; + IntRect bounds; + IntRect absBounds; + Node* lastChild = node->lastChild(); + WTF::Vector<IntRect>* columns = NULL; + if (isArea) { + HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node); + bounds = area->getAreaRect(); + 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; + absBounds = nodeRenderer->absoluteBoundingBoxRect(); + absBounds.move(globalOffsetX, globalOffsetY); + hasClip = nodeRenderer->hasOverflowClip(); + if (nodeRenderer->isRenderBlock()) { + RenderBlock* renderBlock = (RenderBlock*) nodeRenderer; + if (renderBlock->hasColumns()) { + columns = renderBlock->columnRects(); + columnGap = renderBlock->columnGap(); + direction = renderBlock->style()->direction(); + } + } + if ((hasClip != false || columns != NULL) && lastChild) { + clipTracker.grow(clipTracker.size() + 1); + ClipColumnTracker& clip = clipTracker.last(); + clip.mBounds = absBounds; + clip.mLastChild = OneAfter(lastChild); + clip.mNode = node; + clip.mColumns = columns; + clip.mColumnGap = columnGap; + clip.mHasClip = hasClip; + clip.mDirection = direction; + if (columns != NULL) { + const IntRect& oRect = nodeRenderer->overflowRect(true); + clip.mBounds.move(oRect.x(), oRect.y()); + } + } + if (node->isTextNode() && mAllowableTypes != android::NORMAL_CACHEDNODETYPE) { + if (last->mSomeParentTakesFocus) // don't look at text inside focusable node + continue; + android::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 = (android::CachedNodeType) checkType; + // !!! test ! is the following line correctly needed for frames to work? + cachedNode.init(cachedFrame, 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.focusRings()) == false) + continue; + cachedNode.setBounds(bounds); + if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) + continue; + if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) + continue; + computeFocusRings = true; + 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 = (HTMLInputElement*) node; + if (input->inputType() == HTMLInputElement::FILE) + continue; + isInput = true; + isTextField = input->isTextField(); + isPassword = input->inputType() == HTMLInputElement::PASSWORD; + maxLength = input->maxLength(); + name = String(input->name().domString()); + isUnclipped = isTransparent; // can't detect if this is drawn on top (example: deviant.com login parts) + } else if (node->hasTagName(HTMLNames::textareaTag)) + isTextArea = true; + else if (node->hasTagName(HTMLNames::aTag)) { + EventTargetNode* target = (EventTargetNode*) node; + hasMouseOver = target->getEventListener(EventNames::mouseoverEvent); + isAnchor = true; + String href = ((HTMLAnchorElement*) node)->href(); +#ifdef ANDROID_JAVASCRIPT_SECURITY + anchorHasSrcUrl = !href.isEmpty() && !WebCore::protocolIs(href, "javascript"); + // Set the exported string for protocols that we know will not be + // handled by WebCore. + if (WebCore::protocolIs(href, "mailto") || + WebCore::protocolIs(href, "tel") || + WebCore::protocolIs(href, "geo")) + exported = String(KURL::decode_string(href.deprecatedString())); +#endif + } + if (isTextField || isTextArea) { + RenderTextControl* renderText = + static_cast<RenderTextControl*>(nodeRenderer); + if (isFocus) + cachedRoot->setSelection(renderText->selectionStart(), renderText->selectionEnd()); + exported = String(renderText->text()); + // FIXME: Would it be better to use (float) size()? + // FIXME: Are we sure there will always be a style and font, and it's correct? + RenderStyle* style = nodeRenderer->style(); + if (style) { + textSize = style->fontSize(); + isRtlText = style->direction() == RTL; + } + } + takesFocus = true; + if (isAnchor) { + bounds = absBounds; + } else { + bool isFocusable = node->isKeyboardFocusable(NULL) || + node->isMouseFocusable() || node->isFocusable(); + if (isFocusable == false) { + if (node->isEventTargetNode() == false) + continue; + bool overOrOut = HasOverOrOut(node); + bool hasTrigger = HasTriggerEvent(node); + if (overOrOut == false && hasTrigger == false) + continue; + takesFocus = hasTrigger; + } + bounds = node->getRect(); + // For Bank of America site + if (isTextField && nodeRenderer->paddingLeft() > 100) { + int paddingLeft = nodeRenderer->paddingLeft(); + int paddingTop = nodeRenderer->paddingTop(); + int x = bounds.x() + paddingLeft; + int y = bounds.y() + paddingTop; + int width = bounds.width() - paddingLeft - nodeRenderer->paddingRight(); + int height = bounds.height() - paddingTop - nodeRenderer->paddingBottom(); + bounds.setLocation(IntPoint(x, y)); + bounds.setSize(IntSize(width, height)); + } + if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) + continue; + if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) + continue; + bounds.move(globalOffsetX, globalOffsetY); + } + computeFocusRings = true; + keepNode: + cachedNode.init(cachedFrame, node); + if (computeFocusRings == false) { + cachedNode.setBounds(bounds); + cachedNode.focusRings().append(bounds); + } else if (ConstructPartRects(node, bounds, cachedNode.boundsPtr(), + globalOffsetX, globalOffsetY, &cachedNode.focusRings()) == false) + continue; + 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); + continue; + } + const IntRect& parentClip = clipTrack.mBounds; + if (hasClip == false && isAnchor) + clip = parentClip; + else + clip.intersect(parentClip); + hasClip = true; + DBG_NAV_LOGD("absBounds={%d,%d,%d,%d} parentClip={%d,%d,%d,%d}\n", + absBounds.x(), absBounds.y(), absBounds.width(), absBounds.height(), + parentClip.x(), parentClip.y(), parentClip.width(), parentClip.height()); + } + if (hasClip && cachedNode.clip(clip) == false) { + cachedNode.setBounds(clip); + cachedNode.focusRings().append(clip); + isUnclipped = true; + } + cachedNode.setNavableRects(); + cachedNode.setAnchorHasSrcUrl(anchorHasSrcUrl); + cachedNode.setChildFrameIndex(-1); + cachedNode.setExport(exported); + cachedNode.setHasFocusRing(hasFocusRing); + cachedNode.setHasMouseOver(hasMouseOver); + cachedNode.setHitBounds(absBounds); + cachedNode.setIndex(cacheIndex); + cachedNode.setIsAnchor(isAnchor); + cachedNode.setIsArea(isArea); + cachedNode.setIsFocus(isFocus); + cachedNode.setIsInput(isInput); + cachedNode.setIsPassword(isPassword); + cachedNode.setIsRtlText(isRtlText); + cachedNode.setIsTextArea(isTextArea); + cachedNode.setIsTextField(isTextField); + cachedNode.setIsTransparent(isTransparent); + cachedNode.setIsUnclipped(isUnclipped); + cachedNode.setMaxLength(maxLength); + cachedNode.setName(name); + cachedNode.setParentIndex(last->mCachedNodeIndex); + if (last->mParentLastChild == NULL) + last->mParentLastChild = OneAfter(node->parentNode()->lastChild()); + cachedNode.setParentGroup(last->mParentLastChild); + cachedNode.setTextSize(textSize); + cachedNode.setType(type); +#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) { + android::CachedNode* cachedNodePtr = cachedFrame->getIndex(lastIndex); + cachedRoot->setCachedFocus(cachedFrame, cachedNodePtr); + } + if (lastChild != NULL) { + tracker.grow(tracker.size() + 1); + Tracker& working = tracker.last(); + working.mCachedNodeIndex = lastIndex; + working.mLastChild = OneAfter(lastChild); + working.mParentLastChild = OneAfter(node->parentNode()->lastChild()); + last = &tracker.at(tracker.size() - 2); + working.mSomeParentTakesFocus = last->mSomeParentTakesFocus | takesFocus; + } + } + cacheIndex++; +tryNextNode: + ; + } + while (tracker.size() > 1) { + Tracker* last = &tracker.last(); + int lastChildIndex = cachedFrame->size() - 1; + if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex)) + cacheIndex--; + tracker.removeLast(); + } +} + +bool CacheBuilder::CleanUpContainedNodes(android::CachedFrame* cachedFrame, + const Tracker* 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; + android::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->focusRings().clear(); + lastCached->setNavableRects(); + return false; + } + android::CachedNode* onlyChildCached = cachedFrame->lastNode(); + Node* onlyChild = (Node*) onlyChildCached->nodePointer(); + bool outerIsMouseMoveOnly = + lastNode->isKeyboardFocusable(NULL) == false && + lastNode->isMouseFocusable() == false && + lastNode->isFocusable() == false && + lastNode->isEventTargetNode() == true && + HasOverOrOut(lastNode) == true && + HasTriggerEvent(lastNode) == false; + if (cachedFrame->focusIndex() == lastChildIndex) + cachedFrame->setFocusIndex(last->mCachedNodeIndex); + if (onlyChildCached->parent() == lastCached) + onlyChildCached->setParentIndex(lastCached->parentIndex()); + if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL)) + *lastCached = *onlyChildCached; + cachedFrame->removeLast(); + 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) +{ + static const struct { + char mLow; + char mHigh; + char mException1; + char mException2; + } zipRange[] = { + { 99, 99, -1, -1 }, // AK Alaska + { 35, 36, -1, -1 }, // AL Alabama + { 71, 72, -1, -1 }, // AR Arkansas + { 96, 96, -1, -1 }, // AS American Samoa + { 85, 86, -1, -1 }, // AZ Arizona + { 90, 96, -1, -1 }, // CA California + { 80, 81, -1, -1 }, // CO Colorado + { 6, 6, -1, -1 }, // CT Connecticut + { 20, 20, -1, -1 }, // DC District of Columbia + { 19, 19, -1, -1 }, // DE Delaware + { 32, 34, -1, -1 }, // FL Florida + { 96, 96, -1, -1 }, // FM Federated States of Micronesia + { 30, 31, -1, -1 }, // GA Georgia + { 96, 96, -1, -1 }, // GU Guam + { 96, 96, -1, -1 }, // HI Hawaii + { 50, 52, -1, -1 }, // IA Iowa + { 83, 83, -1, -1 }, // ID Idaho + { 60, 62, -1, -1 }, // IL Illinois + { 46, 47, -1, -1 }, // IN Indiana + { 66, 67, 73, -1 }, // KS Kansas + { 40, 42, -1, -1 }, // KY Kentucky + { 70, 71, -1, -1 }, // LA Louisiana + { 1, 2, -1, -1 }, // MA Massachusetts + { 20, 21, -1, -1 }, // MD Maryland + { 3, 4, -1, -1 }, // ME Maine + { 96, 96, -1, -1 }, // MH Marshall Islands + { 48, 49, -1, -1 }, // MI Michigan + { 55, 56, -1, -1 }, // MN Minnesota + { 63, 65, -1, -1 }, // MO Missouri + { 96, 96, -1, -1 }, // MP Northern Mariana Islands + { 38, 39, -1, -1 }, // MS Mississippi + { 55, 56, -1, -1 }, // MT Montana + { 27, 28, -1, -1 }, // NC North Carolina + { 58, 58, -1, -1 }, // ND North Dakota + { 68, 69, -1, -1 }, // NE Nebraska + { 3, 4, -1, -1 }, // NH New Hampshire + { 7, 8, -1, -1 }, // NJ New Jersey + { 87, 88, 86, -1 }, // NM New Mexico + { 88, 89, 96, -1 }, // NV Nevada + { 10, 14, 0, 6 }, // NY New York + { 43, 45, -1, -1 }, // OH Ohio + { 73, 74, -1, -1 }, // OK Oklahoma + { 97, 97, -1, -1 }, // OR Oregon + { 15, 19, -1, -1 }, // PA Pennsylvania + { 6, 6, 0, 9 }, // PR Puerto Rico + { 96, 96, -1, -1 }, // PW Palau + { 2, 2, -1, -1 }, // RI Rhode Island + { 29, 29, -1, -1 }, // SC South Carolina + { 57, 57, -1, -1 }, // SD South Dakota + { 37, 38, -1, -1 }, // TN Tennessee + { 75, 79, 87, 88 }, // TX Texas + { 84, 84, -1, -1 }, // UT Utah + { 22, 24, 20, -1 }, // VA Virginia + { 6, 9, -1, -1 }, // VI Virgin Islands + { 5, 5, -1, -1 }, // VT Vermont + { 98, 99, -1, -1 }, // WA Washington + { 53, 54, -1, -1 }, // WI Wisconsin + { 24, 26, -1, -1 }, // WV West Virginia + { 82, 83, -1, -1 } // WY Wyoming + }; + + int zip = zipPtr[0] - '0'; + zip *= 10; + zip += zipPtr[1] - '0'; + int low = zipRange[stateIndex].mLow; + int high = zipRange[stateIndex].mHigh; + if (zip >= low && zip <= high) + return true; + if (zip == zipRange[stateIndex].mException1) + return true; + if (zip == zipRange[stateIndex].mException2) + return true; + return false; +} + +CacheBuilder::FoundState CacheBuilder::FindAddress(const UChar* chars, unsigned length, int* start, int* end) +{ + FindState addressState; + FindReset(&addressState); + addressState.mWords[0] = addressState.mStarts[0] = chars; + 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 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->mBases[s->mWordCount] = baseChars; + s->mWords[s->mWordCount] = chars - s->mNumberCount; + s->mStarts[s->mWordCount] = s->mCurrentStart; + 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->mFirstLower != NULL) { + 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; + memmove(s->mBases, &s->mBases[shift], (sizeof(s->mBases) / sizeof(s->mBases[0]) - shift) * sizeof(s->mBases[0])); + memmove(s->mWords, &s->mWords[shift], (sizeof(s->mWords) / sizeof(s->mWords[0]) - shift) * sizeof(s->mWords[0])); + memmove(s->mStarts, &s->mStarts[shift], (sizeof(s->mStarts) / sizeof(s->mStarts[0]) - shift) * sizeof(s->mStarts[0])); + 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->mBases[s->mWordCount] = baseChars; + s->mWords[s->mWordCount] = chars; + s->mStarts[s->mWordCount] = s->mCurrentStart; + 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->mBases[s->mWordCount] = baseChars; + s->mWords[s->mWordCount] = chars; + s->mStarts[s->mWordCount] = s->mCurrentStart; + 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; + 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; + 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: // minus two below skips city before state + for (int wordsIndex = s->mStateWord - 2; 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]; + 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) + return FOUND_NONE; + s->mStartResult = s->mWords[wordReduction] - s->mStarts[wordReduction]; + } + } + return FOUND_COMPLETE; + } + nextTest: + names += offset; + } + } + 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; + memmove(s->mBases, &s->mBases[shift], (sizeof(s->mBases) / sizeof(s->mBases[0]) - shift) * sizeof(s->mBases[0])); + memmove(s->mWords, &s->mWords[shift], (sizeof(s->mWords) / sizeof(s->mWords[0]) - shift) * sizeof(s->mWords[0])); + memmove(s->mStarts, &s->mStarts[shift], (sizeof(s->mStarts) / sizeof(s->mStarts[0]) - shift) * sizeof(s->mStarts[0])); + 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; +} + +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; + WebCoreViewBridge* bridge = frame->view()->getWebCoreViewBridge(); + if (bridge == NULL) + return; + WebCoreViewBridge* parent; + while ((parent = bridge->getParent()) != NULL) { + const WebCore::IntRect& rect = bridge->getBounds(); + *x += rect.x(); + *y += rect.y(); + bridge = 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) +{ + EventTargetNode* target = (EventTargetNode*) node; + return target->getEventListener(EventNames::mouseoverEvent) || + target->getEventListener(EventNames::mouseoutEvent); + +} + +bool CacheBuilder::HasTriggerEvent(Node* node) +{ + EventTargetNode* target = (EventTargetNode*) node; + return target->getEventListener(EventNames::clickEvent) || + target->getEventListener(EventNames::mousedownEvent) || + target->getEventListener(EventNames::mouseupEvent) || + target->getEventListener(EventNames::keydownEvent) || + target->getEventListener(EventNames::keyupEvent); +} + +// #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; +} + +// does not find text to keep it fast +// (this assume text nodes are more rarely moved than other nodes) +Node* CacheBuilder::findByCenter(int x, int y) const +{ + DBG_NAV_LOGD("x=%d y=%d\n", x, y); + Frame* frame = FrameAnd(this); + Node* node = frame->document(); + ASSERT(node != NULL); + int globalOffsetX, globalOffsetY; + GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY); + while ((node = node->traverseNextNode()) != NULL) { + Frame* child = HasFrame(node); + if (child != NULL) { + if (child->document() == NULL) + continue; + CacheBuilder* cacheBuilder = Builder(child); + // if (cacheBuilder->mViewBounds.isEmpty()) + // continue; + Node* result = cacheBuilder->findByCenter(x, y); + if (result != NULL) + return result; + } + if (node->isTextNode()) + continue; + IntRect bounds; + if (node->hasTagName(HTMLNames::areaTag)) { + HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node); + bounds = area->getAreaRect(); + bounds.move(globalOffsetX, globalOffsetY); + } else + bounds = node->getRect(); + if (bounds.isEmpty()) + continue; + bounds.move(globalOffsetX, globalOffsetY); + if (x != bounds.x() + (bounds.width() >> 1)) + continue; + if (y != bounds.y() + (bounds.height() >> 1)) + continue; + if (node->isKeyboardFocusable(NULL)) + return node; + if (node->isMouseFocusable()) + return node; + if (node->isFocusable()) + return node; + if (node->isEventTargetNode() == false) + continue; + if (AnyIsClick(node)) + continue; + if (HasTriggerEvent(node) == false) + continue; + return node; + } + return NULL; +} + +bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node, + android::CachedNodeType* type, String* exported) const +{ + Text* textNode = static_cast<Text*>(node); + StringImpl* string = textNode->string(); + 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 (android::CachedNodeType checkType = android::ADDRESS_CACHEDNODETYPE; + checkType <= android::PHONE_CACHEDNODETYPE; + checkType = (android::CachedNodeType) (checkType << 1)) + { + if ((checkType & mAllowableTypes) == 0) + continue; + InlineTextBox* inlineTextBox = baseInline; + FindState findState; + FindReset(&findState); + start = baseStart; + if (checkType == android::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 android::ADDRESS_CACHEDNODETYPE: + state = FindPartialAddress(baseChars, chars, length, &findState); + break; + case android::EMAIL_CACHEDNODETYPE: + state = FindPartialEMail(chars, length, &findState); + break; + case android::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 = 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 == android::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->string(); + 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(); + } while (true); + if (state == FOUND_NONE) + break; + // search for next text node, if any + Text* nextNode; + do { + do { + do { + node = node->traverseNextNode(); + if (node == NULL || node->hasTagName(HTMLNames::aTag)) { + if (state == FOUND_PARTIAL && + checkType == android::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->string(); + baseChars = string->characters(); + inlineTextBox = baseInline; + start = inlineTextBox->start(); + finalNode: + findState.mEndResult = 0; + } while (true); +tryNextCheckType: + node = textNode; + baseInline = saveInline; + string = textNode->string(); + baseChars = string->characters(); + } + if (foundBetter) { + android::CachedNodeType temp = *type; + switch (temp) { + case android::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 android::EMAIL_CACHEDNODETYPE: + exported->insert(WebCore::String("mailto:"), 0); + break; + case android::PHONE_CACHEDNODETYPE: + exported->insert(WebCore::String("tel:"), 0); + break; + default: + break; + } + return true; + } +noTextMatch: + walk->reset(); + return false; +} + +bool CacheBuilder::IsMailboxChar(UChar ch) +{ + static const unsigned body[] = {0x03ff6000, 0x87fffffe, 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::outOfDate() +{ + Node* kitFocusNode = currentFocus(); + if (mLastKnownFocus != kitFocusNode) { + DBG_NAV_LOGD("%s\n", "mLastKnownFocus != kitFocusNode"); + return true; + } + if (kitFocusNode == NULL) + return false; + IntRect kitBounds = kitFocusNode->getRect(); + bool result = kitBounds != mLastKnownFocusBounds; + if (result == true) + DBG_NAV_LOGD("%s\n", "kitBounds != mLastKnownFocusBounds"); + return result; +} + +void CacheBuilder::setLastFocus(Node* node) +{ + ASSERT(node); + mLastKnownFocus = node; + mLastKnownFocusBounds = node->getRect(); +} + +bool CacheBuilder::setData(android::CachedFrame* cachedFrame) +{ + FrameAndroid* frameAndroid = FrameAnd(this); + Document* doc = frameAndroid->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 (layer->width() == 0 || layer->height() == 0) + return false; + if (!frameAndroid->view()) + return false; + WebCoreViewBridge* view = frameAndroid->view()->getWebCoreViewBridge(); + cachedFrame->setLocalViewBounds(view->getBounds()); + cachedFrame->setContentsSize(layer->width(), layer->height()); + if (cachedFrame->childCount() == 0) + return true; + android::CachedFrame* lastCachedFrame = cachedFrame->lastChild(); + cachedFrame = cachedFrame->firstChild(); + do { + CacheBuilder* cacheBuilder = Builder((Frame* )cachedFrame->framePointer()); + cacheBuilder->setData(cachedFrame); + } while (cachedFrame++ != lastCachedFrame); + return true; +} + +bool CacheBuilder::validNode(void* matchFrame, void* matchNode) const +{ + Frame* frame = FrameAnd(this); + if (matchFrame == frame) { + if (matchNode == NULL) + return true; + Node* node = frame->document(); + while (node != NULL) { + if (node == matchNode) { + const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ? + static_cast<HTMLAreaElement*>(node)->getAreaRect() : 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 = frame->tree()->firstChild(); + while (child) { + bool result = Builder(child)->validNode(matchFrame, matchNode); + if (result) + return result; + child = child->tree()->nextSibling(); + } +#if DEBUG_NAV_UI + if (frame->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.right() <= 0 || bounds.bottom() <= 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; +} + +bool CacheBuilder::ConstructPartRects(Node* node, const IntRect& bounds, + IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result) +{ + WTF::Vector<ClipColumnTracker> clipTracker(1); + ClipColumnTracker* baseTracker = clipTracker.data(); // sentinel + bzero(baseTracker, sizeof(ClipColumnTracker)); + if (node->hasChildNodes() && node->hasTagName(HTMLNames::buttonTag) == false + && node->hasTagName(HTMLNames::selectTag) == false) { + // collect all text rects from first to last child + Node* test = node->firstChild(); + Node* last = NULL; + Node* prior = node; + while ((prior = prior->lastChild()) != NULL) + last = prior; + ASSERT(last != NULL); + bool nodeIsAnchor = node->hasTagName(HTMLNames::aTag); + do { + do { + const ClipColumnTracker* lastClip = &clipTracker.last(); + if (test != lastClip->mLastChild) + break; + clipTracker.removeLast(); + } while (true); + RenderObject* renderer = test->renderer(); + if (renderer == NULL) + continue; + EVisibility vis = renderer->style()->visibility(); + if (vis == HIDDEN) + continue; + if (test->isTextNode()) { + RenderText* renderText = (RenderText*) renderer; + InlineTextBox *textBox = renderText->firstTextBox(); + if (textBox == NULL) + continue; + bool hasClip = renderer->hasOverflowClip(); + size_t clipIndex = clipTracker.size(); + IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX); + if (hasClip || --clipIndex > 0) { + clipBounds = hasClip ? renderer->absoluteBoundingBoxRect() : + clipTracker.at(clipIndex).mBounds; // x, y fixup done by ConstructTextRect + } + if (ConstructTextRect((Text*) test, textBox, 0, INT_MAX, + x, y, focusBounds, clipBounds, result) == false) { + return false; + } + continue; + } + if (test->hasTagName(HTMLNames::imgTag)) { + IntRect bounds = test->getRect(); + if (AddPartRect(bounds, x, y, result, focusBounds) == false) + return false; + continue; + } + if (renderer->hasOverflowClip() == false) { + if (nodeIsAnchor && test->hasTagName(HTMLNames::divTag)) { + IntRect bounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by AddPartRect + int left = bounds.x() + renderer->paddingLeft() + + renderer->borderLeft(); + int top = bounds.y() + renderer->paddingTop() + + renderer->borderTop(); + int right = bounds.right() - renderer->paddingRight() + - renderer->borderRight(); + int bottom = bounds.bottom() - renderer->paddingBottom() + - renderer->borderBottom(); + if (left >= right || top >= bottom) + continue; + bounds = IntRect(left, top, right - left, bottom - top); + if (AddPartRect(bounds, x, y, result, focusBounds) == false) + return false; + } + continue; + } + Node* lastChild = test->lastChild(); + if (lastChild == NULL) + continue; + clipTracker.grow(clipTracker.size() + 1); + ClipColumnTracker& clip = clipTracker.last(); + clip.mBounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by ConstructTextRect + clip.mLastChild = OneAfter(lastChild); + clip.mNode = test; + } while (test != last && (test = test->traverseNextNode()) != NULL); + } + if (result->size() == 0) { + if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) + return false; + if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) + return false; + result->append(bounds); + *focusBounds = bounds; + } + return true; +} + +static inline bool isNotSpace(UChar c) +{ + return c <= 0x7F ? 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->string(); + const UChar* chars = string->characters(); + int renderX, renderY; + renderText->absolutePosition(renderX, renderY); + do { + int textBoxStart = textBox->start(); + int textBoxEnd = textBoxStart + textBox->len(); + if (textBoxEnd <= start) + continue; + if (textBoxEnd > relEnd) + textBoxEnd = relEnd; + IntRect bounds = textBox->selectionRect(renderX, renderY, + 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 (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/WebCore/platform/android/nav/CacheBuilder.h b/WebCore/platform/android/nav/CacheBuilder.h new file mode 100644 index 0000000..de5d153 --- /dev/null +++ b/WebCore/platform/android/nav/CacheBuilder.h @@ -0,0 +1,251 @@ +/* libs/WebKitLib/WebKit/platform/bridge/android/CacheBuilder.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CacheBuilder_H +#define CacheBuilder_H + +#include "CachedDebug.h" +#include "CachedNodeType.h" +#include "IntRect.h" +#include "PlatformString.h" +#include "TextDirection.h" +#include "wtf/Vector.h" + +#define NAVIGATION_MAX_PHONE_LENGTH 14 + +namespace android { + class CachedFrame; + class CachedNode; + class CachedRoot; +} + +namespace WebCore { + +class Document; +class Frame; +class FrameAndroid; +class InlineTextBox; +class Node; +class PlatformGraphicsContext; +class RenderFlow; +class RenderObject; +class RenderLayer; +class Text; + +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 = android::ALL_CACHEDNODETYPES; } + void buildCache(android::CachedRoot* root); + static bool ConstructPartRects(Node* node, const IntRect& bounds, + IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result); + Node* currentFocus() const; + void disallowAddressDetection() { mAllowableTypes = (android::CachedNodeType) ( + mAllowableTypes & ~android::ADDRESS_CACHEDNODETYPE); } + void disallowEmailDetection() { mAllowableTypes = (android::CachedNodeType) ( + mAllowableTypes & ~android::EMAIL_CACHEDNODETYPE); } + void disallowPhoneDetection() { mAllowableTypes = (android::CachedNodeType) ( + mAllowableTypes & ~android::PHONE_CACHEDNODETYPE); } + static FoundState FindAddress(const UChar* , unsigned length, int* start, int* end); + Node* findByCenter(int x, int y) const; + static void GetGlobalOffset(Frame* , int* x, int * y); + static void GetGlobalOffset(Node* , int* x, int * y); + bool outOfDate(); + void setLastFocus(Node* ); + bool validNode(void* framePtr, void* nodePtr) const; +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]; + android::CachedNodeType mStoreType; + 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* 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; + }; + struct ClipColumnTracker { + IntRect mBounds; + Node* mLastChild; + Node* mNode; + WTF::Vector<IntRect>* mColumns; + int mColumnGap; + TextDirection mDirection; + bool mHasClip; + }; + struct Tracker { + int mCachedNodeIndex; + Node* mLastChild; + Node* mParentLastChild; + bool mSomeParentTakesFocus; + }; + void adjustForColumns(const ClipColumnTracker& track, + android::CachedNode* node, IntRect* bounds); + 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); + void BuildFrame(FrameAndroid* root, FrameAndroid* frame, + android::CachedRoot* cachedRoot, android::CachedFrame* cachedFrame); + bool CleanUpContainedNodes(android::CachedFrame* cachedFrame, + const Tracker* 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 FrameAndroid* FrameAnd(CacheBuilder* focusNav); + static FrameAndroid* 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* , android::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(android::CachedFrame* ); + Node* tryFocus(Direction direction); + Node* trySegment(Direction direction, int mainStart, int mainEnd); + Node* mLastKnownFocus; + IntRect mLastKnownFocusBounds; + android::CachedNodeType mAllowableTypes; +#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); + int flowBoxes(RenderFlow* flow, int ifIndex, int indent); + void flush(); + FrameAndroid* 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 renderTree(RenderObject* , int indent, Node* , int count); + 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/WebCore/platform/android/nav/CachedDebug.h b/WebCore/platform/android/nav/CachedDebug.h new file mode 100644 index 0000000..14b6fad --- /dev/null +++ b/WebCore/platform/android/nav/CachedDebug.h @@ -0,0 +1,75 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CachedDebug_H +#define CachedDebug_H + +#ifndef DUMP_NAV_CACHE +#ifdef NDEBUG +#define DUMP_NAV_CACHE 0 +#else +#define DUMP_NAV_CACHE 1 +#endif +#endif + +#ifndef DEBUG_NAV_UI +#ifdef NDEBUG +#define DEBUG_NAV_UI 0 +#else +#define DEBUG_NAV_UI 1 +#endif +#endif + +#if DEBUG_NAV_UI +#define DBG_NAV_LOG(message) LOGD("%s %s", __FUNCTION__, message) +#define DBG_NAV_LOGD(format, ...) LOGD("%s " format, __FUNCTION__, __VA_ARGS__) +#define DBG_NAV_LOGD_ONCE(format, ...) \ +do { \ + static uint32_t savedTime = 0; \ + uint32_t curTime = SkTime::GetMSecs(); \ + uint32_t tempSavedTime = savedTime; \ + savedTime = curTime; \ + if (curTime - tempSavedTime < 100) \ + break; \ + LOGD("%s " format, __FUNCTION__, __VA_ARGS__); \ +} while (false) +#define DEBUG_NAV_UI_LOGD(...) LOGD(__VA_ARGS__) +#else +#define DBG_NAV_LOG(message) ((void)0) +#define DBG_NAV_LOGD(format, ...) ((void)0) +#define DBG_NAV_LOGD_ONCE(format, ...) ((void)0) +#define DEBUG_NAV_UI_LOGD(...) ((void)0) +#endif + +#if DUMP_NAV_CACHE != 0 && !defined DUMP_NAV_CACHE_USING_PRINTF && defined NDEBUG +#define DUMP_NAV_CACHE_USING_PRINTF +#endif + +#if DUMP_NAV_CACHE +#ifdef DUMP_NAV_CACHE_USING_PRINTF +#include <stdio.h> +extern FILE* gNavCacheLogFile; +#define NAV_CACHE_LOG_FILE "/data/data/com.android.browser/navlog" +#define DUMP_NAV_LOGD(...) do { if (gNavCacheLogFile) \ + fprintf(gNavCacheLogFile, __VA_ARGS__); else LOGD(__VA_ARGS__); } while (false) +#else +#define DUMP_NAV_LOGD(...) LOGD(__VA_ARGS__) +#endif +#else +#define DUMP_NAV_LOGD(...) ((void)0) +#endif + +#endif diff --git a/WebCore/platform/android/nav/CachedFrame.cpp b/WebCore/platform/android/nav/CachedFrame.cpp new file mode 100644 index 0000000..657670d --- /dev/null +++ b/WebCore/platform/android/nav/CachedFrame.cpp @@ -0,0 +1,1303 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "CachedPrefix.h" +#include "CachedHistory.h" +#include "CachedNode.h" +#include "CachedRoot.h" + +#include "CachedFrame.h" + +#define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning + +#define MIN_OVERLAP 3 // if rects overlap by 2 pixels or fewer, treat them as non-intersecting + +namespace android { + +bool CachedFrame::CheckBetween(Direction direction, const WebCore::IntRect& bestRect, + const WebCore::IntRect& prior, WebCore::IntRect* result) +{ + int left, top, width, height; + if (direction & UP_DOWN) { + top = direction == UP ? bestRect.bottom() : prior.bottom(); + int bottom = direction == UP ? prior.y() : bestRect.y(); + height = bottom - top; + if (height < 0) + return false; + left = prior.x(); + int testLeft = bestRect.x(); + if (left > testLeft) + left = testLeft; + int right = prior.right(); + int testRight = bestRect.right(); + if (right < testRight) + right = testRight; + width = right - left; + } else { + left = direction == LEFT ? bestRect.right() : prior.right(); + int right = direction == LEFT ? prior.x() : bestRect.x(); + width = right - left; + if (width < 0) + return false; + top = prior.y(); + int testTop = bestRect.y(); + if (top > testTop) + top = testTop; + int bottom = prior.bottom(); + int testBottom = bestRect.bottom(); + if (bottom < testBottom) + bottom = testBottom; + height = bottom - top; + } + *result = WebCore::IntRect(left, top, width, height); + return true; +} + +bool CachedFrame::checkBetween(BestData* best, Direction direction) +{ + const WebCore::IntRect& bestRect = best->bounds(); + BestData test; + test.mDistance = INT_MAX; + test.mNode = NULL; + int index = direction; + int limit = index + DIRECTION_COUNT; + do { + WebCore::IntRect edges; + Direction check = (Direction) (index & DIRECTION_MASK); + if (CheckBetween(check, bestRect, + history()->priorBounds(), &edges) == false) + continue; + WebCore::IntRect clip = mRoot->scrolledBounds(); + clip.intersect(edges); + if (clip.isEmpty()) + continue; + findClosest(&test, direction, check, &clip); + if (test.mNode == NULL) + continue; + if (direction == check) + break; + } while (++index < limit); + if (test.mNode == NULL) + return false; + *best = test; + return true; +} + +bool CachedFrame::checkVisited(const CachedNode* node, Direction direction) const +{ + return history()->checkVisited(node, direction); +} + +void CachedFrame::clearFocus() +{ + if (mFocus < 0) + return; + CachedNode& focus = mCachedNodes[mFocus]; + focus.clearFocus(this); + mFocus = -1; +} + +// the thing that sucks is that when you're on a link, you want to navigate next door to a link just like this one, but can't make it +// so with all my other sucky compares, maybe there needs to be one that prefers links that are aligned with the current focus... + +// returns 0 if test is preferable to best, 1 if not preferable, or -1 if unknown +int CachedFrame::compare(BestData& testData, const BestData& bestData, const CachedNode* focus) const +{ +// start here; + // if the test minor axis line intersects the line segment between focus center and best center, choose it + // give more weight to exact major axis alignment (rows, columns) + if (testData.mInNav != bestData.mInNav) { + if (bestData.mInNav) { + testData.mNode->setCondition(CachedNode::IN_FOCUS); + return REJECT_TEST; + } + return TEST_IS_BEST; + } + if (testData.mInNav) { + if (bestData.mMajorDelta < testData.mMajorDelta) { + testData.mNode->setCondition(CachedNode::CLOSER_IN_FOCUS); + return REJECT_TEST; + } + if (testData.mMajorDelta < bestData.mMajorDelta) + return TEST_IS_BEST; + } + if (testData.mMajorDelta < 0 && bestData.mMajorDelta >= 0) { + testData.mNode->setCondition(CachedNode::FURTHER); + return REJECT_TEST; + } + if ((testData.mMajorDelta ^ bestData.mMajorDelta) < 0) // one above, one below (or one left, one right) + return TEST_IS_BEST; +// SkFixed focusMultiplier = SK_Fixed1; +// if (focus != NULL) { +// if (testData.mMajorDelta < bestData.mMajorDelta) { +// // use bestData.mMajorDelta, +// } else if (bestData.mMajorDelta < testData.mMajorDelta) { +// +// } + bool bestInWorking = bestData.inOrSubsumesWorking(); + bool testInWorking = testData.inOrSubsumesWorking(); + if (bestInWorking && testData.mWorkingOutside && testData.mNavOutside) { + testData.mNode->setCondition(CachedNode::IN_WORKING); + return REJECT_TEST; + } + if (testInWorking && bestData.mWorkingOutside && bestData.mNavOutside) + return TEST_IS_BEST; + bool bestInNav = directionChange() && bestData.inOrSubsumesNav(); + bool testInNav = directionChange() && testData.inOrSubsumesNav(); + if (bestInWorking == false && testInWorking == false) { + if (bestInNav && testData.mNavOutside) { + testData.mNode->setCondition(CachedNode::IN_UMBRA); + return REJECT_TEST; + } + if (testInNav && bestData.mNavOutside) + return TEST_IS_BEST; + } +#if 01 // hopefully butt test will remove need for this + if (testData.mFocusChild != bestData.mFocusChild) { + if (bestData.mFocusChild) { + testData.mNode->setCondition(CachedNode::IN_FOCUS_CHILDREN); + return REJECT_TEST; + } + return TEST_IS_BEST; + } +#endif + bool bestTestIn = (bestInWorking || bestInNav) && (testInWorking || testInNav); + bool testOverlap = bestTestIn || (testData.mWorkingOverlap != 0 && bestData.mWorkingOverlap == 0); + bool bestOverlap = bestTestIn || (testData.mWorkingOverlap == 0 && bestData.mWorkingOverlap != 0); +#if 01 // this isn't working? + if (testOverlap == bestOverlap) { + if (bestData.mMajorButt < 10 && testData.mMajorButt >= 40) { + testData.mNode->setCondition(CachedNode::BUTTED_UP); + return REJECT_TEST; + } + if (testData.mMajorButt < 10 && bestData.mMajorButt >= 40) + return TEST_IS_BEST; + } +#endif + if (bestOverlap && bestData.mMajorDelta < testData.mMajorDelta) { // choose closest major axis center + testData.mNode->setCondition(CachedNode::CLOSER); + return REJECT_TEST; + } + if (testOverlap && testData.mMajorDelta < bestData.mMajorDelta) + return TEST_IS_BEST; + if (bestOverlap && bestData.mMajorDelta2 < testData.mMajorDelta2) { + testData.mNode->setCondition(CachedNode::CLOSER_TOP); + return REJECT_TEST; + } + if (testOverlap && testData.mMajorDelta2 < bestData.mMajorDelta2) + return TEST_IS_BEST; +#if 01 + if (bestOverlap && ((bestData.mSideDistance <= 0 && testData.mSideDistance > 0) || + abs(bestData.mSideDistance) < abs(testData.mSideDistance))) { + testData.mNode->setCondition(CachedNode::LEFTMOST); + return REJECT_TEST; + } + if (testOverlap && ((testData.mSideDistance <= 0 && bestData.mSideDistance > 0) || + abs(testData.mSideDistance) < abs(bestData.mSideDistance))) + return TEST_IS_BEST; +// fix me : the following ASSERT fires -- not sure if this case should be handled or not +// ASSERT(bestOverlap == false && testOverlap == false); +#endif + SkFixed testMultiplier = testData.mWorkingOverlap > testData.mNavOverlap ? + testData.mWorkingOverlap : testData.mNavOverlap; + SkFixed bestMultiplier = bestData.mWorkingOverlap > bestData.mNavOverlap ? + bestData.mWorkingOverlap : bestData.mNavOverlap; + int testDistance = testData.mDistance; + int bestDistance = bestData.mDistance; +// start here; + // this fails if they're off by 1 + // try once again to implement sliding scale so that off by 1 is nearly like zero, + // and off by a lot causes sideDistance to have little or no effect + // try elliptical distance -- lengthen side contribution + // these ASSERTs should not fire, but do fire on mail.google.com + // can't debug yet, won't reproduce + ASSERT(testDistance >= 0); + ASSERT(bestDistance >= 0); + testDistance += testDistance; // multiply by 2 + testDistance *= testDistance; + bestDistance += bestDistance; // multiply by 2 + bestDistance *= bestDistance; + int side = testData.mSideDistance; + int negative = side < 0 && bestData.mSideDistance > 0; + side *= side; + if (negative) + side = -side; + testDistance += side; + side = bestData.mSideDistance; + negative = side < 0 && testData.mSideDistance > 0; + side *= side; + if (negative) + side = -side; + bestDistance += side; + if (testMultiplier > (SK_Fixed1 >> 1) || bestMultiplier > (SK_Fixed1 >> 1)) { // considerable working overlap? + testDistance = SkFixedMul(testDistance, bestMultiplier); + bestDistance = SkFixedMul(bestDistance, testMultiplier); + } + if (bestDistance < testDistance) { + testData.mNode->setCondition(CachedNode::CLOSER_OVERLAP); + return REJECT_TEST; + } + if (testDistance < bestDistance) + return TEST_IS_BEST; +#if 0 + int distance = testData.mDistance + testData.mSideDistance; + int best = bestData.mDistance + bestData.mSideDistance; + if (distance > best) { + testData.mNode->setCondition(CachedNode::CLOSER_RAW_DISTANCE); + return REJECT_TEST; + } + else if (distance < best) + return TEST_IS_BEST; + best = bestData.mSideDistance; + if (testData.mSideDistance > best) { + testData.mNode->setCondition(CachedNode::SIDE_DISTANCE); + return REJECT_TEST; + } + if (testData.mSideDistance < best) + return TEST_IS_BEST; +#endif + if (testData.mPreferred < bestData.mPreferred) { + testData.mNode->setCondition(CachedNode::PREFERRED); + return REJECT_TEST; + } + if (testData.mPreferred > bestData.mPreferred) + return TEST_IS_BEST; + return UNDECIDED; +} + +bool CachedFrame::containsFrame(const CachedFrame* test) const +{ + if (this == test) + return true; + for (const CachedFrame* frame = mCachedFrames.begin(); + frame != mCachedFrames.end(); frame++) { + if (frame->containsFrame(test)) + return true; + } + return false; +} + +const CachedNode* CachedFrame::currentFocus(const CachedFrame** framePtr) const +{ + if (framePtr) + *framePtr = this; + if (mFocus < 0) + return NULL; + const CachedNode* result = &mCachedNodes[mFocus]; + const CachedFrame* frame = hasFrame(result); + if (frame != NULL) + return frame->currentFocus(framePtr); + (const_cast<CachedNode*>(result))->fixUpFocusRects(); + return result; +} + +bool CachedFrame::directionChange() const +{ + return history()->directionChange(); +} + +#ifdef BROWSER_DEBUG +CachedNode* CachedFrame::find(WebCore::Node* node) // !!! probably debugging only +{ + for (CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++) + if (node == test->webCoreNode()) + return test; + for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end(); + frame++) { + CachedNode* result = frame->find(node); + if (result != NULL) + return result; + } + return NULL; +} +#endif + +const CachedNode* CachedFrame::findBestAt(const WebCore::IntRect& rect, + int* best, const CachedNode** directHit, const CachedFrame** framePtr, int* x, int* y) const +{ + const CachedNode* result = NULL; + int rectWidth = rect.width(); + WebCore::IntPoint center = WebCore::IntPoint(rect.x() + (rectWidth >> 1), + rect.y() + (rect.height() >> 1)); + mRoot->setupScrolledBounds(); + for (const CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++) { + if (test->disabled()) + continue; + size_t parts = test->navableRects(); + BestData testData; + testData.mNode = test; + testData.mMouseBounds = testData.mNodeBounds = test->getBounds(); + bool checkForHidden = true; + for (size_t part = 0; part < parts; part++) { + if (test->focusRings().at(part).intersects(rect)) { + if (checkForHidden && mRoot->maskIfHidden(&testData) == true) + break; + checkForHidden = false; + WebCore::IntRect testRect = test->focusRings().at(part); + testRect.intersect(testData.mMouseBounds); + if (testRect.contains(center)) { + // We have a direct hit. + if (*directHit == NULL) { + *directHit = test; + *framePtr = this; + *x = center.x(); + *y = center.y(); + } else { + // We have hit another one before + const CachedNode* d = *directHit; + if (d->getBounds().contains(testRect)) { + // This rectangle is inside the other one, so it is + // the best one. + *directHit = test; + *framePtr = this; + } + } + } + if (NULL != *directHit) { + // If we have a direct hit already, there is no need to + // calculate the distances, or check the other focusring parts + break; + } + WebCore::IntRect both = rect; + int smaller = testRect.width() < testRect.height() ? + testRect.width() : testRect.height(); + smaller -= rectWidth; + int inset = smaller < rectWidth ? smaller : rectWidth; + inset >>= 1; // inflate doubles the width decrease + if (inset > 1) + both.inflate(1 - inset); + both.intersect(testRect); + if (both.isEmpty()) + continue; + WebCore::IntPoint testCenter = WebCore::IntPoint(testRect.x() + + (testRect.width() >> 1), testRect.y() + (testRect.height() >> 1)); + int dx = testCenter.x() - center.x(); + int dy = testCenter.y() - center.y(); + int distance = dx * dx + dy * dy; + if (*best > distance) { + *best = distance; + result = test; + *framePtr = this; + *x = both.x() + (both.width() >> 1); + *y = both.y() + (both.height() >> 1); + } + } + } + } + for (const CachedFrame* frame = mCachedFrames.begin(); + frame != mCachedFrames.end(); frame++) { + const CachedNode* frameResult = frame->findBestAt(rect, best, directHit, + framePtr, x, y); + if (NULL != frameResult) + result = frameResult; + } + if (NULL != *directHit) { + result = *directHit; + } + return result; +} + +const CachedFrame* CachedFrame::findBestFrameAt(int x, int y) const +{ + if (mLocalViewBounds.contains(x, y) == false) + return NULL; + const CachedFrame* result = this; + for (const CachedFrame* frame = mCachedFrames.begin(); + frame != mCachedFrames.end(); frame++) { + const CachedFrame* frameResult = frame->findBestFrameAt(x, y); + if (NULL != frameResult) + result = frameResult; + } + return result; +} + +const CachedNode* CachedFrame::findBestHitAt(const WebCore::IntRect& rect, + int* best, const CachedFrame** framePtr, int* x, int* y) const +{ + const CachedNode* result = NULL; + int rectWidth = rect.width(); + WebCore::IntPoint center = WebCore::IntPoint(rect.x() + (rectWidth >> 1), + rect.y() + (rect.height() >> 1)); + mRoot->setupScrolledBounds(); + for (const CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++) { + if (test->disabled()) + continue; + const WebCore::IntRect& testRect = test->hitBounds(); + if (testRect.intersects(rect) == false) + continue; + BestData testData; + testData.mNode = test; + testData.mMouseBounds = testData.mNodeBounds = testRect; + if (mRoot->maskIfHidden(&testData) == true) + continue; + const WebCore::IntRect& bounds = testData.mMouseBounds; + WebCore::IntPoint testCenter = WebCore::IntPoint(bounds.x() + + (bounds.width() >> 1), bounds.y() + (bounds.height() >> 1)); + int dx = testCenter.x() - center.x(); + int dy = testCenter.y() - center.y(); + int distance = dx * dx + dy * dy; + if (*best <= distance) + continue; + *best = distance; + result = test; + *framePtr = this; + const WebCore::IntRect& focusRect = test->focusRings().at(0); + *x = focusRect.x() + (focusRect.width() >> 1); + *y = focusRect.y() + (focusRect.height() >> 1); + } + for (const CachedFrame* frame = mCachedFrames.begin(); + frame != mCachedFrames.end(); frame++) { + const CachedNode* frameResult = frame->findBestHitAt(rect, best, + framePtr, x, y); + if (NULL != frameResult) + result = frameResult; + } + return result; +} + +void CachedFrame::findClosest(BestData* bestData, Direction originalDirection, + Direction direction, WebCore::IntRect* clip) const +{ + const CachedNode* test = mCachedNodes.begin(); + while ((test = test->traverseNextNode()) != NULL) { + const CachedFrame* child = hasFrame(test); + if (child != NULL) { + const CachedNode* childDoc = child->validDocument(); + if (childDoc == NULL) + continue; + child->findClosest(bestData, originalDirection, direction, clip); + } + if (test->noSecondChance()) + continue; + if (test->isFocusable(*clip) == false) + continue; + if (checkVisited(test, originalDirection) == false) + continue; + size_t partMax = test->navableRects(); + for (size_t part = 0; part < partMax; part++) { + WebCore::IntRect testBounds = test->focusRings().at(part); + if (clip->intersects(testBounds) == false) + continue; + if (clip->contains(testBounds) == false) { + if (direction & UP_DOWN) { +// if (testBounds.x() > clip->x() || testBounds.right() < clip->right()) +// continue; + testBounds.setX(clip->x()); + testBounds.setWidth(clip->width()); + } else { +// if (testBounds.y() > clip->y() || testBounds.bottom() < clip->bottom()) +// continue; + testBounds.setY(clip->y()); + testBounds.setHeight(clip->height()); + } + if (clip->contains(testBounds) == false) + continue; + } + int distance; + // seems like distance for UP for instance needs to be 'test top closest to + // clip bottom' -- keep the old code but try this instead + switch (direction) { +#if 0 + case LEFT: distance = testBounds.x() - clip->x(); break; + case RIGHT: distance = clip->right() - testBounds.right(); break; + case UP: distance = testBounds.y() - clip->y(); break; + case DOWN: distance = clip->bottom() - testBounds.bottom(); break; +#else + case LEFT: distance = clip->right() - testBounds.x(); break; + case RIGHT: distance = testBounds.right() - clip->x(); break; + case UP: distance = clip->bottom() - testBounds.y(); break; + case DOWN: distance = testBounds.bottom() - clip->y(); break; +#endif + default: + distance = 0; ASSERT(0); + } + if (distance < bestData->mDistance) { + bestData->mNode = test; + bestData->mFrame = this; + bestData->mDistance = distance; + bestData->mMouseBounds = bestData->mNodeBounds = + test->focusRings().at(part); + CachedHistory* cachedHistory = history(); + switch (direction) { + case LEFT: + bestData->setLeftDirection(cachedHistory); + break; + case RIGHT: + bestData->setRightDirection(cachedHistory); + break; + case UP: + bestData->setUpDirection(cachedHistory); + break; + case DOWN: + bestData->setDownDirection(cachedHistory); + break; + default: + ASSERT(0); + } + } + } + } +} + +bool CachedFrame::finishInit() +{ + CachedNode* lastCached = lastNode(); + lastCached->setLast(); + CachedFrame* child = mCachedFrames.begin(); + while (child != mCachedFrames.end()) { + child->mParent = this; + if (child->finishInit()) + setFocusIndex(child->indexInParent()); + child++; + } + return focusIndex() > 0; +} + +const CachedNode* CachedFrame::frameDown(const CachedNode* test, const CachedNode* limit, BestData* bestData, + const CachedNode* focus) const +{ + BestData originalData = *bestData; + do { + if (moveInFrame(&CachedFrame::frameDown, test, bestData, focus)) + continue; + BestData testData; + if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST) + continue; + if (checkVisited(test, DOWN) == false) + continue; + size_t parts = test->navableRects(); + for (size_t part = 0; part < parts; part++) { + testData.mNodeBounds = test->focusRings().at(part); + if (testData.setDownDirection(history())) + continue; + int result = framePartCommon(testData, test, bestData, focus); + if (result == REJECT_TEST) + continue; + if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger + BestData innerData = testData; + frameDown(document(), test, &innerData, focus); + if (checkVisited(innerData.mNode, DOWN)) { + *bestData = innerData; + continue; + } + } + if (checkVisited(test, DOWN)) + *bestData = testData; + } + } while ((test = test->traverseNextNode()) != limit); + ASSERT(focus == NULL || bestData->mNode != focus); + // does the best contain something (or, is it contained by an area which is not the focus?) + // if so, is the conainer/containee should have been chosen, but wasn't -- so there's a better choice + // in the doc list prior to this choice + // + return bestData->mNode; +} + +const CachedNode* CachedFrame::frameLeft(const CachedNode* test, const CachedNode* limit, BestData* bestData, + const CachedNode* focus) const +{ + BestData originalData = *bestData; + do { + if (moveInFrame(&CachedFrame::frameLeft, test, bestData, focus)) + continue; + BestData testData; + if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST) + continue; + if (checkVisited(test, LEFT) == false) + continue; + size_t parts = test->navableRects(); + for (size_t part = 0; part < parts; part++) { + testData.mNodeBounds = test->focusRings().at(part); + if (testData.setLeftDirection(history())) + continue; + int result = framePartCommon(testData, test, bestData, focus); + if (result == REJECT_TEST) + continue; + if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger + BestData innerData = testData; + frameLeft(document(), test, &innerData, focus); + if (checkVisited(innerData.mNode, LEFT)) { + *bestData = innerData; + continue; + } + } + if (checkVisited(test, LEFT)) + *bestData = testData; + } + } while ((test = test->traverseNextNode()) != limit); // FIXME ??? left and up should use traversePreviousNode to choose reverse document order + ASSERT(focus == NULL || bestData->mNode != focus); + return bestData->mNode; +} + +int CachedFrame::frameNodeCommon(BestData& testData, const CachedNode* test, BestData* bestData, BestData* originalData, + const CachedNode* focus) const +{ + testData.mFrame = this; + testData.mNode = test; + test->clearCondition(); + if (test->disabled()) { + testData.mNode->setCondition(CachedNode::DISABLED); + return REJECT_TEST; + } + if (mRoot->scrolledBounds().intersects(test->bounds()) == false) { + testData.mNode->setCondition(CachedNode::FOCUSABLE); + return REJECT_TEST; + } +// if (isFocusable(test, &testData.mNodeBounds, walk) == false) { +// testData.mNode->setCondition(CachedNode::FOCUSABLE); +// return REJECT_TEST; +// } +// + if (test == focus) { + testData.mNode->setCondition(CachedNode::NOT_FOCUS_NODE); + return REJECT_TEST; + } +// if (test->bounds().contains(history()->focusBounds())) { +// testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS); +// return REJECT_TEST; +// } + void* par = focus ? focus->parentGroup() : NULL; + testData.mFocusChild = test->parentGroup() == par; +#if 0 // not debugged + if (focus && focus->hasMouseOver() && test->hasMouseOver() == false && + focus->bounds().contains(test->bounds())) + return REJECT_TEST; +#endif + if (bestData->mNode == NULL) + return TEST_IS_BEST; +#if 0 // not debugged + if (focus && focus->hasMouseOver() && test->hasMouseOver() == false && + focus->bounds().contains(test->bounds())) + return REJECT_TEST; + if (test->hasMouseOver() != bestData->mNode->hasMouseOver()) { + if (test->hasMouseOver()) { + if (test->bounds().contains(bestData->mNode->bounds())) { + const_cast<CachedNode*>(bestData->mNode)->setDisabled(true); + bestData->mNode = NULL; // force part tests to be ignored, yet still set up remaining test data for later comparison + return TEST_IS_BEST; + } + } else { + if (bestData->mNode->bounds().contains(test->bounds())) { + test->setCondition(CachedNode::ANCHOR_IN_ANCHOR); + return REJECT_TEST; + } + } + } +#endif + if (focus && testData.mNode->parentIndex() != bestData->mNode->parentIndex()) { + int focusParentIndex = focus->parentIndex(); + if (focusParentIndex >= 0) { + if (bestData->mNode->parentIndex() == focusParentIndex) + return REJECT_TEST; + if (testData.mNode->parentIndex() == focusParentIndex) + return TEST_IS_BEST; + } + } + if (testData.mNode->parent() == bestData->mNode) { + testData.mNode->setCondition(CachedNode::CHILD); + return REJECT_TEST; + } + if (testData.mNode == bestData->mNode->parent()) + return TEST_IS_BEST; + int testInBest = testData.isContainer(bestData); /* -1 pick best over test, 0 no containership, 1 pick test over best */ + if (testInBest == 1) { + if (test->isArea() || bestData->mNode->isArea()) + return UNDECIDED; + bestData->mNode = NULL; // force part tests to be ignored, yet still set up remaining test data for later comparisons + return TEST_IS_BEST; + } + if (testInBest == -1) { + testData.mNode->setCondition(CachedNode::OUTSIDE_OF_BEST); + return REJECT_TEST; + } + if (originalData->mNode != NULL) { // test is best case + testInBest = testData.isContainer(originalData); + if (testInBest == -1) { /* test is inside best */ + testData.mNode->setCondition(CachedNode::OUTSIDE_OF_ORIGINAL); + return REJECT_TEST; + } + } + return UNDECIDED; +} + +int CachedFrame::framePartCommon(BestData& testData, + const CachedNode* test, BestData* bestData, const CachedNode* focus) const +{ + if (testData.mNodeBounds.contains(history()->focusBounds())) { + testData.mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS); + return REJECT_TEST; + } + testData.setDistances(); + if (bestData->mNode != NULL) { + int compared = compare(testData, *bestData, focus); + if (compared == 0 && test->isArea() == false && bestData->mNode->isArea() == false) + goto pickTest; + if (compared >= 0) + return compared; + } +pickTest: + return -1; // pick test +} + +const CachedNode* CachedFrame::frameRight(const CachedNode* test, const CachedNode* limit, BestData* bestData, + const CachedNode* focus) const +{ + BestData originalData = *bestData; + do { + if (moveInFrame(&CachedFrame::frameRight, test, bestData, focus)) + continue; + BestData testData; + if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST) + continue; + if (checkVisited(test, RIGHT) == false) + continue; + size_t parts = test->navableRects(); + for (size_t part = 0; part < parts; part++) { + testData.mNodeBounds = test->focusRings().at(part); + if (testData.setRightDirection(history())) + continue; + int result = framePartCommon(testData, test, bestData, focus); + if (result == REJECT_TEST) + continue; + if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger + BestData innerData = testData; + frameRight(document(), test, &innerData, focus); + if (checkVisited(innerData.mNode, RIGHT)) { + *bestData = innerData; + continue; + } + } + if (checkVisited(test, RIGHT)) + *bestData = testData; + } + } while ((test = test->traverseNextNode()) != limit); + ASSERT(focus == NULL || bestData->mNode != focus); + return bestData->mNode; +} + +const CachedNode* CachedFrame::frameUp(const CachedNode* test, const CachedNode* limit, BestData* bestData, + const CachedNode* focus) const +{ + BestData originalData = *bestData; + do { + if (moveInFrame(&CachedFrame::frameUp, test, bestData, focus)) + continue; + BestData testData; + if (frameNodeCommon(testData, test, bestData, &originalData, focus) == REJECT_TEST) + continue; + if (checkVisited(test, UP) == false) + continue; + size_t parts = test->navableRects(); + for (size_t part = 0; part < parts; part++) { + testData.mNodeBounds = test->focusRings().at(part); + if (testData.setUpDirection(history())) + continue; + int result = framePartCommon(testData, test, bestData, focus); + if (result == REJECT_TEST) + continue; + if (result == 0 && limit == NULL) { // retry all data up to this point, since smaller may have replaced node preferable to larger + BestData innerData = testData; + frameUp(document(), test, &innerData, focus); + if (checkVisited(innerData.mNode, UP)) { + *bestData = innerData; + continue; + } + } + if (checkVisited(test, UP)) + *bestData = testData; + } + } while ((test = test->traverseNextNode()) != limit); // FIXME ??? left and up should use traversePreviousNode to choose reverse document order + ASSERT(focus == NULL || bestData->mNode != focus); + return bestData->mNode; +} + +const CachedFrame* CachedFrame::hasFrame(const CachedNode* node) const +{ + return node->isFrame() ? &mCachedFrames[node->childFrameIndex()] : NULL; +} + +CachedHistory* CachedFrame::history() const +{ + return mRoot->rootHistory(); +} + +void CachedFrame::init(const CachedRoot* root, int childFrameIndex, + WebCore::FrameAndroid* frame) +{ + mContents = WebCore::IntRect(0, 0, 0, 0); // fixed up for real in setData() + mLocalViewBounds = WebCore::IntRect(0, 0, 0, 0); + mViewBounds = WebCore::IntRect(0, 0, 0, 0); + mRoot = root; + mFocus = -1; + mFrame = frame; + mParent = NULL; // set up parents after stretchy arrays are set up + mIndex = childFrameIndex; +} + +int CachedFrame::minWorkingHorizontal() const +{ + return history()->minWorkingHorizontal(); +} + +int CachedFrame::minWorkingVertical() const +{ + return history()->minWorkingVertical(); +} + +int CachedFrame::maxWorkingHorizontal() const +{ + return history()->maxWorkingHorizontal(); +} + +int CachedFrame::maxWorkingVertical() const +{ + return history()->maxWorkingVertical(); +} + +bool CachedFrame::moveInFrame(MoveInDirection moveInDirection, + const CachedNode* test, BestData* bestData, + const CachedNode* focus) const +{ + const CachedFrame* frame = hasFrame(test); + if (frame == NULL) + return false; // if it's not a frame, let the caller have another swing at it + const CachedNode* childDoc = frame->validDocument(); + if (childDoc == NULL) + return true; + (frame->*moveInDirection)(childDoc, NULL, bestData, focus); + return true; +} + +const WebCore::IntRect& CachedFrame::_navBounds() const +{ + return history()->navBounds(); +} + +void CachedFrame::resetClippedOut() +{ + for (CachedNode* test = mCachedNodes.begin(); test != mCachedNodes.end(); test++) + { + if (test->clippedOut()) { + test->setDisabled(false); + test->setClippedOut(false); + } + } + for (CachedFrame* frame = mCachedFrames.begin(); frame != mCachedFrames.end(); + frame++) { + frame->resetClippedOut(); + } +} + +void CachedFrame::setData() +{ + if (this != mRoot) { + mViewBounds = mLocalViewBounds; + mViewBounds.intersect(mRoot->mViewBounds); + } + int x, y; + if (parent() == NULL) + x = y = 0; + else { + x = mLocalViewBounds.x(); + y = mLocalViewBounds.y(); + } + mContents.setX(x); + mContents.setY(y); + CachedFrame* child = mCachedFrames.begin(); + while (child != mCachedFrames.end()) { + child->setData(); + child++; + } +} + +bool CachedFrame::setFocus(WebCore::Frame* frame, WebCore::Node* node, + int x, int y) +{ + if (NULL == node) { + const_cast<CachedRoot*>(mRoot)->setCachedFocus(NULL, NULL); + return true; + } + if (mFrame != frame) { + for (CachedFrame* testF = mCachedFrames.begin(); testF != mCachedFrames.end(); + testF++) { + if (testF->setFocus(frame, node, x, y)) + return true; + } + DBG_NAV_LOGD("no frame frame=%p node=%p", frame, node); + return false; + } + bool first = true; + CachedNode const * const end = mCachedNodes.end(); + do { + for (CachedNode* test = mCachedNodes.begin(); test != end; test++) { + if (test->nodePointer() != node && first) + continue; + size_t partMax = test->navableRects(); + WTF::Vector<WebCore::IntRect>& focusRings = test->focusRings(); + for (size_t part = 0; part < partMax; part++) { + const WebCore::IntRect& testBounds = focusRings.at(part); + if (testBounds.contains(x, y) == false) + continue; + if (test->isFocus()) { + DBG_NAV_LOGD("already set? test=%d frame=%p node=%p x=%d y=%d", + test->index(), frame, node, x, y); + return true; + } + const_cast<CachedRoot*>(mRoot)->setCachedFocus(this, test); + return true; + } + } + DBG_NAV_LOGD("moved? frame=%p node=%p x=%d y=%d", frame, node, x, y); + } while ((first ^= true) == false); +failed: + DBG_NAV_LOGD("no match frame=%p node=%p", frame, node); + return false; +} + +const CachedNode* CachedFrame::validDocument() const +{ + const CachedNode* doc = document(); + return doc != NULL && mViewBounds.isEmpty() == false ? doc : NULL; +} + +bool CachedFrame::BestData::canBeReachedByAnotherDirection() +{ + if (mMajorButt > -MIN_OVERLAP) + return false; + mMajorButt = -mMajorButt; + return mNavOutside; +} + +int CachedFrame::BestData::isContainer(CachedFrame::BestData* other) +{ + int _x = x(); + int otherRight = other->right(); + if (_x >= otherRight) + return 0; // does not intersect + int _y = y(); + int otherBottom = other->bottom(); + if (_y >= otherBottom) + return 0; // does not intersect + int _right = right(); + int otherX = other->x(); + if (otherX >= _right) + return 0; // does not intersect + int _bottom = bottom(); + int otherY = other->y(); + if (otherY >= _bottom) + return 0; // does not intersect + int intoX = otherX - _x; + int intoY = otherY - _y; + int intoRight = otherRight - _right; + int intoBottom = otherBottom - _bottom; + bool contains = intoX >= 0 && intoY >= 0 && intoRight <= 0 && intoBottom <= 0; + if (contains && mNode->partRectsContains(other->mNode)) { +// if (mIsArea == false && hasMouseOver()) +// other->mMouseOver = mNode; + return mNode->isArea() ? 1 : -1; + } + bool containedBy = intoX <= 0 && intoY <= 0 && intoRight >= 0 && intoBottom >= 0; + if (containedBy && other->mNode->partRectsContains(mNode)) { +// if (other->mIsArea == false && other->hasMouseOver()) +// mMouseOver = other->mNode; + return other->mNode->isArea() ? -1 : 1; + } + return 0; +} + +// distance scale factor factor as a 16.16 scalar +SkFixed CachedFrame::BestData::Overlap(int span, int left, int right) +{ + unsigned result; + if (left > 0 && left < span && right > span) + result = (unsigned) left; + else if (right > 0 && right < span && left > span) + result = (unsigned) right; + else if (left > 0 && right > 0) + return SK_Fixed1; + else + return 0; + result = (result << 16) / (unsigned) span; // linear proportion, always less than fixed 1 + return (SkFixed) result; +// !!! experiment with weight -- enable if overlaps are preferred too much +// or reverse weighting if overlaps are preferred to little +// return (SkFixed) (result * result >> 16); // but fall off with square +} + +void CachedFrame::BestData::setDistances() +{ + mDistance = abs(mMajorDelta); + int sideDistance = mWorkingDelta; + if (mWorkingOverlap < SK_Fixed1) { + if (mPreferred > 0) + sideDistance = mWorkingDelta2; + } else if (sideDistance >= 0 && mWorkingDelta2 >=- 0) + sideDistance = 0; + else { + ASSERT(sideDistance <= 0 && mWorkingDelta2 <= 0); + if (sideDistance < mWorkingDelta2) + sideDistance = mWorkingDelta2; + } + // if overlap, smaller abs mWorkingDelta is better, smaller abs majorDelta is better + // if not overlap, positive mWorkingDelta is better + mSideDistance = sideDistance; +} + +bool CachedFrame::BestData::setDownDirection(const CachedHistory* history) +{ + const WebCore::IntRect& navBounds = history->navBounds(); + mMajorButt = mNodeBounds.y() - navBounds.bottom(); + int testX = mNodeBounds.x(); + int testRight = mNodeBounds.right(); + setNavOverlap(navBounds.width(), navBounds.right() - testX, + testRight - navBounds.x()); + if (canBeReachedByAnotherDirection()) { + mNode->setCondition(CachedNode::BEST_DIRECTION); + return REJECT_TEST; + } + int inNavTop = mNodeBounds.y() - navBounds.y(); + mMajorDelta2 = inNavTop; + mMajorDelta = mMajorDelta2 + ((mNodeBounds.height() - + navBounds.height()) >> 1); + if (mMajorDelta2 <= 1 && mMajorDelta <= 1) { + mNode->setCondition(CachedNode::CENTER_FURTHER); // never move up or sideways + return REJECT_TEST; + } + int inNavBottom = navBounds.bottom() - mNodeBounds.bottom(); + setNavInclusion(testRight - navBounds.right(), navBounds.x() - testX); + bool subsumes = navBounds.height() > 0 && inOrSubsumesNav(); + if (inNavTop <= 0 && inNavBottom <= 0 && subsumes) { + mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS); + return REJECT_TEST; + } + int maxV = history->maxWorkingVertical(); + int minV = history->minWorkingVertical(); + setWorkingOverlap(testRight - testX, maxV - testX, testRight - minV); + setWorkingInclusion(testRight - maxV, minV - testX); + if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavBottom >= 0) { + mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER); + return REJECT_TEST; + } + mInNav = history->directionChange() && inNavTop >= 0 && + inNavBottom > 0 && subsumes; + return false; +} + +bool CachedFrame::BestData::setLeftDirection(const CachedHistory* history) +{ + const WebCore::IntRect& navBounds = history->navBounds(); + mMajorButt = navBounds.x() - mNodeBounds.right(); + int testY = mNodeBounds.y(); + int testBottom = mNodeBounds.bottom(); + setNavOverlap(navBounds.height(), navBounds.bottom() - testY, + testBottom - navBounds.y()); + if (canBeReachedByAnotherDirection()) { + mNode->setCondition(CachedNode::BEST_DIRECTION); + return REJECT_TEST; + } + int inNavRight = navBounds.right() - mNodeBounds.right(); + mMajorDelta2 = inNavRight; + mMajorDelta = mMajorDelta2 - ((navBounds.width() - + mNodeBounds.width()) >> 1); + if (mMajorDelta2 <= 1 && mMajorDelta <= 1) { + mNode->setCondition(CachedNode::CENTER_FURTHER); // never move right or sideways + return REJECT_TEST; + } + int inNavLeft = mNodeBounds.x() - navBounds.x(); + setNavInclusion(navBounds.y() - testY, testBottom - navBounds.bottom()); + bool subsumes = navBounds.width() > 0 && inOrSubsumesNav(); + if (inNavLeft <= 0 && inNavRight <= 0 && subsumes) { + mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS); + return REJECT_TEST; + } + int maxH = history->maxWorkingHorizontal(); + int minH = history->minWorkingHorizontal(); + setWorkingOverlap(testBottom - testY, maxH - testY, testBottom - minH); + setWorkingInclusion(minH - testY, testBottom - maxH); + if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavLeft >= 0) { + mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER); + return REJECT_TEST; + } + mInNav = history->directionChange() && inNavLeft >= 0 && + inNavRight > 0 && subsumes; /* both L/R in or out */ + return false; +} + +bool CachedFrame::BestData::setRightDirection(const CachedHistory* history) +{ + const WebCore::IntRect& navBounds = history->navBounds(); + mMajorButt = mNodeBounds.x() - navBounds.right(); + int testY = mNodeBounds.y(); + int testBottom = mNodeBounds.bottom(); + setNavOverlap(navBounds.height(), navBounds.bottom() - testY, + testBottom - navBounds.y()); + if (canBeReachedByAnotherDirection()) { + mNode->setCondition(CachedNode::BEST_DIRECTION); + return REJECT_TEST; + } + int inNavLeft = mNodeBounds.x() - navBounds.x(); + mMajorDelta2 = inNavLeft; + mMajorDelta = mMajorDelta2 + ((mNodeBounds.width() - + navBounds.width()) >> 1); + if (mMajorDelta2 <= 1 && mMajorDelta <= 1) { + mNode->setCondition(CachedNode::CENTER_FURTHER); // never move left or sideways + return REJECT_TEST; + } + int inNavRight = navBounds.right() - mNodeBounds.right(); + setNavInclusion(testBottom - navBounds.bottom(), navBounds.y() - testY); + bool subsumes = navBounds.width() > 0 && inOrSubsumesNav(); + if (inNavLeft <= 0 && inNavRight <= 0 && subsumes) { + mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS); + return REJECT_TEST; + } + int maxH = history->maxWorkingHorizontal(); + int minH = history->minWorkingHorizontal(); + setWorkingOverlap(testBottom - testY, testBottom - minH, maxH - testY); + setWorkingInclusion(testBottom - maxH, minH - testY); + if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavRight >= 0) { + mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER); + return REJECT_TEST; + } + mInNav = history->directionChange() && inNavLeft >= 0 && + inNavRight > 0 && subsumes; /* both L/R in or out */ + return false; +} + +bool CachedFrame::BestData::setUpDirection(const CachedHistory* history) +{ + const WebCore::IntRect& navBounds = history->navBounds(); + mMajorButt = navBounds.y() - mNodeBounds.bottom(); + int testX = mNodeBounds.x(); + int testRight = mNodeBounds.right(); + setNavOverlap(navBounds.width(), navBounds.right() - testX, + testRight - navBounds.x()); + if (canBeReachedByAnotherDirection()) { + mNode->setCondition(CachedNode::BEST_DIRECTION); + return REJECT_TEST; + } + int inNavBottom = navBounds.bottom() - mNodeBounds.bottom(); + mMajorDelta2 = inNavBottom; + mMajorDelta = mMajorDelta2 - ((navBounds.height() - + mNodeBounds.height()) >> 1); + if (mMajorDelta2 <= 1 && mMajorDelta <= 1) { + mNode->setCondition(CachedNode::CENTER_FURTHER); // never move down or sideways + return REJECT_TEST; + } + int inNavTop = mNodeBounds.y() - navBounds.y(); + setNavInclusion(navBounds.x() - testX, testRight - navBounds.right()); + bool subsumes = navBounds.height() > 0 && inOrSubsumesNav(); + if (inNavTop <= 0 && inNavBottom <= 0 && subsumes) { + mNode->setCondition(CachedNode::NOT_ENCLOSING_FOCUS); + return REJECT_TEST; + } + int maxV = history->maxWorkingVertical(); + int minV = history->minWorkingVertical(); + setWorkingOverlap(testRight - testX, testRight - minV, maxV - testX); + setWorkingInclusion(minV - testX, testRight - maxV); + if (mWorkingOverlap == 0 && mNavOverlap == 0 && inNavTop >= 0) { + mNode->setCondition(CachedNode::OVERLAP_OR_EDGE_FURTHER); + return REJECT_TEST; + } + mInNav = history->directionChange() && inNavTop >= 0 && + inNavBottom > 0 && subsumes; /* both L/R in or out */ + return false; +} + +void CachedFrame::BestData::setNavInclusion(int left, int right) +{ + // if left and right <= 0, test node is completely in umbra of focus + // prefer leftmost center + // if left and right > 0, test node subsumes focus + mNavDelta = left; + mNavDelta2 = right; +} + +void CachedFrame::BestData::setNavOverlap(int span, int left, int right) +{ + mNavOutside = left < MIN_OVERLAP || right < MIN_OVERLAP; // if left or right < 0, test node is not in umbra of focus + mNavOverlap = Overlap(span, left, right); // prefer smallest negative left +} + +void CachedFrame::BestData::setWorkingInclusion(int left, int right) +{ + mWorkingDelta = left; + mWorkingDelta2 = right; +} + +// distance scale factor factor as a 16.16 scalar +void CachedFrame::BestData::setWorkingOverlap(int span, int left, int right) +{ + mWorkingOutside = left < MIN_OVERLAP || right < MIN_OVERLAP; // if left or right < 0, test node is not in umbra of focus + mWorkingOverlap = Overlap(span, left, right); + mPreferred = left <= 0 ? 0 : left; +} + +#if DUMP_NAV_CACHE + +#define DEBUG_PRINT_RECT(prefix, debugName, field) \ + { const WebCore::IntRect& r = b->field; \ + DUMP_NAV_LOGD("%s DebugTestRect TEST%s_" #debugName "={%d, %d, %d, %d}; //" #field "\n", \ + prefix, mFrameName, r.x(), r.y(), r.width(), r.height()); } + +CachedFrame* CachedFrame::Debug::base() const { + CachedFrame* nav = (CachedFrame*) ((char*) this - OFFSETOF(CachedFrame, mDebug)); + return nav; +} + +void CachedFrame::Debug::print() const +{ + CachedFrame* b = base(); + DEBUG_PRINT_RECT("//", CONTENTS, mContents); + DEBUG_PRINT_RECT("", BOUNDS, mLocalViewBounds); + DEBUG_PRINT_RECT("//", VIEW, mViewBounds); + DUMP_NAV_LOGD("// CachedNode mCachedNodes={ // count=%d\n", b->mCachedNodes.size()); + for (CachedNode* node = b->mCachedNodes.begin(); + node != b->mCachedNodes.end(); node++) + node->mDebug.print(); + DUMP_NAV_LOGD("// }; // end of nodes\n"); + DUMP_NAV_LOGD("// CachedFrame mCachedFrames={ // count=%d\n", b->mCachedFrames.size()); + for (CachedFrame* child = b->mCachedFrames.begin(); + child != b->mCachedFrames.end(); child++) + { + child->mDebug.print(); + } + DUMP_NAV_LOGD("// }; // end of child frames\n"); + DUMP_NAV_LOGD("// void* mFrame=(void*) %p;\n", b->mFrame); + DUMP_NAV_LOGD("// CachedFrame* mParent=%p;\n", b->mParent); + DUMP_NAV_LOGD("// int mIndex=%d;\n", b->mIndex); + DUMP_NAV_LOGD("// const CachedRoot* mRoot=%p;\n", b->mRoot); + DUMP_NAV_LOGD("// int mFocus=%d;\n", b->mFocus); +} + +bool CachedFrame::Debug::validate(const CachedNode* node) const +{ + const CachedFrame* b = base(); + if (b->mCachedNodes.size() == 0) + return false; + if (node >= b->mCachedNodes.begin() && node < b->mCachedNodes.end()) + return true; + for (const CachedFrame* child = b->mCachedFrames.begin(); + child != b->mCachedFrames.end(); child++) + if (child->mDebug.validate(node)) + return true; + return false; +} + +#undef DEBUG_PRINT_RECT + +#endif + +} diff --git a/WebCore/platform/android/nav/CachedFrame.h b/WebCore/platform/android/nav/CachedFrame.h new file mode 100644 index 0000000..c7b1327 --- /dev/null +++ b/WebCore/platform/android/nav/CachedFrame.h @@ -0,0 +1,220 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CachedFrame_H +#define CachedFrame_H + +#include "CachedNode.h" +#include "IntRect.h" +#include "SkFixed.h" +#include "wtf/Vector.h" + +namespace WebCore { + class Frame; + class FrameAndroid; + class Node; +} + +namespace android { + +class CachedHistory; +class CachedRoot; + + // first node referenced by cache is always document +class CachedFrame { +public: + enum Direction { + UNINITIALIZED = -1, + LEFT, + RIGHT, + UP, + DOWN, + DIRECTION_COUNT, + DIRECTION_MASK = DIRECTION_COUNT - 1, + UP_DOWN = UP & DOWN, // mask and result + RIGHT_DOWN = RIGHT & DOWN, // mask and result + }; + enum Compare { + UNDECIDED = -1, + TEST_IS_BEST, + REJECT_TEST + }; + CachedFrame() {} + void add(CachedNode& node) { mCachedNodes.append(node); } + void addFrame(CachedFrame& child) { mCachedFrames.append(child); } + bool checkVisited(const CachedNode* , CachedFrame::Direction ) const; + size_t childCount() { return mCachedFrames.size(); } + void clearFocus(); + bool containsFrame(const CachedFrame* ) const; + const CachedNode* currentFocus() const { return currentFocus(NULL); } + const CachedNode* currentFocus(const CachedFrame** ) const; + bool directionChange() const; + const CachedNode* document() const { return mCachedNodes.begin(); } + bool empty() const { return mCachedNodes.size() < 2; } // must have 1 past doc + const CachedNode* findBestAt(const WebCore::IntRect& , int* best, + const CachedNode** , const CachedFrame** , int* x, int* y) const; + const CachedFrame* findBestFrameAt(int x, int y) const; + const CachedNode* findBestHitAt(const WebCore::IntRect& , + int* best, const CachedFrame** , int* x, int* y) const; + bool finishInit(); + CachedFrame* firstChild() { return mCachedFrames.begin(); } + const CachedFrame* firstChild() const { return mCachedFrames.begin(); } + int focusIndex() const { return mFocus; } + void* framePointer() const { return mFrame; } + CachedNode* getIndex(int index) { return index >= 0 ? + &mCachedNodes[index] : NULL; } + const CachedFrame* hasFrame(const CachedNode* node) const; + int indexInParent() const { return mIndex; } + void init(const CachedRoot* root, int index, WebCore::FrameAndroid* frame); + const CachedFrame* lastChild() const { return &mCachedFrames.last(); } + CachedNode* lastNode() { return &mCachedNodes.last(); } + CachedFrame* lastChild() { return &mCachedFrames.last(); } + const CachedFrame* parent() const { return mParent; } + CachedFrame* parent() { return mParent; } + void removeLast() { mCachedNodes.removeLast(); } + void resetClippedOut(); + void setContentsSize(int width, int height) { mContents.setWidth(width); + mContents.setHeight(height); } + void setData(); + bool setFocus(WebCore::Frame* , WebCore::Node* , int x, int y); + void setFocusIndex(int focusIndex) const { mFocus = focusIndex; } + void setLocalViewBounds(const WebCore::IntRect& bounds) { mLocalViewBounds = bounds; } + int size() { return mCachedNodes.size(); } + const CachedNode* validDocument() const; +protected: + struct BestData { + WebCore::IntRect mNodeBounds; + WebCore::IntRect mMouseBounds; + int mDistance; + int mSideDistance; + int mMajorDelta; // difference of center of object + // used only when leading and trailing edges contain another set of edges + int mMajorDelta2; // difference of leading edge (only used when center is same) + int mMajorButt; // checks for next cell butting up against or close to previous one + int mWorkingDelta; + int mWorkingDelta2; + int mNavDelta; + int mNavDelta2; + const CachedFrame* mFrame; + const CachedNode* mNode; + SkFixed mWorkingOverlap; // this and below are fuzzy answers instead of bools + SkFixed mNavOverlap; + SkFixed mPreferred; + bool mFocusChild; + bool mInNav; + bool mNavOutside; + bool mWorkingOutside; + int bottom() const { return bounds().bottom(); } + const WebCore::IntRect& bounds() const { return mNodeBounds; } + bool canBeReachedByAnotherDirection(); + int height() const { return bounds().height(); } + bool inOrSubsumesNav() const { return (mNavDelta ^ mNavDelta2) >= 0; } + bool inOrSubsumesWorking() const { return (mWorkingDelta ^ mWorkingDelta2) >= 0; } + int isContainer(BestData* ); + static SkFixed Overlap(int span, int left, int right); + void reset() { mNode = NULL; } + int right() const { return bounds().right(); } + void setDistances(); + bool setDownDirection(const CachedHistory* ); + bool setLeftDirection(const CachedHistory* ); + bool setRightDirection(const CachedHistory* ); + bool setUpDirection(const CachedHistory* ); + void setNavInclusion(int left, int right); + void setNavOverlap(int span, int left, int right); + void setWorkingInclusion(int left, int right); + void setWorkingOverlap(int span, int left, int right); + int width() const { return bounds().width(); } + int x() const { return bounds().x(); } + int y() const { return bounds().y(); } + }; + typedef const CachedNode* (CachedFrame::*MoveInDirection)( + const CachedNode* test, const CachedNode* limit, BestData* bestData, + const CachedNode* focus) const; + void adjustToTextColumn(int* delta) const; + static bool CheckBetween(Direction , const WebCore::IntRect& bestRect, + const WebCore::IntRect& prior, WebCore::IntRect* result); + bool checkBetween(BestData* , Direction ); + int compare(BestData& testData, const BestData& bestData, const + CachedNode* focus) const; + void findClosest(BestData* , Direction original, Direction test, + WebCore::IntRect* clip) const; + int frameNodeCommon(BestData& testData, const CachedNode* test, + BestData* bestData, BestData* originalData, + const CachedNode* focus) const; + int framePartCommon(BestData& testData, const CachedNode* test, + BestData* bestData, const CachedNode* focus) const; + const CachedNode* frameDown(const CachedNode* test, const CachedNode* limit, + BestData* , const CachedNode* focus) const; + const CachedNode* frameLeft(const CachedNode* test, const CachedNode* limit, + BestData* , const CachedNode* focus) const; + const CachedNode* frameRight(const CachedNode* test, const CachedNode* limit, + BestData* , const CachedNode* focus) const; + const CachedNode* frameUp(const CachedNode* test, const CachedNode* limit, + BestData* , const CachedNode* focus) const; + int minWorkingHorizontal() const; + int minWorkingVertical() const; + int maxWorkingHorizontal() const; + int maxWorkingVertical() const; + bool moveInFrame(MoveInDirection , const CachedNode* test, BestData* best, + const CachedNode* focus) const; + const WebCore::IntRect& _navBounds() const; + WebCore::IntRect mContents; + WebCore::IntRect mLocalViewBounds; + WebCore::IntRect mViewBounds; + WTF::Vector<CachedNode> mCachedNodes; + WTF::Vector<CachedFrame> mCachedFrames; + void* mFrame; // WebCore::Frame*, used only to compare pointers + CachedFrame* mParent; + int mIndex; // index within parent's array of children, or -1 if root + const CachedRoot* mRoot; + mutable int mFocus; +private: + CachedHistory* history() const; +#ifdef BROWSER_DEBUG +public: + CachedNode* find(WebCore::Node* ); // !!! probably debugging only + int mDebugIndex; + int mDebugLoopbackOffset; +#endif +#if !defined NDEBUG || DUMP_NAV_CACHE +public: + class Debug { +public: + Debug() { +#if DUMP_NAV_CACHE + mFrameName[0] = '\0'; +#endif +#if !defined NDEBUG + mInUse = true; +#endif + } +#if !defined NDEBUG + ~Debug() { mInUse = false; } + bool mInUse; +#endif +#if DUMP_NAV_CACHE + CachedFrame* base() const; + void print() const; + bool validate(const CachedNode* ) const; + char mFrameName[256]; +#endif + } mDebug; +#endif +}; + +} + +#endif diff --git a/WebCore/platform/android/nav/CachedHistory.cpp b/WebCore/platform/android/nav/CachedHistory.cpp new file mode 100644 index 0000000..589b64a --- /dev/null +++ b/WebCore/platform/android/nav/CachedHistory.cpp @@ -0,0 +1,196 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "CachedPrefix.h" +#include "CachedFrame.h" +#include "CachedNode.h" +#if DUMP_NAV_CACHE +#include "CachedRoot.h" +#endif + +#include "CachedHistory.h" + +namespace android { + +CachedHistory::CachedHistory() { + memset(this, 0, sizeof(CachedHistory)); // this assume the class has no virtuals + mLastMove = CachedFrame::UNINITIALIZED; + mPriorMove = CachedFrame::UNINITIALIZED; +} + + +void CachedHistory::addToVisited(const CachedNode* node, CachedFrame::Direction direction) +{ + memmove(&mVisited[1], &mVisited[0], sizeof(mVisited) - sizeof(mVisited[0])); + mVisited[0].mNode = node; + mVisited[0].mDirection = direction; +} + +bool CachedHistory::checkVisited(const CachedNode* node, CachedFrame::Direction direction) const +{ + // if the direction is unchanged and we've already visited this node, don't visit it again + int index = 0; + while (index < NAVIGATION_VISIT_DEPTH - 1) { + if (direction != mVisited[index].mDirection) + break; + index++; // compare with last direction, previous to last node (where the arrow took us from) + if (node == mVisited[index].mNode) + return false; + } + return true; +} + +void CachedHistory::pinMaxMin(const WebCore::IntRect& viewBounds) +{ + if (mMinWorkingHorizontal < viewBounds.y() || + mMinWorkingHorizontal >= viewBounds.bottom()) + mMinWorkingHorizontal = viewBounds.y(); + if (mMaxWorkingHorizontal > viewBounds.bottom() || + mMaxWorkingHorizontal <= viewBounds.y()) + mMaxWorkingHorizontal = viewBounds.bottom(); + if (mMinWorkingVertical < viewBounds.x() || + mMinWorkingVertical >= viewBounds.right()) + mMinWorkingVertical = viewBounds.x(); + if (mMaxWorkingVertical > viewBounds.right() || + mMaxWorkingVertical <= viewBounds.x()) + mMaxWorkingVertical = viewBounds.right(); +} + +void CachedHistory::reset() +{ + memset(mVisited, 0, sizeof(mVisited)); +// mLastScroll = 0; + mPriorBounds = mFocusBounds = WebCore::IntRect(0, 0, 0, 0); + mDirectionChange = false; + mFocusIsInput = false; + mPriorIsInput = false; + mDidFirstLayout = false; + mPriorMove = mLastMove = CachedFrame::UNINITIALIZED; + mMinWorkingHorizontal = mMinWorkingVertical = INT_MIN; + mMaxWorkingHorizontal = mMaxWorkingVertical = INT_MAX; +} + +void CachedHistory::setWorking(CachedFrame::Direction newMove, + const CachedNode* focus, const WebCore::IntRect& viewBounds) +{ + CachedFrame::Direction lastAxis = (CachedFrame::Direction) (mLastMove & ~CachedFrame::RIGHT_DOWN); // up, left or uninitialized + CachedFrame::Direction newAxis = (CachedFrame::Direction) (newMove & ~CachedFrame::RIGHT_DOWN); + bool change = newAxis != lastAxis; + mDirectionChange = change && mLastMove != CachedFrame::UNINITIALIZED; + if (focus != NULL || mLastMove != CachedFrame::UNINITIALIZED) { + mPriorMove = mLastMove; + mLastMove = newMove; + } + const WebCore::IntRect* navBounds = &mNavBounds; + if (focus != NULL) { + WebCore::IntRect focusBounds; + focus->getBounds(&focusBounds); + if (focusBounds.isEmpty() == false && focusBounds != mFocusBounds) + mNavBounds = mFocusBounds = focusBounds; + mPriorIsInput = mFocusIsInput; + mFocusIsInput = focus->isInput(); // focus->localName() == "input"; + } else + mFocusBounds = WebCore::IntRect(0, 0, 0, 0); + if (change) { // uninitialized or change in direction + if (lastAxis != CachedFrame::LEFT && navBounds->height() > 0) { + mMinWorkingHorizontal = navBounds->y(); + mMaxWorkingHorizontal = navBounds->bottom(); + } + if (lastAxis != CachedFrame::UP && navBounds->width() > 0) { + mMinWorkingVertical = navBounds->x(); + mMaxWorkingVertical = navBounds->right(); + } + } else if (mPriorIsInput) { + if (newAxis == CachedFrame::UP_DOWN) { + if (mPriorBounds.x() == mMinWorkingVertical && mPriorBounds.right() == mMaxWorkingVertical) { + mMinWorkingVertical = navBounds->x(); + mMaxWorkingVertical = navBounds->right(); + } + } else { + if (mPriorBounds.y() == mMinWorkingHorizontal && mPriorBounds.bottom() == mMaxWorkingHorizontal) { + mMinWorkingHorizontal = navBounds->y(); + mMaxWorkingHorizontal = navBounds->bottom(); + } + } + } + pinMaxMin(viewBounds); +} + +#if DUMP_NAV_CACHE + +#define DEBUG_PRINT_BOOL(field) \ + DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false") + +#define DEBUG_PRINT_RECT(field) \ + { const WebCore::IntRect& r = b->field; \ + DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \ + r.x(), r.y(), r.width(), r.height()); } + +CachedHistory* CachedHistory::Debug::base() const { + CachedHistory* nav = (CachedHistory*) ((char*) this - OFFSETOF(CachedHistory, mDebug)); + return nav; +} + +const char* CachedHistory::Debug::direction(CachedFrame::Direction d) const +{ + switch (d) { + case CachedFrame::LEFT: return "LEFT"; break; + case CachedFrame::RIGHT: return "RIGHT"; break; + case CachedFrame::UP: return "UP"; break; + case CachedFrame::DOWN: return "DOWN"; break; + default: return "UNINITIALIZED"; + } +} + +void CachedHistory::Debug::print(CachedRoot* root) const +{ + CachedHistory* b = base(); + DUMP_NAV_LOGD("// Visited mVisited[]={\n"); + for (size_t i = 0; i < NAVIGATION_VISIT_DEPTH; i++) { + const Visited& visit = b->mVisited[i]; + const CachedNode* node = visit.mNode; + int index = root != NULL && root->CachedFrame::mDebug.validate(node) ? + node->index() : -1; + DUMP_NAV_LOGD(" // { 0x%p (%d), %s },\n", node, index, direction(visit.mDirection)); + } + DUMP_NAV_LOGD("// };\n"); +// DUMP_NAV_LOGD("// int mLastScroll=%d;\n", b->mLastScroll); + DEBUG_PRINT_RECT(mFocusBounds); + DEBUG_PRINT_RECT(mNavBounds); + DEBUG_PRINT_RECT(mPriorBounds); + DEBUG_PRINT_BOOL(mDirectionChange); + DEBUG_PRINT_BOOL(mFocusIsInput); + DEBUG_PRINT_BOOL(mPriorIsInput); + DUMP_NAV_LOGD("// CachedFrame::Direction mLastMove=%s, mPriorMove=%s;\n", + direction(b->mLastMove), direction(b->mPriorMove)); + int max = b->mMaxWorkingHorizontal; + DUMP_NAV_LOGD("static int TEST_MAX_H = %d;\n", max); + int min = b->mMinWorkingHorizontal; + if (min == INT_MIN) + min++; + DUMP_NAV_LOGD("static int TEST_MIN_H = %d;\n", min); + max = b->mMaxWorkingVertical; + DUMP_NAV_LOGD("static int TEST_MAX_V = %d;\n", max); + min = b->mMinWorkingVertical; + if (min == INT_MIN) + min++; + DUMP_NAV_LOGD("static int TEST_MIN_V = %d;\n", min); + DUMP_NAV_LOGD("\n"); +} + +#endif + +} diff --git a/WebCore/platform/android/nav/CachedHistory.h b/WebCore/platform/android/nav/CachedHistory.h new file mode 100644 index 0000000..818115e --- /dev/null +++ b/WebCore/platform/android/nav/CachedHistory.h @@ -0,0 +1,83 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CachedHistory_H +#define CachedHistory_H + +#include "CachedFrame.h" + +#define NAVIGATION_VISIT_DEPTH 8 // the number of nodes last visited -- used to detect ping-ponging (number should be tuned) + +namespace android { + +class CachedRoot; + +// CachedHistory is maintained even if DOM is rebuilt by running script. +// It uses blind pointers for comparison in the previously visited nodes. +class CachedHistory { +public: + CachedHistory(); + void addToVisited(const CachedNode* , CachedFrame::Direction ); + bool checkVisited(const CachedNode* , CachedFrame::Direction ) const; + bool didFirstLayout() const { return mDidFirstLayout; } + bool directionChange() const { return mDirectionChange; } + const WebCore::IntRect& focusBounds() const { return mFocusBounds; } + int minWorkingHorizontal() const { return mMinWorkingHorizontal; } + int minWorkingVertical() const { return mMinWorkingVertical; } + int maxWorkingHorizontal() const { return mMaxWorkingHorizontal; } + int maxWorkingVertical() const { return mMaxWorkingVertical; } + const WebCore::IntRect& navBounds() const { return mNavBounds; } + const WebCore::IntRect& priorBounds() const { return mPriorBounds; } + void setDidFirstLayout(bool did) { mDidFirstLayout = did; } + void setNavBounds(const WebCore::IntRect& loc) { mNavBounds = loc; } + void setWorking(CachedFrame::Direction , const CachedNode* focus, + const WebCore::IntRect& viewBounds); + void reset(); +private: + void pinMaxMin(const WebCore::IntRect& viewBounds); + struct Visited { + const CachedNode* mNode; + CachedFrame::Direction mDirection; + } mVisited[NAVIGATION_VISIT_DEPTH]; + WebCore::IntRect mFocusBounds; // chosen focus ring + WebCore::IntRect mMouseBounds; // constricted bounds, if focus ring is partially visible + WebCore::IntRect mNavBounds; // focus ring bounds plus optional keystroke movement + WebCore::IntRect mPriorBounds; // prior chosen focus ring (for reversing narrowing) + bool mDirectionChange; + bool mFocusIsInput; // defer max/min to non-focus node if focus is too broad + bool mPriorIsInput; // defer max/min to non-focus node if focus is too broad + bool mDidFirstLayout; // set true when page is newly laid out + CachedFrame::Direction mLastMove; + CachedFrame::Direction mPriorMove; + int mMinWorkingHorizontal; + int mMaxWorkingHorizontal; + int mMinWorkingVertical; + int mMaxWorkingVertical; + friend class CachedRoot; +#if DUMP_NAV_CACHE +public: + class Debug { +public: + CachedHistory* base() const; + const char* direction(CachedFrame::Direction d) const; + void print(CachedRoot* ) const; + } mDebug; +#endif +}; + +} + +#endif diff --git a/WebCore/platform/android/nav/CachedNode.cpp b/WebCore/platform/android/nav/CachedNode.cpp new file mode 100644 index 0000000..0545b9b --- /dev/null +++ b/WebCore/platform/android/nav/CachedNode.cpp @@ -0,0 +1,332 @@ +/* + * + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CachedPrefix.h" +#include "CachedFrame.h" +#include "CachedHistory.h" +#include "Node.h" +#include "PlatformString.h" + +#include "android_graphics.h" +#include "CachedNode.h" + +namespace android { + +void CachedNode::clearFocus(CachedFrame* parent) +{ + if (isFrame()) { + CachedFrame* child = const_cast<CachedFrame*>(parent->hasFrame(this)); + child->clearFocus(); + } + mIsFocus = false; +} + +bool CachedNode::Clip(const WebCore::IntRect& outer, WebCore::IntRect* inner, + WTF::Vector<WebCore::IntRect>* rings) +{ + if (outer.contains(*inner)) + return true; +// DBG_NAV_LOGD("outer:{%d,%d,%d,%d} does not contain inner:{%d,%d,%d,%d}", +// outer.x(), outer.y(), outer.width(), outer.height(), +// inner->x(), inner->y(), inner->width(), inner->height()); + bool intersects = outer.intersects(*inner); + size_t size = intersects ? rings->size() : 0; + *inner = WebCore::IntRect(0, 0, 0, 0); + if (intersects) { + WebCore::IntRect * const start = rings->begin(); + WebCore::IntRect* ring = start + size - 1; + do { + ring->intersect(outer); + if (ring->isEmpty()) { + if ((size_t) (ring - start) != --size) + *ring = start[size]; + } else + inner->unite(*ring); + } while (ring-- != start); + } + rings->shrink(size); +// DBG_NAV_LOGD("size:%d", size); + return size != 0; +} + +bool CachedNode::clip(const WebCore::IntRect& bounds) +{ + return Clip(bounds, &mBounds, &mFocusRing); +} + +#define OVERLAP 3 + +void CachedNode::fixUpFocusRects() +{ + if (mFixedUpFocusRects) + return; + mFixedUpFocusRects = true; + if (mNavableRects <= 1) + return; +#if DEBUG_NAV_UI + { + WebCore::IntRect* boundsPtr = mFocusRing.begin() - 1; + const WebCore::IntRect* const boundsEnd = mFocusRing.begin() + mFocusRing.size(); + while (++boundsPtr < boundsEnd) + LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, boundsPtr - mFocusRing.begin(), + boundsPtr->x(), boundsPtr->y(), boundsPtr->width(), boundsPtr->height()); + } +#endif + // q: need to know when rects are for drawing and hit-testing, but not mouse down calcs? + bool again; + do { + again = false; + size_t size = mFocusRing.size(); + WebCore::IntRect* unitBoundsPtr = mFocusRing.begin() - 1; + const WebCore::IntRect* const unitBoundsEnd = mFocusRing.begin() + size; + while (++unitBoundsPtr < unitBoundsEnd) { + // any other unitBounds to the left or right of this one? + int unitTop = unitBoundsPtr->y(); + int unitBottom = unitBoundsPtr->bottom(); + int unitLeft = unitBoundsPtr->x(); + int unitRight = unitBoundsPtr->right(); + WebCore::IntRect* testBoundsPtr = mFocusRing.begin() - 1; + while (++testBoundsPtr < unitBoundsEnd) { + if (unitBoundsPtr == testBoundsPtr) + continue; + int testTop = testBoundsPtr->y(); + int testBottom = testBoundsPtr->bottom(); + int testLeft = testBoundsPtr->x(); + int testRight = testBoundsPtr->right(); + int candidateTop = unitTop > testTop ? unitTop : testTop; + int candidateBottom = unitBottom < testBottom ? unitBottom : testBottom; + int candidateLeft = unitRight < testLeft ? unitRight : testRight; + int candidateRight = unitRight > testLeft ? unitLeft : testLeft; + bool leftRight = true; + if (candidateTop + OVERLAP >= candidateBottom || + candidateLeft + OVERLAP >= candidateRight) { + candidateTop = unitBottom < testTop ? unitBottom : testBottom; + candidateBottom = unitBottom > testTop ? unitTop : testTop; + candidateLeft = unitLeft > testLeft ? unitLeft : testLeft; + candidateRight = unitRight < testRight ? unitRight : testRight; + if (candidateTop + OVERLAP >= candidateBottom || + candidateLeft + OVERLAP >= candidateRight) + continue; + leftRight = false; + } + // construct candidate to add + WebCore::IntRect candidate = WebCore::IntRect(candidateLeft, candidateTop, + candidateRight - candidateLeft, candidateBottom - candidateTop); + // does a different unit bounds intersect the candidate? if so, don't add + WebCore::IntRect* checkBoundsPtr = mFocusRing.begin() - 1; + while (++checkBoundsPtr < unitBoundsEnd) { + if (checkBoundsPtr->intersects(candidate) == false) + continue; + if (leftRight) { + if (candidateTop >= checkBoundsPtr->y() && + candidateBottom > checkBoundsPtr->bottom()) + candidateTop = checkBoundsPtr->bottom(); + else if (candidateTop < checkBoundsPtr->y() && + candidateBottom <= checkBoundsPtr->bottom()) + candidateBottom = checkBoundsPtr->y(); + else + goto nextCheck; + } else { + if (candidateLeft >= checkBoundsPtr->x() && + candidateRight > checkBoundsPtr->right()) + candidateLeft = checkBoundsPtr->right(); + else if (candidateLeft < checkBoundsPtr->x() && + candidateRight <= checkBoundsPtr->right()) + candidateRight = checkBoundsPtr->x(); + else + goto nextCheck; + } + } + candidate = WebCore::IntRect(candidateLeft, candidateTop, + candidateRight - candidateLeft, candidateBottom - candidateTop); + ASSERT(candidate.isEmpty() == false); +#if DEBUG_NAV_UI + LOGD("%s %d:(%d, %d, %d, %d)\n", __FUNCTION__, mFocusRing.size(), + candidate.x(), candidate.y(), candidate.width(), candidate.height()); +#endif + mFocusRing.append(candidate); + again = true; + goto tryAgain; + nextCheck: + continue; + } + } +tryAgain: + ; + } while (again); +} + + +void CachedNode::focusRingBounds(WebCore::IntRect* bounds) const +{ + int partMax = mNavableRects; + ASSERT(partMax > 0); + *bounds = mFocusRing[0]; + for (int partIndex = 1; partIndex < partMax; partIndex++) + bounds->unite(mFocusRing[partIndex]); + bounds->inflate(FOCUS_RING_HIT_TEST_RADIUS); +} + +void CachedNode::init(CachedFrame* frame, WebCore::Node* node) +{ + bzero(this, sizeof(CachedNode)); + mExport = WebCore::String(); + mName = WebCore::String(); + mNode = node; + mParentIndex = mChildFrameIndex = -1; + mType = android::NORMAL_CACHEDNODETYPE; +} + +void CachedNode::move(int x, int y) +{ + mBounds.move(x, y); + // mHitTestBounds will be moved by caller + WebCore::IntRect* first = mFocusRing.begin(); + WebCore::IntRect* last = first + mFocusRing.size(); + --first; + while (++first != last) + first->move(x, y); +} + +bool CachedNode::partRectsContains(const CachedNode* other) const +{ + int outerIndex = 0; + int outerMax = mNavableRects; + int innerMax = other->mNavableRects; + do { + const WebCore::IntRect& outerBounds = mFocusRing[outerIndex]; + int innerIndex = 0; + do { + const WebCore::IntRect& innerBounds = other->mFocusRing[innerIndex]; + if (innerBounds.contains(outerBounds)) + return true; + } while (++innerIndex < innerMax); + } while (++outerIndex < outerMax); + return false; +} + +#if DUMP_NAV_CACHE + +#define DEBUG_PRINT_BOOL(field) \ + DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false") + +#define DEBUG_PRINT_RECT(field) \ + { const WebCore::IntRect& r = b->field; \ + DUMP_NAV_LOGD("// IntRect " #field "={%d, %d, %d, %d};\n", \ + r.x(), r.y(), r.width(), r.height()); } + +CachedNode* CachedNode::Debug::base() const { + CachedNode* nav = (CachedNode*) ((char*) this - OFFSETOF(CachedNode, mDebug)); + return nav; +} + +const char* CachedNode::Debug::condition(Condition t) const +{ + switch (t) { + case NOT_REJECTED: return "NOT_REJECTED"; break; + case BUTTED_UP: return "BUTTED_UP"; break; + case CENTER_FURTHER: return "CENTER_FURTHER"; break; + case CLOSER: return "CLOSER"; break; + case CLOSER_IN_FOCUS: return "CLOSER_IN_FOCUS"; break; + case CLOSER_OVERLAP: return "CLOSER_OVERLAP"; break; + case CLOSER_TOP: return "CLOSER_TOP"; break; + case FOCUSABLE: return "FOCUSABLE"; break; + case FURTHER: return "FURTHER"; break; + case IN_UMBRA: return "IN_UMBRA"; break; + case IN_WORKING: return "IN_WORKING"; break; + case LEFTMOST: return "LEFTMOST"; break; + case OVERLAP_OR_EDGE_FURTHER: return "OVERLAP_OR_EDGE_FURTHER"; break; + case PREFERRED: return "PREFERRED"; break; + case ANCHOR_IN_ANCHOR: return "ANCHOR_IN_ANCHOR"; break; + case BEST_DIRECTION: return "BEST_DIRECTION"; break; + case CHILD: return "CHILD"; break; + case DISABLED: return "DISABLED"; break; + case IN_FOCUS: return "IN_FOCUS"; break; + case IN_FOCUS_CHILDREN: return "IN_FOCUS_CHILDREN"; break; + case NOT_ENCLOSING_FOCUS: return "NOT_ENCLOSING_FOCUS"; break; + // case NOT_FOCUS_CHILD: return "NOT_FOCUS_CHILD"; break; + case NOT_FOCUS_NODE: return "NOT_FOCUS_NODE"; break; + case OUTSIDE_OF_BEST: return "OUTSIDE_OF_BEST"; break; + case OUTSIDE_OF_ORIGINAL: return "OUTSIDE_OF_ORIGINAL"; break; + default: return "???"; + } +} + +const char* CachedNode::Debug::type(android::CachedNodeType t) const +{ + switch (t) { + case NORMAL_CACHEDNODETYPE: return "NORMAL"; break; + case ADDRESS_CACHEDNODETYPE: return "ADDRESS"; break; + case EMAIL_CACHEDNODETYPE: return "EMAIL"; break; + case PHONE_CACHEDNODETYPE: return "PHONE"; break; + default: return "???"; + } +} + +void CachedNode::Debug::print() const +{ + CachedNode* b = base(); + char scratch[256]; + size_t index = snprintf(scratch, sizeof(scratch), "// char* mExport=\""); + const UChar* ch = b->mExport.characters(); + while (ch && *ch && index < sizeof(scratch)) + scratch[index++] = *ch++; + DUMP_NAV_LOGD("%.*s\"\n", index, scratch); + index = snprintf(scratch, sizeof(scratch), "// char* mName=\""); + ch = b->mName.characters(); + while (ch && *ch && index < sizeof(scratch)) + scratch[index++] = *ch++; + DUMP_NAV_LOGD("%.*s\"\n", index, scratch); + DEBUG_PRINT_RECT(mBounds); + const WTF::Vector<WebCore::IntRect>& rects = b->focusRings(); + size_t size = rects.size(); + DUMP_NAV_LOGD("// IntRect focusRings={ // size=%d\n", size); + for (size_t i = 0; i < size; i++) + DUMP_NAV_LOGD(" // {%d, %d, %d, %d}, // %d\n", rects[i].x(), rects[i].y(), + rects[i].width(), rects[i].height(), i); + DUMP_NAV_LOGD("// };\n"); + DUMP_NAV_LOGD("// void* mNode=%p; // (%d) \n", b->mNode, mNodeIndex); + DUMP_NAV_LOGD("// void* mParentGroup=%p; // (%d) \n", b->mParentGroup, mParentGroupIndex); + DUMP_NAV_LOGD("// int mChildFrameIndex=%d;\n", b->mChildFrameIndex); + DUMP_NAV_LOGD("// int mIndex=%d;\n", b->mIndex); + DUMP_NAV_LOGD("// int mMaxLength=%d;\n", b->mMaxLength); + DUMP_NAV_LOGD("// int mNavableRects=%d;\n", b->mNavableRects); + DUMP_NAV_LOGD("// int mParentIndex=%d;\n", b->mParentIndex); + DUMP_NAV_LOGD("// int mTextSize=%d;\n", b->mTextSize); + DUMP_NAV_LOGD("// Condition mCondition=%s;\n", condition(b->mCondition)); + DUMP_NAV_LOGD("// Type mType=%s;\n", type(b->mType)); + DEBUG_PRINT_BOOL(mClippedOut); + DEBUG_PRINT_BOOL(mDisabled); + DEBUG_PRINT_BOOL(mFixedUpFocusRects); + DEBUG_PRINT_BOOL(mHasMouseOver); + DEBUG_PRINT_BOOL(mIsAnchor); + DEBUG_PRINT_BOOL(mIsArea); + DEBUG_PRINT_BOOL(mIsFocus); + DEBUG_PRINT_BOOL(mIsInput); + DEBUG_PRINT_BOOL(mIsParentAnchor); + DEBUG_PRINT_BOOL(mIsPassword); + DEBUG_PRINT_BOOL(mIsTextArea); + DEBUG_PRINT_BOOL(mIsTextField); + DEBUG_PRINT_BOOL(mIsTransparent); + DEBUG_PRINT_BOOL(mIsUnclipped); + DEBUG_PRINT_BOOL(mLast); + DUMP_NAV_LOGD("\n"); +} + +#endif + +} diff --git a/WebCore/platform/android/nav/CachedNode.h b/WebCore/platform/android/nav/CachedNode.h new file mode 100644 index 0000000..5cc493d --- /dev/null +++ b/WebCore/platform/android/nav/CachedNode.h @@ -0,0 +1,217 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CachedNode_H +#define CachedNode_H + +#include "AtomicString.h" +#include "CachedDebug.h" +#include "CachedNodeType.h" +#include "IntRect.h" +#include "PlatformString.h" +#include "wtf/Vector.h" + +namespace WebCore { + class Node; +} + +namespace android { + +class CachedFrame; + +class CachedNode { +public: +// Nodes are rejected because either they are spacially not the best (first set) +// or because they have the wrong DOM attribute (in focus, a focused child, etc) +// findClosest() gives only spacially rejected nodes a second chance + enum Condition { // if bigger than 32, increase bitfield size below + // rejections that get a second chance + NOT_REJECTED = 0, + SECOND_CHANCE_START = NOT_REJECTED, // must be first in list + BUTTED_UP, + CENTER_FURTHER, + CLOSER, + CLOSER_IN_FOCUS, + CLOSER_OVERLAP, + CLOSER_TOP, + FOCUSABLE, + FURTHER, + IN_UMBRA, + IN_WORKING, + LEFTMOST, + OVERLAP_OR_EDGE_FURTHER, + PREFERRED, // better overlap measure + SECOND_CHANCE_END = PREFERRED, // must be last in list + // rejections that don't get a second chance + ANCHOR_IN_ANCHOR, + BEST_DIRECTION, // can be reached by another direction + CHILD, + DISABLED, + IN_FOCUS, + IN_FOCUS_CHILDREN, + NOT_ENCLOSING_FOCUS, + // NOT_FOCUS_CHILD, + NOT_FOCUS_NODE, + OUTSIDE_OF_BEST, // containership + OUTSIDE_OF_ORIGINAL, // containership + CONDITION_SIZE // FIXME: test that CONDITION_SIZE fits in mCondition + }; + CachedNode() {} + bool anchorHasSrcUrl() const { return mAnchorHasSrcUrl; } + const WebCore::IntRect& bounds() const { return mBounds; } + WebCore::IntRect* boundsPtr() { return &mBounds; } + int childFrameIndex() const { return mChildFrameIndex; } + void clearCondition() const { mCondition = NOT_REJECTED; } + void clearFocus(CachedFrame* ); + static bool Clip(const WebCore::IntRect& outer, WebCore::IntRect* inner, + WTF::Vector<WebCore::IntRect>* rings); + bool clip(const WebCore::IntRect& ); + bool clippedOut() { return mClippedOut; } + bool disabled() const { return mDisabled; } + const CachedNode* document() const { return &this[-mIndex]; } + void fixUpFocusRects(); + void focusRingBounds(WebCore::IntRect* ) const; + WTF::Vector<WebCore::IntRect>& focusRings() { return mFocusRing; } + const WTF::Vector<WebCore::IntRect>& focusRings() const { return mFocusRing; } + const WebCore::IntRect& getBounds() const { return mBounds; } + void getBounds(WebCore::IntRect* bounds) const { *bounds = mBounds; } + const WebCore::String& getExport() const { return mExport; } + bool hasFocusRing() const { return mHasFocusRing; } + bool hasMouseOver() const { return mHasMouseOver; } + const WebCore::IntRect& hitBounds() const { return mHitBounds; } + int index() const { return mIndex; } + void init(CachedFrame* , WebCore::Node* node); + bool isAnchor() const { return mIsAnchor; } + bool isArea() const { return mIsArea; } + bool isFocus() const { return mIsFocus; } + bool isFocusable(const WebCore::IntRect& clip) const { + return clip.intersects(mBounds); + } + bool isFrame() const { return mChildFrameIndex >= 0 ; } + bool isInput() const { return mIsInput; } + bool isPassword() const { return mIsPassword; } + bool isRtlText() const { return mIsRtlText; } + bool isTextArea() const { return mIsTextArea; } + bool isTextField() const { return mIsTextField; } + bool isTransparent() const { return mIsTransparent; } + bool isUnclipped() const { return mIsUnclipped; } + int maxLength() const { return mMaxLength; }; + void move(int x, int y); + const WebCore::String& name() const { return mName; } + int navableRects() const { return mNavableRects; } + void* nodePointer() const { return mNode; } + bool noSecondChance() const { return mCondition > SECOND_CHANCE_END; } + const CachedNode* parent() const { return document() + mParentIndex; } + void* parentGroup() const { return mParentGroup; } + int parentIndex() const { return mParentIndex; } + bool partRectsContains(const CachedNode* other) const; + void reset(); + void setAnchorHasSrcUrl(bool isURL) { mAnchorHasSrcUrl = isURL; } + void setBounds(const WebCore::IntRect& bounds) { mBounds = bounds; } + void setChildFrameIndex(int index) { mChildFrameIndex = index; } + void setClippedOut(bool clipped) { mClippedOut = clipped; } + void setCondition(Condition condition) const { mCondition = condition; } + void setDisabled(bool disabled) { mDisabled = disabled; } + void setExport(const WebCore::String& exported) { mExport = exported; } + void setHasFocusRing(bool hasFocusRing) { mHasFocusRing = hasFocusRing; } + void setHasMouseOver(bool hasMouseOver) { mHasMouseOver = hasMouseOver; } + void setHitBounds(const WebCore::IntRect& bounds) { mHitBounds = bounds; } + void setIndex(int index) { mIndex = index; } + void setIsAnchor(bool isAnchor) { mIsAnchor = isAnchor; } + void setIsArea(bool isArea) { mIsArea = isArea; } + void setIsFocus(bool isFocus) { mIsFocus = isFocus; } + void setIsInput(bool isInput) { mIsInput = isInput; } + void setIsParentAnchor(bool isAnchor) { mIsParentAnchor = isAnchor; } + void setIsPassword(bool isPassword) { mIsPassword = isPassword; } + void setIsRtlText(bool isRtlText) { mIsRtlText = isRtlText; } + void setIsTextArea(bool isTextArea) { mIsTextArea = isTextArea; } + void setIsTextField(bool isTextField) { mIsTextField = isTextField; } + void setIsTransparent(bool isTransparent) { mIsTransparent = isTransparent; } + void setIsUnclipped(bool unclipped) { mIsUnclipped = unclipped; } + void setLast() { mLast = true; } + void setMaxLength(int maxLength) { mMaxLength = maxLength; } + void setName(const WebCore::String& name) { mName = name; } + void setNavableRects() { mNavableRects = mFocusRing.size(); } + void setParentGroup(void* group) { mParentGroup = group; } + void setParentIndex(int parent) { mParentIndex = parent; } + void setTextSize(int textSize) { mTextSize = textSize; } + void setType(CachedNodeType type) { mType = type; } + const CachedNode* traverseNextNode() const { return mLast ? NULL : &this[1]; } + int textSize() const { return mTextSize; } + CachedNodeType type() const { return mType; } +private: + WebCore::String mExport; + WebCore::String mName; + WebCore::IntRect mBounds; + WebCore::IntRect mHitBounds; + WTF::Vector<WebCore::IntRect> mFocusRing; + void* mNode; // WebCore::Node*, only used to match pointers + void* mParentGroup; // WebCore::Node*, only used to match pointers + int mChildFrameIndex; // set to -1 if node is not a frame + int mIndex; // index of itself, to find first in array (document) + int mMaxLength; + int mNavableRects; // FIXME: could be bitfield once I limit max number of rects + int mParentIndex; + int mTextSize; + mutable Condition mCondition : 5; // why the node was not chosen on the first pass + CachedNodeType mType : 3; + bool mAnchorHasSrcUrl : 1; + bool mClippedOut : 1; + bool mDisabled : 1; + bool mFixedUpFocusRects : 1; + bool mHasFocusRing : 1; + bool mHasMouseOver : 1; + bool mIsAnchor : 1; + bool mIsArea : 1; + bool mIsFocus : 1; + bool mIsInput : 1; + bool mIsParentAnchor : 1; + bool mIsPassword : 1; + bool mIsRtlText : 1; + bool mIsTextArea : 1; + bool mIsTextField : 1; + bool mIsTransparent : 1; + bool mIsUnclipped : 1; + bool mLast : 1; +#ifdef BROWSER_DEBUG +public: + WebCore::Node* webCoreNode() const { return (WebCore::Node*) mNode; } + bool mDisplayMeasure; + mutable bool mInCompare; + // mutable int mCondition; + int mSideDistance; + int mSecondSide; +#endif +#if DEBUG_NAV_UI || DUMP_NAV_CACHE +public: + class Debug { +public: + CachedNode* base() const; + const char* condition(Condition t) const; + void print() const; + const char* type(CachedNodeType t) const; +#if DUMP_NAV_CACHE + int mNodeIndex; + int mParentGroupIndex; +#endif + } mDebug; + friend class CachedNode::Debug; +#endif +}; + +} + +#endif diff --git a/WebCore/platform/android/nav/CachedNodeType.h b/WebCore/platform/android/nav/CachedNodeType.h new file mode 100644 index 0000000..93f4345 --- /dev/null +++ b/WebCore/platform/android/nav/CachedNodeType.h @@ -0,0 +1,32 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CachedNodeType_H +#define CachedNodeType_H + +namespace android { + +enum CachedNodeType { + NORMAL_CACHEDNODETYPE = 0, + ADDRESS_CACHEDNODETYPE = 1, + EMAIL_CACHEDNODETYPE = 2, + PHONE_CACHEDNODETYPE = 4, + ALL_CACHEDNODETYPES = 7 +}; + +} + +#endif diff --git a/WebCore/platform/android/nav/CachedPrefix.h b/WebCore/platform/android/nav/CachedPrefix.h new file mode 100644 index 0000000..3994a5a --- /dev/null +++ b/WebCore/platform/android/nav/CachedPrefix.h @@ -0,0 +1,35 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CachedPrefix_H +#define CachedPrefix_H + +#ifndef LOG_TAG +#define LOG_TAG "navcache" +#endif + +#include "config.h" +#include "CachedDebug.h" + +#ifdef LOG +#undef LOG +#endif + +#include <utils/Log.h> + +#define OFFSETOF(type, field) ((char*)&(((type*)1)->field) - (char*)1) // avoids gnu warning + +#endif diff --git a/WebCore/platform/android/nav/CachedRoot.cpp b/WebCore/platform/android/nav/CachedRoot.cpp new file mode 100644 index 0000000..07b31b7 --- /dev/null +++ b/WebCore/platform/android/nav/CachedRoot.cpp @@ -0,0 +1,1054 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "CachedPrefix.h" +#include "CachedHistory.h" +#include "CachedNode.h" +#include "SkBitmap.h" +#include "SkBounder.h" +#include "SkCanvas.h" +#include "SkRegion.h" + +#include "CachedRoot.h" + +#ifdef DUMP_NAV_CACHE_USING_PRINTF + extern android::Mutex gWriteLogMutex; +#endif + +namespace android { + +class CommonCheck : public SkBounder { +public: + enum Type { + kNo_Type, + kDrawBitmap_Type, + kDrawGlyph_Type, + kDrawPaint_Type, + kDrawPath_Type, + kDrawPicture_Type, + kDrawPoints_Type, + kDrawPosText_Type, + kDrawPosTextH_Type, + kDrawRect_Type, + kDrawSprite_Type, + kDrawText_Type, + kDrawTextOnPath_Type + }; + + static bool isTextType(Type t) { + return t == kDrawPosTextH_Type || t == kDrawText_Type; + } + + CommonCheck() : mType(kNo_Type), mAllOpaque(true), mIsOpaque(true) { + setEmpty(); + } + + bool doRect(Type type) { + mType = type; + return doIRect(mUnion); + } + + bool joinGlyphs(const SkIRect& rect) { + bool isGlyph = mType == kDrawGlyph_Type; + if (isGlyph) + mUnion.join(rect); + return isGlyph; + } + + void setAllOpaque(bool opaque) { mAllOpaque = opaque; } + void setEmpty() { mUnion.setEmpty(); } + void setIsOpaque(bool opaque) { mIsOpaque = opaque; } + void setType(Type type) { mType = type; } + + Type mType; + SkIRect mUnion; + bool mAllOpaque; + bool mIsOpaque; +}; + +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + static const char* TypeNames[] = { + "kNo_Type", + "kDrawBitmap_Type", + "kDrawGlyph_Type", + "kDrawPaint_Type", + "kDrawPath_Type", + "kDrawPicture_Type", + "kDrawPoints_Type", + "kDrawPosText_Type", + "kDrawPosTextH_Type", + "kDrawRect_Type", + "kDrawSprite_Type", + "kDrawText_Type", + "kDrawTextOnPath_Type" + }; +#endif + +#define kMargin 16 +#define kSlop 2 + +class BoundsCheck : public CommonCheck { +public: + BoundsCheck() { + mAllDrawnIn.setEmpty(); + mLastAll.setEmpty(); + mLastOver.setEmpty(); + } + + static int Area(SkIRect test) { + return test.width() * test.height(); + } + + void checkLast() { + if (mAllDrawnIn.isEmpty()) + return; + if (mLastAll.isEmpty() || Area(mLastAll) < Area(mAllDrawnIn)) { + mLastAll = mAllDrawnIn; + mDrawnOver.setEmpty(); + } + mAllDrawnIn.setEmpty(); + } + + bool hidden() { + return (mLastAll.isEmpty() && mLastOver.isEmpty()) || + mDrawnOver.contains(mBounds); + } + + virtual bool onIRect(const SkIRect& rect) { + if (joinGlyphs(rect)) + return false; + bool interestingType = mType == kDrawBitmap_Type || + mType == kDrawRect_Type || isTextType(mType); + if (SkIRect::Intersects(mBounds, rect) == false) { +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + LOGD("%s (no intersect) rect={%d,%d,%d,%d} mType=%s\n", __FUNCTION__, + rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, + TypeNames[mType]); +#endif + if (interestingType) + checkLast(); + return false; + } + if (interestingType == false) + return false; + if (mBoundsSlop.contains(rect) || + (mBounds.fLeft == rect.fLeft && mBounds.fRight == rect.fRight && + mBounds.fTop >= rect.fTop && mBounds.fBottom <= rect.fBottom) || + (mBounds.fTop == rect.fTop && mBounds.fBottom == rect.fBottom && + mBounds.fLeft >= rect.fLeft && mBounds.fRight <= rect.fRight)) { + mDrawnOver.setEmpty(); + mAllDrawnIn.join(rect); +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + LOGD("%s (contains) rect={%d,%d,%d,%d}" + " mAllDrawnIn={%d,%d,%d,%d} mType=%s\n", __FUNCTION__, + rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, + mAllDrawnIn.fLeft, mAllDrawnIn.fTop, mAllDrawnIn.fRight, mAllDrawnIn.fBottom, + TypeNames[mType]); +#endif + } else { + checkLast(); + if (!isTextType(mType)) { + if ( +#if 0 +// should the opaqueness of the bitmap disallow its ability to draw over? +// not sure that this test is needed + (mType != kDrawBitmap_Type || + (mIsOpaque && mAllOpaque)) && +#endif + mLastAll.isEmpty() == false) + mDrawnOver.op(rect, SkRegion::kUnion_Op); + } else { +// FIXME +// sometimes the text is not drawn entirely inside the focus area, even though +// it is the correct text. Until I figure out why, I allow text drawn at the +// end that is not covered up by something else to represent the focusable link +// example that triggers this that should be figured out: +// http://cdn.labpixies.com/campaigns/blackjack/blackjack.html?lang=en&country=US&libs=assets/feature/core +// ( http://tinyurl.com/ywsyzb ) + mLastOver = rect; + } +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + const SkIRect& drawnOver = mDrawnOver.getBounds(); + LOGD("%s (overlaps) rect={%d,%d,%d,%d}" + " mDrawnOver={%d,%d,%d,%d} mType=%s mIsOpaque=%s mAllOpaque=%s\n", __FUNCTION__, + rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, + drawnOver.fLeft, drawnOver.fTop, drawnOver.fRight, drawnOver.fBottom, + TypeNames[mType], mIsOpaque ? "true" : "false", mAllOpaque ? "true" : "false"); +#endif + } + return false; + } + + SkIRect mBounds; + SkIRect mBoundsSlop; + SkRegion mDrawnOver; + SkIRect mLastOver; + SkIRect mAllDrawnIn; + SkIRect mLastAll; +}; + +class BoundsCanvas : public SkCanvas { +public: + + BoundsCanvas(CommonCheck* bounder) : mBounder(*bounder) { + mTransparentLayer = 0; + setBounder(bounder); + } + + virtual ~BoundsCanvas() { + setBounder(NULL); + } + + virtual void drawPaint(const SkPaint& paint) { + mBounder.setType(CommonCheck::kDrawPaint_Type); + SkCanvas::drawPaint(paint); + } + + virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint) { + mBounder.setType(CommonCheck::kDrawPoints_Type); + SkCanvas::drawPoints(mode, count, pts, paint); + } + + virtual void drawRect(const SkRect& rect, const SkPaint& paint) { + mBounder.setType(CommonCheck::kDrawRect_Type); + SkCanvas::drawRect(rect, paint); + } + + virtual void drawPath(const SkPath& path, const SkPaint& paint) { + mBounder.setType(CommonCheck::kDrawPath_Type); + SkCanvas::drawPath(path, paint); + } + + virtual void commonDrawBitmap(const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint) { + mBounder.setType(CommonCheck::kDrawBitmap_Type); + mBounder.setIsOpaque(bitmap.isOpaque()); + SkCanvas::commonDrawBitmap(bitmap, matrix, paint); + } + + virtual void drawSprite(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint = NULL) { + mBounder.setType(CommonCheck::kDrawSprite_Type); + mBounder.setIsOpaque(bitmap.isOpaque()); + SkCanvas::drawSprite(bitmap, left, top, paint); + } + + virtual void drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint& paint) { + mBounder.setEmpty(); + mBounder.setType(CommonCheck::kDrawGlyph_Type); + SkCanvas::drawText(text, byteLength, x, y, paint); + mBounder.doRect(CommonCheck::kDrawText_Type); + } + + virtual void drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint) { + mBounder.setEmpty(); + mBounder.setType(CommonCheck::kDrawGlyph_Type); + SkCanvas::drawPosText(text, byteLength, pos, paint); + mBounder.doRect(CommonCheck::kDrawPosText_Type); + } + + virtual void drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, + const SkPaint& paint) { + mBounder.setEmpty(); + mBounder.setType(CommonCheck::kDrawGlyph_Type); + SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint); + if (mBounder.mUnion.isEmpty()) + return; + SkPaint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + SkPoint upDown[2] = { {xpos[0], constY + metrics.fAscent}, + {xpos[0], constY + metrics.fDescent} }; + const SkMatrix& matrix = getTotalMatrix(); + matrix.mapPoints(upDown, 2); + if (upDown[0].fX == upDown[1].fX) { + mBounder.mUnion.fTop = SkScalarFloor(upDown[0].fY); + mBounder.mUnion.fBottom = SkScalarFloor(upDown[1].fY); + } + mBounder.doRect(CommonCheck::kDrawPosTextH_Type); + } + + virtual void drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint) { + mBounder.setEmpty(); + mBounder.setType(CommonCheck::kDrawGlyph_Type); + SkCanvas::drawTextOnPath(text, byteLength, path, matrix, paint); + mBounder.doRect(CommonCheck::kDrawTextOnPath_Type); + } + + virtual void drawPicture(SkPicture& picture) { + mBounder.setType(CommonCheck::kDrawPicture_Type); + SkCanvas::drawPicture(picture); + } + + virtual int saveLayer(const SkRect* bounds, const SkPaint* paint, + SaveFlags flags) { + int depth = SkCanvas::saveLayer(bounds, paint, flags); + if (mTransparentLayer == 0 && paint && paint->getAlpha() < 255) { + mTransparentLayer = depth; + mBounder.setAllOpaque(false); + } + return depth; + } + + virtual void restore() { + int depth = getSaveCount(); + if (depth == mTransparentLayer) { + mTransparentLayer = 0; + mBounder.setAllOpaque(true); + } + SkCanvas::restore(); + } + + int mTransparentLayer; + CommonCheck& mBounder; +}; + +/* +CenterCheck examines the text in a picture, within a viewable rectangle, +and returns via center() the optimal amount to scroll in x to display the +paragraph of text. + +The caller of CenterCheck has configured (but not allocated) a bitmap +the height and three times the width of the view. The picture is drawn centered +in the bitmap, so text that would be revealed, if the view was scrolled up to +a view-width to the left or right, is considered. +*/ +class CenterCheck : public CommonCheck { +public: + CenterCheck(int x, int y, int width) : mX(x), mY(y), + mHitLeft(x), mHitRight(x), mMostLeft(INT_MAX), mMostRight(-INT_MAX), + mViewLeft(width), mViewRight(width << 1) { + mHit.set(x - CENTER_SLOP, y - CENTER_SLOP, + x + CENTER_SLOP, y + CENTER_SLOP); + mPartial.setEmpty(); + } + + int center() { + doRect(); // process the final line of text + /* If the touch coordinates aren't near any text, return 0 */ + if (mHitLeft == mHitRight) { + DBG_NAV_LOGD("abort: mHitLeft=%d ==mHitRight", mHitLeft); + return 0; + } + int leftOver = mHitLeft - mViewLeft; + int rightOver = mHitRight - mViewRight; + int center; + /* If the touched text is too large to entirely fit on the screen, + center it. */ + if (leftOver < 0 && rightOver > 0) { + center = (leftOver + rightOver) >> 1; + DBG_NAV_LOGD("overlap: leftOver=%d rightOver=%d center=%d", + leftOver, rightOver, center); + return center; + } + center = (mMostLeft + mMostRight) >> 1; // the paragraph center + if (leftOver > 0 && rightOver >= 0) { // off to the right + if (center > mMostLeft) // move to center loses left-most text? + center = mMostLeft; + } else if (rightOver < 0 && leftOver <= 0) { // off to the left + if (center < mMostRight) // move to center loses right-most text? + center = mMostRight; + } else { +#ifdef DONT_CENTER_IF_ALREADY_VISIBLE + center = 0; // paragraph is already fully visible +#endif + } + DBG_NAV_LOGD("scroll: leftOver=%d rightOver=%d center=%d", + leftOver, rightOver, center); + return center; + } + +protected: + virtual bool onIRect(const SkIRect& rect) { + if (joinGlyphs(rect)) // assembles glyphs into a text string + return false; + if (!isTextType(mType)) + return false; + /* Text on one line may be broken into several parts. Reassemble + the text into a rectangle before considering it. */ + if (rect.fTop < mPartial.fBottom && rect.fBottom > + mPartial.fTop && mPartial.fRight + CENTER_SLOP >= rect.fLeft) { + DBG_NAV_LOGD("join mPartial=(%d, %d, %d, %d) rect=(%d, %d, %d, %d)", + mPartial.fLeft, mPartial.fTop, mPartial.fRight, mPartial.fBottom, + rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); + mPartial.join(rect); + return false; + } + if (mPartial.isEmpty() == false) + doRect(); // process the previous line of text + mPartial = rect; + return false; + } + + void doRect() + { + /* Record the outer bounds of the lines of text that was 'hit' by the + touch coordinates, given some slop */ + if (SkIRect::Intersects(mPartial, mHit)) { + if (mHitLeft > mPartial.fLeft) + mHitLeft = mPartial.fLeft; + if (mHitRight < mPartial.fRight) + mHitRight = mPartial.fRight; + DBG_NAV_LOGD("mHitLeft=%d mHitRight=%d", mHitLeft, mHitRight); + } + /* If the considered text is completely to the left or right of the + touch coordinates, skip it */ + if (mPartial.fLeft > mX || mPartial.fRight < mX) + return; + int leftOver = mPartial.fLeft - mViewLeft; + int rightOver = mPartial.fRight - mViewRight; + /* If leftOver <= 0, the text starts off the screen. + If rightOver >= 0, the text ends off the screen. + */ + if (leftOver <= 0 && rightOver >= 0) // discard wider than screen + return; +#ifdef DONT_CENTER_IF_ALREADY_VISIBLE + if (leftOver > 0 && rightOver < 0) // discard already visible + return; +#endif + /* record the smallest margins on the left and right */ + if (mMostLeft > leftOver) + mMostLeft = leftOver; + if (mMostRight < rightOver) + mMostRight = rightOver; + DBG_NAV_LOGD("leftOver=%d rightOver=%d mMostLeft=%d mMostRight=%d", + leftOver, rightOver, mMostLeft, mMostRight); + } + + static const int CENTER_SLOP = 10; // space between text parts and lines + /* const */ SkIRect mHit; // sloppy hit rectangle + SkIRect mPartial; // accumulated text bounds, per line + const int mX; // touch location + const int mY; + int mHitLeft; // touched text extremes + int mHitRight; + int mMostLeft; // paragraph extremes + int mMostRight; + const int mViewLeft; // middle third of 3x-wide view + const int mViewRight; +}; + +class ImageCheck : public CommonCheck { +public: + ImageCheck() : mLastIsImage(false) {} + + virtual bool onIRect(const SkIRect& rect) { + if (joinGlyphs(rect)) + return false; + mLastIsImage = mType == kDrawBitmap_Type; + return false; + } + + bool mLastIsImage; +}; + +class JiggleCheck : public CommonCheck { +public: + JiggleCheck(int delta, int width) : mDelta(delta), mMaxX(width) { + mMaxJiggle = 0; + mMinX = mMinJiggle = abs(delta); + mMaxWidth = width + mMinX; + } + + int jiggle() { + if (mMinJiggle > mMaxJiggle) + return mDelta; + int avg = (mMinJiggle + mMaxJiggle + 1) >> 1; + return mDelta < 0 ? -avg : avg; + } + + virtual bool onIRect(const SkIRect& rect) { + if (joinGlyphs(rect)) + return false; + if (mType != kDrawBitmap_Type && !isTextType(mType)) + return false; + int min, max; + if (mDelta < 0) { + min = mMinX - rect.fLeft; + max = mMaxWidth - rect.fRight; + } else { + min = rect.fRight - mMaxX; + max = rect.fLeft; + } + if (min <= 0) + return false; + if (max >= mMinX) + return false; + if (mMinJiggle > min) + mMinJiggle = min; + if (mMaxJiggle < max) + mMaxJiggle = max; + return false; + } + + int mDelta; + int mMaxJiggle; + int mMaxX; + int mMinJiggle; + int mMinX; + int mMaxWidth; +}; + +bool CachedRoot::adjustForScroll(BestData* best, CachedFrame::Direction direction, + WebCore::IntPoint* scrollPtr, bool findClosest) +{ + WebCore::IntRect newOutset; + const CachedNode* newNode = best->mNode; + // see if there's a middle node + // if the middle node is in the visited list, + // or if none was computed and the newNode is in the visited list, + // treat result as NULL + if (newNode != NULL && findClosest) { + if (best->bounds().intersects(mHistory->mPriorBounds) == false && + checkBetween(best, direction)) + newNode = best->mNode; + if (findClosest && maskIfHidden(best)) { + innerMove(document(), best, direction, scrollPtr, false); + return true; + } + newNode->focusRingBounds(&newOutset); + } + int delta; + bool newNodeInView = scrollDelta(newOutset, direction, &delta); + if (delta && scrollPtr && (newNode == NULL || newNodeInView == false || + (best->mNavOutside && best->mWorkingOutside))) + *scrollPtr = WebCore::IntPoint(direction & UP_DOWN ? 0 : delta, + direction & UP_DOWN ? delta : 0); + return false; +} + + +int CachedRoot::checkForCenter(int x, int y) const +{ + int width = mViewBounds.width(); + CenterCheck centerCheck(x + width - mViewBounds.x(), y - mViewBounds.y(), + width); + BoundsCanvas checker(¢erCheck); + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width * 3, + mViewBounds.height()); + checker.setBitmapDevice(bitmap); + checker.translate(SkIntToScalar(width - mViewBounds.x()), + SkIntToScalar(-mViewBounds.y())); + checker.drawPicture(*mPicture); + return centerCheck.center(); +} + +void CachedRoot::checkForJiggle(int* xDeltaPtr) const +{ + int xDelta = *xDeltaPtr; + JiggleCheck jiggleCheck(xDelta, mViewBounds.width()); + BoundsCanvas checker(&jiggleCheck); + SkBitmap bitmap; + int absDelta = abs(xDelta); + bitmap.setConfig(SkBitmap::kARGB_8888_Config, mViewBounds.width() + + absDelta, mViewBounds.height()); + checker.setBitmapDevice(bitmap); + checker.translate(SkIntToScalar(-mViewBounds.x() - + (xDelta < 0 ? xDelta : 0)), SkIntToScalar(-mViewBounds.y())); + checker.drawPicture(*mPicture); + *xDeltaPtr = jiggleCheck.jiggle(); +} + +const CachedNode* CachedRoot::findAt(const WebCore::IntRect& rect, + const CachedFrame** framePtr, int* x, int* y) const +{ + int best = INT_MAX; + (const_cast<CachedRoot*>(this))->resetClippedOut(); + const CachedNode* directHit = NULL; + const CachedNode* node = findBestAt(rect, &best, &directHit, framePtr, x, y); + DBG_NAV_LOGD("node=%d (%p)", node == NULL ? 0 : node->index(), + node == NULL ? NULL : node->nodePointer()); + if (node == NULL) { + node = findBestHitAt(rect, &best, framePtr, x, y); + DBG_NAV_LOGD("node=%d (%p)", node == NULL ? 0 : node->index(), + node == NULL ? NULL : node->nodePointer()); + } + if (node == NULL) { + *framePtr = findBestFrameAt(rect.x() + (rect.width() >> 1), + rect.y() + (rect.height() >> 1)); + } + return node; +} + +WebCore::IntPoint CachedRoot::focusLocation() const +{ + const WebCore::IntRect& bounds = mHistory->mNavBounds; + return WebCore::IntPoint(bounds.x() + (bounds.width() >> 1), + bounds.y() + (bounds.height() >> 1)); +} + +// These reset the values because we only want to get the selection the first time. +// After that, the selection is no longer accurate. +int CachedRoot::getAndResetSelectionEnd() +{ + int end = mSelectionEnd; + mSelectionEnd = -1; + return end; +} + +int CachedRoot::getAndResetSelectionStart() +{ + int start = mSelectionStart; + mSelectionStart = -1; + return start; +} + +void CachedRoot::getSimulatedMousePosition(WebCore::IntPoint* point) +{ +#ifndef NDEBUG + ASSERT(CachedFrame::mDebug.mInUse); +#endif + const WebCore::IntRect& mouseBounds = mHistory->mMouseBounds; + point->setX(mouseBounds.x() + (mouseBounds.width() >> 1)); + point->setY(mouseBounds.y() + (mouseBounds.height() >> 1)); +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + const WebCore::IntRect& navBounds = mHistory->mNavBounds; + LOGD("%s mHistory->mNavBounds={%d,%d,%d,%d} " + "mHistory->mMouseBounds={%d,%d,%d,%d} point={%d,%d}\n", __FUNCTION__, + navBounds.x(), navBounds.y(), navBounds.width(), navBounds.height(), + mouseBounds.x(), mouseBounds.y(), mouseBounds.width(), mouseBounds.height(), + point->x(), point->y()); +#endif +} + +void CachedRoot::init(WebCore::FrameAndroid* frame, CachedHistory* history) +{ + CachedFrame::init(this, -1, frame); + reset(); + mHistory = history; + mPicture = NULL; +} + +bool CachedRoot::innerDown(const CachedNode* test, BestData* bestData) const +{ + ASSERT(minWorkingVertical() >= mViewBounds.x()); + ASSERT(maxWorkingVertical() <= mViewBounds.right()); + setupScrolledBounds(); + // (line up) + mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll); + int testTop = mScrolledBounds.y(); + int viewBottom = mViewBounds.bottom(); + if (mHistory->mFocusBounds.isEmpty() == false && + mHistory->mFocusBounds.bottom() > viewBottom && viewBottom < mContents.height()) + return false; + if (mHistory->mNavBounds.isEmpty() == false) { + int navTop = mHistory->mNavBounds.y(); + int scrollBottom; + if (testTop < navTop && navTop < (scrollBottom = mScrolledBounds.bottom())) { + mScrolledBounds.setHeight(scrollBottom - navTop); + mScrolledBounds.setY(navTop); + } + } + frameDown(test, NULL, bestData, currentFocus()); + return true; +} + +bool CachedRoot::innerLeft(const CachedNode* test, BestData* bestData) const +{ + ASSERT(minWorkingHorizontal() >= mViewBounds.y()); + ASSERT(maxWorkingHorizontal() <= mViewBounds.bottom()); + setupScrolledBounds(); + mScrolledBounds.setX(mScrolledBounds.x() - mMaxXScroll); + mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll); + int testRight = mScrolledBounds.right(); + int viewLeft = mViewBounds.x(); + if (mHistory->mFocusBounds.isEmpty() == false && + mHistory->mFocusBounds.x() < viewLeft && viewLeft > mContents.x()) + return false; + if (mHistory->mNavBounds.isEmpty() == false) { + int navRight = mHistory->mNavBounds.right(); + int scrollLeft; + if (testRight > navRight && navRight > (scrollLeft = mScrolledBounds.x())) + mScrolledBounds.setWidth(navRight - scrollLeft); + } + frameLeft(test, NULL, bestData, currentFocus()); + return true; +} + + +void CachedRoot::innerMove(const CachedNode* node, BestData* bestData, + Direction direction, WebCore::IntPoint* scroll, bool firstCall) +{ + bestData->reset(); + mFocusChild = false; + bool outOfFocus = mFocus < 0; + bool firstTime = mHistory->didFirstLayout() && outOfFocus; +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + LOGD("%s mHistory->didFirstLayout()=%s && mFocus=%d\n", __FUNCTION__, + mHistory->didFirstLayout() ? "true" : "false", mFocus); +#endif + if (firstTime) + mHistory->reset(); + mHistory->setWorking(direction, currentFocus(), mViewBounds); + bool findClosest = false; + if (mScrollOnly == false) { + switch (direction) { + case LEFT: + if (outOfFocus) + mHistory->mNavBounds = WebCore::IntRect(mViewBounds.right(), + mViewBounds.y(), 1, mViewBounds.height()); + findClosest = innerLeft(node, bestData); + break; + case RIGHT: + if (outOfFocus) + mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x() - 1, + mViewBounds.y(), 1, mViewBounds.height()); + findClosest = innerRight(node, bestData); + break; + case UP: + if (outOfFocus) + mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x(), + mViewBounds.bottom(), mViewBounds.width(), 1); + findClosest = innerUp(node, bestData); + break; + case DOWN: + if (outOfFocus) + mHistory->mNavBounds = WebCore::IntRect(mViewBounds.x(), + mViewBounds.y() - 1, mViewBounds.width(), 1); + findClosest = innerDown(node, bestData); + break; + case UNINITIALIZED: + default: + ASSERT(0); + } + } + if (firstCall) + mHistory->mPriorBounds = mHistory->mNavBounds; // bounds always advances, even if new node is ultimately NULL + bestData->mMouseBounds = bestData->mNodeBounds; + if (adjustForScroll(bestData, direction, scroll, findClosest)) + return; + if (bestData->mNode != NULL) { + mHistory->addToVisited(bestData->mNode, direction); + mHistory->mNavBounds = mHistory->mFocusBounds = bestData->mNodeBounds; + mHistory->mMouseBounds = bestData->mMouseBounds; + } else if (scroll->x() != 0 || scroll->y() != 0) { + WebCore::IntRect newBounds = mHistory->mNavBounds; + int offsetX = scroll->x(); + int offsetY = scroll->y(); + newBounds.move(offsetX, offsetY); + if (mViewBounds.x() > newBounds.x()) + offsetX = mViewBounds.x() - mHistory->mNavBounds.x(); + else if (mViewBounds.right() < newBounds.right()) + offsetX = mViewBounds.right() - mHistory->mNavBounds.right(); + if (mViewBounds.y() > newBounds.y()) + offsetY = mViewBounds.y() - mHistory->mNavBounds.y(); + else if (mViewBounds.bottom() < newBounds.bottom()) + offsetY = mViewBounds.bottom() - mHistory->mNavBounds.bottom(); + mHistory->mNavBounds.move(offsetX, offsetY); + } + mHistory->setDidFirstLayout(false); +} + +bool CachedRoot::innerRight(const CachedNode* test, BestData* bestData) const +{ + ASSERT(minWorkingHorizontal() >= mViewBounds.y()); + ASSERT(maxWorkingHorizontal() <= mViewBounds.bottom()); + setupScrolledBounds(); + // (align) + mScrolledBounds.setWidth(mScrolledBounds.width() + mMaxXScroll); + int testLeft = mScrolledBounds.x(); + int viewRight = mViewBounds.right(); + if (mHistory->mFocusBounds.isEmpty() == false && + mHistory->mFocusBounds.right() > viewRight && viewRight < mContents.width()) + return false; + if (mHistory->mNavBounds.isEmpty() == false) { + int navLeft = mHistory->mNavBounds.x(); + int scrollRight; + if (testLeft < navLeft && navLeft < (scrollRight = mScrolledBounds.right())) { + mScrolledBounds.setWidth(scrollRight - navLeft); + mScrolledBounds.setX(navLeft); + } + } + frameRight(test, NULL, bestData, currentFocus()); + return true; +} + +bool CachedRoot::innerUp(const CachedNode* test, BestData* bestData) const +{ + ASSERT(minWorkingVertical() >= mViewBounds.x()); + ASSERT(maxWorkingVertical() <= mViewBounds.right()); + setupScrolledBounds(); + mScrolledBounds.setY(mScrolledBounds.y() - mMaxYScroll); + mScrolledBounds.setHeight(mScrolledBounds.height() + mMaxYScroll); + int testBottom = mScrolledBounds.bottom(); + int viewTop = mViewBounds.y(); + if (mHistory->mFocusBounds.isEmpty() == false && + mHistory->mFocusBounds.y() < viewTop && viewTop > mContents.y()) + return false; + if (mHistory->mNavBounds.isEmpty() == false) { + int navBottom = mHistory->mNavBounds.bottom(); + int scrollTop; + if (testBottom > navBottom && navBottom > (scrollTop = mScrolledBounds.y())) + mScrolledBounds.setHeight(navBottom - scrollTop); + } + frameUp(test, NULL, bestData, currentFocus()); + return true; +} + +bool CachedRoot::isImage(int x, int y) const +{ + ImageCheck imageCheck; + BoundsCanvas checker(&imageCheck); + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); + checker.setBitmapDevice(bitmap); + checker.translate(SkIntToScalar(-x), SkIntToScalar(-y)); + checker.drawPicture(*mPicture); + return imageCheck.mLastIsImage; +} + +bool CachedRoot::maskIfHidden(BestData* best) const +{ + if (mPicture == NULL) { +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + LOGD("%s missing picture\n", __FUNCTION__); +#endif + return false; + } + const CachedNode* bestNode = best->mNode; + if (bestNode->isUnclipped()) + return false; + // given the picture matching this nav cache + // create an SkBitmap with dimensions of the focus intersected w/ extended view + const WebCore::IntRect& nodeBounds = bestNode->getBounds(); + WebCore::IntRect bounds = nodeBounds; + bounds.intersect(mScrolledBounds); + int leftMargin = bounds.x() == nodeBounds.x() ? kMargin : 0; + int topMargin = bounds.y() == nodeBounds.y() ? kMargin : 0; + int rightMargin = bounds.right() == nodeBounds.right() ? kMargin : 0; + int bottomMargin = bounds.bottom() == nodeBounds.bottom() ? kMargin : 0; + bool unclipped = (leftMargin & topMargin & rightMargin & bottomMargin) != 0; + WebCore::IntRect marginBounds = nodeBounds; + marginBounds.inflate(kMargin); + marginBounds.intersect(mScrolledBounds); + BoundsCheck boundsCheck; + BoundsCanvas checker(&boundsCheck); + boundsCheck.mBounds.set(leftMargin, topMargin, + leftMargin + bounds.width(), topMargin + bounds.height()); + boundsCheck.mBoundsSlop = boundsCheck.mBounds; + boundsCheck.mBoundsSlop.inset(-kSlop, -kSlop); + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, marginBounds.width(), + marginBounds.height()); + checker.setBitmapDevice(bitmap); + // insert probes to be called when the data corresponding to this focus ring is drawn + // need to know if focus ring was generated by text, image, or parent (like div) + // ? need to know (like imdb menu bar) to give up sometimes (when?) + checker.translate(SkIntToScalar(leftMargin - bounds.x()), + SkIntToScalar(topMargin - bounds.y())); + checker.drawPicture(*mPicture); + boundsCheck.checkLast(); + // was it not drawn or clipped out? + if (boundsCheck.hidden()) { // if hidden, return false so that nav can try again + CachedNode* node = const_cast<CachedNode*>(best->mNode); +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + const SkIRect& m = boundsCheck.mBounds; + const SkIRect& s = boundsCheck.mBoundsSlop; + LOGD("%s hidden node:%p (%d) mBounds={%d,%d,%d,%d} mBoundsSlop=" + "{%d,%d,%d,%d}\n", __FUNCTION__, node, node->index(), + m.fLeft, m.fTop, m.fRight, m.fBottom, + s.fLeft, s.fTop, s.fRight, s.fBottom); + const SkIRect& o = boundsCheck.mDrawnOver.getBounds(); + const SkIRect& l = boundsCheck.mLastAll; + const SkIRect& u = boundsCheck.mUnion; + LOGD("%s hidden mDrawnOver={%d,%d,%d,%d} mLastAll={%d,%d,%d,%d}" + " mUnion={%d,%d,%d,%d}\n", __FUNCTION__, + o.fLeft, o.fTop, o.fRight, o.fBottom, + l.fLeft, l.fTop, l.fRight, l.fBottom, + u.fLeft, u.fTop, u.fRight, u.fBottom); + const SkIRect& a = boundsCheck.mAllDrawnIn; + const WebCore::IntRect& c = mScrolledBounds; + const WebCore::IntRect& b = nodeBounds; + LOGD("%s hidden mAllDrawnIn={%d,%d,%d,%d} mScrolledBounds={%d,%d,%d,%d}" + " nodeBounds={%d,%d,%d,%d}\n", __FUNCTION__, + a.fLeft, a.fTop, a.fRight, a.fBottom, + c.x(), c.y(), c.right(), c.bottom(), + b.x(), b.y(), b.right(), b.bottom()); + LOGD("%s bits.mWidth=%d bits.mHeight=%d transX=%d transY=%d\n", __FUNCTION__, + marginBounds.width(),marginBounds.height(), + kMargin - bounds.x(), kMargin - bounds.y()); +#endif + node->setDisabled(true); + node->setClippedOut(unclipped == false); + return true; + } + // was it partially occluded by later drawing? + // if partially occluded, modify the bounds so that the mouse click has a better x,y + const SkIRect& over = boundsCheck.mDrawnOver.getBounds(); + if (over.isEmpty() == false) { +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + SkIRect orig = boundsCheck.mBounds; +#endif + SkIRect& base = boundsCheck.mBounds; + if (base.fLeft < over.fRight && base.fRight > over.fRight) + base.fLeft = over.fRight; + else if (base.fRight > over.fLeft && base.fLeft < over.fLeft) + base.fRight = over.fLeft; + if (base.fTop < over.fBottom && base.fBottom > over.fBottom) + base.fTop = over.fBottom; + else if (base.fBottom > over.fTop && base.fTop < over.fTop) + base.fBottom = over.fTop; +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + const SkIRect& modded = boundsCheck.mBounds; + LOGD("%s partially occluded node:%p (%d) old:{%d,%d,%d,%d} new:{%d,%d,%d,%d}\n", + __FUNCTION__, best->mNode, best->mNode->index(), + orig.fLeft, orig.fTop, orig.fRight, orig.fBottom, + base.fLeft, base.fTop, base.fRight, base.fBottom); +#endif + best->mMouseBounds = WebCore::IntRect(bounds.x() + base.fLeft - kMargin, + bounds.y() + base.fTop - kMargin, base.width(), base.height()); + } + return false; +} + +const CachedNode* CachedRoot::moveFocus(Direction direction, const CachedFrame** framePtr, + WebCore::IntPoint* scroll) +{ +#ifndef NDEBUG + ASSERT(CachedFrame::mDebug.mInUse); +#endif + CachedRoot* frame = this; + const CachedNode* node = frame->document(); + if (node == NULL) + return NULL; + if (mViewBounds.isEmpty()) + return NULL; + resetClippedOut(); + setData(); + BestData bestData; + innerMove(node, &bestData, direction, scroll, true); + *framePtr = bestData.mFrame; + return const_cast<CachedNode*>(bestData.mNode); +} + +void CachedRoot::reset() +{ +#ifndef NDEBUG + ASSERT(CachedFrame::mDebug.mInUse); +#endif + mContents = mViewBounds = WebCore::IntRect(0, 0, 0, 0); + mMaxXScroll = mMaxYScroll = 0; + mSelectionStart = mSelectionEnd = -1; + mScrollOnly = false; +// resetNavClipBounds(); +} + +bool CachedRoot::scrollDelta(WebCore::IntRect& newOutset, Direction direction, int* delta) +{ + switch (direction) { + case LEFT: + *delta = -mMaxXScroll; + return newOutset.x() >= mViewBounds.x(); + case RIGHT: + *delta = mMaxXScroll; + return newOutset.right() <= mViewBounds.right(); + case UP: + *delta = -mMaxYScroll; + return newOutset.y() >= mViewBounds.y(); + case DOWN: + *delta = mMaxYScroll; + return newOutset.bottom() <= mViewBounds.bottom(); + default: + *delta = 0; + ASSERT(0); + } + return false; +} + +void CachedRoot::setCachedFocus(CachedFrame* frame, CachedNode* node) +{ +#if !defined NDEBUG + ASSERT(CachedFrame::mDebug.mInUse); +#endif +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + const CachedNode* focus = currentFocus(); + WebCore::IntRect bounds; + if (focus) + bounds = focus->bounds(); + LOGD("%s old focus %d (nodePointer=%p) bounds={%d,%d,%d,%d}\n", __FUNCTION__, + focus ? focus->index() : 0, + focus ? focus->nodePointer() : NULL, bounds.x(), bounds.y(), + bounds.width(), bounds.height()); +#endif + clearFocus(); + if (node == NULL) + return; + node->setIsFocus(true); + ASSERT(node->isFrame() == false); + frame->setFocusIndex(node - frame->document()); + ASSERT(frame->focusIndex() > 0 && frame->focusIndex() < (int) frame->size()); + CachedFrame* parent; + while ((parent = frame->parent()) != NULL) { + parent->setFocusIndex(frame->indexInParent()); + frame = parent; + } +#if DEBUG_NAV_UI && !defined BROWSER_DEBUG + focus = currentFocus(); + bounds = WebCore::IntRect(0, 0, 0, 0); + if (focus) + bounds = focus->bounds(); + LOGD("%s new focus %d (nodePointer=%p) bounds={%d,%d,%d,%d}\n", __FUNCTION__, + focus ? focus->index() : 0, + focus ? focus->nodePointer() : NULL, bounds.x(), bounds.y(), + bounds.width(), bounds.height()); +#endif +} + +void CachedRoot::setupScrolledBounds() const +{ + mScrolledBounds = mViewBounds; +} + +#if DUMP_NAV_CACHE + +#define DEBUG_PRINT_BOOL(field) \ + DUMP_NAV_LOGD("// bool " #field "=%s;\n", b->field ? "true" : "false") + +CachedRoot* CachedRoot::Debug::base() const { + CachedRoot* nav = (CachedRoot*) ((char*) this - OFFSETOF(CachedRoot, mDebug)); + return nav; +} + +void CachedRoot::Debug::print() const +{ +#ifdef DUMP_NAV_CACHE_USING_PRINTF + gWriteLogMutex.lock(); + ASSERT(gNavCacheLogFile == NULL); + gNavCacheLogFile = fopen(NAV_CACHE_LOG_FILE, "a"); +#endif + CachedRoot* b = base(); + b->CachedFrame::mDebug.print(); + b->mHistory->mDebug.print(b); + DUMP_NAV_LOGD("// int mMaxXScroll=%d, mMaxYScroll=%d;\n", + b->mMaxXScroll, b->mMaxYScroll); + DEBUG_PRINT_BOOL(mFocusChild); +#ifdef DUMP_NAV_CACHE_USING_PRINTF + if (gNavCacheLogFile) + fclose(gNavCacheLogFile); + gNavCacheLogFile = NULL; + gWriteLogMutex.unlock(); +#endif +} + +#endif + +} diff --git a/WebCore/platform/android/nav/CachedRoot.h b/WebCore/platform/android/nav/CachedRoot.h new file mode 100644 index 0000000..e28141e --- /dev/null +++ b/WebCore/platform/android/nav/CachedRoot.h @@ -0,0 +1,101 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef CachedRoot_H +#define CachedRoot_H + +#include "CachedFrame.h" +#include "IntPoint.h" +#include "SkPicture.h" + +namespace android { + +class CachedHistory; +class CachedNode; + +class CachedRoot : public CachedFrame { +public: + bool adjustForScroll(BestData* , Direction , WebCore::IntPoint* scrollPtr, + bool findClosest); + int checkForCenter(int x, int y) const; + void checkForJiggle(int* ) const; + int documentHeight() { return mContents.height(); } + int documentWidth() { return mContents.width(); } + const CachedNode* findAt(const WebCore::IntRect& , const CachedFrame** , + int* x, int* y) const; + bool focusChild() const { return mFocusChild; } + WebCore::IntPoint focusLocation() const; + int generation() const { return mGeneration; } + int textGeneration() const { return mTextGeneration; } + int getAndResetSelectionEnd(); + int getAndResetSelectionStart(); +// const WebCore::IntRect& navClipBounds() const { return mClippedBounds; } + void getSimulatedMousePosition(WebCore::IntPoint* ); +// bool hasNavClipBounds() { return mClippedBounds.isEmpty() == false; } + void init(WebCore::FrameAndroid* , CachedHistory* ); + bool innerDown(const CachedNode* , BestData* ) const; + bool innerLeft(const CachedNode* , BestData* ) const; + void innerMove(const CachedNode* ,BestData* bestData, Direction , + WebCore::IntPoint* scroll, bool firstCall); + bool innerRight(const CachedNode* , BestData* ) const; + bool innerUp(const CachedNode* , BestData* ) const; + bool isImage(int x, int y) const; + bool maskIfHidden(BestData* ) const; + const CachedNode* moveFocus(Direction , const CachedFrame** , WebCore::IntPoint* scroll); + void reset(); +// void resetNavClipBounds() { mClippedBounds = WebCore::IntRect(-1, -1, 0, 0); } + CachedHistory* rootHistory() const { return mHistory; } + bool scrollDelta(WebCore::IntRect& focusRingBounds, Direction , int* delta); + const WebCore::IntRect& scrolledBounds() const { return mScrolledBounds; } + void setCachedFocus(CachedFrame* , CachedNode* ); + void setGeneration(int generation) { mGeneration = generation; } + void setTextGeneration(int textGeneration) { mTextGeneration = textGeneration; } + void setFocusChild(bool state) const { mFocusChild = state; } + void setMaxScroll(int x, int y) { mMaxXScroll = x; mMaxYScroll = y; } +// void setNavClipBounds(const WebCore::IntRect& r) { mClippedBounds = r; } + void setPicture(SkPicture* picture) { mPicture = picture; } + void setScrollOnly(bool state) { mScrollOnly = state; } + void setSelection(int start, int end) { mSelectionStart = start; mSelectionEnd = end; } + void setupScrolledBounds() const; + void setVisibleRect(const WebCore::IntRect& r) { mViewBounds = r; } + int width() const { return mPicture ? mPicture->width() : 0; } +private: + CachedHistory* mHistory; + SkPicture* mPicture; + // WebCore::IntRect mClippedBounds; + mutable WebCore::IntRect mScrolledBounds; // view bounds + amount visible as result of scroll + int mGeneration; + int mTextGeneration; + int mMaxXScroll; + int mMaxYScroll; + // These two are ONLY used when the tree is rebuilt and the focus is a textfield/area + int mSelectionStart; + int mSelectionEnd; + mutable bool mFocusChild; // temporary state set if walked nodes are children of focus + bool mScrollOnly; +#if DUMP_NAV_CACHE +public: + class Debug { +public: + CachedRoot* base() const; + void print() const; + } mDebug; +#endif +}; + +} + +#endif diff --git a/WebCore/platform/android/nav/SelectText.cpp b/WebCore/platform/android/nav/SelectText.cpp new file mode 100644 index 0000000..e84d2dc --- /dev/null +++ b/WebCore/platform/android/nav/SelectText.cpp @@ -0,0 +1,246 @@ +/* + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "webcoreglue" + +#include <config.h> +#include "CachedDebug.h" +#include "SelectText.h" +#include "SkBitmap.h" +#include "SkBounder.h" +#include "SkCanvas.h" +#include "SkMatrix.h" +#include "SkPicture.h" +#include "SkPoint.h" +#include "SkRect.h" +#include "SkRegion.h" + +#ifdef LOG +#undef LOG +#endif + +#include <utils/Log.h> + +class CommonCheck : public SkBounder { +public: + CommonCheck() : mMatrix(NULL), mPaint(NULL) {} + + virtual void setUp(const SkPaint& paint, const SkMatrix& matrix, SkScalar y) { + mMatrix = &matrix; + mPaint = &paint; + mY = y; + mTop = INT_MAX; + mBottom = INT_MAX; + } + + int top() { + if (mTop == INT_MAX) { + SkPoint result; + SkPaint::FontMetrics metrics; + mPaint->getFontMetrics(&metrics); + mMatrix->mapXY(0, metrics.fAscent + mY, &result); + mTop = SkScalarFloor(result.fY); + DBG_NAV_LOGD("mMatrix:(%d, %d) mTop=%g", + mMatrix->getTranslateX(), mMatrix->getTranslateY(), mTop); + } + return mTop; + } + + int bottom() { + if (mBottom == INT_MAX) { + SkPoint result; + SkPaint::FontMetrics metrics; + mPaint->getFontMetrics(&metrics); + mMatrix->mapXY(0, metrics.fDescent + mY, &result); + mBottom = SkScalarCeil(result.fY); + } + return mBottom; + } + +protected: + const SkMatrix* mMatrix; + const SkPaint* mPaint; + int mBottom; + int mTop; + SkScalar mY; +}; + +class FirstCheck : public CommonCheck { +public: + FirstCheck(int x, int y) + : mDistance(INT_MAX), mFocusX(x), mFocusY(y) { + mBestBounds.setEmpty(); + } + + const SkIRect& bestBounds() { + DBG_NAV_LOGD("mBestBounds:(%d, %d, %d, %d) mTop=%g mBottom=%g", + mBestBounds.fLeft, mBestBounds.fTop, mBestBounds.fRight, + mBestBounds.fBottom, SkScalarToFloat(mTop), SkScalarToFloat(mBottom)); + return mBestBounds; + } + + void offsetBounds(int dx, int dy) { + mBestBounds.offset(dx, dy); + } + + virtual bool onIRect(const SkIRect& rect) { + if (mBestBounds.isEmpty()) { + mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); + } else { + int dx = ((rect.fLeft + rect.fRight) >> 1) - mFocusX; + int dy = SkScalarRound(((top() + bottom()) / 2)) - mFocusY; + int distance = dx * dx + dy * dy; + if (mDistance > distance) { + mDistance = distance; + mBestBounds.set(rect.fLeft, top(), rect.fRight, bottom()); + } + } + return false; + } +protected: + SkIRect mBestBounds; + int mDistance; + int mFocusX; + int mFocusY; +}; + +class MultilineBuilder : public CommonCheck { +public: + MultilineBuilder(const SkIRect& start, const SkIRect& end, int dx, int dy, + SkRegion* region) + : mStart(start), mEnd(end), mSelectRegion(region), mCapture(false) { + mLast.setEmpty(); + mStart.offset(-dx, -dy); + mEnd.offset(-dx, -dy); + } + + virtual bool onIRect(const SkIRect& rect) { + bool captureLast = false; + if ((rect.fLeft == mStart.fLeft && rect.fRight == mStart.fRight && + top() == mStart.fTop && bottom() == mStart.fBottom) || + (rect.fLeft == mEnd.fLeft && rect.fRight == mEnd.fRight && + top() == mEnd.fTop && bottom() == mEnd.fBottom)) { + captureLast = mCapture; + mCapture ^= true; + } + if (mCapture || captureLast) { + SkIRect full; + full.set(rect.fLeft, top(), rect.fRight, bottom()); + if (mLast.fTop < full.fBottom && mLast.fBottom > full.fTop) { + if (full.fLeft > mLast.fRight) + full.fLeft = mLast.fRight; + else if (full.fRight < mLast.fLeft) + full.fRight = mLast.fLeft; + } + mSelectRegion->op(full, SkRegion::kUnion_Op); + mLast = full; + if (mStart == mEnd) + mCapture = false; + } + return false; + } +protected: + SkIRect mStart; + SkIRect mEnd; + SkIRect mLast; + SkRegion* mSelectRegion; + bool mCapture; +}; + +class TextCanvas : public SkCanvas { +public: + + TextCanvas(CommonCheck* bounder, const SkPicture& picture, const SkIRect& area) + : mBounder(*bounder) { + setBounder(bounder); + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, area.width(), + area.height()); + setBitmapDevice(bitmap); + translate(SkIntToScalar(-area.fLeft), SkIntToScalar(-area.fTop)); + } + + virtual ~TextCanvas() { + setBounder(NULL); + } + + virtual void drawPaint(const SkPaint& paint) { + } + + virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[], + const SkPaint& paint) { + } + + virtual void drawRect(const SkRect& rect, const SkPaint& paint) { + } + + virtual void drawPath(const SkPath& path, const SkPaint& paint) { + } + + virtual void commonDrawBitmap(const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint) { + } + + virtual void drawSprite(const SkBitmap& bitmap, int left, int top, + const SkPaint* paint = NULL) { + } + + virtual void drawText(const void* text, size_t byteLength, SkScalar x, + SkScalar y, const SkPaint& paint) { + mBounder.setUp(paint, getTotalMatrix(), y); + SkCanvas::drawText(text, byteLength, x, y, paint); + } + + virtual void drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, + const SkPaint& paint) { + mBounder.setUp(paint, getTotalMatrix(), constY); + SkCanvas::drawPosTextH(text, byteLength, xpos, constY, paint); + } + + virtual void drawVertices(VertexMode vmode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkXfermode* xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + } + + CommonCheck& mBounder; +}; + +void CopyPaste::buildSelection(const SkPicture& picture, const SkIRect& area, + const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region) { + DBG_NAV_LOGD("area=(%d, %d, %d, %d) selStart=(%d, %d, %d, %d)" + " selEnd=(%d, %d, %d, %d)", + area.fLeft, area.fTop, area.fRight, area.fBottom, + selStart.fLeft, selStart.fTop, selStart.fRight, selStart.fBottom, + selEnd.fLeft, selEnd.fTop, selEnd.fRight, selEnd.fBottom); + MultilineBuilder builder(selStart, selEnd, area.fLeft, area.fTop, region); + TextCanvas checker(&builder, picture, area); + checker.drawPicture(const_cast<SkPicture&>(picture)); + region->translate(area.fLeft, area.fTop); +} + +SkIRect CopyPaste::findClosest(const SkPicture& picture, const SkIRect& area, + int x, int y) { + FirstCheck _check(x - area.fLeft, y - area.fTop); + DBG_NAV_LOGD("area=(%d, %d, %d, %d) x=%d y=%d", area.fLeft, area.fTop, + area.fRight, area.fBottom, x, y); + TextCanvas checker(&_check, picture, area); + checker.drawPicture(const_cast<SkPicture&>(picture)); + _check.offsetBounds(area.fLeft, area.fTop); + return _check.bestBounds(); +} diff --git a/WebCore/platform/android/nav/SelectText.h b/WebCore/platform/android/nav/SelectText.h new file mode 100644 index 0000000..ba0b089 --- /dev/null +++ b/WebCore/platform/android/nav/SelectText.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2008, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SELECT_TEXT_H +#define SELECT_TEXT_H + +class SkPicture; +struct SkIRect; +struct SkIPoint; +class SkRegion; + +class CopyPaste { +public: + static void buildSelection(const SkPicture& , const SkIRect& area, + const SkIRect& selStart, const SkIRect& selEnd, SkRegion* region); + static SkIRect findClosest(const SkPicture& , const SkIRect& area, + int x, int y); +}; + +#endif diff --git a/WebCore/platform/android/nav/WebView.cpp b/WebCore/platform/android/nav/WebView.cpp new file mode 100644 index 0000000..7e4791d --- /dev/null +++ b/WebCore/platform/android/nav/WebView.cpp @@ -0,0 +1,1922 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "webviewglue" + +#include <config.h> + +#include "android_graphics.h" +#include "AtomicString.h" +#include "CachedFrame.h" +#include "CachedNode.h" +#include "CachedRoot.h" +#include "Frame.h" +#include "GraphicsJNI.h" +#include "IntPoint.h" +#include "IntRect.h" +#include "Node.h" +#include "PlatformGraphicsContext.h" +#include "PlatformString.h" +#include "SelectText.h" +#include "SkCanvas.h" +#include "SkPath.h" +#include "SkPicture.h" +#include "SkPixelXorXfermode.h" +#include "SkRect.h" +#include "SkTime.h" +#include "WebViewCore.h" + +#ifdef LOG // WebKit may define this as well; if so, undefine it so LOGD will work +#undef LOG +#endif + +#ifdef GET_NATIVE_VIEW +#undef GET_NATIVE_VIEW +#endif + +#define GET_NATIVE_VIEW(env, obj) ((WebView*)env->GetIntField(obj, gWebViewField)) + +#include <ui/KeycodeLabels.h> +#include <utils/Log.h> +#include <JNIHelp.h> +#include <jni.h> + +#define REPLAY_BUFFER_SIZE 4096 + +namespace android { + +// This class provides JNI for making calls into native code from the UI side +// of the multi-threaded WebView. +struct CommonParams { + enum Trigger { + NO_DATA, + CLEAR_FOCUS_PARAMS, + FIRST_MOVE_FOCUS_PARAMS, + MOVE_FOCUS_PARAMS, + MOTION_UP_PARAMS + } mTrigger; + int mGeneration; +}; + +struct CacheParams { + void setFocus(const CachedNode* node, + const CachedFrame* frame, const CachedRoot* root, + const WebCore::IntPoint& focusLocation) + { + mNode = (WebCore::Node* ) (node ? node->nodePointer() : NULL); + mFrame = (WebCore::Frame* ) (node ? frame->framePointer() : NULL); + mX = focusLocation.x(); + mY = focusLocation.y(); + } + + WebCore::Node* mNode; + WebCore::Frame* mFrame; + int mX; + int mY; +}; + +struct ClearFocusParams { + CommonParams d; + CacheParams c; + int mX; + int mY; +}; + +struct MotionUpParams { + CommonParams d; + int mX; + int mY; + int mSlop; + bool mIsClick; +}; + +struct FirstMoveFocusParams { + CommonParams d; + int mKeyCode; + int mCount; + bool mIgnoreScroll; +}; + +struct MoveFocusParams { + FirstMoveFocusParams d; + CacheParams c; + void* mSentFocus; + WebCore::IntRect mClipRect; + WebCore::IntRect mSentBounds; + WebCore::IntRect mVisibleRect; + CachedHistory mHistory; // FIXME: make this a subset + int mXMax; + int mYMax; +}; + +typedef MoveFocusParams LargestParams; + +#if DEBUG_NAV_UI +static const char* TriggerNames[] = { + "*** no data ! ***", + "clearFocus", + "firstMoveFocus", + "moveFocus", + "motionUp" +}; +#endif + +class FocusReplay { +public: +FocusReplay() : mStart(mBuffer), mEnd(mBuffer), mLastGeneration(0) +{ +} + +// find the most recent common data +void add(const CommonParams& data, size_t len) +{ + DBG_NAV_LOGD("mStart=%d mEnd=%d trigger=%s moveGeneration=%d", mStart - mBuffer, + mEnd - mBuffer, TriggerNames[data.mTrigger], data.mGeneration); + mLastGeneration = data.mGeneration; + char* limit = mBuffer + sizeof(mBuffer); + int used = mEnd - mStart; + if (used < 0) + used += sizeof(mBuffer); + int needed = (int) len - ((int) sizeof(mBuffer) - used); + if (needed >= 0) + reclaim(++needed); + if (mEnd + len <= limit) { + memcpy(mEnd, (void*) &data, len); + mEnd += len; + DBG_NAV_LOGD("mStart=%d mEnd=%d", mStart - mBuffer, mEnd - mBuffer); + return; + } + size_t partial = limit - mEnd; + memcpy(mEnd, (void*) &data, partial); + const void* remainder = (const void*) ((const char*) &data + partial); + partial = len - partial; + memcpy(mBuffer, remainder, partial); + mEnd = mBuffer + partial; + DBG_NAV_LOGD("wrap mStart=%d mEnd=%d", + mStart - mBuffer, mEnd - mBuffer); +} + +int count() +{ + DBG_NAV_LOGD("mStart=%d mEnd=%d", + mStart - mBuffer, mEnd - mBuffer); + if (mStart == mEnd) + return 0; + char* limit = mBuffer + sizeof(mBuffer); + char* saveStart = mStart; + int result = 0; + while (true) { + ++result; + mStart += triggerSize(); + if (mStart == mEnd) + break; + if (mStart >= limit) + mStart -= sizeof(mBuffer); + } + mStart = saveStart; + DBG_NAV_LOGD("count=%d", result); + return result; +} + +void discard(int generation) +{ + DBG_NAV_LOGD("generation=%d", generation); + LargestParams storage; + const CommonParams& params = storage.d.d; + char* pos = position(); + retrieve(&storage.d.d); + if (params.mGeneration > generation) { + DBG_NAV_LOGD("params.mGeneration=%d > generation=%d", + params.mGeneration, generation); + rewind(pos); + DBG_NAV_LOGD("mStart=%d mEnd=%d", mStart - mBuffer, mEnd - mBuffer); + return; + } + LOG_ASSERT(params.mGeneration == generation, "params.mGeneration != generation"); + DBG_NAV_LOGD("mStart=%d mEnd=%d", mStart - mBuffer, mEnd - mBuffer); +} + +int lastAdd() +{ + return mLastGeneration; +} + +char* position() +{ + return mStart; +} + +int retrieve(CommonParams* data) +{ + if (mEnd == mStart) { + // changed from LOGD to LOGV, as it always fires when I click to center + // text (mrr) + LOGV("%s *** no data to retrieve (error condition) ***", __FUNCTION__); + data->mTrigger = CommonParams::NO_DATA; + return data->mGeneration = INT_MAX; + } + DBG_NAV_LOGD("mStart=%d mEnd=%d", + mStart - mBuffer, mEnd - mBuffer); + char* limit = mBuffer + sizeof(mBuffer); + size_t size = triggerSize(); + if (mStart < mEnd) { + LOG_ASSERT((size_t) (mEnd - mStart) >= size, "mEnd - mStart < size"); + memcpy(data, mStart, size); + mStart += size; + } else { + int partial = limit - mStart; + if (partial > (int) size) + partial = size; + memcpy(data, mStart, partial); + mStart += partial; + void* remainder = (void*) ((char*) data + partial); + partial = size - partial; + if (partial > 0) { + memcpy(remainder, mBuffer, partial); + mStart = mBuffer + partial; + LOG_ASSERT(mStart <= mEnd, "mStart > mEnd"); + } + } + if (mStart == limit) { + mStart = mBuffer; + if (mEnd == limit) + mEnd = mBuffer; + } + DBG_NAV_LOGD("mStart=%d mEnd=%d trigger=%s moveGeneration=%d", + mStart - mBuffer, mEnd - mBuffer, TriggerNames[data->mTrigger], + data->mGeneration); + return data->mGeneration; +} + +void rewind(char* pos) +{ + mStart = pos; +} + +private: +void reclaim(int needed) +{ + DBG_NAV_LOGD("needed=%d", needed); + char* limit = mBuffer + sizeof(mBuffer); + do { + size_t size = triggerSize(); + mStart += size; + needed -= size; + if (mStart >= limit) { + mStart = mBuffer + (mStart - limit); + if (mEnd == limit) + mEnd = mBuffer; + } + } while (needed > 0 && mStart != mEnd); + DBG_NAV_LOGD("mStart=%d mEnd=%d", + mStart - mBuffer, mEnd - mBuffer); +} + +size_t triggerSize() +{ + LOG_ASSERT(mStart != mEnd, "mStart == mEnd"); + char* limit = mBuffer + sizeof(mBuffer); + LOG_ASSERT(mStart + sizeof(CommonParams::Trigger) <= limit, "trigger not in limit"); + CommonParams::Trigger trigger; + memcpy(&trigger, mStart, sizeof(trigger)); + switch (trigger) { + case CommonParams::CLEAR_FOCUS_PARAMS: + return sizeof(ClearFocusParams); + case CommonParams::FIRST_MOVE_FOCUS_PARAMS: + return sizeof(FirstMoveFocusParams); + case CommonParams::MOVE_FOCUS_PARAMS: + return sizeof(MoveFocusParams); + case CommonParams::MOTION_UP_PARAMS: + return sizeof(MotionUpParams); + default: + LOG_ASSERT(0, "trigger undefined"); + } + return 0; +} + +char mBuffer[REPLAY_BUFFER_SIZE]; +char* mStart; +char* mEnd; +int mLastGeneration; +}; // end of helper class ReplayFocus + +static jfieldID gWebViewField; + +//------------------------------------- + +extern JavaVM* jnienv_to_javavm(JNIEnv* env); +extern JNIEnv* javavm_to_jnienv(JavaVM* vm); + +static bool checkException(JNIEnv* env) +{ + if (env->ExceptionCheck() != 0) + { + LOGE("*** Uncaught exception returned from Java call!\n"); + env->ExceptionDescribe(); + return true; + } + return false; +} + +static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[]) +{ + jmethodID m = env->GetMethodID(clazz, name, signature); + LOG_ASSERT(m, "Could not find method %s", name); + return m; +} + +//------------------------------------- +class WebView +{ +public: +enum FrameCachePermission { + DONT_ALLOW_NEWER, + ALLOW_NEWER, + ALLOW_NEWEST +}; + +enum OutOfFocusFix { + DO_NOTHING, + CLEAR_TEXT_ENTRY, + UPDATE_TEXT_ENTRY +}; + +struct JavaGlue +{ + JavaVM* mJVM; + jobject mObj; + jmethodID mClearTextEntry; + jmethodID mScrollBy; + jmethodID mSendFinalFocus; + jmethodID mSendKitFocus; + jmethodID mSendMotionUp; + jmethodID mSetFocusData; + jmethodID mGetScaledMaxXScroll; + jmethodID mGetScaledMaxYScroll; + jmethodID mGetVisibleRect; + jmethodID mUpdateTextEntry; + jmethodID mViewInvalidate; + jmethodID mPostInvalidateDelayed; + jfieldID mRectLeft; + jfieldID mRectTop; + jmethodID mRectWidth; + jmethodID mRectHeight; + jfieldID mFocusNode; + jmethodID mSetAll; +} mJavaGlue; + +WebView(JNIEnv* env, jobject javaWebView, int viewImpl) +{ + jclass clazz = env->FindClass("android/webkit/WebView"); + // mJavaGlue = new JavaGlue; + mJavaGlue.mJVM = jnienv_to_javavm(env); + mJavaGlue.mObj = env->NewGlobalRef(javaWebView); + mJavaGlue.mScrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(II)V"); + mJavaGlue.mClearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V"); + mJavaGlue.mSendFinalFocus = GetJMethod(env, clazz, "sendFinalFocus", "(IIII)V"); + mJavaGlue.mSendKitFocus = GetJMethod(env, clazz, "sendKitFocus", "()V"); + mJavaGlue.mSendMotionUp = GetJMethod(env, clazz, "sendMotionUp", "(IIIIIIIZZ)V"); + mJavaGlue.mSetFocusData = GetJMethod(env, clazz, "setFocusData", "(IIIIIIZ)V"); + mJavaGlue.mGetScaledMaxXScroll = GetJMethod(env, clazz, "getScaledMaxXScroll", "()I"); + mJavaGlue.mGetScaledMaxYScroll = GetJMethod(env, clazz, "getScaledMaxYScroll", "()I"); + mJavaGlue.mGetVisibleRect = GetJMethod(env, clazz, "sendOurVisibleRect", "()Landroid/graphics/Rect;"); + mJavaGlue.mUpdateTextEntry = GetJMethod(env, clazz, "updateTextEntry", "()V"); + mJavaGlue.mViewInvalidate = GetJMethod(env, clazz, "viewInvalidate", "()V"); + jclass viewClass = env->FindClass("android/view/View"); + mJavaGlue.mPostInvalidateDelayed = GetJMethod(env, viewClass, + "postInvalidateDelayed", "(JIIII)V"); + jclass rectClass = env->FindClass("android/graphics/Rect"); + LOG_ASSERT(rectClass, "Could not find Rect class"); + mJavaGlue.mRectLeft = env->GetFieldID(rectClass, "left", "I"); + mJavaGlue.mRectTop = env->GetFieldID(rectClass, "top", "I"); + mJavaGlue.mRectWidth = GetJMethod(env, rectClass, "width", "()I"); + mJavaGlue.mRectHeight = GetJMethod(env, rectClass, "height", "()I"); + + // Set up class for updateFocusNode + jclass focusnodeClass = env->FindClass("android/webkit/WebView$FocusNode"); + LOG_ASSERT(focusnodeClass, "Could not find FocusNode class!"); + mJavaGlue.mFocusNode = env->GetFieldID(clazz, "mFocusNode", "Landroid/webkit/WebView$FocusNode;"); + mJavaGlue.mSetAll = GetJMethod(env, focusnodeClass, "setAll", "(ZZZZZIIIIIIIILjava/lang/String;Ljava/lang/String;I)V"); + env->DeleteLocalRef(focusnodeClass); + + env->SetIntField(javaWebView, gWebViewField, (jint)this); + mViewImpl = (WebViewCore*) viewImpl; + mFrameCacheUI = NULL; + mNavPictureUI = NULL; + mInvalidNode = NULL; + mGeneration = 0; + mHeightCanMeasure = false; + mFollowedLink = false; + mLastDx = 0; + mLastDxTime = 0; + mRingAnimationEnd = 0; + mUIButtons = NULL; + mSelStart.setEmpty(); + mSelEnd.setEmpty(); +} + +~WebView() +{ + if (mJavaGlue.mObj) + { + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->DeleteGlobalRef(mJavaGlue.mObj); + mJavaGlue.mObj = 0; + } + delete mFrameCacheUI; + delete mNavPictureUI; + if (mUIButtons != NULL) { + mUIButtons->deleteAll(); + delete mUIButtons; + } +} + +void clearFocus(int x, int y, bool inval) +{ + DBG_NAV_LOGD("x=%d y=%d inval=%s", x, y, + inval ? "true" : "false"); + clearTextEntry(); + CachedRoot* root = getFrameCache(ALLOW_NEWER); + if (root == NULL) + return; + const CachedFrame* oldFrame = NULL; + const CachedNode* oldFocusNode = root->currentFocus(&oldFrame); + WebCore::IntPoint focusLocation = WebCore::IntPoint(0, 0); + setFocusData(root->generation(), NULL, NULL, x, y, oldFocusNode == NULL); + sendKitFocus(); + if (oldFocusNode != NULL) { + DBG_NAV_LOG("oldFocusNode != NULL"); + focusLocation = root->focusLocation(); + root->setCachedFocus(NULL, NULL); + if (inval) + viewInvalidate(); + } + ClearFocusParams params; + params.d.mTrigger = CommonParams::CLEAR_FOCUS_PARAMS; + params.d.mGeneration = mGeneration; + params.c.setFocus(oldFocusNode, oldFrame, root, focusLocation); + params.mX = x; + params.mY = y; + mReplay.add(params.d, sizeof(params)); +} + +void clearTextEntry() +{ + DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mClearTextEntry); + checkException(env); +} + +#if DUMP_NAV_CACHE +void debugDump() +{ + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (root) + root->mDebug.print(); +} +#endif + +#ifdef BUCKET_EXPERIMENT +void clearContentPicture() { + if (mViewImpl != NULL) { + mViewImpl->mUIBuckets.clear(); + } +} + +void drawContentPicture(SkCanvas* canvas) { + WTF::Vector<WebViewCore::Bucket>& buckets = mViewImpl->mUIBuckets; + const WebViewCore::Bucket* const end = buckets.end(); + for (WebViewCore::Bucket* bucket = buckets.begin(); bucket != end; bucket++) { + SkRect bounds; + const WebCore::IntRect& buckB = bucket->mBounds; + bounds.set(SkIntToScalar(buckB.x()), SkIntToScalar(buckB.y()), + SkIntToScalar(buckB.right()), SkIntToScalar(buckB.bottom())); + if (canvas->quickReject(bounds, SkCanvas::kBW_EdgeType)) { + continue; + } + canvas->drawPicture(*bucket->mPicture); + } +} +#endif + +// Traverse our stored array of buttons that are in our picture, and update +// their subpictures according to their current focus state. +// Called from the UI thread. This is the one place in the UI thread where we +// access the buttons stored in the WebCore thread. +void nativeRecordButtons() +{ + // Check to see if the WebCore thread has created a new set of buttons. + // We don't have to grab the mutex for this test, since the only place it + // could be set to null is right below, or in WebViewCore's constructor. + if (mViewImpl->mButtons != NULL) { + if (mUIButtons != NULL) { + mUIButtons->deleteAll(); + delete mUIButtons; + } + // Transfer ownership of the array from the WebCore thread (viewimpl) to + // the UI thread + mViewImpl->gButtonMutex.lock(); + mUIButtons = mViewImpl->mButtons; + mViewImpl->mButtons = NULL; + mViewImpl->gButtonMutex.unlock(); + } + + // If mUIButtons has never been set, or it has been set with an array of no + // buttons (which happens if a page does not have any buttons), then return. + if (mUIButtons == NULL || mUIButtons->count() == 0) + return; + + // Find the focused node so we can determine which node has focus, and + // therefore which state to paint them in. + // FIXME: In a future change, we should keep track of whether the focus has + // changed to short circuit (note that we would still need to update if we + // received new buttons from the WebCore thread). + WebCore::Node* focus = NULL; + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (root != NULL) { + const CachedNode* cachedNode = root->currentFocus(); + if (cachedNode != NULL) + focus = (WebCore::Node*) cachedNode->nodePointer(); + } + + // Traverse the array, and update each button, depending on whether it is + // focused. + Container** end = mUIButtons->end(); + for (Container** ptr = mUIButtons->begin(); ptr != end; ptr++) { + (*ptr)->updateFocusState(focus); + } +} + +void drawFocusRing(SkCanvas* canvas) +{ + const CachedRoot* root = getFrameCache(ALLOW_NEWER); + if (root == NULL) { + DBG_NAV_LOG("root==NULL"); + mFollowedLink = false; + return; + } + const CachedNode* node = root->currentFocus(); + if (node == NULL) { + DBG_NAV_LOG("node==NULL"); + mFollowedLink = false; + return; + } + if (node->hasFocusRing() == false) { + DBG_NAV_LOG("node->hasFocusRing() == false"); + return; + } + const WTF::Vector<WebCore::IntRect>& rings = node->focusRings(); + if (0 == rings.size()) { + DBG_NAV_LOG("rings.size()==0"); + return; + } + + bool isButton = false; + // If this is a button drawn by us (rather than webkit) do not draw the + // focus ring, since its focus will be shown by a change in what we draw. + if (mUIButtons != NULL) { + // Should be in sync with recordButtons, since that will be called + // before this. + if (mUIButtons->count() > 0) { + WebCore::Node* focusPointer = (WebCore::Node*) node->nodePointer(); + Container** end = mUIButtons->end(); + for (Container** ptr = mUIButtons->begin(); ptr != end; ptr++) { + if ((*ptr)->matches(focusPointer)) { + isButton = true; + break; + } + } + } + } + FocusRing::Flavor flavor = isButton ? FocusRing::BUTTON_NO_RING : + node->type() != NORMAL_CACHEDNODETYPE ? + FocusRing::FAKE_FLAVOR : node->nodePointer() == mInvalidNode ? + FocusRing::INVALID_FLAVOR : FocusRing::NORMAL_FLAVOR; + if (flavor != FocusRing::INVALID_FLAVOR && mFollowedLink) { + flavor = (FocusRing::Flavor) (flavor + FocusRing::NORMAL_ANIMATING); + } +#if DEBUG_NAV_UI + const WebCore::IntRect& ring = rings[0]; + DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p) flavor=%s rings=%d" + " (%d, %d, %d, %d)", node->index(), node->nodePointer(), + flavor == FocusRing::FAKE_FLAVOR ? "FAKE_FLAVOR" : + flavor == FocusRing::INVALID_FLAVOR ? "INVALID_FLAVOR" : + flavor == FocusRing::NORMAL_ANIMATING ? "NORMAL_ANIMATING" : + flavor == FocusRing::FAKE_ANIMATING ? "FAKE_ANIMATING" : + flavor == FocusRing::BUTTON_NO_RING ? "BUTTON_NO_RING" : + flavor == FocusRing::BUTTON_ANIMATING ? "BUTTON_ANIMATING" : "NORMAL_FLAVOR", + rings.size(), ring.x(), ring.y(), ring.width(), ring.height()); +#endif + if (flavor >= FocusRing::NORMAL_ANIMATING) { + SkMSec time = SkTime::GetMSecs(); + if (time < mRingAnimationEnd) { + WebCore::IntRect bounds = node->bounds(); + bounds.inflate(SkScalarCeil(FOCUS_RING_OUTER_DIAMETER)); + postInvalidateDelayed(mRingAnimationEnd - time, bounds); + } else { + mFollowedLink = false; + flavor = (FocusRing::Flavor) (flavor - FocusRing::NORMAL_ANIMATING); + } + } + FocusRing::DrawRing(canvas, rings, flavor); +} + +OutOfFocusFix fixOutOfDateFocus(bool useReplay) +{ + if (mFrameCacheUI == NULL) { + DBG_NAV_LOG("mFrameCacheUI == NULL"); + return DO_NOTHING; + } + const CachedFrame* cachedFrame = NULL; + const CachedNode* cachedFocusNode = mFrameCacheUI->currentFocus(&cachedFrame); + if (cachedFocusNode == NULL) { + DBG_NAV_LOG("cachedFocusNode == NULL"); + return DO_NOTHING; + } + CachedRoot* webRoot = mViewImpl->mFrameCacheKit; + if (webRoot == NULL) { + DBG_NAV_LOG("webRoot == NULL"); + return DO_NOTHING; + } + int uiWidth = mFrameCacheUI->width(); + int webWidth = webRoot->width(); + if (uiWidth != webWidth) { + DBG_NAV_LOGD("uiWidth=%d webWidth=%d", uiWidth, webWidth); + } else { + WebCore::IntRect cachedBounds = cachedFocusNode->bounds(); + const CachedFrame* webFrame = NULL; + const CachedNode* webFocusNode = webRoot->currentFocus(&webFrame); + if (webFocusNode != NULL && webRoot->containsFrame(cachedFrame)) { + if (useReplay && mReplay.count() == 0) { + DBG_NAV_LOG("mReplay.count() == 0"); + return DO_NOTHING; + } + if (webFocusNode->index() == cachedFocusNode->index() && + webFrame->indexInParent() == cachedFrame->indexInParent()) + { + DBG_NAV_LOG("index =="); + return DO_NOTHING; + } + WebCore::IntRect webBounds = webFocusNode->bounds(); + if (cachedBounds.contains(webBounds)) { + DBG_NAV_LOG("contains"); + return DO_NOTHING; + } + if (webBounds.contains(cachedBounds)) { + DBG_NAV_LOG("webBounds contains"); + return DO_NOTHING; + } + } + const CachedFrame* foundFrame = NULL; + int x, y; + const CachedNode* found = findAt(webRoot, cachedBounds, &foundFrame, &x, &y); +#if DEBUG_NAV_UI + DBG_NAV_LOGD("found=%p (%d) frame=%p (%d)", + found, found != NULL ? found->index() : -1, + foundFrame, foundFrame != NULL ? foundFrame->indexInParent() : -1); + if (found != NULL) { + WebCore::IntRect newBounds = found->bounds(); + DBG_NAV_LOGD("cachedBounds=(%d,%d,%d,%d) found=(%d,%d,%d,%d)", + cachedBounds.x(), cachedBounds.y(), cachedBounds.width(), cachedBounds.height(), + newBounds.x(), newBounds.y(), newBounds.width(), newBounds.height()); + } +#endif + webRoot->setCachedFocus(const_cast<CachedFrame*>(foundFrame), + const_cast<CachedNode*>(found)); + if (found != NULL) + webRoot->rootHistory()->setNavBounds(found->bounds()); + WebCore::Frame* framePointer = foundFrame != NULL ? (WebCore::Frame*) foundFrame->framePointer() : NULL; + WebCore::Node* nodePointer = found != NULL ? (WebCore::Node*) found->nodePointer() : NULL; + setFocusData(webRoot->generation(), framePointer, nodePointer, x, y, found == NULL); + sendFinalFocus(framePointer, nodePointer, x, y); + if (found && (found->isTextArea() || found->isTextField())) + return UPDATE_TEXT_ENTRY; + } +checkOldFocus: + return cachedFocusNode->isTextArea() || cachedFocusNode->isTextField() ? CLEAR_TEXT_ENTRY : DO_NOTHING; +} + +bool focusIsTextArea(FrameCachePermission allowNewer) +{ + CachedRoot* root = getFrameCache(allowNewer); + if (root == NULL) { + DBG_NAV_LOG("root==NULL"); + return false; + } + const CachedNode* focus = root->currentFocus(); + if (focus == NULL) + return false; + return focus->isTextArea() || focus->isTextField(); +} + +void focusRingBounds(WebCore::IntRect* bounds) +{ + DBG_NAV_LOGD("%s", ""); + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (root != NULL) { + const CachedNode* cachedNode = root->currentFocus(); + if (cachedNode != NULL) { + cachedNode->focusRingBounds(bounds); + DBG_NAV_LOGD("bounds={%d,%d,%d,%d}", bounds->x(), bounds->y(), + bounds->width(), bounds->height()); + return; + } + } + *bounds = WebCore::IntRect(0, 0, 0, 0); +} + +CachedRoot* getFrameCache(FrameCachePermission allowNewer) +{ + if (mViewImpl->mUpdatedFrameCache == false) + return mFrameCacheUI; + mViewImpl->gRecomputeFocusMutex.lock(); + bool recomputeInProgress = mViewImpl->mRecomputeEvents.size() > 0; + mViewImpl->gRecomputeFocusMutex.unlock(); + if (allowNewer != ALLOW_NEWEST && recomputeInProgress) + return mFrameCacheUI; + if (allowNewer == DONT_ALLOW_NEWER && mViewImpl->mLastGeneration < mGeneration) + return mFrameCacheUI; + DBG_NAV_LOGD("%s", "mViewImpl->mUpdatedFrameCache == true"); + mViewImpl->gFrameCacheMutex.lock(); + OutOfFocusFix fix = DO_NOTHING; + if (allowNewer != DONT_ALLOW_NEWER) + fix = fixOutOfDateFocus(mViewImpl->mUseReplay); + delete mFrameCacheUI; + delete mNavPictureUI; + mViewImpl->mUpdatedFrameCache = false; + mFrameCacheUI = mViewImpl->mFrameCacheKit; + mNavPictureUI = mViewImpl->mNavPictureKit; + mViewImpl->mFrameCacheKit = NULL; + mViewImpl->mNavPictureKit = NULL; + mViewImpl->gFrameCacheMutex.unlock(); + if (fix == UPDATE_TEXT_ENTRY) + updateTextEntry(); + else if (fix == CLEAR_TEXT_ENTRY) + clearTextEntry(); + return mFrameCacheUI; +} + +int getScaledMaxXScroll() +{ + LOG_ASSERT(mJavaGlue.mObj, "A java object was not associated with this native WebView!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + int result = env->CallIntMethod(mJavaGlue.mObj, mJavaGlue.mGetScaledMaxXScroll); + checkException(env); + return result; +} + +int getScaledMaxYScroll() +{ + LOG_ASSERT(mJavaGlue.mObj, "A java object was not associated with this native WebView!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + int result = env->CallIntMethod(mJavaGlue.mObj, mJavaGlue.mGetScaledMaxYScroll); + checkException(env); + return result; +} + +void getVisibleRect(WebCore::IntRect* rect) +{ + LOG_ASSERT(mJavaGlue.mObj, "A java object was not associated with this native WebView!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + jobject jRect = env->CallObjectMethod(mJavaGlue.mObj, mJavaGlue.mGetVisibleRect); + checkException(env); + int left = (int) env->GetIntField(jRect, mJavaGlue.mRectLeft); + checkException(env); + rect->setX(left); + int top = (int) env->GetIntField(jRect, mJavaGlue.mRectTop); + checkException(env); + rect->setY(top); + int width = (int) env->CallIntMethod(jRect, mJavaGlue.mRectWidth); + checkException(env); + rect->setWidth(width); + int height = (int) env->CallIntMethod(jRect, mJavaGlue.mRectHeight); + checkException(env); + rect->setHeight(height); + env->DeleteLocalRef(jRect); + checkException(env); +} + +bool hasSrcUrl() +{ + const CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (NULL == root) + return false; + const CachedNode* cachedNode = root->currentFocus(); + return NULL == cachedNode ? false : cachedNode->anchorHasSrcUrl(); +} + +static CachedFrame::Direction KeyToDirection(KeyCode keyCode) +{ + switch (keyCode) { + case kKeyCodeDpadRight: + DBG_NAV_LOGD("keyCode=%s", "right"); + return CachedFrame::RIGHT; + case kKeyCodeDpadLeft: + DBG_NAV_LOGD("keyCode=%s", "left"); + return CachedFrame::LEFT; + case kKeyCodeDpadDown: + DBG_NAV_LOGD("keyCode=%s", "down"); + return CachedFrame::DOWN; + case kKeyCodeDpadUp: + DBG_NAV_LOGD("keyCode=%s", "up"); + return CachedFrame::UP; + default: + LOGD("------- bad key sent to WebView::moveFocus"); + return CachedFrame::UNINITIALIZED; + } +} + +bool invalidFrame(WebCore::Frame* frame, const CachedRoot* root) +{ + if (frame == NULL) + return false; + int frameBuild = mViewImpl->retrieveFrameGeneration(frame); + int rootBuild = root->generation(); + return frameBuild > rootBuild; +} + +bool isImage(int x, int y) +{ + const CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + return root ? root->isImage(x, y) : false; +} + +/* returns true if the key had no effect (neither scrolled nor changed focus) */ +bool moveFocus(int keyCode, int count, bool ignoreScroll, bool inval, + void* lastSentFocus, const WebCore::IntRect* lastSentBounds) +{ + CachedRoot* root = getFrameCache(ALLOW_NEWER); + if (root == NULL) { + DBG_NAV_LOG("root == NULL"); + setFocusData(0, NULL, NULL, 0, 0, true); + sendKitFocus(); // will build cache and retry + FirstMoveFocusParams params; + params.d.mTrigger = CommonParams::FIRST_MOVE_FOCUS_PARAMS; + params.d.mGeneration = mGeneration; + params.mKeyCode = keyCode; + params.mCount = count; + params.mIgnoreScroll = ignoreScroll; + mReplay.add(params.d, sizeof(params)); + return true; + } + + CachedFrame::Direction direction = KeyToDirection((KeyCode) keyCode); + const CachedFrame* cachedFrame, * oldFrame = NULL; + const CachedNode* focus = root->currentFocus(&oldFrame); + WebCore::IntPoint focusLocation = root->focusLocation(); + DBG_NAV_LOGD("old focus %d (nativeNode=%p) focusLocation={%d, %d}", + focus ? focus->index() : 0, + focus ? focus->nodePointer() : NULL, focusLocation.x(), focusLocation.y()); + WebCore::IntRect visibleRect; + getVisibleRect(&visibleRect); + DBG_NAV_LOGD("getVisibleRect %d,%d,%d,%d", + visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height()); + root->setVisibleRect(visibleRect); + int xMax = getScaledMaxXScroll(); + int yMax = getScaledMaxYScroll(); + root->setMaxScroll(xMax, yMax); + CachedHistory savedHistory = *root->rootHistory(); +#if 0 && DUMP_NAV_CACHE + savedHistory.mDebug.print(root); +#endif + bool oldNodeIsTextArea = focusIsTextArea(DONT_ALLOW_NEWER); + const CachedNode* cachedNode = NULL; + int dx = 0; + int dy = 0; + int counter = count; + root->setScrollOnly(mFollowedLink); + while (--counter >= 0) { + WebCore::IntPoint scroll = WebCore::IntPoint(0, 0); + cachedNode = root->moveFocus(direction, &cachedFrame, &scroll); + dx += scroll.x(); + dy += scroll.y(); + } + DBG_NAV_LOGD("new focus %d (nativeNode=%p) focusLocation={%d, %d}", + cachedNode ? cachedNode->index() : 0, + cachedNode ? cachedNode->nodePointer() : NULL, root->focusLocation().x(), + root->focusLocation().y()); + // If !mHeightCanMeasure (such as in the browser), we want to scroll no + // matter what +#if 0 && DUMP_NAV_CACHE + root->rootHistory()->mDebug.print(root); +#endif + if (ignoreScroll == false && (mHeightCanMeasure == false || + NULL == cachedNode || + (focus && focus->nodePointer() == cachedNode->nodePointer()))) + { + if (count == 1 && dx != 0 && dy == 0 && -mLastDx == dx && + SkTime::GetMSecs() - mLastDxTime < 1000) + root->checkForJiggle(&dx); + DBG_NAV_LOGD("scrollBy %d,%d", dx, dy); + if ((dx | dy) != 0) + this->scrollBy(dx, dy); + mLastDx = dx; + mLastDxTime = SkTime::GetMSecs(); + ignoreScroll = true; // if move re-executes, don't scroll the second time + } + bool result = false; + if (cachedNode != NULL) { + WebCore::IntPoint pos; + root->setCachedFocus((CachedFrame*) cachedFrame, (CachedNode*) cachedNode); + root->getSimulatedMousePosition(&pos); + if (lastSentFocus == cachedNode->nodePointer() && lastSentBounds && + *lastSentBounds == cachedNode->bounds()) + { + sendFinalFocus((WebCore::Frame*) cachedFrame->framePointer(), + (WebCore::Node*) cachedNode->nodePointer(), pos.x(), pos.y()); + } else { + setFocusData(root->generation(), + (WebCore::Frame*) cachedFrame->framePointer(), + (WebCore::Node*) cachedNode->nodePointer(), pos.x(), pos.y(), + true); + sendKitFocus(); + if (inval) + viewInvalidate(); + MoveFocusParams params; + params.d.d.mTrigger = CommonParams::MOVE_FOCUS_PARAMS; + params.d.d.mGeneration = mGeneration; + params.c.setFocus(focus, oldFrame, root, focusLocation); + params.mSentFocus = cachedNode->nodePointer(); + params.mSentBounds = cachedNode->bounds(); + params.mVisibleRect = visibleRect; + params.mHistory = savedHistory; + DBG_NAV_LOGD("history.mDidFirstLayout=%s", + params.mHistory.didFirstLayout() ? "true" : "false"); + params.mXMax = xMax; + params.mYMax = yMax; + params.d.mKeyCode = keyCode; + params.d.mCount = count; + params.d.mIgnoreScroll = ignoreScroll; + mReplay.add(params.d.d, sizeof(params)); + } + } else { + if (visibleRect.intersects(root->rootHistory()->focusBounds()) == false) { + setFocusData(root->generation(), NULL, NULL, 0, 0, true); + sendKitFocus(); // will build cache and retry + } + FirstMoveFocusParams params; + params.d.mTrigger = CommonParams::FIRST_MOVE_FOCUS_PARAMS; + params.d.mGeneration = mGeneration; + params.mKeyCode = keyCode; + params.mCount = count; + params.mIgnoreScroll = ignoreScroll; + mReplay.add(params.d, sizeof(params)); + int docHeight = root->documentHeight(); + int docWidth = root->documentWidth(); + if (visibleRect.bottom() + dy > docHeight) + dy = docHeight - visibleRect.bottom(); + else if (visibleRect.y() + dy < 0) + dy = -visibleRect.y(); + if (visibleRect.right() + dx > docWidth) + dx = docWidth - visibleRect.right(); + else if (visibleRect.x() < 0) + dx = -visibleRect.x(); + result = direction == CachedFrame::LEFT ? dx >= 0 : + direction == CachedFrame::RIGHT ? dx <= 0 : + direction == CachedFrame::UP ? dy >= 0 : dy <= 0; + } + if (focusIsTextArea(DONT_ALLOW_NEWER)) + updateTextEntry(); + else if (oldNodeIsTextArea) + clearTextEntry(); + return result; +} + +void notifyFocusSet(FrameCachePermission inEditingMode) +{ + if (focusIsTextArea(inEditingMode)) + updateTextEntry(); + else if (inEditingMode) + clearTextEntry(); +#if DEBUG_NAV_UI + if (mFrameCacheUI != NULL) { + const CachedNode* focus = mFrameCacheUI->currentFocus(); + DBG_NAV_LOGD("focus %d (nativeNode=%p)", + focus ? focus->index() : 0, + focus ? focus->nodePointer() : NULL); + } +#endif +} + +void notifyProgressFinished() +{ + DBG_NAV_LOGD("focusIsTextArea=%d", focusIsTextArea(DONT_ALLOW_NEWER)); + updateTextEntry(); +#if DEBUG_NAV_UI + if (mFrameCacheUI != NULL) { + const CachedNode* focus = mFrameCacheUI->currentFocus(); + DBG_NAV_LOGD("focus %d (nativeNode=%p)", + focus ? focus->index() : 0, + focus ? focus->nodePointer() : NULL); + } +#endif +} + +void recomputeFocus() +{ + int generation; + do { + mViewImpl->gRecomputeFocusMutex.lock(); + if (mViewImpl->mRecomputeEvents.size() == 0) { + mViewImpl->gRecomputeFocusMutex.unlock(); + return; + } + generation = mViewImpl->mRecomputeEvents.first(); + mViewImpl->mRecomputeEvents.remove(0); + mViewImpl->gRecomputeFocusMutex.unlock(); + DBG_NAV_LOGD("generation=%d", generation); + CachedRoot* root = getFrameCache(ALLOW_NEWEST); + if (root == NULL) { + DBG_NAV_LOG("root==NULL"); + return; + } + LargestParams storage; + const CommonParams& params = storage.d.d; + char* pos = mReplay.position(); + while (mReplay.retrieve(&storage.d.d) < generation) + DBG_NAV_LOGD("dropped ", params.mGeneration); + if (params.mGeneration > generation) { + DBG_NAV_LOGD("params.mGeneration=%d > generation=%d", + params.mGeneration, generation); + mReplay.rewind(pos); + return; + } + int lastAdd = mReplay.lastAdd(); + do { + LOG_ASSERT(params.mTrigger != CommonParams::NO_DATA, "expected data"); + bool inval = generation == mGeneration; + switch (params.mTrigger) { + case CommonParams::CLEAR_FOCUS_PARAMS: { + const ClearFocusParams& sParams = *(ClearFocusParams*) &storage; + const CacheParams& cParams = sParams.c; + if (invalidFrame(cParams.mFrame, root)) { + DBG_NAV_LOGD("dropped %s generation=%d", + TriggerNames[params.mTrigger], generation); + return; + } + root->setFocus(cParams.mFrame, cParams.mNode, cParams.mX, cParams.mY); + clearFocus(sParams.mX, sParams.mY, inval); + DBG_NAV_LOGD("clearFocus(x,y)={%d,%d}", sParams.mX, sParams.mY); + } break; + case CommonParams::MOTION_UP_PARAMS: { + const MotionUpParams& mParams = *(MotionUpParams*) &storage; + // const CacheParams& cParams = mParams.c; + // if (invalidFrame(cParams.mFrame, root) == false) + // root->setFocus(cParams.mFrame, cParams.mNode, + // cParams.mX, cParams.mY); + motionUp(mParams.mX, mParams.mY, mParams.mSlop, mParams.mIsClick, inval, true); + DBG_NAV_LOGD("motionUp mX=%d mY=%d", mParams.mX, mParams.mY); + } break; + case CommonParams::FIRST_MOVE_FOCUS_PARAMS: { + if (invalidFrame((WebCore::Frame*) root->framePointer(), root)) { + DBG_NAV_LOGD("dropped %s generation=%d", + TriggerNames[params.mTrigger], generation); + return; + } + const FirstMoveFocusParams& fParams = *(FirstMoveFocusParams*) &storage; + DBG_NAV_LOGD("first moveFocus keyCode=%d count=%d" + " ignoreScroll=%s", fParams.mKeyCode, fParams.mCount, + fParams.mIgnoreScroll ? "true" : "false"); + moveFocus(fParams.mKeyCode, fParams.mCount, + fParams.mIgnoreScroll, inval, NULL, NULL); + } break; + case CommonParams::MOVE_FOCUS_PARAMS: { + const MoveFocusParams& mParams = *(MoveFocusParams*) &storage; + const CacheParams& cParams = mParams.c; + if (invalidFrame(cParams.mFrame, root)) { + DBG_NAV_LOGD("dropped %s generation=%d", + TriggerNames[params.mTrigger], generation); + return; + } + DBG_NAV_LOGD("moveFocus keyCode=%d count=%d ignoreScroll=%s " + "history.mDidFirstLayout=%s", mParams.d.mKeyCode, + mParams.d.mCount, mParams.d.mIgnoreScroll ? "true" : "false", + mParams.mHistory.didFirstLayout() ? "true" : "false"); + if (root->setFocus(cParams.mFrame, cParams.mNode, + cParams.mX, cParams.mY) == false) { + DBG_NAV_LOGD("can't restore focus frame=%p node=%p", + "x=%d y=%d %s", cParams.mFrame, cParams.mNode, + cParams.mX, cParams.mY, TriggerNames[params.mTrigger]); + return; + } + root->setVisibleRect(mParams.mVisibleRect); + root->setMaxScroll(mParams.mXMax, mParams.mYMax); + *root->rootHistory() = mParams.mHistory; + moveFocus(mParams.d.mKeyCode, mParams.d.mCount, + mParams.d.mIgnoreScroll, inval, + mParams.mSentFocus, &mParams.mSentBounds); + } break; + default: + LOG_ASSERT(0, "unknown trigger"); + } + if (params.mGeneration >= lastAdd) + break; + root = getFrameCache(DONT_ALLOW_NEWER); // re-execution may have retrieved newer cache + mReplay.retrieve(&storage.d.d); + DBG_NAV_LOGD("continuation mGeneration %d", params.mGeneration); + } while (true); + } while (true); +} + +void resetFocus() +{ + DEBUG_NAV_UI_LOGD("%s", __FUNCTION__); + CachedRoot* root = getFrameCache(ALLOW_NEWER); + if (root == NULL) + return; + root->setCachedFocus(NULL, NULL); +} + +const CachedNode* findAt(CachedRoot* root, const WebCore::IntRect& rect, + const CachedFrame** framePtr, int* rxPtr, int* ryPtr) +{ + *rxPtr = 0; + *ryPtr = 0; + *framePtr = NULL; + if (root == NULL) + return NULL; + WebCore::IntRect visibleRect; + getVisibleRect(&visibleRect); + root->setVisibleRect(visibleRect); + return root->findAt(rect, framePtr, rxPtr, ryPtr); +} + +void selectBestAt(const WebCore::IntRect& rect) +{ + const CachedFrame* frame; + int rx, ry; + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + const CachedNode* node = findAt(root, rect, &frame, &rx, &ry); + int rootGeneration = root != NULL ? root->generation() : 0; + setFocusData(rootGeneration, + frame ? (WebCore::Frame*) frame->framePointer() : NULL, + node ? (WebCore::Node*) node->nodePointer() : NULL, rx, ry, false); + if (node == NULL) { + DBG_NAV_LOGD("no nodes found root=%p", root); + if (root != NULL) { + root->clearFocus(); + root->setCachedFocus(NULL, NULL); + } + sendKitFocus(); + viewInvalidate(); + clearTextEntry(); + return; + } + DBG_NAV_LOGD("CachedNode:%p (%d)", node, node->index()); + const CachedFrame* oldFrame = NULL; + const CachedNode* oldFocusNode = root->currentFocus(&oldFrame); + bool oldNodeIsTextArea = focusIsTextArea(DONT_ALLOW_NEWER); + root->setCachedFocus(const_cast<CachedFrame*>(frame), + const_cast<CachedNode*>(node)); + viewInvalidate(); + if (focusIsTextArea(DONT_ALLOW_NEWER)) + updateTextEntry(); + else if (oldNodeIsTextArea) + clearTextEntry(); +} + +WebCore::IntRect getNavBounds() +{ + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (NULL == root) + return WebCore::IntRect(0, 0, 0, 0); + return root->rootHistory()->navBounds(); +} + +void setNavBounds(const WebCore::IntRect& rect) +{ + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (NULL == root) + return; + root->rootHistory()->setNavBounds(rect); +} + +void markNodeInvalid(WebCore::Node* node) +{ + DBG_NAV_LOGD("node=%p", node); + mInvalidNode = node; + viewInvalidate(); +} + +void motionUp(int x, int y, int slop, bool isClick, bool inval, bool retry) +{ + mFollowedLink = false; + const CachedFrame* frame; + WebCore::IntRect rect = WebCore::IntRect(x - slop, y - slop, slop * 2, slop * 2); + int rx, ry; + CachedRoot* root = getFrameCache(ALLOW_NEWER); + const CachedNode* result = findAt(root, rect, &frame, &rx, &ry); + if (result == NULL) { + DBG_NAV_LOGD("no nodes found root=%p", root); + int rootGeneration = 0; + if (root != NULL) { + root->clearFocus(); + rootGeneration = root->generation(); + if (retry == false) { // scroll first time only + int dx = root->checkForCenter(x, y); + if (dx != 0) { + scrollBy(dx, 0); + retry = true; // don't recompute later since we scrolled + } + } + } + sendMotionUp(rootGeneration, frame ? + (WebCore::Frame*) frame->framePointer() : NULL, + NULL, x, y, slop, isClick, retry); + if (inval) + viewInvalidate(); + if (retry == false) { + MotionUpParams params; + params.d.mTrigger = CommonParams::MOTION_UP_PARAMS; + params.d.mGeneration = mGeneration; + params.mX = x; + params.mY = y; + params.mSlop = slop; + params.mIsClick = isClick; + mReplay.add(params.d, sizeof(params)); + } + clearTextEntry(); + return; + } + DBG_NAV_LOGD("CachedNode:%p (%d) x=%d y=%d rx=%d ry=%d", result, + result->index(), x, y, rx, ry); + // const CachedFrame* oldFrame = NULL; + // const CachedNode* oldFocusNode = root->currentFocus(&oldFrame); + // WebCore::IntPoint focusLocation = root->focusLocation(); + bool oldNodeIsTextArea = retry == false && focusIsTextArea(DONT_ALLOW_NEWER); + root->setCachedFocus(const_cast<CachedFrame*>(frame), + const_cast<CachedNode*>(result)); + bool newNodeIsTextArea = focusIsTextArea(DONT_ALLOW_NEWER); + const WebCore::String& exported = result->getExport(); + if (exported.length() == 0 || newNodeIsTextArea) { + sendMotionUp(root->generation(), + frame ? (WebCore::Frame*) frame->framePointer() : NULL, + result ? (WebCore::Node*) result->nodePointer() : NULL, rx, ry, + slop, isClick, retry); + if (inval) + viewInvalidate(); + if (retry == false) { + MotionUpParams params; + params.d.mTrigger = CommonParams::MOTION_UP_PARAMS; + params.d.mGeneration = mGeneration; + params.mX = x; + params.mY = y; + params.mSlop = slop; + params.mIsClick = isClick; + // params.c.setFocus(oldFocusNode, oldFrame, root, focusLocation); + mReplay.add(params.d, sizeof(params)); + } + } else if (inval) + viewInvalidate(); + if (newNodeIsTextArea) + updateTextEntry(); + else { + if (isClick) + setFollowedLink(true); + if (oldNodeIsTextArea) + clearTextEntry(); + } +} + +void setFollowedLink(bool followed) +{ + if ((mFollowedLink = followed) != false) { + mRingAnimationEnd = SkTime::GetMSecs() + 500; + viewInvalidate(); + } +} + +void setHeightCanMeasure(bool measure) +{ + mHeightCanMeasure = measure; +} + +SkIRect mSelStart, mSelEnd; +SkRegion mSelRegion; +#define MIN_ARROW_DISTANCE (20 * 20) + +void moveSelection(int x, int y, bool extendSelection) { + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (NULL == root) + return; + const SkPicture& picture = *mNavPictureUI; + WebCore::IntRect r; + getVisibleRect(&r); + SkIRect area; + area.set(r.x(), r.y(), r.right(), r.bottom()); + if (extendSelection == false) + mSelStart = mSelEnd = CopyPaste::findClosest(picture, area, x, y); + else + mSelEnd = CopyPaste::findClosest(picture, area, x, y); + DBG_NAV_LOGD("x=%d y=%d extendSelection=%s mSelStart=(%d, %d, %d, %d)" + " mSelEnd=(%d, %d, %d, %d)", x, y, extendSelection ? "true" : "false", + mSelStart.fLeft, mSelStart.fTop, mSelStart.fRight, mSelStart.fBottom, + mSelEnd.fLeft, mSelEnd.fTop, mSelEnd.fRight, mSelEnd.fBottom); +} + +const SkRegion& getSelection() { + return mSelRegion; +} + +void drawSelection(SkCanvas* canvas, int x, int y, bool extendSelection) { + if (extendSelection == false) { + int dx = x - mSelStart.fLeft; + dx *= dx; + int otherX = x - mSelStart.fRight; + if (dx > (otherX *= otherX)) + dx = otherX; + int dy = y - mSelStart.fTop; + int dist = dx * dx + dy * dy; + if (dist > MIN_ARROW_DISTANCE) + drawSelectionArrow(canvas, x, y); + else + drawSelectionPointer(canvas, x, y, true); + } else { + drawSelectionRegion(canvas); + drawSelectionPointer(canvas, x, y, false); + } +} + +void drawSelectionRegion(SkCanvas* canvas) { + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (NULL == root) + return; + WebCore::IntRect r; + getVisibleRect(&r); + SkIRect area; + area.set(r.x(), r.y(), r.right(), r.bottom()); + mSelRegion.setEmpty(); + CopyPaste::buildSelection(*mNavPictureUI, area, mSelStart, mSelEnd, &mSelRegion); + SkPath path; + mSelRegion.getBoundaryPath(&path); + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SkColorSetARGB(0x40, 255, 51, 204)); + canvas->drawPath(path, paint); +} + +void drawSelectionPointer(SkCanvas* canvas, int x, int y, bool gridded) { + SkPath path; + getSelectionCaret(&path); + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(SK_ColorBLACK); + SkPixelXorXfermode xorMode(SK_ColorWHITE); + paint.setXfermode(&xorMode); + int sc = canvas->save(); + if (gridded) { + bool useLeft = x <= (mSelStart.fLeft + mSelStart.fRight) >> 1; + canvas->translate(SkIntToScalar(useLeft ? mSelStart.fLeft : + mSelStart.fRight), SkIntToScalar(mSelStart.fTop)); + } else + canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); + canvas->drawPath(path, paint); + canvas->restoreToCount(sc); +} + +void drawSelectionArrow(SkCanvas* canvas, int x, int y) { + SkPath path; + getSelectionArrow(&path); + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setColor(SK_ColorBLACK); + paint.setStrokeWidth(SK_Scalar1 * 2); + int sc = canvas->save(); + canvas->translate(SkIntToScalar(x), SkIntToScalar(y)); + canvas->drawPath(path, paint); + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(SK_ColorWHITE); + canvas->drawPath(path, paint); + canvas->restoreToCount(sc); +} + +void getSelectionArrow(SkPath* path) { + const int arrow[] = { + 0, 14, 3, 11, 5, 15, 9, 15, 7, 11, 11, 11 + }; + for (unsigned index = 0; index < sizeof(arrow)/sizeof(arrow[0]); index += 2) + path->lineTo(SkIntToScalar(arrow[index]), SkIntToScalar(arrow[index + 1])); + path->close(); +} + +void getSelectionCaret(SkPath* path) { + SkScalar height = SkIntToScalar(mSelStart.fBottom - mSelStart.fTop); + SkScalar dist = height / 4; + path->lineTo(0, height); + SkScalar bottom = height + dist; + path->lineTo(-dist, bottom); + SkScalar edge = bottom - SK_Scalar1/2; + path->moveTo(-dist, edge); + path->lineTo(dist, edge); + path->moveTo(dist, bottom); + path->lineTo(0, height); +} + +void sendFinalFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y) +{ + DBG_NAV_LOGD("framePtr=%p nodePtr=%p x=%d y=%d", framePtr, nodePtr, x, y); + LOG_ASSERT(mJavaGlue.mObj, "A java object was not associated with this native WebView!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mSendFinalFocus, + (jint) framePtr, (jint) nodePtr, x, y); + checkException(env); +} + +void sendKitFocus() +{ + LOG_ASSERT(mJavaGlue.mObj, "A java object was not associated with this native WebView!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mSendKitFocus); + checkException(env); +} + +void sendMotionUp(int buildGeneration, + WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y, int slop, + bool isClick, bool retry) +{ + mViewImpl->mTouchGeneration = mViewImpl->mGeneration = ++mGeneration; + DBG_NAV_LOGD("buildGeneration=%d mGeneration=%d framePtr=%p nodePtr=%p" + " x=%d y=%d slop=%d", buildGeneration, + mGeneration, framePtr, nodePtr, x, y, slop); + LOG_ASSERT(mJavaGlue.mObj, "A WebView was not associated with this WebViewNative!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mSendMotionUp, mGeneration, + buildGeneration, (jint) framePtr, (jint) nodePtr, x, y, slop, isClick, retry); + checkException(env); +} + +void setFocusData(int buildGeneration, WebCore::Frame* framePtr, + WebCore::Node* nodePtr, int x, int y, bool ignoreNullFocus) +{ + mViewImpl->mMoveGeneration = mViewImpl->mGeneration = ++mGeneration; + DBG_NAV_LOGD("moveGeneration=%d buildGeneration=%d framePtr=%p nodePtr=%p" + " x=%d y=%d", mGeneration, buildGeneration, framePtr, nodePtr, x, y); + LOG_ASSERT(mJavaGlue.mObj, "A java object was not associated with this native WebView!"); + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mSetFocusData, mGeneration, + buildGeneration, (jint) framePtr, (jint) nodePtr, x, y, ignoreNullFocus); + checkException(env); +} + +void scrollBy(int dx, int dy) +{ + LOG_ASSERT(mJavaGlue.mObj, "A java object was not associated with this native WebView!"); + + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mScrollBy, dx, dy); + checkException(env); +} + +bool updateFocusNode(JNIEnv* env) +{ + CachedRoot* root = getFrameCache(DONT_ALLOW_NEWER); + if (root == NULL) { + DBG_NAV_LOG("root==NULL"); + return false; + } + const CachedFrame* cachedFrame = NULL; + const CachedNode* cachedFocusNode = root->currentFocus(&cachedFrame); + if (cachedFocusNode == NULL) { + DBG_NAV_LOG("cachedFocusNode==NULL"); + return false; + } + DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)", + cachedFocusNode->index(), + cachedFocusNode->nodePointer()); + jobject focusnode = env->GetObjectField(mJavaGlue.mObj, mJavaGlue.mFocusNode); + LOG_ASSERT(focusnode, "Could not find WebView's FocusNode"); + + bool isTextArea = cachedFocusNode->isTextArea(); + bool isTextField = cachedFocusNode->isTextField(); + int maxLength; + jstring jName; + if (isTextField) { + maxLength = cachedFocusNode->maxLength(); + const WebCore::String& name = cachedFocusNode->name(); + jName = env->NewString((jchar*)name.characters(), name.length()); + } else { + maxLength = -1; + jName = NULL; + } + WebCore::IntRect bounds = cachedFocusNode->bounds(); + WebCore::String value = cachedFocusNode->getExport(); + jstring val = !value.isEmpty() ? env->NewString((jchar *)value.characters(), value.length()) : NULL; + env->CallVoidMethod(focusnode, mJavaGlue.mSetAll, isTextField, isTextArea, cachedFocusNode->isPassword(), + cachedFocusNode->isAnchor(), cachedFocusNode->isRtlText(), maxLength, cachedFocusNode->textSize(), + bounds.x(), bounds.y(), bounds.right(), bounds.bottom(), (int)(cachedFocusNode->nodePointer()), + (int)(cachedFrame->framePointer()), val, jName, root->textGeneration()); + env->DeleteLocalRef(val); + env->DeleteLocalRef(focusnode); + if (isTextField) + env->DeleteLocalRef(jName); + return true; +} + +void updateTextEntry() +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mUpdateTextEntry); + checkException(env); +} + +void viewInvalidate() +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mViewInvalidate); + checkException(env); +} + +void postInvalidateDelayed(int64_t delay, const WebCore::IntRect& bounds) +{ + JNIEnv* env = javavm_to_jnienv(mJavaGlue.mJVM); + env->CallVoidMethod(mJavaGlue.mObj, mJavaGlue.mPostInvalidateDelayed, + delay, bounds.x(), bounds.y(), bounds.right(), bounds.bottom()); + checkException(env); +} + +private: // local state for WebView + // private to getFrameCache(); other functions operate in a different thread + CachedRoot* mFrameCacheUI; // navigation data ready for use + FocusReplay mReplay; + WebViewCore* mViewImpl; + SkTDArray<Container*>* mUIButtons; + WebCore::Node* mInvalidNode; + int mGeneration; // associate unique ID with sent kit focus to match with ui + SkPicture* mNavPictureUI; + bool mFollowedLink; + SkMSec mRingAnimationEnd; + // Corresponds to the same-named boolean on the java side. + bool mHeightCanMeasure; + int mLastDx; + SkMSec mLastDxTime; +}; // end of WebView class + +/* + * Native JNI methods + */ +static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string) +{ + int length = string.length(); + if (0 == length) + return 0; + jstring ret = env->NewString((jchar *)string.characters(), length); + env->DeleteLocalRef(ret); + return ret; +} + +#ifdef BUCKET_EXPERIMENT +static void nativeClearContentPicture(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->clearContentPicture(); +} +#endif + +static void nativeClearFocus(JNIEnv *env, jobject obj, int x, int y) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->clearFocus(x, y, true); +} + +static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl) +{ + WebView* webview = new WebView(env, obj, viewImpl); + // NEED THIS OR SOMETHING LIKE IT! + //Release(obj); +} + +static void nativeDebugDump(JNIEnv *env, jobject obj) +{ +#if DUMP_NAV_CACHE + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->debugDump(); +#endif +} + +#ifdef BUCKET_EXPERIMENT +static void nativeDrawContentPicture(JNIEnv *env, jobject obj, + jobject canv) +{ + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); + if (canv == NULL) { + DBG_NAV_LOG("canv==NULL"); + return; + } + WebView* view = GET_NATIVE_VIEW(env, obj); + if (view == NULL) { + DBG_NAV_LOG("view==NULL"); + return; + } + view->drawContentPicture(canvas); +} +#endif + +static void nativeDrawFocusRing(JNIEnv *env, jobject obj, + jobject canv) +{ + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); + if (canv == NULL) { + DBG_NAV_LOG("canv==NULL"); + return; + } + WebView* view = GET_NATIVE_VIEW(env, obj); + if (view == NULL) { + DBG_NAV_LOG("view==NULL"); + return; + } + view->drawFocusRing(canvas); +} + +static void nativeDrawSelection(JNIEnv *env, jobject obj, + jobject canv, jint x, jint y, bool ex) +{ + SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); + if (canv == NULL) { + DBG_NAV_LOG("canv==NULL"); + return; + } + WebView* view = GET_NATIVE_VIEW(env, obj); + if (view == NULL) { + DBG_NAV_LOG("view==NULL"); + return; + } + view->drawSelection(canvas, x, y, ex); +} + +static bool nativeHasSrcUrl(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + return view->hasSrcUrl(); +} + +static bool nativeIsImage(JNIEnv *env, jobject obj, jint x, jint y) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + return view->isImage(x,y); +} + +static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj) +{ + int L, T, R, B; + GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B); + return WebCore::IntRect(L, T, R - L, B - T); +} + +static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + WebCore::IntRect rect = jrect_to_webrect(env, jrect); + view->selectBestAt(rect); +} + +static void nativeMarkNodeInvalid(JNIEnv *env, jobject obj, int node) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->markNodeInvalid((WebCore::Node*) node); +} + +static void nativeMotionUp(JNIEnv *env, jobject obj, + int x, int y, int slop, bool isClick) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->motionUp(x, y, slop, isClick, true, false); +} + +static bool nativeUpdateFocusNode(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + return view->updateFocusNode(env); +} + +static bool nativeMoveFocus(JNIEnv *env, jobject obj, + int key, int count, bool ignoreScroll) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + DBG_NAV_LOGD("env=%p obj=%p view=%p", env, obj, view); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + return view->moveFocus(key, count, ignoreScroll, true, NULL, NULL); +} + +static void nativeNotifyFocusSet(JNIEnv *env, jobject obj, bool inEditingMode) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->notifyFocusSet((WebView::FrameCachePermission) inEditingMode); +} + +static void nativeRecomputeFocus(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->recomputeFocus(); +} + +static void nativeRecordButtons(JNIEnv* env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->nativeRecordButtons(); +} + +static void nativeResetFocus(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->resetFocus(); +} + +static void nativeSetFollowedLink(JNIEnv *env, jobject obj, bool followed) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->setFollowedLink(followed); +} + +static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure"); + view->setHeightCanMeasure(measure); +} + +static jobject nativeGetFocusRingBounds(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + jclass rectClass = env->FindClass("android/graphics/Rect"); + LOG_ASSERT(rectClass, "Could not find Rect class!"); + jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V"); + LOG_ASSERT(init, "Could not find constructor for Rect"); + WebCore::IntRect webRect; + view->focusRingBounds(&webRect); + jobject rect = env->NewObject(rectClass, init, webRect.x(), + webRect.y(), webRect.right(), webRect.bottom()); + return rect; +} + +static jobject nativeGetNavBounds(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + jclass rectClass = env->FindClass("android/graphics/Rect"); + LOG_ASSERT(rectClass, "Could not find Rect class!"); + jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V"); + LOG_ASSERT(init, "Could not find constructor for Rect"); + WebCore::IntRect webRect = view->getNavBounds(); + jobject rect = env->NewObject(rectClass, init, webRect.x(), + webRect.y(), webRect.right(), webRect.bottom()); + return rect; +} + +static void nativeSetNavBounds(JNIEnv *env, jobject obj, jobject jrect) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + WebCore::IntRect rect = jrect_to_webrect(env, jrect); + view->setNavBounds(rect); +} + +static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring updatedText, jint generation) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in nativeUpdateCachedTextfield"); + CachedRoot* root = view->getFrameCache(WebView::DONT_ALLOW_NEWER); + if (!root) + return; + const CachedNode* cachedFocusNode = root->currentFocus(); + if (!cachedFocusNode || (!cachedFocusNode->isTextField() && !cachedFocusNode->isTextArea())) + return; + WebCore::String webcoreString; + const jchar* outputChars = NULL == updatedText ? NULL : env->GetStringChars(updatedText, NULL); + if (outputChars != NULL) { + webcoreString = WebCore::String((const UChar*) outputChars, env->GetStringLength(updatedText)); + } + (const_cast<CachedNode*>(cachedFocusNode))->setExport(webcoreString); + root->setTextGeneration(generation); + if (outputChars) + env->ReleaseStringChars(updatedText, outputChars); + checkException(env); +} + +static void nativeDestroy(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOGD("nativeDestroy view: %p", view); + LOG_ASSERT(view, "view not set in nativeDestroy"); + delete view; +} + +static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y, bool ex) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + view->moveSelection(x, y, ex); +} + +static jobject nativeGetSelection(JNIEnv *env, jobject obj) +{ + WebView* view = GET_NATIVE_VIEW(env, obj); + LOG_ASSERT(view, "view not set in %s", __FUNCTION__); + return GraphicsJNI::createRegion(env, new SkRegion(view->getSelection())); +} + +/* + * JNI registration + */ +static JNINativeMethod gJavaWebViewMethods[] = { +#ifdef BUCKET_EXPERIMENT + { "nativeClearContentPicture", "()V", + (void*) nativeClearContentPicture }, +#endif + { "nativeClearFocus", "(II)V", + (void*) nativeClearFocus }, + { "nativeCreate", "(I)V", + (void*) nativeCreate }, + { "nativeDebugDump", "()V", + (void*) nativeDebugDump }, + { "nativeDestroy", "()V", + (void*) nativeDestroy }, +#ifdef BUCKET_EXPERIMENT + { "nativeDrawContentPicture", "(Landroid/graphics/Canvas;)V", + (void*) nativeDrawContentPicture }, +#endif + { "nativeDrawFocusRing", "(Landroid/graphics/Canvas;)V", + (void*) nativeDrawFocusRing }, + { "nativeDrawSelection", "(Landroid/graphics/Canvas;IIZ)V", + (void*) nativeDrawSelection }, + { "nativeUpdateFocusNode", "()Z", + (void*) nativeUpdateFocusNode }, + { "nativeGetFocusRingBounds", "()Landroid/graphics/Rect;", + (void*) nativeGetFocusRingBounds }, + { "nativeGetNavBounds", "()Landroid/graphics/Rect;", + (void*) nativeGetNavBounds }, + { "nativeHasSrcUrl", "()Z", + (void*) nativeHasSrcUrl }, + { "nativeMarkNodeInvalid", "(I)V", + (void*) nativeMarkNodeInvalid }, + { "nativeMotionUp", "(IIIZ)V", + (void*) nativeMotionUp }, + { "nativeMoveFocus", "(IIZ)Z", + (void*) nativeMoveFocus }, + { "nativeNotifyFocusSet", "(Z)V", + (void*) nativeNotifyFocusSet }, + { "nativeRecomputeFocus", "()V", + (void*) nativeRecomputeFocus }, + { "nativeRecordButtons", "()V", + (void*) nativeRecordButtons }, + { "nativeResetFocus", "()V", + (void*) nativeResetFocus }, + { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V", + (void*) nativeSelectBestAt }, + { "nativeSetFollowedLink", "(Z)V", + (void*) nativeSetFollowedLink }, + { "nativeSetHeightCanMeasure", "(Z)V", + (void*) nativeSetHeightCanMeasure }, + { "nativeSetNavBounds", "(Landroid/graphics/Rect;)V", + (void*) nativeSetNavBounds }, + { "nativeIsImage", "(II)Z", + (void*) nativeIsImage }, + { "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V", + (void*) nativeUpdateCachedTextfield }, + { "nativeMoveSelection", "(IIZ)V", + (void*) nativeMoveSelection }, + { "nativeGetSelection", "()Landroid/graphics/Region;", + (void*) nativeGetSelection } +}; + +int register_webview(JNIEnv* env) +{ + jclass clazz = env->FindClass("android/webkit/WebView"); + LOG_ASSERT(clazz != NULL, "Unable to find class android/webkit/WebView"); + gWebViewField = env->GetFieldID(clazz, "mNativeClass", "I"); + LOG_ASSERT(gWebViewField != NULL, "Unable to find android/webkit/WebView.mNativeClass"); + + return jniRegisterNativeMethods(env, "android/webkit/WebView", gJavaWebViewMethods, NELEM(gJavaWebViewMethods)); +} + +} // namespace android diff --git a/WebCore/platform/android/sort.cpp b/WebCore/platform/android/sort.cpp new file mode 100644 index 0000000..58b620d --- /dev/null +++ b/WebCore/platform/android/sort.cpp @@ -0,0 +1,48 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" + +namespace std +{ + void sort(const void** start, const void** end, const void** temp, Comparator comp) + { + size_t endlen = end - start; + size_t midlen = endlen / 2; + const void** mid = start + midlen; + if (midlen > 1) + sort(start, mid, temp, comp); + if (end - mid > 1) + sort(mid, end, temp, comp); + memcpy(temp, start, midlen * sizeof(*start)); + size_t i = 0; + size_t j = midlen; + size_t off = 0; + while (i < midlen && j < endlen) + start[off++] = (*comp)(start[j], temp[i]) ? start[j++] : temp[i++]; + if (i < midlen) + memcpy(&start[off], &temp[i], (midlen - i) * sizeof(*start)); + } + + void sort(const void** start, const void** end, Comparator comp) { + if (end - start > 1) { + const void** temp = new sortType[(end - start) / 2]; + sort(start, end, temp, comp); + delete[] temp; + } + } +} diff --git a/WebCore/platform/android/stl/algorithm b/WebCore/platform/android/stl/algorithm new file mode 100644 index 0000000..9657ceb --- /dev/null +++ b/WebCore/platform/android/stl/algorithm @@ -0,0 +1,237 @@ +#ifdef __cplusplus + +/* + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1996-1998 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + + // extracted from stl_algobase.h + // full STL is not compatible with the ARM build + // a limited number of STL functions is used by webkit: swap, max, and min are + // included below for webkit compatibility + +#ifdef __GLIBCPP_INTERNAL_ALGOBASE_H +#error "real STL defined" +#endif + +#ifndef __ANDROID_ALGORITHM +#define __ANDROID_ALGORITHM + +#ifndef __ANDROID_LIMITS +#include <limits> +#endif + +#include <SkScalar.h> // for SK_ScalarNaN +#ifdef PREFIX_FOR_WEBCORE +#include <SkTSearch.h> +namespace WebCore { + class InlineTextBox; + class RenderLayer; +} +#endif +#include <float.h> +#include <math.h> +#include <stdint.h> + +#ifndef WCHAR_MAX + #define WCHAR_MAX 0xFFFF +#endif + +namespace std +{ + template<typename _Tp> + inline void + swap(_Tp& __a, _Tp& __b) + { + _Tp __tmp = __a; + __a = __b; + __b = __tmp; + } + + #undef min + #undef max + + template<typename _Tp> + inline const _Tp& + min(const _Tp& __a, const _Tp& __b) + { + return __b < __a ? __b : __a; + } + + template<typename _Tp> + inline const _Tp& + max(const _Tp& __a, const _Tp& __b) + { + return __a < __b ? __b : __a; + } + +template <class _InputIter, class _OutputIter> +inline _OutputIter copy(_InputIter __first, _InputIter __last, + _OutputIter __result) +{ + for (size_t __n = __last - __first; __n > 0; --__n) { + *__result = *__first; + ++__first; + ++__result; + } + return __result; +} + +template <class _ForwardIter, class _Tp> +void fill(_ForwardIter __first, _ForwardIter __last, const _Tp& __value) { + for ( ; __first != __last; ++__first) + *__first = __value; +} + +#ifndef UINTPTR_MAX +#define UINTPTR_MAX UINT32_MAX +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX (0xffffffff) +#endif + +template <typename T> +struct numeric_limits { + /// Returns the minimum value for type T. + static inline T min (void) { return (T(0)); } + /// Returns the minimum value for type T. + static inline T max (void) { return (T(0)); } + static const bool is_signed = false; ///< True if the type is signed. + static const bool is_integer = false; ///< True if stores an exact value. + static const bool is_integral = false; ///< True if fixed size and cast-copyable. +}; + +template <typename T> +struct numeric_limits<T*> { + static inline T* min (void) { return (NULL); } + static inline T* max (void) { return (UINTPTR_MAX); } + static const bool is_signed = false; + static const bool is_integer = true; + static const bool is_integral = true; +}; + +#define _NUMERIC_LIMITS(type, minVal, maxVal, quietNaN, bSigned, bInteger, bIntegral) \ +template <> \ +struct numeric_limits<type> { \ + static inline type infinity (void) { return (maxVal); } \ + static inline type min (void) { return (minVal); } \ + static inline type max (void) { return (maxVal); } \ + static inline type quiet_NaN() { return (quietNaN); } \ + static const bool is_signed = bSigned; \ + static const bool is_integer = bInteger; \ + static const bool is_integral = bIntegral; \ +} + +//-------------------------------------------------------------------------------------- +// type min max NaN signed integer integral +//-------------------------------------------------------------------------------------- +_NUMERIC_LIMITS (bool, false, true, 0, false, true, true); +_NUMERIC_LIMITS (char, SCHAR_MIN, SCHAR_MAX, 0, true, true, true); +_NUMERIC_LIMITS (int, INT_MIN, INT_MAX, 0, true, true, true); +_NUMERIC_LIMITS (short, SHRT_MIN, SHRT_MAX, 0, true, true, true); +_NUMERIC_LIMITS (long, LONG_MIN, LONG_MAX, 0, true, true, true); +#if HAVE_THREE_CHAR_TYPES +_NUMERIC_LIMITS (signed char, SCHAR_MIN, SCHAR_MAX, 0, true, true, true); +#endif +_NUMERIC_LIMITS (unsigned char, 0, UCHAR_MAX, 0, false, true, true); +_NUMERIC_LIMITS (unsigned int, 0, UINT_MAX, 0, false, true, true); +_NUMERIC_LIMITS (unsigned short,0, USHRT_MAX, 0, false, true, true); +_NUMERIC_LIMITS (unsigned long, 0, ULONG_MAX, 0, false, true, true); +_NUMERIC_LIMITS (wchar_t, 0, WCHAR_MAX, 0, false, true, true); +_NUMERIC_LIMITS (float, FLT_MIN, FLT_MAX, SK_ScalarNaN, true, false, true); +_NUMERIC_LIMITS (double, DBL_MIN, DBL_MAX, SK_ScalarNaN, true, false, true); +_NUMERIC_LIMITS (long double, LDBL_MIN, LDBL_MAX, SK_ScalarNaN, true, false, true); +#ifdef HAVE_LONG_LONG +_NUMERIC_LIMITS (long long, LLONG_MIN, LLONG_MAX, 0, true, true, true); +_NUMERIC_LIMITS (unsigned long long, 0, ULLONG_MAX, 0, false, true, true); +#endif +//-------------------------------------------------------------------------------------- + +typedef int ptrdiff_t; + +#ifdef PREFIX_FOR_WEBCORE + typedef const void* sortType; + typedef bool (* Comparator)(const void*, const void*); + + inline bool binary_search(const unsigned short* const base, + const unsigned short* const end, + short target) + { + return SkTSearch<unsigned short>(base, end - base, target, sizeof(unsigned short)) >= 0; + } + + extern void sort(const void** start, const void** end, Comparator comp); + + inline void sort (WebCore::InlineTextBox** start, WebCore::InlineTextBox**end, + bool (* comp)(const WebCore::InlineTextBox*, const WebCore::InlineTextBox*)) + { + sort((const void**) start, (const void**) end, (Comparator) comp); + } + + template<typename P> inline void stable_sort(P** start, P** end, + bool (* comp)(P*, P*)) + { + sort((const void**) start, (const void**) end, (Comparator) comp); + } + + template<typename P> void stable_sort(P* start, P* end, P* temp, + bool (& comp)(const P&, const P&)) { + size_t endlen = end - start; + size_t midlen = endlen / 2; + P* mid = start + midlen; + if (midlen > 1) + stable_sort(start, mid, temp, comp); + if (end - mid > 1) + stable_sort(mid, end, temp, comp); + memcpy(temp, start, midlen * sizeof(*start)); + size_t i = 0; + size_t j = midlen; + size_t off = 0; + while (i < midlen && j < endlen) + start[off++] = (comp)(start[j], temp[i]) ? start[j++] : temp[i++]; + if (i < midlen) + memcpy(&start[off], &temp[i], (midlen - i) * sizeof(*start)); + } + + template<typename P> void stable_sort(P* start, P* end, + bool (& comp)(const P&, const P&)) { + if (end - start > 1) { + P* temp = new P[(end - start) / 2]; + stable_sort(start, end, temp, comp); + delete[] temp; + } + } + + class ostream { + int this_class_intentionally_left_empty; + }; +#endif + +} + +#endif + +#endif // __cplusplus + diff --git a/WebCore/platform/android/stl/concept_checks.h b/WebCore/platform/android/stl/concept_checks.h new file mode 100644 index 0000000..36df283 --- /dev/null +++ b/WebCore/platform/android/stl/concept_checks.h @@ -0,0 +1,811 @@ +/* + * Copyright (c) 1999 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +#ifndef __CONCEPT_CHECKS_H +#define __CONCEPT_CHECKS_H + +/* + Use these macro like assertions, but they assert properties + on types (usually template arguments). In technical terms they + verify whether a type "models" a "concept". + + This set of requirements and the terminology used here is derived + from the book "Generic Programming and the STL" by Matt Austern + (Addison Wesley). For further information please consult that + book. The requirements also are intended to match the ANSI/ISO C++ + standard. + + This file covers the basic concepts and the iterator concepts. + There are several other files that provide the requirements + for the STL containers: + container_concepts.h + sequence_concepts.h + assoc_container_concepts.h + + Jeremy Siek, 1999 + + TO DO: + - some issues with regards to concept classification and mutability + including AssociativeContianer -> ForwardContainer + and SortedAssociativeContainer -> ReversibleContainer + - HashedAssociativeContainer + - Allocator + - Function Object Concepts + + */ + +#ifndef __STL_USE_CONCEPT_CHECKS + +// Some compilers lack the features that are necessary for concept checks. +// On those compilers we define the concept check macros to do nothing. +#define __STL_REQUIRES(__type_var, __concept) do {} while(0) +#define __STL_CLASS_REQUIRES(__type_var, __concept) \ + static int __##__type_var##_##__concept +#define __STL_CONVERTIBLE(__type_x, __type_y) do {} while(0) +#define __STL_REQUIRES_SAME_TYPE(__type_x, __type_y) do {} while(0) +#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y) \ + static int __##__type_x##__type_y##_require_same_type +#define __STL_GENERATOR_CHECK(__func, __ret) do {} while(0) +#define __STL_CLASS_GENERATOR_CHECK(__func, __ret) \ + static int __##__func##__ret##_generator_check +#define __STL_UNARY_FUNCTION_CHECK(__func, __ret, __arg) do {} while(0) +#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg) \ + static int __##__func##__ret##__arg##_unary_function_check +#define __STL_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \ + do {} while(0) +#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \ + static int __##__func##__ret##__first##__second##_binary_function_check +#define __STL_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \ + do {} while(0) +#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \ + static int __##__opname##__ret##__first##__second##_require_binary_op + +#else /* __STL_USE_CONCEPT_CHECKS */ + +// This macro tests whether the template argument "__type_var" +// satisfies the requirements of "__concept". Here is a list of concepts +// that we know how to check: +// _Allocator +// _Assignable +// _DefaultConstructible +// _EqualityComparable +// _LessThanComparable +// _TrivialIterator +// _InputIterator +// _OutputIterator +// _ForwardIterator +// _BidirectionalIterator +// _RandomAccessIterator +// _Mutable_TrivialIterator +// _Mutable_ForwardIterator +// _Mutable_BidirectionalIterator +// _Mutable_RandomAccessIterator + +#define __STL_REQUIRES(__type_var, __concept) \ +do { \ + void (*__x)( __type_var ) = __concept##_concept_specification< __type_var >\ + ::__concept##_requirement_violation; __x = __x; } while (0) + +// Use this to check whether type X is convertible to type Y +#define __STL_CONVERTIBLE(__type_x, __type_y) \ +do { \ + void (*__x)( __type_x , __type_y ) = _STL_CONVERT_ERROR< __type_x , \ + __type_y >::__type_X_is_not_convertible_to_type_Y; \ + __x = __x; } while (0) + +// Use this to test whether two template arguments are the same type +#define __STL_REQUIRES_SAME_TYPE(__type_x, __type_y) \ +do { \ + void (*__x)( __type_x , __type_y ) = _STL_SAME_TYPE_ERROR< __type_x, \ + __type_y >::__type_X_not_same_as_type_Y; \ + __x = __x; } while (0) + + +// function object checks +#define __STL_GENERATOR_CHECK(__func, __ret) \ +do { \ + __ret (*__x)( __func&) = \ + _STL_GENERATOR_ERROR< \ + __func, __ret>::__generator_requirement_violation; \ + __x = __x; } while (0) + + +#define __STL_UNARY_FUNCTION_CHECK(__func, __ret, __arg) \ +do { \ + __ret (*__x)( __func&, const __arg& ) = \ + _STL_UNARY_FUNCTION_ERROR< \ + __func, __ret, __arg>::__unary_function_requirement_violation; \ + __x = __x; } while (0) + + +#define __STL_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \ +do { \ + __ret (*__x)( __func&, const __first&, const __second& ) = \ + _STL_BINARY_FUNCTION_ERROR< \ + __func, __ret, __first, __second>::__binary_function_requirement_violation; \ + __x = __x; } while (0) + + +#define __STL_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \ + do { \ + __ret (*__x)( __first&, __second& ) = _STL_BINARY##__opname##_ERROR< \ + __ret, __first, __second>::__binary_operator_requirement_violation; \ + __ret (*__y)( const __first&, const __second& ) = \ + _STL_BINARY##__opname##_ERROR< __ret, __first, __second>:: \ + __const_binary_operator_requirement_violation; \ + __y = __y; __x = __x; } while (0) + + +#ifdef __STL_NO_FUNCTION_PTR_IN_CLASS_TEMPLATE + +#define __STL_CLASS_REQUIRES(__type_var, __concept) +#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y) +#define __STL_CLASS_GENERATOR_CHECK(__func, __ret) +#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg) +#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) +#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) + +#else + +// Use this macro inside of template classes, where you would +// like to place requirements on the template arguments to the class +// Warning: do not pass pointers and such (e.g. T*) in as the __type_var, +// since the type_var is used to construct identifiers. Instead typedef +// the pointer type, then use the typedef name for the __type_var. +#define __STL_CLASS_REQUIRES(__type_var, __concept) \ + typedef void (* __func##__type_var##__concept)( __type_var ); \ + template <__func##__type_var##__concept _Tp1> \ + struct __dummy_struct_##__type_var##__concept { }; \ + static __dummy_struct_##__type_var##__concept< \ + __concept##_concept_specification< \ + __type_var>::__concept##_requirement_violation> \ + __dummy_ptr_##__type_var##__concept + + +#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y) \ + typedef void (* __func_##__type_x##__type_y##same_type)( __type_x, \ + __type_y ); \ + template < __func_##__type_x##__type_y##same_type _Tp1> \ + struct __dummy_struct_##__type_x##__type_y##_same_type { }; \ + static __dummy_struct_##__type_x##__type_y##_same_type< \ + _STL_SAME_TYPE_ERROR<__type_x, __type_y>::__type_X_not_same_as_type_Y> \ + __dummy_ptr_##__type_x##__type_y##_same_type + + +#define __STL_CLASS_GENERATOR_CHECK(__func, __ret) \ + typedef __ret (* __f_##__func##__ret##_generator)( __func& ); \ + template <__f_##__func##__ret##_generator _Tp1> \ + struct __dummy_struct_##__func##__ret##_generator { }; \ + static __dummy_struct_##__func##__ret##_generator< \ + _STL_GENERATOR_ERROR< \ + __func, __ret>::__generator_requirement_violation> \ + __dummy_ptr_##__func##__ret##_generator + + +#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg) \ + typedef __ret (* __f_##__func##__ret##__arg##_unary_check)( __func&, \ + const __arg& ); \ + template <__f_##__func##__ret##__arg##_unary_check _Tp1> \ + struct __dummy_struct_##__func##__ret##__arg##_unary_check { }; \ + static __dummy_struct_##__func##__ret##__arg##_unary_check< \ + _STL_UNARY_FUNCTION_ERROR< \ + __func, __ret, __arg>::__unary_function_requirement_violation> \ + __dummy_ptr_##__func##__ret##__arg##_unary_check + + +#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second) \ + typedef __ret (* __f_##__func##__ret##__first##__second##_binary_check)( __func&, const __first&,\ + const __second& ); \ + template <__f_##__func##__ret##__first##__second##_binary_check _Tp1> \ + struct __dummy_struct_##__func##__ret##__first##__second##_binary_check { }; \ + static __dummy_struct_##__func##__ret##__first##__second##_binary_check< \ + _STL_BINARY_FUNCTION_ERROR<__func, __ret, __first, __second>:: \ + __binary_function_requirement_violation> \ + __dummy_ptr_##__func##__ret##__first##__second##_binary_check + + +#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second) \ + typedef __ret (* __f_##__func##__ret##__first##__second##_binary_op)(const __first&, \ + const __second& ); \ + template <__f_##__func##__ret##__first##__second##_binary_op _Tp1> \ + struct __dummy_struct_##__func##__ret##__first##__second##_binary_op { }; \ + static __dummy_struct_##__func##__ret##__first##__second##_binary_op< \ + _STL_BINARY##__opname##_ERROR<__ret, __first, __second>:: \ + __binary_operator_requirement_violation> \ + __dummy_ptr_##__func##__ret##__first##__second##_binary_op + +#endif + +/* helper class for finding non-const version of a type. Need to have + something to assign to etc. when testing constant iterators. */ + +template <class _Tp> +struct _Mutable_trait { + typedef _Tp _Type; +}; +template <class _Tp> +struct _Mutable_trait<const _Tp> { + typedef _Tp _Type; +}; + + +/* helper function for avoiding compiler warnings about unused variables */ +template <class _Type> +void __sink_unused_warning(_Type) { } + +template <class _TypeX, class _TypeY> +struct _STL_CONVERT_ERROR { + static void + __type_X_is_not_convertible_to_type_Y(_TypeX __x, _TypeY) { + _TypeY __y = __x; + __sink_unused_warning(__y); + } +}; + + +template <class _Type> struct __check_equal { }; + +template <class _TypeX, class _TypeY> +struct _STL_SAME_TYPE_ERROR { + static void + __type_X_not_same_as_type_Y(_TypeX , _TypeY ) { + __check_equal<_TypeX> t1 = __check_equal<_TypeY>(); + } +}; + + +// Some Functon Object Checks + +template <class _Func, class _Ret> +struct _STL_GENERATOR_ERROR { + static _Ret __generator_requirement_violation(_Func& __f) { + return __f(); + } +}; + +template <class _Func> +struct _STL_GENERATOR_ERROR<_Func, void> { + static void __generator_requirement_violation(_Func& __f) { + __f(); + } +}; + + +template <class _Func, class _Ret, class _Arg> +struct _STL_UNARY_FUNCTION_ERROR { + static _Ret + __unary_function_requirement_violation(_Func& __f, + const _Arg& __arg) { + return __f(__arg); + } +}; + +template <class _Func, class _Arg> +struct _STL_UNARY_FUNCTION_ERROR<_Func, void, _Arg> { + static void + __unary_function_requirement_violation(_Func& __f, + const _Arg& __arg) { + __f(__arg); + } +}; + +template <class _Func, class _Ret, class _First, class _Second> +struct _STL_BINARY_FUNCTION_ERROR { + static _Ret + __binary_function_requirement_violation(_Func& __f, + const _First& __first, + const _Second& __second) { + return __f(__first, __second); + } +}; + +template <class _Func, class _First, class _Second> +struct _STL_BINARY_FUNCTION_ERROR<_Func, void, _First, _Second> { + static void + __binary_function_requirement_violation(_Func& __f, + const _First& __first, + const _Second& __second) { + __f(__first, __second); + } +}; + + +#define __STL_DEFINE_BINARY_OP_CHECK(_OP, _NAME) \ +template <class _Ret, class _First, class _Second> \ +struct _STL_BINARY##_NAME##_ERROR { \ + static _Ret \ + __const_binary_operator_requirement_violation(const _First& __first, \ + const _Second& __second) { \ + return __first _OP __second; \ + } \ + static _Ret \ + __binary_operator_requirement_violation(_First& __first, \ + _Second& __second) { \ + return __first _OP __second; \ + } \ +} + +__STL_DEFINE_BINARY_OP_CHECK(==, _OP_EQUAL); +__STL_DEFINE_BINARY_OP_CHECK(!=, _OP_NOT_EQUAL); +__STL_DEFINE_BINARY_OP_CHECK(<, _OP_LESS_THAN); +__STL_DEFINE_BINARY_OP_CHECK(<=, _OP_LESS_EQUAL); +__STL_DEFINE_BINARY_OP_CHECK(>, _OP_GREATER_THAN); +__STL_DEFINE_BINARY_OP_CHECK(>=, _OP_GREATER_EQUAL); +__STL_DEFINE_BINARY_OP_CHECK(+, _OP_PLUS); +__STL_DEFINE_BINARY_OP_CHECK(*, _OP_TIMES); +__STL_DEFINE_BINARY_OP_CHECK(/, _OP_DIVIDE); +__STL_DEFINE_BINARY_OP_CHECK(-, _OP_SUBTRACT); +__STL_DEFINE_BINARY_OP_CHECK(%, _OP_MOD); +// ... + +// TODO, add unary operators (prefix and postfix) + +/* + The presence of this class is just to trick EDG into displaying + these error messages before any other errors. Without the + classes, the errors in the functions get reported after + other class errors deep inside the library. The name + choice just makes for an eye catching error message :) + */ +struct _STL_ERROR { + + template <class _Type> + static _Type + __default_constructor_requirement_violation(_Type) { + return _Type(); + } + template <class _Type> + static _Type + __assignment_operator_requirement_violation(_Type __a) { + __a = __a; + return __a; + } + template <class _Type> + static _Type + __copy_constructor_requirement_violation(_Type __a) { + _Type __c(__a); + return __c; + } + template <class _Type> + static _Type + __const_parameter_required_for_copy_constructor(_Type /* __a */, + const _Type& __b) { + _Type __c(__b); + return __c; + } + template <class _Type> + static _Type + __const_parameter_required_for_assignment_operator(_Type __a, + const _Type& __b) { + __a = __b; + return __a; + } + template <class _Type> + static _Type + __less_than_comparable_requirement_violation(_Type __a, _Type __b) { + if (__a < __b || __a > __b || __a <= __b || __a >= __b) return __a; + return __b; + } + template <class _Type> + static _Type + __equality_comparable_requirement_violation(_Type __a, _Type __b) { + if (__a == __b || __a != __b) return __a; + return __b; + } + template <class _Iterator> + static void + __dereference_operator_requirement_violation(_Iterator __i) { + __sink_unused_warning(*__i); + } + template <class _Iterator> + static void + __dereference_operator_and_assignment_requirement_violation(_Iterator __i) { + *__i = *__i; + } + template <class _Iterator> + static void + __preincrement_operator_requirement_violation(_Iterator __i) { + ++__i; + } + template <class _Iterator> + static void + __postincrement_operator_requirement_violation(_Iterator __i) { + __i++; + } + template <class _Iterator> + static void + __predecrement_operator_requirement_violation(_Iterator __i) { + --__i; + } + template <class _Iterator> + static void + __postdecrement_operator_requirement_violation(_Iterator __i) { + __i--; + } + template <class _Iterator, class _Type> + static void + __postincrement_operator_and_assignment_requirement_violation(_Iterator __i, + _Type __t) { + *__i++ = __t; + } + template <class _Iterator, class _Distance> + static _Iterator + __iterator_addition_assignment_requirement_violation(_Iterator __i, + _Distance __n) { + __i += __n; + return __i; + } + template <class _Iterator, class _Distance> + static _Iterator + __iterator_addition_requirement_violation(_Iterator __i, _Distance __n) { + __i = __i + __n; + __i = __n + __i; + return __i; + } + template <class _Iterator, class _Distance> + static _Iterator + __iterator_subtraction_assignment_requirement_violation(_Iterator __i, + _Distance __n) { + __i -= __n; + return __i; + } + template <class _Iterator, class _Distance> + static _Iterator + __iterator_subtraction_requirement_violation(_Iterator __i, _Distance __n) { + __i = __i - __n; + return __i; + } + template <class _Iterator, class _Distance> + static _Distance + __difference_operator_requirement_violation(_Iterator __i, _Iterator __j, + _Distance __n) { + __n = __i - __j; + return __n; + } + template <class _Exp, class _Type, class _Distance> + static _Type + __element_access_operator_requirement_violation(_Exp __x, _Type*, + _Distance __n) { + return __x[__n]; + } + template <class _Exp, class _Type, class _Distance> + static void + __element_assignment_operator_requirement_violation(_Exp __x, + _Type* __t, + _Distance __n) { + __x[__n] = *__t; + } + +}; /* _STL_ERROR */ + +/* Associated Type Requirements */ + +__STL_BEGIN_NAMESPACE +template <class _Iterator> struct iterator_traits; +__STL_END_NAMESPACE + +template <class _Iter> +struct __value_type_type_definition_requirement_violation { + typedef typename __STD::iterator_traits<_Iter>::value_type value_type; +}; + +template <class _Iter> +struct __difference_type_type_definition_requirement_violation { + typedef typename __STD::iterator_traits<_Iter>::difference_type + difference_type; +}; + +template <class _Iter> +struct __reference_type_definition_requirement_violation { + typedef typename __STD::iterator_traits<_Iter>::reference reference; +}; + +template <class _Iter> +struct __pointer_type_definition_requirement_violation { + typedef typename __STD::iterator_traits<_Iter>::pointer pointer; +}; + +template <class _Iter> +struct __iterator_category_type_definition_requirement_violation { + typedef typename __STD::iterator_traits<_Iter>::iterator_category + iterator_category; +}; + +/* Assignable Requirements */ + + +template <class _Type> +struct _Assignable_concept_specification { + static void _Assignable_requirement_violation(_Type __a) { + _STL_ERROR::__assignment_operator_requirement_violation(__a); + _STL_ERROR::__copy_constructor_requirement_violation(__a); + _STL_ERROR::__const_parameter_required_for_copy_constructor(__a,__a); + _STL_ERROR::__const_parameter_required_for_assignment_operator(__a,__a); + } +}; + +/* DefaultConstructible Requirements */ + + +template <class _Type> +struct _DefaultConstructible_concept_specification { + static void _DefaultConstructible_requirement_violation(_Type __a) { + _STL_ERROR::__default_constructor_requirement_violation(__a); + } +}; + +/* EqualityComparable Requirements */ + +template <class _Type> +struct _EqualityComparable_concept_specification { + static void _EqualityComparable_requirement_violation(_Type __a) { + _STL_ERROR::__equality_comparable_requirement_violation(__a, __a); + } +}; + +/* LessThanComparable Requirements */ +template <class _Type> +struct _LessThanComparable_concept_specification { + static void _LessThanComparable_requirement_violation(_Type __a) { + _STL_ERROR::__less_than_comparable_requirement_violation(__a, __a); + } +}; + +/* TrivialIterator Requirements */ + +template <class _TrivialIterator> +struct _TrivialIterator_concept_specification { +static void +_TrivialIterator_requirement_violation(_TrivialIterator __i) { + typedef typename + __value_type_type_definition_requirement_violation<_TrivialIterator>:: + value_type __T; + // Refinement of Assignable + _Assignable_concept_specification<_TrivialIterator>:: + _Assignable_requirement_violation(__i); + // Refinement of DefaultConstructible + _DefaultConstructible_concept_specification<_TrivialIterator>:: + _DefaultConstructible_requirement_violation(__i); + // Refinement of EqualityComparable + _EqualityComparable_concept_specification<_TrivialIterator>:: + _EqualityComparable_requirement_violation(__i); + // Valid Expressions + _STL_ERROR::__dereference_operator_requirement_violation(__i); +} +}; + +template <class _TrivialIterator> +struct _Mutable_TrivialIterator_concept_specification { +static void +_Mutable_TrivialIterator_requirement_violation(_TrivialIterator __i) { + _TrivialIterator_concept_specification<_TrivialIterator>:: + _TrivialIterator_requirement_violation(__i); + // Valid Expressions + _STL_ERROR::__dereference_operator_and_assignment_requirement_violation(__i); +} +}; + +/* InputIterator Requirements */ + +template <class _InputIterator> +struct _InputIterator_concept_specification { +static void +_InputIterator_requirement_violation(_InputIterator __i) { + // Refinement of TrivialIterator + _TrivialIterator_concept_specification<_InputIterator>:: + _TrivialIterator_requirement_violation(__i); + // Associated Types + __difference_type_type_definition_requirement_violation<_InputIterator>(); + __reference_type_definition_requirement_violation<_InputIterator>(); + __pointer_type_definition_requirement_violation<_InputIterator>(); + __iterator_category_type_definition_requirement_violation<_InputIterator>(); + // Valid Expressions + _STL_ERROR::__preincrement_operator_requirement_violation(__i); + _STL_ERROR::__postincrement_operator_requirement_violation(__i); +} +}; + +/* OutputIterator Requirements */ + +template <class _OutputIterator> +struct _OutputIterator_concept_specification { +static void +_OutputIterator_requirement_violation(_OutputIterator __i) { + // Refinement of Assignable + _Assignable_concept_specification<_OutputIterator>:: + _Assignable_requirement_violation(__i); + // Associated Types + __iterator_category_type_definition_requirement_violation<_OutputIterator>(); + // Valid Expressions + _STL_ERROR::__dereference_operator_requirement_violation(__i); + _STL_ERROR::__preincrement_operator_requirement_violation(__i); + _STL_ERROR::__postincrement_operator_requirement_violation(__i); + _STL_ERROR:: + __postincrement_operator_and_assignment_requirement_violation(__i, *__i); +} +}; + +/* ForwardIterator Requirements */ + +template <class _ForwardIterator> +struct _ForwardIterator_concept_specification { +static void +_ForwardIterator_requirement_violation(_ForwardIterator __i) { + // Refinement of InputIterator + _InputIterator_concept_specification<_ForwardIterator>:: + _InputIterator_requirement_violation(__i); +} +}; + +template <class _ForwardIterator> +struct _Mutable_ForwardIterator_concept_specification { +static void +_Mutable_ForwardIterator_requirement_violation(_ForwardIterator __i) { + _ForwardIterator_concept_specification<_ForwardIterator>:: + _ForwardIterator_requirement_violation(__i); + // Refinement of OutputIterator + _OutputIterator_concept_specification<_ForwardIterator>:: + _OutputIterator_requirement_violation(__i); +} +}; + +/* BidirectionalIterator Requirements */ + +template <class _BidirectionalIterator> +struct _BidirectionalIterator_concept_specification { +static void +_BidirectionalIterator_requirement_violation(_BidirectionalIterator __i) { + // Refinement of ForwardIterator + _ForwardIterator_concept_specification<_BidirectionalIterator>:: + _ForwardIterator_requirement_violation(__i); + // Valid Expressions + _STL_ERROR::__predecrement_operator_requirement_violation(__i); + _STL_ERROR::__postdecrement_operator_requirement_violation(__i); +} +}; + +template <class _BidirectionalIterator> +struct _Mutable_BidirectionalIterator_concept_specification { +static void +_Mutable_BidirectionalIterator_requirement_violation( + _BidirectionalIterator __i) +{ + _BidirectionalIterator_concept_specification<_BidirectionalIterator>:: + _BidirectionalIterator_requirement_violation(__i); + // Refinement of mutable_ForwardIterator + _Mutable_ForwardIterator_concept_specification<_BidirectionalIterator>:: + _Mutable_ForwardIterator_requirement_violation(__i); + typedef typename + __value_type_type_definition_requirement_violation< + _BidirectionalIterator>::value_type __T; + typename _Mutable_trait<__T>::_Type* __tmp_ptr = 0; + // Valid Expressions + _STL_ERROR:: + __postincrement_operator_and_assignment_requirement_violation(__i, + *__tmp_ptr); +} +}; + +/* RandomAccessIterator Requirements */ + +template <class _RandAccIter> +struct _RandomAccessIterator_concept_specification { +static void +_RandomAccessIterator_requirement_violation(_RandAccIter __i) { + // Refinement of BidirectionalIterator + _BidirectionalIterator_concept_specification<_RandAccIter>:: + _BidirectionalIterator_requirement_violation(__i); + // Refinement of LessThanComparable + _LessThanComparable_concept_specification<_RandAccIter>:: + _LessThanComparable_requirement_violation(__i); + typedef typename + __value_type_type_definition_requirement_violation<_RandAccIter> + ::value_type + value_type; + typedef typename + __difference_type_type_definition_requirement_violation<_RandAccIter> + ::difference_type + _Dist; + typedef typename _Mutable_trait<_Dist>::_Type _MutDist; + + // Valid Expressions + _STL_ERROR::__iterator_addition_assignment_requirement_violation(__i, + _MutDist()); + _STL_ERROR::__iterator_addition_requirement_violation(__i, + _MutDist()); + _STL_ERROR:: + __iterator_subtraction_assignment_requirement_violation(__i, + _MutDist()); + _STL_ERROR::__iterator_subtraction_requirement_violation(__i, + _MutDist()); + _STL_ERROR::__difference_operator_requirement_violation(__i, __i, + _MutDist()); + typename _Mutable_trait<value_type>::_Type* __dummy_ptr = 0; + _STL_ERROR::__element_access_operator_requirement_violation(__i, + __dummy_ptr, + _MutDist()); +} +}; + +template <class _RandAccIter> +struct _Mutable_RandomAccessIterator_concept_specification { +static void +_Mutable_RandomAccessIterator_requirement_violation(_RandAccIter __i) +{ + _RandomAccessIterator_concept_specification<_RandAccIter>:: + _RandomAccessIterator_requirement_violation(__i); + // Refinement of mutable_BidirectionalIterator + _Mutable_BidirectionalIterator_concept_specification<_RandAccIter>:: + _Mutable_BidirectionalIterator_requirement_violation(__i); + typedef typename + __value_type_type_definition_requirement_violation<_RandAccIter> + ::value_type + value_type; + typedef typename + __difference_type_type_definition_requirement_violation<_RandAccIter> + ::difference_type + _Dist; + + typename _Mutable_trait<value_type>::_Type* __tmp_ptr = 0; + // Valid Expressions + _STL_ERROR::__element_assignment_operator_requirement_violation(__i, + __tmp_ptr, _Dist()); +} +}; + +#define __STL_TYPEDEF_REQUIREMENT(__REQUIREMENT) \ +template <class Type> \ +struct __##__REQUIREMENT##__typedef_requirement_violation { \ + typedef typename Type::__REQUIREMENT __REQUIREMENT; \ +} + +__STL_TYPEDEF_REQUIREMENT(value_type); +__STL_TYPEDEF_REQUIREMENT(difference_type); +__STL_TYPEDEF_REQUIREMENT(size_type); +__STL_TYPEDEF_REQUIREMENT(reference); +__STL_TYPEDEF_REQUIREMENT(const_reference); +__STL_TYPEDEF_REQUIREMENT(pointer); +__STL_TYPEDEF_REQUIREMENT(const_pointer); + + +template <class _Alloc> +struct _Allocator_concept_specification { +static void +_Allocator_requirement_violation(_Alloc __a) { + // Refinement of DefaultConstructible + _DefaultConstructible_concept_specification<_Alloc>:: + _DefaultConstructible_requirement_violation(__a); + // Refinement of EqualityComparable + _EqualityComparable_concept_specification<_Alloc>:: + _EqualityComparable_requirement_violation(__a); + // Associated Types + __value_type__typedef_requirement_violation<_Alloc>(); + __difference_type__typedef_requirement_violation<_Alloc>(); + __size_type__typedef_requirement_violation<_Alloc>(); + __reference__typedef_requirement_violation<_Alloc>(); + __const_reference__typedef_requirement_violation<_Alloc>(); + __pointer__typedef_requirement_violation<_Alloc>(); + __const_pointer__typedef_requirement_violation<_Alloc>(); + typedef typename _Alloc::value_type _Tp; + //__STL_REQUIRES_SAME_TYPE(typename _Alloc::__STL_TEMPLATE rebind<_Tp>::other, + // _Alloc); +} +}; + +#endif /* __STL_USE_CONCEPT_CHECKS */ + +#endif /* __CONCEPT_CHECKS_H */ + +// Local Variables: +// mode:C++ +// End: diff --git a/WebCore/platform/android/stl/cstring b/WebCore/platform/android/stl/cstring new file mode 100644 index 0000000..77c9175 --- /dev/null +++ b/WebCore/platform/android/stl/cstring @@ -0,0 +1,128 @@ +// -*- C++ -*- forwarding header. + +// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 +// Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING. If not, write to the Free +// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. + +// As a special exception, you may use this file as part of a free software +// library without restriction. Specifically, if other files instantiate +// templates or use macros or inline functions from this file, or you compile +// this file and link it with other files to produce an executable, this +// file does not by itself cause the resulting executable to be covered by +// the GNU General Public License. This exception does not however +// invalidate any other reasons why the executable file might be covered by +// the GNU General Public License. + +// +// ISO C++ 14882: 20.4.6 C library +// + +/** @file cstring + * This is a Standard C++ Library file. You should @c #include this file + * in your programs, rather than any of the "*.h" implementation files. + * + * This is the C++ version of the Standard C Library header @c string.h, + * and its contents are (mostly) the same as that header, but are all + * contained in the namespace @c std. + */ + +#ifndef _GLIBCXX_CSTRING +#define _GLIBCXX_CSTRING 1 + +#pragma GCC system_header + +#include <cstddef> + +#include <string.h> + +// Get rid of those macros defined in <string.h> in lieu of real functions. +#undef memcpy +#undef memmove +#undef strcpy +#undef strncpy +#undef strcat +#undef strncat +#undef memcmp +#undef strcmp +#undef strcoll +#undef strncmp +#undef strxfrm +#undef memchr +#undef strchr +#undef strcspn +#undef strpbrk +#undef strrchr +#undef strspn +#undef strstr +#undef strtok +#undef memset +#undef strerror +#undef strlen + +namespace std +{ + using ::memcpy; + using ::memmove; + using ::strcpy; + using ::strncpy; + using ::strcat; + using ::strncat; + using ::memcmp; + using ::strcmp; +// using ::strcoll; + using ::strncmp; +// using ::strxfrm; + using ::strcspn; + using ::strspn; + using ::strtok; + using ::memset; + using ::strerror; + using ::strlen; + + using ::memchr; + + inline void* + memchr(void* __p, int __c, size_t __n) + { return memchr(const_cast<const void*>(__p), __c, __n); } + + using ::strchr; + + inline char* + strchr(char* __s1, int __n) + { return __builtin_strchr(const_cast<const char*>(__s1), __n); } + + using ::strpbrk; + + inline char* + strpbrk(char* __s1, const char* __s2) + { return __builtin_strpbrk(const_cast<const char*>(__s1), __s2); } + + using ::strrchr; + + inline char* + strrchr(char* __s1, int __n) + { return __builtin_strrchr(const_cast<const char*>(__s1), __n); } + + using ::strstr; + + inline char* + strstr(char* __s1, const char* __s2) + { return __builtin_strstr(const_cast<const char*>(__s1), __s2); } +} + +#endif diff --git a/WebCore/platform/android/stl/heap.h b/WebCore/platform/android/stl/heap.h new file mode 100644 index 0000000..d1fb319 --- /dev/null +++ b/WebCore/platform/android/stl/heap.h @@ -0,0 +1,47 @@ +/* + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright (c) 1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +#ifndef __SGI_STL_HEAP_H +#define __SGI_STL_HEAP_H + +#include <concept_checks.h> +#include <stl_config.h> +#include <stl_heap.h> + +#ifdef __STL_USE_NAMESPACES + +using __STD::push_heap; +using __STD::pop_heap; +using __STD::make_heap; +using __STD::sort_heap; + +#endif /* __STL_USE_NAMESPACES */ + + +#endif /* __SGI_STL_HEAP_H */ + +// Local Variables: +// mode:C++ +// End: diff --git a/WebCore/platform/android/stl/iostream.h b/WebCore/platform/android/stl/iostream.h new file mode 100644 index 0000000..2b7d968 --- /dev/null +++ b/WebCore/platform/android/stl/iostream.h @@ -0,0 +1 @@ +// this file intentionally left blank diff --git a/WebCore/platform/android/stl/limits b/WebCore/platform/android/stl/limits new file mode 100644 index 0000000..d4d7960 --- /dev/null +++ b/WebCore/platform/android/stl/limits @@ -0,0 +1,23 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef __ANDROID_LIMITS +#define __ANDROID_LIMITS + +#include <limits.h> + +#endif // __ANDROID_LIMITS diff --git a/WebCore/platform/android/stl/memory b/WebCore/platform/android/stl/memory new file mode 100644 index 0000000..b224c33 --- /dev/null +++ b/WebCore/platform/android/stl/memory @@ -0,0 +1,132 @@ +/* + * Copyright (c) 1997-1999 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ + +//minimal file to get build to work + +#ifdef _CPP_MEMORY +#error "real STL defined" +#endif + +#ifndef __ANDROID_MEMORY +#define __ANDROID_MEMORY +//we good to do this? +#define __STL_MEMBER_TEMPLATES + +#define __STL_NOTHROW + +/*#if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && \*/ + +#if defined(__STL_MEMBER_TEMPLATES) +template<class _Tp1> struct auto_ptr_ref { + _Tp1* _M_ptr; + auto_ptr_ref(_Tp1* __p) : _M_ptr(__p) {} +}; +#endif + +namespace std { + +template <class _Tp> class auto_ptr { +private: + _Tp* _M_ptr; + +public: + typedef _Tp element_type; + + explicit auto_ptr(_Tp* __p = 0) __STL_NOTHROW : _M_ptr(__p) {} + auto_ptr(auto_ptr& __a) __STL_NOTHROW : _M_ptr(__a.release()) {} + +#ifdef __STL_MEMBER_TEMPLATES + template <class _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) __STL_NOTHROW + : _M_ptr(__a.release()) {} +#endif /* __STL_MEMBER_TEMPLATES */ + + auto_ptr& operator=(auto_ptr& __a) __STL_NOTHROW { + if (&__a != this) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } + +#ifdef __STL_MEMBER_TEMPLATES + template <class _Tp1> + auto_ptr& operator=(auto_ptr<_Tp1>& __a) __STL_NOTHROW { + if (__a.get() != this->get()) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } +#endif /* __STL_MEMBER_TEMPLATES */ + + // Note: The C++ standard says there is supposed to be an empty throw + // specification here, but omitting it is standard conforming. Its + // presence can be detected only if _Tp::~_Tp() throws, but (17.4.3.6/2) + // this is prohibited. + ~auto_ptr() { delete _M_ptr; } + + _Tp& operator*() const __STL_NOTHROW { + return *_M_ptr; + } + _Tp* operator->() const __STL_NOTHROW { + return _M_ptr; + } + _Tp* get() const __STL_NOTHROW { + return _M_ptr; + } + _Tp* release() __STL_NOTHROW { + _Tp* __tmp = _M_ptr; + _M_ptr = 0; + return __tmp; + } + void reset(_Tp* __p = 0) __STL_NOTHROW { + if (__p != _M_ptr) { + delete _M_ptr; + _M_ptr = __p; + } + } + +/*#if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && \*/ +#if defined(__STL_MEMBER_TEMPLATES) + +public: + auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW + : _M_ptr(__ref._M_ptr) {} + + auto_ptr& operator=(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW { + if (__ref._M_ptr != this->get()) { + delete _M_ptr; + _M_ptr = __ref._M_ptr; + } + return *this; + } + + template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW + { return auto_ptr_ref<_Tp1>(this->release()); } + template <class _Tp1> operator auto_ptr<_Tp1>() __STL_NOTHROW + { return auto_ptr<_Tp1>(this->release()); } + +#endif /* auto ptr conversions && member templates */ + +}; + +} //namespace std + +#endif /* __ANDROID_MEMORY */ + + +// Local Variables: +// mode:C++ +// End: + diff --git a/WebCore/platform/android/stl/new.h b/WebCore/platform/android/stl/new.h new file mode 100644 index 0000000..13d6d4e --- /dev/null +++ b/WebCore/platform/android/stl/new.h @@ -0,0 +1,17 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + diff --git a/WebCore/platform/android/stl/stl_config.h b/WebCore/platform/android/stl/stl_config.h new file mode 100644 index 0000000..8c107c1 --- /dev/null +++ b/WebCore/platform/android/stl/stl_config.h @@ -0,0 +1,576 @@ +/* + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright (c) 1997 + * Silicon Graphics + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ + +#ifndef __STL_CONFIG_H +# define __STL_CONFIG_H + +// Flags: +// * __STL_NO_BOOL: defined if the compiler doesn't have bool as a builtin +// type. +// * __STL_HAS_WCHAR_T: defined if the compier has wchar_t as a builtin type. +// * __STL_NO_DRAND48: defined if the compiler doesn't have the drand48 +// function. +// * __STL_STATIC_TEMPLATE_MEMBER_BUG: defined if the compiler can't handle +// static members of template classes. +// * __STL_STATIC_CONST_INIT_BUG: defined if the compiler can't handle a +// constant-initializer in the declaration of a static const data member +// of integer type. (See section 9.4.2, paragraph 4, of the C++ standard.) +// * __STL_CLASS_PARTIAL_SPECIALIZATION: defined if the compiler supports +// partial specialization of template classes. +// * __STL_PARTIAL_SPECIALIZATION_SYNTAX: defined if the compiler +// supports partial specialization syntax for full specialization of +// class templates. (Even if it doesn't actually support partial +// specialization itself.) +// * __STL_FUNCTION_TMPL_PARTIAL_ORDER: defined if the compiler supports +// partial ordering of function templates. (a.k.a partial specialization +// of function templates.) +// * __STL_MEMBER_TEMPLATES: defined if the compiler supports template +// member functions of classes. +// * __STL_MEMBER_TEMPLATE_CLASSES: defined if the compiler supports +// nested classes that are member templates of other classes. +// * __STL_TEMPLATE_FRIENDS: defined if the compiler supports templatized +// friend declarations. +// * __STL_EXPLICIT_FUNCTION_TMPL_ARGS: defined if the compiler +// supports calling a function template by providing its template +// arguments explicitly. +// * __STL_LIMITED_DEFAULT_TEMPLATES: defined if the compiler is unable +// to handle default template parameters that depend on previous template +// parameters. +// * __STL_NON_TYPE_TMPL_PARAM_BUG: defined if the compiler has trouble with +// function template argument deduction for non-type template parameters. +// * __SGI_STL_NO_ARROW_OPERATOR: defined if the compiler is unable +// to support the -> operator for iterators. +// * __STL_DEFAULT_CONSTRUCTOR_BUG: defined if T() does not work properly +// when T is a builtin type. +// * __STL_USE_EXCEPTIONS: defined if the compiler (in the current compilation +// mode) supports exceptions. +// * __STL_USE_NAMESPACES: defined if the compiler has the necessary +// support for namespaces. +// * __STL_NO_EXCEPTION_HEADER: defined if the compiler does not have a +// standard-conforming header <exception>. +// * __STL_NO_BAD_ALLOC: defined if the compiler does not have a <new> +// header, or if <new> does not contain a bad_alloc class. If a bad_alloc +// class exists, it is assumed to be in namespace std. +// * __STL_SGI_THREADS: defined if this is being compiled for an SGI IRIX +// system in multithreaded mode, using native SGI threads instead of +// pthreads. +// * __STL_WIN32THREADS: defined if this is being compiled on a WIN32 +// compiler in multithreaded mode. +// * __STL_PTHREADS: defined if we should use portable pthreads +// synchronization. +// * __STL_UITHREADS: defined if we should use UI / solaris / UnixWare threads +// synchronization. UIthreads are similar to pthreads, but are based +// on an earlier version of the Posix threads standard. +// * __STL_LONG_LONG if the compiler has long long and unsigned long long +// types. (They're not in the C++ standard, but they are expected to be +// included in the forthcoming C9X standard.) +// * __STL_THREADS is defined if thread safety is needed. +// * __STL_VOLATILE is defined to be "volatile" if threads are being +// used, and the empty string otherwise. +// * __STL_USE_CONCEPT_CHECKS enables some extra compile-time error +// checking to make sure that user-defined template arguments satisfy +// all of the appropriate requirements. This may result in more +// comprehensible error messages. It incurs no runtime overhead. This +// feature requires member templates and partial specialization. +// * __STL_NO_USING_CLAUSE_IN_CLASS: The compiler does not handle "using" +// clauses inside of class definitions. +// * __STL_NO_FRIEND_TEMPLATE_CLASS: The compiler does not handle friend +// declaractions where the friend is a template class. +// * __STL_NO_FUNCTION_PTR_IN_CLASS_TEMPLATE: The compiler does not +// support the use of a function pointer type as the argument +// for a template. +// * __STL_MEMBER_TEMPLATE_KEYWORD: standard C++ requires the template +// keyword in a few new places (14.2.4). This flag is set for +// compilers that support (and require) this usage. + + +// User-settable macros that control compilation: +// * __STL_USE_SGI_ALLOCATORS: if defined, then the STL will use older +// SGI-style allocators, instead of standard-conforming allocators, +// even if the compiler supports all of the language features needed +// for standard-conforming allocators. +// * __STL_NO_NAMESPACES: if defined, don't put the library in namespace +// std, even if the compiler supports namespaces. +// * __STL_NO_RELOPS_NAMESPACE: if defined, don't put the relational +// operator templates (>, <=. >=, !=) in namespace std::rel_ops, even +// if the compiler supports namespaces and partial ordering of +// function templates. +// * __STL_ASSERTIONS: if defined, then enable runtime checking through the +// __stl_assert macro. +// * _PTHREADS: if defined, use Posix threads for multithreading support. +// * _UITHREADS:if defined, use SCO/Solaris/UI threads for multithreading +// support +// * _NOTHREADS: if defined, don't use any multithreading support. +// * _STL_NO_CONCEPT_CHECKS: if defined, disables the error checking that +// we get from __STL_USE_CONCEPT_CHECKS. +// * __STL_USE_NEW_IOSTREAMS: if defined, then the STL will use new, +// standard-conforming iostreams (e.g. the <iosfwd> header). If not +// defined, the STL will use old cfront-style iostreams (e.g. the +// <iostream.h> header). + +// Other macros defined by this file: + +// * bool, true, and false, if __STL_NO_BOOL is defined. +// * typename, as a null macro if it's not already a keyword. +// * explicit, as a null macro if it's not already a keyword. +// * namespace-related macros (__STD, __STL_BEGIN_NAMESPACE, etc.) +// * exception-related macros (__STL_TRY, __STL_UNWIND, etc.) +// * __stl_assert, either as a test or as a null macro, depending on +// whether or not __STL_ASSERTIONS is defined. + +# if defined(_PTHREADS) && !defined(_NOTHREADS) +# define __STL_PTHREADS +# endif + +# if defined(_UITHREADS) && !defined(_PTHREADS) && !defined(_NOTHREADS) +# define __STL_UITHREADS +# endif + +# if defined(__sgi) && !defined(__GNUC__) +# include <standards.h> +# if !defined(_BOOL) +# define __STL_NO_BOOL +# endif +# if defined(_MIPS_SIM) && _MIPS_SIM == _ABIO32 +# define __STL_STATIC_CONST_INIT_BUG +# endif +# if defined(_WCHAR_T_IS_KEYWORD) +# define __STL_HAS_WCHAR_T +# endif +# if !defined(_TYPENAME_IS_KEYWORD) +# define __STL_NEED_TYPENAME +# endif +# ifdef _PARTIAL_SPECIALIZATION_OF_CLASS_TEMPLATES +# define __STL_CLASS_PARTIAL_SPECIALIZATION +# endif +# if (_COMPILER_VERSION >= 730) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 +# define __STL_FUNCTION_TMPL_PARTIAL_ORDER +# endif +# ifdef _MEMBER_TEMPLATES +# define __STL_MEMBER_TEMPLATES +# define __STL_TEMPLATE_FRIENDS +# define __STL_MEMBER_TEMPLATE_CLASSES +# endif +# if defined(_MEMBER_TEMPLATE_KEYWORD) +# define __STL_MEMBER_TEMPLATE_KEYWORD +# endif +# if defined(_STANDARD_C_PLUS_PLUS) +# define __STL_EXPLICIT_FUNCTION_TMPL_ARGS +# endif +# if (_COMPILER_VERSION >= 730) && defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32 +# define __STL_MEMBER_TEMPLATE_KEYWORD +# endif +# if COMPILER_VERSION < 720 || (defined(_MIPS_SIM) && _MIPS_SIM == _ABIO32) +# define __STL_DEFAULT_CONSTRUCTOR_BUG +# endif +# if !defined(_EXPLICIT_IS_KEYWORD) +# define __STL_NEED_EXPLICIT +# endif +# ifdef __EXCEPTIONS +# define __STL_USE_EXCEPTIONS +# endif +# if (_COMPILER_VERSION >= 721) && defined(_NAMESPACES) +# define __STL_HAS_NAMESPACES +# endif +# if (_COMPILER_VERSION < 721) || \ + !defined(__STL_HAS_NAMESPACES) || defined(__STL_NO_NAMESPACES) +# define __STL_NO_EXCEPTION_HEADER +# endif +# if _COMPILER_VERSION < 730 || !defined(_STANDARD_C_PLUS_PLUS) || \ + !defined(_NAMESPACES) +# define __STL_NO_BAD_ALLOC +# endif +# if !defined(_NOTHREADS) && !defined(__STL_PTHREADS) +# define __STL_SGI_THREADS +# endif +# if defined(_LONGLONG) && defined(_SGIAPI) && _SGIAPI +# define __STL_LONG_LONG +# endif +# if _COMPILER_VERSION >= 730 && defined(_STANDARD_C_PLUS_PLUS) +# define __STL_USE_NEW_IOSTREAMS +# endif +# if _COMPILER_VERSION >= 730 && defined(_STANDARD_C_PLUS_PLUS) +# define __STL_CAN_THROW_RANGE_ERRORS +# endif +# if _COMPILER_VERSION >= 730 && defined(_STANDARD_C_PLUS_PLUS) +# define __SGI_STL_USE_AUTO_PTR_CONVERSIONS +# endif +# endif + + +/* + * Jochen Schlick '1999 - added new #defines (__STL)_UITHREADS (for + * providing SCO / Solaris / UI thread support) + * - added the necessary defines for the SCO UDK 7 + * compiler (and its template friend behavior) + * - all UDK7 specific STL changes are based on the + * macro __USLC__ being defined + */ +// SCO UDK 7 compiler (UnixWare 7x, OSR 5, UnixWare 2x) +# if defined(__USLC__) +# define __STL_HAS_WCHAR_T +# define __STL_CLASS_PARTIAL_SPECIALIZATION +# define __STL_PARTIAL_SPECIALIZATION_SYNTAX +# define __STL_FUNCTION_TMPL_PARTIAL_ORDER +# define __STL_MEMBER_TEMPLATES +# define __STL_MEMBER_TEMPLATE_CLASSES +# define __STL_USE_EXCEPTIONS +# define __STL_HAS_NAMESPACES +# define __STL_USE_NAMESPACES +# define __STL_LONG_LONG +# if defined(_REENTRANT) +# define _UITHREADS /* if UnixWare < 7.0.1 */ +# define __STL_UITHREADS +// use the following defines instead of the UI threads defines when +// you want to use POSIX threads +//# define _PTHREADS /* only if UnixWare >=7.0.1 */ +//# define __STL_PTHREADS +# endif +# endif + + + +# ifdef __GNUC__ +# if __GNUC__ == 2 && __GNUC_MINOR__ <= 7 +# define __STL_STATIC_TEMPLATE_MEMBER_BUG +# endif +# if __GNUC__ < 2 +# define __STL_NEED_TYPENAME +# define __STL_NEED_EXPLICIT +# endif +# if __GNUC__ == 2 && __GNUC_MINOR__ <= 8 +# define __STL_NO_EXCEPTION_HEADER +# define __STL_NO_BAD_ALLOC +# endif +# if __GNUC__ == 2 && __GNUC_MINOR__ >= 8 || __GNUC__ > 2 +# define __STL_CLASS_PARTIAL_SPECIALIZATION +# define __STL_FUNCTION_TMPL_PARTIAL_ORDER +# define __STL_EXPLICIT_FUNCTION_TMPL_ARGS +# define __STL_MEMBER_TEMPLATES +# define __STL_CAN_THROW_RANGE_ERRORS + // g++ 2.8.1 supports member template functions, but not member + // template nested classes. +# if __GNUC_MINOR__ >= 9 || __GNUC__ > 2 +# define __STL_MEMBER_TEMPLATE_CLASSES +# define __STL_TEMPLATE_FRIENDS +# define __SGI_STL_USE_AUTO_PTR_CONVERSIONS +# define __STL_HAS_NAMESPACES +//# define __STL_USE_NEW_IOSTREAMS +# endif +# endif +# define __STL_DEFAULT_CONSTRUCTOR_BUG +# ifdef __EXCEPTIONS +# define __STL_USE_EXCEPTIONS +# endif +# ifdef _REENTRANT +# define __STL_PTHREADS +# endif +# if (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 95) +# define __STL_NO_FUNCTION_PTR_IN_CLASS_TEMPLATE +# endif +# endif + +# if defined(__SUNPRO_CC) +# define __STL_NO_BOOL +# define __STL_NEED_TYPENAME +# define __STL_NEED_EXPLICIT +# define __STL_USE_EXCEPTIONS +# ifdef _REENTRANT +# define __STL_PTHREADS +# endif +# define __SGI_STL_NO_ARROW_OPERATOR +# define __STL_PARTIAL_SPECIALIZATION_SYNTAX +# define __STL_NO_EXCEPTION_HEADER +# define __STL_NO_BAD_ALLOC +# endif + +# if defined(__COMO__) +# define __STL_MEMBER_TEMPLATES +# define __STL_MEMBER_TEMPLATE_CLASSES +# define __STL_TEMPLATE_FRIENDS +# define __STL_CLASS_PARTIAL_SPECIALIZATION +# define __STL_USE_EXCEPTIONS +# define __STL_HAS_NAMESPACES +# endif + +// Intel compiler, which uses the EDG front end. +# if defined(__ICL) +# define __STL_LONG_LONG +# define __STL_MEMBER_TEMPLATES +# define __STL_MEMBER_TEMPLATE_CLASSES +# define __STL_TEMPLATE_FRIENDS +# define __STL_FUNCTION_TMPL_PARTIAL_ORDER +# define __STL_CLASS_PARTIAL_SPECIALIZATION +# define __STL_NO_DRAND48 +# define __STL_HAS_NAMESPACES +# define __STL_USE_EXCEPTIONS +# define __STL_MEMBER_TEMPLATE_KEYWORD +# ifdef _CPPUNWIND +# define __STL_USE_EXCEPTIONS +# endif +# ifdef _MT +# define __STL_WIN32THREADS +# endif +# endif + +// Mingw32, egcs compiler using the Microsoft C runtime +# if defined(__MINGW32__) +# define __STL_NO_DRAND48 +# ifdef _MT +# define __STL_WIN32THREADS +# endif +# endif + +// Cygwin32, egcs compiler on MS Windows +# if defined(__CYGWIN__) +# define __STL_NO_DRAND48 +# endif + + + +// Microsoft compiler. +# if defined(_MSC_VER) && !defined(__ICL) && !defined(__MWERKS__) +# define __STL_NO_DRAND48 +# define __STL_STATIC_CONST_INIT_BUG +# define __STL_NEED_TYPENAME +# define __STL_NO_USING_CLAUSE_IN_CLASS +# define __STL_NO_FRIEND_TEMPLATE_CLASS +# if _MSC_VER < 1100 /* 1000 is version 4.0, 1100 is 5.0, 1200 is 6.0. */ +# define __STL_NEED_EXPLICIT +# define __STL_NO_BOOL +# define __STL_NO_BAD_ALLOC +# endif +# if _MSC_VER > 1000 +# include <yvals.h> +# define __STL_DONT_USE_BOOL_TYPEDEF +# endif +# define __STL_NON_TYPE_TMPL_PARAM_BUG +# define __SGI_STL_NO_ARROW_OPERATOR +# define __STL_DEFAULT_CONSTRUCTOR_BUG +# ifdef _CPPUNWIND +# define __STL_USE_EXCEPTIONS +# endif +# ifdef _MT +# define __STL_WIN32THREADS +# endif +# if _MSC_VER >= 1200 +# define __STL_PARTIAL_SPECIALIZATION_SYNTAX +# define __STL_HAS_NAMESPACES +# define __STL_CAN_THROW_RANGE_ERRORS +# define NOMINMAX +# undef min +# undef max +// disable warning 'initializers put in unrecognized initialization area' +# pragma warning ( disable : 4075 ) +// disable warning 'empty controlled statement found' +# pragma warning ( disable : 4390 ) +// disable warning 'debug symbol greater than 255 chars' +# pragma warning ( disable : 4786 ) +# endif +# if _MSC_VER < 1100 +# define __STL_NO_EXCEPTION_HEADER +# define __STL_NO_BAD_ALLOC +# endif + // Because of a Microsoft front end bug, we must not provide a + // namespace qualifier when declaring a friend function. +# define __STD_QUALIFIER +# endif + +# if defined(__BORLANDC__) +# define __STL_NO_BAD_ALLOC +# define __STL_NO_DRAND48 +# define __STL_DEFAULT_CONSTRUCTOR_BUG +# if __BORLANDC__ >= 0x540 /* C++ Builder 4.0 */ +# define __STL_CLASS_PARTIAL_SPECIALIZATION +# define __STL_FUNCTION_TMPL_PARTIAL_ORDER +# define __STL_EXPLICIT_FUNCTION_TMPL_ARGS +# define __STL_MEMBER_TEMPLATES +# define __STL_TEMPLATE_FRIENDS +# else +# define __STL_NEED_TYPENAME +# define __STL_LIMITED_DEFAULT_TEMPLATES +# define __SGI_STL_NO_ARROW_OPERATOR +# define __STL_NON_TYPE_TMPL_PARAM_BUG +# endif +# ifdef _CPPUNWIND +# define __STL_USE_EXCEPTIONS +# endif +# ifdef __MT__ +# define __STL_WIN32THREADS +# endif +# endif + +# if defined(__STL_NO_BOOL) && !defined(__STL_DONT_USE_BOOL_TYPEDEF) + typedef int bool; +# define true 1 +# define false 0 +# endif + +# ifdef __STL_NEED_TYPENAME +# define typename +# endif + +# ifdef __STL_LIMITED_DEFAULT_TEMPLATES +# define __STL_DEPENDENT_DEFAULT_TMPL(_Tp) +# else +# define __STL_DEPENDENT_DEFAULT_TMPL(_Tp) = _Tp +# endif + +# ifdef __STL_MEMBER_TEMPLATE_KEYWORD +# define __STL_TEMPLATE template +# else +# define __STL_TEMPLATE +# endif + +# ifdef __STL_NEED_EXPLICIT +# define explicit +# endif + +# ifdef __STL_EXPLICIT_FUNCTION_TMPL_ARGS +# define __STL_NULL_TMPL_ARGS <> +# else +# define __STL_NULL_TMPL_ARGS +# endif + +# if defined(__STL_CLASS_PARTIAL_SPECIALIZATION) \ + || defined (__STL_PARTIAL_SPECIALIZATION_SYNTAX) +# define __STL_TEMPLATE_NULL template<> +# else +# define __STL_TEMPLATE_NULL +# endif + +// Use standard-conforming allocators if we have the necessary language +// features. __STL_USE_SGI_ALLOCATORS is a hook so that users can +// disable new-style allocators, and continue to use the same kind of +// allocators as before, without having to edit library headers. +# if defined(__STL_CLASS_PARTIAL_SPECIALIZATION) && \ + defined(__STL_MEMBER_TEMPLATES) && \ + defined(__STL_MEMBER_TEMPLATE_CLASSES) && \ + !defined(__STL_NO_BOOL) && \ + !defined(__STL_NON_TYPE_TMPL_PARAM_BUG) && \ + !defined(__STL_LIMITED_DEFAULT_TEMPLATES) && \ + !defined(__STL_USE_SGI_ALLOCATORS) +# define __STL_USE_STD_ALLOCATORS +# endif + +# ifndef __STL_DEFAULT_ALLOCATOR +# ifdef __STL_USE_STD_ALLOCATORS +# define __STL_DEFAULT_ALLOCATOR(T) allocator< T > +# else +# define __STL_DEFAULT_ALLOCATOR(T) alloc +# endif +# endif + +// __STL_NO_NAMESPACES is a hook so that users can disable namespaces +// without having to edit library headers. __STL_NO_RELOPS_NAMESPACE is +// a hook so that users can disable the std::rel_ops namespace, keeping +// the relational operator template in namespace std, without having to +// edit library headers. +# if defined(__STL_HAS_NAMESPACES) && !defined(__STL_NO_NAMESPACES) +# define __STL_USE_NAMESPACES +# define __STD std +# define __STL_BEGIN_NAMESPACE namespace std { +# define __STL_END_NAMESPACE } +# if defined(__STL_FUNCTION_TMPL_PARTIAL_ORDER) && \ + !defined(__STL_NO_RELOPS_NAMESPACE) +# define __STL_USE_NAMESPACE_FOR_RELOPS +# define __STL_BEGIN_RELOPS_NAMESPACE namespace std { namespace rel_ops { +# define __STL_END_RELOPS_NAMESPACE } } +# define __STD_RELOPS std::rel_ops +# else /* Use std::rel_ops namespace */ +# define __STL_USE_NAMESPACE_FOR_RELOPS +# define __STL_BEGIN_RELOPS_NAMESPACE namespace std { +# define __STL_END_RELOPS_NAMESPACE } +# define __STD_RELOPS std +# endif /* Use std::rel_ops namespace */ +# else +# define __STD +# define __STL_BEGIN_NAMESPACE +# define __STL_END_NAMESPACE +# undef __STL_USE_NAMESPACE_FOR_RELOPS +# define __STL_BEGIN_RELOPS_NAMESPACE +# define __STL_END_RELOPS_NAMESPACE +# define __STD_RELOPS +# undef __STL_USE_NAMESPACES +# endif + +// Some versions of the EDG front end sometimes require an explicit +// namespace spec where they shouldn't. This macro facilitates that. +// If the bug becomes irrelevant, then all uses of __STD_QUALIFIER +// should be removed. The 7.3 beta SGI compiler has this bug, but the +// MR version is not expected to have it. + +# if defined(__STL_USE_NAMESPACES) && !defined(__STD_QUALIFIER) +# define __STD_QUALIFIER std:: +# else +# define __STD_QUALIFIER +# endif + +# ifdef __STL_USE_EXCEPTIONS +# define __STL_TRY try +# define __STL_CATCH_ALL catch(...) +# define __STL_THROW(x) throw x +# define __STL_RETHROW throw +# define __STL_NOTHROW throw() +# define __STL_UNWIND(action) catch(...) { action; throw; } +# else +# define __STL_TRY +# define __STL_CATCH_ALL if (false) +# define __STL_THROW(x) +# define __STL_RETHROW +# define __STL_NOTHROW +# define __STL_UNWIND(action) +# endif + +#ifdef __STL_ASSERTIONS +# include <stdio.h> +# define __stl_assert(expr) \ + if (!(expr)) { fprintf(stderr, "%s:%d STL assertion failure: %s\n", \ + __FILE__, __LINE__, # expr); abort(); } +#else +# define __stl_assert(expr) +#endif + +#if defined(__STL_WIN32THREADS) || defined(__STL_SGI_THREADS) \ + || defined(__STL_PTHREADS) || defined(__STL_UITHREADS) +# define __STL_THREADS +# define __STL_VOLATILE volatile +#else +# define __STL_VOLATILE +#endif + +#if defined(__STL_CLASS_PARTIAL_SPECIALIZATION) \ + && defined(__STL_MEMBER_TEMPLATES) \ + && !defined(_STL_NO_CONCEPT_CHECKS) +# define __STL_USE_CONCEPT_CHECKS +#endif + + +#endif /* __STL_CONFIG_H */ + +// Local Variables: +// mode:C++ +// End: diff --git a/WebCore/platform/android/stl/stl_heap.h b/WebCore/platform/android/stl/stl_heap.h new file mode 100644 index 0000000..651d21a --- /dev/null +++ b/WebCore/platform/android/stl/stl_heap.h @@ -0,0 +1,297 @@ +/* + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * Copyright (c) 1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +/* NOTE: This is an internal header file, included by other STL headers. + * You should not attempt to use it directly. + */ + +#ifndef __SGI_STL_INTERNAL_HEAP_H +#define __SGI_STL_INTERNAL_HEAP_H + +__STL_BEGIN_NAMESPACE + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +#pragma set woff 1209 +#endif + +// Heap-manipulation functions: push_heap, pop_heap, make_heap, sort_heap. + +template <class _RandomAccessIterator, class _Distance, class _Tp> +void +__push_heap(_RandomAccessIterator __first, + _Distance __holeIndex, _Distance __topIndex, _Tp __value) +{ + _Distance __parent = (__holeIndex - 1) / 2; + while (__holeIndex > __topIndex && *(__first + __parent) < __value) { + *(__first + __holeIndex) = *(__first + __parent); + __holeIndex = __parent; + __parent = (__holeIndex - 1) / 2; + } + *(__first + __holeIndex) = __value; +} + +template <class _RandomAccessIterator, class _Distance, class _Tp> +inline void +__push_heap_aux(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Distance*, _Tp*) +{ + __push_heap(__first, _Distance((__last - __first) - 1), _Distance(0), + _Tp(*(__last - 1))); +} + +template <class _RandomAccessIterator> +inline void +push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type, + _LessThanComparable); + __push_heap_aux(__first, __last, + __DISTANCE_TYPE(__first), __VALUE_TYPE(__first)); +} + +template <class _RandomAccessIterator, class _Distance, class _Tp, + class _Compare> +void +__push_heap(_RandomAccessIterator __first, _Distance __holeIndex, + _Distance __topIndex, _Tp __value, _Compare __comp) +{ + _Distance __parent = (__holeIndex - 1) / 2; + while (__holeIndex > __topIndex && __comp(*(__first + __parent), __value)) { + *(__first + __holeIndex) = *(__first + __parent); + __holeIndex = __parent; + __parent = (__holeIndex - 1) / 2; + } + *(__first + __holeIndex) = __value; +} + +template <class _RandomAccessIterator, class _Compare, + class _Distance, class _Tp> +inline void +__push_heap_aux(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Compare __comp, + _Distance*, _Tp*) +{ + __push_heap(__first, _Distance((__last - __first) - 1), _Distance(0), + _Tp(*(__last - 1)), __comp); +} + +template <class _RandomAccessIterator, class _Compare> +inline void +push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, + _Compare __comp) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + __push_heap_aux(__first, __last, __comp, + __DISTANCE_TYPE(__first), __VALUE_TYPE(__first)); +} + +template <class _RandomAccessIterator, class _Distance, class _Tp> +void +__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex, + _Distance __len, _Tp __value) +{ + _Distance __topIndex = __holeIndex; + _Distance __secondChild = 2 * __holeIndex + 2; + while (__secondChild < __len) { + if (*(__first + __secondChild) < *(__first + (__secondChild - 1))) + __secondChild--; + *(__first + __holeIndex) = *(__first + __secondChild); + __holeIndex = __secondChild; + __secondChild = 2 * (__secondChild + 1); + } + if (__secondChild == __len) { + *(__first + __holeIndex) = *(__first + (__secondChild - 1)); + __holeIndex = __secondChild - 1; + } + __push_heap(__first, __holeIndex, __topIndex, __value); +} + +template <class _RandomAccessIterator, class _Tp, class _Distance> +inline void +__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, + _RandomAccessIterator __result, _Tp __value, _Distance*) +{ + *__result = *__first; + __adjust_heap(__first, _Distance(0), _Distance(__last - __first), __value); +} + +template <class _RandomAccessIterator, class _Tp> +inline void +__pop_heap_aux(_RandomAccessIterator __first, _RandomAccessIterator __last, + _Tp*) +{ + __pop_heap(__first, __last - 1, __last - 1, + _Tp(*(__last - 1)), __DISTANCE_TYPE(__first)); +} + +template <class _RandomAccessIterator> +inline void pop_heap(_RandomAccessIterator __first, + _RandomAccessIterator __last) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type, + _LessThanComparable); + __pop_heap_aux(__first, __last, __VALUE_TYPE(__first)); +} + +template <class _RandomAccessIterator, class _Distance, + class _Tp, class _Compare> +void +__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex, + _Distance __len, _Tp __value, _Compare __comp) +{ + _Distance __topIndex = __holeIndex; + _Distance __secondChild = 2 * __holeIndex + 2; + while (__secondChild < __len) { + if (__comp(*(__first + __secondChild), *(__first + (__secondChild - 1)))) + __secondChild--; + *(__first + __holeIndex) = *(__first + __secondChild); + __holeIndex = __secondChild; + __secondChild = 2 * (__secondChild + 1); + } + if (__secondChild == __len) { + *(__first + __holeIndex) = *(__first + (__secondChild - 1)); + __holeIndex = __secondChild - 1; + } + __push_heap(__first, __holeIndex, __topIndex, __value, __comp); +} + +template <class _RandomAccessIterator, class _Tp, class _Compare, + class _Distance> +inline void +__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, + _RandomAccessIterator __result, _Tp __value, _Compare __comp, + _Distance*) +{ + *__result = *__first; + __adjust_heap(__first, _Distance(0), _Distance(__last - __first), + __value, __comp); +} + +template <class _RandomAccessIterator, class _Tp, class _Compare> +inline void +__pop_heap_aux(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Tp*, _Compare __comp) +{ + __pop_heap(__first, __last - 1, __last - 1, _Tp(*(__last - 1)), __comp, + __DISTANCE_TYPE(__first)); +} + +template <class _RandomAccessIterator, class _Compare> +inline void +pop_heap(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Compare __comp) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + __pop_heap_aux(__first, __last, __VALUE_TYPE(__first), __comp); +} + +template <class _RandomAccessIterator, class _Tp, class _Distance> +void +__make_heap(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Tp*, _Distance*) +{ + if (__last - __first < 2) return; + _Distance __len = __last - __first; + _Distance __parent = (__len - 2)/2; + + while (true) { + __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent))); + if (__parent == 0) return; + __parent--; + } +} + +template <class _RandomAccessIterator> +inline void +make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type, + _LessThanComparable); + __make_heap(__first, __last, + __VALUE_TYPE(__first), __DISTANCE_TYPE(__first)); +} + +template <class _RandomAccessIterator, class _Compare, + class _Tp, class _Distance> +void +__make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, + _Compare __comp, _Tp*, _Distance*) +{ + if (__last - __first < 2) return; + _Distance __len = __last - __first; + _Distance __parent = (__len - 2)/2; + + while (true) { + __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)), + __comp); + if (__parent == 0) return; + __parent--; + } +} + +template <class _RandomAccessIterator, class _Compare> +inline void +make_heap(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Compare __comp) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + __make_heap(__first, __last, __comp, + __VALUE_TYPE(__first), __DISTANCE_TYPE(__first)); +} + +template <class _RandomAccessIterator> +void sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type, + _LessThanComparable); + while (__last - __first > 1) + pop_heap(__first, __last--); +} + +template <class _RandomAccessIterator, class _Compare> +void +sort_heap(_RandomAccessIterator __first, + _RandomAccessIterator __last, _Compare __comp) +{ + __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator); + while (__last - __first > 1) + pop_heap(__first, __last--, __comp); +} + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +#pragma reset woff 1209 +#endif + +__STL_END_NAMESPACE + +#endif /* __SGI_STL_INTERNAL_HEAP_H */ + +// Local Variables: +// mode:C++ +// End: diff --git a/WebCore/platform/android/stl/stl_iterator_base.h b/WebCore/platform/android/stl/stl_iterator_base.h new file mode 100644 index 0000000..d03332b --- /dev/null +++ b/WebCore/platform/android/stl/stl_iterator_base.h @@ -0,0 +1,273 @@ +/* + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1996-1998 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + */ + +/* NOTE: This is an internal header file, included by other STL headers. + * You should not attempt to use it directly. + */ + +#ifndef __SGI_STL_INTERNAL_ITERATOR_BASE_H +#define __SGI_STL_INTERNAL_ITERATOR_BASE_H + +// This file contains all of the general iterator-related utilities. +// The internal file stl_iterator.h contains predefined iterators, +// such as front_insert_iterator and istream_iterator. + +#include <concept_checks.h> + +struct input_iterator_tag {}; +struct output_iterator_tag {}; +struct forward_iterator_tag : public input_iterator_tag {}; +struct bidirectional_iterator_tag : public forward_iterator_tag {}; +struct random_access_iterator_tag : public bidirectional_iterator_tag {}; + +// The base classes input_iterator, output_iterator, forward_iterator, +// bidirectional_iterator, and random_access_iterator are not part of +// the C++ standard. (They have been replaced by struct iterator.) +// They are included for backward compatibility with the HP STL. + +template <class _Tp, class _Distance> struct input_iterator { + typedef input_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Tp* pointer; + typedef _Tp& reference; +}; + +struct output_iterator { + typedef output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; +}; + +template <class _Tp, class _Distance> struct forward_iterator { + typedef forward_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Tp* pointer; + typedef _Tp& reference; +}; + + +template <class _Tp, class _Distance> struct bidirectional_iterator { + typedef bidirectional_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Tp* pointer; + typedef _Tp& reference; +}; + +template <class _Tp, class _Distance> struct random_access_iterator { + typedef random_access_iterator_tag iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Tp* pointer; + typedef _Tp& reference; +}; + +template <class _Category, class _Tp, class _Distance = ptrdiff_t, + class _Pointer = _Tp*, class _Reference = _Tp&> +struct iterator { + typedef _Category iterator_category; + typedef _Tp value_type; + typedef _Distance difference_type; + typedef _Pointer pointer; + typedef _Reference reference; +}; + +template <class _Iterator> +struct iterator_traits { + typedef typename _Iterator::iterator_category iterator_category; + typedef typename _Iterator::value_type value_type; + typedef typename _Iterator::difference_type difference_type; + typedef typename _Iterator::pointer pointer; + typedef typename _Iterator::reference reference; +}; + +template <class _Tp> +struct iterator_traits<_Tp*> { + typedef random_access_iterator_tag iterator_category; + typedef _Tp value_type; + typedef ptrdiff_t difference_type; + typedef _Tp* pointer; + typedef _Tp& reference; +}; + +template <class _Tp> +struct iterator_traits<const _Tp*> { + typedef random_access_iterator_tag iterator_category; + typedef _Tp value_type; + typedef ptrdiff_t difference_type; + typedef const _Tp* pointer; + typedef const _Tp& reference; +}; + +// The overloaded functions iterator_category, distance_type, and +// value_type are not part of the C++ standard. (They have been +// replaced by struct iterator_traits.) They are included for +// backward compatibility with the HP STL. + +// We introduce internal names for these functions. + +template <class _Iter> +inline typename iterator_traits<_Iter>::iterator_category +__iterator_category(const _Iter&) +{ + typedef typename iterator_traits<_Iter>::iterator_category _Category; + return _Category(); +} + +template <class _Iter> +inline typename iterator_traits<_Iter>::difference_type* +__distance_type(const _Iter&) +{ + return static_cast<typename iterator_traits<_Iter>::difference_type*>(0); +} + +template <class _Iter> +inline typename iterator_traits<_Iter>::value_type* +__value_type(const _Iter&) +{ + return static_cast<typename iterator_traits<_Iter>::value_type*>(0); +} + +template <class _Iter> +inline typename iterator_traits<_Iter>::iterator_category +iterator_category(const _Iter& __i) { return __iterator_category(__i); } + + +template <class _Iter> +inline typename iterator_traits<_Iter>::difference_type* +distance_type(const _Iter& __i) { return __distance_type(__i); } + +template <class _Iter> +inline typename iterator_traits<_Iter>::value_type* +value_type(const _Iter& __i) { return __value_type(__i); } + +#define __ITERATOR_CATEGORY(__i) __iterator_category(__i) +#define __DISTANCE_TYPE(__i) __distance_type(__i) +#define __VALUE_TYPE(__i) __value_type(__i) + +template <class _InputIterator, class _Distance> +inline void __distance(_InputIterator __first, _InputIterator __last, + _Distance& __n, input_iterator_tag) +{ + while (__first != __last) { ++__first; ++__n; } +} + +template <class _RandomAccessIterator, class _Distance> +inline void __distance(_RandomAccessIterator __first, + _RandomAccessIterator __last, + _Distance& __n, random_access_iterator_tag) +{ + __STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator); + __n += __last - __first; +} + +template <class _InputIterator, class _Distance> +inline void distance(_InputIterator __first, + _InputIterator __last, _Distance& __n) +{ + __STL_REQUIRES(_InputIterator, _InputIterator); + __distance(__first, __last, __n, iterator_category(__first)); +} + +#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION + +template <class _InputIterator> +inline typename iterator_traits<_InputIterator>::difference_type +__distance(_InputIterator __first, _InputIterator __last, input_iterator_tag) +{ + typename iterator_traits<_InputIterator>::difference_type __n = 0; + while (__first != __last) { + ++__first; ++__n; + } + return __n; +} + +template <class _RandomAccessIterator> +inline typename iterator_traits<_RandomAccessIterator>::difference_type +__distance(_RandomAccessIterator __first, _RandomAccessIterator __last, + random_access_iterator_tag) { + __STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator); + return __last - __first; +} + +template <class _InputIterator> +inline typename iterator_traits<_InputIterator>::difference_type +distance(_InputIterator __first, _InputIterator __last) { + typedef typename iterator_traits<_InputIterator>::iterator_category + _Category; + __STL_REQUIRES(_InputIterator, _InputIterator); + return __distance(__first, __last, _Category()); +} + +#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ + +template <class _InputIter, class _Distance> +inline void __advance(_InputIter& __i, _Distance __n, input_iterator_tag) { + while (__n--) ++__i; +} + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +#pragma set woff 1183 +#endif + +template <class _BidirectionalIterator, class _Distance> +inline void __advance(_BidirectionalIterator& __i, _Distance __n, + bidirectional_iterator_tag) { + __STL_REQUIRES(_BidirectionalIterator, _BidirectionalIterator); + if (__n >= 0) + while (__n--) ++__i; + else + while (__n++) --__i; +} + +#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) +#pragma reset woff 1183 +#endif + +template <class _RandomAccessIterator, class _Distance> +inline void __advance(_RandomAccessIterator& __i, _Distance __n, + random_access_iterator_tag) { + __STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator); + __i += __n; +} + +template <class _InputIterator, class _Distance> +inline void advance(_InputIterator& __i, _Distance __n) { + __STL_REQUIRES(_InputIterator, _InputIterator); + __advance(__i, __n, iterator_category(__i)); +} + +#endif /* __SGI_STL_INTERNAL_ITERATOR_BASE_H */ + + + +// Local Variables: +// mode:C++ +// End: diff --git a/WebCore/platform/android/stl/strings.h b/WebCore/platform/android/stl/strings.h new file mode 100644 index 0000000..13d6d4e --- /dev/null +++ b/WebCore/platform/android/stl/strings.h @@ -0,0 +1,17 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + diff --git a/WebCore/platform/cf/KURLCFNet.cpp b/WebCore/platform/cf/KURLCFNet.cpp index f060b28..2130667 100644 --- a/WebCore/platform/cf/KURLCFNet.cpp +++ b/WebCore/platform/cf/KURLCFNet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,6 +27,7 @@ #include "KURL.h" #include <wtf/RetainPtr.h> +#include <wtf/Vector.h> #include <CoreFoundation/CFURL.h> using namespace std; @@ -35,46 +36,35 @@ namespace WebCore { KURL::KURL(CFURLRef url) { - if (!url) { - parse(0, 0); - return; - } - - CFIndex bytesLength = CFURLGetBytes(url, 0, 0); - Vector<char, 512> buffer(bytesLength + 6); // 5 for "file:", 1 for null character to end C string - char* bytes = &buffer[5]; - CFURLGetBytes(url, reinterpret_cast<UInt8*>(bytes), bytesLength); - bytes[bytesLength] = '\0'; - if (bytes[0] != '/') { - parse(bytes, 0); - return; - } - - buffer[0] = 'f'; - buffer[1] = 'i'; - buffer[2] = 'l'; - buffer[3] = 'e'; - buffer[4] = ':'; - - parse(buffer.data(), 0); + if (url) { + CFIndex bytesLength = CFURLGetBytes(url, 0, 0); + Vector<char, 2048> buffer(bytesLength + 6); // 6 for "file:", 1 for NUL terminator + char* bytes = &buffer[5]; + CFURLGetBytes(url, (UInt8*)bytes, bytesLength); + bytes[bytesLength] = '\0'; + if (bytes[0] == '/') { + buffer[0] = 'f'; + buffer[1] = 'i'; + buffer[2] = 'l'; + buffer[3] = 'e'; + buffer[4] = ':'; + parse(buffer.data(), 0); + } else + parse(bytes, 0); + } else + parse("", 0); } CFURLRef KURL::createCFURL() const { - // FIXME: What should this return for invalid URLs? - // Currently it throws away the high bytes of the characters in the string in that case, - // which is clearly wrong. - - Vector<char, 512> buffer; - copyToBuffer(buffer); - + const UInt8 *bytes = (const UInt8 *)urlString.latin1(); // NOTE: We use UTF-8 here since this encoding is used when computing strings when returning URL components // (e.g calls to NSURL -path). However, this function is not tolerant of illegal UTF-8 sequences, which // could either be a malformed string or bytes in a different encoding, like Shift-JIS, so we fall back // onto using ISO Latin-1 in those cases. - CFURLRef result = CFURLCreateAbsoluteURLWithBytes(0, reinterpret_cast<const UInt8*>(buffer.data()), buffer.size(), kCFStringEncodingUTF8, 0, true); + CFURLRef result = CFURLCreateAbsoluteURLWithBytes(0, bytes, urlString.length(), kCFStringEncodingUTF8, 0, true); if (!result) - result = CFURLCreateAbsoluteURLWithBytes(0, reinterpret_cast<const UInt8*>(buffer.data()), buffer.size(), kCFStringEncodingISOLatin1, 0, true); + result = CFURLCreateAbsoluteURLWithBytes(0, bytes, urlString.length(), kCFStringEncodingISOLatin1, 0, true); return result; } @@ -89,7 +79,9 @@ String KURL::fileSystemPath() const #else CFURLPathStyle pathStyle = kCFURLPOSIXPathStyle; #endif - return RetainPtr<CFStringRef>(AdoptCF, CFURLCopyFileSystemPath(cfURL.get(), pathStyle)).get(); + + RetainPtr<CFStringRef> path(AdoptCF, CFURLCopyFileSystemPath(cfURL.get(), pathStyle)); + return path.get(); } } diff --git a/WebCore/platform/graphics/AffineTransform.h b/WebCore/platform/graphics/AffineTransform.h index 2ba4ce7..c0dc72f 100644 --- a/WebCore/platform/graphics/AffineTransform.h +++ b/WebCore/platform/graphics/AffineTransform.h @@ -32,6 +32,8 @@ #include <QMatrix> #elif PLATFORM(CAIRO) #include <cairo.h> +#elif PLATFORM(SGL) +#include "SkMatrix.h" #elif PLATFORM(WX) && USE(WXGC) #include <wx/defs.h> #include <wx/graphics.h> @@ -111,6 +113,8 @@ public: operator QMatrix() const; #elif PLATFORM(CAIRO) operator cairo_matrix_t() const; +#elif PLATFORM(SGL) + operator SkMatrix() const; #elif PLATFORM(WX) && USE(WXGC) operator wxGraphicsMatrix() const; #endif @@ -127,6 +131,8 @@ private: QMatrix m_transform; #elif PLATFORM(CAIRO) cairo_matrix_t m_transform; +#elif PLATFORM(SGL) + SkMatrix m_transform; #elif PLATFORM(WX) && USE(WXGC) wxGraphicsMatrix m_transform; #endif diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 9ef1d6a..e731ed6 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -92,8 +92,10 @@ void BitmapImage::destroyDecodedData(bool incremental) if (!incremental) { // Reset the image source, since Image I/O has an underlying cache that it uses // while animating that it seems to never clear. +#if !PLATFORM(SGL) m_source.clear(); m_source.setData(m_data.get(), m_allDataReceived); +#endif } } } @@ -232,7 +234,7 @@ void BitmapImage::resetAnimation() m_repetitionsComplete = 0; m_animationFinished = false; int frameSize = m_size.width() * m_size.height() * 4; - + // For extremely large animations, when the animation is reset, we just throw everything away. if (frameCount() * frameSize > cLargeAnimationCutoff) destroyDecodedData(); diff --git a/WebCore/platform/graphics/BitmapImage.h b/WebCore/platform/graphics/BitmapImage.h index 0185027..154d829 100644 --- a/WebCore/platform/graphics/BitmapImage.h +++ b/WebCore/platform/graphics/BitmapImage.h @@ -44,6 +44,10 @@ class NSImage; typedef struct HBITMAP__ *HBITMAP; #endif +#if PLATFORM(SGL) + class SkBitmapRef; +#endif + namespace WebCore { struct FrameData; } @@ -127,6 +131,11 @@ public: virtual bool getHBITMAPOfSize(HBITMAP, LPSIZE); #endif +#if PLATFORM(SGL) + virtual SkBitmapRef* getBitmap(); + virtual void setURL(const String& str); +#endif + virtual NativeImagePtr nativeImageForCurrentFrame() { return frameAtIndex(currentFrame()); } private: @@ -184,6 +193,10 @@ private: mutable RetainPtr<CFDataRef> m_tiffRep; // Cached TIFF rep for frame 0. Only built lazily if someone queries for one. #endif +#if PLATFORM(SGL) + SkBitmapRef* m_bitmapRef; +#endif + Color m_solidColor; // If we're a 1x1 solid color, this is the color to use to fill. bool m_isSolidColor; // Whether or not we are a 1x1 solid image. diff --git a/WebCore/platform/graphics/Color.cpp b/WebCore/platform/graphics/Color.cpp index 52d2bd7..b0efff1 100644 --- a/WebCore/platform/graphics/Color.cpp +++ b/WebCore/platform/graphics/Color.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,6 +26,7 @@ #include "config.h" #include "Color.h" +#include "DeprecatedString.h" #include "PlatformString.h" #include <math.h> #include <wtf/Assertions.h> @@ -34,7 +35,6 @@ #include "ColorData.c" using namespace std; -using namespace WTF; namespace WebCore { @@ -92,26 +92,24 @@ RGBA32 makeRGBAFromHSLA(double hue, double saturation, double lightness, double // originally moved here from the CSS parser bool Color::parseHexColor(const String& name, RGBA32& rgb) { - unsigned length = name.length(); - if (length != 3 && length != 6) - return false; - unsigned value = 0; - for (unsigned i = 0; i < length; ++i) { - if (!isASCIIHexDigit(name[i])) - return false; - value <<= 4; - value |= toASCIIHexValue(name[i]); + int len = name.length(); + if (len == 3 || len == 6) { + bool ok; + int val = name.deprecatedString().toInt(&ok, 16); + if (ok) { + if (len == 6) { + rgb = 0xFF000000 | val; + return true; + } + // #abc converts to #aabbcc according to the specs + rgb = 0xFF000000 + | (val & 0xF00) << 12 | (val & 0xF00) << 8 + | (val & 0xF0) << 8 | (val & 0xF0) << 4 + | (val & 0xF) << 4 | (val & 0xF); + return true; + } } - if (length == 6) { - rgb = 0xFF000000 | value; - return true; - } - // #abc converts to #aabbcc - rgb = 0xFF000000 - | (value & 0xF00) << 12 | (value & 0xF00) << 8 - | (value & 0xF0) << 8 | (value & 0xF0) << 4 - | (value & 0xF) << 4 | (value & 0xF); - return true; + return false; } int differenceSquared(const Color& c1, const Color& c2) @@ -149,25 +147,10 @@ String Color::name() const return String::format("#%02X%02X%02X", red(), green(), blue()); } -static inline const NamedColor* findNamedColor(const String& name) -{ - char buffer[64]; // easily big enough for the longest color name - unsigned length = name.length(); - if (length > sizeof(buffer) - 1) - return 0; - for (unsigned i = 0; i < length; ++i) { - UChar c = name[i]; - if (!c || c > 0x7F) - return 0; - buffer[i] = toASCIILower(static_cast<char>(c)); - } - buffer[length] = '\0'; - return findColor(buffer, length); -} - void Color::setNamedColor(const String& name) { - const NamedColor* foundColor = findNamedColor(name); + DeprecatedString dname = name.deprecatedString(); + const NamedColor* foundColor = dname.isAllASCII() ? findColor(dname.latin1(), dname.length()) : 0; m_color = foundColor ? foundColor->RGBValue : 0; m_color |= 0xFF000000; m_valid = foundColor; diff --git a/WebCore/platform/graphics/Color.h b/WebCore/platform/graphics/Color.h index 5706c6f..7bbca18 100644 --- a/WebCore/platform/graphics/Color.h +++ b/WebCore/platform/graphics/Color.h @@ -118,6 +118,10 @@ public: static const RGBA32 lightGray = 0xFFC0C0C0; static const RGBA32 transparent = 0x00000000; +#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR + static const RGBA32 tap = 0x4D1A1A1A; +#endif + private: RGBA32 m_color; bool m_valid : 1; diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp index 05704d3..82bf3b1 100644 --- a/WebCore/platform/graphics/Font.cpp +++ b/WebCore/platform/graphics/Font.cpp @@ -63,7 +63,12 @@ Font::CodePath Font::codePath = Auto; struct WidthIterator { WidthIterator(const Font* font, const TextRun& run); - void advance(int to, GlyphBuffer* glyphBuffer = 0); +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + bool +#else + void +#endif + advance(int to, GlyphBuffer* glyphBuffer = 0); bool advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer = 0); const Font* m_font; @@ -107,7 +112,13 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run) } } +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS +#define SIGNAL_ADJUSTED_WIDTHS() adjustedWidths = true +bool WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) +#else +#define SIGNAL_ADJUSTED_WIDTHS() void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) +#endif { if (offset > m_end) offset = m_end; @@ -121,6 +132,10 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) float runWidthSoFar = m_runWidthSoFar; float lastRoundingWidth = m_finalRoundingWidth; +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + bool adjustedWidths = false; +#endif + while (currentCharacter < offset) { UChar32 c = *cp; unsigned clusterLength = 1; @@ -162,20 +177,27 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) if (c == '\t' && m_run.allowTabs()) { float tabWidth = m_font->tabWidth(); width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth); + SIGNAL_ADJUSTED_WIDTHS(); } else { width = fontData->widthForGlyph(glyph); +#ifndef ANDROID_NEVER_ROUND_FONT_METRICS // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all characters that // match the width of the space character have the same width as the space character. - if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) + if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding()) { width = fontData->m_adjustedSpaceWidth; + SIGNAL_ADJUSTED_WIDTHS(); + } +#endif } if (hasExtraSpacing && !m_run.spacingDisabled()) { // Account for letter-spacing. - if (width && m_font->letterSpacing()) + if (width && m_font->letterSpacing()) { width += m_font->letterSpacing(); + SIGNAL_ADJUSTED_WIDTHS(); + } if (Font::treatAsSpace(c)) { // Account for padding. WebCore uses space padding to justify text. @@ -189,12 +211,15 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) width += m_padPerSpace; m_padding -= m_padPerSpace; } + SIGNAL_ADJUSTED_WIDTHS(); } // Account for word spacing. // We apply additional space between "words" by adding width to the space character. - if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) + if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) { width += m_font->wordSpacing(); + SIGNAL_ADJUSTED_WIDTHS(); + } } } @@ -209,10 +234,13 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) float oldWidth = width; +#ifndef ANDROID_NEVER_ROUND_FONT_METRICS // Force characters that are used to determine word boundaries for the rounding hack // to be integer width, so following words will start on an integer boundary. - if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) + if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c)) { width = ceilf(width); + SIGNAL_ADJUSTED_WIDTHS(); + } // Check to see if the next character is a "rounding hack character", if so, adjust // width so that the total run width will be on an integer boundary. @@ -220,7 +248,9 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) || (m_run.applyRunRounding() && currentCharacter >= m_end)) { float totalWidth = runWidthSoFar + width; width += ceilf(totalWidth) - totalWidth; + SIGNAL_ADJUSTED_WIDTHS(); } +#endif runWidthSoFar += width; @@ -233,6 +263,10 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) m_currentCharacter = currentCharacter; m_runWidthSoFar = runWidthSoFar; m_finalRoundingWidth = lastRoundingWidth; + +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + return adjustedWidths; +#endif } bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer) @@ -621,8 +655,11 @@ void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const Fl WidthIterator it(this, run); it.advance(from); float beforeWidth = it.m_runWidthSoFar; +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + bool adjustedWidths = +#endif it.advance(to, &glyphBuffer); - + // We couldn't generate any glyphs for the run. Give up. if (glyphBuffer.isEmpty()) return; @@ -633,6 +670,9 @@ void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const Fl float finalRoundingWidth = it.m_finalRoundingWidth; it.advance(run.length()); startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth; +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + adjustedWidths = true; // give up on simple/fast case +#endif } else startX += beforeWidth; @@ -641,6 +681,11 @@ void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const Fl for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) glyphBuffer.swap(i, end); +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + // mark the GlyphBuffer as having adjusted widths or not + // used by drawGlyph as an optimization hint + glyphBuffer.setHasAdjustedWidths(adjustedWidths); +#endif // Calculate the starting point of the glyphs to be displayed by adding // all the advances up to the first glyph. FloatPoint startPoint(startX, point.y()); diff --git a/WebCore/platform/graphics/Font.h b/WebCore/platform/graphics/Font.h index 0ea04eb..84048ed 100644 --- a/WebCore/platform/graphics/Font.h +++ b/WebCore/platform/graphics/Font.h @@ -266,7 +266,8 @@ public: FontSelector* fontSelector() const; #endif static bool treatAsSpace(UChar c) { return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0; } - static bool treatAsZeroWidthSpace(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == 0x200e || c == 0x200f || c >= 0x202a && c <= 0x202e; } +// ANDROID: extra parentheses around expressions to suppress warnings + static bool treatAsZeroWidthSpace(UChar c) { return c < 0x20 || (c >= 0x7F && c < 0xA0) || c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e); } private: FontDescription m_fontDescription; #if !PLATFORM(QT) diff --git a/WebCore/platform/graphics/FontFallbackList.cpp b/WebCore/platform/graphics/FontFallbackList.cpp index 0fdd99c..049cf7f 100644 --- a/WebCore/platform/graphics/FontFallbackList.cpp +++ b/WebCore/platform/graphics/FontFallbackList.cpp @@ -36,11 +36,10 @@ namespace WebCore { FontFallbackList::FontFallbackList() - : RefCounted<FontFallbackList>(0) - , m_familyIndex(0) - , m_pitch(UnknownPitch) - , m_loadingCustomFonts(false) - , m_fontSelector(0) +: m_familyIndex(0) +, m_pitch(UnknownPitch) +, m_loadingCustomFonts(false) +, m_fontSelector(0) { } diff --git a/WebCore/platform/graphics/FontFamily.cpp b/WebCore/platform/graphics/FontFamily.cpp index c87c661..a185756 100644 --- a/WebCore/platform/graphics/FontFamily.cpp +++ b/WebCore/platform/graphics/FontFamily.cpp @@ -29,7 +29,7 @@ namespace WebCore { FontFamily::FontFamily(const FontFamily& other) - : RefCounted<FontFamily>(0) + : RefCounted<FontFamily>() , m_family(other.m_family) , m_next(other.m_next) { diff --git a/WebCore/platform/graphics/FontFamily.h b/WebCore/platform/graphics/FontFamily.h index 7a382cc..65a64b4 100644 --- a/WebCore/platform/graphics/FontFamily.h +++ b/WebCore/platform/graphics/FontFamily.h @@ -34,7 +34,7 @@ namespace WebCore { class FontFamily : public RefCounted<FontFamily> { public: - FontFamily() : RefCounted<FontFamily>(0) { } + FontFamily() { } FontFamily(const FontFamily&); FontFamily& operator=(const FontFamily&); diff --git a/WebCore/platform/graphics/FontSelector.h b/WebCore/platform/graphics/FontSelector.h index b0e9dfa..c5b3651 100644 --- a/WebCore/platform/graphics/FontSelector.h +++ b/WebCore/platform/graphics/FontSelector.h @@ -36,9 +36,8 @@ class FontDescription; class FontSelector : public RefCounted<FontSelector> { public: - FontSelector() : RefCounted<FontSelector>(0) { } - virtual ~FontSelector() { } - virtual FontData* getFontData(const FontDescription&, const AtomicString& familyName) = 0; + virtual ~FontSelector() {}; + virtual FontData* getFontData(const FontDescription& fontDescription, const AtomicString& familyName) = 0; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index f65996d..c8e308c 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -53,6 +53,9 @@ typedef FloatSize GlyphBufferAdvance; #elif PLATFORM(WX) typedef Glyph GlyphBufferGlyph; typedef FloatSize GlyphBufferAdvance; +#elif PLATFORM(SGL) +typedef Glyph GlyphBufferGlyph; +typedef FloatSize GlyphBufferAdvance; #elif PLATFORM(QT) typedef unsigned short GlyphBufferGlyph; typedef FloatSize GlyphBufferAdvance; @@ -60,6 +63,10 @@ typedef FloatSize GlyphBufferAdvance; class GlyphBuffer { public: +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + GlyphBuffer() : m_hasAdjustedWidths(true) {} +#endif + bool isEmpty() const { return m_fontData.isEmpty(); } int size() const { return m_fontData.size(); } @@ -103,7 +110,7 @@ public: Glyph glyphAt(int index) const { -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(WX) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(WX) || PLATFORM(SGL) return m_glyphs[index]; #elif PLATFORM(CAIRO) return m_glyphs[index].index; @@ -114,7 +121,7 @@ public: { #if PLATFORM(CG) return m_advances[index].width; -#elif PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX) +#elif PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX) || PLATFORM(SGL) return m_advances[index].width(); #endif } @@ -142,7 +149,7 @@ public: cairoGlyph.index = glyph; m_glyphs.append(cairoGlyph); m_advances.append(FloatSize(width, 0)); -#elif PLATFORM(QT) || PLATFORM(WX) +#elif PLATFORM(QT) || PLATFORM(WX) || PLATFORM(SGL) m_glyphs.append(glyph); m_advances.append(FloatSize(width, 0)); #endif @@ -154,6 +161,18 @@ public: #endif } +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + void setHasAdjustedWidths(bool adjustedWidths) { + m_hasAdjustedWidths = adjustedWidths; + } + /** Returns true in the general case, which means that one or more of the + glyphs may have a width or height that has been changed from the raw + value returned by the font. If this returns false, then the drawing + code can use that as a hint if it means it can draw the run faster. + */ + bool hasAdjustedWidths() const { return m_hasAdjustedWidths; } +#endif + private: Vector<const SimpleFontData*, 2048> m_fontData; Vector<GlyphBufferGlyph, 2048> m_glyphs; @@ -161,6 +180,12 @@ private: #if PLATFORM(WIN) Vector<FloatSize, 2048> m_offsets; #endif +#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS + // defaults to true for general case. Set to false sometimes in + // drawSimpleText as a hint to drawGlphs that the widths are exactly those + // from the font (i.e. no tweaks for rounding or CSS styling + bool m_hasAdjustedWidths; +#endif }; } diff --git a/WebCore/platform/graphics/GlyphPageTreeNode.h b/WebCore/platform/graphics/GlyphPageTreeNode.h index 40b8154..2619888 100644 --- a/WebCore/platform/graphics/GlyphPageTreeNode.h +++ b/WebCore/platform/graphics/GlyphPageTreeNode.h @@ -58,14 +58,12 @@ struct GlyphData { // to be overriding the parent's node, but provide no additional information. struct GlyphPage : public RefCounted<GlyphPage> { GlyphPage() - : RefCounted<GlyphPage>(0) - , m_owner(0) + : m_owner(0) { } GlyphPage(GlyphPageTreeNode* owner) - : RefCounted<GlyphPage>(0) - , m_owner(owner) + : m_owner(owner) { } diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index 732c8e3..f75c287 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -34,6 +34,10 @@ #include <wtf/Noncopyable.h> #include <wtf/Platform.h> +#ifdef ANDROID_CANVAS_IMPL + #include "PlatformGraphics.h" +#endif + #if PLATFORM(CG) typedef struct CGContext PlatformGraphicsContext; #elif PLATFORM(CAIRO) @@ -41,6 +45,10 @@ typedef struct _cairo PlatformGraphicsContext; #elif PLATFORM(QT) class QPainter; typedef QPainter PlatformGraphicsContext; +#elif PLATFORM(SGL) +namespace WebCore { +class PlatformGraphicsContext; +} #elif PLATFORM(WX) class wxGCDC; class wxWindowDC; @@ -83,7 +91,6 @@ namespace WebCore { class Font; class GraphicsContextPrivate; class GraphicsContextPlatformPrivate; - class ImageBuffer; class KURL; class Path; class TextRun; @@ -130,6 +137,84 @@ namespace WebCore { void drawEllipse(const IntRect&); void drawConvexPolygon(size_t numPoints, const FloatPoint*, bool shouldAntialias = false); +#ifdef ANDROID_CANVAS_IMPL + /** Fill the specified path using the optional gradient or pattern, using the following + precedence. If/when gradients/patterns are added to the graphics context, these + parameters can go away + 1) use gradient if gradient != null + 2) use pattern if pattern != null + 3) use color in the graphics context + */ + void fillPath(const Path&, PlatformGradient*, PlatformPattern*); + /** Stroke the specified path using the optional gradient or pattern, using the following + precedence. If/when gradients/patterns are added to the graphics context, these + parameters can go away + 1) use gradient if gradient != null + 2) use pattern if pattern != null + 3) use color in the graphics context + */ + void strokePath(const Path&, PlatformGradient*, PlatformPattern*); + /** Fill the specified rect using the optional gradient or pattern, using the following + precedence. If/when gradients/patterns are added to the graphics context, these + parameters can go away + 1) use gradient if gradient != null + 2) use pattern if pattern != null + 3) use color in the graphics context + */ + void fillRect(const FloatRect&, PlatformGradient*, PlatformPattern*); + /** Stroke the specified rect using the optional gradient or pattern, using the following + precedence. If/when gradients/patterns are added to the graphics context, these + parameters can go away + 1) use gradient if gradient != null + 2) use pattern if pattern != null + 3) use color in the graphics context + */ + void strokeRect(const FloatRect&, float lineWidth, PlatformGradient*, PlatformPattern*); + + /** Return a platform specific linear-gradient. Use freePlatformGradient() when you are + done with it. + stopData is { stop, red, green, blue, alpha } per entry + */ + static PlatformGradient* newPlatformLinearGradient(const FloatPoint& p0, const FloatPoint& p1, + const float stopData[5], int count); + + /** Return a platform specific radial-gradient. Use freePlatformGradient() when you are + done with it. + stopData is { stop, red, green, blue, alpha } per entry + */ + static PlatformGradient* newPlatformRadialGradient(const FloatPoint& p0, float r0, + const FloatPoint& p1, float r1, + const float stopData[5], int count); + static void freePlatformGradient(PlatformGradient*); + + /** Return a platform specific pattern. Use freePlatformPattern() when you are + done with it. + */ + static PlatformPattern* newPlatformPattern(Image* image, + Image::TileRule hRule, Image::TileRule vRule); + static void freePlatformPattern(PlatformPattern*); + + /** platform-specific factory method to return a bitmap graphicscontext, + called by <canvas> when we need to draw offscreen. Caller is responsible for + deleting the context. Use drawOffscreenContext() to draw the context's image + onto another graphics context. + */ + static GraphicsContext* createOffscreenContext(int width, int height); + /** Called with a context returned by createOffscreenContext. Draw the underlying + bitmap to the current context. Similar to drawImage(), but this hides how + to extract the bitmap from ctx from the portable code. + If srcRect is NULL, it is assumed that we want to draw the entire bitmap represented + by the GraphicsContext. + */ + void drawOffscreenContext(GraphicsContext* ctx, const WebCore::FloatRect* srcRect, + const WebCore::FloatRect& dstRect); + + /** Return the clip bounds in local coordinates. It can be an approximation, as long as + the returned bounds completely enclose the actual clip. + */ + FloatRect getClipLocalBounds() const; +#endif + // Arc drawing (used by border-radius in CSS) just supports stroking at the moment. void strokeArc(const IntRect&, int startAngle, int angleSpan); @@ -145,14 +230,11 @@ namespace WebCore { void drawImage(Image*, const IntRect& destRect, const IntRect& srcRect, CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false); void drawImage(Image*, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false); - void drawImage(ImageBuffer*, const FloatRect& srcRect, const FloatRect& destRect); void drawTiledImage(Image*, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator = CompositeSourceOver); void drawTiledImage(Image*, const IntRect& destRect, const IntRect& srcRect, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile, CompositeOperator = CompositeSourceOver); - - void paintBuffer(ImageBuffer*, const IntRect&); #if PLATFORM(CG) void setUseLowQualityImageInterpolation(bool = true); bool useLowQualityImageInterpolation() const; diff --git a/WebCore/platform/graphics/Image.h b/WebCore/platform/graphics/Image.h index 6ffe163..4ef5bf7 100644 --- a/WebCore/platform/graphics/Image.h +++ b/WebCore/platform/graphics/Image.h @@ -54,6 +54,10 @@ typedef struct HBITMAP__ *HBITMAP; #include <QPixmap> #endif +#if PLATFORM(SGL) +class SkBitmapRef; +#endif + namespace WebCore { class AffineTransform; @@ -133,6 +137,11 @@ public: virtual bool getHBITMAPOfSize(HBITMAP, LPSIZE) { return false; } #endif +#if PLATFORM(SGL) + virtual SkBitmapRef* getBitmap() { return 0; } + virtual void setURL(const String& str) {} +#endif + protected: static void fillWithSolidColor(GraphicsContext* ctxt, const FloatRect& dstRect, const Color& color, CompositeOperator op); diff --git a/WebCore/platform/graphics/ImageBuffer.h b/WebCore/platform/graphics/ImageBuffer.h index 2ac2b4e..c815c8d 100644 --- a/WebCore/platform/graphics/ImageBuffer.h +++ b/WebCore/platform/graphics/ImageBuffer.h @@ -29,7 +29,6 @@ #include "IntSize.h" #include <wtf/OwnPtr.h> -#include <wtf/PassRefPtr.h> #include <memory> #if PLATFORM(CG) @@ -41,6 +40,10 @@ typedef struct CGImage* CGImageRef; class QPainter; #endif +#if PLATFORM(SGL) +#include <memory> +#endif + #if PLATFORM(CAIRO) typedef struct _cairo_surface cairo_surface_t; #endif @@ -48,9 +51,6 @@ typedef struct _cairo_surface cairo_surface_t; namespace WebCore { class GraphicsContext; - class ImageData; - class IntPoint; - class IntRect; class RenderObject; class ImageBuffer : Noncopyable { @@ -68,9 +68,6 @@ namespace WebCore { #elif PLATFORM(CAIRO) cairo_surface_t* surface() const; #endif - - PassRefPtr<ImageData> getImageData(const IntRect& rect) const; - void putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint); private: void* m_data; @@ -89,6 +86,9 @@ namespace WebCore { ImageBuffer(cairo_surface_t* surface); mutable cairo_surface_t* m_surface; #endif +#if PLATFORM(SGL) + ImageBuffer(const IntSize&, std::auto_ptr<GraphicsContext>); +#endif }; } diff --git a/WebCore/platform/graphics/ImageSource.h b/WebCore/platform/graphics/ImageSource.h index 8db5cd4..c99b00f 100644 --- a/WebCore/platform/graphics/ImageSource.h +++ b/WebCore/platform/graphics/ImageSource.h @@ -40,6 +40,10 @@ class QPixmap; #elif PLATFORM(CAIRO) struct _cairo_surface; typedef struct _cairo_surface cairo_surface_t; +#elif PLATFORM(SGL) +#include "SkString.h" +class SkBitmapRef; +class PrivateAndroidImageSourceRec; #endif namespace WebCore { @@ -59,6 +63,14 @@ typedef CGImageRef NativeImagePtr; class ImageDecoderQt; typedef ImageDecoderQt* NativeImageSourcePtr; typedef QPixmap* NativeImagePtr; +#elif PLATFORM(SGL) +class String; +struct NativeImageSourcePtr { + SkString m_url; + PrivateAndroidImageSourceRec* m_image; +}; +typedef const Vector<char>* NativeBytePtr; +typedef SkBitmapRef* NativeImagePtr; #else class ImageDecoder; typedef ImageDecoder* NativeImageSourcePtr; @@ -92,6 +104,10 @@ public: bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha. bool frameIsCompleteAtIndex(size_t); // Whether or not the frame is completely decoded. +#if PLATFORM(SGL) + void clearURL(); + void setURL(const String& url); +#endif private: NativeImageSourcePtr m_decoder; }; diff --git a/WebCore/platform/graphics/Path.h b/WebCore/platform/graphics/Path.h index 5150649..d63ba0b 100644 --- a/WebCore/platform/graphics/Path.h +++ b/WebCore/platform/graphics/Path.h @@ -32,6 +32,9 @@ typedef struct CGPath PlatformPath; #elif PLATFORM(QT) class QPainterPath; typedef QPainterPath PlatformPath; +#elif PLATFORM(SGL) +class SkPath; +typedef SkPath PlatformPath; #elif PLATFORM(WX) && USE(WXGC) class wxGraphicsPath; typedef wxGraphicsPath PlatformPath; diff --git a/WebCore/platform/graphics/SimpleFontData.h b/WebCore/platform/graphics/SimpleFontData.h index c444a6f..b2eec1f 100644 --- a/WebCore/platform/graphics/SimpleFontData.h +++ b/WebCore/platform/graphics/SimpleFontData.h @@ -1,7 +1,7 @@ /* * This file is part of the internal font implementation. * - * Copyright (C) 2006, 2008 Apple Computer, Inc. + * Copyright (C) 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -37,10 +37,6 @@ typedef struct OpaqueATSUStyle* ATSUStyle; #include <usp10.h> #endif -#if PLATFORM(CAIRO) -#include <cairo.h> -#endif - namespace WebCore { class FontDescription; @@ -110,7 +106,7 @@ public: static bool shouldApplyMacAscentHack(); #endif -#if PLATFORM(CAIRO) +#if PLATFORM(GTK) void setFont(cairo_t*) const; #endif @@ -124,12 +120,6 @@ private: void commonInit(); -#if PLATFORM(WIN) - void initGDIFont(); - void platformCommonDestroy(); - float widthForGDIGlyph(Glyph glyph) const; -#endif - public: int m_ascent; int m_descent; diff --git a/WebCore/platform/graphics/android/AffineTransformAndroid.cpp b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp new file mode 100644 index 0000000..d00aa40 --- /dev/null +++ b/WebCore/platform/graphics/android/AffineTransformAndroid.cpp @@ -0,0 +1,168 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "AffineTransform.h" + +#include "FloatRect.h" +#include "IntRect.h" + +#include "android_graphics.h" + +namespace WebCore { + +static const double deg2rad = 0.017453292519943295769; // pi/180 + +AffineTransform::AffineTransform() +{ + m_transform.reset(); +} + +AffineTransform::AffineTransform(double a, double b, double c, double d, double tx, double ty) +{ + m_transform.reset(); + + m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); + + m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(c)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); +} + +void AffineTransform::setMatrix(double a, double b, double c, double d, double tx, double ty) +{ + m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); + + m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); + m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(c)); + m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); +} + +void AffineTransform::map(double x, double y, double *x2, double *y2) const +{ + SkPoint src, dst; + src.set(SkDoubleToScalar(x), SkDoubleToScalar(y)); + m_transform.mapPoints(&dst, &src, 1); + + *x2 = SkScalarToDouble(dst.fX); + *y2 = SkScalarToDouble(dst.fY); +} + +IntRect AffineTransform::mapRect(const IntRect &rect) const +{ + SkRect src, dst; + SkIRect ir; + + android_setrect(&src, rect); + m_transform.mapRect(&dst, src); + dst.round(&ir); + + return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); +} + +FloatRect AffineTransform::mapRect(const FloatRect &rect) const +{ + SkRect src, dst; + SkIRect ir; + + android_setrect(&src, rect); + m_transform.mapRect(&dst, src); + dst.round(&ir); + + return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); +} + +bool AffineTransform::isIdentity() const +{ + return m_transform.isIdentity(); +} + +void AffineTransform::reset() +{ + m_transform.reset(); +} + +AffineTransform &AffineTransform::scale(double sx, double sy) +{ + m_transform.preScale(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); + return *this; +} + +AffineTransform &AffineTransform::rotate(double d) +{ + m_transform.preRotate(SkDoubleToScalar(d)); + return *this; +} + +AffineTransform &AffineTransform::translate(double tx, double ty) +{ + m_transform.preTranslate(SkDoubleToScalar(tx), SkDoubleToScalar(ty)); + return *this; +} + +AffineTransform &AffineTransform::shear(double sx, double sy) +{ + m_transform.preSkew(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); + return *this; +} + +double AffineTransform::det() const +{ + return SkScalarToDouble(m_transform[SkMatrix::kMScaleX]) * SkScalarToDouble(m_transform[SkMatrix::kMScaleY]) - + SkScalarToDouble(m_transform[SkMatrix::kMSkewX]) * SkScalarToDouble(m_transform[SkMatrix::kMSkewY]); +} + +AffineTransform AffineTransform::inverse() const +{ + AffineTransform inverse; + + m_transform.invert(&inverse.m_transform); + + return inverse; +} + +AffineTransform::operator SkMatrix() const +{ + return m_transform; +} + +bool AffineTransform::operator==(const AffineTransform &m2) const +{ + return m_transform == m2.m_transform; +} + +AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) +{ + // is this the correct order??? + m_transform.setConcat(m_transform, m2.m_transform); + return *this; +} + +AffineTransform AffineTransform::operator* (const AffineTransform &m2) +{ + AffineTransform cat; + + // is this the correct order??? + cat.m_transform.setConcat(m_transform, m2.m_transform); + return cat; +} + +} diff --git a/WebCore/platform/graphics/android/FontAndroid.cpp b/WebCore/platform/graphics/android/FontAndroid.cpp new file mode 100644 index 0000000..340d1c1 --- /dev/null +++ b/WebCore/platform/graphics/android/FontAndroid.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "Font.h" + +#include "FontData.h" +#include "FontFallbackList.h" +#include "GraphicsContext.h" +#include "GlyphBuffer.h" +#include "PlatformGraphicsContext.h" +#include "IntRect.h" + +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkTemplates.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +namespace WebCore { + +void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, int from, int numGlyphs, + const FloatPoint& point) const { + SkCanvas* canvas = gc->platformContext()->mCanvas; + SkPaint paint; + + font->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->fillColor().rgb()); + + SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert + + const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); + SkScalar x = SkFloatToScalar(point.x()); + SkScalar y = SkFloatToScalar(point.y()); + + if (glyphBuffer.hasAdjustedWidths()) { + const GlyphBufferAdvance* adv = glyphBuffer.advances(from); + SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); + SkPoint* pos = storage.get(); + + for (int i = 0; i < numGlyphs; i++) { + pos[i].set(x, y); + x += SkFloatToScalar(adv[i].width()); + y += SkFloatToScalar(adv[i].height()); + } + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } else { + canvas->drawText(glyphs, numGlyphs << 1, x, y, paint); + } +} + +FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int, int) const +{ + SkPaint paint; + SkScalar width, left; + SkPaint::FontMetrics metrics; + + primaryFont()->platformData().setupPaint(&paint); + + width = paint.measureText(run.characters(), run.length() << 1); + SkScalar spacing = paint.getFontMetrics(&metrics); + + return FloatRect(point.x(), + point.y() - floorf(SkScalarToFloat(-metrics.fAscent)), + roundf(SkScalarToFloat(width)), + roundf(SkScalarToFloat(spacing))); +} + +void Font::drawComplexText(GraphicsContext* gc, TextRun const& run, FloatPoint const& point, int, int) const +{ + SkCanvas* canvas = gc->platformContext()->mCanvas; + SkPaint paint; + + primaryFont()->platformData().setupPaint(&paint); + paint.setColor(gc->fillColor().rgb()); + +#if 0 + int n = run.to() - run.from(); +printf("------------- complex draw %d chars", n); + for (int i = 0; i < n; i++) + printf(" %04X", run.data(run.from())[i]); + printf("\n"); +#endif + + canvas->drawText(run.characters(), run.length() << 1, + SkFloatToScalar(point.x()), SkFloatToScalar(point.y()), + paint); +} + +float Font::floatWidthForComplexText(const TextRun& run) const +{ + SkPaint paint; + + primaryFont()->platformData().setupPaint(&paint); + +//printf("--------- complext measure %d chars\n", run.to() - run.from()); + + SkScalar width = paint.measureText(run.characters(), run.length() << 1); + return SkScalarToFloat(width); +} + +int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const +{ + SkPaint paint; + int count = run.length(); + SkAutoSTMalloc<64, SkScalar> storage(count); + SkScalar* widths = storage.get(); + + primaryFont()->platformData().setupPaint(&paint); + + count = paint.getTextWidths(run.characters(), count << 1, widths); + + if (count > 0) + { + SkScalar pos = 0; + for (int i = 0; i < count; i++) + { + if (x < SkScalarRound(pos + SkScalarHalf(widths[i]))) + return i; + pos += widths[i]; + } + } + return count; +} + +} diff --git a/WebCore/platform/graphics/android/FontCacheAndroid.cpp b/WebCore/platform/graphics/android/FontCacheAndroid.cpp new file mode 100644 index 0000000..903159e --- /dev/null +++ b/WebCore/platform/graphics/android/FontCacheAndroid.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006 Apple Computer, 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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 "FontCache.h" +#include "FontPlatformData.h" +#include "Font.h" + +#include "SkPaint.h" +#include "SkTypeface.h" +#include "SkUtils.h" + +namespace WebCore { + +void FontCache::platformInit() +{ +} + +const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) +{ + return font.primaryFont(); // do I need to make a copy (i.e. does the caller delete what I return? + +#if 0 + // IMLangFontLink::MapFont Method does what we want. + IMLangFontLink2* langFontLink = getFontLinkInterface(); + if (!langFontLink) + return 0; + + FontData* fontData = 0; + HDC hdc = GetDC(0); + DWORD fontCodePages; + langFontLink->GetFontCodePages(hdc, font.primaryFont()->m_font.hfont(), &fontCodePages); + + DWORD actualCodePages; + long cchActual; + langFontLink->GetStrCodePages(characters, length, fontCodePages, &actualCodePages, &cchActual); + if (cchActual) { + HFONT result; + if (langFontLink->MapFont(hdc, actualCodePages, characters[0], &result) == S_OK) { + fontData = new FontData(FontPlatformData(result, font.fontDescription().computedPixelSize())); + fontData->setIsMLangFont(); + } + } + + ReleaseDC(0, hdc); + return fontData; +#endif +} + +FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) +{ + return 0; +} + +FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& font) +{ + static AtomicString str("sans-serif"); + return getCachedFontPlatformData(font, str); +} + +static char* AtomicStringToUTF8String(const AtomicString& utf16) +{ + SkASSERT(sizeof(uint16_t) == sizeof(utf16.characters()[0])); + const uint16_t* uni = (uint16_t*)utf16.characters(); + + size_t bytes = SkUTF16_ToUTF8(uni, utf16.length(), NULL); + char* utf8 = (char*)sk_malloc_throw(bytes + 1); + + (void)SkUTF16_ToUTF8(uni, utf16.length(), utf8); + utf8[bytes] = 0; + return utf8; +} + +bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family) +{ + ASSERT(0); // FIXME HACK unimplemented + return false; +} + +FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) +{ + char* storage = 0; + const char* name = 0; + + if (family.length() == 0) { + static const struct { + FontDescription::GenericFamilyType mType; + const char* mName; + } gNames[] = { + { FontDescription::SerifFamily, "serif" }, + { FontDescription::SansSerifFamily, "sans-serif" }, + { FontDescription::MonospaceFamily, "monospace" }, + { FontDescription::CursiveFamily, "cursive" }, + { FontDescription::FantasyFamily, "fantasy" } + }; + + FontDescription::GenericFamilyType type = fontDescription.genericFamily(); + for (unsigned i = 0; i < SK_ARRAY_COUNT(gNames); i++) + { + if (type == gNames[i].mType) + { + name = gNames[i].mName; + break; + } + } + // if we fall out of the loop, its ok for name to still be 0 + } + else { // convert the name to utf8 + storage = AtomicStringToUTF8String(family); + name = storage; + } + + int style = SkTypeface::kNormal; + if (fontDescription.weight() >= cBoldWeight) + style |= SkTypeface::kBold; + if (fontDescription.italic()) + style |= SkTypeface::kItalic; + + SkTypeface* tf = SkTypeface::Create(name, (SkTypeface::Style)style); + + FontPlatformData* result = new FontPlatformData(tf, + fontDescription.computedSize(), + (style & SkTypeface::kBold) && !tf->isBold(), + (style & SkTypeface::kItalic) && !tf->isItalic()); + tf->unref(); + sk_free(storage); + return result; +} + +} + diff --git a/WebCore/platform/graphics/android/FontCustomPlatformData.cpp b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp new file mode 100644 index 0000000..eea5e36 --- /dev/null +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "FontCustomPlatformData.h" + +#include "SkTypeface.h" +#include "SkStream.h" +#include "SharedBuffer.h" +#include "FontPlatformData.h" + +namespace WebCore { + +FontCustomPlatformData::FontCustomPlatformData(SkTypeface* face) +{ + face->safeRef(); + m_typeface = face; +} + +FontCustomPlatformData::~FontCustomPlatformData() +{ + m_typeface->safeUnref(); + // the unref is enough to release the font data... +} + +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic) +{ + // turn bold/italic into fakeBold/fakeItalic + if (m_typeface != NULL) { + if (m_typeface->isBold() == bold) + bold = false; + if (m_typeface->isItalic() == italic) + italic = false; + } + return FontPlatformData(m_typeface, size, bold, italic); +} + +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) +{ + // pass true until we know how we can share the data, and not have to + // make a copy of it. + SkStream* stream = new SkMemoryStream(buffer->data(), buffer->size(), true); + SkTypeface* face = SkTypeface::CreateFromStream(stream); + if (NULL == face) { + SkDebugf("--------- SkTypeface::CreateFromBuffer failed %d\n", + buffer->size()); + return NULL; + } + + SkAutoUnref aur(face); + + return new FontCustomPlatformData(face); +} + +} diff --git a/WebCore/platform/graphics/android/FontCustomPlatformData.h b/WebCore/platform/graphics/android/FontCustomPlatformData.h new file mode 100644 index 0000000..f072b6f --- /dev/null +++ b/WebCore/platform/graphics/android/FontCustomPlatformData.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FontCustomPlatformData_h_ +#define FontCustomPlatformData_h_ + +#include <wtf/Noncopyable.h> + +class SkTypeface; + +namespace WebCore { + + class SharedBuffer; + class FontPlatformData; + + class FontCustomPlatformData : Noncopyable { + public: + FontCustomPlatformData(SkTypeface* face); + ~FontCustomPlatformData(); + + SkTypeface* typeface() const { return m_typeface; } + + FontPlatformData fontPlatformData(int size, bool bold, bool italic); + + private: + SkTypeface* m_typeface; + }; + + FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); + +} // namespace WebCore + +#endif // FontCustomPlatformData_h_ + diff --git a/WebCore/platform/graphics/android/FontDataAndroid.cpp b/WebCore/platform/graphics/android/FontDataAndroid.cpp new file mode 100644 index 0000000..a90f536 --- /dev/null +++ b/WebCore/platform/graphics/android/FontDataAndroid.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2006 Apple Computer, 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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 "Font.h" +#include "FontCache.h" +#include "SimpleFontData.h" +#include "FloatRect.h" +#include "FontDescription.h" + +#include "SkPaint.h" +#include "SkTypeface.h" +#include "SkTime.h" + +namespace WebCore { + +void SimpleFontData::platformInit() +{ + SkPaint paint; + SkPaint::FontMetrics metrics; + + m_font.setupPaint(&paint); + (void)paint.getFontMetrics(&metrics); + + // use ceil instead of round to favor descent, given a lot of accidental + // clipping of descenders (e.g. 14pt 'g') in textedit fields + int d = SkScalarCeil(metrics.fDescent); + int s = SkScalarRound(metrics.fDescent - metrics.fAscent); + int a = s - d; + + m_ascent = a; + m_descent = d; + m_xHeight = SkScalarToFloat(-metrics.fAscent) * 0.56f; // hack I stole from the window's port + m_lineSpacing = a + d; + m_lineGap = SkScalarRound(metrics.fLeading); +} + +void SimpleFontData::platformDestroy() +{ + delete m_smallCapsFontData; +} + +SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const +{ + if (!m_smallCapsFontData) { + m_smallCapsFontData = new SimpleFontData(FontPlatformData(m_font, fontDescription.computedSize() * 0.7f)); + } + return m_smallCapsFontData; +} + +#define kMaxBufferCount 64 + +bool SimpleFontData::containsCharacters(const UChar* characters, int length) const +{ + SkPaint paint; + uint16_t glyphs[kMaxBufferCount]; + + m_font.setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + while (length > 0) { + int n = SkMin32(length, SK_ARRAY_COUNT(glyphs)); + + int count = paint.textToGlyphs(characters, n << 1, glyphs); + for (int i = 0; i < count; i++) { + if (0 == glyphs[i]) { + return false; // missing glyph + } + } + + characters += n; + length -= n; + } + return true; +} + +void SimpleFontData::determinePitch() +{ + m_treatAsFixedPitch = false; +} + +float SimpleFontData::platformWidthForGlyph(Glyph glyph) const +{ + SkASSERT(sizeof(glyph) == 2); // compile-time assert + + SkPaint paint; + + m_font.setupPaint(&paint); + + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + SkScalar width = paint.measureText(&glyph, 2); + + return SkScalarToFloat(width); +} + +} diff --git a/WebCore/platform/graphics/android/FontPlatformData.h b/WebCore/platform/graphics/android/FontPlatformData.h new file mode 100644 index 0000000..d6933d9 --- /dev/null +++ b/WebCore/platform/graphics/android/FontPlatformData.h @@ -0,0 +1,104 @@ +/* + * This file is part of the internal font implementation. It should not be included by anyone other than + * FontMac.cpp, FontWin.cpp and Font.cpp. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef FontPlatformData_H +#define FontPlatformData_H + +class SkPaint; +class SkTypeface; + +namespace WebCore { + +class FontPlatformData +{ +public: + static FontPlatformData Deleted() + { + return FontPlatformData(NULL, -1, false, false); + } + + FontPlatformData(); + FontPlatformData(const FontPlatformData&); + FontPlatformData(SkTypeface*, float textSize, bool fakeBold, bool fakeItalic); + FontPlatformData(const FontPlatformData& src, float textSize); + ~FontPlatformData(); + + FontPlatformData& operator=(const FontPlatformData&); + bool operator==(const FontPlatformData& a) const; + + void setupPaint(SkPaint*) const; + unsigned hash() const; + +private: + SkTypeface* mTypeface; + float mTextSize; + bool mFakeBold; + bool mFakeItalic; +}; + +#if 0 // windows port +class FontPlatformData +{ +public: + class Deleted {}; + + // Used for deleted values in the font cache's hash tables. + FontPlatformData(Deleted) + : m_font((HFONT)-1), m_fontFace(0), m_scaledFont(0), m_size(0) + {} + + FontPlatformData() + : m_font(0), m_fontFace(0), m_scaledFont(0), m_size(0) + {} + + FontPlatformData(HFONT, int size); + ~FontPlatformData(); + + HFONT hfont() const { return m_font; } + cairo_font_face_t* fontFace() const { return m_fontFace; } + cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } + + int size() const { return m_size; } + + unsigned hash() const + { + return StringImpl::computeHash((UChar*)(&m_font), sizeof(HFONT) / sizeof(UChar)); + } + + bool operator==(const FontPlatformData& other) const + { + return m_font == other.m_font && m_fontFace == other.m_fontFace && + m_scaledFont == other.m_scaledFont && m_size == other.m_size; + } + +private: + HFONT m_font; + cairo_font_face_t* m_fontFace; + cairo_scaled_font_t* m_scaledFont; + int m_size; +}; +#endif + +} + +#endif diff --git a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp new file mode 100644 index 0000000..4b4186a --- /dev/null +++ b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp @@ -0,0 +1,151 @@ +/* + * This file is part of the internal font implementation. It should not be included by anyone other than + * FontMac.cpp, FontWin.cpp and Font.cpp. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include "FontPlatformData.h" + +#include "SkPaint.h" +#include "SkTypeface.h" + +//#define TRACE_FONTPLATFORMDATA_LIFE +//#define COUNT_FONTPLATFORMDATA_LIFE + +#ifdef COUNT_FONTPLATFORMDATA_LIFE +static int gCount; +static int gMaxCount; + +static void inc_count() +{ + if (++gCount > gMaxCount) + { + gMaxCount = gCount; + SkDebugf("---------- FontPlatformData %d\n", gMaxCount); + } +} + +static void dec_count() { --gCount; } +#else + #define inc_count() + #define dec_count() +#endif + +#ifdef TRACE_FONTPLATFORMDATA_LIFE + #define trace(num) SkDebugf("FontPlatformData%d %p %g %d %d\n", num, mTypeface, mTextSize, mFakeBold, mFakeItalic) +#else + #define trace(num) +#endif + +namespace WebCore { + +FontPlatformData::FontPlatformData() + : mTypeface(NULL), mTextSize(0), mFakeBold(false), mFakeItalic(false) +{ + inc_count(); + trace(1); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src) +{ + src.mTypeface->safeRef(); + mTypeface = src.mTypeface; + + mTextSize = src.mTextSize; + mFakeBold = src.mFakeBold; + mFakeItalic = src.mFakeItalic; + + inc_count(); + trace(2); +} + +FontPlatformData::FontPlatformData(SkTypeface* tf, float textSize, bool fakeBold, bool fakeItalic) + : mTypeface(tf), mTextSize(textSize), mFakeBold(fakeBold), mFakeItalic(fakeItalic) +{ + mTypeface->safeRef(); + + inc_count(); + trace(3); +} + +FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) + : mTypeface(src.mTypeface), mTextSize(textSize), mFakeBold(src.mFakeBold), mFakeItalic(src.mFakeItalic) +{ + mTypeface->safeRef(); + + inc_count(); + trace(4); +} + +FontPlatformData::~FontPlatformData() +{ + dec_count(); +#ifdef TRACE_FONTPLATFORMDATA_LIFE + SkDebugf("----------- ~FontPlatformData\n"); +#endif + + mTypeface->safeUnref(); +} + +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) +{ + SkRefCnt_SafeAssign(mTypeface, src.mTypeface); + + mTextSize = src.mTextSize; + mFakeBold = src.mFakeBold; + mFakeItalic = src.mFakeItalic; + + return *this; +} + +void FontPlatformData::setupPaint(SkPaint* paint) const +{ + float ts = mTextSize; + if (!(ts > 0)) + ts = 12; + + paint->setAntiAlias(true); + paint->setSubpixelText(true); + paint->setTextSize(SkFloatToScalar(ts)); + paint->setTypeface(mTypeface); + paint->setFakeBoldText(mFakeBold); + paint->setTextSkewX(mFakeItalic ? -SK_Scalar1/4 : 0); + paint->setTextEncoding(SkPaint::kUTF16_TextEncoding); +} + +bool FontPlatformData::operator==(const FontPlatformData& a) const +{ + return SkTypeface::Equal(mTypeface, a.mTypeface) && + mTextSize == a.mTextSize && + mFakeBold == a.mFakeBold && + mFakeItalic == a.mFakeItalic; +} + +unsigned FontPlatformData::hash() const +{ + unsigned h = SkTypeface::UniqueID(mTypeface); + h ^= 0x01010101 * (((int)mFakeBold << 1) | (int)mFakeItalic); + h ^= *(uint32_t*)&mTextSize; + return h; +} + +} + diff --git a/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp b/WebCore/platform/graphics/android/GlyphMapAndroid.cpp index b679ced..9b77348 100644 --- a/WebCore/platform/graphics/win/GlyphPageTreeNodeCairoWin.cpp +++ b/WebCore/platform/graphics/android/GlyphMapAndroid.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,45 +28,42 @@ #include "config.h" #include "GlyphPageTreeNode.h" - #include "SimpleFontData.h" +#include "SkTemplates.h" +#include "SkPaint.h" +#include "SkUtils.h" + namespace WebCore { +#define NO_BREAK_SPACE_UNICHAR 0xA0 + bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { - // bufferLength will be greater than the requested number of glyphs if the buffer contains surrogate pairs. - // We won't support this for now. - if (bufferLength > length) + if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) { + SkDebugf("%s last char is high-surrogate", __FUNCTION__); + return false; + } + + SkPaint paint; + fontData->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); + + SkAutoSTMalloc <GlyphPage::size, uint16_t> glyphStorage(length); + uint16_t* glyphs = glyphStorage.get(); + unsigned count = paint.textToGlyphs(buffer, bufferLength << 1, glyphs); + if (count != length) { + SkDebugf("%s count != length\n", __FUNCTION__); return false; - - bool haveGlyphs = false; - - HDC dc = GetDC((HWND)0); - SaveDC(dc); - SelectObject(dc, fontData->platformData().hfont()); - - TEXTMETRIC tm; - GetTextMetrics(dc, &tm); - - WORD localGlyphBuffer[GlyphPage::size * 2]; - DWORD result = GetGlyphIndices(dc, buffer, bufferLength, localGlyphBuffer, 0); - bool success = result != GDI_ERROR && static_cast<unsigned>(result) == bufferLength; - if (success) { - for (unsigned i = 0; i < length; i++) { - Glyph glyph = localGlyphBuffer[i]; - if (!glyph) - setGlyphDataForIndex(offset + i, 0, 0); - else { - setGlyphDataForIndex(offset + i, glyph, fontData); - haveGlyphs = true; - } - } } - RestoreDC(dc, -1); - ReleaseDC(0, dc); - return haveGlyphs; + unsigned allGlyphs = 0; // track if any of the glyphIDs are non-zero + for (unsigned i = 0; i < length; i++) { + setGlyphDataForIndex(offset + i, glyphs[i], fontData); + allGlyphs |= glyphs[i]; + } + return allGlyphs != 0; } } + diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp new file mode 100644 index 0000000..23ac51d --- /dev/null +++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -0,0 +1,1114 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "Path.h" + +#include "SkBlurDrawLooper.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkDashPathEffect.h" +#include "SkDevice.h" +#include "SkPaint.h" +#include "SkPorterDuff.h" +#include "WebCoreViewBridge.h" +#include "PlatformGraphicsContext.h" +#include "AffineTransform.h" + +#include "android_graphics.h" +#include "SkGradientShader.h" +#include "SkBitmapRef.h" +#include "SkString.h" + +using namespace std; + +#define GC2Canvas(ctx) (ctx)->m_data->mPgc->mCanvas + +namespace WebCore { + +static int RoundToInt(float x) +{ + return (int)roundf(x); +} + +/* TODO / questions + + mAlpha: how does this interact with the alpha in Color? multiply them together? + mPorterDuffMode: do I always respect this? If so, then + the rgb() & 0xFF000000 check will abort drawing too often + Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor() +*/ + +class GraphicsContextPlatformPrivate { +public: + GraphicsContext* mCG; // back-ptr to our parent + PlatformGraphicsContext* mPgc; + + struct State { + float mMiterLimit; + float mAlpha; + SkDrawLooper* mLooper; + SkPaint::Cap mLineCap; + SkPaint::Join mLineJoin; + SkPorterDuff::Mode mPorterDuffMode; + int mDashRatio; //ratio of the length of a dash to its width + + State() + { + mMiterLimit = 4; + mAlpha = 1; + mLooper = NULL; + mLineCap = SkPaint::kDefault_Cap; + mLineJoin = SkPaint::kDefault_Join; + mPorterDuffMode = SkPorterDuff::kSrcOver_Mode; + mDashRatio = 3; + } + + State(const State& other) + { + other.mLooper->safeRef(); + memcpy(this, &other, sizeof(State)); + } + + ~State() + { + mLooper->safeUnref(); + } + + SkDrawLooper* setDrawLooper(SkDrawLooper* dl) + { + SkRefCnt_SafeAssign(mLooper, dl); + return dl; + } + + SkColor applyAlpha(SkColor c) const + { + int s = RoundToInt(mAlpha * 256); + if (s >= 256) + return c; + if (s < 0) + return 0; + + int a = SkAlphaMul(SkColorGetA(c), s); + return (c & 0x00FFFFFF) | (a << 24); + } + }; + + SkDeque mStateStack; + State* mState; + + GraphicsContextPlatformPrivate(GraphicsContext* cg, PlatformGraphicsContext* pgc) + : mCG(cg) + , mPgc(pgc), mStateStack(sizeof(State)) + + { + State* state = (State*)mStateStack.push_back(); + new (state) State(); + mState = state; + } + + ~GraphicsContextPlatformPrivate() + { + if (mPgc && mPgc->deleteUs()) + delete mPgc; + + // we force restores so we don't leak any subobjects owned by our + // stack of State records. + while (mStateStack.count() > 0) + this->restore(); + } + + void save() + { + State* newState = (State*)mStateStack.push_back(); + new (newState) State(*mState); + mState = newState; + } + + void restore() + { + mState->~State(); + mStateStack.pop_back(); + mState = (State*)mStateStack.back(); + } + + void setup_paint_common(SkPaint* paint) const + { +#ifdef SK_DEBUGx + { + SkPaint defaultPaint; + + SkASSERT(*paint == defaultPaint); + } +#endif + + paint->setAntiAlias(true); + paint->setDither(true); + paint->setPorterDuffXfermode(mState->mPorterDuffMode); + paint->setLooper(mState->mLooper); + } + + void setup_paint_fill(SkPaint* paint) const + { + this->setup_paint_common(paint); + + paint->setColor(mState->applyAlpha(mCG->fillColor().rgb())); + } + + /* sets up the paint for stroking. Returns true if the style is really + just a dash of squares (the size of the paint's stroke-width. + */ + bool setup_paint_stroke(SkPaint* paint, SkRect* rect) { + this->setup_paint_common(paint); + + float width = mCG->strokeThickness(); + + //this allows dashing and dotting to work properly for hairline strokes + if (0 == width) { + width = 1; + } + + paint->setColor(mState->applyAlpha(mCG->strokeColor().rgb())); + paint->setStyle(SkPaint::kStroke_Style); + paint->setStrokeWidth(SkFloatToScalar(width)); + paint->setStrokeCap(mState->mLineCap); + paint->setStrokeJoin(mState->mLineJoin); + paint->setStrokeMiter(SkFloatToScalar(mState->mMiterLimit)); + + if (rect != NULL && (RoundToInt(width) & 1)) { + rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); + } + + switch (mCG->strokeStyle()) { + case NoStroke: + case SolidStroke: + width = 0; + break; + case DashedStroke: + width = mState->mDashRatio * width; + break; + /* no break */ + case DottedStroke: + break; + } + + if (width > 0) { + SkScalar intervals[] = { width, width }; + SkPathEffect* pe = new SkDashPathEffect(intervals, 2, 0); + paint->setPathEffect(pe)->unref(); + // return true if we're basically a dotted dash of squares + return RoundToInt(width) == RoundToInt(paint->getStrokeWidth()); + } + return false; + } + +private: + // not supported yet + State& operator=(const State&); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// + +GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height) +{ + PlatformGraphicsContext* pgc = new PlatformGraphicsContext(); + + SkBitmap bitmap; + + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.allocPixels(); + bitmap.eraseColor(0); + pgc->mCanvas->setBitmapDevice(bitmap); + + GraphicsContext* ctx = new GraphicsContext(pgc); +//printf("-------- create offscreen <canvas> %p\n", ctx); + return ctx; +} + +void GraphicsContext::drawOffscreenContext(GraphicsContext* ctx, const FloatRect* srcRect, const FloatRect& dstRect) +{ + const SkBitmap& bm = GC2Canvas(ctx)->getDevice()->accessBitmap(false); + SkIRect src; + SkRect dst; + SkPaint paint; + + paint.setFilterBitmap(true); + + GC2Canvas(this)->drawBitmapRect(bm, + srcRect ? android_setrect(&src, *srcRect) : NULL, + *android_setrect(&dst, dstRect), + &paint); +} + +FloatRect GraphicsContext::getClipLocalBounds() const +{ + SkRect r; + + if (!GC2Canvas(this)->getClipBounds(&r)) + r.setEmpty(); + + return FloatRect(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), + SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +GraphicsContext::GraphicsContext(PlatformGraphicsContext *gc) + : m_common(createGraphicsContextPrivate()) + , m_data(new GraphicsContextPlatformPrivate(this, gc)) +{ + setPaintingDisabled(NULL == gc || NULL == gc->mCanvas); +} + +GraphicsContext::~GraphicsContext() +{ + delete m_data; + this->destroyGraphicsContextPrivate(m_common); +} + +void GraphicsContext::savePlatformState() +{ + // save our private State + m_data->save(); + // save our native canvas + GC2Canvas(this)->save(); +} + +void GraphicsContext::restorePlatformState() +{ + // restore our native canvas + GC2Canvas(this)->restore(); + // restore our private State + m_data->restore(); +} + +// Draws a filled rectangle with a stroked border. +void GraphicsContext::drawRect(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + + if (fillColor().alpha()) { + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawRect(r, paint); + } + + if (strokeStyle() != NoStroke && strokeColor().alpha()) { + paint.reset(); + m_data->setup_paint_stroke(&paint, &r); + GC2Canvas(this)->drawRect(r, paint); + } +} + +// This is only used to draw borders. +void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) +{ + if (paintingDisabled()) + return; + + StrokeStyle style = strokeStyle(); + if (style == NoStroke) + return; + + SkPaint paint; + SkCanvas* canvas = GC2Canvas(this); + const int idx = SkAbs32(point2.x() - point1.x()); + const int idy = SkAbs32(point2.y() - point1.y()); + + // special-case horizontal and vertical lines that are really just dots + if (m_data->setup_paint_stroke(&paint, NULL) && (0 == idx || 0 == idy)) { + const SkScalar diameter = paint.getStrokeWidth(); + const SkScalar radius = SkScalarHalf(diameter); + SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x())); + SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y())); + SkScalar dx, dy; + int count; + SkRect bounds; + + if (0 == idy) { // horizontal + bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius); + x += radius; + dx = diameter * 2; + dy = 0; + count = idx; + } else { // vertical + bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy)); + y += radius; + dx = 0; + dy = diameter * 2; + count = idy; + } + + // the actual count is the number of ONs we hit alternating + // ON(diameter), OFF(diameter), ... + { + SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter); + // now computer the number of cells (ON and OFF) + count = SkScalarRound(width); + // now compute the number of ONs + count = (count + 1) >> 1; + } + + SkAutoMalloc storage(count * sizeof(SkPoint)); + SkPoint* verts = (SkPoint*)storage.get(); + // now build the array of vertices to past to drawPoints + for (int i = 0; i < count; i++) { + verts[i].set(x, y); + x += dx; + y += dy; + } + + paint.setStyle(SkPaint::kFill_Style); + paint.setPathEffect(NULL); + + // clipping to bounds is not required for correctness, but it does + // allow us to reject the entire array of points if we are completely + // offscreen. This is common in a webpage for android, where most of + // the content is clipped out. If drawPoints took an (optional) bounds + // parameter, that might even be better, as we would *just* use it for + // culling, and not both wacking the canvas' save/restore stack. + canvas->save(SkCanvas::kClip_SaveFlag); + canvas->clipRect(bounds); + canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint); + canvas->restore(); + } else { + SkPoint pts[2]; + android_setpt(&pts[0], point1); + android_setpt(&pts[1], point2); + canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); + } +} + +static void setrect_for_underline(SkRect* r, GraphicsContext* context, const IntPoint& point, int yOffset, int width) +{ + float lineThickness = context->strokeThickness(); +// if (lineThickness < 1) // do we really need/want this? +// lineThickness = 1; + + yOffset += 1; // we add 1 to have underlines appear below the text + + r->fLeft = SkIntToScalar(point.x()); + r->fTop = SkIntToScalar(point.y() + yOffset); + r->fRight = r->fLeft + SkIntToScalar(width); + r->fBottom = r->fTop + SkFloatToScalar(lineThickness); +} + +void GraphicsContext::drawLineForText(IntPoint const& pt, int width, bool) +{ + if (paintingDisabled()) + return; + + SkRect r; + setrect_for_underline(&r, this, pt, 0, width); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(this->strokeColor().rgb()); + + GC2Canvas(this)->drawRect(r, paint); +} + +void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt, int width, bool grammar) +{ + if (paintingDisabled()) + return; + + SkRect r; + setrect_for_underline(&r, this, pt, 0, width); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(SK_ColorRED); // is this specified somewhere? + + GC2Canvas(this)->drawRect(r, paint); +} + +// This method is only used to draw the little circles used in lists. +void GraphicsContext::drawEllipse(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect oval; + + android_setrect(&oval, rect); + + if (fillColor().rgb() & 0xFF000000) { + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawOval(oval, paint); + } + if (strokeStyle() != NoStroke) { + paint.reset(); + m_data->setup_paint_stroke(&paint, &oval); + GC2Canvas(this)->drawOval(oval, paint); + } +} + +static inline int fast_mod(int value, int max) +{ + int sign = SkExtractSign(value); + + value = SkApplySign(value, sign); + if (value >= max) { + value %= max; + } + return SkApplySign(value, sign); +} + +void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) +{ + if (paintingDisabled()) + return; + + SkPath path; + SkPaint paint; + SkRect oval; + + android_setrect(&oval, r); + + if (strokeStyle() == NoStroke) { + m_data->setup_paint_fill(&paint); // we want the fill color + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness())); + } + else { + m_data->setup_paint_stroke(&paint, NULL); + } + + // we do this before converting to scalar, so we don't overflow SkFixed + startAngle = fast_mod(startAngle, 360); + angleSpan = fast_mod(angleSpan, 360); + + path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan)); + GC2Canvas(this)->drawPath(path, paint); +} + +void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias) +{ + if (paintingDisabled()) + return; + + if (numPoints <= 1) + return; + + SkPaint paint; + SkPath path; + + path.incReserve(numPoints); + path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y())); + for (size_t i = 1; i < numPoints; i++) + path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y())); + + if (fillColor().rgb() & 0xFF000000) { + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawPath(path, paint); + } + + if (strokeStyle() != NoStroke) { + paint.reset(); + m_data->setup_paint_stroke(&paint, NULL); + GC2Canvas(this)->drawPath(path, paint); + } +} + +#ifdef ANDROID_CANVAS_IMPL + +static void check_set_shader(SkPaint* paint, SkShader* s0, SkShader* s1) +{ + if (s0) { + paint->setShader(s0); + } + else if (s1) { + paint->setShader(s1); + } +} + +void GraphicsContext::fillPath(const Path& webCorePath, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + + m_data->setup_paint_fill(&paint); + check_set_shader(&paint, grad, pat); + + const SkPath& path = *webCorePath.platformPath(); + +#if 0 + SkDebugf("---- fillPath\n"); + SkPath::Iter iter(path, false); + SkPoint pts[4]; + for (;;) { + SkString str; + const SkPoint* p = &pts[1]; + int n = 0; + const char* name = ""; + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + name = " M"; + p = &pts[0]; + n = 1; + break; + case SkPath::kLine_Verb: + name = " L"; + n = 1; + break; + case SkPath::kQuad_Verb: + name = " Q"; + n = 2; + break; + case SkPath::kCubic_Verb: + name = " C"; + n = 3; + break; + case SkPath::kClose_Verb: + name = " X"; + n = 0; + break; + case SkPath::kDone_Verb: + goto DONE; + } + str.append(name); + for (int i = 0; i < n; i++) { + str.append(" "); + str.appendScalar(p[i].fX); + str.append(" "); + str.appendScalar(p[i].fY); + } + SkDebugf("\"%s\"\n", str.c_str()); + } +DONE: +#endif + + GC2Canvas(this)->drawPath(path, paint); +} + +void GraphicsContext::strokePath(const Path& webCorePath, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + + m_data->setup_paint_stroke(&paint, NULL); + check_set_shader(&paint, grad, pat); + + GC2Canvas(this)->drawPath(*webCorePath.platformPath(), paint); +} + +void GraphicsContext::fillRect(const FloatRect& rect, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkRect r; + SkPaint paint; + + m_data->setup_paint_fill(&paint); + check_set_shader(&paint, grad, pat); + + GC2Canvas(this)->drawRect(*android_setrect(&r, rect), paint); +} + +void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, + const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkPath path; + SkScalar radii[8]; + SkRect r; + + radii[0] = SkIntToScalar(topLeft.width()); + radii[1] = SkIntToScalar(topLeft.height()); + radii[2] = SkIntToScalar(topRight.width()); + radii[3] = SkIntToScalar(topRight.height()); + radii[4] = SkIntToScalar(bottomRight.width()); + radii[5] = SkIntToScalar(bottomRight.height()); + radii[6] = SkIntToScalar(bottomLeft.width()); + radii[7] = SkIntToScalar(bottomLeft.height()); + path.addRoundRect(*android_setrect(&r, rect), radii); + + m_data->setup_paint_fill(&paint); + GC2Canvas(this)->drawPath(path, paint); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth, PlatformGradient* grad, PlatformPattern* pat) +{ + if (paintingDisabled()) + return; + + SkRect r; + SkPaint paint; + + m_data->setup_paint_stroke(&paint, NULL); + paint.setStrokeWidth(SkFloatToScalar(lineWidth)); + check_set_shader(&paint, grad, pat); + + GC2Canvas(this)->drawRect(*android_setrect(&r, rect), paint); +} + +static U8CPU F2B(float x) +{ + return (int)(x * 255); +} + +static SkColor make_color(float a, float r, float g, float b) +{ + return SkColorSetARGB(F2B(a), F2B(r), F2B(g), F2B(b)); +} + +PlatformGradient* GraphicsContext::newPlatformLinearGradient(const FloatPoint& p0, const FloatPoint& p1, + const float stopData[5], int count) +{ + SkPoint pts[2]; + + android_setpt(&pts[0], p0); + android_setpt(&pts[1], p1); + + SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + count); + + for (int i = 0; i < count; i++) + { + pos[i] = SkFloatToScalar(stopData[0]); + colors[i] = make_color(stopData[4], stopData[1], stopData[2], stopData[3]); + stopData += 5; + } + + return SkGradientShader::CreateLinear(pts, colors, pos, count, + SkShader::kClamp_TileMode); +} + +PlatformGradient* GraphicsContext::newPlatformRadialGradient(const FloatPoint& p0, float r0, + const FloatPoint& p1, float r1, + const float stopData[5], int count) +{ + SkPoint center; + + android_setpt(¢er, p1); + + SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); + SkColor* colors = (SkColor*)storage.get(); + SkScalar* pos = (SkScalar*)(colors + count); + + for (int i = 0; i < count; i++) + { + pos[i] = SkFloatToScalar(stopData[0]); + colors[i] = make_color(stopData[4], stopData[1], stopData[2], stopData[3]); + stopData += 5; + } + + return SkGradientShader::CreateRadial(center, SkFloatToScalar(r1), + colors, pos, count, + SkShader::kClamp_TileMode); +} + +void GraphicsContext::freePlatformGradient(PlatformGradient* shader) +{ + shader->safeUnref(); +} + +PlatformPattern* GraphicsContext::newPlatformPattern(Image* image, + Image::TileRule hRule, + Image::TileRule vRule) +{ +//printf("----------- Image %p, [%d %d] %d %d\n", image, image->width(), image->height(), hRule, vRule); + if (NULL == image) + return NULL; + + SkBitmapRef* bm = image->nativeImageForCurrentFrame(); + if (NULL == bm) + return NULL; + + return SkShader::CreateBitmapShader(bm->bitmap(), + android_convert_TileRule(hRule), + android_convert_TileRule(vRule)); +} + +void GraphicsContext::freePlatformPattern(PlatformPattern* shader) +{ + shader->safeUnref(); +} + +#endif + +#if 0 +static int getBlendedColorComponent(int c, int a) +{ + // We use white. + float alpha = (float)(a) / 255; + int whiteBlend = 255 - a; + c -= whiteBlend; + return (int)(c/alpha); +} +#endif + +void GraphicsContext::fillRect(const IntRect& rect, const Color& color) +{ + if (paintingDisabled()) + return; + + if (color.rgb() & 0xFF000000) { + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + m_data->setup_paint_common(&paint); + paint.setColor(color.rgb()); + GC2Canvas(this)->drawRect(r, paint); + } +} + +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) +{ + if (paintingDisabled()) + return; + + if (color.rgb() & 0xFF000000) { + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + m_data->setup_paint_common(&paint); + paint.setColor(color.rgb()); + GC2Canvas(this)->drawRect(r, paint); + } +} + +void GraphicsContext::clip(const IntRect& rect) +{ + if (paintingDisabled()) + return; + + SkRect r; + + GC2Canvas(this)->clipRect(*android_setrect(&r, rect)); +} + +void GraphicsContext::clip(const Path& path) +{ + if (paintingDisabled()) + return; + +// path.platformPath()->dump(false, "clip path"); + + GC2Canvas(this)->clipPath(*path.platformPath()); +} + +void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) +{ + if (paintingDisabled()) + return; + +//printf("-------- addInnerRoundedRectClip: [%d %d %d %d] thickness=%d\n", rect.x(), rect.y(), rect.width(), rect.height(), thickness); + + SkPath path; + SkRect r; + android_setrect(&r, rect); + path.addOval(r, SkPath::kCW_Direction); + // only perform the inset if we won't invert r + if (2*thickness < rect.width() && 2*thickness < rect.height()) + { + r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness)); + path.addOval(r, SkPath::kCCW_Direction); + } + GC2Canvas(this)->clipPath(path); +} + +void GraphicsContext::clipOut(const IntRect& r) +{ + if (paintingDisabled()) + return; + + SkRect rect; + + GC2Canvas(this)->clipRect(*android_setrect(&rect, r), SkRegion::kDifference_Op); +} + +void GraphicsContext::clipOutEllipseInRect(const IntRect& r) +{ + if (paintingDisabled()) + return; + + SkRect oval; + SkPath path; + + path.addOval(*android_setrect(&oval, r), SkPath::kCCW_Direction); + GC2Canvas(this)->clipPath(path, SkRegion::kDifference_Op); +} + +void GraphicsContext::clipOut(const Path& p) +{ + if (paintingDisabled()) + return; + + GC2Canvas(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +#if SVG_SUPPORT +KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext() +{ + return new KRenderingDeviceContextQuartz(platformContext()); +} +#endif + +void GraphicsContext::beginTransparencyLayer(float opacity) +{ + if (paintingDisabled()) + return; + + SkCanvas* canvas = GC2Canvas(this); + + if (opacity < 1) + { + canvas->saveLayerAlpha(NULL, (int)(opacity * 255), SkCanvas::kHasAlphaLayer_SaveFlag); + } + else + canvas->save(); +} + +void GraphicsContext::endTransparencyLayer() +{ + if (paintingDisabled()) + return; + + GC2Canvas(this)->restore(); +} + +void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color) +{ + if (paintingDisabled()) + return; + + if (blur > 0) + { + SkColor c; + + if (color.isValid()) + c = color.rgb(); + else + c = SkColorSetARGB(0xFF/3, 0, 0, 0); // "std" Apple shadow color + + SkDrawLooper* dl = new SkBlurDrawLooper(SkIntToScalar(blur), + SkIntToScalar(size.width()), + SkIntToScalar(size.height()), + c); + m_data->mState->setDrawLooper(dl)->unref(); + } + else + m_data->mState->setDrawLooper(NULL); +} + +void GraphicsContext::clearShadow() +{ + if (paintingDisabled()) + return; + + m_data->mState->setDrawLooper(NULL); +} + +void GraphicsContext::drawFocusRing(const Color& color) +{ + // Do nothing, since we draw the focus ring independently. +} + +PlatformGraphicsContext* GraphicsContext::platformContext() const +{ + ASSERT(!paintingDisabled()); + return m_data->mPgc; +} + +void GraphicsContext::setMiterLimit(float limit) +{ + m_data->mState->mMiterLimit = limit; +} + +void GraphicsContext::setAlpha(float alpha) +{ + m_data->mState->mAlpha = alpha; +} + +void GraphicsContext::setCompositeOperation(CompositeOperator op) +{ + m_data->mState->mPorterDuffMode = android_convert_compositeOp(op); +} + +void GraphicsContext::clearRect(const FloatRect& rect) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + m_data->setup_paint_fill(&paint); + paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode); + GC2Canvas(this)->drawRect(r, paint); +} + +void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) +{ + if (paintingDisabled()) + return; + + SkPaint paint; + SkRect r; + + android_setrect(&r, rect); + + m_data->setup_paint_stroke(&paint, NULL); + paint.setStrokeWidth(SkFloatToScalar(lineWidth)); + GC2Canvas(this)->drawRect(r, paint); +} + +void GraphicsContext::setLineCap(LineCap cap) +{ + switch (cap) { + case ButtCap: + m_data->mState->mLineCap = SkPaint::kButt_Cap; + break; + case RoundCap: + m_data->mState->mLineCap = SkPaint::kRound_Cap; + break; + case SquareCap: + m_data->mState->mLineCap = SkPaint::kSquare_Cap; + break; + default: + SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap)); + break; + } +} + +void GraphicsContext::setLineJoin(LineJoin join) +{ + switch (join) { + case MiterJoin: + m_data->mState->mLineJoin = SkPaint::kMiter_Join; + break; + case RoundJoin: + m_data->mState->mLineJoin = SkPaint::kRound_Join; + break; + case BevelJoin: + m_data->mState->mLineJoin = SkPaint::kBevel_Join; + break; + default: + SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join)); + break; + } +} + +void GraphicsContext::scale(const FloatSize& size) +{ + if (paintingDisabled()) + return; + GC2Canvas(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height())); +} + +void GraphicsContext::rotate(float angleInRadians) +{ + if (paintingDisabled()) + return; + GC2Canvas(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f))); +} + +void GraphicsContext::translate(float x, float y) +{ + if (paintingDisabled()) + return; + GC2Canvas(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +void GraphicsContext::concatCTM(const AffineTransform& xform) +{ + if (paintingDisabled()) + return; + +//printf("-------------- GraphicsContext::concatCTM\n"); + GC2Canvas(this)->concat((SkMatrix) xform); +} + +FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) +{ + if (paintingDisabled()) + return FloatRect(); + + const SkMatrix& matrix = GC2Canvas(this)->getTotalMatrix(); + SkRect r; + android_setrect(&r, rect); + matrix.mapRect(&r); + FloatRect result(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) +{ + // appears to be PDF specific, so we ignore it +#if 0 + if (paintingDisabled()) + return; + + CFURLRef urlRef = link.createCFURL(); + if (urlRef) { + CGContextRef context = platformContext(); + + // Get the bounding box to handle clipping. + CGRect box = CGContextGetClipBoundingBox(context); + + IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height); + IntRect rect = destRect; + rect.intersect(intBox); + + CGPDFContextSetURLForRect(context, urlRef, + CGRectApplyAffineTransform(rect, CGContextGetCTM(context))); + + CFRelease(urlRef); + } +#endif +} + +// we don't need to push these down, since we query the current state and build our paint at draw-time + +void GraphicsContext::setPlatformStrokeColor(const Color&) {} +void GraphicsContext::setPlatformStrokeThickness(float) {} +void GraphicsContext::setPlatformFillColor(const Color&) {} + + +// functions new to Feb-19 tip of tree merge: +AffineTransform GraphicsContext::getCTM() const { + notImplemented(); + return AffineTransform(); +} + +} diff --git a/WebCore/platform/graphics/android/ImageAndroid.cpp b/WebCore/platform/graphics/android/ImageAndroid.cpp new file mode 100644 index 0000000..00145af --- /dev/null +++ b/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2004, 2005, 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "AffineTransform.h" +#include "BitmapImage.h" +#include "Image.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "PlatformGraphicsContext.h" +#include "PlatformString.h" +#include "SharedBuffer.h" + +#include "android_graphics.h" +#include "SkBitmapRef.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkString.h" + +#include <utils/AssetManager.h> + +//#define TRACE_SUBSAMPLED_BITMAPS + +android::AssetManager* gGlobalAssetMgr; + +namespace WebCore { + +void FrameData::clear() +{ + if (m_frame) { + m_frame->unref(); + m_frame = 0; + m_duration = 0.; + m_hasAlpha = true; + } +} + +SkBitmapRef* BitmapImage::getBitmap() +{ + return m_bitmapRef; +} + +void BitmapImage::initPlatformData() +{ + m_bitmapRef = NULL; + m_source.clearURL(); +} + +void BitmapImage::invalidatePlatformData() +{ + if (m_bitmapRef) { + m_bitmapRef->unref(); + m_bitmapRef = NULL; + } +} + +void BitmapImage::checkForSolidColor() +{ + m_isSolidColor = false; + if (this->frameCount() > 1) { + if (!m_bitmapRef) { + return; + } + + const SkBitmap& bm = m_bitmapRef->bitmap(); + + if (bm.width() == 1 && bm.height() == 1) { + SkAutoLockPixels alp(bm); + if (bm.getPixels() == NULL) { + return; + } + + SkPMColor color; + switch (bm.getConfig()) { + case SkBitmap::kARGB_8888_Config: + color = *bm.getAddr32(0, 0); + break; + case SkBitmap::kRGB_565_Config: + color = SkPixel16ToPixel32(*bm.getAddr16(0, 0)); + break; + case SkBitmap::kIndex8_Config: { + SkColorTable* ctable = bm.getColorTable(); + if (!ctable) { + return; + } + color = (*ctable)[*bm.getAddr8(0, 0)]; + break; + } + default: // don't check other configs + return; + } + m_isSolidColor = true; + m_solidColor = android_SkPMColorToWebCoreColor(color); + } + } +} + +void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, + const FloatRect& srcRect, CompositeOperator compositeOp) +{ + SkBitmapRef* image = this->nativeImageForCurrentFrame(); + if (!image) { // If it's too early we won't have an image yet. + return; + } + + // in case we get called with an incomplete bitmap + const SkBitmap& bitmap = image->bitmap(); + if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { + return; + } + + SkIRect srcR; + SkRect dstR; + float invScaleX = (float)bitmap.width() / image->origWidth(); + float invScaleY = (float)bitmap.height() / image->origHeight(); + + android_setrect(&dstR, dstRect); + android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY); + if (srcR.isEmpty() || dstR.isEmpty()) { + return; + } + + SkCanvas* canvas = ctxt->platformContext()->mCanvas; + SkPaint paint; + + paint.setFilterBitmap(true); + paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); + canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); + + startAnimation(); + +#ifdef TRACE_SUBSAMPLED_BITMAPS + if (bitmap.width() != image->origWidth() || + bitmap.height() != image->origHeight()) { + SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n", + bitmap.width(), bitmap.height(), + image->origWidth(), image->origHeight()); + } +#endif +} + +void BitmapImage::setURL(const String& str) +{ + m_source.setURL(str); +} + +/////////////////////////////////////////////////////////////////////////////// + +void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, + const AffineTransform& patternTransform, + const FloatPoint& phase, CompositeOperator compositeOp, + const FloatRect& destRect) +{ + SkBitmapRef* image = this->nativeImageForCurrentFrame(); + if (!image) { // If it's too early we won't have an image yet. + return; + } + + // in case we get called with an incomplete bitmap + const SkBitmap& bitmap = image->bitmap(); + if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { + return; + } + + SkRect dstR; + android_setrect(&dstR, destRect); + if (dstR.isEmpty()) { + return; + } + + SkCanvas* canvas = ctxt->platformContext()->mCanvas; + SkPaint paint; + + SkShader* shader = SkShader::CreateBitmapShader(bitmap, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + paint.setShader(shader)->unref(); + // now paint is the only owner of shader + paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); + paint.setFilterBitmap(true); + + SkMatrix matrix(patternTransform); + + float scaleX = (float)image->origWidth() / bitmap.width(); + float scaleY = (float)image->origHeight() / bitmap.height(); + matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY)); + + matrix.postTranslate(SkFloatToScalar(phase.x()), + SkFloatToScalar(phase.y())); + shader->setLocalMatrix(matrix); + canvas->drawRect(dstR, paint); + +#ifdef TRACE_SUBSAMPLED_BITMAPS + if (bitmap.width() != image->origWidth() || + bitmap.height() != image->origHeight()) { + SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n", + bitmap.width(), bitmap.height(), + image->origWidth(), image->origHeight(), + SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height())); + } +#endif +} + +// missingImage, textAreaResizeCorner +Image* Image::loadPlatformResource(const char *name) +{ + if (NULL == gGlobalAssetMgr) { + gGlobalAssetMgr = new android::AssetManager(); + gGlobalAssetMgr->addDefaultAssets(); + } + + SkString path("webkit/"); + path.append(name); + path.append(".png"); + + android::Asset* a = gGlobalAssetMgr->open(path.c_str(), + android::Asset::ACCESS_BUFFER); + if (a == NULL) { + SkDebugf("---------------- failed to open image asset %s\n", name); + return NULL; + } + + Image* image = new BitmapImage; + RefPtr<SharedBuffer> buffer = + new SharedBuffer((const char*)a->getBuffer(false), a->getLength()); + image->setData(buffer, true); + delete a; + return image; +} + +} diff --git a/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp new file mode 100644 index 0000000..65fb7cd --- /dev/null +++ b/WebCore/platform/graphics/android/ImageBufferAndroid.cpp @@ -0,0 +1,53 @@ +/* ImageBufferAndroid.cpp +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "ImageBuffer.h" + +#include "GraphicsContext.h" + +using namespace std; + +namespace WebCore { + +auto_ptr<ImageBuffer> ImageBuffer::create(const IntSize& size, bool grayScale) +{ + // Ignore grayScale for now, since SkBitmap doesn't support it... yet + + GraphicsContext* ctx = GraphicsContext::createOffscreenContext(size.width(), size.height()); + + auto_ptr<GraphicsContext> context(ctx); + + return auto_ptr<ImageBuffer>(new ImageBuffer(size, context)); +} + + +ImageBuffer::ImageBuffer(const IntSize& size, auto_ptr<GraphicsContext> context) + : m_data(NULL), m_size(size), m_context(context.release()) +{ +} + +ImageBuffer::~ImageBuffer() +{ +} + +GraphicsContext* ImageBuffer::context() const +{ + return m_context.get(); +} + +} diff --git a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp new file mode 100644 index 0000000..144968d --- /dev/null +++ b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp @@ -0,0 +1,336 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "ImageDecoder.h" +#include "ImageSource.h" +#include "IntSize.h" +#include "SharedBuffer.h" +#include "PlatformString.h" + +#include "JavaSharedClient.h" + +#include "SkBitmapRef.h" +#include "SkImageRef.h" +#include "SkImageDecoder.h" +#include "SkStream.h" +#include "SkTemplates.h" + +SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src); + +//#define TRACE_SUBSAMPLE_BITMAPS +//#define TRACE_RLE_BITMAPS + +#include "SkImageRef_GlobalPool.h" +#include "SkImageRef_ashmem.h" + +// made this up, so we don't waste a file-descriptor on small images, plus +// we don't want to lose too much on the round-up to a page size (4K) +#define MIN_ASHMEM_ALLOC_SIZE (32*1024) + +static bool should_use_ashmem(const SkBitmap& bm) { + return bm.getSize() >= MIN_ASHMEM_ALLOC_SIZE; +} + +/* Images larger than this should be subsampled. Using ashmem, the decoded + pixels will be purged as needed, so this value can be pretty large. Making + it too small hurts image quality (e.g. abc.com background). 2Meg works for + the sites I've tested, but if we hit important sites that need more, we + should try increasing it and see if it has negative impact on performance + (i.e. we end up thrashing because we need to keep decoding images that have + been purged. + + Perhaps this value should be some fraction of the available RAM... +*/ +static size_t computeMaxBitmapSizeForCache() { + return 2*1024*1024; +} + +/* 8bit images larger than this should be recompressed in RLE, to reduce + on the imageref cache. + + Downside: performance, since we have to decode/encode + and then rle-decode when we draw. +*/ +static bool shouldReencodeAsRLE(const SkBitmap& bm) { + const SkBitmap::Config c = bm.config(); + return (SkBitmap::kIndex8_Config == c || SkBitmap::kA8_Config == c) + && + bm.width() >= 64 // narrower than this won't compress well in RLE + && + bm.getSize() > (250*1024); +} + +/////////////////////////////////////////////////////////////////////////////// + +class PrivateAndroidImageSourceRec : public SkBitmapRef { +public: + PrivateAndroidImageSourceRec(const SkBitmap& bm, int origWidth, + int origHeight, int sampleSize) + : SkBitmapRef(bm), fSampleSize(sampleSize), fAllDataReceived(false) { + this->setOrigSize(origWidth, origHeight); + } + + int fSampleSize; + bool fAllDataReceived; +}; + +namespace WebCore { + +class SharedBufferStream : public SkMemoryStream { +public: + SharedBufferStream(SharedBuffer* buffer) + : SkMemoryStream(buffer->data(), buffer->size(), false) { + fBuffer = buffer; + buffer->ref(); + } + + virtual ~SharedBufferStream() { + // we can't necessarily call fBuffer->deref() here, as we may be + // in a different thread from webkit, and SharedBuffer is not + // threadsafe. Therefore we defer it until it can be executed in the + // webkit thread. +// SkDebugf("-------- enqueue buffer %p for deref\n", fBuffer); + JavaSharedClient::EnqueueFunctionPtr(CallDeref, fBuffer); + } + +private: + // don't allow this to change our data. should not get called, but we + // override here just to be sure + virtual void setMemory(const void* data, size_t length, bool copyData) { + sk_throw(); + } + + // we share ownership of this with webkit + SharedBuffer* fBuffer; + + // will be invoked in the webkit thread + static void CallDeref(void* buffer) { +// SkDebugf("-------- call deref on buffer %p\n", buffer); + ((SharedBuffer*)buffer)->deref(); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// + +ImageSource::ImageSource() { + m_decoder.m_image = NULL; +} + +ImageSource::~ImageSource() { + delete m_decoder.m_image; +} + +bool ImageSource::initialized() const { + return m_decoder.m_image != NULL; +} + +static int computeSampleSize(const SkBitmap& bitmap) { + const size_t maxSize = computeMaxBitmapSizeForCache(); + size_t size = bitmap.getSize(); + int sampleSize = 1; + + while (size > maxSize) { + sampleSize <<= 1; + size >>= 2; + } + +#ifdef TRACE_SUBSAMPLE_BITMAPS + if (sampleSize > 1) { + SkDebugf("------- bitmap [%d %d] config=%d origSize=%d predictSize=%d sampleSize=%d\n", + bitmap.width(), bitmap.height(), bitmap.config(), + bitmap.getSize(), size, sampleSize); + } +#endif + return sampleSize; +} + +static SkPixelRef* convertToRLE(SkBitmap* bm, const void* data, size_t len) { + if (!shouldReencodeAsRLE(*bm)) { + return NULL; + } + + SkBitmap tmp; + + if (!SkImageDecoder::DecodeMemory(data, len, &tmp) || !tmp.getPixels()) { + return NULL; + } + + SkPixelRef* ref = SkCreateRLEPixelRef(tmp); + if (NULL == ref) { + return NULL; + } + +#ifdef TRACE_RLE_BITMAPS + SkDebugf("---- reencode bitmap as RLE: [%d %d] encodeSize=%d\n", + tmp.width(), tmp.height(), len); +#endif + + bm->setConfig(SkBitmap::kRLE_Index8_Config, tmp.width(), tmp.height()); + return ref; +} + +void ImageSource::clearURL() +{ + m_decoder.m_url.reset(); +} + +void ImageSource::setURL(const String& url) +{ + if (url.startsWith("data:")) { + clearURL(); + } else { + m_decoder.m_url.setUTF16(url.characters(), url.length()); + } +} + +void ImageSource::setData(SharedBuffer* data, bool allDataReceived) +{ + if (NULL == m_decoder.m_image) { + SkBitmap tmp; + + SkMemoryStream stream(data->data(), data->size(), false); + SkImageDecoder* codec = SkImageDecoder::Factory(&stream); + SkAutoTDelete<SkImageDecoder> ad(codec); + + if (!codec || !codec->decode(&stream, &tmp, SkBitmap::kNo_Config, + SkImageDecoder::kDecodeBounds_Mode)) { + return; + } + + int origW = tmp.width(); + int origH = tmp.height(); + int sampleSize = computeSampleSize(tmp); + if (sampleSize > 1) { + codec->setSampleSize(sampleSize); + stream.rewind(); + if (!codec->decode(&stream, &tmp, SkBitmap::kNo_Config, + SkImageDecoder::kDecodeBounds_Mode)) { + return; + } + } + + m_decoder.m_image = new PrivateAndroidImageSourceRec(tmp, origW, origH, + sampleSize); + } + + PrivateAndroidImageSourceRec* decoder = m_decoder.m_image; + if (allDataReceived && !decoder->fAllDataReceived) { + decoder->fAllDataReceived = true; + + SkBitmap* bm = &decoder->bitmap(); + SkPixelRef* ref = convertToRLE(bm, data->data(), data->size()); + + if (NULL == ref) { + SkStream* strm = new SharedBufferStream(data); + // imageref now owns the stream object + if (should_use_ashmem(*bm)) { +// SkDebugf("---- use ashmem for image [%d %d]\n", bm.width(), bm.height()); + ref = new SkImageRef_ashmem(strm, bm->config(), decoder->fSampleSize); + } else { +// SkDebugf("---- use globalpool for image [%d %d]\n", bm.width(), bm.height()); + ref = new SkImageRef_GlobalPool(strm, bm->config(), decoder->fSampleSize); + } + + // SkDebugf("----- allDataReceived [%d %d]\n", bm->width(), bm->height()); + } + + // we promise to never change the pixels (makes picture recording fast) + ref->setImmutable(); + // give it the URL if we have one + ref->setURI(m_decoder.m_url); + // our bitmap is now the only owner of the imageref + bm->setPixelRef(ref)->unref(); + } +} + +bool ImageSource::isSizeAvailable() +{ + return m_decoder.m_image != NULL; +} + +IntSize ImageSource::size() const +{ + if (m_decoder.m_image) { + return IntSize(m_decoder.m_image->origWidth(), m_decoder.m_image->origHeight()); + } + return IntSize(0, 0); +} + +int ImageSource::repetitionCount() +{ + return 1; + // A property with value 0 means loop forever. +} + +size_t ImageSource::frameCount() const +{ + // i.e. 0 frames if we're not decoded, or 1 frame if we are + return m_decoder.m_image != NULL; +} + +SkBitmapRef* ImageSource::createFrameAtIndex(size_t index) +{ + SkASSERT(index == 0); + SkASSERT(m_decoder.m_image != NULL); + m_decoder.m_image->ref(); + return m_decoder.m_image; +} + +float ImageSource::frameDurationAtIndex(size_t index) +{ + SkASSERT(index == 0); + float duration = 0; + + // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. + // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify + // a duration of <= 10 ms. See gfxImageFrame::GetTimeout in Gecko or Radar 4051389 for more. + if (duration <= 0.010f) + duration = 0.100f; + return duration; +} + +bool ImageSource::frameHasAlphaAtIndex(size_t index) +{ + SkASSERT(0 == index); + + if (NULL == m_decoder.m_image) + return true; // if we're not sure, assume the worse-case + const PrivateAndroidImageSourceRec& decoder = *m_decoder.m_image; + // if we're 16bit, we know even without all the data available + if (decoder.bitmap().getConfig() == SkBitmap::kRGB_565_Config) + return false; + + if (!decoder.fAllDataReceived) + return true; // if we're not sure, assume the worse-case + + return !decoder.bitmap().isOpaque(); +} + +bool ImageSource::frameIsCompleteAtIndex(size_t index) +{ + SkASSERT(0 == index); + return m_decoder.m_image && m_decoder.m_image->fAllDataReceived; +} + +void ImageSource::clear() +{ + // do nothing, since the cache is managed elsewhere +} + +} diff --git a/WebCore/platform/graphics/android/PathAndroid.cpp b/WebCore/platform/graphics/android/PathAndroid.cpp new file mode 100644 index 0000000..68f4c0d --- /dev/null +++ b/WebCore/platform/graphics/android/PathAndroid.cpp @@ -0,0 +1,231 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "Path.h" +#include "FloatRect.h" +#include "AffineTransform.h" + +#include "SkPath.h" +#include "SkRegion.h" + +#include "android_graphics.h" + +namespace WebCore { + +Path::Path() +{ + m_path = new SkPath; +// m_path->setFlags(SkPath::kWinding_FillType); +} + +Path::Path(const Path& other) +{ + m_path = new SkPath(*other.m_path); +} + +Path::~Path() +{ + delete m_path; +} + +Path& Path::operator=(const Path& other) +{ + *m_path = *other.m_path; + return *this; +} + +bool Path::isEmpty() const +{ + return m_path->isEmpty(); +} + +bool Path::contains(const FloatPoint& point, WindRule rule) const +{ + SkRegion rgn, clip; + + int x = (int)floorf(point.x()); + int y = (int)floorf(point.y()); + clip.setRect(x, y, x + 1, y + 1); + + SkPath::FillType ft = m_path->getFillType(); // save + m_path->setFillType(rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType); + + bool contains = rgn.setPath(*m_path, clip); + + m_path->setFillType(ft); // restore + return contains; +} + +void Path::translate(const FloatSize& size) +{ + m_path->offset(SkFloatToScalar(size.width()), SkFloatToScalar(size.height())); +} + +FloatRect Path::boundingRect() const +{ + SkRect r; + + m_path->computeBounds(&r, SkPath::kExact_BoundsType); + return FloatRect( SkScalarToFloat(r.fLeft), + SkScalarToFloat(r.fTop), + SkScalarToFloat(r.width()), + SkScalarToFloat(r.height())); +} + +void Path::moveTo(const FloatPoint& point) +{ + m_path->moveTo(SkFloatToScalar(point.x()), SkFloatToScalar(point.y())); +} + +void Path::addLineTo(const FloatPoint& p) +{ + m_path->lineTo(SkFloatToScalar(p.x()), SkFloatToScalar(p.y())); +} + +void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep) +{ + m_path->quadTo( SkFloatToScalar(cp.x()), SkFloatToScalar(cp.y()), + SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y())); +} + +void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep) +{ + m_path->cubicTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()), + SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()), + SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y())); +} + +void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) +{ + m_path->arcTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()), + SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()), + SkFloatToScalar(radius)); +} + +void Path::closeSubpath() +{ + m_path->close(); +} + +static const float gPI = 3.1415926f; + +void Path::addArc(const FloatPoint& p, float r, float sa, float ea, + bool clockwise) { + SkScalar cx = SkFloatToScalar(p.x()); + SkScalar cy = SkFloatToScalar(p.y()); + SkScalar radius = SkFloatToScalar(r); + + SkRect oval; + oval.set(cx - radius, cy - radius, cx + radius, cy + radius); + + float sweep = ea - sa; + // check for a circle + if (sweep >= 2*gPI || sweep <= -2*gPI) { + m_path->addOval(oval); + } else { + SkScalar startDegrees = SkFloatToScalar(sa * 180 / gPI); + SkScalar sweepDegrees = SkFloatToScalar(sweep * 180 / gPI); + + if (clockwise && sweepDegrees > 0) { + sweepDegrees -= SkIntToScalar(360); + } else if (!clockwise && sweepDegrees < 0) { + sweepDegrees = SkIntToScalar(360) - sweepDegrees; + } + +// SkDebugf("addArc sa=%g ea=%g cw=%d start=%g sweep=%g\n", sa, ea, clockwise, +// SkScalarToFloat(startDegrees), SkScalarToFloat(sweepDegrees)); + + m_path->arcTo(oval, startDegrees, sweepDegrees, false); + } +} + +void Path::addRect(const FloatRect& rect) +{ + SkRect r; + + android_setrect(&r, rect); + m_path->addRect(r); +} + +void Path::addEllipse(const FloatRect& rect) +{ + SkRect r; + + android_setrect(&r, rect); + m_path->addOval(r); +} + +void Path::clear() +{ + m_path->reset(); +} + +static FloatPoint* setfpts(FloatPoint dst[], const SkPoint src[], int count) +{ + for (int i = 0; i < count; i++) + { + dst[i].setX(SkScalarToFloat(src[i].fX)); + dst[i].setY(SkScalarToFloat(src[i].fY)); + } + return dst; +} + +void Path::apply(void* info, PathApplierFunction function) const +{ + SkPath::Iter iter(*m_path, false); + SkPoint pts[4]; + + PathElement elem; + FloatPoint fpts[3]; + + for (;;) + { + switch (iter.next(pts)) { + case SkPath::kMove_Verb: + elem.type = PathElementMoveToPoint; + elem.points = setfpts(fpts, &pts[0], 1); + break; + case SkPath::kLine_Verb: + elem.type = PathElementAddLineToPoint; + elem.points = setfpts(fpts, &pts[1], 1); + break; + case SkPath::kQuad_Verb: + elem.type = PathElementAddQuadCurveToPoint; + elem.points = setfpts(fpts, &pts[1], 2); + break; + case SkPath::kCubic_Verb: + elem.type = PathElementAddCurveToPoint; + elem.points = setfpts(fpts, &pts[1], 3); + break; + case SkPath::kClose_Verb: + elem.type = PathElementCloseSubpath; + elem.points = NULL; + break; + case SkPath::kDone_Verb: + return; + } + function(info, &elem); + } +} + +void Path::transform(const AffineTransform& xform) +{ + m_path->transform(xform); +} + +} diff --git a/WebCore/platform/graphics/android/PlatformGraphics.h b/WebCore/platform/graphics/android/PlatformGraphics.h new file mode 100644 index 0000000..6efdb43 --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphics.h @@ -0,0 +1,25 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef PlatformGraphics_d +#define PlatformGraphics_d + +typedef class SkShader PlatformGradient; +typedef class SkShader PlatformPattern; + +#endif + diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp new file mode 100644 index 0000000..af443b8 --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp @@ -0,0 +1,73 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "Node.h" +#include "PlatformGraphicsContext.h" +#include "SkCanvas.h" + +namespace WebCore { + +PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas) : mCanvas(canvas), m_deleteCanvas(false) +{ + // This is useful only if this Canvas is part of an SkPicture object. + m_buttons = new SkTDArray<Container*>; +} + +PlatformGraphicsContext::PlatformGraphicsContext() : m_deleteCanvas(true) +{ + mCanvas = new SkCanvas; + // Since this is our own private SkCanvas, and has no relation to a picture + // storing button references would be meaningless. + m_buttons = NULL; +} + +PlatformGraphicsContext::~PlatformGraphicsContext() +{ + if (m_deleteCanvas) { +// printf("-------------------- deleting offscreen canvas\n"); + delete mCanvas; + } + if (m_buttons != NULL) { + m_buttons->deleteAll(); + delete m_buttons; + } +} + +SkTDArray<Container*>* PlatformGraphicsContext::getAndClearButtonInfo() +{ + // The caller is now responsible for deleting the array + SkTDArray<Container*>* buttons = m_buttons; + m_buttons = NULL; + return buttons; +} + +void PlatformGraphicsContext::storeButtonInfo(Node* node, const IntRect& r) +{ + if (m_buttons == NULL) + return; + // Initialize all of the nodes to have disabled state, so that we guarantee + // that we paint all of them the first time. + RenderSkinAndroid::State state = RenderSkinAndroid::kDisabled; + Container* container = new Container(node, r, state); + // Place a reference to our subpicture in the Picture. + mCanvas->drawPicture(*(container->picture())); + // Keep track of the information about the button. + *m_buttons->append() = container; +} + +} // WebCore diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.h b/WebCore/platform/graphics/android/PlatformGraphicsContext.h new file mode 100644 index 0000000..a2d7ebe --- /dev/null +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.h @@ -0,0 +1,108 @@ +/* + * + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef platform_graphics_context_h +#define platform_graphics_context_h + +#include "IntRect.h" +#include "RenderSkinAndroid.h" +#include "RenderSkinButton.h" +#include "SkCanvas.h" +#include "SkPicture.h" +#include "SkTDArray.h" + +class SkCanvas; +class WebCore::Node; + +class Container { +public: + Container(WebCore::Node* node, const WebCore::IntRect& r, + WebCore::RenderSkinAndroid::State is) + : m_node(node), m_rect(r), m_state(is) + { + m_picture = new SkPicture; + } + + ~Container() + { + m_picture->unref(); + } + + bool matches(WebCore::Node* match) { return m_node == match; } + + // Provide a pointer to our SkPicture. + SkPicture* picture() { return m_picture; } + + // Update the focus state of this button, depending on whether it + // corresponds to the focused node passed in. If its state has changed, + // re-record to the subpicture, so the master picture will reflect the + // change. + void updateFocusState(WebCore::Node* focus) + { + WebCore::RenderSkinAndroid::State state = m_node == focus ? + WebCore::RenderSkinAndroid::kFocused : WebCore::RenderSkinAndroid::kNormal; + if (state == m_state) + return; + m_state = state; + SkCanvas* canvas = m_picture->beginRecording(m_rect.width(), m_rect.height()); + WebCore::RenderSkinButton::Draw(canvas, m_rect, state); + m_picture->endRecording(); + } +private: + // Mark copy and assignment private so noone can use them. + Container& operator=(const Container& src) { return *this; } + Container(const Container& src) { } + // Only used for comparison, since after it is stored it will be transferred + // to the UI thread. + WebCore::Node* m_node; + // The rectangle representing the bounds of the button. + WebCore::IntRect m_rect; + // An SkPicture that, thanks to storeButtonInfo, is pointed to by the master + // picture, so that we can rerecord this button without rerecording the + // world. + SkPicture* m_picture; + // The state of the button - Currently kFocused or kNormal (and kDisabled + // as an initial value), but could be expanded to include other states. + WebCore::RenderSkinAndroid::State m_state; +}; + +namespace WebCore { + +class PlatformGraphicsContext { +public: + PlatformGraphicsContext(); + PlatformGraphicsContext(SkCanvas* canvas); + ~PlatformGraphicsContext(); + + SkCanvas* mCanvas; + + bool deleteUs() const { return m_deleteCanvas; } + // If our graphicscontext has a button list, add a new container for the + // nod/rect, and record a new subpicture for this node/button in the current + // mCanvas + void storeButtonInfo(Node* node, const IntRect& r); + // Detaches button array (if any), returning it to the caller and setting our + // internal ptr to NULL + SkTDArray<Container*>* getAndClearButtonInfo(); +private: + bool m_deleteCanvas; + SkTDArray<Container*>* m_buttons; +}; + +} +#endif + diff --git a/WebCore/platform/graphics/android/SkBitmapRef.h b/WebCore/platform/graphics/android/SkBitmapRef.h new file mode 100644 index 0000000..77afcf3 --- /dev/null +++ b/WebCore/platform/graphics/android/SkBitmapRef.h @@ -0,0 +1,54 @@ +/* include/graphics/SkBitmapRef.h +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef SkBitmapRef_DEFINED +#define SkBitmapRef_DEFINED + +#include "SkRefCnt.h" +#include "SkBitmap.h" + +class SkBitmapRef : public SkRefCnt { +public: + SkBitmapRef() : fOrigWidth(0), fOrigHeight(0), fAccessed(false) {} + explicit SkBitmapRef(const SkBitmap& src) + : fBitmap(src), + fOrigWidth(src.width()), + fOrigHeight(src.height()), + fAccessed(false) {} + + const SkBitmap& bitmap() const { return fBitmap; } + SkBitmap& bitmap() { return fBitmap; } + + int origWidth() const { return fOrigWidth; } + int origHeight() const { return fOrigHeight; } + + void setOrigSize(int width, int height) { + fOrigWidth = width; + fOrigHeight = height; + } + // return true if this is not the first access + // mark it true so all subsequent calls return true + bool accessed() { bool result = fAccessed; + fAccessed = true; return result; } + +private: + SkBitmap fBitmap; + int fOrigWidth, fOrigHeight; + bool fAccessed; +}; + +#endif diff --git a/WebCore/platform/graphics/android/android_graphics.cpp b/WebCore/platform/graphics/android/android_graphics.cpp new file mode 100644 index 0000000..3964ee1 --- /dev/null +++ b/WebCore/platform/graphics/android/android_graphics.cpp @@ -0,0 +1,223 @@ +/* + * + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "android_graphics.h" +#include "IntPoint.h" +#include "IntRect.h" +#include "FloatPoint.h" +#include "FloatRect.h" +#include "SkCanvas.h" +#include "SkColorPriv.h" +#include "SkCornerPathEffect.h" +#include "SkGradientShader.h" +#include "SkPath.h" +#include "SkRegion.h" + +SkPoint* android_setpt(SkPoint* dst, const WebCore::IntPoint& src) +{ + dst->set(SkIntToScalar(src.x()), SkIntToScalar(src.y())); + return dst; +} + +SkPoint* android_setpt(SkPoint* dst, const WebCore::FloatPoint& src) +{ + dst->set(SkFloatToScalar(src.x()), SkFloatToScalar(src.y())); + return dst; +} + +SkRect* android_setrect(SkRect* dst, const WebCore::IntRect& src) +{ + dst->set(SkIntToScalar(src.x()), + SkIntToScalar(src.y()), + SkIntToScalar(src.x() + src.width()), + SkIntToScalar(src.y() + src.height())); + return dst; +} + +SkIRect* android_setrect(SkIRect* dst, const WebCore::IntRect& src) +{ + dst->set(src.x(), src.y(), + src.x() + src.width(), + src.y() + src.height()); + return dst; +} + +SkRect* android_setrect(SkRect* dst, const WebCore::FloatRect& src) +{ + dst->set(SkFloatToScalar(src.x()), + SkFloatToScalar(src.y()), + SkFloatToScalar(src.x() + src.width()), + SkFloatToScalar(src.y() + src.height())); + return dst; +} + +SkIRect* android_setrect(SkIRect* dst, const WebCore::FloatRect& src) +{ + dst->set(SkScalarRound(SkFloatToScalar(src.x())), + SkScalarRound(SkFloatToScalar(src.y())), + SkScalarRound(SkFloatToScalar(src.x() + src.width())), + SkScalarRound(SkFloatToScalar(src.y() + src.height()))); + return dst; +} + +SkIRect* android_setrect_scaled(SkIRect* dst, const WebCore::FloatRect& src, + float sx, float sy) +{ + dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)), + SkScalarRound(SkFloatToScalar(src.y() * sy)), + SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)), + SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy))); + return dst; +} + +static const struct CompositOpToPorterDuffMode { + uint8_t mCompositOp; + uint8_t mPorterDuffMode; +} gMapCompositOpsToPorterDuffModes[] = { + { WebCore::CompositeClear, SkPorterDuff::kClear_Mode }, + { WebCore::CompositeCopy, SkPorterDuff::kSrcOver_Mode }, // TODO + { WebCore::CompositeSourceOver, SkPorterDuff::kSrcOver_Mode }, + { WebCore::CompositeSourceIn, SkPorterDuff::kSrcIn_Mode }, + { WebCore::CompositeSourceOut, SkPorterDuff::kSrcOut_Mode }, + { WebCore::CompositeSourceAtop, SkPorterDuff::kSrcATop_Mode }, + { WebCore::CompositeDestinationOver, SkPorterDuff::kDstOver_Mode }, + { WebCore::CompositeDestinationIn, SkPorterDuff::kDstIn_Mode }, + { WebCore::CompositeDestinationOut, SkPorterDuff::kDstOut_Mode }, + { WebCore::CompositeDestinationAtop, SkPorterDuff::kDstATop_Mode }, + { WebCore::CompositeXOR, SkPorterDuff::kXor_Mode }, + { WebCore::CompositePlusDarker, SkPorterDuff::kDarken_Mode }, + { WebCore::CompositeHighlight, SkPorterDuff::kSrcOver_Mode }, // TODO + { WebCore::CompositePlusLighter, SkPorterDuff::kLighten_Mode } +}; + +SkPorterDuff::Mode android_convert_compositeOp(WebCore::CompositeOperator op) +{ + const CompositOpToPorterDuffMode* table = gMapCompositOpsToPorterDuffModes; + + for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToPorterDuffModes); i++) { + if (table[i].mCompositOp == op) { + return (SkPorterDuff::Mode)table[i].mPorterDuffMode; + } + } + + SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositOperator %d\n", op)); + return SkPorterDuff::kSrcOver_Mode; // fall-back +} + +SkShader::TileMode android_convert_TileRule(WebCore::Image::TileRule rule) +{ + // stretch == clamp + // repeat == repeat + // RoundTile??? + + return WebCore::Image::RepeatTile == rule ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +static U8CPU InvScaleByte(U8CPU component, uint32_t scale) +{ + SkASSERT(component == (uint8_t)component); + return (component * scale + 0x8000) >> 16; +} + +// move this guy into SkColor.h +static SkColor SkPMColorToColor(SkPMColor pm) +{ + if (0 == pm) + return 0; + + unsigned a = SkGetPackedA32(pm); + uint32_t scale = (255 << 16) / a; + + return SkColorSetARGB(a, + InvScaleByte(SkGetPackedR32(pm), scale), + InvScaleByte(SkGetPackedG32(pm), scale), + InvScaleByte(SkGetPackedB32(pm), scale)); +} + +WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm) +{ + SkColor c = SkPMColorToColor(pm); + + return WebCore::Color(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), SkColorGetA(c)); +} + +const static SkColor focusOuterColors[] = { + SkColorSetARGB(0xff, 0xB3, 0x3F, 0x08), // normal focus ring select + SkColorSetARGB(0xff, 0x46, 0xb0, 0x00), // fake focus ring select, for phone, email, text + SkColorSetARGB(0x00, 0x00, 0x00, 0x00), // no ring, for buttons + SkColorSetARGB(0xff, 0xb0, 0x16, 0x00), // invalid focus ring color + SkColorSetARGB(0xff, 0xAD, 0x5C, 0x0A), // normal focus ring pressed + SkColorSetARGB(0xff, 0x36, 0xc0, 0x00) // fake focus ring pressed +}; + +const static SkColor focusInnerColors[] = { + SkColorSetARGB(0xff, 0xFE, 0x92, 0x30), // normal focus ring select + SkColorSetARGB(0xff, 0x8c, 0xd9, 0x00), // fake focus ring select, for phone, email, text + SkColorSetARGB(0x00, 0x00, 0x00, 0x00), // no ring, for buttons + SkColorSetARGB(0xff, 0xd9, 0x2c, 0x00), // invalid focus ring color + SkColorSetARGB(0xff, 0xFE, 0xBD, 0x3A), // normal focus ring pressed + SkColorSetARGB(0xff, 0x7c, 0xe9, 0x00) // fake focus ring pressed +}; + +const static SkColor focusPressedColors[] = { + SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B), // normal focus ring pressed + SkColorSetARGB(0x80, 0x7c, 0xe9, 0x00), // fake focus ring pressed + SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B) // button focus ring pressed +}; + +#define FOCUS_RING_ROUNDEDNESS SkIntToScalar(5) // used to draw corners +#define FOCUS_RING_INNER_DIAMETER SkFixedToScalar(SkIntToFixed(3)>>1) // 3/2 == 1.5 +#define FOCUS_RING_OUTER_OUTSET 2 // used to inflate rects added to region + +void FocusRing::DrawRing(SkCanvas* canvas, + const Vector<WebCore::IntRect>& rects, Flavor flavor) +{ + unsigned rectCount = rects.size(); + SkRegion rgn; + SkPath path; + for (unsigned i = 0; i < rectCount; i++) + { + SkIRect r; + + android_setrect(&r, rects[i]); + r.inset(-FOCUS_RING_OUTER_OUTSET, -FOCUS_RING_OUTER_OUTSET); + rgn.op(r, SkRegion::kUnion_Op); + } + rgn.getBoundaryPath(&path); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setPathEffect(new SkCornerPathEffect(FOCUS_RING_ROUNDEDNESS))->unref(); + if (flavor >= NORMAL_ANIMATING) { // pressed + paint.setColor(focusPressedColors[flavor - NORMAL_ANIMATING]); + canvas->drawPath(path, paint); + } + if (flavor == BUTTON_ANIMATING) + return; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(FOCUS_RING_OUTER_DIAMETER); + paint.setColor(focusOuterColors[flavor]); + canvas->drawPath(path, paint); + paint.setStrokeWidth(FOCUS_RING_INNER_DIAMETER); + paint.setColor(focusInnerColors[flavor]); + canvas->drawPath(path, paint); +} + + diff --git a/WebCore/platform/graphics/android/android_graphics.h b/WebCore/platform/graphics/android/android_graphics.h new file mode 100644 index 0000000..91c56b7 --- /dev/null +++ b/WebCore/platform/graphics/android/android_graphics.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef android_graphics_DEFINED +#define android_graphics_DEFINED + +#include "Color.h" +#include "Image.h" +#include "wtf/Vector.h" + +#include "SkColor.h" +#include "SkPorterDuff.h" +#include "SkScalar.h" +#include "SkShader.h" + +class SkCanvas; +struct SkPoint; +struct SKRect; + +namespace WebCore { + class FloatRect; + class IntPoint; + class IntRect; +} + +SkPoint* android_setpt(SkPoint* dst, const WebCore::IntPoint& src); +SkPoint* android_setpt(SkPoint* dst, const WebCore::FloatPoint& src); +SkRect* android_setrect(SkRect* dst, const WebCore::IntRect& src); +SkRect* android_setrect(SkRect* dst, const WebCore::FloatRect& src); +SkIRect* android_setrect(SkIRect* dst, const WebCore::IntRect& src); +SkIRect* android_setrect(SkIRect* dst, const WebCore::FloatRect& src); +SkIRect* android_setrect_scaled(SkIRect* dst, const WebCore::FloatRect& src, + float sx, float sy); + +SkPorterDuff::Mode android_convert_compositeOp(WebCore::CompositeOperator); +SkShader::TileMode android_convert_TileRule(WebCore::Image::TileRule); + +WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm); + +// Data and methods for focus rings + +// used to inflate node cache entry +#define FOCUS_RING_HIT_TEST_RADIUS SkIntToScalar(5) + +// used to inval rectangle enclosing pressed state of focus ring +#define FOCUS_RING_OUTER_DIAMETER SkFixedToScalar(SkIntToFixed(13)>>2) // 13/4 == 3.25 + +struct FocusRing { +public: + enum Flavor { + NORMAL_FLAVOR, + FAKE_FLAVOR, + BUTTON_NO_RING, + INVALID_FLAVOR, + NORMAL_ANIMATING, + FAKE_ANIMATING, + BUTTON_ANIMATING, + ANIMATING_COUNT = 2 + }; + + static void DrawRing(SkCanvas* , + const Vector<WebCore::IntRect>& rects, Flavor ); +}; + +#endif + diff --git a/WebCore/platform/graphics/cairo/FontCairo.cpp b/WebCore/platform/graphics/cairo/FontCairo.cpp deleted file mode 100644 index 06e926f..0000000 --- a/WebCore/platform/graphics/cairo/FontCairo.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com - * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> - * - * 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "Font.h" - -#include "GlyphBuffer.h" -#include "GraphicsContext.h" -#include "SimpleFontData.h" - -namespace WebCore { - -void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, - int from, int numGlyphs, const FloatPoint& point) const -{ - cairo_t* cr = graphicsContext->platformContext(); - cairo_save(cr); - - // Set the text color to use for drawing. - float red, green, blue, alpha; - Color penColor = graphicsContext->fillColor(); - penColor.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(cr, red, green, blue, alpha); - - font->setFont(cr); - - GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); - - float offset = point.x(); - - for (int i = 0; i < numGlyphs; i++) { - glyphs[i].x = offset; - glyphs[i].y = point.y(); - offset += glyphBuffer.advanceAt(from + i); - } - cairo_show_glyphs(cr, glyphs, numGlyphs); - - cairo_restore(cr); -} - -} diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 90e34e9..08e8616 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -33,7 +33,6 @@ #include "CairoPath.h" #include "FloatRect.h" #include "Font.h" -#include "ImageBuffer.h" #include "IntRect.h" #include "NotImplemented.h" #include "Path.h" @@ -103,13 +102,11 @@ cairo_t* GraphicsContext::platformContext() const void GraphicsContext::savePlatformState() { cairo_save(m_data->cr); - m_data->save(); } void GraphicsContext::restorePlatformState() { cairo_restore(m_data->cr); - m_data->restore(); } // Draws a filled rectangle with a stroked border. @@ -382,7 +379,6 @@ void GraphicsContext::clip(const IntRect& rect) cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); - m_data->clip(rect); } void GraphicsContext::drawFocusRing(const Color& color) @@ -434,6 +430,7 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, if (paintingDisabled()) return; +#if PLATFORM(GTK) cairo_t* cr = m_data->cr; cairo_save(cr); @@ -444,14 +441,13 @@ void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& origin, else cairo_set_source_rgb(cr, 1, 0, 0); -#if PLATFORM(GTK) // We ignore most of the provided constants in favour of the platform style pango_cairo_show_error_underline(cr, origin.x(), origin.y(), width, cMisspellingLineThickness); + + cairo_restore(cr); #else notImplemented(); #endif - - cairo_restore(cr); } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) @@ -484,7 +480,6 @@ void GraphicsContext::translate(float x, float y) cairo_t* cr = m_data->cr; cairo_translate(cr, x, y); - m_data->translate(x, y); } IntPoint GraphicsContext::origin() @@ -548,6 +543,9 @@ void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) notImplemented(); } +#if PLATFORM(GTK) +// FIXME: This should be moved to something like GraphicsContextCairoGTK.cpp, +// as there is a Windows implementation in platform/graphics/win/GraphicsContextCairoWin.cpp void GraphicsContext::concatCTM(const AffineTransform& transform) { if (paintingDisabled()) @@ -556,8 +554,8 @@ void GraphicsContext::concatCTM(const AffineTransform& transform) cairo_t* cr = m_data->cr; const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); cairo_transform(cr, matrix); - m_data->concatCTM(transform); } +#endif void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) { @@ -601,7 +599,6 @@ void GraphicsContext::beginTransparencyLayer(float opacity) cairo_t* cr = m_data->cr; cairo_push_group(cr); m_data->layers.append(opacity); - m_data->beginTransparencyLayer(); } void GraphicsContext::endTransparencyLayer() @@ -614,7 +611,6 @@ void GraphicsContext::endTransparencyLayer() cairo_pop_group_to_source(cr); cairo_paint_with_alpha(cr, m_data->layers.last()); m_data->layers.removeLast(); - m_data->endTransparencyLayer(); } void GraphicsContext::clearRect(const FloatRect& rect) @@ -776,7 +772,6 @@ void GraphicsContext::clip(const Path& path) cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING); cairo_clip(cr); cairo_set_fill_rule(cr, savedFillRule); - m_data->clip(path); } void GraphicsContext::clipOut(const Path& path) @@ -802,7 +797,6 @@ void GraphicsContext::rotate(float radians) return; cairo_rotate(m_data->cr, radians); - m_data->rotate(radians); } void GraphicsContext::scale(const FloatSize& size) @@ -811,7 +805,6 @@ void GraphicsContext::scale(const FloatSize& size) return; cairo_scale(m_data->cr, size.width(), size.height()); - m_data->scale(size); } void GraphicsContext::clipOut(const IntRect& r) @@ -896,35 +889,6 @@ void GraphicsContext::setUseAntialiasing(bool enable) cairo_set_antialias(m_data->cr, enable ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); } -void GraphicsContext::paintBuffer(ImageBuffer* buffer, const IntRect& r) -{ - if (paintingDisabled()) - return; - cairo_surface_t* image = buffer->surface(); - if (!image) - return; - cairo_surface_flush(image); - cairo_surface_reference(image); - cairo_t* cr = platformContext(); - cairo_save(cr); - cairo_translate(cr, r.x(), r.y()); - cairo_set_source_surface(cr, image, 0, 0); - cairo_surface_destroy(image); - cairo_rectangle(cr, 0, 0, r.width(), r.height()); - cairo_fill(cr); - cairo_restore(cr); -} - -void GraphicsContext::drawImage(ImageBuffer* buffer, const FloatRect& srcRect, const FloatRect& dstRect) -{ - cairo_surface_flush(buffer->surface()); - cairo_save(platformContext()); - cairo_set_source_surface(platformContext(), buffer->surface(), srcRect.x(), srcRect.y()); - cairo_rectangle(platformContext(), dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height()); - cairo_fill(platformContext()); - cairo_restore(platformContext()); -} - } // namespace WebCore #endif // PLATFORM(CAIRO) diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index 26db336..f0ef490 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -62,28 +62,8 @@ public: #if PLATFORM(WIN) // On Windows, we need to update the HDC for form controls to draw in the right place. - void save(); - void restore(); - void clip(const IntRect&); - void clip(const Path&); - void scale(const FloatSize&); - void rotate(float); - void translate(float, float); - void concatCTM(const AffineTransform&); void beginTransparencyLayer() { m_transparencyCount++; } void endTransparencyLayer() { m_transparencyCount--; } -#else - // On everything else, we do nothing. - void save() {} - void restore() {} - void clip(const IntRect&) {} - void clip(const Path&) {} - void scale(const FloatSize&) {} - void rotate(float) {} - void translate(float, float) {} - void concatCTM(const AffineTransform&) {} - void beginTransparencyLayer() {} - void endTransparencyLayer() {} #endif cairo_t* cr; diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 392e9df..776529e 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -28,10 +28,8 @@ #include "ImageBuffer.h" #include "GraphicsContext.h" -#include "ImageData.h" -#include "NotImplemented.h" - #include <cairo.h> +#include "NotImplemented.h" using namespace std; @@ -74,15 +72,5 @@ cairo_surface_t* ImageBuffer::surface() const return m_surface; } -PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const -{ - notImplemented(); - return 0; -} - -void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&) -{ - notImplemented(); -} } diff --git a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp index 66e39ba..8cc15b3 100644 --- a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp @@ -26,20 +26,18 @@ #include "config.h" #include "ImageSource.h" +#include "SharedBuffer.h" #if PLATFORM(CAIRO) -#include "BMPImageDecoder.h" #include "GIFImageDecoder.h" -#include "ICOImageDecoder.h" #include "JPEGImageDecoder.h" #include "PNGImageDecoder.h" -#include "SharedBuffer.h" -#include <cairo.h> - -#if !PLATFORM(WIN) +#include "BMPImageDecoder.h" +#include "ICOImageDecoder.h" #include "XBMImageDecoder.h" -#endif + +#include <cairo.h> namespace WebCore { @@ -80,20 +78,17 @@ ImageDecoder* createDecoder(const Vector<char>& data) !memcmp(contents, "\000\000\002\000", 4)) return new ICOImageDecoder(); -#if !PLATFORM(WIN) // XBMs require 8 bytes of info. if (length >= 8 && strncmp(contents, "#define ", 8) == 0) return new XBMImageDecoder(); -#endif // Give up. We don't know what the heck this is. return 0; } ImageSource::ImageSource() - : m_decoder(0) -{ -} + : m_decoder(0) +{} ImageSource::~ImageSource() { diff --git a/WebCore/platform/graphics/cairo/PathCairo.cpp b/WebCore/platform/graphics/cairo/PathCairo.cpp index 8d17567..e4c9f72 100644 --- a/WebCore/platform/graphics/cairo/PathCairo.cpp +++ b/WebCore/platform/graphics/cairo/PathCairo.cpp @@ -76,15 +76,15 @@ void Path::clear() bool Path::isEmpty() const { + // FIXME: if/when the patch to get current pt return status is applied + // double dx,dy; + // return cairo_get_current_point(cr, &dx, &dy); + cairo_t* cr = platformPath()->m_cr; -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,5,10) - return !cairo_has_current_point(cr); -#else cairo_path_t* p = cairo_copy_path(cr); bool hasData = p->num_data; cairo_path_destroy(p); return !hasData; -#endif } void Path::translate(const FloatSize& p) diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index de546ac..c490dcb 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -30,14 +30,10 @@ #include "AffineTransform.h" #include "FloatConversion.h" #include "GraphicsContextPlatformPrivateCG.h" -#include "ImageBuffer.h" #include "KURL.h" #include "Path.h" -#include <CoreGraphics/CGBitmapContext.h> #include <CoreGraphics/CGPDFContext.h> #include <wtf/MathExtras.h> -#include <wtf/OwnArrayPtr.h> -#include <wtf/RetainPtr.h> using namespace std; @@ -928,49 +924,6 @@ void GraphicsContext::setCompositeOperation(CompositeOperator mode) CGContextSetBlendMode(platformContext(), target); } #endif - -void GraphicsContext::paintBuffer(ImageBuffer* buffer, const IntRect& r) -{ - CGContextRef context = buffer->context()->platformContext(); - if (!context) - return; - CGContextFlush(context); - if (CGImageRef image = CGBitmapContextCreateImage(context)) { - CGContextDrawImage(platformContext(), roundToDevicePixels(r), image); - CGImageRelease(image); - } -} - -void GraphicsContext::drawImage(ImageBuffer* buffer, const FloatRect& srcRect, const FloatRect& destRect) -{ - CGContextRef context = buffer->context()->platformContext(); - CGContextFlush(context); - RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context)); - float iw = CGImageGetWidth(image.get()); - float ih = CGImageGetHeight(image.get()); - if (srcRect.x() == 0 && srcRect.y() == 0 && iw == srcRect.width() && ih == srcRect.height()) { - // Fast path, yay! - CGContextDrawImage(platformContext(), destRect, image.get()); - } else { - // Slow path, boo! - // FIXME: We can do this without creating a separate image - - size_t csw = static_cast<size_t>(ceilf(srcRect.width())); - size_t csh = static_cast<size_t>(ceilf(srcRect.height())); - - RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); - size_t bytesPerRow = csw * 4; - OwnArrayPtr<char> buffer(new char[csh * bytesPerRow]); - RetainPtr<CGContextRef> clippedSourceContext(AdoptCF, CGBitmapContextCreate(buffer.get(), csw, csh, - 8, bytesPerRow, colorSpace.get(), kCGImageAlphaPremultipliedLast)); - CGContextTranslateCTM(clippedSourceContext.get(), -srcRect.x(), -srcRect.y()); - CGContextDrawImage(clippedSourceContext.get(), CGRectMake(0, 0, iw, ih), image.get()); - - RetainPtr<CGImageRef> clippedSourceImage(AdoptCF, CGBitmapContextCreateImage(clippedSourceContext.get())); - - CGContextDrawImage(platformContext(), destRect, clippedSourceImage.get()); - } -} - + } diff --git a/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/WebCore/platform/graphics/cg/ImageBufferCG.cpp index 7cb28e3..2d1ac01 100644 --- a/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) 2008 Apple, Inc * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,7 +27,6 @@ #include "ImageBuffer.h" #include "GraphicsContext.h" -#include "ImageData.h" #include <ApplicationServices/ApplicationServices.h> #include <wtf/Assertions.h> @@ -75,7 +73,6 @@ ImageBuffer::ImageBuffer(void* imageData, const IntSize& size, auto_ptr<Graphics , m_context(context.release()) , m_cgImage(0) { - ASSERT((reinterpret_cast<size_t>(imageData) & 2) == 0); } ImageBuffer::~ImageBuffer() @@ -101,113 +98,4 @@ CGImageRef ImageBuffer::cgImage() const return m_cgImage; } -PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const -{ - if (!m_data) - return 0; - - PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); - unsigned char* data = result->data()->data().data(); - - if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) - memset(data, 0, result->data()->length()); - - int originx = rect.x(); - int destx = 0; - if (originx < 0) { - destx = -originx; - originx = 0; - } - int endx = rect.x() + rect.width(); - if (endx > m_size.width()) - endx = m_size.width(); - int numColumns = endx - originx; - - int originy = rect.y(); - int desty = 0; - if (originy < 0) { - desty = -originy; - originy = 0; - } - int endy = rect.y() + rect.height(); - if (endy > m_size.height()) - endy = m_size.height(); - int numRows = endy - originy; - - unsigned srcBytesPerRow = 4 * m_size.width(); - unsigned destBytesPerRow = 4 * rect.width(); - - // m_size.height() - originy to handle the accursed flipped y axis in CG backing store - unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data) + (m_size.height() - originy - 1) * srcBytesPerRow + originx * 4; - unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; - for (int y = 0; y < numRows; ++y) { - for (int x = 0; x < numColumns; x++) { - int basex = x * 4; - if (unsigned char alpha = srcRows[basex + 3]) { - destRows[0] = (srcRows[basex] * 255) / alpha; - destRows[1] = (srcRows[basex + 1] * 255) / alpha; - destRows[2] = (srcRows[basex + 2] * 255) / alpha; - destRows[3] = alpha; - } else { - reinterpret_cast<uint32_t*>(destRows)[0] = reinterpret_cast<uint32_t*>(srcRows)[0]; - } - destRows += 4; - } - srcRows -= srcBytesPerRow; - } - return result; -} - -void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) -{ - ASSERT(sourceRect.width() > 0); - ASSERT(sourceRect.height() > 0); - - int originx = sourceRect.x(); - int destx = destPoint.x() + sourceRect.x(); - ASSERT(destx >= 0); - ASSERT(destx < m_size.width()); - ASSERT(originx >= 0); - ASSERT(originx <= sourceRect.right()); - - int endx = destPoint.x() + sourceRect.right(); - ASSERT(endx <= m_size.width()); - - int numColumns = endx - destx; - - int originy = sourceRect.y(); - int desty = destPoint.y() + sourceRect.y(); - ASSERT(desty >= 0); - ASSERT(desty < m_size.height()); - ASSERT(originy >= 0); - ASSERT(originy <= sourceRect.bottom()); - - int endy = destPoint.y() + sourceRect.bottom(); - ASSERT(endy <= m_size.height()); - int numRows = endy - desty; - - unsigned srcBytesPerRow = 4 * source->width(); - unsigned destBytesPerRow = 4 * m_size.width(); - - unsigned char* srcRows = source->data()->data().data() + originy * srcBytesPerRow + originx * 4; - - // -desty to handle the accursed flipped y axis - unsigned char* destRows = reinterpret_cast<unsigned char*>(m_data) + (m_size.height() - desty - 1) * destBytesPerRow + destx * 4; - for (int y = 0; y < numRows; ++y) { - for (int x = 0; x < numColumns; x++) { - unsigned char alpha = srcRows[x * 4 + 3]; - if (alpha != 255) { - destRows[x * 4 + 0] = (srcRows[0] * alpha) / 255; - destRows[x * 4 + 1] = (srcRows[1] * alpha) / 255; - destRows[x * 4 + 2] = (srcRows[2] * alpha) / 255; - destRows[x * 4 + 3] = alpha; - } else { - reinterpret_cast<uint32_t*>(destRows + x * 4)[0] = reinterpret_cast<uint32_t*>(srcRows + x * 4)[0]; - } - } - destRows -= destBytesPerRow; - srcRows += srcBytesPerRow; - } -} - } diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.cpp b/WebCore/platform/graphics/cg/ImageSourceCG.cpp index 08e8172..2bfc204 100644 --- a/WebCore/platform/graphics/cg/ImageSourceCG.cpp +++ b/WebCore/platform/graphics/cg/ImageSourceCG.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,17 +25,15 @@ #include "config.h" #include "ImageSource.h" +#include "SharedBuffer.h" #if PLATFORM(CG) #include "IntSize.h" -#include "SharedBuffer.h" #include <ApplicationServices/ApplicationServices.h> namespace WebCore { -static const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32"); - ImageSource::ImageSource() : m_decoder(0) { @@ -54,13 +52,15 @@ void ImageSource::clear() } } +const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32"); + CFDictionaryRef imageSourceOptions() { static CFDictionaryRef options; if (!options) { - const void* keys[2] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 }; - const void* values[2] = { kCFBooleanTrue, kCFBooleanTrue }; + const void *keys[2] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32 }; + const void *values[2] = { kCFBooleanTrue, kCFBooleanTrue }; options = CFDictionaryCreate(NULL, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } @@ -154,20 +154,7 @@ size_t ImageSource::frameCount() const CGImageRef ImageSource::createFrameAtIndex(size_t index) { - CGImageRef image = CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions()); - CFStringRef imageUTI = CGImageSourceGetType(m_decoder); - static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image"); - if (!imageUTI || !CFEqual(imageUTI, xbmUTI)) - return image; - - // If it is an xbm image, mask out all the white areas to render them transparent. - const CGFloat maskingColors[6] = {255, 255, 255, 255, 255, 255}; - CGImageRef maskedImage = CGImageCreateWithMaskingColors(image, maskingColors); - if (!maskedImage) - return image; - - CGImageRelease(image); - return maskedImage; + return CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions()); } bool ImageSource::frameIsCompleteAtIndex(size_t index) diff --git a/WebCore/platform/graphics/gtk/FontGtk.cpp b/WebCore/platform/graphics/gtk/FontGtk.cpp index 387b61c..5d50c6e 100644 --- a/WebCore/platform/graphics/gtk/FontGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontGtk.cpp @@ -159,6 +159,34 @@ static void setPangoAttributes(const Font* font, const TextRun& run, PangoLayout pango_context_set_base_dir(pangoContext, direction); } +void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) const +{ + cairo_t* cr = graphicsContext->platformContext(); + cairo_save(cr); + + // Set the text color to use for drawing. + float red, green, blue, alpha; + Color penColor = graphicsContext->fillColor(); + penColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha); + + font->setFont(cr); + + GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); + + float offset = point.x(); + + for (int i = 0; i < numGlyphs; i++) { + glyphs[i].x = offset; + glyphs[i].y = point.y(); + offset += glyphBuffer.advanceAt(from + i); + } + cairo_show_glyphs(cr, glyphs, numGlyphs); + + cairo_restore(cr); +} + void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { cairo_t* cr = context->platformContext(); diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp index b28146f..ef5cb4a 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp @@ -42,7 +42,7 @@ FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const { FontPlatformData::init(); - CString familyNameString = familyName.string().utf8(); + CString familyNameString = familyName.domString().utf8(); const char* fcfamily = familyNameString.data(); int fcslant = FC_SLANT_ROMAN; int fcweight = FC_WEIGHT_NORMAL; diff --git a/WebCore/platform/graphics/gtk/IconGtk.cpp b/WebCore/platform/graphics/gtk/IconGtk.cpp index a8e6536..c6e9a14 100644 --- a/WebCore/platform/graphics/gtk/IconGtk.cpp +++ b/WebCore/platform/graphics/gtk/IconGtk.cpp @@ -41,8 +41,7 @@ namespace WebCore { Icon::Icon() - : RefCounted<Icon>(0) - , m_icon(0) + : m_icon(0) { notImplemented(); } diff --git a/WebCore/platform/graphics/mac/IconMac.mm b/WebCore/platform/graphics/mac/IconMac.mm index b630ba6..cda73a0 100644 --- a/WebCore/platform/graphics/mac/IconMac.mm +++ b/WebCore/platform/graphics/mac/IconMac.mm @@ -29,13 +29,11 @@ namespace WebCore { Icon::Icon() - : RefCounted<Icon>(0) { } Icon::Icon(NSImage *image) - : RefCounted<Icon>(0) - , m_nsImage(image) + : m_nsImage(image) { // Need this because WebCore uses AppKit's flipped coordinate system exclusively. [image setFlipped:YES]; diff --git a/WebCore/platform/graphics/mac/ImageMac.mm b/WebCore/platform/graphics/mac/ImageMac.mm index 121eb78..0b14d71 100644 --- a/WebCore/platform/graphics/mac/ImageMac.mm +++ b/WebCore/platform/graphics/mac/ImageMac.mm @@ -31,6 +31,7 @@ #import "GraphicsContext.h" #import "PlatformString.h" #import "WebCoreFrameBridge.h" +#import "WebCoreSystemInterface.h" namespace WebCore { diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index 541d5f6..85c7a9e 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -163,12 +163,11 @@ void MediaPlayerPrivate::createQTMovie(const String& url) m_qtMovie = 0; // Disable streaming support for now, <rdar://problem/5693967> - if (protocolIs(url, "rtsp")) + if (url.startsWith("rtsp:")) return; - - NSURL *cocoaURL = KURL(url); - NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: - cocoaURL, QTMovieURLAttribute, + + NSDictionary* movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys: + KURL(url.deprecatedString()).getNSURL(), QTMovieURLAttribute, [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute, [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute, nil]; @@ -177,7 +176,7 @@ void MediaPlayerPrivate::createQTMovie(const String& url) m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]); // FIXME: Find a proper way to detect streaming content. - m_isStreaming = protocolIs(url, "rtsp"); + m_isStreaming = url.startsWith("rtsp:"); if (!m_qtMovie) return; diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index 5d90514..1f45c94 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -62,7 +62,7 @@ bool initFontData(SimpleFontData* fontData) ATSUStyle fontStyle; if (ATSUCreateStyle(&fontStyle) != noErr) return false; - + ATSUFontID fontId = fontData->m_font.m_atsuFontID; if (!fontId) { ATSUDisposeStyle(fontStyle); @@ -96,45 +96,6 @@ static NSString *webFallbackFontFamily(void) return webFallbackFontFamily.get(); } -#if !ERROR_DISABLED -#ifdef __LP64__ -static NSString* pathFromFont(NSFont*) -{ - // FMGetATSFontRefFromFont is not available in 64-bit. As pathFromFont is only used for debugging - // purposes, returning nil is acceptable. - return nil; -} -#else -static NSString* pathFromFont(NSFont *font) -{ - ATSFontRef atsFont = FMGetATSFontRefFromFont(wkGetNSFontATSUFontId(font)); - FSRef fileRef; - -#ifndef BUILDING_ON_TIGER - OSStatus status = ATSFontGetFileReference(atsFont, &fileRef); - if (status != noErr) - return nil; -#else - FSSpec oFile; - OSStatus status = ATSFontGetFileSpecification(atsFont, &oFile); - if (status != noErr) - return nil; - - status = FSpMakeFSRef(&oFile, &fileRef); - if (status != noErr) - return nil; -#endif - - UInt8 filePathBuffer[PATH_MAX]; - status = FSRefMakePath(&fileRef, filePathBuffer, PATH_MAX); - if (status == noErr) - return [NSString stringWithUTF8String:(const char*)filePathBuffer]; - - return nil; -} -#endif // __LP64__ -#endif // !ERROR_DISABLED - void SimpleFontData::platformInit() { m_styleGroup = 0; @@ -167,7 +128,7 @@ void SimpleFontData::platformInit() #endif m_font.setFont([[NSFontManager sharedFontManager] convertFont:m_font.font() toFamily:fallbackFontFamily]); #if !ERROR_DISABLED - NSString *filePath = pathFromFont(initialFont.get()); + NSString *filePath = wkPathFromFont(initialFont.get()); if (!filePath) filePath = @"not known"; #endif @@ -204,15 +165,7 @@ void SimpleFontData::platformInit() int iAscent; int iDescent; int iLineGap; -#ifdef BUILDING_ON_TIGER - wkGetFontMetrics(m_font.m_cgFont, &iAscent, &iDescent, &iLineGap, &m_unitsPerEm); -#else - iAscent = CGFontGetAscent(m_font.m_cgFont); - iDescent = CGFontGetDescent(m_font.m_cgFont); - iLineGap = CGFontGetLeading(m_font.m_cgFont); - m_unitsPerEm = CGFontGetUnitsPerEm(m_font.m_cgFont); -#endif - + wkGetFontMetrics(m_font.m_cgFont, &iAscent, &iDescent, &iLineGap, &m_unitsPerEm); float pointSize = m_font.m_size; float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize; float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize; diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index ec35d96..cf097c8 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -36,7 +36,6 @@ #include "Path.h" #include "Color.h" #include "GraphicsContext.h" -#include "ImageBuffer.h" #include "Font.h" #include "Pen.h" #include "NotImplemented.h" @@ -46,7 +45,6 @@ #include <QPolygonF> #include <QPainterPath> #include <QPaintDevice> -#include <QPixmap> #include <QDebug> #ifndef M_PI @@ -907,37 +905,6 @@ void GraphicsContext::setUseAntialiasing(bool enable) m_data->p()->setRenderHint(QPainter::Antialiasing, enable); } -void GraphicsContext::paintBuffer(ImageBuffer* buffer, const IntRect& r) -{ - if (paintingDisabled()) - return; - QPixmap pixmap = *buffer->pixmap(); - if (pixmap.isNull()) - return; - QPainter* painter = platformContext(); - QPen currentPen = painter->pen(); - qreal currentOpacity = painter->opacity(); - QBrush currentBrush = painter->brush(); - QBrush currentBackground = painter->background(); - if (painter->isActive()) - painter->end(); - static_cast<QPainter*>(painter)->drawPixmap(r, pixmap); - painter->begin(&pixmap); - painter->setPen(currentPen); - painter->setBrush(currentBrush); - painter->setOpacity(currentOpacity); - painter->setBackground(currentBackground); -} - -void GraphicsContext::drawImage(ImageBuffer* buffer, const FloatRect& srcRect, const FloatRect& dstRect) -{ - QPainter* painter = static_cast<QPainter*>(platformContext()); - QPixmap px = *buffer->pixmap(); - if (px.isNull()) - return; - painter->drawPixmap(dstRect, px, srcRect); -} - } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/graphics/qt/IconQt.cpp b/WebCore/platform/graphics/qt/IconQt.cpp index c9c900b..e39d161 100644 --- a/WebCore/platform/graphics/qt/IconQt.cpp +++ b/WebCore/platform/graphics/qt/IconQt.cpp @@ -22,6 +22,7 @@ #include "Icon.h" #include "GraphicsContext.h" +#include "DeprecatedString.h" #include "PlatformString.h" #include "IntRect.h" #include "NotImplemented.h" @@ -34,7 +35,6 @@ namespace WebCore { Icon::Icon() - : RefCounted<Icon>(0) { } diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index 449677e..c95d8c8 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -27,8 +27,6 @@ #include "ImageBuffer.h" #include "GraphicsContext.h" -#include "ImageData.h" -#include "NotImplemented.h" #include <QPainter> #include <QPixmap> @@ -71,15 +69,4 @@ QPixmap* ImageBuffer::pixmap() const return &m_pixmap; } -PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const -{ - notImplemented(); - return 0; -} - -void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&) -{ - notImplemented(); -} - } diff --git a/WebCore/platform/graphics/win/FontCacheWin.cpp b/WebCore/platform/graphics/win/FontCacheWin.cpp index 1ee6818..502863b 100644 --- a/WebCore/platform/graphics/win/FontCacheWin.cpp +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,10 +35,8 @@ #include "UnicodeRange.h" #include <windows.h> #include <mlang.h> -#if PLATFORM(CG) #include <ApplicationServices/ApplicationServices.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> -#endif using std::min; @@ -47,9 +45,7 @@ namespace WebCore void FontCache::platformInit() { -#if PLATFORM(CG) wkSetUpFontCache(1536 * 1024 * 4); // This size matches Mac. -#endif } IMLangFontLink2* FontCache::getFontLinkInterface() @@ -418,14 +414,7 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD FontPlatformData* result = new FontPlatformData(hfont, fontDescription.computedPixelSize(), fontDescription.bold(), fontDescription.italic(), useGDI); - -#if PLATFORM(CG) - bool fontCreationFailed = !result->cgFont(); -#elif PLATFORM(CAIRO) - bool fontCreationFailed = !result->fontFace(); -#endif - - if (fontCreationFailed) { + if (!result->cgFont()) { // The creation of the CGFontRef failed for some reason. We already asserted in debug builds, but to make // absolutely sure that we don't use this font, go ahead and return 0 so that we can fall back to the next // font. diff --git a/WebCore/platform/wx/wxcode/non-kerned-drawing.h b/WebCore/platform/graphics/win/FontCairoWin.cpp index d005985..302e79d 100644 --- a/WebCore/platform/wx/wxcode/non-kerned-drawing.h +++ b/WebCore/platform/graphics/win/FontCairoWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Kevin Watters, Kevin Ollivier. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,17 +20,23 @@ * 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 "Font.h"
-#include "GlyphBuffer.h"
-
-#include <wx/defs.h>
-#include <wx/dcclient.h>
-
-namespace WebCore {
-
-extern void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point);
-
-}
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Font.h" + +#include "GlyphBuffer.h" +#include "GraphicsContext.h" +#include "NotImplemented.h" +#include "SimpleFontData.h" + +namespace WebCore { + +void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) const +{ + notImplemented(); +} + +} diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.h b/WebCore/platform/graphics/win/FontCustomPlatformData.h index 909b79e..9f30424 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.h @@ -32,9 +32,8 @@ class SharedBuffer; struct FontCustomPlatformData : Noncopyable { FontCustomPlatformData(CGFontRef cgFont) - : m_cgFont(cgFont) - { - } + : m_cgFont(cgFont) + {} ~FontCustomPlatformData(); FontPlatformData fontPlatformData(int size, bool bold, bool italic); @@ -42,7 +41,7 @@ struct FontCustomPlatformData : Noncopyable { CGFontRef m_cgFont; }; -FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); +FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer); } diff --git a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp deleted file mode 100644 index e54d85a..0000000 --- a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "FontCustomPlatformDataCairo.h" - -#include "SharedBuffer.h" -#include "FontPlatformData.h" -#include <wtf/RetainPtr.h> - -namespace WebCore { - -FontCustomPlatformDataCairo::~FontCustomPlatformDataCairo() -{ - cairo_font_face_destroy(m_fontFace); -} - -FontPlatformData FontCustomPlatformDataCairo::fontPlatformData(int size, bool bold, bool italic) -{ - return FontPlatformData(m_fontFace, size, bold, italic); -} - -static void releaseData(void* data) -{ - static_cast<SharedBuffer*>(data)->deref(); -} - -FontCustomPlatformDataCairo* createFontCustomPlatformData(SharedBuffer* buffer) -{ - ASSERT_ARG(buffer, buffer); - - buffer->ref(); - HFONT font = reinterpret_cast<HFONT>(buffer); - cairo_font_face_t* fontFace = cairo_win32_font_face_create_for_hfont(font); - if (!fontFace) - return 0; - - static cairo_user_data_key_t bufferKey; - cairo_font_face_set_user_data(fontFace, &bufferKey, buffer, releaseData); - - return new FontCustomPlatformDataCairo(fontFace); -} - -} diff --git a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h b/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h deleted file mode 100644 index 87794b5..0000000 --- a/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef FontCustomPlatformDataCairo_h -#define FontCustomPlatformDataCairo_h - -#include <wtf/Noncopyable.h> - -#include <cairo.h> - -namespace WebCore { - -class FontPlatformData; -class SharedBuffer; - -struct FontCustomPlatformDataCairo : Noncopyable { - FontCustomPlatformDataCairo(cairo_font_face_t* fontFace) - : m_fontFace(fontFace) - { - } - ~FontCustomPlatformDataCairo(); - - FontPlatformData fontPlatformData(int size, bool bold, bool italic); - - cairo_font_face_t* m_fontFace; -}; - -FontCustomPlatformDataCairo* createFontCustomPlatformData(SharedBuffer*); - -} - -#endif diff --git a/WebCore/platform/graphics/win/FontPlatformData.h b/WebCore/platform/graphics/win/FontPlatformData.h index 5bbda2f..15d23cd 100644 --- a/WebCore/platform/graphics/win/FontPlatformData.h +++ b/WebCore/platform/graphics/win/FontPlatformData.h @@ -2,7 +2,7 @@ * This file is part of the internal font implementation. It should not be included by anyone other than * FontMac.cpp, FontWin.cpp and Font.cpp. * - * Copyright (C) 2006, 2007, 2008 Apple Inc. + * Copyright (C) 2006, 2007 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,12 +26,8 @@ #include "StringImpl.h" -#if PLATFORM(CAIRO) -#include <cairo-win32.h> -#endif - -typedef struct HFONT__* HFONT; -typedef struct CGFont* CGFontRef; +typedef struct HFONT__ *HFONT; +typedef struct CGFont *CGFontRef; namespace WebCore { @@ -44,53 +40,30 @@ public: // Used for deleted values in the font cache's hash tables. FontPlatformData(Deleted) - : m_font((HFONT)-1) -#if PLATFORM(CG) - , m_cgFont(0) -#elif PLATFORM(CAIRO) - , m_fontFace(0) -#endif - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_useGDI(false) - { - } + : m_font((HFONT)-1) + , m_cgFont(NULL) + , m_size(0) + , m_syntheticBold(false) + , m_syntheticOblique(false) + , m_useGDI(false) + {} FontPlatformData() - : m_font(0) -#if PLATFORM(CG) - , m_cgFont(0) -#elif PLATFORM(CAIRO) - , m_fontFace(0) -#endif - , m_size(0) - , m_syntheticBold(false) - , m_syntheticOblique(false) - , m_useGDI(false) - { - } + : m_font(0) + , m_cgFont(NULL) + , m_size(0) + , m_syntheticBold(false) + , m_syntheticOblique(false) + , m_useGDI(false) + {} FontPlatformData(HFONT, float size, bool bold, bool oblique, bool useGDI); FontPlatformData(float size, bool bold, bool oblique); - -#if PLATFORM(CG) FontPlatformData(CGFontRef, float size, bool bold, bool oblique); -#elif PLATFORM(CAIRO) - FontPlatformData(cairo_font_face_t*, float size, bool bold, bool oblique); -#endif ~FontPlatformData(); - void platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName); - HFONT hfont() const { return m_font; } -#if PLATFORM(CG) CGFontRef cgFont() const { return m_cgFont; } -#elif PLATFORM(CAIRO) - void setFont(cairo_t* ft) const; - cairo_font_face_t* fontFace() const { return m_fontFace; } - cairo_scaled_font_t* scaledFont() const { return m_scaledFont; } -#endif float size() const { return m_size; } void setSize(float size) { m_size = size; } @@ -105,26 +78,14 @@ public: bool operator==(const FontPlatformData& other) const { - return m_font == other.m_font && -#if PLATFORM(CG) - m_cgFont == other.m_cgFont && -#elif PLATFORM(CAIRO) - m_fontFace == other.m_fontFace && - m_scaledFont == other.m_scaledFont && -#endif - m_size == other.m_size && + return m_font == other.m_font && m_cgFont ==other.m_cgFont && m_size == other.m_size && m_syntheticBold == other.m_syntheticBold && m_syntheticOblique == other.m_syntheticOblique && m_useGDI == other.m_useGDI; } private: HFONT m_font; -#if PLATFORM(CG) CGFontRef m_cgFont; -#elif PLATFORM(CAIRO) - cairo_font_face_t* m_fontFace; - cairo_scaled_font_t* m_scaledFont; -#endif float m_size; bool m_syntheticBold; diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp deleted file mode 100644 index ddd3104..0000000 --- a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of the internal font implementation. It should not be included by anyone other than - * FontMac.cpp, FontWin.cpp and Font.cpp. - * - * Copyright (C) 2006, 2007, 2008 Apple Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "FontPlatformData.h" - -#include "PlatformString.h" -#include "StringHash.h" -#include <ApplicationServices/ApplicationServices.h> -#include <wtf/HashMap.h> -#include <wtf/RetainPtr.h> -#include <wtf/Vector.h> - -using std::min; - -namespace WebCore { - -static inline USHORT readBigEndianWord(const BYTE* word) { return (word[0] << 8) | word[1]; } - -static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) -{ - const DWORD cMaxNameTableSize = 1024 * 1024; - - static HashMap<String, RetainPtr<CFStringRef> > nameMap; - - // Check our hash first. - String faceString(faceName); - RetainPtr<CFStringRef> result = nameMap.get(faceString); - if (result) - return result.get(); - - // We need to obtain the PostScript name from the name table and use it instead,. - DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards - if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize) - return NULL; - - Vector<BYTE> bufferVector(bufferSize); - BYTE* buffer = bufferVector.data(); - if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR) - return NULL; - - if (bufferSize < 6) - return NULL; - - USHORT numberOfRecords = readBigEndianWord(buffer + 2); - UINT stringsOffset = readBigEndianWord(buffer + 4); - if (bufferSize < stringsOffset) - return NULL; - - BYTE* strings = buffer + stringsOffset; - - // Now walk each name record looking for a Mac or Windows PostScript name. - UINT offset = 6; - for (int i = 0; i < numberOfRecords; i++) { - if (bufferSize < offset + 12) - return NULL; - - USHORT platformID = readBigEndianWord(buffer + offset); - USHORT encodingID = readBigEndianWord(buffer + offset + 2); - USHORT languageID = readBigEndianWord(buffer + offset + 4); - USHORT nameID = readBigEndianWord(buffer + offset + 6); - USHORT length = readBigEndianWord(buffer + offset + 8); - USHORT nameOffset = readBigEndianWord(buffer + offset + 10); - - if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) { - // This is a Windows PostScript name and is therefore UTF-16. - // Pass Big Endian as the encoding. - if (bufferSize < stringsOffset + nameOffset + length) - return NULL; - result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false)); - break; - } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) { - // This is a Mac PostScript name and is therefore ASCII. - // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html - if (bufferSize < stringsOffset + nameOffset + length) - return NULL; - result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false)); - break; - } - - offset += 12; - } - - if (result) - nameMap.set(faceString, result); - return result.get(); -} - -void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName) -{ - // Try the face name first. Windows may end up localizing this name, and CG doesn't know about - // the localization. If the create fails, we'll try the PostScript name. - RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName))); - m_cgFont = CGFontCreateWithFontName(fullName.get()); - if (!m_cgFont) { - CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc); - if (postScriptName) { - m_cgFont = CGFontCreateWithFontName(postScriptName); - ASSERT(m_cgFont); - } - } -} - -FontPlatformData::FontPlatformData(CGFontRef font, float size, bool bold, bool oblique) - : m_font(0) - , m_size(size) - , m_cgFont(font) - , m_syntheticBold(bold) - , m_syntheticOblique(oblique) - , m_useGDI(false) -{ -} - -} diff --git a/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp deleted file mode 100644 index 438d0a9..0000000 --- a/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This file is part of the internal font implementation. It should not be included by anyone other than - * FontMac.cpp, FontWin.cpp and Font.cpp. - * - * Copyright (C) 2006, 2007, 2008 Apple Inc. - * Copyright (C) 2007 Alp Toker - * Copyright (C) 2008 Brent Fulgham - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "FontPlatformData.h" - -#include "PlatformString.h" -#include "StringHash.h" -#include <wtf/HashMap.h> -#include <wtf/RetainPtr.h> -#include <wtf/Vector.h> - -#include <cairo-win32.h> - -using std::min; - -namespace WebCore { - -void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* faceName) -{ - m_fontFace = cairo_win32_font_face_create_for_hfont(font); - cairo_matrix_t sizeMatrix, ctm; - cairo_matrix_init_identity(&ctm); - cairo_matrix_init_scale(&sizeMatrix, size, size); - - static cairo_font_options_t* fontOptions = 0; - if (!fontOptions) - { - fontOptions = cairo_font_options_create(); - cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_SUBPIXEL); - } - - m_scaledFont = cairo_scaled_font_create(m_fontFace, &sizeMatrix, &ctm, fontOptions); -} - -FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool oblique) - : m_font(0) - , m_size(size) - , m_fontFace(fontFace) - , m_scaledFont(0) - , m_syntheticBold(bold) - , m_syntheticOblique(oblique) - , m_useGDI(false) -{ - cairo_matrix_t fontMatrix; - cairo_matrix_init_scale(&fontMatrix, size, size); - cairo_matrix_t ctm; - cairo_matrix_init_identity(&ctm); - cairo_font_options_t* options = cairo_font_options_create(); - - // We force antialiasing and disable hinting to provide consistent - // typographic qualities for custom fonts on all platforms. - cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); - - m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); - cairo_font_options_destroy(options); -} - -void FontPlatformData::setFont(cairo_t* cr) const -{ - ASSERT(m_scaledFont); - - cairo_set_scaled_font(cr, m_scaledFont); -} - -} diff --git a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp index f209dbf..a5fab36 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataWin.cpp @@ -2,8 +2,7 @@ * This file is part of the internal font implementation. It should not be included by anyone other than * FontMac.cpp, FontWin.cpp and Font.cpp. * - * Copyright (C) 2006, 2007, 2008 Apple Inc. - * Copyright (C) 2008 Brent Fulgham + * Copyright (C) 2006, 2007 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,6 +26,7 @@ #include "PlatformString.h" #include "StringHash.h" +#include <ApplicationServices/ApplicationServices.h> #include <wtf/HashMap.h> #include <wtf/RetainPtr.h> #include <wtf/Vector.h> @@ -39,6 +39,77 @@ static const int Bold = (1 << 0); static const int Italic = (1 << 1); static const int BoldOblique = (1 << 2); +static inline USHORT readBigEndianWord(const BYTE* word) { return (word[0] << 8) | word[1]; } + +static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) +{ + const DWORD cMaxNameTableSize = 1024 * 1024; + + static HashMap<String, RetainPtr<CFStringRef> > nameMap; + + // Check our hash first. + String faceString(faceName); + RetainPtr<CFStringRef> result = nameMap.get(faceString); + if (result) + return result.get(); + + // We need to obtain the PostScript name from the name table and use it instead,. + DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards + if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize) + return NULL; + + Vector<BYTE> bufferVector(bufferSize); + BYTE* buffer = bufferVector.data(); + if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR) + return NULL; + + if (bufferSize < 6) + return NULL; + + USHORT numberOfRecords = readBigEndianWord(buffer + 2); + UINT stringsOffset = readBigEndianWord(buffer + 4); + if (bufferSize < stringsOffset) + return NULL; + + BYTE* strings = buffer + stringsOffset; + + // Now walk each name record looking for a Mac or Windows PostScript name. + UINT offset = 6; + for (int i = 0; i < numberOfRecords; i++) { + if (bufferSize < offset + 12) + return NULL; + + USHORT platformID = readBigEndianWord(buffer + offset); + USHORT encodingID = readBigEndianWord(buffer + offset + 2); + USHORT languageID = readBigEndianWord(buffer + offset + 4); + USHORT nameID = readBigEndianWord(buffer + offset + 6); + USHORT length = readBigEndianWord(buffer + offset + 8); + USHORT nameOffset = readBigEndianWord(buffer + offset + 10); + + if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) { + // This is a Windows PostScript name and is therefore UTF-16. + // Pass Big Endian as the encoding. + if (bufferSize < stringsOffset + nameOffset + length) + return NULL; + result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false)); + break; + } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) { + // This is a Mac PostScript name and is therefore ASCII. + // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html + if (bufferSize < stringsOffset + nameOffset + length) + return NULL; + result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false)); + break; + } + + offset += 12; + } + + if (result) + nameMap.set(faceString, result); + return result.get(); +} + static int CALLBACK enumStylesCallback(const LOGFONT* logFont, const TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) { int *style = reinterpret_cast<int*>(lParam); @@ -60,12 +131,7 @@ static int CALLBACK enumStylesCallback(const LOGFONT* logFont, const TEXTMETRIC* FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool oblique, bool useGDI) : m_font(font) , m_size(size) -#if PLATFORM(CG) , m_cgFont(0) -#elif PLATFORM(CAIRO) - , m_fontFace(0) - , m_scaledFont(0) -#endif , m_syntheticBold(false) , m_syntheticOblique(false) , m_useGDI(useGDI) @@ -113,8 +179,17 @@ FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool obliq m_syntheticOblique = true; } - platformDataInit(font, size, hdc, faceName); - + // Try the face name first. Windows may end up localizing this name, and CG doesn't know about + // the localization. If the create fails, we'll try the PostScript name. + RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName))); + m_cgFont = CGFontCreateWithFontName(fullName.get()); + if (!m_cgFont) { + CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc); + if (postScriptName) { + m_cgFont = CGFontCreateWithFontName(postScriptName); + ASSERT(m_cgFont); + } + } free(metrics); } @@ -125,12 +200,17 @@ FontPlatformData::FontPlatformData(HFONT font, float size, bool bold, bool obliq FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) : m_font(0) , m_size(size) -#if PLATFORM(CG) , m_cgFont(0) -#elif PLATFORM(CAIRO) - , m_fontFace(0) - , m_scaledFont(0) -#endif + , m_syntheticBold(bold) + , m_syntheticOblique(oblique) + , m_useGDI(false) +{ +} + +FontPlatformData::FontPlatformData(CGFontRef font, float size, bool bold, bool oblique) + : m_font(0) + , m_size(size) + , m_cgFont(font) , m_syntheticBold(bold) , m_syntheticOblique(oblique) , m_useGDI(false) diff --git a/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp b/WebCore/platform/graphics/win/GlyphPageTreeNodeWin.cpp index c11fc1b..23b756c 100644 --- a/WebCore/platform/graphics/win/GlyphPageTreeNodeCGWin.cpp +++ b/WebCore/platform/graphics/win/GlyphPageTreeNodeWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,9 +36,9 @@ namespace WebCore { bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { - // bufferLength will be greater than the requested number of glyphs if the buffer contains surrogate pairs. + // The bufferLength will be greater than the glyph page size if the buffer has Unicode supplementary characters. // We won't support this for now. - if (bufferLength > length) + if (bufferLength > GlyphPage::size) return false; bool haveGlyphs = false; diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 99d528d..58829b5 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -168,6 +168,77 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo m_data->restore(); } +void GraphicsContextPlatformPrivate::save() +{ + if (!m_hdc) + return; + SaveDC(m_hdc); +} + +void GraphicsContextPlatformPrivate::restore() +{ + if (!m_hdc) + return; + RestoreDC(m_hdc, -1); +} + +void GraphicsContextPlatformPrivate::clip(const IntRect& clipRect) +{ + if (!m_hdc) + return; + IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); +} + +void GraphicsContextPlatformPrivate::clip(const Path&) +{ + notImplemented(); +} + +void GraphicsContextPlatformPrivate::scale(const FloatSize& size) +{ + if (!m_hdc) + return; + XFORM xform; + xform.eM11 = size.width(); + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = size.height(); + xform.eDx = 0.0f; + xform.eDy = 0.0f; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +static const double deg2rad = 0.017453292519943295769; // pi/180 + +void GraphicsContextPlatformPrivate::rotate(float degreesAngle) +{ + float radiansAngle = degreesAngle * deg2rad; + float cosAngle = cosf(radiansAngle); + float sinAngle = sinf(radiansAngle); + XFORM xform; + xform.eM11 = cosAngle; + xform.eM12 = -sinAngle; + xform.eM21 = sinAngle; + xform.eM22 = cosAngle; + xform.eDx = 0.0f; + xform.eDy = 0.0f; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + +void GraphicsContextPlatformPrivate::translate(float x , float y) +{ + if (!m_hdc) + return; + XFORM xform; + xform.eM11 = 1.0f; + xform.eM12 = 0.0f; + xform.eM21 = 0.0f; + xform.eM22 = 1.0f; + xform.eDx = x; + xform.eDy = y; + ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); +} + void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) { if (!m_hdc) diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index 0e9c636..c3fbdd7 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -92,21 +92,21 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo cairo_surface_mark_dirty(surface); } -void GraphicsContextPlatformPrivate::concatCTM(const AffineTransform& transform) +void GraphicsContext::concatCTM(const AffineTransform& transform) { - cairo_surface_t* surface = cairo_get_target(cr); + cairo_surface_t* surface = cairo_get_target(platformContext()); HDC hdc = cairo_win32_surface_get_dc(surface); SaveDC(hdc); - const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); - + cairo_matrix_t mat; + cairo_get_matrix(platformContext(), &mat); XFORM xform; - xform.eM11 = matrix->xx; - xform.eM12 = matrix->xy; - xform.eM21 = matrix->yx; - xform.eM22 = matrix->yy; - xform.eDx = matrix->x0; - xform.eDy = matrix->y0; + xform.eM11 = mat.xx; + xform.eM12 = mat.xy; + xform.eM21 = mat.yx; + xform.eM22 = mat.yy; + xform.eDx = mat.x0; + xform.eDy = mat.y0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); } diff --git a/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index 373d2cf..a8f2148 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -26,12 +26,6 @@ #include "config.h" #include "GraphicsContext.h" -#if PLATFORM(CG) -#include "GraphicsContextPlatformPrivateCG.h" -#elif PLATFORM(CAIRO) -#include "GraphicsContextPlatformPrivateCairo.h" -#endif - #include "AffineTransform.h" #include "NotImplemented.h" #include "Path.h" @@ -43,77 +37,6 @@ namespace WebCore { class SVGResourceImage; -void GraphicsContextPlatformPrivate::save() -{ - if (!m_hdc) - return; - SaveDC(m_hdc); -} - -void GraphicsContextPlatformPrivate::restore() -{ - if (!m_hdc) - return; - RestoreDC(m_hdc, -1); -} - -void GraphicsContextPlatformPrivate::clip(const IntRect& clipRect) -{ - if (!m_hdc) - return; - IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); -} - -void GraphicsContextPlatformPrivate::clip(const Path&) -{ - notImplemented(); -} - -void GraphicsContextPlatformPrivate::scale(const FloatSize& size) -{ - if (!m_hdc) - return; - XFORM xform; - xform.eM11 = size.width(); - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = size.height(); - xform.eDx = 0.0f; - xform.eDy = 0.0f; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - -static const double deg2rad = 0.017453292519943295769; // pi/180 - -void GraphicsContextPlatformPrivate::rotate(float degreesAngle) -{ - float radiansAngle = degreesAngle * deg2rad; - float cosAngle = cosf(radiansAngle); - float sinAngle = sinf(radiansAngle); - XFORM xform; - xform.eM11 = cosAngle; - xform.eM12 = -sinAngle; - xform.eM21 = sinAngle; - xform.eM22 = cosAngle; - xform.eDx = 0.0f; - xform.eDy = 0.0f; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - -void GraphicsContextPlatformPrivate::translate(float x , float y) -{ - if (!m_hdc) - return; - XFORM xform; - xform.eM11 = 1.0f; - xform.eM12 = 0.0f; - xform.eM21 = 0.0f; - xform.eM22 = 1.0f; - xform.eDx = x; - xform.eDy = y; - ModifyWorldTransform(m_hdc, &xform, MWT_LEFTMULTIPLY); -} - #if ENABLE(SVG) GraphicsContext* contextForImage(SVGResourceImage*) { diff --git a/WebCore/platform/graphics/win/IconWin.cpp b/WebCore/platform/graphics/win/IconWin.cpp index b6bc926..60cebe3 100644 --- a/WebCore/platform/graphics/win/IconWin.cpp +++ b/WebCore/platform/graphics/win/IconWin.cpp @@ -28,14 +28,12 @@ namespace WebCore { Icon::Icon() - : RefCounted<Icon>(0) - , m_hIcon(0) + : m_hIcon(0) { } Icon::Icon(HICON icon) - : RefCounted<Icon>(0) - , m_hIcon(icon) + : m_hIcon(icon) { } diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index c7d3bf8..65b3db6 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -28,6 +28,7 @@ #if ENABLE(VIDEO) #include "MediaPlayerPrivateQuickTimeWin.h" +#include "DeprecatedString.h" #include "GraphicsContext.h" #include "KURL.h" #include "QTMovieWin.h" diff --git a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp index 4040ed8..0c31672 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp @@ -54,9 +54,32 @@ void SimpleFontData::platformInit() m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; - - if (m_font.useGDI()) - return initGDIFont(); + + if (m_font.useGDI()) { + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + OUTLINETEXTMETRIC metrics; + GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); + TEXTMETRIC& textMetrics = metrics.otmTextMetrics; + m_ascent = textMetrics.tmAscent; + m_descent = textMetrics.tmDescent; + m_lineGap = textMetrics.tmExternalLeading; + m_lineSpacing = m_ascent + m_descent + m_lineGap; + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. + + GLYPHMETRICS gm; + MAT2 mat = { 1, 0, 0, 1 }; + DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); + if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) + m_xHeight = gm.gmptGlyphOrigin.y; + + m_unitsPerEm = metrics.otmEMSquare; + + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + return; + } CGFontRef font = m_font.cgFont(); int iAscent = CGFontGetAscent(font); @@ -121,13 +144,24 @@ void SimpleFontData::platformDestroy() CGFontRelease(m_font.cgFont()); } - platformCommonDestroy(); + // We don't hash this on Win32, so it's effectively owned by us. + delete m_smallCapsFontData; + + ScriptFreeCache(&m_scriptCache); + delete m_scriptFontProperties; } float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - if (m_font.useGDI()) - return widthForGDIGlyph(glyph); + if (m_font.useGDI()) { + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + int width; + GetCharWidthI(hdc, glyph, 1, 0, &width); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + return width; + } CGFontRef font = m_font.cgFont(); float pointSize = m_font.size(); diff --git a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp index fd017b4..e7b2c81 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -34,10 +34,7 @@ #include "Font.h" #include "FontCache.h" #include "FontDescription.h" -#include "MathExtras.h" #include "NotImplemented.h" -#include <cairo.h> -#include <cairo-win32.h> #include <mlang.h> #include <tchar.h> @@ -48,83 +45,58 @@ void SimpleFontData::platformInit() m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; + + if (m_font.useGDI()) { + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + OUTLINETEXTMETRIC metrics; + GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); + TEXTMETRIC& textMetrics = metrics.otmTextMetrics; + m_ascent = textMetrics.tmAscent; + m_descent = textMetrics.tmDescent; + m_lineGap = textMetrics.tmExternalLeading; + m_lineSpacing = m_ascent + m_descent + m_lineGap; + m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. - if (m_font.useGDI()) - return initGDIFont(); - - HDC hdc = GetDC(0); - SaveDC(hdc); - - cairo_scaled_font_t* scaledFont = m_font.scaledFont(); - const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_font.size(); - - cairo_win32_scaled_font_select_font(scaledFont, hdc); - - TEXTMETRIC textMetrics; - GetTextMetrics(hdc, &textMetrics); - m_ascent = lroundf(textMetrics.tmAscent * metricsMultiplier); - m_descent = lroundf(textMetrics.tmDescent * metricsMultiplier); - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. - m_lineGap = lroundf(textMetrics.tmExternalLeading * metricsMultiplier); - m_lineSpacing = m_ascent + m_descent + m_lineGap; - - OUTLINETEXTMETRIC metrics; - if (GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics) > 0) { - // This is a TrueType font. We might be able to get an accurate xHeight GLYPHMETRICS gm; MAT2 mat = { 1, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y * metricsMultiplier; - } + m_xHeight = gm.gmptGlyphOrigin.y; - cairo_win32_scaled_font_done_font(scaledFont); + m_unitsPerEm = metrics.otmEMSquare; - m_isSystemFont = false; - m_scriptCache = 0; - m_scriptFontProperties = 0; + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + + return; + } - RestoreDC(hdc, -1); - ReleaseDC(0, hdc); + // FIXME: This section should determine font dimensions (see CG implementation). + notImplemented(); } void SimpleFontData::platformDestroy() { - cairo_font_face_destroy(m_font.fontFace()); - cairo_scaled_font_destroy(m_font.scaledFont()); - - DeleteObject(m_font.hfont()); - - platformCommonDestroy(); + notImplemented(); } float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { - if (m_font.useGDI()) - return widthForGDIGlyph(glyph); - - HDC hdc = GetDC(0); - SaveDC(hdc); - - cairo_scaled_font_t* scaledFont = m_font.scaledFont(); - cairo_win32_scaled_font_select_font(scaledFont, hdc); - - int width; - GetCharWidthI(hdc, glyph, 1, 0, &width); - - cairo_win32_scaled_font_done_font(scaledFont); + if (m_font.useGDI()) { + HDC hdc = GetDC(0); + HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); + int width; + GetCharWidthI(hdc, glyph, 1, 0, &width); + SelectObject(hdc, oldFont); + ReleaseDC(0, hdc); + return width; + } - RestoreDC(hdc, -1); - ReleaseDC(0, hdc); + // FIXME: Flesh out with Cairo/win32 font implementation + notImplemented(); - const double metricsMultiplier = cairo_win32_scaled_font_get_metrics_factor(scaledFont) * m_font.size(); - return width * metricsMultiplier; -} - -void SimpleFontData::setFont(cairo_t* cr) const -{ - ASSERT(cr); - m_font.setFont(cr); + return 0; } } diff --git a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp index 6d1c417..344d964 100644 --- a/WebCore/platform/graphics/win/SimpleFontDataWin.cpp +++ b/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -60,42 +60,6 @@ bool SimpleFontData::shouldApplyMacAscentHack() return g_shouldApplyMacAscentHack; } -void SimpleFontData::initGDIFont() -{ - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - OUTLINETEXTMETRIC metrics; - GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); - TEXTMETRIC& textMetrics = metrics.otmTextMetrics; - m_ascent = textMetrics.tmAscent; - m_descent = textMetrics.tmDescent; - m_lineGap = textMetrics.tmExternalLeading; - m_lineSpacing = m_ascent + m_descent + m_lineGap; - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. - - GLYPHMETRICS gm; - MAT2 mat = { 1, 0, 0, 1 }; - DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); - if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y; - - m_unitsPerEm = metrics.otmEMSquare; - - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - - return; -} - -void SimpleFontData::platformCommonDestroy() -{ - // We don't hash this on Win32, so it's effectively owned by us. - delete m_smallCapsFontData; - - ScriptFreeCache(&m_scriptCache); - delete m_scriptFontProperties; -} - SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const { if (!m_smallCapsFontData) { @@ -173,17 +137,6 @@ void SimpleFontData::determinePitch() ReleaseDC(0, dc); } -float SimpleFontData::widthForGDIGlyph(Glyph glyph) const -{ - HDC hdc = GetDC(0); - HGDIOBJ oldFont = SelectObject(hdc, m_font.hfont()); - int width; - GetCharWidthI(hdc, glyph, 1, 0, &width); - SelectObject(hdc, oldFont); - ReleaseDC(0, hdc); - return width; -} - SCRIPT_FONTPROPERTIES* SimpleFontData::scriptFontProperties() const { if (!m_scriptFontProperties) { diff --git a/WebCore/platform/graphics/wx/FontPlatformData.h b/WebCore/platform/graphics/wx/FontPlatformData.h index 0f4c29b..f48068b 100644 --- a/WebCore/platform/graphics/wx/FontPlatformData.h +++ b/WebCore/platform/graphics/wx/FontPlatformData.h @@ -76,7 +76,7 @@ public: case UNINITIALIZED: return 0; case VALID: - return computeHash(); + return m_fontHash; } } @@ -89,8 +89,9 @@ public: } unsigned computeHash() const { - ASSERT(m_font.Ok()); - return reinterpret_cast<unsigned>(&m_font); + wxCharBuffer charBuffer(m_font.GetNativeFontInfoDesc().mb_str(wxConvUTF8)); + const char* contents = charBuffer; + return StringImpl::computeHash( (UChar*)contents, strlen(contents)); } private: diff --git a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp index 8e7c621..f3fb480 100755 --- a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp +++ b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp @@ -83,7 +83,7 @@ FontPlatformData::FontPlatformData(const FontDescription& desc, const AtomicStri italicToWxFontStyle(desc.italic()), fontWeightToWxFontWeight(desc.bold()), false, - family.string() + family.domString() ); #else m_font = wxFont( desc.computedPixelSize(), @@ -91,7 +91,7 @@ FontPlatformData::FontPlatformData(const FontDescription& desc, const AtomicStri italicToWxFontStyle(desc.italic()), fontWeightToWxFontWeight(desc.bold()), false, - family.string() + family.domString() ); #endif m_fontState = VALID; diff --git a/WebCore/platform/graphics/wx/FontWx.cpp b/WebCore/platform/graphics/wx/FontWx.cpp index 07223e9..e94ae3b 100644 --- a/WebCore/platform/graphics/wx/FontWx.cpp +++ b/WebCore/platform/graphics/wx/FontWx.cpp @@ -33,9 +33,9 @@ #include "NotImplemented.h" #include "SimpleFontData.h" -#include <wx/dcclient.h> #include "fontprops.h" -#include "non-kerned-drawing.h" +#include <wx/defs.h> +#include <wx/dcclient.h> namespace WebCore { @@ -45,11 +45,33 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo // prepare DC Color color = graphicsContext->fillColor(); - // We can't use wx drawing methods on Win/Linux because they automatically kern text - // so we've created a function with platform dependent drawing implementations that - // will hopefully be folded into wx once the API has solidified. - // see platform/wx/wxcode/<platform> for the implementations. - drawTextWithSpacing(graphicsContext, font, color, glyphBuffer, from, numGlyphs, point); +#if USE(WXGC) + wxGCDC* dc = (wxGCDC*)graphicsContext->platformContext(); + wxFont wxfont = font->getWxFont(); + if (wxfont.IsOk()) + dc->SetFont(wxfont); + dc->SetTextForeground(color); +#else + wxDC* dc = graphicsContext->platformContext(); + dc->SetTextBackground(color); + dc->SetTextForeground(color); + dc->SetFont(font->getWxFont()); +#endif + + // convert glyphs to wxString + GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); + int offset = point.x(); + wxString text = wxEmptyString; + for (unsigned i = 0; i < numGlyphs; i++) { + text = text.Append((wxChar)glyphs[i]); + offset += glyphBuffer.advanceAt(from + i); + } + + // the y point is actually the bottom point of the text, turn it into the top + float height = font->ascent() - font->descent(); + wxCoord ypoint = (wxCoord) (point.y() - height); + + dc->DrawText(text, (wxCoord)point.x(), ypoint); } FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const diff --git a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp index 951d3a3..78149ad 100644 --- a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp +++ b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp @@ -227,11 +227,6 @@ void GraphicsContext::drawEllipse(const IntRect& rect) m_data->context->DrawEllipse(rect.x(), rect.y(), rect.width(), rect.height()); } -void GraphicsContext::drawImage(WebCore::ImageBuffer*, WebCore::FloatRect const&, WebCore::FloatRect const&) -{ - notImplemented(); -} - void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) { if (paintingDisabled()) @@ -427,16 +422,6 @@ void GraphicsContext::setCompositeOperation(CompositeOperator op) m_data->context->SetLogicalFunction(getWxCompositingOperation(op, false)); } -void GraphicsContext::beginPath() -{ - notImplemented(); -} - -void GraphicsContext::addPath(const Path& path) -{ - notImplemented(); -} - void GraphicsContext::setPlatformStrokeColor(const Color& color) { if (paintingDisabled()) @@ -481,11 +466,4 @@ void GraphicsContext::setUseAntialiasing(bool enable) notImplemented(); } -void GraphicsContext::paintBuffer(ImageBuffer* buffer, const IntRect& r) -{ - if (paintingDisabled()) - return; - notImplemented(); -} - } diff --git a/WebCore/platform/graphics/wx/ImageBufferWx.cpp b/WebCore/platform/graphics/wx/ImageBufferWx.cpp index 0c832ea..bc54a88 100644 --- a/WebCore/platform/graphics/wx/ImageBufferWx.cpp +++ b/WebCore/platform/graphics/wx/ImageBufferWx.cpp @@ -25,10 +25,7 @@ #include "config.h" #include "ImageBuffer.h" - #include "GraphicsContext.h" -#include "ImageData.h" -#include "NotImplemented.h" namespace WebCore { @@ -46,15 +43,4 @@ GraphicsContext* ImageBuffer::context() const return 0; } -PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const -{ - notImplemented(); - return 0; -} - -void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&) -{ - notImplemented(); -} - } diff --git a/WebCore/platform/graphics/wx/PathWx.cpp b/WebCore/platform/graphics/wx/PathWx.cpp index 5ff9914..86ecb93 100644 --- a/WebCore/platform/graphics/wx/PathWx.cpp +++ b/WebCore/platform/graphics/wx/PathWx.cpp @@ -171,10 +171,4 @@ void Path::apply(void* info, PathApplierFunction function) const notImplemented(); } -bool Path::isEmpty() const -{ - notImplemented(); - return false; -} - } diff --git a/WebCore/platform/gtk/CookieJarGtk.cpp b/WebCore/platform/gtk/CookieJarGtk.cpp index 2f76ebc..2813a2e 100644 --- a/WebCore/platform/gtk/CookieJarGtk.cpp +++ b/WebCore/platform/gtk/CookieJarGtk.cpp @@ -17,6 +17,7 @@ #include "config.h" #include "CookieJar.h" +#include "DeprecatedString.h" #include "KURL.h" #include "PlatformString.h" #include "StringHash.h" diff --git a/WebCore/platform/gtk/CursorGtk.cpp b/WebCore/platform/gtk/CursorGtk.cpp index 15f492f..94c6975 100644 --- a/WebCore/platform/gtk/CursorGtk.cpp +++ b/WebCore/platform/gtk/CursorGtk.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "CursorGtk.h" +#include "DeprecatedString.h" #include "NotImplemented.h" #include <wtf/Assertions.h> diff --git a/WebCore/platform/gtk/FileChooserGtk.cpp b/WebCore/platform/gtk/FileChooserGtk.cpp index 10c1a5f..c41a693 100644 --- a/WebCore/platform/gtk/FileChooserGtk.cpp +++ b/WebCore/platform/gtk/FileChooserGtk.cpp @@ -57,8 +57,7 @@ static bool stringByAdoptingFileSystemRepresentation(gchar* systemFilename, Stri } FileChooser::FileChooser(FileChooserClient* client, const String& filename) - : RefCounted<FileChooser>(0) - , m_client(client) + : m_client(client) , m_filename(filename) , m_icon(chooseIcon(filename)) { diff --git a/WebCore/platform/gtk/FileSystemGtk.cpp b/WebCore/platform/gtk/FileSystemGtk.cpp index df95dde..904fe9f 100644 --- a/WebCore/platform/gtk/FileSystemGtk.cpp +++ b/WebCore/platform/gtk/FileSystemGtk.cpp @@ -30,8 +30,6 @@ #include <glib/gstdio.h> #include <glib/gutils.h> -#include <unistd.h> - namespace WebCore { bool fileExists(const String& path) @@ -115,20 +113,6 @@ bool makeAllDirectories(const String& path) return result == 0; } -String homeDirectoryPath() -{ - return String::fromUTF8(g_get_home_dir()); -} - -String pathGetFileName(const String& pathName) -{ - char* baseName = g_path_get_basename(pathName.utf8().data()); - String fileName = String::fromUTF8(baseName); - g_free(baseName); - - return fileName; -} - CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) { gchar* filename = g_strdup_printf("%sXXXXXX", prefix); @@ -168,9 +152,4 @@ int writeToFile(PlatformFileHandle handle, const char* data, int length) return totalBytesWritten; } - -bool unloadModule(PlatformModule module) -{ - return g_module_close(module); -} } diff --git a/WebCore/platform/gtk/KeyEventGtk.cpp b/WebCore/platform/gtk/KeyEventGtk.cpp index dea0502..5d2efcf 100644 --- a/WebCore/platform/gtk/KeyEventGtk.cpp +++ b/WebCore/platform/gtk/KeyEventGtk.cpp @@ -2,7 +2,6 @@ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007 Holger Hans Peter Freyther - * Copyright (C) 2008 Collabora, Ltd. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,13 +29,13 @@ #include "config.h" #include "PlatformKeyboardEvent.h" +#include "DeprecatedString.h" #include "KeyboardCodes.h" #include "NotImplemented.h" #include "TextEncoding.h" #include <gdk/gdk.h> #include <gdk/gdkkeysyms.h> -#include <gtk/gtkversion.h> namespace WebCore { @@ -485,8 +484,6 @@ static String singleCharacterString(guint val) return retVal; } -// Keep this in sync with the other platform event constructors -// TODO: m_gdkEventKey should be refcounted PlatformKeyboardEvent::PlatformKeyboardEvent(GdkEventKey* event) : m_type((event->type == GDK_KEY_RELEASE) ? KeyUp : KeyDown) , m_text(singleCharacterString(event->keyval)) @@ -494,29 +491,20 @@ PlatformKeyboardEvent::PlatformKeyboardEvent(GdkEventKey* event) , m_keyIdentifier(keyIdentifierForGdkKeyCode(event->keyval)) , m_autoRepeat(false) , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(event->keyval)) - , m_isKeypad(event->keyval >= GDK_KP_Space && event->keyval <= GDK_KP_9) + , m_isKeypad(false) , m_shiftKey((event->state & GDK_SHIFT_MASK) || (event->keyval == GDK_3270_BackTab)) , m_ctrlKey(event->state & GDK_CONTROL_MASK) , m_altKey(event->state & GDK_MOD1_MASK) -#if GTK_CHECK_VERSION(2,10,0) - , m_metaKey(event->state & GDK_META_MASK) -#else - // GDK_MOD2_MASK doesn't always mean meta so we can't use it - , m_metaKey(false) -#endif - , m_gdkEventKey(event) + , m_metaKey(event->state & GDK_MOD2_MASK) { } -void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCompatibilityMode) +void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool) { // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions. ASSERT(m_type == KeyDown); m_type = type; - if (backwardCompatibilityMode) - return; - if (type == RawKeyDown) { m_text = String(); m_unmodifiedText = String(); @@ -532,9 +520,4 @@ bool PlatformKeyboardEvent::currentCapsLockState() return false; } -GdkEventKey* PlatformKeyboardEvent::gdkEventKey() const -{ - return m_gdkEventKey; -} - } diff --git a/WebCore/platform/gtk/LocalizedStringsGtk.cpp b/WebCore/platform/gtk/LocalizedStringsGtk.cpp index 4be5ba1..041cd83 100644 --- a/WebCore/platform/gtk/LocalizedStringsGtk.cpp +++ b/WebCore/platform/gtk/LocalizedStringsGtk.cpp @@ -290,9 +290,4 @@ String unknownFileSizeText() return String::fromUTF8(_("Unknown")); } -String imageTitle(const String& filename, const IntSize& size) -{ - return String(); -} - } diff --git a/WebCore/platform/gtk/MouseEventGtk.cpp b/WebCore/platform/gtk/MouseEventGtk.cpp index f441f00..29ea371 100644 --- a/WebCore/platform/gtk/MouseEventGtk.cpp +++ b/WebCore/platform/gtk/MouseEventGtk.cpp @@ -31,13 +31,11 @@ #include "Assertions.h" #include <gdk/gdk.h> -#include <gtk/gtkversion.h> namespace WebCore { // FIXME: Would be even better to figure out which modifier is Alt instead of always using GDK_MOD1_MASK. -// Keep this in sync with the other platform event constructors PlatformMouseEvent::PlatformMouseEvent(GdkEventButton* event) { m_timestamp = event->time; @@ -46,12 +44,7 @@ PlatformMouseEvent::PlatformMouseEvent(GdkEventButton* event) m_shiftKey = event->state & GDK_SHIFT_MASK; m_ctrlKey = event->state & GDK_CONTROL_MASK; m_altKey = event->state & GDK_MOD1_MASK; -#if GTK_CHECK_VERSION(2,10,0) - m_metaKey = event->state & GDK_META_MASK; -#else - // GDK_MOD2_MASK doesn't always mean meta so we can't use it - m_metaKey = false; -#endif + m_metaKey = event->state & GDK_MOD2_MASK; switch (event->type) { case GDK_BUTTON_PRESS: diff --git a/WebCore/platform/gtk/PasteboardGtk.cpp b/WebCore/platform/gtk/PasteboardGtk.cpp index 15a7e64..745728c 100644 --- a/WebCore/platform/gtk/PasteboardGtk.cpp +++ b/WebCore/platform/gtk/PasteboardGtk.cpp @@ -102,20 +102,16 @@ void Pasteboard::setHelper(PasteboardHelper* helper) void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, Frame* frame) { - GtkClipboard* clipboard = m_helper->getClipboard(frame); -#if GTK_CHECK_VERSION(2,10,0) gchar* text = g_strdup(frame->selectedText().utf8().data()); gchar* markup = g_strdup(createMarkup(selectedRange, 0, AnnotateForInterchange).utf8().data()); PasteboardSelectionData* data = new PasteboardSelectionData(text, markup); gint n_targets; GtkTargetEntry* targets = gtk_target_table_new_from_list(m_helper->getCopyTargetList(frame), &n_targets); + GtkClipboard* clipboard = m_helper->getClipboard(frame); gtk_clipboard_set_with_data(clipboard, targets, n_targets, clipboard_get_contents_cb, clipboard_clear_contents_cb, data); gtk_target_table_free(targets, n_targets); -#else - gtk_clipboard_set_text(clipboard, frame->selectedText().utf8().data(), frame->selectedText().utf8().length()); -#endif } void Pasteboard::writeURL(const KURL& url, const String&, Frame* frame) diff --git a/WebCore/platform/gtk/PopupMenuGtk.cpp b/WebCore/platform/gtk/PopupMenuGtk.cpp index f228c8b..b4689f6 100644 --- a/WebCore/platform/gtk/PopupMenuGtk.cpp +++ b/WebCore/platform/gtk/PopupMenuGtk.cpp @@ -33,8 +33,7 @@ namespace WebCore { PopupMenu::PopupMenu(PopupMenuClient* client) - : RefCounted<PopupMenu>(0) - , m_popupClient(client) + : m_popupClient(client) , m_popup(0) { } diff --git a/WebCore/platform/gtk/TemporaryLinkStubs.cpp b/WebCore/platform/gtk/TemporaryLinkStubs.cpp index 7abe32e..525a6f8 100644 --- a/WebCore/platform/gtk/TemporaryLinkStubs.cpp +++ b/WebCore/platform/gtk/TemporaryLinkStubs.cpp @@ -34,8 +34,7 @@ #include "GlobalHistory.h" #include "KURL.h" #include "NotImplemented.h" -#include "PluginDatabase.h" -#include "PluginPackage.h" +#include "PluginInfoStore.h" #include "SharedBuffer.h" using namespace WebCore; @@ -60,14 +59,12 @@ Vector<char> loadResourceIntoArray(const char* resourceName) bool WebCore::historyContains(const UChar*, unsigned) { return false; } -PluginSet PluginDatabase::getPluginsInPaths() const { notImplemented(); return PluginSet(); } -Vector<String> PluginDatabase::defaultPluginPaths() { notImplemented(); return Vector<String>(); } -bool PluginDatabase::isPreferredPluginPath(const String&) { notImplemented(); return false; } -int PluginPackage::compare(const PluginPackage&) const { notImplemented(); return 0; } -bool PluginPackage::fetchInfo() { notImplemented(); return false; } -unsigned PluginPackage::hash() const { notImplemented(); return 0; } -bool PluginPackage::equal(const PluginPackage&, const PluginPackage&) { notImplemented(); return false; } -bool PluginPackage::load() { notImplemented(); return false; } +PluginInfo* PluginInfoStore::createPluginInfoForPluginAtIndex(unsigned) { notImplemented(); return 0;} +unsigned PluginInfoStore::pluginCount() const { notImplemented(); return 0; } +String PluginInfoStore::pluginNameForMIMEType(const String& mimeType) { notImplemented(); return String(); } +bool WebCore::PluginInfoStore::supportsMIMEType(const WebCore::String&) { notImplemented(); return false; } +void WebCore::refreshPlugins(bool) { notImplemented(); } + Color WebCore::focusRingColor() { return 0xFF0000FF; } void WebCore::setFocusRingColorChangeFunction(void (*)()) { } diff --git a/WebCore/platform/gtk/ThreadingGtk.cpp b/WebCore/platform/gtk/ThreadingGtk.cpp new file mode 100644 index 0000000..5ce6ba2 --- /dev/null +++ b/WebCore/platform/gtk/ThreadingGtk.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * 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. + * 3. Neither the name of Apple Inc. ("Apple") 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 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 "Threading.h" + +#include "HashMap.h" +#include "Logging.h" + +#include <glib.h> + +namespace WebCore { + +struct FunctionWithContext { + MainThreadFunction* function; + void* context; +}; + +static gboolean callFunctionOnMainThread(gpointer data) +{ + FunctionWithContext* functionWithContext = static_cast<FunctionWithContext*>(data); + functionWithContext->function(functionWithContext->context); + delete functionWithContext; + return FALSE; +} + +void callOnMainThread(MainThreadFunction* function, void* context) +{ + ASSERT(function); + FunctionWithContext* functionWithContext = new FunctionWithContext; + functionWithContext->function = function; + functionWithContext->context = context; + g_timeout_add(0, callFunctionOnMainThread, functionWithContext); +} + +void initializeThreading() +{ + if (!g_thread_supported()) + g_thread_init(NULL); + ASSERT(g_thread_supported()); +} + +static Mutex& threadMapMutex() +{ + static Mutex mutex; + return mutex; +} + +static HashMap<ThreadIdentifier, GThread*>& threadMap() +{ + static HashMap<ThreadIdentifier, GThread*> map; + return map; +} + +static ThreadIdentifier establishIdentifierForThread(GThread*& thread) +{ + MutexLocker locker(threadMapMutex()); + + static ThreadIdentifier identifierCount = 1; + + threadMap().add(identifierCount, thread); + + return identifierCount++; +} + +static ThreadIdentifier identifierByGthreadHandle(GThread*& thread) +{ + MutexLocker locker(threadMapMutex()); + + HashMap<ThreadIdentifier, GThread*>::iterator i = threadMap().begin(); + for (; i != threadMap().end(); ++i) { + if (i->second == thread) + return i->first; + } + + return 0; +} + +static GThread* threadForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + + return threadMap().get(id); +} + +static void clearThreadForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + + ASSERT(threadMap().contains(id)); + + threadMap().remove(id); +} + +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data) +{ + GThread* thread; + if (!(thread = g_thread_create(entryPoint, data, TRUE, 0))) { + LOG_ERROR("Failed to create thread at entry point %p with data %p", entryPoint, data); + return 0; + } + + ThreadIdentifier threadID = establishIdentifierForThread(thread); + LOG(Threading, "Created thread with thread id %u", threadID); + return threadID; +} + +int waitForThreadCompletion(ThreadIdentifier threadID, void** result) +{ + ASSERT(threadID); + + GThread* thread = threadForIdentifier(threadID); + + *result = g_thread_join(thread); + + clearThreadForIdentifier(threadID); + return 0; +} + +void detachThread(ThreadIdentifier) +{ +} + +ThreadIdentifier currentThread() +{ + GThread* currentThread = g_thread_self(); + if (ThreadIdentifier id = identifierByGthreadHandle(currentThread)) + return id; + return establishIdentifierForThread(currentThread); +} + +Mutex::Mutex() + : m_mutex(g_mutex_new()) +{ +} + +Mutex::~Mutex() +{ + g_mutex_free(m_mutex); +} + +void Mutex::lock() +{ + g_mutex_lock(m_mutex); +} + +bool Mutex::tryLock() +{ + return g_mutex_trylock(m_mutex); +} + +void Mutex::unlock() +{ + g_mutex_unlock(m_mutex); +} + +ThreadCondition::ThreadCondition() + : m_condition(g_cond_new()) +{ +} + +ThreadCondition::~ThreadCondition() +{ + g_cond_free(m_condition); +} + +void ThreadCondition::wait(Mutex& mutex) +{ + g_cond_wait(m_condition, mutex.impl()); +} + +void ThreadCondition::signal() +{ + g_cond_signal(m_condition); +} + +void ThreadCondition::broadcast() +{ + g_cond_broadcast(m_condition); +} + + +} diff --git a/WebCore/platform/gtk/WheelEventGtk.cpp b/WebCore/platform/gtk/WheelEventGtk.cpp index 08f02cc..3368f25 100644 --- a/WebCore/platform/gtk/WheelEventGtk.cpp +++ b/WebCore/platform/gtk/WheelEventGtk.cpp @@ -29,11 +29,9 @@ #include "PlatformWheelEvent.h" #include <gdk/gdk.h> -#include <gtk/gtkversion.h> namespace WebCore { -// Keep this in sync with the other platform event constructors PlatformWheelEvent::PlatformWheelEvent(GdkEventScroll* event) { static const float delta = 1; @@ -63,12 +61,7 @@ PlatformWheelEvent::PlatformWheelEvent(GdkEventScroll* event) m_shiftKey = event->state & GDK_SHIFT_MASK; m_ctrlKey = event->state & GDK_CONTROL_MASK; m_altKey = event->state & GDK_MOD1_MASK; -#if GTK_CHECK_VERSION(2,10,0) - m_metaKey = event->state & GDK_META_MASK; -#else - // GDK_MOD2_MASK doesn't always mean meta so we can't use it - m_metaKey = false; -#endif + m_metaKey = event->state & GDK_MOD2_MASK; m_isContinuous = false; } diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index dc3c767..1b70cd4 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -23,7 +23,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" #include "GIFImageDecoder.h" #include "GIFImageReader.h" diff --git a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp index 520e1ae..cc246e2 100644 --- a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp @@ -38,7 +38,6 @@ #include "config.h" #include "JPEGImageDecoder.h" #include <assert.h> -#include <stdio.h> #if PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX) diff --git a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp index 6dd0073..99da437 100644 --- a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp @@ -35,7 +35,6 @@ * version of this file under any of the LGPL, the MPL or the GPL. */ -#include "config.h" #include "PNGImageDecoder.h" #include "png.h" #include "assert.h" diff --git a/WebCore/platform/mac/ClipboardMac.mm b/WebCore/platform/mac/ClipboardMac.mm index efd1fcf..01ec573 100644 --- a/WebCore/platform/mac/ClipboardMac.mm +++ b/WebCore/platform/mac/ClipboardMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,17 +26,27 @@ #import "config.h" #import "ClipboardMac.h" +#import "CachedImage.h" +#import "Document.h" #import "DOMElementInternal.h" #import "DragClient.h" #import "DragController.h" #import "Editor.h" +#import "EventHandler.h" +#import "FloatRect.h" #import "FoundationExtras.h" +#import "Frame.h" +#import "HTMLImageElement.h" #import "Image.h" #import "Page.h" #import "Pasteboard.h" +#import "Range.h" #import "RenderImage.h" +#import "WebCoreFrameBridge.h" #import "WebCoreSystemInterface.h" +@class WebArchive; + namespace WebCore { ClipboardMac::ClipboardMac(bool forDragging, NSPasteboard *pasteboard, ClipboardAccessPolicy policy, Frame *frame) @@ -322,7 +332,7 @@ void ClipboardMac::declareAndWriteDragImage(Element* element, const KURL& url, c { ASSERT(frame); if (Page* page = frame->page()) - page->dragController()->client()->declareAndWriteDragImage(m_pasteboard.get(), [DOMElement _wrapElement:element], url, title, frame); + page->dragController()->client()->declareAndWriteDragImage(m_pasteboard.get(), [DOMElement _wrapElement:element], url.getNSURL(), title, frame); } DragImageRef ClipboardMac::createDragImage(IntPoint& loc) const diff --git a/WebCore/platform/mac/CookieJar.mm b/WebCore/platform/mac/CookieJar.mm index 5fe7a63..7c288ad 100644 --- a/WebCore/platform/mac/CookieJar.mm +++ b/WebCore/platform/mac/CookieJar.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,12 +26,14 @@ #import "config.h" #import "CookieJar.h" -#import "BlockExceptions.h" #import "KURL.h" +#import "BlockExceptions.h" +#import "PlatformString.h" + #import <wtf/RetainPtr.h> #ifdef BUILDING_ON_TIGER -typedef unsigned NSUInteger; +typedef unsigned int NSUInteger; #endif namespace WebCore { @@ -40,7 +42,7 @@ String cookies(const Document* /*document*/, const KURL& url) { BEGIN_BLOCK_OBJC_EXCEPTIONS; - NSURL *cookieURL = url; + NSURL *cookieURL = url.getNSURL(); NSArray *cookiesForURL = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:cookieURL]; // <rdar://problem/5632883> On 10.5, NSHTTPCookieStorage would happily store an empty cookie, which would be sent as "Cookie: =". @@ -68,14 +70,14 @@ void setCookies(Document* /*document*/, const KURL& url, const KURL& policyBaseU if (cookieStr.isEmpty()) return; - NSURL *cookieURL = url; + NSURL *cookieURL = url.getNSURL(); // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034> // cookiesWithResponseHeaderFields doesn't parse cookies without a value String cookieString = cookieStr.contains('=') ? cookieStr : cookieStr + "="; NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[NSDictionary dictionaryWithObject:cookieString forKey:@"Set-Cookie"] forURL:cookieURL]; - [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:cookieURL mainDocumentURL:policyBaseURL]; + [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:cookieURL mainDocumentURL:policyBaseURL.getNSURL()]; END_BLOCK_OBJC_EXCEPTIONS; } diff --git a/WebCore/platform/network/curl/ResourceError.h b/WebCore/platform/mac/DeprecatedStringListMac.mm index 0a8eb64..abb6329 100644 --- a/WebCore/platform/network/curl/ResourceError.h +++ b/WebCore/platform/mac/DeprecatedStringListMac.mm @@ -1,6 +1,5 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,26 +23,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceError_h -#define ResourceError_h - -#include "ResourceErrorBase.h" +#include "config.h" +#import "DeprecatedStringList.h" namespace WebCore { -class ResourceError : public ResourceErrorBase +NSArray *DeprecatedStringList::getNSArray() const { -public: - ResourceError() - { - } - - ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) - : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) - { + NSMutableArray *array = [NSMutableArray array]; + for (ConstIterator it = begin(); it != end(); ++it) { + [array addObject:(*it).getNSString()]; } -}; - + return array; } -#endif // ResourceError_h_ +} diff --git a/WebCore/platform/mac/DeprecatedStringMac.mm b/WebCore/platform/mac/DeprecatedStringMac.mm new file mode 100644 index 0000000..6e58eba --- /dev/null +++ b/WebCore/platform/mac/DeprecatedStringMac.mm @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "DeprecatedString.h" + +#import <wtf/Assertions.h> +#import "TextEncoding.h" + +using namespace WebCore; + +void DeprecatedString::setBufferFromCFString(CFStringRef cfs) +{ + if (!cfs) { + return; + } + CFIndex size = CFStringGetLength(cfs); + UniChar fixedSizeBuffer[1024]; + UniChar *buffer; + if (size > (CFIndex)(sizeof(fixedSizeBuffer) / sizeof(UniChar))) { + buffer = (UniChar *)fastMalloc(size * sizeof(UniChar)); + } else { + buffer = fixedSizeBuffer; + } + CFStringGetCharacters(cfs, CFRangeMake (0, size), buffer); + setUnicode((const DeprecatedChar *)buffer, (unsigned)size); + if (buffer != fixedSizeBuffer) { + fastFree(buffer); + } +} + + +DeprecatedString DeprecatedString::fromCFString(CFStringRef cfs) +{ + DeprecatedString qs; + qs.setBufferFromCFString(cfs); + return qs; +} + +DeprecatedString DeprecatedString::fromNSString(NSString *nss) +{ + DeprecatedString qs; + qs.setBufferFromCFString((CFStringRef)nss); + return qs; +} + +NSString *DeprecatedString::getNSString() const +{ + // The Cocoa calls in this method don't need exceptions blocked + // because they are simple NSString calls that can't throw. + + int length = dataHandle[0]->_length; + if (dataHandle[0]->_isUnicodeValid) { + return [NSString stringWithCharacters:(const unichar *)unicode() length:length]; + } + + if (dataHandle[0]->_isAsciiValid) { + return [[[NSString alloc] initWithBytes:ascii() length:length encoding:NSISOLatin1StringEncoding] autorelease]; + } + + FATAL("invalid character cache"); + return nil; +} diff --git a/WebCore/platform/mac/FileChooserMac.mm b/WebCore/platform/mac/FileChooserMac.mm index a83418c..e8f9159 100644 --- a/WebCore/platform/mac/FileChooserMac.mm +++ b/WebCore/platform/mac/FileChooserMac.mm @@ -93,8 +93,7 @@ using namespace WebCore; namespace WebCore { FileChooser::FileChooser(FileChooserClient* client, const String& filename) - : RefCounted<FileChooser>(0) - , m_client(client) + : m_client(client) , m_filename(filename) , m_icon(chooseIcon(filename)) , m_controller(AdoptNS, [[WebCoreOpenPanelController alloc] initWithFileChooser:this]) diff --git a/WebCore/platform/mac/KURLMac.mm b/WebCore/platform/mac/KURLMac.mm index c913ab4..102f3a5 100644 --- a/WebCore/platform/mac/KURLMac.mm +++ b/WebCore/platform/mac/KURLMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,45 +23,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "config.h" +#include "config.h" #import "KURL.h" #import "FoundationExtras.h" +#import <wtf/Assertions.h> +#import <wtf/Vector.h> namespace WebCore { KURL::KURL(NSURL *url) { - if (!url) { + if (url) { + CFIndex bytesLength = CFURLGetBytes((CFURLRef)url, 0, 0); + Vector<char, 2048> buffer(bytesLength + 6); // 5 for "file:", 1 for NUL terminator + char *bytes = &buffer[5]; + CFURLGetBytes((CFURLRef)url, (UInt8 *)bytes, bytesLength); + bytes[bytesLength] = '\0'; + if (bytes[0] == '/') { + buffer[0] = 'f'; + buffer[1] = 'i'; + buffer[2] = 'l'; + buffer[3] = 'e'; + buffer[4] = ':'; + parse(buffer.data(), 0); + } else + parse(bytes, 0); + } else parse(0, 0); - return; - } - - CFIndex bytesLength = CFURLGetBytes(reinterpret_cast<CFURLRef>(url), 0, 0); - Vector<char, 512> buffer(bytesLength + 6); // 5 for "file:", 1 for null character to end C string - char* bytes = &buffer[5]; - CFURLGetBytes(reinterpret_cast<CFURLRef>(url), reinterpret_cast<UInt8*>(bytes), bytesLength); - bytes[bytesLength] = '\0'; - if (bytes[0] != '/') { - parse(bytes, 0); - return; - } - - buffer[0] = 'f'; - buffer[1] = 'i'; - buffer[2] = 'l'; - buffer[3] = 'e'; - buffer[4] = ':'; - - parse(buffer.data(), 0); } -KURL::operator NSURL *() const +NSURL *KURL::getNSURL() const { - if (isNull()) + if (urlString.isNull()) return nil; - // CFURL can't hold an empty URL, unlike NSURL. + // CFURL can't hold an empty URL, unlike NSURL if (isEmpty()) return [NSURL URLWithString:@""]; diff --git a/WebCore/platform/mac/LocalizedStringsMac.mm b/WebCore/platform/mac/LocalizedStringsMac.mm index d4af7cd..f5b208d 100644 --- a/WebCore/platform/mac/LocalizedStringsMac.mm +++ b/WebCore/platform/mac/LocalizedStringsMac.mm @@ -27,7 +27,6 @@ #import "LocalizedStrings.h" #import "BlockExceptions.h" -#import "IntSize.h" #import "PlatformString.h" #import "WebCoreViewFactory.h" @@ -505,12 +504,4 @@ String unknownFileSizeText() return String(); } -String imageTitle(const String& filename, const IntSize& size) -{ - BEGIN_BLOCK_OBJC_EXCEPTIONS; - return [[WebCoreViewFactory sharedFactory] imageTitleForFilename:filename size:size]; - END_BLOCK_OBJC_EXCEPTIONS; - return String(); -} - } diff --git a/WebCore/platform/mac/PasteboardMac.mm b/WebCore/platform/mac/PasteboardMac.mm index 1433dbd..87db2f9 100644 --- a/WebCore/platform/mac/PasteboardMac.mm +++ b/WebCore/platform/mac/PasteboardMac.mm @@ -40,6 +40,7 @@ #import "MIMETypeRegistry.h" #import "RenderImage.h" #import "WebCoreNSStringExtras.h" +#import "WebCoreSystemInterface.h" #import "markup.h" #import <wtf/RetainPtr.h> @@ -209,7 +210,7 @@ void Pasteboard::writeURL(NSPasteboard* pasteboard, NSArray* types, const KURL& ASSERT(!url.isEmpty()); - NSURL *cocoaURL = url; + NSURL *cocoaURL = url.getNSURL(); NSString *userVisibleString = frame->editor()->client()->userVisibleString(cocoaURL); NSString *title = (NSString*)titleStr; @@ -269,7 +270,7 @@ void Pasteboard::writeImage(Node* node, const KURL& url, const String& title) ASSERT(node); Frame* frame = node->document()->frame(); - NSURL *cocoaURL = url; + NSURL *cocoaURL = url.getNSURL(); ASSERT(cocoaURL); NSArray* types = writableTypesForImage(); diff --git a/WebCore/platform/mac/PopupMenuMac.mm b/WebCore/platform/mac/PopupMenuMac.mm index 16cbe75..0d6565b 100644 --- a/WebCore/platform/mac/PopupMenuMac.mm +++ b/WebCore/platform/mac/PopupMenuMac.mm @@ -35,8 +35,7 @@ namespace WebCore { using namespace HTMLNames; PopupMenu::PopupMenu(PopupMenuClient* client) - : RefCounted<PopupMenu>(0) - , m_popupClient(client) + : m_popupClient(client) { } diff --git a/WebCore/platform/mac/SSLKeyGeneratorMac.mm b/WebCore/platform/mac/SSLKeyGeneratorMac.mm index 2b1190b..5c4161a 100644 --- a/WebCore/platform/mac/SSLKeyGeneratorMac.mm +++ b/WebCore/platform/mac/SSLKeyGeneratorMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -46,7 +46,7 @@ String signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String& ch { return [[WebCoreKeyGenerator sharedGenerator] signedPublicKeyAndChallengeStringWithStrengthIndex:keySizeIndex challenge:challengeString - pageURL:url]; + pageURL:url.getNSURL()]; } } diff --git a/WebCore/platform/mac/SharedBufferMac.mm b/WebCore/platform/mac/SharedBufferMac.mm index d241eaf..c3753c2 100644 --- a/WebCore/platform/mac/SharedBufferMac.mm +++ b/WebCore/platform/mac/SharedBufferMac.mm @@ -95,8 +95,7 @@ PassRefPtr<SharedBuffer> SharedBuffer::wrapNSData(NSData *nsData) } SharedBuffer::SharedBuffer(NSData *nsData) - : RefCounted<SharedBuffer>(0) - , m_nsData(nsData) + : m_nsData(nsData) { } diff --git a/WebCore/platform/mac/SystemTimeMac.cpp b/WebCore/platform/mac/SystemTimeMac.cpp index dd5e500..a541b1d 100644 --- a/WebCore/platform/mac/SystemTimeMac.cpp +++ b/WebCore/platform/mac/SystemTimeMac.cpp @@ -26,7 +26,8 @@ #include "config.h" #include "SystemTime.h" -#include <CoreGraphics/CGEventSource.h> +#include "WebCoreSystemInterface.h" + #include <CoreFoundation/CFDate.h> namespace WebCore { @@ -38,7 +39,7 @@ double currentTime() float userIdleTime() { - return static_cast<float>(CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType)); + return wkSecondsSinceLastInputEvent(); } } diff --git a/WebCore/platform/mac/MainThreadMac.mm b/WebCore/platform/mac/Threading.mm index 47329ef..02d39a3 100644 --- a/WebCore/platform/mac/MainThreadMac.mm +++ b/WebCore/platform/mac/Threading.mm @@ -27,7 +27,7 @@ */ #import "config.h" -#import "MainThread.h" +#import "Threading.h" @interface WebCoreFunctionWrapper : NSObject { WebCore::MainThreadFunction* m_function; diff --git a/WebCore/platform/mac/WebCoreSystemInterface.h b/WebCore/platform/mac/WebCoreSystemInterface.h index 873d3bf..fbbacd2 100644 --- a/WebCore/platform/mac/WebCoreSystemInterface.h +++ b/WebCore/platform/mac/WebCoreSystemInterface.h @@ -98,6 +98,7 @@ extern OSStatus (*wkGetATSStyleGroup)(ATSUStyle, void** styleGroup); extern CGFontRef (*wkGetCGFontFromNSFont)(NSFont*); extern NSFont* (*wkGetFontInLanguageForRange)(NSFont*, NSString*, NSRange); extern NSFont* (*wkGetFontInLanguageForCharacter)(NSFont*, UniChar); +extern void (*wkGetFontMetrics)(CGFontRef, int* ascent, int* descent, int* lineGap, unsigned* unitsPerEm); extern BOOL (*wkGetGlyphTransformedAdvances)(CGFontRef, NSFont*, CGAffineTransform*, ATSGlyphRef*, CGSize* advance); extern ATSLayoutRecord* (*wkGetGlyphVectorFirstRecord)(void* glyphVector); extern int (*wkGetGlyphVectorNumGlyphs)(void* glyphVector); @@ -120,6 +121,7 @@ extern NSDate *(*wkGetNSURLResponseLastModifiedDate)(NSURLResponse *response); extern BOOL (*wkGetNSURLResponseMustRevalidate)(NSURLResponse *response); extern void (*wkGetWheelEventDeltas)(NSEvent*, float* deltaX, float* deltaY, BOOL* continuous); extern OSStatus (*wkInitializeGlyphVector)(int count, void* glyphs); +extern NSString* (*wkPathFromFont)(NSFont*); extern void (*wkPopupMenu)(NSMenu*, NSPoint location, float width, NSView*, int selectedItem, NSFont*); extern int (*wkQTMovieDataRate)(QTMovie*); extern float (*wkQTMovieMaxTimeLoaded)(QTMovie*); @@ -135,12 +137,9 @@ extern void (*wkSetUpFontCache)(size_t); extern void (*wkSignalCFReadStreamEnd)(CFReadStreamRef stream); extern void (*wkSignalCFReadStreamError)(CFReadStreamRef stream, CFStreamError *error); extern void (*wkSignalCFReadStreamHasBytes)(CFReadStreamRef stream); - -#ifdef BUILDING_ON_TIGER -extern void (*wkGetFontMetrics)(CGFontRef, int* ascent, int* descent, int* lineGap, unsigned* unitsPerEm); extern BOOL (*wkSupportsMultipartXMixedReplace)(NSMutableURLRequest *); -#endif - +extern Class (*wkNSURLProtocolClassForReqest)(NSURLRequest *); +extern float (*wkSecondsSinceLastInputEvent)(void); #ifdef __cplusplus } #endif diff --git a/WebCore/platform/mac/WebCoreSystemInterface.mm b/WebCore/platform/mac/WebCoreSystemInterface.mm index 6fea621..9e8856d 100644 --- a/WebCore/platform/mac/WebCoreSystemInterface.mm +++ b/WebCore/platform/mac/WebCoreSystemInterface.mm @@ -41,6 +41,7 @@ OSStatus (*wkGetATSStyleGroup)(ATSUStyle, void** styleGroup); CGFontRef (*wkGetCGFontFromNSFont)(NSFont*); NSFont* (*wkGetFontInLanguageForRange)(NSFont*, NSString*, NSRange); NSFont* (*wkGetFontInLanguageForCharacter)(NSFont*, UniChar); +void (*wkGetFontMetrics)(CGFontRef, int* ascent, int* descent, int* lineGap, unsigned* unitsPerEm); BOOL (*wkGetGlyphTransformedAdvances)(CGFontRef, NSFont*, CGAffineTransform*, ATSGlyphRef*, CGSize* advance); ATSLayoutRecord* (*wkGetGlyphVectorFirstRecord)(void* glyphVector); int (*wkGetGlyphVectorNumGlyphs)(void* glyphVector); @@ -63,6 +64,7 @@ NSDate *(*wkGetNSURLResponseLastModifiedDate)(NSURLResponse *response); BOOL (*wkGetNSURLResponseMustRevalidate)(NSURLResponse *response); void (*wkGetWheelEventDeltas)(NSEvent*, float* deltaX, float* deltaY, BOOL* continuous); OSStatus (*wkInitializeGlyphVector)(int count, void* glyphs); +NSString* (*wkPathFromFont)(NSFont*); void (*wkPopupMenu)(NSMenu*, NSPoint location, float width, NSView*, int selectedItem, NSFont*); int (*wkQTMovieDataRate)(QTMovie*); float (*wkQTMovieMaxTimeLoaded)(QTMovie*); @@ -88,8 +90,6 @@ CFReadStreamRef (*wkCreateCustomCFReadStream)(void *(*formCreate)(CFReadStreamRe void (*wkSetNSURLConnectionDefersCallbacks)(NSURLConnection *, BOOL); void (*wkSetNSURLRequestShouldContentSniff)(NSMutableURLRequest *, BOOL); id (*wkCreateNSURLConnectionDelegateProxy)(void); - -#ifdef BUILDING_ON_TIGER -void (*wkGetFontMetrics)(CGFontRef, int* ascent, int* descent, int* lineGap, unsigned* unitsPerEm); BOOL (*wkSupportsMultipartXMixedReplace)(NSMutableURLRequest *); -#endif +Class (*wkNSURLProtocolClassForReqest)(NSURLRequest *); +float (*wkSecondsSinceLastInputEvent)(void); diff --git a/WebCore/platform/network/AuthenticationChallengeBase.cpp b/WebCore/platform/network/AuthenticationChallenge.cpp index df80441..f7275f3 100644 --- a/WebCore/platform/network/AuthenticationChallengeBase.cpp +++ b/WebCore/platform/network/AuthenticationChallenge.cpp @@ -29,17 +29,14 @@ namespace WebCore { -AuthenticationChallengeBase::AuthenticationChallengeBase() +AuthenticationChallenge::AuthenticationChallenge() : m_isNull(true) , m_previousFailureCount(0) { } -AuthenticationChallengeBase::AuthenticationChallengeBase(const ProtectionSpace& protectionSpace, - const Credential& proposedCredential, - unsigned previousFailureCount, - const ResourceResponse& response, - const ResourceError& error) +AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, + unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error) : m_isNull(false) , m_protectionSpace(protectionSpace) , m_proposedCredential(proposedCredential) @@ -49,49 +46,62 @@ AuthenticationChallengeBase::AuthenticationChallengeBase(const ProtectionSpace& { } -unsigned AuthenticationChallengeBase::previousFailureCount() const +unsigned AuthenticationChallenge::previousFailureCount() const { return m_previousFailureCount; } -const Credential& AuthenticationChallengeBase::proposedCredential() const +const Credential& AuthenticationChallenge::proposedCredential() const { return m_proposedCredential; } -const ProtectionSpace& AuthenticationChallengeBase::protectionSpace() const +const ProtectionSpace& AuthenticationChallenge::protectionSpace() const { return m_protectionSpace; } -const ResourceResponse& AuthenticationChallengeBase::failureResponse() const +const ResourceResponse& AuthenticationChallenge::failureResponse() const { return m_failureResponse; } -const ResourceError& AuthenticationChallengeBase::error() const +const ResourceError& AuthenticationChallenge::error() const { return m_error; } -bool AuthenticationChallengeBase::isNull() const +bool AuthenticationChallenge::isNull() const { return m_isNull; } -void AuthenticationChallengeBase::nullify() +void AuthenticationChallenge::nullify() { m_isNull = true; } -bool AuthenticationChallengeBase::compare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) +bool operator==(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { - if (a.isNull() && b.isNull()) + if (a.isNull() != b.isNull()) + return false; + if (a.isNull()) return true; - - if (a.isNull() || b.isNull()) + +#if PLATFORM(MAC) + if (a.sender() != b.sender()) return false; + if (a.nsURLAuthenticationChallenge() != b.nsURLAuthenticationChallenge()) + return false; +#elif USE(CFNETWORK) + if (a.sourceHandle() != b.sourceHandle()) + return false; + + if (a.cfURLAuthChallengeRef() != b.cfURLAuthChallengeRef()) + return false; +#endif + if (a.protectionSpace() != b.protectionSpace()) return false; @@ -107,7 +117,14 @@ bool AuthenticationChallengeBase::compare(const AuthenticationChallenge& a, cons if (a.error() != b.error()) return false; - return AuthenticationChallenge::platformCompare(a, b); + return true; +} + +bool operator!=(const AuthenticationChallenge& a, const AuthenticationChallenge& b) +{ + return !(a == b); } } + + diff --git a/WebCore/platform/network/mac/AuthenticationChallenge.h b/WebCore/platform/network/AuthenticationChallenge.h index e8f3a2d..3e46ea4 100644 --- a/WebCore/platform/network/mac/AuthenticationChallenge.h +++ b/WebCore/platform/network/AuthenticationChallenge.h @@ -25,8 +25,15 @@ #ifndef AuthenticationChallenge_h #define AuthenticationChallenge_h -#include "AuthenticationChallengeBase.h" +#include "Credential.h" +#include "ProtectionSpace.h" +#include "ResourceResponse.h" +#include "ResourceError.h" +#include <wtf/RefPtr.h> + + +#if PLATFORM(MAC) #include <wtf/RetainPtr.h> #ifndef __OBJC__ typedef struct objc_object *id; @@ -34,26 +41,64 @@ class NSURLAuthenticationChallenge; #else @class NSURLAuthenticationChallenge; #endif +#endif + +#if USE(CFNETWORK) +typedef struct _CFURLAuthChallenge* CFURLAuthChallengeRef; +#endif + namespace WebCore { -class AuthenticationChallenge : public AuthenticationChallengeBase { +class ResourceHandle; + +class AuthenticationChallenge { + public: - AuthenticationChallenge() {} + AuthenticationChallenge(); AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error); +#if PLATFORM(MAC) AuthenticationChallenge(NSURLAuthenticationChallenge *); +#elif USE(CFNETWORK) + AuthenticationChallenge(CFURLAuthChallengeRef, ResourceHandle* sourceHandle); +#endif + unsigned previousFailureCount() const; + const Credential& proposedCredential() const; + const ProtectionSpace& protectionSpace() const; + const ResourceResponse& failureResponse() const; + const ResourceError& error() const; + + bool isNull() const; + void nullify(); + +#if PLATFORM(MAC) id sender() const { return m_sender.get(); } NSURLAuthenticationChallenge *nsURLAuthenticationChallenge() const { return m_macChallenge.get(); } - +#elif USE(CFNETWORK) + ResourceHandle* sourceHandle() const { return m_sourceHandle.get(); } + CFURLAuthChallengeRef cfURLAuthChallengeRef() const { return m_cfChallenge.get(); } +#endif private: - friend class AuthenticationChallengeBase; - static bool platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b); + bool m_isNull; + ProtectionSpace m_protectionSpace; + Credential m_proposedCredential; + unsigned m_previousFailureCount; + ResourceResponse m_failureResponse; + ResourceError m_error; +#if PLATFORM(MAC) RetainPtr<id> m_sender; RetainPtr<NSURLAuthenticationChallenge *> m_macChallenge; +#elif USE(CFNETWORK) + RefPtr<ResourceHandle> m_sourceHandle; + RetainPtr<CFURLAuthChallengeRef> m_cfChallenge; +#endif + }; +bool operator==(const AuthenticationChallenge&, const AuthenticationChallenge&); +bool operator!=(const AuthenticationChallenge&, const AuthenticationChallenge&); } - #endif + diff --git a/WebCore/platform/network/AuthenticationChallengeBase.h b/WebCore/platform/network/AuthenticationChallengeBase.h deleted file mode 100644 index 5810a6d..0000000 --- a/WebCore/platform/network/AuthenticationChallengeBase.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 AuthenticationChallengeBase_h -#define AuthenticationChallengeBase_h - -#include "Credential.h" -#include "ProtectionSpace.h" -#include "ResourceResponse.h" -#include "ResourceError.h" - -namespace WebCore { - -class AuthenticationChallenge; - -class AuthenticationChallengeBase { -public: - AuthenticationChallengeBase(); - AuthenticationChallengeBase(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error); - - unsigned previousFailureCount() const; - const Credential& proposedCredential() const; - const ProtectionSpace& protectionSpace() const; - const ResourceResponse& failureResponse() const; - const ResourceError& error() const; - - bool isNull() const; - void nullify(); - - static bool compare(const AuthenticationChallenge& a, const AuthenticationChallenge& b); - -protected: - // The AuthenticationChallenge subclass may "shadow" this method to compare platform specific fields - static bool platformCompare(const AuthenticationChallengeBase& a, const AuthenticationChallengeBase& b) { return true; } - - bool m_isNull; - ProtectionSpace m_protectionSpace; - Credential m_proposedCredential; - unsigned m_previousFailureCount; - ResourceResponse m_failureResponse; - ResourceError m_error; -}; - -inline bool operator==(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { return AuthenticationChallengeBase::compare(a, b); } -inline bool operator!=(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { return !(a == b); } - -} - -#endif diff --git a/WebCore/platform/network/FormData.cpp b/WebCore/platform/network/FormData.cpp index 3453169..ed2d811 100644 --- a/WebCore/platform/network/FormData.cpp +++ b/WebCore/platform/network/FormData.cpp @@ -25,45 +25,25 @@ namespace WebCore { -inline FormData::FormData() +FormData::FormData(const void* data, size_t size) { + appendData(data, size); } -PassRefPtr<FormData> FormData::create() +FormData::FormData(const CString& s) { - return adoptRef(new FormData); + appendData(s.data(), s.length()); } -PassRefPtr<FormData> FormData::create(const void* data, size_t size) -{ - RefPtr<FormData> result = create(); - result->appendData(data, size); - return result.release(); -} - -PassRefPtr<FormData> FormData::create(const CString& string) -{ - RefPtr<FormData> result = create(); - result->appendData(string.data(), string.length()); - return result.release(); -} - -PassRefPtr<FormData> FormData::create(const Vector<char>& vector) -{ - RefPtr<FormData> result = create(); - result->appendData(vector.data(), vector.size()); - return result.release(); -} - -inline FormData::FormData(const FormData& data) - : RefCounted<FormData>(1) +FormData::FormData(const FormData& data) + : RefCounted<FormData>() , m_elements(data.m_elements) { } PassRefPtr<FormData> FormData::copy() const { - return adoptRef(new FormData(*this)); + return new FormData(*this); } void FormData::appendData(const void* data, size_t size) diff --git a/WebCore/platform/network/FormData.h b/WebCore/platform/network/FormData.h index 168020e..c33ef52 100644 --- a/WebCore/platform/network/FormData.h +++ b/WebCore/platform/network/FormData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -59,10 +59,9 @@ inline bool operator!=(const FormDataElement& a, const FormDataElement& b) class FormData : public RefCounted<FormData> { public: - static PassRefPtr<FormData> create(); - static PassRefPtr<FormData> create(const void*, size_t); - static PassRefPtr<FormData> create(const CString&); - static PassRefPtr<FormData> create(const Vector<char>&); + FormData() { } + FormData(const void* data, size_t); + FormData(const CString&); PassRefPtr<FormData> copy() const; void appendData(const void* data, size_t); @@ -75,10 +74,9 @@ public: const Vector<FormDataElement>& elements() const { return m_elements; } private: - FormData(); - FormData(const FormData&); + FormData(const FormData&); - Vector<FormDataElement> m_elements; + Vector<FormDataElement> m_elements; }; inline bool operator==(const FormData& a, const FormData& b) diff --git a/WebCore/platform/network/HTTPParsers.cpp b/WebCore/platform/network/HTTPParsers.cpp index 0858fc9..e71be5f 100644 --- a/WebCore/platform/network/HTTPParsers.cpp +++ b/WebCore/platform/network/HTTPParsers.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -103,8 +103,7 @@ bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& del String filenameFromHTTPContentDisposition(const String& value) { - Vector<String> keyValuePairs; - value.split(';', keyValuePairs); + Vector<String> keyValuePairs = value.split(';'); unsigned length = keyValuePairs.size(); for (unsigned i = 0; i < length; i++) { diff --git a/WebCore/platform/network/qt/ResourceError.h b/WebCore/platform/network/ResourceError.cpp index 0a8eb64..fd0674c 100644 --- a/WebCore/platform/network/qt/ResourceError.h +++ b/WebCore/platform/network/ResourceError.cpp @@ -24,26 +24,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceError_h -#define ResourceError_h +#include "config.h" +#include "ResourceError.h" -#include "ResourceErrorBase.h" - -namespace WebCore { - -class ResourceError : public ResourceErrorBase -{ -public: - ResourceError() - { - } - - ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) - : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) - { - } -}; - -} - -#endif // ResourceError_h_ diff --git a/WebCore/platform/network/ResourceError.h b/WebCore/platform/network/ResourceError.h new file mode 100644 index 0000000..165dccd --- /dev/null +++ b/WebCore/platform/network/ResourceError.h @@ -0,0 +1,168 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 ResourceError_h +#define ResourceError_h + +#include "PlatformString.h" + +#if PLATFORM(CF) +#include <wtf/RetainPtr.h> +#endif + +#ifdef __OBJC__ +@class NSError; +#else +class NSError; +#endif + +#if USE(CFNETWORK) +#include <CoreFoundation/CFStream.h> +#endif + +namespace WebCore { + + class ResourceError { + public: + ResourceError() + : m_errorCode(0) +#if PLATFORM(CF) + , m_dataIsUpToDate(true) +#endif + , m_isNull(true) + { + } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : m_domain(domain) + , m_errorCode(errorCode) + , m_failingURL(failingURL) + , m_localizedDescription(localizedDescription) +#if PLATFORM(CF) + , m_dataIsUpToDate(true) +#endif + , m_isNull(false) + { + } + +#if PLATFORM(CF) +#if PLATFORM(MAC) + ResourceError(NSError* error) +#else + ResourceError(CFStreamError error); + ResourceError(CFErrorRef error) +#endif + : m_dataIsUpToDate(false) + , m_platformError(error) + , m_isNull(!error) + { + } +#endif + +#if 0 + static const String CocoaErrorDomain; + static const String POSIXDomain; + static const String OSStatusDomain; + static const String MachDomain; + static const String WebKitDomain; +#endif + + bool isNull() const { return m_isNull; } + + const String& domain() const { unpackPlatformErrorIfNeeded(); return m_domain; } + int errorCode() const { unpackPlatformErrorIfNeeded(); return m_errorCode; } + const String& failingURL() const { unpackPlatformErrorIfNeeded(); return m_failingURL; } + const String& localizedDescription() const { unpackPlatformErrorIfNeeded(); return m_localizedDescription; } + +#if PLATFORM(CF) +#if PLATFORM(MAC) + operator NSError*() const; +#else + operator CFErrorRef() const; + operator CFStreamError() const; +#endif +#endif + + private: + void unpackPlatformErrorIfNeeded() const + { +#if PLATFORM(CF) + if (!m_dataIsUpToDate) + const_cast<ResourceError*>(this)->unpackPlatformError(); +#endif + } + +#if PLATFORM(CF) + void unpackPlatformError(); +#endif + + String m_domain; + int m_errorCode; + String m_failingURL; + String m_localizedDescription; + +#if PLATFORM(CF) + bool m_dataIsUpToDate; +#endif +#if PLATFORM(MAC) + mutable RetainPtr<NSError> m_platformError; +#elif PLATFORM(CF) + mutable RetainPtr<CFErrorRef> m_platformError; +#endif + bool m_isNull; +}; + +inline bool operator==(const ResourceError& a, const ResourceError& b) +{ + if (a.isNull() && b.isNull()) + return true; + if (a.isNull() || b.isNull()) + return false; + if (a.domain() != b.domain()) + return false; + if (a.errorCode() != b.errorCode()) + return false; + if (a.failingURL() != b.failingURL()) + return false; + if (a.localizedDescription() != b.localizedDescription()) + return false; +#if PLATFORM(CF) +#if PLATFORM(MAC) + if ((NSError *)a != (NSError *)b) + return false; +#else + if ((CFErrorRef)a != (CFErrorRef)b) + return false; +#endif +#endif + return true; +} + +inline bool operator!=(const ResourceError& a, const ResourceError& b) { return !(a == b); } + +} // namespace WebCore + +#endif // ResourceError_h_ diff --git a/WebCore/platform/network/ResourceErrorBase.h b/WebCore/platform/network/ResourceErrorBase.h deleted file mode 100644 index ccdf36a..0000000 --- a/WebCore/platform/network/ResourceErrorBase.h +++ /dev/null @@ -1,83 +0,0 @@ -// -*- mode: c++; c-basic-offset: 4 -*- -/* - * Copyright (C) 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 ResourceErrorBase_h -#define ResourceErrorBase_h - -#include "PlatformString.h" - -namespace WebCore { - -class ResourceError; - -class ResourceErrorBase { -public: - bool isNull() const { return m_isNull; } - - const String& domain() const { lazyInit(); return m_domain; } - int errorCode() const { lazyInit(); return m_errorCode; } - const String& failingURL() const { lazyInit(); return m_failingURL; } - const String& localizedDescription() const { lazyInit(); return m_localizedDescription; } - - static bool compare(const ResourceError& a, const ResourceError& b); - -protected: - ResourceErrorBase() - : m_errorCode(0) - , m_isNull(true) - { - } - - ResourceErrorBase(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) - : m_domain(domain) - , m_errorCode(errorCode) - , m_failingURL(failingURL) - , m_localizedDescription(localizedDescription) - , m_isNull(false) - { - } - - void lazyInit() const; - - // The ResourceError subclass may "shadow" this method to lazily initialize platform specific fields - void platformLazyInit() {} - - // The ResourceError subclass may "shadow" this method to compare platform specific fields - static bool platformCompare(const ResourceError& a, const ResourceError& b) { return true; } - - String m_domain; - int m_errorCode; - String m_failingURL; - String m_localizedDescription; - bool m_isNull; -}; - -inline bool operator==(const ResourceError& a, const ResourceError& b) { return ResourceErrorBase::compare(a, b); } -inline bool operator!=(const ResourceError& a, const ResourceError& b) { return !(a == b); } - -} // namespace WebCore - -#endif // ResourceErrorBase_h_ diff --git a/WebCore/platform/network/ResourceHandle.cpp b/WebCore/platform/network/ResourceHandle.cpp index ef07a05..969a9f6 100644 --- a/WebCore/platform/network/ResourceHandle.cpp +++ b/WebCore/platform/network/ResourceHandle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -43,7 +43,7 @@ ResourceHandle::ResourceHandle(const ResourceRequest& request, ResourceHandleCli PassRefPtr<ResourceHandle> ResourceHandle::create(const ResourceRequest& request, ResourceHandleClient* client, Frame* frame, bool defersLoading, bool shouldContentSniff, bool mightDownloadFromHandle) { - RefPtr<ResourceHandle> newHandle(adoptRef(new ResourceHandle(request, client, defersLoading, shouldContentSniff, mightDownloadFromHandle))); + RefPtr<ResourceHandle> newHandle(new ResourceHandle(request, client, defersLoading, shouldContentSniff, mightDownloadFromHandle)); if (!portAllowed(request)) { newHandle->scheduleBlockedFailure(); @@ -172,11 +172,11 @@ bool ResourceHandle::portAllowed(const ResourceRequest& request) return true; // Allow ports 21 and 22 for FTP URLs, as Mozilla does. - if ((port == 21 || port == 22) && request.url().protocolIs("ftp")) + if ((port == 21 || port == 22) && request.url().deprecatedString().startsWith("ftp:", false)) return true; // Allow any port number in a file URL, since the port number is ignored. - if (request.url().protocolIs("file")) + if (request.url().deprecatedString().startsWith("file:", false)) return true; return false; diff --git a/WebCore/platform/network/ResourceHandle.h b/WebCore/platform/network/ResourceHandle.h index e5a8065..c94a875 100644 --- a/WebCore/platform/network/ResourceHandle.h +++ b/WebCore/platform/network/ResourceHandle.h @@ -134,7 +134,7 @@ public: friend LRESULT __stdcall ResourceHandleWndProc(HWND, unsigned message, WPARAM, LPARAM); #endif -#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX) +#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(WX) || defined(ANDROID) ResourceHandleInternal* getInternal() { return d.get(); } #endif diff --git a/WebCore/platform/network/ResourceHandleInternal.h b/WebCore/platform/network/ResourceHandleInternal.h index 16f960e..c4b8b25 100644 --- a/WebCore/platform/network/ResourceHandleInternal.h +++ b/WebCore/platform/network/ResourceHandleInternal.h @@ -60,6 +60,12 @@ class NSURLConnection; #endif #endif +#ifdef ANDROID_BRIDGE +namespace android { + class WebCoreResourceLoader; +} +#endif + // The allocations and releases in ResourceHandleInternal are // Cocoa-exception-free (either simple Foundation classes or // WebCoreResourceLoaderImp which avoids doing work in dealloc). @@ -111,6 +117,9 @@ namespace WebCore { #elif USE(CFNETWORK) , m_currentCFChallenge(0) #endif +#ifdef ANDROID_BRIDGE + , m_loader(0) +#endif { } @@ -174,6 +183,9 @@ namespace WebCore { #if USE(CFNETWORK) CFURLAuthChallengeRef m_currentCFChallenge; #endif +#ifdef ANDROID_BRIDGE + android::WebCoreResourceLoader* m_loader; +#endif AuthenticationChallenge m_currentWebChallenge; }; diff --git a/WebCore/platform/network/ResourceResponseBase.cpp b/WebCore/platform/network/ResourceResponseBase.cpp index 0c41847..06cc5ce 100644 --- a/WebCore/platform/network/ResourceResponseBase.cpp +++ b/WebCore/platform/network/ResourceResponseBase.cpp @@ -30,9 +30,14 @@ namespace WebCore { +inline const ResourceResponse& ResourceResponseBase::asResourceResponse() const +{ + return *static_cast<const ResourceResponse*>(this); +} + bool ResourceResponseBase::isHTTP() const { - lazyInit(); + updateResourceResponse(); String protocol = m_url.protocol(); @@ -41,14 +46,14 @@ bool ResourceResponseBase::isHTTP() const const KURL& ResourceResponseBase::url() const { - lazyInit(); + updateResourceResponse(); return m_url; } void ResourceResponseBase::setUrl(const KURL& url) { - lazyInit(); + updateResourceResponse(); m_isNull = false; m_url = url; @@ -56,14 +61,14 @@ void ResourceResponseBase::setUrl(const KURL& url) const String& ResourceResponseBase::mimeType() const { - lazyInit(); + updateResourceResponse(); return m_mimeType; } void ResourceResponseBase::setMimeType(const String& mimeType) { - lazyInit(); + updateResourceResponse(); m_isNull = false; m_mimeType = mimeType; @@ -71,14 +76,14 @@ void ResourceResponseBase::setMimeType(const String& mimeType) long long ResourceResponseBase::expectedContentLength() const { - lazyInit(); + updateResourceResponse(); return m_expectedContentLength; } void ResourceResponseBase::setExpectedContentLength(long long expectedContentLength) { - lazyInit(); + updateResourceResponse(); m_isNull = false; m_expectedContentLength = expectedContentLength; @@ -86,14 +91,14 @@ void ResourceResponseBase::setExpectedContentLength(long long expectedContentLen const String& ResourceResponseBase::textEncodingName() const { - lazyInit(); + updateResourceResponse(); return m_textEncodingName; } void ResourceResponseBase::setTextEncodingName(const String& encodingName) { - lazyInit(); + updateResourceResponse(); m_isNull = false; m_textEncodingName = encodingName; @@ -102,14 +107,14 @@ void ResourceResponseBase::setTextEncodingName(const String& encodingName) // FIXME should compute this on the fly const String& ResourceResponseBase::suggestedFilename() const { - lazyInit(); + updateResourceResponse(); return m_suggestedFilename; } void ResourceResponseBase::setSuggestedFilename(const String& suggestedName) { - lazyInit(); + updateResourceResponse(); m_isNull = false; m_suggestedFilename = suggestedName; @@ -117,56 +122,56 @@ void ResourceResponseBase::setSuggestedFilename(const String& suggestedName) int ResourceResponseBase::httpStatusCode() const { - lazyInit(); + updateResourceResponse(); return m_httpStatusCode; } void ResourceResponseBase::setHTTPStatusCode(int statusCode) { - lazyInit(); + updateResourceResponse(); m_httpStatusCode = statusCode; } const String& ResourceResponseBase::httpStatusText() const { - lazyInit(); + updateResourceResponse(); return m_httpStatusText; } void ResourceResponseBase::setHTTPStatusText(const String& statusText) { - lazyInit(); + updateResourceResponse(); m_httpStatusText = statusText; } String ResourceResponseBase::httpHeaderField(const String& name) const { - lazyInit(); + updateResourceResponse(); return m_httpHeaderFields.get(name); } void ResourceResponseBase::setHTTPHeaderField(const String& name, const String& value) { - lazyInit(); + updateResourceResponse(); m_httpHeaderFields.set(name, value); } const HTTPHeaderMap& ResourceResponseBase::httpHeaderFields() const { - lazyInit(); + updateResourceResponse(); return m_httpHeaderFields; } bool ResourceResponseBase::isAttachment() const { - lazyInit(); + updateResourceResponse(); String value = m_httpHeaderFields.get("Content-Disposition"); int loc = value.find(';'); @@ -178,38 +183,43 @@ bool ResourceResponseBase::isAttachment() const void ResourceResponseBase::setExpirationDate(time_t expirationDate) { - lazyInit(); + updateResourceResponse(); m_expirationDate = expirationDate; } time_t ResourceResponseBase::expirationDate() const { - lazyInit(); + updateResourceResponse(); return m_expirationDate; } void ResourceResponseBase::setLastModifiedDate(time_t lastModifiedDate) { - lazyInit(); + updateResourceResponse(); m_lastModifiedDate = lastModifiedDate; } time_t ResourceResponseBase::lastModifiedDate() const { - lazyInit(); + updateResourceResponse(); return m_lastModifiedDate; } -void ResourceResponseBase::lazyInit() const +void ResourceResponseBase::updateResourceResponse() const { - const_cast<ResourceResponse*>(static_cast<const ResourceResponse*>(this))->platformLazyInit(); + if (m_isUpToDate) + return; + + const_cast<ResourceResponse&>(asResourceResponse()).doUpdateResourceResponse(); + + m_isUpToDate = true; } -bool ResourceResponseBase::compare(const ResourceResponse& a, const ResourceResponse& b) +bool operator==(const ResourceResponse& a, const ResourceResponse& b) { if (a.isNull() != b.isNull()) return false; @@ -231,7 +241,11 @@ bool ResourceResponseBase::compare(const ResourceResponse& a, const ResourceResp return false; if (a.expirationDate() != b.expirationDate()) return false; - return ResourceResponse::platformCompare(a, b); -} +#if PLATFORM(MAC) + if (a.nsURLResponse() != b.nsURLResponse()) + return false; +#endif + return true; + } } diff --git a/WebCore/platform/network/ResourceResponseBase.h b/WebCore/platform/network/ResourceResponseBase.h index 503156a..50b6c43 100644 --- a/WebCore/platform/network/ResourceResponseBase.h +++ b/WebCore/platform/network/ResourceResponseBase.h @@ -77,16 +77,21 @@ class ResourceResponseBase { void setLastModifiedDate(time_t); time_t lastModifiedDate() const; - static bool compare(const ResourceResponse& a, const ResourceResponse& b); - - const ResourceResponse& asResourceResponse() const; + inline const ResourceResponse& asResourceResponse() const; protected: + // Used when response is initialized from a platform representation + ResourceResponseBase(bool isNull) + : m_isUpToDate(false) + , m_isNull(isNull) + { + } + ResourceResponseBase() : m_expectedContentLength(0) , m_httpStatusCode(0) , m_expirationDate(0) - , m_lastModifiedDate(0) + , m_isUpToDate(true) , m_isNull(true) { } @@ -99,34 +104,29 @@ class ResourceResponseBase { , m_suggestedFilename(filename) , m_httpStatusCode(0) , m_expirationDate(0) - , m_lastModifiedDate(0) + , m_isUpToDate(true) , m_isNull(false) { } - void lazyInit() const; - - // The ResourceResponse subclass may "shadow" this method to lazily initialize platform specific fields - void platformLazyInit() {} - - // The ResourceResponse subclass may "shadow" this method to compare platform specific fields - static bool platformCompare(const ResourceResponse& a, const ResourceResponse& b) { return true; } + void updateResourceResponse() const; KURL m_url; String m_mimeType; long long m_expectedContentLength; String m_textEncodingName; String m_suggestedFilename; - int m_httpStatusCode; + mutable int m_httpStatusCode; String m_httpStatusText; HTTPHeaderMap m_httpHeaderFields; time_t m_expirationDate; time_t m_lastModifiedDate; + mutable bool m_isUpToDate; bool m_isNull; }; -inline bool operator==(const ResourceResponse& a, const ResourceResponse& b) { return ResourceResponseBase::compare(a, b); } +bool operator==(const ResourceResponse& a, const ResourceResponse& b); inline bool operator!=(const ResourceResponse& a, const ResourceResponse& b) { return !(a == b); } } // namespace WebCore diff --git a/WebCore/platform/network/android/ResourceHandleAndroid.cpp b/WebCore/platform/network/android/ResourceHandleAndroid.cpp new file mode 100644 index 0000000..35b3c5b --- /dev/null +++ b/WebCore/platform/network/android/ResourceHandleAndroid.cpp @@ -0,0 +1,144 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "config.h" +#include "ResourceHandle.h" + +#include "DocLoader.h" +#include "FrameAndroid.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleInternal.h" +#include "WebCoreFrameBridge.h" +#include "WebCoreResourceLoader.h" + +// #define notImplemented() do { fprintf(stderr, "FIXME: UNIMPLEMENTED %s %s:%d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__); } while(0) + +namespace WebCore { + +ResourceHandleInternal::~ResourceHandleInternal() +{ + Release(m_loader); +} + +ResourceHandle::~ResourceHandle() +{ +} + +bool ResourceHandle::start(Frame* frame) +{ + FrameAndroid* f = Android(frame); + android::WebCoreResourceLoader* loader; + bool highPriority = true; + CachedResource* r = d->m_request.getCachedResource(); + if (r) { + CachedResource::Type t = r->type(); + highPriority = !(t == CachedResource::ImageResource || + t == CachedResource::FontResource); + } + loader = f->bridge()->startLoadingResource(this, d->m_request, highPriority, false); + + if (loader) { + Release(d->m_loader); + d->m_loader = loader; + } + + return loader != NULL; +} + +void ResourceHandle::cancel() +{ + if (d->m_loader) + d->m_loader->cancel(); +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + return 0; +} + +bool ResourceHandle::supportsBufferedData() +{ + // We don't support buffering data on the native side. + return false; +} + +void ResourceHandle::setDefersLoading(bool defers) +{ + notImplemented(); +} + +/* +* This static method is called to check to see if a POST response is in +* the cache. The JNI call through to the HTTP cache stored on the Java +* side may be slow, but is only used during a navigation to +* a POST response. +*/ +bool ResourceHandle::willLoadFromCache(ResourceRequest& request) +{ + // set the cache policy correctly, copied from + // network/mac/ResourceHandleMac.mm + request.setCachePolicy(ReturnCacheDataDontLoad); + return android::WebCoreResourceLoader::willLoadFromCache(request.url()); +} + +bool ResourceHandle::loadsBlocked() +{ + // FIXME, need to check whether connection pipe is blocked. + // return false for now + return false; +} + +// Class to handle synchronized loading of resources. +class SyncLoader : public ResourceHandleClient { +public: + SyncLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) { + m_error = &error; + m_response = &response; + m_data = &data; + } + ~SyncLoader() {} + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse& response) { + *m_response = response; + } + + virtual void didReceiveData(ResourceHandle*, const char* data, int len, int lengthReceived) { + m_data->append(data, len); + } + + virtual void didFail(ResourceHandle*, const ResourceError& error) { + *m_error = error; + } + +private: + ResourceError* m_error; + ResourceResponse* m_response; + Vector<char>* m_data; +}; + +void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, + ResourceError& error, ResourceResponse& response, Vector<char>& data, + Frame* frame) +{ + FrameAndroid* f = Android(frame); + SyncLoader s(error, response, data); + ResourceHandle h(request, &s, false, false, false); + // This blocks until the load is finished. + f->bridge()->startLoadingResource(&h, request, true, true); +} + +} // namespace WebCore diff --git a/WebCore/platform/network/android/ResourceRequest.h b/WebCore/platform/network/android/ResourceRequest.h new file mode 100644 index 0000000..33676ae --- /dev/null +++ b/WebCore/platform/network/android/ResourceRequest.h @@ -0,0 +1,93 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 ResourceRequest_h +#define ResourceRequest_h + +#include "CachedResource.h" +#include "ResourceRequestBase.h" + +namespace WebCore { + + struct ResourceRequest : ResourceRequestBase { + + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(url.deprecatedString()), UseProtocolCachePolicy) + , m_cachedResource(0) +#ifdef ANDROID_USER_GESTURE + , m_wasUserGesture(false) +#endif + { + } + + ResourceRequest(const KURL& url) + : ResourceRequestBase(url, UseProtocolCachePolicy) + , m_cachedResource(0) +#ifdef ANDROID_USER_GESTURE + , m_wasUserGesture(false) +#endif + { + } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + , m_cachedResource(0) +#ifdef ANDROID_USER_GESTURE + , m_wasUserGesture(false) +#endif + { + setHTTPReferrer(referrer); + } + + ResourceRequest() + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + , m_cachedResource(0) +#ifdef ANDROID_USER_GESTURE + , m_wasUserGesture(false) +#endif + { + } + + void doUpdatePlatformRequest() {} + void doUpdateResourceRequest() {} + void setCachedResource(CachedResource* r) { m_cachedResource = r; } + CachedResource* getCachedResource() const { return m_cachedResource; } +#ifdef ANDROID_USER_GESTURE + void setUserGesture(bool userGesture) { m_wasUserGesture = userGesture; } + bool userGesture() const { return m_wasUserGesture; } +#endif + private: + friend class ResourceRequestBase; + CachedResource* m_cachedResource; +#ifdef ANDROID_USER_GESTURE + bool m_wasUserGesture; +#endif + }; + +} // namespace WebCore + +#endif // ResourceRequest_h diff --git a/WebCore/platform/network/ResourceErrorBase.cpp b/WebCore/platform/network/android/ResourceResponse.h index 5dfc890..94c93f5 100644 --- a/WebCore/platform/network/ResourceErrorBase.cpp +++ b/WebCore/platform/network/android/ResourceResponse.h @@ -24,37 +24,37 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "ResourceError.h" +#ifndef ResourceResponse_h +#define ResourceResponse_h -namespace WebCore { +#include "ResourceResponseBase.h" -void ResourceErrorBase::lazyInit() const -{ - const_cast<ResourceError*>(static_cast<const ResourceError*>(this))->platformLazyInit(); -} +#include "NotImplemented.h" -bool ResourceErrorBase::compare(const ResourceError& a, const ResourceError& b) -{ - if (a.isNull() && b.isNull()) - return true; +namespace WebCore { - if (a.isNull() || b.isNull()) - return false; +class ResourceResponse : public ResourceResponseBase { +public: + ResourceResponse() + : ResourceResponseBase() + { + } - if (a.domain() != b.domain()) - return false; + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) + { + } - if (a.errorCode() != b.errorCode()) - return false; +private: + friend class ResourceResponseBase; - if (a.failingURL() != b.failingURL()) - return false; + void doUpdateResourceResponse() + { + notImplemented(); + } - if (a.localizedDescription() != b.localizedDescription()) - return false; +}; - return platformCompare(a, b); -} +} // namespace WebCore -} +#endif // ResourceResponse_h diff --git a/WebCore/platform/network/cf/AuthenticationCF.cpp b/WebCore/platform/network/cf/AuthenticationCF.cpp index bb05a39..72a6c9a 100644 --- a/WebCore/platform/network/cf/AuthenticationCF.cpp +++ b/WebCore/platform/network/cf/AuthenticationCF.cpp @@ -37,42 +37,18 @@ namespace WebCore { -AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace, - const Credential& proposedCredential, - unsigned previousFailureCount, - const ResourceResponse& response, - const ResourceError& error) - : AuthenticationChallengeBase(protectionSpace, - proposedCredential, - previousFailureCount, - response, - error) -{ -} - -AuthenticationChallenge::AuthenticationChallenge(CFURLAuthChallengeRef cfChallenge, - ResourceHandle* sourceHandle) - : AuthenticationChallengeBase(core(CFURLAuthChallengeGetProtectionSpace(cfChallenge)), - core(CFURLAuthChallengeGetProposedCredential(cfChallenge)), - CFURLAuthChallengeGetPreviousFailureCount(cfChallenge), - (CFURLResponseRef)CFURLAuthChallengeGetFailureResponse(cfChallenge), - CFURLAuthChallengeGetError(cfChallenge)) +AuthenticationChallenge::AuthenticationChallenge(CFURLAuthChallengeRef cfChallenge, ResourceHandle* sourceHandle) + : m_isNull(false) + , m_protectionSpace(core(CFURLAuthChallengeGetProtectionSpace(cfChallenge))) + , m_proposedCredential(core(CFURLAuthChallengeGetProposedCredential(cfChallenge))) + , m_previousFailureCount(CFURLAuthChallengeGetPreviousFailureCount(cfChallenge)) + , m_failureResponse((CFURLResponseRef)CFURLAuthChallengeGetFailureResponse(cfChallenge)) + , m_error(CFURLAuthChallengeGetError(cfChallenge)) , m_sourceHandle(sourceHandle) , m_cfChallenge(cfChallenge) { } -bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) -{ - if (a.sourceHandle() != b.sourceHandle()) - return false; - - if (a.cfURLAuthChallengeRef() != b.cfURLAuthChallengeRef()) - return false; - - return true; -} - CFURLAuthChallengeRef createCF(const AuthenticationChallenge& coreChallenge) { CFURLProtectionSpaceRef protectionSpace = createCF(coreChallenge.protectionSpace()); diff --git a/WebCore/platform/network/cf/AuthenticationChallenge.h b/WebCore/platform/network/cf/AuthenticationChallenge.h deleted file mode 100644 index 9697d7e..0000000 --- a/WebCore/platform/network/cf/AuthenticationChallenge.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 AuthenticationChallenge_h -#define AuthenticationChallenge_h - -#include "AuthenticationChallengeBase.h" -#include "ResourceHandle.h" -#include <wtf/RefPtr.h> - -typedef struct _CFURLAuthChallenge* CFURLAuthChallengeRef; - -namespace WebCore { - -class ResourceHandle; - -class AuthenticationChallenge : public AuthenticationChallengeBase { -public: - AuthenticationChallenge() {} - AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error); - AuthenticationChallenge(CFURLAuthChallengeRef, ResourceHandle* sourceHandle); - - ResourceHandle* sourceHandle() const { return m_sourceHandle.get(); } - CFURLAuthChallengeRef cfURLAuthChallengeRef() const { return m_cfChallenge.get(); } - -private: - friend class AuthenticationChallengeBase; - static bool platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b); - - RefPtr<ResourceHandle> m_sourceHandle; - RetainPtr<CFURLAuthChallengeRef> m_cfChallenge; -}; - -} - -#endif diff --git a/WebCore/platform/network/cf/FormDataStreamCFNet.cpp b/WebCore/platform/network/cf/FormDataStreamCFNet.cpp index 71fbfe7..c2416a1 100644 --- a/WebCore/platform/network/cf/FormDataStreamCFNet.cpp +++ b/WebCore/platform/network/cf/FormDataStreamCFNet.cpp @@ -389,11 +389,11 @@ void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData) PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef request) { if (RetainPtr<CFDataRef> bodyData = CFURLRequestCopyHTTPRequestBody(request)) - return FormData::create(CFDataGetBytePtr(bodyData.get()), CFDataGetLength(bodyData.get())); + return new FormData(CFDataGetBytePtr(bodyData.get()), CFDataGetLength(bodyData.get())); if (wkCanAccessCFURLRequestHTTPBodyParts()) { if (RetainPtr<CFArrayRef> bodyParts = wkCFURLRequestCopyHTTPRequestBodyParts(request)) { - RefPtr<FormData> formData = FormData::create(); + RefPtr<FormData> formData = new FormData(); CFIndex count = CFArrayGetCount(bodyParts.get()); for (CFIndex i = 0; i < count; i++) { @@ -405,8 +405,9 @@ PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef request) } else if (typeID == CFDataGetTypeID()) { CFDataRef data = (CFDataRef)bodyPart; formData->appendData(CFDataGetBytePtr(data), CFDataGetLength(data)); - } else + } else { ASSERT_NOT_REACHED(); + } } return formData.release(); } diff --git a/WebCore/platform/network/cf/ResourceError.h b/WebCore/platform/network/cf/ResourceError.h deleted file mode 100644 index 1cde47e..0000000 --- a/WebCore/platform/network/cf/ResourceError.h +++ /dev/null @@ -1,74 +0,0 @@ -// -*- mode: c++; c-basic-offset: 4 -*- -/* - * Copyright (C) 2006 Apple Computer, 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 ResourceError_h -#define ResourceError_h - -#include "ResourceErrorBase.h" - -#include <wtf/RetainPtr.h> -#include <CoreFoundation/CFStream.h> - -namespace WebCore { - -class ResourceError : public ResourceErrorBase { -public: - ResourceError() - : m_dataIsUpToDate(true) - { - } - - ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) - : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) - , m_dataIsUpToDate(true) - { - } - - ResourceError(CFStreamError error); - - ResourceError(CFErrorRef error) - : m_dataIsUpToDate(false) - , m_platformError(error) - { - m_isNull = !error; - } - - operator CFErrorRef() const; - operator CFStreamError() const; - -private: - friend class ResourceErrorBase; - - void platformLazyInit(); - static bool platformCompare(const ResourceError& a, const ResourceError& b); - - bool m_dataIsUpToDate; - mutable RetainPtr<CFErrorRef> m_platformError; -}; - -} // namespace WebCore - -#endif // ResourceError_h_ diff --git a/WebCore/platform/network/cf/ResourceErrorCF.cpp b/WebCore/platform/network/cf/ResourceErrorCF.cpp index 8e82cd5..f9ad76d 100644 --- a/WebCore/platform/network/cf/ResourceErrorCF.cpp +++ b/WebCore/platform/network/cf/ResourceErrorCF.cpp @@ -45,11 +45,10 @@ const CFStringRef failingURLKey = CFSTR("NSErrorFailingURLKey"); // FIXME: Once <rdar://problem/5050841> is fixed we can remove this constructor. ResourceError::ResourceError(CFStreamError error) - : m_dataIsUpToDate(true) + : m_errorCode(error.error) + , m_isNull(false) + , m_dataIsUpToDate(true) { - m_isNull = false; - m_errorCode = error.error; - switch(error.domain) { case kCFStreamErrorDomainCustom: m_domain ="NSCustomErrorDomain"; @@ -63,11 +62,8 @@ ResourceError::ResourceError(CFStreamError error) } } -void ResourceError::platformLazyInit() +void ResourceError::unpackPlatformError() { - if (m_dataIsUpToDate) - return; - if (!m_platformError) return; @@ -106,11 +102,6 @@ void ResourceError::platformLazyInit() m_dataIsUpToDate = true; } -bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& b) -{ - return (CFErrorRef)a == (CFErrorRef)b; -} - ResourceError::operator CFErrorRef() const { if (m_isNull) { @@ -129,7 +120,7 @@ ResourceError::operator CFErrorRef() const if (!m_failingURL.isEmpty()) { RetainPtr<CFStringRef> failingURLString(AdoptCF, m_failingURL.createCFString()); CFDictionarySetValue(userInfo.get(), failingURLStringKey, failingURLString.get()); - RetainPtr<CFURLRef> url(AdoptCF, KURL(m_failingURL).createCFURL()); + RetainPtr<CFURLRef> url(AdoptCF, KURL(m_failingURL.deprecatedString()).createCFURL()); CFDictionarySetValue(userInfo.get(), failingURLKey, url.get()); } @@ -142,7 +133,7 @@ ResourceError::operator CFErrorRef() const ResourceError::operator CFStreamError() const { - lazyInit(); + unpackPlatformErrorIfNeeded(); CFStreamError result; result.error = m_errorCode; diff --git a/WebCore/platform/network/cf/ResourceRequest.h b/WebCore/platform/network/cf/ResourceRequest.h index 5c49b06..5070380 100644 --- a/WebCore/platform/network/cf/ResourceRequest.h +++ b/WebCore/platform/network/cf/ResourceRequest.h @@ -38,7 +38,7 @@ namespace WebCore { struct ResourceRequest : ResourceRequestBase { ResourceRequest(const String& url) - : ResourceRequestBase(KURL(url), UseProtocolCachePolicy) + : ResourceRequestBase(KURL(url.deprecatedString()), UseProtocolCachePolicy) { } diff --git a/WebCore/platform/network/cf/ResourceResponse.h b/WebCore/platform/network/cf/ResourceResponse.h index 7e7c0fa..c930dfc 100644 --- a/WebCore/platform/network/cf/ResourceResponse.h +++ b/WebCore/platform/network/cf/ResourceResponse.h @@ -36,20 +36,16 @@ namespace WebCore { class ResourceResponse : public ResourceResponseBase { public: ResourceResponse() - : m_isUpToDate(true) + : ResourceResponseBase() { } ResourceResponse(CFURLResponseRef cfResponse) - : m_cfResponse(cfResponse) - , m_isUpToDate(false) - { - m_isNull = !cfResponse; - } + : ResourceResponseBase(!cfResponse) + , m_cfResponse(cfResponse) { } ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) - , m_isUpToDate(true) { } @@ -58,11 +54,10 @@ public: private: friend class ResourceResponseBase; - void platformLazyInit(); - static bool platformCompare(const ResourceResponse& a, const ResourceResponse& b); + void doUpdateResourceResponse(); RetainPtr<CFURLResponseRef> m_cfResponse; - bool m_isUpToDate; + }; } // namespace WebCore diff --git a/WebCore/platform/network/cf/ResourceResponseCFNet.cpp b/WebCore/platform/network/cf/ResourceResponseCFNet.cpp index 79efe89..d643f8e 100644 --- a/WebCore/platform/network/cf/ResourceResponseCFNet.cpp +++ b/WebCore/platform/network/cf/ResourceResponseCFNet.cpp @@ -62,16 +62,10 @@ static time_t toTimeT(CFAbsoluteTime time) return min(max(minTimeAsDouble, time + kCFAbsoluteTimeIntervalSince1970), maxTimeAsDouble); } -void ResourceResponse::platformLazyInit() +void ResourceResponse::doUpdateResourceResponse() { - if (m_isUpToDate) + if (!m_cfResponse.get()) return; - m_isUpToDate = true; - - if (m_isNull) { - ASSERT(!m_cfResponse.get()); - return; - } // FIXME: We may need to do MIME type sniffing here (unless that is done in CFURLResponseGetMIMEType). @@ -108,10 +102,4 @@ void ResourceResponse::platformLazyInit() m_httpStatusCode = 0; } -bool ResourceResponse::platformCompare(const ResourceResponse& a, const ResourceResponse& b) -{ - return CFEqual(a.cfURLResponse(), b.cfURLResponse()); -} - - } diff --git a/WebCore/platform/network/curl/ResourceHandleManager.cpp b/WebCore/platform/network/curl/ResourceHandleManager.cpp index b8fa696..249aa4e 100644 --- a/WebCore/platform/network/curl/ResourceHandleManager.cpp +++ b/WebCore/platform/network/curl/ResourceHandleManager.cpp @@ -42,20 +42,12 @@ #include <errno.h> #include <wtf/Vector.h> -#if PLATFORM(GTK) - #if GLIB_CHECK_VERSION(2,12,0) - #define USE_GLIB_BASE64 - #endif -#endif - namespace WebCore { const int selectTimeoutMS = 5; const double pollTimeSeconds = 0.05; const int maxRunningJobs = 5; -static const bool ignoreSSLErrors = getenv("WEBKIT_IGNORE_SSL_ERRORS"); - ResourceHandleManager::ResourceHandleManager() : m_downloadTimer(this, &ResourceHandleManager::downloadTimerCallback) , m_cookieJarFileName(0) @@ -174,7 +166,7 @@ static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data) if (httpCode >= 300 && httpCode < 400) { String location = d->m_response.httpHeaderField("location"); if (!location.isEmpty()) { - KURL newURL = KURL(job->request().url(), location); + KURL newURL = KURL(job->request().url(), location.deprecatedString()); ResourceRequest redirectedRequest = job->request(); redirectedRequest.setURL(newURL); @@ -279,17 +271,13 @@ void ResourceHandleManager::downloadTimerCallback(Timer<ResourceHandleManager>* // Temporarily disable timers since signals may interrupt select(), raising EINTR errors on some platforms setDeferringTimers(true); - int rc = 0; + int rc; do { FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); curl_multi_fdset(m_curlMultiHandle, &fdread, &fdwrite, &fdexcep, &maxfd); - // When the 3 file descriptors are empty, winsock will return -1 - // and bail out, stopping the file download. So make sure we - // have valid file descriptors before calling select. - if (maxfd >= 0) - rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); + rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); } while (rc == -1 && errno == EINTR); setDeferringTimers(false); @@ -463,44 +451,35 @@ bool ResourceHandleManager::startScheduledJobs() return started; } -// FIXME: This function does not deal properly with text encodings. static void parseDataUrl(ResourceHandle* handle) { - String data = handle->request().url().string(); + DeprecatedString data = handle->request().url().deprecatedString(); ASSERT(data.startsWith("data:", false)); - String header; + DeprecatedString header; bool base64 = false; int index = data.find(','); if (index != -1) { - header = data.substring(5, index - 5).lower(); - data = data.substring(index + 1); + header = data.mid(5, index - 5).lower(); + data = data.mid(index + 1); if (header.endsWith(";base64")) { base64 = true; header = header.left(header.length() - 7); } } else - data = String(); - - data = decodeURLEscapeSequences(data); - - size_t outLength = 0; - char* outData = 0; - if (base64 && !data.isEmpty()) { - // Use the GLib Base64 if available, since WebCore's decoder isn't - // general-purpose and fails on Acid3 test 97 (whitespace). -#ifdef USE_GLIB_BASE64 - outData = reinterpret_cast<char*>(g_base64_decode(data.utf8().data(), &outLength)); -#else + data = DeprecatedString(); + + data = KURL::decode_string(data); + + if (base64) { Vector<char> out; - if (base64Decode(data.latin1().data(), data.length(), out)) - data = String(out.data(), out.size()); + if (base64Decode(data.ascii(), data.length(), out)) + data = DeprecatedString(out.data(), out.size()); else - data = String(); -#endif + data = DeprecatedString(); } if (header.isEmpty()) @@ -512,22 +491,13 @@ static void parseDataUrl(ResourceHandle* handle) response.setMimeType(extractMIMETypeFromMediaType(header)); response.setTextEncodingName(extractCharsetFromMediaType(header)); - if (outData) - response.setExpectedContentLength(outLength); - else - response.setExpectedContentLength(data.length()); + response.setExpectedContentLength(data.length()); response.setHTTPStatusCode(200); client->didReceiveResponse(handle, response); - if (outData) - client->didReceiveData(handle, outData, outLength, 0); - else - client->didReceiveData(handle, data.latin1().data(), data.length(), 0); - -#ifdef USE_GLIB_BASE64 - g_free(outData); -#endif + if (!data.isEmpty()) + client->didReceiveData(handle, data.ascii(), data.length(), 0); client->didFinishLoading(handle); } @@ -535,8 +505,9 @@ static void parseDataUrl(ResourceHandle* handle) void ResourceHandleManager::startJob(ResourceHandle* job) { KURL kurl = job->request().url(); + String protocol = kurl.protocol(); - if (kurl.protocolIs("data")) { + if (equalIgnoringCase(protocol, "data")) { parseDataUrl(job); return; } @@ -545,15 +516,15 @@ void ResourceHandleManager::startJob(ResourceHandle* job) kurl.setRef(""); ResourceHandleInternal* d = job->getInternal(); - String url = kurl.string(); + DeprecatedString url = kurl.deprecatedString(); if (kurl.isLocalFile()) { - String query = kurl.query(); + DeprecatedString query = kurl.query(); // Remove any query part sent to a local file. if (!query.isEmpty()) url = url.left(url.find(query)); // Determine the MIME type based on the path. - d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url)); + d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(String(url))); } d->m_handle = curl_easy_init(); @@ -573,16 +544,12 @@ void ResourceHandleManager::startJob(ResourceHandle* job) curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(d->m_handle, CURLOPT_SHARE, m_curlShareHandle); curl_easy_setopt(d->m_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5); // 5 minutes - // FIXME: Enable SSL verification when we have a way of shipping certs - // and/or reporting SSL errors to the user. - if (ignoreSSLErrors) - curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYPEER, false); // enable gzip and deflate through Accept-Encoding: curl_easy_setopt(d->m_handle, CURLOPT_ENCODING, ""); // url must remain valid through the request ASSERT(!d->m_url); - d->m_url = strdup(url.latin1().data()); + d->m_url = strdup(url.ascii()); curl_easy_setopt(d->m_handle, CURLOPT_URL, d->m_url); if (m_cookieJarFileName) { @@ -625,7 +592,7 @@ void ResourceHandleManager::startJob(ResourceHandle* job) // timeout will occur and do curl_multi_perform if (ret && ret != CURLM_CALL_MULTI_PERFORM) { #ifndef NDEBUG - printf("Error %d starting job %s\n", ret, job->request().url().string().latin1().data()); + printf("Error %d starting job %s\n", ret, job->request().url().deprecatedString().ascii()); #endif job->cancel(); return; diff --git a/WebCore/platform/network/curl/ResourceRequest.h b/WebCore/platform/network/curl/ResourceRequest.h index c7edb91..fbe2cbc 100644 --- a/WebCore/platform/network/curl/ResourceRequest.h +++ b/WebCore/platform/network/curl/ResourceRequest.h @@ -35,7 +35,7 @@ namespace WebCore { struct ResourceRequest : ResourceRequestBase { ResourceRequest(const String& url) - : ResourceRequestBase(KURL(url), UseProtocolCachePolicy) + : ResourceRequestBase(KURL(url.deprecatedString()), UseProtocolCachePolicy) { } diff --git a/WebCore/platform/network/curl/ResourceResponse.h b/WebCore/platform/network/curl/ResourceResponse.h index 086eae8..ca364b6 100644 --- a/WebCore/platform/network/curl/ResourceResponse.h +++ b/WebCore/platform/network/curl/ResourceResponse.h @@ -34,7 +34,8 @@ namespace WebCore { class ResourceResponse : public ResourceResponseBase { public: ResourceResponse() - : m_responseFired(false) + : ResourceResponseBase(false), + m_responseFired(false) { } @@ -48,7 +49,14 @@ public: bool responseFired() { return m_responseFired; } private: + friend class ResourceResponseBase; + + void doUpdateResourceResponse() + { + } + bool m_responseFired; + }; } // namespace WebCore diff --git a/WebCore/platform/network/mac/AuthenticationMac.mm b/WebCore/platform/network/mac/AuthenticationMac.mm index 54e7681..1555cac 100644 --- a/WebCore/platform/network/mac/AuthenticationMac.mm +++ b/WebCore/platform/network/mac/AuthenticationMac.mm @@ -37,40 +37,18 @@ namespace WebCore { -AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace, - const Credential& proposedCredential, - unsigned previousFailureCount, - const ResourceResponse& response, - const ResourceError& error) - : AuthenticationChallengeBase(protectionSpace, - proposedCredential, - previousFailureCount, - response, - error) -{ -} - AuthenticationChallenge::AuthenticationChallenge(NSURLAuthenticationChallenge *macChallenge) - : AuthenticationChallengeBase(core([macChallenge protectionSpace]), - core([macChallenge proposedCredential]), - [macChallenge previousFailureCount], - [macChallenge failureResponse], - [macChallenge error]) + : m_isNull(false) + , m_protectionSpace(core([macChallenge protectionSpace])) + , m_proposedCredential(core([macChallenge proposedCredential])) + , m_previousFailureCount([macChallenge previousFailureCount]) + , m_failureResponse([macChallenge failureResponse]) + , m_error([macChallenge error]) , m_sender([macChallenge sender]) , m_macChallenge(macChallenge) { } -bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) -{ - if (a.sender() != b.sender()) - return false; - - if (a.nsURLAuthenticationChallenge() != b.nsURLAuthenticationChallenge()) - return false; - - return true; -} NSURLAuthenticationChallenge *mac(const AuthenticationChallenge& coreChallenge) { diff --git a/WebCore/platform/network/mac/ResourceErrorMac.mm b/WebCore/platform/network/mac/ResourceErrorMac.mm index 92f4d40..90afe01 100644 --- a/WebCore/platform/network/mac/ResourceErrorMac.mm +++ b/WebCore/platform/network/mac/ResourceErrorMac.mm @@ -1,6 +1,6 @@ // -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,9 +25,8 @@ */ #import "config.h" -#import "ResourceError.h" - #import "KURL.h" +#import "ResourceError.h" #import <Foundation/Foundation.h> @interface NSError (WebExtras) @@ -36,11 +35,8 @@ namespace WebCore { -void ResourceError::platformLazyInit() +void ResourceError::unpackPlatformError() { - if (m_dataIsUpToDate) - return; - m_domain = [m_platformError.get() domain]; m_errorCode = [m_platformError.get() code]; @@ -53,11 +49,6 @@ void ResourceError::platformLazyInit() m_dataIsUpToDate = true; } -bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& b) -{ - return (NSError*)a == (NSError*)b; -} - ResourceError::operator NSError*() const { if (m_isNull) { @@ -72,9 +63,8 @@ ResourceError::operator NSError*() const [userInfo.get() setValue:m_localizedDescription forKey:NSLocalizedDescriptionKey]; if (!m_failingURL.isEmpty()) { - NSURL *cocoaURL = KURL(m_failingURL); [userInfo.get() setValue:m_failingURL forKey:@"NSErrorFailingURLStringKey"]; - [userInfo.get() setValue:cocoaURL forKey:@"NSErrorFailingURLKey"]; + [userInfo.get() setValue:KURL(m_failingURL.deprecatedString()).getNSURL() forKey:@"NSErrorFailingURLKey"]; } m_platformError.adoptNS([[NSError alloc] initWithDomain:m_domain code:m_errorCode userInfo:userInfo.get()]); @@ -84,3 +74,4 @@ ResourceError::operator NSError*() const } } // namespace WebCore + diff --git a/WebCore/platform/network/mac/ResourceRequest.h b/WebCore/platform/network/mac/ResourceRequest.h index 6d8cb6d..9f6652a 100644 --- a/WebCore/platform/network/mac/ResourceRequest.h +++ b/WebCore/platform/network/mac/ResourceRequest.h @@ -1,6 +1,6 @@ // -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * Redistribution and use in source and binary forms, with or without @@ -42,7 +42,7 @@ namespace WebCore { class ResourceRequest : public ResourceRequestBase { public: ResourceRequest(const String& url) - : ResourceRequestBase(KURL(url), UseProtocolCachePolicy) + : ResourceRequestBase(KURL(url.deprecatedString()), UseProtocolCachePolicy) { } diff --git a/WebCore/platform/network/mac/ResourceRequestMac.mm b/WebCore/platform/network/mac/ResourceRequestMac.mm index 6735024..1fdbd4e 100644 --- a/WebCore/platform/network/mac/ResourceRequestMac.mm +++ b/WebCore/platform/network/mac/ResourceRequestMac.mm @@ -59,7 +59,7 @@ void ResourceRequest::doUpdateResourceRequest() m_httpHeaderFields.set(name, [headers objectForKey:name]); if (NSData* bodyData = [m_nsRequest.get() HTTPBody]) - m_httpBody = FormData::create([bodyData bytes], [bodyData length]); + m_httpBody = new FormData([bodyData bytes], [bodyData length]); else if (NSInputStream* bodyStream = [m_nsRequest.get() HTTPBodyStream]) if (FormData* formData = httpBodyFromStream(bodyStream)) m_httpBody = formData; @@ -75,17 +75,15 @@ void ResourceRequest::doUpdatePlatformRequest() NSMutableURLRequest* nsRequest = [m_nsRequest.get() mutableCopy]; if (nsRequest) - [nsRequest setURL:url()]; + [nsRequest setURL:url().getNSURL()]; else - nsRequest = [[NSMutableURLRequest alloc] initWithURL:url()]; - -#ifdef BUILDING_ON_TIGER + nsRequest = [[NSMutableURLRequest alloc] initWithURL:url().getNSURL()]; + wkSupportsMultipartXMixedReplace(nsRequest); -#endif [nsRequest setCachePolicy:(NSURLRequestCachePolicy)cachePolicy()]; [nsRequest setTimeoutInterval:timeoutInterval()]; - [nsRequest setMainDocumentURL:mainDocumentURL()]; + [nsRequest setMainDocumentURL:mainDocumentURL().getNSURL()]; if (!httpMethod().isEmpty()) [nsRequest setHTTPMethod:httpMethod()]; [nsRequest setHTTPShouldHandleCookies:allowHTTPCookies()]; diff --git a/WebCore/platform/network/mac/ResourceResponse.h b/WebCore/platform/network/mac/ResourceResponse.h index 1313bae..be4f8dc 100644 --- a/WebCore/platform/network/mac/ResourceResponse.h +++ b/WebCore/platform/network/mac/ResourceResponse.h @@ -41,20 +41,16 @@ namespace WebCore { class ResourceResponse : public ResourceResponseBase { public: ResourceResponse() - : m_isUpToDate(true) + : ResourceResponseBase() { } ResourceResponse(NSURLResponse* nsResponse) - : m_nsResponse(nsResponse) - , m_isUpToDate(false) - { - m_isNull = !nsResponse; - } + : ResourceResponseBase(!nsResponse) + , m_nsResponse(nsResponse) { } ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) - , m_isUpToDate(true) { } @@ -63,11 +59,10 @@ public: private: friend class ResourceResponseBase; - void platformLazyInit(); - static bool platformCompare(const ResourceResponse& a, const ResourceResponse& b); + void doUpdateResourceResponse(); RetainPtr<NSURLResponse> m_nsResponse; - bool m_isUpToDate; + }; } // namespace WebCore diff --git a/WebCore/platform/network/mac/ResourceResponseMac.mm b/WebCore/platform/network/mac/ResourceResponseMac.mm index 0af5b32..58e9cf8 100644 --- a/WebCore/platform/network/mac/ResourceResponseMac.mm +++ b/WebCore/platform/network/mac/ResourceResponseMac.mm @@ -52,17 +52,13 @@ NSURLResponse *ResourceResponse::nsURLResponse() const expectedContentLength = -1; else expectedContentLength = static_cast<NSInteger>(m_expectedContentLength); - const_cast<ResourceResponse*>(this)->m_nsResponse.adoptNS([[NSURLResponse alloc] initWithURL:m_url MIMEType:m_mimeType expectedContentLength:expectedContentLength textEncodingName:m_textEncodingName]); + const_cast<ResourceResponse*>(this)->m_nsResponse.adoptNS([[NSURLResponse alloc] initWithURL:m_url.getNSURL() MIMEType:m_mimeType expectedContentLength:expectedContentLength textEncodingName:m_textEncodingName]); } return m_nsResponse.get(); } -void ResourceResponse::platformLazyInit() +void ResourceResponse::doUpdateResourceResponse() { - if (m_isUpToDate) - return; - m_isUpToDate = true; - if (m_isNull) { ASSERT(!m_nsResponse); return; @@ -108,9 +104,4 @@ void ResourceResponse::platformLazyInit() } } -bool ResourceResponse::platformCompare(const ResourceResponse& a, const ResourceResponse& b) -{ - return a.nsURLResponse() == b.nsURLResponse(); -} - } diff --git a/WebCore/platform/network/qt/ResourceHandleQt.cpp b/WebCore/platform/network/qt/ResourceHandleQt.cpp index 3b25f25..9037401 100644 --- a/WebCore/platform/network/qt/ResourceHandleQt.cpp +++ b/WebCore/platform/network/qt/ResourceHandleQt.cpp @@ -31,6 +31,7 @@ #include "Frame.h" #include "DocLoader.h" #include "ResourceHandle.h" +#include "DeprecatedString.h" #include "ResourceHandleClient.h" #include "ResourceHandleInternal.h" #include "qwebpage_p.h" diff --git a/WebCore/platform/network/qt/ResourceRequest.h b/WebCore/platform/network/qt/ResourceRequest.h index 6b40639..dab4ffa 100644 --- a/WebCore/platform/network/qt/ResourceRequest.h +++ b/WebCore/platform/network/qt/ResourceRequest.h @@ -37,7 +37,7 @@ namespace WebCore { struct ResourceRequest : ResourceRequestBase { ResourceRequest(const String& url) - : ResourceRequestBase(KURL(url), UseProtocolCachePolicy) + : ResourceRequestBase(KURL(url.deprecatedString()), UseProtocolCachePolicy) { } diff --git a/WebCore/platform/network/qt/ResourceResponse.h b/WebCore/platform/network/qt/ResourceResponse.h index 36b67ea..fb658c9 100644 --- a/WebCore/platform/network/qt/ResourceResponse.h +++ b/WebCore/platform/network/qt/ResourceResponse.h @@ -29,11 +29,14 @@ #include "ResourceResponseBase.h" +#include "NotImplemented.h" + namespace WebCore { class ResourceResponse : public ResourceResponseBase { public: ResourceResponse() + : ResourceResponseBase(false) { } @@ -41,6 +44,15 @@ public: : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) { } + +private: + friend class ResourceResponseBase; + + void doUpdateResourceResponse() + { + notImplemented(); + } + }; } // namespace WebCore diff --git a/WebCore/platform/network/win/CookieJarWin.cpp b/WebCore/platform/network/win/CookieJarWin.cpp index 9eedcb0..2c1d58b 100644 --- a/WebCore/platform/network/win/CookieJarWin.cpp +++ b/WebCore/platform/network/win/CookieJarWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,10 +24,9 @@ */ #include "config.h" -#include "CookieJar.h" - #include "KURL.h" #include "PlatformString.h" +#include "DeprecatedString.h" #include "Document.h" #include "ResourceHandle.h" #include <windows.h> @@ -39,9 +38,8 @@ #include <Wininet.h> #endif -// FIXME: This should be broken up into CookieJarCFNet.cpp and CookieJarWin.cpp or CookieJarWinINet.cpp. - -namespace WebCore { +namespace WebCore +{ #if USE(CFNETWORK) static const CFStringRef s_setCookieKeyCF = CFSTR("Set-Cookie"); @@ -77,9 +75,11 @@ void setCookies(Document* /*document*/, const KURL& url, const KURL& policyURL, CFHTTPCookieStorageSetCookies(defaultCookieStorage, cookiesCF.get(), urlCF.get(), policyURLCF.get()); #else // FIXME: Deal with the policy URL. - String str = url.string(); - String val = value; - InternetSetCookie(str.charactersWithNullTermination(), 0, val.charactersWithNullTermination()); + DeprecatedString str = url.deprecatedString(); + str.append((UChar)'\0'); + DeprecatedString val = value.deprecatedString(); + val.append((UChar)'\0'); + InternetSetCookie((UChar*)str.unicode(), 0, (UChar*)val.unicode()); #endif } @@ -110,17 +110,19 @@ String cookies(const Document* /*document*/, const KURL& url) return (CFStringRef)CFDictionaryGetValue(headerCF.get(), s_cookieCF); #else - String str = url.string(); + DeprecatedString str = url.deprecatedString(); + str.append((UChar)'\0'); - DWORD count = str.length() + 1; - InternetGetCookie(str.charactersWithNullTermination(), 0, 0, &count); + DWORD count = str.length(); + InternetGetCookie((UChar*)str.unicode(), 0, 0, &count); if (count <= 1) // Null terminator counts as 1. return String(); - Vector<UChar> buffer(count); - InternetGetCookie(buffer.data(), 0, buffer, &count); - buffer.shrink(count - 1); // Ignore the null terminator. - return String::adopt(buffer); + UChar* buffer = new UChar[count]; + InternetGetCookie((UChar*)str.unicode(), 0, buffer, &count); + String& result = String(buffer, count-1); // Ignore the null terminator. + delete[] buffer; + return result; #endif } diff --git a/WebCore/platform/network/win/ResourceHandleWin.cpp b/WebCore/platform/network/win/ResourceHandleWin.cpp index 337b752..260652e 100644 --- a/WebCore/platform/network/win/ResourceHandleWin.cpp +++ b/WebCore/platform/network/win/ResourceHandleWin.cpp @@ -146,13 +146,13 @@ void ResourceHandle::onHandleCreated(LPARAM lParam) if (method() == "POST") { // FIXME: Too late to set referrer properly. - String urlStr = url().path(); + DeprecatedString urlStr = url().path(); int fragmentIndex = urlStr.find('#'); if (fragmentIndex != -1) urlStr = urlStr.left(fragmentIndex); static LPCSTR accept[2]={"*/*", NULL}; HINTERNET urlHandle = HttpOpenRequestA(d->m_resourceHandle, - "POST", urlStr.latin1().data(), 0, 0, accept, + "POST", urlStr.latin1(), 0, 0, accept, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_FORMS_SUBMIT | INTERNET_FLAG_RELOAD | @@ -328,12 +328,11 @@ bool ResourceHandle::start(Frame* frame) { ref(); if (url().isLocalFile()) { - String path = url().path(); + DeprecatedString path = url().path(); // windows does not enjoy a leading slash on paths if (path[0] == '/') - path = path.substring(1); - // FIXME: This is wrong. Need to use wide version of this call. - d->m_fileHandle = CreateFileA(path.utf8().data(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + path = path.mid(1); + d->m_fileHandle = CreateFileA(path.ascii(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // FIXME: perhaps this error should be reported asynchronously for // consistency. @@ -373,15 +372,16 @@ bool ResourceHandle::start(Frame* frame) String referrer = frame->loader()->referrer(); if (method() == "POST") { d->m_postReferrer = referrer; - String host = url().host(); - urlHandle = InternetConnectA(internetHandle, host.latin1().data(), + DeprecatedString host = url().host(); + host += "\0"; + urlHandle = InternetConnectA(internetHandle, host.ascii(), url().port(), NULL, // no username NULL, // no password INTERNET_SERVICE_HTTP, flags, (DWORD_PTR)d->m_jobId); } else { - String urlStr = url().string(); + DeprecatedString urlStr = url().deprecatedString(); int fragmentIndex = urlStr.find('#'); if (fragmentIndex != -1) urlStr = urlStr.left(fragmentIndex); @@ -389,8 +389,8 @@ bool ResourceHandle::start(Frame* frame) if (!referrer.isEmpty()) headers += String("Referer: ") + referrer + "\r\n"; - urlHandle = InternetOpenUrlA(internetHandle, urlStr.latin1().data(), - headers.latin1().data(), headers.length(), + urlHandle = InternetOpenUrlA(internetHandle, urlStr.ascii(), + headers.latin1(), headers.length(), flags, (DWORD_PTR)d->m_jobId); } diff --git a/WebCore/platform/pthreads/ThreadingPthreads.cpp b/WebCore/platform/pthreads/ThreadingPthreads.cpp new file mode 100644 index 0000000..c12fd92 --- /dev/null +++ b/WebCore/platform/pthreads/ThreadingPthreads.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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 "Threading.h" + +#include "Logging.h" +#include <wtf/HashMap.h> + +#include <errno.h> + +namespace WebCore { + +static Mutex& threadMapMutex() +{ + static Mutex mutex; + return mutex; +} + +static HashMap<ThreadIdentifier, pthread_t>& threadMap() +{ + static HashMap<ThreadIdentifier, pthread_t> map; + return map; +} + +static ThreadIdentifier establishIdentifierForPthreadHandle(pthread_t& pthreadHandle) +{ + MutexLocker locker(threadMapMutex()); + + static ThreadIdentifier identifierCount = 1; + + threadMap().add(identifierCount, pthreadHandle); + + return identifierCount++; +} + +static ThreadIdentifier identifierByPthreadHandle(const pthread_t& pthreadHandle) +{ + MutexLocker locker(threadMapMutex()); + + HashMap<ThreadIdentifier, pthread_t>::iterator i = threadMap().begin(); + for (; i != threadMap().end(); ++i) { + if (pthread_equal(i->second, pthreadHandle)) + return i->first; + } + + return 0; +} + +static pthread_t pthreadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + + return threadMap().get(id); +} + +static void clearPthreadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + + ASSERT(threadMap().contains(id)); + + threadMap().remove(id); +} + +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data) +{ + pthread_t threadHandle; + if (pthread_create(&threadHandle, NULL, entryPoint, data)) { + LOG_ERROR("Failed to create pthread at entry point %p with data %p", entryPoint, data); + return 0; + } + + ThreadIdentifier threadID = establishIdentifierForPthreadHandle(threadHandle); + LOG(Threading, "Created thread with thread id %u", threadID); + return threadID; +} + +int waitForThreadCompletion(ThreadIdentifier threadID, void** result) +{ + ASSERT(threadID); + + pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID); + + int joinResult = pthread_join(pthreadHandle, result); + if (joinResult == EDEADLK) + LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID); + + clearPthreadHandleForIdentifier(threadID); + return joinResult; +} + +void detachThread(ThreadIdentifier threadID) +{ + ASSERT(threadID); + + pthread_t pthreadHandle = pthreadHandleForIdentifier(threadID); + + pthread_detach(pthreadHandle); + + clearPthreadHandleForIdentifier(threadID); +} + +ThreadIdentifier currentThread() +{ + pthread_t currentThread = pthread_self(); + if (ThreadIdentifier id = identifierByPthreadHandle(currentThread)) + return id; + return establishIdentifierForPthreadHandle(currentThread); +} + +Mutex::Mutex() +{ + pthread_mutex_init(&m_mutex, NULL); +} + +Mutex::~Mutex() +{ + pthread_mutex_destroy(&m_mutex); +} + +void Mutex::lock() +{ + if (pthread_mutex_lock(&m_mutex) != 0) + ASSERT(false); +} + +bool Mutex::tryLock() +{ + int result = pthread_mutex_trylock(&m_mutex); + + if (result == 0) + return true; + else if (result == EBUSY) + return false; + + ASSERT(false); + return false; +} + +void Mutex::unlock() +{ + if (pthread_mutex_unlock(&m_mutex) != 0) + ASSERT(false); +} + +ThreadCondition::ThreadCondition() +{ + pthread_cond_init(&m_condition, NULL); +} + +ThreadCondition::~ThreadCondition() +{ + pthread_cond_destroy(&m_condition); +} + +void ThreadCondition::wait(Mutex& mutex) +{ + if (pthread_cond_wait(&m_condition, &mutex.impl()) != 0) + ASSERT(false); +} + +void ThreadCondition::signal() +{ + if (pthread_cond_signal(&m_condition) != 0) + ASSERT(false); +} + +void ThreadCondition::broadcast() +{ + if (pthread_cond_broadcast(&m_condition) != 0) + ASSERT(false); +} + +} // namespace WebCore diff --git a/WebCore/platform/qt/ClipboardQt.cpp b/WebCore/platform/qt/ClipboardQt.cpp index 191462c..f7c420f 100644 --- a/WebCore/platform/qt/ClipboardQt.cpp +++ b/WebCore/platform/qt/ClipboardQt.cpp @@ -30,6 +30,7 @@ #include "CachedImage.h" #include "CSSHelper.h" +#include "DeprecatedString.h" #include "Document.h" #include "Element.h" #include "Frame.h" @@ -240,12 +241,12 @@ void ClipboardQt::declareAndWriteDragImage(Element* element, const KURL& url, co if (imageURL.isEmpty()) return; - KURL fullURL = frame->document()->completeURL(parseURL(imageURL)); + String fullURL = frame->document()->completeURL(parseURL(imageURL)); if (fullURL.isEmpty()) return; QList<QUrl> urls; - urls.append(fullURL); + urls.append(QUrl(fullURL)); m_writableData->setUrls(urls); if (!isForDragging()) @@ -257,7 +258,7 @@ void ClipboardQt::writeURL(const KURL& url, const String&, Frame* frame) ASSERT(frame); QList<QUrl> urls; - urls.append(frame->document()->completeURL(url.string())); + urls.append(QUrl(frame->document()->completeURL(url.deprecatedString()))); if (!m_writableData) m_writableData = new QMimeData; m_writableData->setUrls(urls); diff --git a/WebCore/platform/qt/CookieJarQt.cpp b/WebCore/platform/qt/CookieJarQt.cpp index 9215e9f..4138ab7 100644 --- a/WebCore/platform/qt/CookieJarQt.cpp +++ b/WebCore/platform/qt/CookieJarQt.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "CookieJar.h" +#include "DeprecatedString.h" #include "KURL.h" #include "PlatformString.h" diff --git a/WebCore/platform/qt/CursorQt.cpp b/WebCore/platform/qt/CursorQt.cpp index fec150a..6d05397 100644 --- a/WebCore/platform/qt/CursorQt.cpp +++ b/WebCore/platform/qt/CursorQt.cpp @@ -33,6 +33,7 @@ #include "Image.h" #include "IntPoint.h" +#include "DeprecatedString.h" #include "NotImplemented.h" #include <stdio.h> diff --git a/WebCore/platform/qt/FileChooserQt.cpp b/WebCore/platform/qt/FileChooserQt.cpp index bf5d00f..c19301f 100644 --- a/WebCore/platform/qt/FileChooserQt.cpp +++ b/WebCore/platform/qt/FileChooserQt.cpp @@ -32,8 +32,7 @@ namespace WebCore { FileChooser::FileChooser(FileChooserClient* client, const String& filename) - : RefCounted<FileChooser>(0) - , m_client(client) + : m_client(client) , m_filename(filename) , m_icon(chooseIcon(filename)) { diff --git a/WebCore/platform/qt/FileSystemQt.cpp b/WebCore/platform/qt/FileSystemQt.cpp index 529da02..7a25591 100644 --- a/WebCore/platform/qt/FileSystemQt.cpp +++ b/WebCore/platform/qt/FileSystemQt.cpp @@ -2,7 +2,6 @@ * Copyright (C) 2007 Staikos Computing Services Inc. * Copyright (C) 2007 Holger Hans Peter Freyther * Copyright (C) 2008 Apple, Inc. All rights reserved. - * Copyright (C) 2008 Collabora, Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,10 +28,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "FileSystem.h" - -#include "CString.h" #include "NotImplemented.h" #include "PlatformString.h" #include <QFile> @@ -80,18 +75,6 @@ String pathByAppendingComponent(const String& path, const String& component) return QDir(path).filePath(component); } -String pathGetFileName(const String&) -{ - notImplemented(); - return String(); -} - -bool unloadModule(PlatformModule) -{ - notImplemented(); - return false; -} - } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/qt/KURLQt.cpp b/WebCore/platform/qt/KURLQt.cpp index 350b97b..ce0d53d 100644 --- a/WebCore/platform/qt/KURLQt.cpp +++ b/WebCore/platform/qt/KURLQt.cpp @@ -38,13 +38,11 @@ KURL::KURL(const QUrl& url) KURL::operator QUrl() const { - unsigned length = m_string.length(); - QByteArray ba; - ba.reserve(length); + ba.reserve(urlString.length()); - for (unsigned i = 0; i < length; ++i) { - const char chr = static_cast<char>(m_string[i]); + for (const char *src = urlString.ascii(); *src; ++src) { + const char chr = *src; switch (chr) { case '{': diff --git a/WebCore/platform/qt/Localizations.cpp b/WebCore/platform/qt/Localizations.cpp index 04ff37e..67bba22 100644 --- a/WebCore/platform/qt/Localizations.cpp +++ b/WebCore/platform/qt/Localizations.cpp @@ -297,10 +297,5 @@ String unknownFileSizeText() return QCoreApplication::translate("QWebPage", "Unknown", "Unknown filesize FTP directory listing item"); } -String imageTitle(const String& filename, const IntSize& size) -{ - return String(); -} - } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/qt/PlugInInfoStoreQt.cpp b/WebCore/platform/qt/PlugInInfoStoreQt.cpp new file mode 100644 index 0000000..57cc3a9 --- /dev/null +++ b/WebCore/platform/qt/PlugInInfoStoreQt.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2007 Trolltech ASA + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + This class provides all functionality needed for loading images, style sheets and html + pages from the web. It has a memory cache for these objects. +*/ +#include "PluginInfoStore.h" +#include "qdebug.h" +#if QT_VERSION < 0x040400 +#include "qwebobjectplugin_p.h" +#endif +#include "NotImplemented.h" + +namespace WebCore { + +PluginInfo* PluginInfoStore::createPluginInfoForPluginAtIndex(unsigned i) +{ + //qDebug() << ">>>>>>>>>>> PluginInfoStore::createPluginInfoForPluginAtIndex(" << i << ")"; + +#if QT_VERSION < 0x040400 + QWebFactoryLoader *loader = QWebFactoryLoader::self(); + if (i > loader->m_pluginInfo.count()) + return 0; + const QWebFactoryLoader::Info &qinfo = loader->m_pluginInfo.at(i); + PluginInfo *info = new PluginInfo; + info->name = qinfo.name; + info->desc = qinfo.description; + foreach (const QWebFactoryLoader::MimeInfo &m, qinfo.mimes) { + MimeClassInfo *mime = new MimeClassInfo; + mime->type = m.type; + mime->plugin = info; + foreach (QString ext, m.extensions) + mime->suffixes.append(ext); + info->mimes.append(mime); + } + return info; +#else + return 0; // ### FIXME +#endif +} + +unsigned PluginInfoStore::pluginCount() const +{ +#if QT_VERSION < 0x040400 + //qDebug() << ">>>>>>>>>>> PluginInfoStore::count =" << QWebFactoryLoader::self()->keys().count(); + return QWebFactoryLoader::self()->keys().count(); +#else + return 0; +#endif +} + +String PluginInfoStore::pluginNameForMIMEType(const String& mimeType) +{ + // FIXME: This method is stubbed out and should really return the name of a plug-in package for + // a given MIME type. + return String(); +} + +bool PluginInfoStore::supportsMIMEType(const WebCore::String& string) +{ +#if QT_VERSION < 0x040400 + bool supports = QWebFactoryLoader::self()->supportsMimeType(string); +#else + bool supports = false; +#endif + //qDebug() << ">>>>>>>>>>> PluginInfoStore::supportsMIMEType(" << string << ") =" << supports; + return supports; +} + +void refreshPlugins(bool) { + notImplemented(); +} + +} diff --git a/WebCore/platform/qt/PopupMenuQt.cpp b/WebCore/platform/qt/PopupMenuQt.cpp index 2a70614..add4d84 100644 --- a/WebCore/platform/qt/PopupMenuQt.cpp +++ b/WebCore/platform/qt/PopupMenuQt.cpp @@ -43,8 +43,7 @@ namespace WebCore { PopupMenu::PopupMenu(PopupMenuClient* client) - : RefCounted<PopupMenu>(0) - , m_popupClient(client) + : m_popupClient(client) { m_popup = new QWebPopup(client); } diff --git a/WebCore/platform/qt/RenderThemeQt.cpp b/WebCore/platform/qt/RenderThemeQt.cpp index d17d6b5..dbf080f 100644 --- a/WebCore/platform/qt/RenderThemeQt.cpp +++ b/WebCore/platform/qt/RenderThemeQt.cpp @@ -126,6 +126,11 @@ bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& return RenderTheme::isControlStyled(style, border, background, backgroundColor); } +void RenderThemeQt::paintResizeControl(GraphicsContext*, const IntRect&) +{ +} + + Color RenderThemeQt::platformActiveSelectionBackgroundColor() const { QPalette pal = QApplication::palette(); diff --git a/WebCore/platform/qt/RenderThemeQt.h b/WebCore/platform/qt/RenderThemeQt.h index 01beae5..053d825 100644 --- a/WebCore/platform/qt/RenderThemeQt.h +++ b/WebCore/platform/qt/RenderThemeQt.h @@ -54,6 +54,8 @@ public: virtual bool isControlStyled(const RenderStyle*, const BorderData&, const BackgroundLayer&, const Color&) const; + virtual void paintResizeControl(GraphicsContext*, const IntRect&); + // The platform selection color. virtual Color platformActiveSelectionBackgroundColor() const; virtual Color platformInactiveSelectionBackgroundColor() const; diff --git a/WebCore/platform/qt/TemporaryLinkStubs.cpp b/WebCore/platform/qt/TemporaryLinkStubs.cpp index 0f25245..817491e 100644 --- a/WebCore/platform/qt/TemporaryLinkStubs.cpp +++ b/WebCore/platform/qt/TemporaryLinkStubs.cpp @@ -4,7 +4,6 @@ * Copyright (C) 2006 George Staikos <staikos@kde.org> * Copyright (C) 2006 Dirk Mueller <mueller@kde.org> * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) 2008 Collabora, Ltd. * * All rights reserved. * @@ -60,8 +59,7 @@ #include "NotImplemented.h" #include "Path.h" #include "PlatformMouseEvent.h" -#include "PluginDatabase.h" -#include "PluginPackage.h" +#include "PluginInfoStore.h" #include "RenderTheme.h" #include "SharedBuffer.h" #include "SystemTime.h" @@ -72,15 +70,6 @@ using namespace WebCore; -PluginSet PluginDatabase::getPluginsInPaths() const { notImplemented(); return PluginSet(); } -Vector<String> PluginDatabase::defaultPluginPaths() { notImplemented(); return Vector<String>(); } -bool PluginDatabase::isPreferredPluginPath(const String&) { notImplemented(); return false; } -int PluginPackage::compare(const PluginPackage&) const { notImplemented(); return 0; } -bool PluginPackage::fetchInfo() { notImplemented(); return false; } -unsigned PluginPackage::hash() const { notImplemented(); return 0; } -bool PluginPackage::equal(const PluginPackage&, const PluginPackage&) { notImplemented(); return false; } -bool PluginPackage::load() { notImplemented(); return false; } - namespace WebCore { Vector<String> supportedKeySizes() { notImplemented(); return Vector<String>(); } diff --git a/WebCore/platform/qt/MainThreadQt.cpp b/WebCore/platform/qt/ThreadingQt.cpp index ab19bf7..71cd17e 100644 --- a/WebCore/platform/qt/MainThreadQt.cpp +++ b/WebCore/platform/qt/ThreadingQt.cpp @@ -29,7 +29,7 @@ */ #include "config.h" -#include "MainThread.h" +#include "Threading.h" #include <QtCore/QObject> #include <QtCore/QCoreApplication> @@ -94,4 +94,4 @@ void callOnMainThread(MainThreadFunction* function, void* context) } -#include "MainThreadQt.moc" +#include "ThreadingQt.moc" diff --git a/WebCore/platform/sql/SQLValue.h b/WebCore/platform/sql/SQLValue.h index 7d85051..ae2432d 100644 --- a/WebCore/platform/sql/SQLValue.h +++ b/WebCore/platform/sql/SQLValue.h @@ -30,7 +30,7 @@ #define SQLValue_h #include "PlatformString.h" -#include <wtf/Threading.h> +#include "Threading.h" namespace WebCore { diff --git a/WebCore/platform/sql/SQLiteAuthorizer.h b/WebCore/platform/sql/SQLiteAuthorizer.h index 0bb0fb3..4c0e8ee 100644 --- a/WebCore/platform/sql/SQLiteAuthorizer.h +++ b/WebCore/platform/sql/SQLiteAuthorizer.h @@ -28,7 +28,7 @@ #ifndef SQLiteAuthorizer_h #define SQLiteAuthorizer_h -#include <wtf/Threading.h> +#include "Threading.h" namespace WebCore { diff --git a/WebCore/platform/sql/SQLiteDatabase.h b/WebCore/platform/sql/SQLiteDatabase.h index 20f461d..5e6ab46 100644 --- a/WebCore/platform/sql/SQLiteDatabase.h +++ b/WebCore/platform/sql/SQLiteDatabase.h @@ -28,7 +28,7 @@ #define SQLDatabase_h #include "PlatformString.h" -#include <wtf/Threading.h> +#include "Threading.h" #if COMPILER(MSVC) #pragma warning(disable: 4800) diff --git a/WebCore/platform/sql/SQLiteTransaction.cpp b/WebCore/platform/sql/SQLiteTransaction.cpp index 5018f5a..3fb54f1 100644 --- a/WebCore/platform/sql/SQLiteTransaction.cpp +++ b/WebCore/platform/sql/SQLiteTransaction.cpp @@ -74,7 +74,6 @@ void SQLiteTransaction::rollback() void SQLiteTransaction::stop() { m_inProgress = false; - m_db.m_transactionInProgress = false; } } // namespace WebCore diff --git a/WebCore/platform/symbian/DeprecatedStringSymbian.cpp b/WebCore/platform/symbian/DeprecatedStringSymbian.cpp new file mode 100644 index 0000000..a1a0a45 --- /dev/null +++ b/WebCore/platform/symbian/DeprecatedStringSymbian.cpp @@ -0,0 +1,82 @@ +/* +* ============================================================================== +* Copyright (c) 2006, Nokia Corporation +* 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 the Nokia Corporation 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" +#include "DeprecatedString.h" + +namespace WebCore { + +void DeprecatedString::setBufferFromDes(const TDesC& des) +{ + setUnicode((const DeprecatedChar *)des.Ptr(), (uint)des.Length()); +} + +void DeprecatedString::setBufferFromDes(const TDesC8& des) +{ + setLatin1((const char*)des.Ptr(), (uint)des.Length()); +} + +DeprecatedString DeprecatedString::fromDes(const TDesC& des) +{ + DeprecatedString s; + s.setBufferFromDes(des); + return s; +} + +DeprecatedString DeprecatedString::fromDes(const TDesC8& des) +{ + DeprecatedString s; + s.setBufferFromDes(des); + return s; +} + +TPtrC DeprecatedString::des() const +{ + if (!dataHandle[0]->_isUnicodeValid) + dataHandle[0]->makeUnicode(); + + TPtrC tstr((const TUint16*)unicode(), length() ); + + return tstr; +} + +TPtrC8 DeprecatedString::des8() const +{ + TPtrC8 tstr((const TUint8*)latin1(), length()); + + return tstr; +} + +}
\ No newline at end of file diff --git a/WebCore/platform/text/AtomicString.cpp b/WebCore/platform/text/AtomicString.cpp index f7b3c91..c584e6c 100644 --- a/WebCore/platform/text/AtomicString.cpp +++ b/WebCore/platform/text/AtomicString.cpp @@ -26,6 +26,7 @@ #include "AtomicString.h" +#include "DeprecatedString.h" #include "StaticConstructors.h" #include "StringHash.h" #include <kjs/identifier.h> @@ -73,16 +74,13 @@ bool operator==(const AtomicString& a, const char* b) return CStringTranslator::equal(impl, b); } -PassRefPtr<StringImpl> AtomicString::add(const char* c) +StringImpl* AtomicString::add(const char* c) { if (!c) return 0; if (!*c) return StringImpl::empty(); - pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable->add<const char*, CStringTranslator>(c); - if (!addResult.second) - return *addResult.first; - return adoptRef(*addResult.first); + return *stringTable->add<const char*, CStringTranslator>(c).first; } struct UCharBuffer { @@ -137,7 +135,7 @@ struct UCharBufferTranslator { } }; -PassRefPtr<StringImpl> AtomicString::add(const UChar* s, int length) +StringImpl* AtomicString::add(const UChar* s, int length) { if (!s) return 0; @@ -145,14 +143,11 @@ PassRefPtr<StringImpl> AtomicString::add(const UChar* s, int length) if (length == 0) return StringImpl::empty(); - UCharBuffer buf = { s, length }; - pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable->add<UCharBuffer, UCharBufferTranslator>(buf); - if (!addResult.second) - return *addResult.first; - return adoptRef(*addResult.first); + UCharBuffer buf = {s, length}; + return *stringTable->add<UCharBuffer, UCharBufferTranslator>(buf).first; } -PassRefPtr<StringImpl> AtomicString::add(const UChar* s) +StringImpl* AtomicString::add(const UChar* s) { if (!s) return 0; @@ -165,13 +160,10 @@ PassRefPtr<StringImpl> AtomicString::add(const UChar* s) return StringImpl::empty(); UCharBuffer buf = {s, length}; - pair<HashSet<StringImpl*>::iterator, bool> addResult = stringTable->add<UCharBuffer, UCharBufferTranslator>(buf); - if (!addResult.second) - return *addResult.first; - return adoptRef(*addResult.first); + return *stringTable->add<UCharBuffer, UCharBufferTranslator>(buf).first; } -PassRefPtr<StringImpl> AtomicString::add(StringImpl* r) +StringImpl* AtomicString::add(StringImpl* r) { if (!r || r->m_inTable) return r; @@ -190,12 +182,12 @@ void AtomicString::remove(StringImpl* r) stringTable->remove(r); } -PassRefPtr<StringImpl> AtomicString::add(const KJS::Identifier& str) +StringImpl* AtomicString::add(const KJS::Identifier& str) { return add(reinterpret_cast<const UChar*>(str.data()), str.size()); } -PassRefPtr<StringImpl> AtomicString::add(const KJS::UString& str) +StringImpl* AtomicString::add(const KJS::UString& str) { return add(reinterpret_cast<const UChar*>(str.data()), str.size()); } @@ -210,6 +202,16 @@ AtomicString::operator UString() const return m_string; } +AtomicString::AtomicString(const DeprecatedString& s) + : m_string(add(reinterpret_cast<const UChar*>(s.unicode()), s.length())) +{ +} + +DeprecatedString AtomicString::deprecatedString() const +{ + return m_string.deprecatedString(); +} + DEFINE_GLOBAL(AtomicString, nullAtom) DEFINE_GLOBAL(AtomicString, emptyAtom, "") DEFINE_GLOBAL(AtomicString, textAtom, "#text") diff --git a/WebCore/platform/text/AtomicString.h b/WebCore/platform/text/AtomicString.h index 2a2ac97..4a0bb5b 100644 --- a/WebCore/platform/text/AtomicString.h +++ b/WebCore/platform/text/AtomicString.h @@ -43,7 +43,7 @@ public: AtomicString(const String& s) : m_string(add(s.impl())) { } operator const String&() const { return m_string; } - const String& string() const { return m_string; }; + const String& domString() const { return m_string; }; operator KJS::Identifier() const; operator KJS::UString() const; @@ -57,16 +57,16 @@ public: bool contains(UChar c) const { return m_string.contains(c); } bool contains(const AtomicString& s, bool caseSensitive = true) const - { return m_string.contains(s.string(), caseSensitive); } + { return m_string.contains(s.domString(), caseSensitive); } int find(UChar c, int start = 0) const { return m_string.find(c, start); } int find(const AtomicString& s, int start = 0, bool caseSentitive = true) const - { return m_string.find(s.string(), start, caseSentitive); } + { return m_string.find(s.domString(), start, caseSentitive); } bool startsWith(const AtomicString& s, bool caseSensitive = true) const - { return m_string.startsWith(s.string(), caseSensitive); } + { return m_string.startsWith(s.domString(), caseSensitive); } bool endsWith(const AtomicString& s, bool caseSensitive = true) const - { return m_string.endsWith(s.string(), caseSensitive); } + { return m_string.endsWith(s.domString(), caseSensitive); } int toInt(bool* ok = 0) const { return m_string.toInt(ok); } double toDouble(bool* ok = 0) const { return m_string.toDouble(ok); } @@ -93,15 +93,18 @@ public: operator QString() const { return m_string; } #endif + AtomicString(const DeprecatedString&); + DeprecatedString deprecatedString() const; + private: String m_string; - static PassRefPtr<StringImpl> add(const char*); - static PassRefPtr<StringImpl> add(const UChar*, int length); - static PassRefPtr<StringImpl> add(const UChar*); - static PassRefPtr<StringImpl> add(StringImpl*); - static PassRefPtr<StringImpl> add(const KJS::UString&); - static PassRefPtr<StringImpl> add(const KJS::Identifier&); + static StringImpl* add(const char*); + static StringImpl* add(const UChar*, int length); + static StringImpl* add(const UChar*); + static StringImpl* add(StringImpl*); + static StringImpl* add(const KJS::UString&); + static StringImpl* add(const KJS::Identifier&); }; inline bool operator==(const AtomicString& a, const AtomicString& b) { return a.impl() == b.impl(); } diff --git a/WebCore/platform/text/BidiResolver.h b/WebCore/platform/text/BidiResolver.h index 3e545d9..d2515a9 100644 --- a/WebCore/platform/text/BidiResolver.h +++ b/WebCore/platform/text/BidiResolver.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2000 Lars Knoll (knoll@kde.org) - * Copyright (C) 2003, 2004, 2006, 2007, 2008 Apple Inc. All right reserved. + * Copyright (C) 2003, 2004, 2006, 2007 Apple Inc. All right reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -85,8 +85,6 @@ struct BidiCharacterRun { } } - void destroy() { delete this; } - int start() const { return m_start; } int stop() const { return m_stop; } unsigned char level() const { return m_level; } @@ -111,7 +109,6 @@ public : , emptyRun(true) , m_firstRun(0) , m_lastRun(0) - , m_logicallyLastRun(0) , m_runCount(0) { } @@ -137,15 +134,14 @@ public : Run* firstRun() const { return m_firstRun; } Run* lastRun() const { return m_lastRun; } - Run* logicallyLastRun() const { return m_logicallyLastRun; } - unsigned runCount() const { return m_runCount; } + int runCount() const { return m_runCount; } void addRun(Run*); void deleteRuns(); protected: void appendRun(); - void reverseRuns(unsigned start, unsigned end); + void reverseRuns(int start, int end); Iterator current; Iterator sor; @@ -161,28 +157,22 @@ protected: Run* m_firstRun; Run* m_lastRun; - Run* m_logicallyLastRun; - unsigned m_runCount; + int m_runCount; }; template <class Iterator, class Run> -inline void BidiResolver<Iterator, Run>::addRun(Run* run) -{ - if (!m_firstRun) - m_firstRun = run; - else - m_lastRun->m_next = run; - m_lastRun = run; - m_runCount++; -} - -template <class Iterator, class Run> void BidiResolver<Iterator, Run>::appendRun() { if (emptyRun || eor.atEnd()) return; - addRun(new Run(sor.offset(), eor.offset() + 1, context(), m_direction)); + Run* bidiRun = new Run(sor.offset(), eor.offset() + 1, context(), m_direction); + if (!m_firstRun) + m_firstRun = bidiRun; + else + m_lastRun->m_next = bidiRun; + m_lastRun = bidiRun; + m_runCount++; eor.increment(*this); sor = eor; @@ -322,8 +312,8 @@ void BidiResolver<Iterator, Run>::deleteRuns() Run* curr = m_firstRun; while (curr) { - Run* s = curr->next(); - curr->destroy(); + Run* s = curr->m_next; + delete curr; curr = s; } @@ -333,18 +323,18 @@ void BidiResolver<Iterator, Run>::deleteRuns() } template <class Iterator, class Run> -void BidiResolver<Iterator, Run>::reverseRuns(unsigned start, unsigned end) +void BidiResolver<Iterator, Run>::reverseRuns(int start, int end) { if (start >= end) return; - ASSERT(end < m_runCount); + ASSERT(start >= 0 && end < m_runCount); // Get the item before the start of the runs to reverse and put it in // |beforeStart|. |curr| should point to the first run to reverse. Run* curr = m_firstRun; Run* beforeStart = 0; - unsigned i = 0; + int i = 0; while (i < start) { i++; beforeStart = curr; @@ -782,8 +772,6 @@ void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& start, c } } - m_logicallyLastRun = m_lastRun; - // reorder line according to run structure... // do not reverse for visually ordered web sites if (!visualOrder) { @@ -808,22 +796,22 @@ void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& start, c if (!(levelLow % 2)) levelLow++; - unsigned count = runCount() - 1; + int count = runCount() - 1; while (levelHigh >= levelLow) { - unsigned i = 0; + int i = 0; Run* currRun = firstRun(); while (i < count) { while (i < count && currRun && currRun->m_level < levelHigh) { i++; currRun = currRun->next(); } - unsigned start = i; + int start = i; while (i <= count && currRun && currRun->m_level >= levelHigh) { i++; currRun = currRun->next(); } - unsigned end = i - 1; + int end = i-1; reverseRuns(start, end); } levelHigh--; diff --git a/WebCore/platform/text/CString.cpp b/WebCore/platform/text/CString.cpp index f1434ad..4300b29 100644 --- a/WebCore/platform/text/CString.cpp +++ b/WebCore/platform/text/CString.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,8 +26,7 @@ #include "config.h" #include "CString.h" - -using std::min; +#include "DeprecatedCString.h" namespace WebCore { @@ -41,12 +40,17 @@ CString::CString(const char* str, unsigned length) init(str, length); } +CString::CString(const DeprecatedCString& str) +{ + init(str.data(), str.length()); +} + void CString::init(const char* str, unsigned length) { if (!str) return; - m_buffer = CStringBuffer::create(length + 1); + m_buffer = new CStringBuffer(length + 1); memcpy(m_buffer->data(), str, length); m_buffer->data()[length] = '\0'; } @@ -68,11 +72,16 @@ unsigned CString::length() const { return m_buffer ? m_buffer->length() - 1 : 0; } + +DeprecatedCString CString::deprecatedCString() const +{ + return DeprecatedCString(data(), length() + 1); +} CString CString::newUninitialized(size_t length, char*& characterBuffer) { CString result; - result.m_buffer = CStringBuffer::create(length + 1); + result.m_buffer = new CStringBuffer(length + 1); char* bytes = result.m_buffer->data(); bytes[length] = '\0'; characterBuffer = bytes; @@ -86,7 +95,7 @@ void CString::copyBufferIfNeeded() int len = m_buffer->length(); RefPtr<CStringBuffer> m_temp = m_buffer; - m_buffer = CStringBuffer::create(len); + m_buffer = new CStringBuffer(len); memcpy(m_buffer->data(), m_temp->data(), len); } diff --git a/WebCore/platform/text/CString.h b/WebCore/platform/text/CString.h index fcb4c8c..bd1e06c 100644 --- a/WebCore/platform/text/CString.h +++ b/WebCore/platform/text/CString.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,22 +26,23 @@ #ifndef CString_h #define CString_h -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> +using std::min; + namespace WebCore { + class DeprecatedCString; + class CStringBuffer : public RefCounted<CStringBuffer> { public: - static PassRefPtr<CStringBuffer> create(unsigned length) { return adoptRef(new CStringBuffer(length)); } + CStringBuffer(unsigned length) : m_vector(length) { } char* data() { return m_vector.data(); } - size_t length() const { return m_vector.size(); } + unsigned length() const { return m_vector.size(); } private: - CStringBuffer(unsigned length) : m_vector(length) { } - Vector<char> m_vector; }; @@ -60,6 +61,9 @@ namespace WebCore { bool isNull() const { return !m_buffer; } + CString(const DeprecatedCString&); + DeprecatedCString deprecatedCString() const; + private: void copyBufferIfNeeded(); void init(const char*, unsigned length); @@ -69,6 +73,6 @@ namespace WebCore { bool operator==(const CString& a, const CString& b); inline bool operator!=(const CString& a, const CString& b) { return !(a == b); } -} // namespace WebCore +} #endif // CString_h diff --git a/WebCore/platform/text/CharacterNames.h b/WebCore/platform/text/CharacterNames.h index bfbb5b0..5b52479 100644 --- a/WebCore/platform/text/CharacterNames.h +++ b/WebCore/platform/text/CharacterNames.h @@ -39,8 +39,6 @@ namespace WebCore { const UChar bullet = 0x2022; const UChar horizontalEllipsis = 0x2026; const UChar ideographicSpace = 0x3000; - const UChar ideographicComma = 0x3001; - const UChar ideographicFullStop = 0x3002; const UChar leftToRightMark = 0x200E; const UChar leftToRightEmbed = 0x202A; const UChar leftToRightOverride = 0x202D; diff --git a/WebCore/platform/text/PlatformString.h b/WebCore/platform/text/PlatformString.h index 9399fdd..f900513 100644 --- a/WebCore/platform/text/PlatformString.h +++ b/WebCore/platform/text/PlatformString.h @@ -43,6 +43,7 @@ class wxString; namespace WebCore { class CString; +class DeprecatedString; struct StringHash; class String { @@ -131,25 +132,17 @@ public: static String format(const char *, ...) WTF_ATTRIBUTE_PRINTF(1, 2); - void split(const String& separator, Vector<String>& result) const; - void split(const String& separator, bool allowEmptyEntries, Vector<String>& result) const; - void split(UChar separator, Vector<String>& result) const; - void split(UChar separator, bool allowEmptyEntries, Vector<String>& result) const; - - int toIntStrict(bool* ok = 0, int base = 10) const; - unsigned toUIntStrict(bool* ok = 0, int base = 10) const; - int64_t toInt64Strict(bool* ok = 0, int base = 10) const; - uint64_t toUInt64Strict(bool* ok = 0, int base = 10) const; + Vector<String> split(const String& separator, bool allowEmptyEntries = false) const; + Vector<String> split(UChar separator, bool allowEmptyEntries = false) const; int toInt(bool* ok = 0) const; - unsigned toUInt(bool* ok = 0) const; int64_t toInt64(bool* ok = 0) const; uint64_t toUInt64(bool* ok = 0) const; double toDouble(bool* ok = 0) const; float toFloat(bool* ok = 0) const; Length* toLengthArray(int& len) const; Length* toCoordsArray(int& len) const; - bool percentage(int& percentage) const; + bool percentage(int &_percentage) const; // Makes a deep copy. Helpful only if you need to use a String on another thread. // Since the underlying StringImpl objects are immutable, there's no other reason @@ -203,7 +196,10 @@ public: // Determines the writing direction using the Unicode Bidi Algorithm rules P2 and P3. WTF::Unicode::Direction defaultWritingDirection() const { return m_impl ? m_impl->defaultWritingDirection() : WTF::Unicode::LeftToRight; } - + + String(const DeprecatedString&); + DeprecatedString deprecatedString() const; + private: RefPtr<StringImpl> m_impl; }; @@ -226,30 +222,12 @@ inline bool equalIgnoringCase(const String& a, const String& b) { return equalIg inline bool equalIgnoringCase(const String& a, const char* b) { return equalIgnoringCase(a.impl(), b); } inline bool equalIgnoringCase(const char* a, const String& b) { return equalIgnoringCase(a, b.impl()); } -inline bool operator!(const String& str) { return str.isNull(); } - - -// String Operations - -bool charactersAreAllASCII(const UChar*, size_t); - -int charactersToIntStrict(const UChar*, size_t, bool* ok = 0, int base = 10); -unsigned charactersToUIntStrict(const UChar*, size_t, bool* ok = 0, int base = 10); -int64_t charactersToInt64Strict(const UChar*, size_t, bool* ok = 0, int base = 10); -uint64_t charactersToUInt64Strict(const UChar*, size_t, bool* ok = 0, int base = 10); - -int charactersToInt(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage -unsigned charactersToUInt(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage -int64_t charactersToInt64(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage -uint64_t charactersToUInt64(const UChar*, size_t, bool* ok = 0); // ignores trailing garbage +bool operator==(const String& a, const DeprecatedString& b); +inline bool operator==(const DeprecatedString& b, const String& a) { return a == b; } +inline bool operator!=(const String& a, const DeprecatedString& b) { return !(a == b); } +inline bool operator!=(const DeprecatedString& b, const String& a ) { return !(a == b); } -double charactersToDouble(const UChar*, size_t, bool* ok = 0); -float charactersToFloat(const UChar*, size_t, bool* ok = 0); - -int find(const UChar*, size_t, UChar, int startPosition = 0); -int reverseFind(const UChar*, size_t, UChar, int startPosition = -1); - -void append(Vector<UChar>&, const String&); +inline bool operator!(const String& str) { return str.isNull(); } #ifdef __OBJC__ // This is for situations in WebKit where the long standing behavior has been @@ -258,49 +236,8 @@ void append(Vector<UChar>&, const String&); inline NSString* nsStringNilIfEmpty(const String& str) { return str.isEmpty() ? nil : (NSString*)str; } #endif -inline bool charactersAreAllASCII(const UChar* characters, size_t length) -{ - UChar ored = 0; - for (size_t i = 0; i < length; ++i) - ored |= characters[i]; - return !(ored & 0xFF80); -} - -inline int find(const UChar* characters, size_t length, UChar character, int startPosition) -{ - if (startPosition >= static_cast<int>(length)) - return -1; - for (size_t i = startPosition; i < length; ++i) { - if (characters[i] == character) - return static_cast<int>(i); - } - return -1; } -inline int reverseFind(const UChar* characters, size_t length, UChar character, int startPosition) -{ - if (startPosition >= static_cast<int>(length) || !length) - return -1; - if (startPosition < 0) - startPosition += static_cast<int>(length); - while (true) { - if (characters[startPosition] == character) - return startPosition; - if (!startPosition) - return -1; - startPosition--; - } - ASSERT_NOT_REACHED(); - return -1; -} - -inline void append(Vector<UChar>& vector, const String& string) -{ - vector.append(string.characters(), string.length()); -} - -} // namespace WebCore - namespace WTF { // StringHash is the default hash for String diff --git a/WebCore/platform/text/RegularExpression.cpp b/WebCore/platform/text/RegularExpression.cpp index 4213d75..0c26d33 100644 --- a/WebCore/platform/text/RegularExpression.cpp +++ b/WebCore/platform/text/RegularExpression.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ #include "config.h" #include "RegularExpression.h" -#include "PlatformString.h" #include "Logging.h" #include <wtf/RefCounted.h> #include <pcre/pcre.h> @@ -37,44 +36,73 @@ namespace WebCore { const size_t maxSubstrings = 10; const size_t maxOffsets = 3 * maxSubstrings; -class RegularExpression::Private : public RefCounted<Private> { +class RegularExpression::Private : public RefCounted<RegularExpression::Private> +{ public: Private(); - Private(const String& pattern, bool caseSensitive); + Private(DeprecatedString pattern, bool caseSensitive, bool glob); ~Private(); - void compile(bool caseSensitive); + void compile(bool caseSensitive, bool glob); - String pattern; + DeprecatedString pattern; JSRegExp* regex; - String lastMatchString; + DeprecatedString lastMatchString; int lastMatchOffsets[maxOffsets]; int lastMatchCount; int lastMatchPos; int lastMatchLength; }; -RegularExpression::Private::Private() - : RefCounted<Private>(0) - , pattern("") +RegularExpression::Private::Private() : pattern("") { - compile(true); + compile(true, false); } -RegularExpression::Private::Private(const String& p, bool caseSensitive) - : RefCounted<Private>(0) - , pattern(p) - , lastMatchPos(-1) - , lastMatchLength(-1) +RegularExpression::Private::Private(DeprecatedString p, bool caseSensitive, bool glob) : pattern(p), lastMatchPos(-1), lastMatchLength(-1) { - compile(caseSensitive); + compile(caseSensitive, glob); +} + +static DeprecatedString RegExpFromGlob(DeprecatedString glob) +{ + DeprecatedString result = glob; + + // escape regexp metacharacters which are NOT glob metacharacters + + result.replace(RegularExpression("\\\\"), "\\\\"); + result.replace(RegularExpression("\\."), "\\."); + result.replace(RegularExpression("\\+"), "\\+"); + result.replace(RegularExpression("\\$"), "\\$"); + // FIXME: incorrect for ^ inside bracket group + result.replace(RegularExpression("\\^"), "\\^"); + + // translate glob metacharacters into regexp metacharacters + result.replace(RegularExpression("\\*"), ".*"); + result.replace(RegularExpression("\\?"), "."); + + // Require the glob to match the whole string + result = "^" + result + "$"; + + return result; } -void RegularExpression::Private::compile(bool caseSensitive) +void RegularExpression::Private::compile(bool caseSensitive, bool glob) { + DeprecatedString p; + + if (glob) { + p = RegExpFromGlob(pattern); + } else { + p = pattern; + } + // Note we don't honor the Qt syntax for various character classes. If we convert + // to a different underlying engine, we may need to change client code that relies + // on the regex syntax (see FrameMac.mm for a couple examples). + const char* errorMessage; - regex = jsRegExpCompile(pattern.characters(), pattern.length(), + regex = jsRegExpCompile(reinterpret_cast<const UChar*>(p.unicode()), p.length(), caseSensitive ? JSRegExpDoNotIgnoreCase : JSRegExpIgnoreCase, JSRegExpSingleLine, 0, &errorMessage); if (!regex) @@ -87,24 +115,20 @@ RegularExpression::Private::~Private() } -RegularExpression::RegularExpression() - : d(new Private) +RegularExpression::RegularExpression() : d(new RegularExpression::Private()) { } -RegularExpression::RegularExpression(const String& pattern, bool caseSensitive) - : d(new Private(pattern, caseSensitive)) +RegularExpression::RegularExpression(const DeprecatedString &pattern, bool caseSensitive, bool glob) : d(new RegularExpression::Private(pattern, caseSensitive, glob)) { } -RegularExpression::RegularExpression(const char* pattern) - : d(new Private(pattern, true)) +RegularExpression::RegularExpression(const char *cpattern) : d(new RegularExpression::Private(cpattern, true, false)) { } -RegularExpression::RegularExpression(const RegularExpression& re) - : d(re.d) +RegularExpression::RegularExpression(const RegularExpression &re) : d (re.d) { } @@ -112,51 +136,57 @@ RegularExpression::~RegularExpression() { } -RegularExpression& RegularExpression::operator=(const RegularExpression& re) +RegularExpression &RegularExpression::operator=(const RegularExpression &re) { RegularExpression tmp(re); - tmp.d.swap(d); + RefPtr<RegularExpression::Private> tmpD = tmp.d; + + tmp.d = d; + d = tmpD; + return *this; } -String RegularExpression::pattern() const +DeprecatedString RegularExpression::pattern() const { return d->pattern; } -int RegularExpression::match(const String& str, int startFrom, int* matchLength) const +int RegularExpression::match(const DeprecatedString &str, int startFrom, int *matchLength) const { d->lastMatchString = str; // First 2 offsets are start and end offsets; 3rd entry is used internally by pcre - d->lastMatchCount = jsRegExpExecute(d->regex, d->lastMatchString.characters(), - d->lastMatchString.length(), startFrom, d->lastMatchOffsets, maxOffsets); + d->lastMatchCount = jsRegExpExecute(d->regex, reinterpret_cast<const UChar*>(d->lastMatchString.unicode()), d->lastMatchString.length(), startFrom, d->lastMatchOffsets, maxOffsets); if (d->lastMatchCount < 0) { if (d->lastMatchCount != JSRegExpErrorNoMatch) LOG_ERROR("RegularExpression: pcre_exec() failed with result %d", d->lastMatchCount); d->lastMatchPos = -1; d->lastMatchLength = -1; - d->lastMatchString = String(); + d->lastMatchString = DeprecatedString(); return -1; } // 1 means 1 match; 0 means more than one match. First match is recorded in offsets. + //ASSERT(d->lastMatchCount < 2); d->lastMatchPos = d->lastMatchOffsets[0]; d->lastMatchLength = d->lastMatchOffsets[1] - d->lastMatchOffsets[0]; - if (matchLength) + if (matchLength != NULL) { *matchLength = d->lastMatchLength; + } return d->lastMatchPos; } -int RegularExpression::search(const String& str, int startFrom) const +int RegularExpression::search(const DeprecatedString &str, int startFrom) const { - if (startFrom < 0) + if (startFrom < 0) { startFrom = str.length() - startFrom; - return match(str, startFrom, 0); + } + return match(str, startFrom, NULL); } -int RegularExpression::searchRev(const String& str) const +int RegularExpression::searchRev(const DeprecatedString &str) const { - // FIXME: Total hack for now. Search forward, return the last, greedy match + // FIXME: Total hack for now. Search forward, return the last, greedy match int start = 0; int pos; int lastPos = -1; @@ -165,7 +195,7 @@ int RegularExpression::searchRev(const String& str) const int matchLength; pos = match(str, start, &matchLength); if (pos >= 0) { - if (pos + matchLength > lastPos + lastMatchLength) { + if ((pos+matchLength) > (lastPos+lastMatchLength)) { // replace last match if this one is later and not a subset of the last match lastPos = pos; lastMatchLength = matchLength; @@ -189,19 +219,4 @@ int RegularExpression::matchedLength() const return d->lastMatchLength; } -void replace(String& string, const RegularExpression& target, const String& replacement) -{ - int index = 0; - while (index < static_cast<int>(string.length())) { - int matchLength; - index = target.match(string, index, &matchLength); - if (index < 0) - break; - string.replace(index, matchLength, replacement); - index += replacement.length(); - if (!matchLength) - break; // Avoid infinite loop on 0-length matches, e.g. [a-z]* - } } - -} // namespace WebCore diff --git a/WebCore/platform/text/RegularExpression.h b/WebCore/platform/text/RegularExpression.h index 5d1991e..ec1cdef 100644 --- a/WebCore/platform/text/RegularExpression.h +++ b/WebCore/platform/text/RegularExpression.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,38 +26,34 @@ #ifndef RegularExpression_h #define RegularExpression_h -#include <wtf/RefPtr.h> +#include "DeprecatedString.h" namespace WebCore { -class String; - class RegularExpression { public: RegularExpression(); - RegularExpression(const String&, bool caseSensitive = false); - RegularExpression(const char*); + RegularExpression(const DeprecatedString &, bool caseSensitive = false, bool glob = false); + RegularExpression(const char *); ~RegularExpression(); - RegularExpression(const RegularExpression&); - RegularExpression& operator=(const RegularExpression&); + RegularExpression(const RegularExpression &); + RegularExpression &operator=(const RegularExpression &); - String pattern() const; - int match(const String&, int startFrom = 0, int* matchLength = 0) const; + DeprecatedString pattern() const; + int match(const DeprecatedString &, int startFrom = 0, int *matchLength = 0) const; - int search(const String&, int startFrom = 0) const; - int searchRev(const String&) const; + int search(const DeprecatedString &, int startFrom = 0) const; + int searchRev(const DeprecatedString &) const; int pos(int n = 0); int matchedLength() const; - + private: class Private; RefPtr<Private> d; }; -void replace(String&, const RegularExpression&, const String&); - -} // namespace WebCore +} -#endif // RegularExpression_h +#endif diff --git a/WebCore/platform/text/SegmentedString.cpp b/WebCore/platform/text/SegmentedString.cpp index 9f5eb26..0b3c7e9 100644 --- a/WebCore/platform/text/SegmentedString.cpp +++ b/WebCore/platform/text/SegmentedString.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -59,10 +59,10 @@ unsigned SegmentedString::length() const ++length; } if (m_composite) { - Deque<SegmentedSubstring>::const_iterator it = m_substrings.begin(); - Deque<SegmentedSubstring>::const_iterator e = m_substrings.end(); - for (; it != e; ++it) - length += it->m_length; + DeprecatedValueListConstIterator<SegmentedSubstring> i = m_substrings.begin(); + DeprecatedValueListConstIterator<SegmentedSubstring> e = m_substrings.end(); + for (; i != e; ++i) + length += (*i).m_length; } return length; } @@ -70,10 +70,10 @@ unsigned SegmentedString::length() const void SegmentedString::setExcludeLineNumbers() { if (m_composite) { - Deque<SegmentedSubstring>::iterator it = m_substrings.begin(); - Deque<SegmentedSubstring>::iterator e = m_substrings.end(); - for (; it != e; ++it) - it->setExcludeLineNumbers(); + DeprecatedValueListIterator<SegmentedSubstring> i = m_substrings.begin(); + DeprecatedValueListIterator<SegmentedSubstring> e = m_substrings.end(); + for (; i != e; ++i) + (*i).setExcludeLineNumbers(); } else m_currentString.setExcludeLineNumbers(); } @@ -120,10 +120,10 @@ void SegmentedString::append(const SegmentedString &s) ASSERT(!s.escaped()); append(s.m_currentString); if (s.m_composite) { - Deque<SegmentedSubstring>::const_iterator it = s.m_substrings.begin(); - Deque<SegmentedSubstring>::const_iterator e = s.m_substrings.end(); - for (; it != e; ++it) - append(*it); + DeprecatedValueListConstIterator<SegmentedSubstring> i = s.m_substrings.begin(); + DeprecatedValueListConstIterator<SegmentedSubstring> e = s.m_substrings.end(); + for (; i != e; ++i) + append(*i); } m_currentChar = m_pushedChar1 ? &m_pushedChar1 : m_currentString.m_current; } @@ -133,10 +133,10 @@ void SegmentedString::prepend(const SegmentedString &s) ASSERT(!escaped()); ASSERT(!s.escaped()); if (s.m_composite) { - Deque<SegmentedSubstring>::const_reverse_iterator it = s.m_substrings.rbegin(); - Deque<SegmentedSubstring>::const_reverse_iterator e = s.m_substrings.rend(); - for (; it != e; ++it) - prepend(*it); + DeprecatedValueListConstIterator<SegmentedSubstring> i = s.m_substrings.fromLast(); + DeprecatedValueListConstIterator<SegmentedSubstring> e = s.m_substrings.end(); + for (; i != e; --i) + prepend(*i); } prepend(s.m_currentString); m_currentChar = m_pushedChar1 ? &m_pushedChar1 : m_currentString.m_current; @@ -146,7 +146,7 @@ void SegmentedString::advanceSubstring() { if (m_composite) { m_currentString = m_substrings.first(); - m_substrings.removeFirst(); + m_substrings.remove(m_substrings.begin()); if (m_substrings.isEmpty()) m_composite = false; } else { @@ -164,10 +164,10 @@ String SegmentedString::toString() const } m_currentString.appendTo(result); if (m_composite) { - Deque<SegmentedSubstring>::const_iterator it = m_substrings.begin(); - Deque<SegmentedSubstring>::const_iterator e = m_substrings.end(); - for (; it != e; ++it) - it->appendTo(result); + DeprecatedValueListConstIterator<SegmentedSubstring> i = m_substrings.begin(); + DeprecatedValueListConstIterator<SegmentedSubstring> e = m_substrings.end(); + for (; i != e; ++i) + (*i).appendTo(result); } return result; } diff --git a/WebCore/platform/text/SegmentedString.h b/WebCore/platform/text/SegmentedString.h index 79ed1f0..52178d3 100644 --- a/WebCore/platform/text/SegmentedString.h +++ b/WebCore/platform/text/SegmentedString.h @@ -20,22 +20,20 @@ #ifndef SegmentedString_h #define SegmentedString_h +#include "DeprecatedValueList.h" #include "PlatformString.h" -#include <wtf/Deque.h> namespace WebCore { class SegmentedString; class SegmentedSubstring { -public: +private: + friend class SegmentedString; + SegmentedSubstring() : m_length(0), m_current(0), m_doNotExcludeLineNumbers(true) {} - SegmentedSubstring(const String& str) - : m_length(str.length()) - , m_current(str.isEmpty() ? 0 : str.characters()) - , m_string(str) - , m_doNotExcludeLineNumbers(true) - { + SegmentedSubstring(const String& str) : m_string(str), m_length(str.length()), m_doNotExcludeLineNumbers(true) { + m_current = m_length == 0 ? 0 : m_string.characters(); } SegmentedSubstring(const UChar* str, int length) : m_length(length), m_current(length == 0 ? 0 : str), m_doNotExcludeLineNumbers(true) {} @@ -47,8 +45,7 @@ public: void setExcludeLineNumbers() { m_doNotExcludeLineNumbers = false; } - void appendTo(String& str) const - { + void appendTo(String& str) const { if (m_string.characters() == m_current) { if (str.isEmpty()) str = m_string; @@ -59,12 +56,9 @@ public: } } -public: + String m_string; int m_length; const UChar* m_current; - -private: - String m_string; bool m_doNotExcludeLineNumbers; }; @@ -83,8 +77,8 @@ public: void clear(); - void append(const SegmentedString&); - void prepend(const SegmentedString&); + void append(const SegmentedString &); + void prepend(const SegmentedString &); bool excludeLineNumbers() const { return m_currentString.excludeLineNumbers(); } void setExcludeLineNumbers(); @@ -155,8 +149,8 @@ public: const UChar* operator->() const { return current(); } private: - void append(const SegmentedSubstring&); - void prepend(const SegmentedSubstring&); + void append(const SegmentedSubstring &); + void prepend(const SegmentedSubstring &); void advanceSlowCase(); void advanceSlowCase(int& lineNumber); @@ -167,7 +161,7 @@ private: UChar m_pushedChar2; SegmentedSubstring m_currentString; const UChar* m_currentChar; - Deque<SegmentedSubstring> m_substrings; + DeprecatedValueList<SegmentedSubstring> m_substrings; bool m_composite; }; diff --git a/WebCore/platform/text/String.cpp b/WebCore/platform/text/String.cpp index ee4cef4..967e7c8 100644 --- a/WebCore/platform/text/String.cpp +++ b/WebCore/platform/text/String.cpp @@ -22,23 +22,17 @@ #include "PlatformString.h" #include "CString.h" -#include "FloatConversion.h" +#include "DeprecatedString.h" #include "StringBuffer.h" #include "TextEncoding.h" -#include <kjs/dtoa.h> #include <kjs/identifier.h> -#include <limits> -#include <stdarg.h> -#include <wtf/ASCIICType.h> #include <wtf/StringExtras.h> #include <wtf/Vector.h> -#include <wtf/unicode/Unicode.h> +#include <stdarg.h> using KJS::Identifier; using KJS::UString; -using namespace WTF; - namespace WebCore { String::String(const UChar* str, unsigned len) @@ -60,6 +54,13 @@ String::String(const UChar* str) m_impl = StringImpl::create(str, len); } +String::String(const DeprecatedString& str) +{ + if (str.isNull()) + return; + m_impl = StringImpl::create(reinterpret_cast<const UChar*>(str.unicode()), str.length()); +} + String::String(const char* str) { if (!str) @@ -288,7 +289,7 @@ bool String::percentage(int& result) const if ((*m_impl)[m_impl->length() - 1] != '%') return false; - result = charactersToIntStrict(m_impl->characters(), m_impl->length() - 1); + result = DeprecatedConstString(reinterpret_cast<const DeprecatedChar*>(m_impl->characters()), m_impl->length() - 1).string().toInt(); return true; } @@ -309,6 +310,15 @@ const UChar* String::charactersWithNullTermination() return m_impl->characters(); } +DeprecatedString String::deprecatedString() const +{ + if (!m_impl) + return DeprecatedString::null; + if (!m_impl->characters()) + return DeprecatedString("", 0); + return DeprecatedString(reinterpret_cast<const DeprecatedChar*>(m_impl->characters()), m_impl->length()); +} + String String::format(const char *format, ...) { va_list args; @@ -390,46 +400,6 @@ String String::number(double n) return String::format("%.6lg", n); } -int String::toIntStrict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toIntStrict(ok, base); -} - -unsigned String::toUIntStrict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toUIntStrict(ok, base); -} - -int64_t String::toInt64Strict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toInt64Strict(ok, base); -} - -uint64_t String::toUInt64Strict(bool* ok, int base) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toUInt64Strict(ok, base); -} - int String::toInt(bool* ok) const { if (!m_impl) { @@ -440,16 +410,6 @@ int String::toInt(bool* ok) const return m_impl->toInt(ok); } -unsigned String::toUInt(bool* ok) const -{ - if (!m_impl) { - if (ok) - *ok = false; - return 0; - } - return m_impl->toUInt(ok); -} - int64_t String::toInt64(bool* ok) const { if (!m_impl) { @@ -475,7 +435,7 @@ double String::toDouble(bool* ok) const if (!m_impl) { if (ok) *ok = false; - return 0.0; + return 0; } return m_impl->toDouble(ok); } @@ -512,10 +472,10 @@ Length* String::toLengthArray(int& len) const return m_impl ? m_impl->toLengthArray(len) : 0; } -void String::split(const String& separator, bool allowEmptyEntries, Vector<String>& result) const +Vector<String> String::split(const String& separator, bool allowEmptyEntries) const { - result.clear(); - + Vector<String> result; + int startPos = 0; int endPos; while ((endPos = find(separator, startPos)) != -1) { @@ -523,33 +483,17 @@ void String::split(const String& separator, bool allowEmptyEntries, Vector<Strin result.append(substring(startPos, endPos - startPos)); startPos = endPos + separator.length(); } - if (allowEmptyEntries || startPos != static_cast<int>(length())) - result.append(substring(startPos)); -} - -void String::split(const String& separator, Vector<String>& result) const -{ - return split(separator, false, result); -} - -void String::split(UChar separator, bool allowEmptyEntries, Vector<String>& result) const -{ - result.clear(); - - int startPos = 0; - int endPos; - while ((endPos = find(separator, startPos)) != -1) { - if (allowEmptyEntries || startPos != endPos) - result.append(substring(startPos, endPos - startPos)); - startPos = endPos + 1; - } - if (allowEmptyEntries || startPos != static_cast<int>(length())) + if (allowEmptyEntries || startPos != (int)length()) result.append(substring(startPos)); + + return result; } -void String::split(UChar separator, Vector<String>& result) const +Vector<String> String::split(UChar separator, bool allowEmptyEntries) const { - return split(String(&separator, 1), false, result); + Vector<String> result; + + return split(String(&separator, 1), allowEmptyEntries); } #ifndef NDEBUG @@ -588,6 +532,17 @@ String String::fromUTF8(const char* string) return UTF8Encoding().decode(string, strlen(string)); } + +bool operator==(const String& a, const DeprecatedString& b) +{ + unsigned l = a.length(); + if (l != b.length()) + return false; + if (!memcmp(a.characters(), b.unicode(), l * sizeof(UChar))) + return true; + return false; +} + String::String(const Identifier& str) { if (str.isNull()) @@ -616,178 +571,6 @@ String::operator UString() const return UString(reinterpret_cast<const KJS::UChar*>(m_impl->characters()), m_impl->length()); } -// String Operations - -static bool isCharacterAllowedInBase(UChar c, int base) -{ - if (c > 0x7F) - return false; - if (isASCIIDigit(c)) - return c - '0' < base; - if (isASCIIAlpha(c)) { - if (base > 36) - base = 36; - return (c >= 'a' && c < 'a' + base - 10) - || (c >= 'A' && c < 'A' + base - 10); - } - return false; -} - -template <typename IntegralType> -static inline IntegralType toIntegralType(const UChar* data, size_t length, bool* ok, int base) -{ - static const IntegralType integralMax = std::numeric_limits<IntegralType>::max(); - static const bool isSigned = std::numeric_limits<IntegralType>::is_signed; - const IntegralType maxMultiplier = integralMax / base; - - IntegralType value = 0; - bool isOk = false; - bool isNegative = false; - - if (!data) - goto bye; - - // skip leading whitespace - while (length && isSpaceOrNewline(*data)) { - length--; - data++; - } - - if (isSigned && length && *data == '-') { - length--; - data++; - isNegative = true; - } else if (length && *data == '+') { - length--; - data++; - } - - if (!length || !isCharacterAllowedInBase(*data, base)) - goto bye; - - while (length && isCharacterAllowedInBase(*data, base)) { - length--; - IntegralType digitValue; - UChar c = *data; - if (isASCIIDigit(c)) - digitValue = c - '0'; - else if (c >= 'a') - digitValue = c - 'a' + 10; - else - digitValue = c - 'A' + 10; - - if (value > maxMultiplier || (value == maxMultiplier && digitValue > (integralMax % base) + isNegative)) - goto bye; - - value = base * value + digitValue; - data++; - } - - if (isNegative) - value = -value; - - // skip trailing space - while (length && isSpaceOrNewline(*data)) { - length--; - data++; - } - - if (!length) - isOk = true; -bye: - if (ok) - *ok = isOk; - return isOk ? value : 0; -} - -static unsigned lengthOfCharactersAsInteger(const UChar* data, size_t length) -{ - size_t i = 0; - - // Allow leading spaces. - for (; i != length; ++i) { - if (!isSpaceOrNewline(data[i])) - break; - } - - // Allow sign. - if (i != length && (data[i] == '+' || data[i] == '-')) - ++i; - - // Allow digits. - for (; i != length; ++i) { - if (!Unicode::isDigit(data[i])) - break; - } - - return i; -} - -int charactersToIntStrict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType<int>(data, length, ok, base); -} - -unsigned charactersToUIntStrict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType<unsigned>(data, length, ok, base); -} - -int64_t charactersToInt64Strict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType<int64_t>(data, length, ok, base); -} - -uint64_t charactersToUInt64Strict(const UChar* data, size_t length, bool* ok, int base) -{ - return toIntegralType<uint64_t>(data, length, ok, base); -} - -int charactersToInt(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType<int>(data, lengthOfCharactersAsInteger(data, length), ok, 10); -} - -unsigned charactersToUInt(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType<unsigned>(data, lengthOfCharactersAsInteger(data, length), ok, 10); -} - -int64_t charactersToInt64(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType<int64_t>(data, lengthOfCharactersAsInteger(data, length), ok, 10); -} - -uint64_t charactersToUInt64(const UChar* data, size_t length, bool* ok) -{ - return toIntegralType<uint64_t>(data, lengthOfCharactersAsInteger(data, length), ok, 10); -} - -double charactersToDouble(const UChar* data, size_t length, bool* ok) -{ - if (!length) { - if (ok) - *ok = false; - return 0.0; - } - - Vector<char, 256> bytes(length + 1); - for (unsigned i = 0; i < length; ++i) - bytes[i] = data[i] < 0x7F ? data[i] : '?'; - bytes[length] = '\0'; - char* end; - double val = kjs_strtod(bytes.data(), &end); - if (ok) - *ok = (end == 0 || *end == '\0'); - return val; -} - -float charactersToFloat(const UChar* data, size_t length, bool* ok) -{ - // FIXME: This will return ok even when the string fits into a double but not a float. - return narrowPrecisionToFloat(charactersToDouble(data, length, ok)); -} - } // namespace WebCore #ifndef NDEBUG diff --git a/WebCore/platform/text/StringImpl.cpp b/WebCore/platform/text/StringImpl.cpp index f9087b5..0643de6 100644 --- a/WebCore/platform/text/StringImpl.cpp +++ b/WebCore/platform/text/StringImpl.cpp @@ -28,6 +28,7 @@ #include "AtomicString.h" #include "CString.h" #include "CharacterNames.h" +#include "DeprecatedString.h" #include "FloatConversion.h" #include "Length.h" #include "StringBuffer.h" @@ -59,7 +60,8 @@ static inline void deleteUCharVector(const UChar* p) // This constructor is used only to create the empty string. StringImpl::StringImpl() - : m_length(0) + : RefCounted<StringImpl>(1) + , m_length(0) , m_data(0) , m_hash(0) , m_inTable(false) @@ -71,7 +73,8 @@ StringImpl::StringImpl() // operation. Because of that, it's the one constructor that doesn't assert the // length is non-zero, since we support copying the empty string. inline StringImpl::StringImpl(const UChar* characters, unsigned length) - : m_length(length) + : RefCounted<StringImpl>(1) + , m_length(length) , m_hash(0) , m_inTable(false) , m_hasTerminatingNullCharacter(false) @@ -82,7 +85,8 @@ inline StringImpl::StringImpl(const UChar* characters, unsigned length) } inline StringImpl::StringImpl(const StringImpl& str, WithTerminatingNullCharacter) - : m_length(str.m_length) + : RefCounted<StringImpl>(1) + , m_length(str.m_length) , m_hash(str.m_hash) , m_inTable(false) , m_hasTerminatingNullCharacter(true) @@ -94,7 +98,8 @@ inline StringImpl::StringImpl(const StringImpl& str, WithTerminatingNullCharacte } inline StringImpl::StringImpl(const char* characters, unsigned length) - : m_length(length) + : RefCounted<StringImpl>(1) + , m_length(length) , m_hash(0) , m_inTable(false) , m_hasTerminatingNullCharacter(false) @@ -111,7 +116,8 @@ inline StringImpl::StringImpl(const char* characters, unsigned length) } inline StringImpl::StringImpl(UChar* characters, unsigned length, AdoptBuffer) - : m_length(length) + : RefCounted<StringImpl>(1) + , m_length(length) , m_data(characters) , m_hash(0) , m_inTable(false) @@ -121,9 +127,15 @@ inline StringImpl::StringImpl(UChar* characters, unsigned length, AdoptBuffer) ASSERT(length); } +// FIXME: These AtomicString constructors return objects with a refCount of 0, +// even though the others return objects with a refCount of 1. That preserves +// the historical behavior for the hash map translator call sites inside the +// AtomicString code, but is it correct? + // This constructor is only for use by AtomicString. StringImpl::StringImpl(const UChar* characters, unsigned length, unsigned hash) - : m_length(length) + : RefCounted<StringImpl>(0) + , m_length(length) , m_hash(hash) , m_inTable(true) , m_hasTerminatingNullCharacter(false) @@ -139,7 +151,8 @@ StringImpl::StringImpl(const UChar* characters, unsigned length, unsigned hash) // This constructor is only for use by AtomicString. StringImpl::StringImpl(const char* characters, unsigned length, unsigned hash) - : m_length(length) + : RefCounted<StringImpl>(0) + , m_length(length) , m_hash(hash) , m_inTable(true) , m_hasTerminatingNullCharacter(false) @@ -212,7 +225,7 @@ static Length parseLength(const UChar* data, unsigned length) ++i; bool ok; - int r = charactersToIntStrict(data, i, &ok); + int r = DeprecatedConstString(reinterpret_cast<const DeprecatedChar*>(data), i).string().toInt(&ok); /* Skip over any remaining digits, we are not that accurate (5.5% => 5%) */ while (i < length && (Unicode::isDigit(data[i]) || data[i] == '.')) @@ -500,54 +513,84 @@ PassRefPtr<StringImpl> StringImpl::capitalize(UChar previous) return adopt(data); } -int StringImpl::toIntStrict(bool* ok, int base) -{ - return charactersToIntStrict(m_data, m_length, ok, base); -} - -unsigned StringImpl::toUIntStrict(bool* ok, int base) -{ - return charactersToUIntStrict(m_data, m_length, ok, base); -} - -int64_t StringImpl::toInt64Strict(bool* ok, int base) -{ - return charactersToInt64Strict(m_data, m_length, ok, base); -} - -uint64_t StringImpl::toUInt64Strict(bool* ok, int base) -{ - return charactersToUInt64Strict(m_data, m_length, ok, base); -} - int StringImpl::toInt(bool* ok) { - return charactersToInt(m_data, m_length, ok); -} + unsigned i = 0; -unsigned StringImpl::toUInt(bool* ok) -{ - return charactersToUInt(m_data, m_length, ok); + // Allow leading spaces. + for (; i != m_length; ++i) + if (!isSpaceOrNewline(m_data[i])) + break; + + // Allow sign. + if (i != m_length && (m_data[i] == '+' || m_data[i] == '-')) + ++i; + + // Allow digits. + for (; i != m_length; ++i) + if (!Unicode::isDigit(m_data[i])) + break; + + return DeprecatedConstString(reinterpret_cast<const DeprecatedChar*>(m_data), i).string().toInt(ok); } int64_t StringImpl::toInt64(bool* ok) { - return charactersToInt64(m_data, m_length, ok); + unsigned i = 0; + + // Allow leading spaces. + for (; i != m_length; ++i) + if (!isSpaceOrNewline(m_data[i])) + break; + + // Allow sign. + if (i != m_length && (m_data[i] == '+' || m_data[i] == '-')) + ++i; + + // Allow digits. + for (; i != m_length; ++i) + if (!Unicode::isDigit(m_data[i])) + break; + + return DeprecatedConstString(reinterpret_cast<const DeprecatedChar*>(m_data), i).string().toInt64(ok); } uint64_t StringImpl::toUInt64(bool* ok) { - return charactersToUInt64(m_data, m_length, ok); + unsigned i = 0; + + // Allow leading spaces. + for (; i != m_length; ++i) + if (!isSpaceOrNewline(m_data[i])) + break; + + // Allow digits. + for (; i != m_length; ++i) + if (!Unicode::isDigit(m_data[i])) + break; + + return DeprecatedConstString(reinterpret_cast<const DeprecatedChar*>(m_data), i).string().toUInt64(ok); } double StringImpl::toDouble(bool* ok) { - return charactersToDouble(m_data, m_length, ok); + if (!m_length) { + if (ok) + *ok = false; + return 0; + } + char *end; + CString latin1String = Latin1Encoding().encode(characters(), length()); + double val = kjs_strtod(latin1String.data(), &end); + if (ok) + *ok = end == 0 || *end == '\0'; + return val; } float StringImpl::toFloat(bool* ok) { - return charactersToFloat(m_data, m_length, ok); + // FIXME: This will return ok even when the string fits into a double but not a float. + return narrowPrecisionToFloat(toDouble(ok)); } static bool equal(const UChar* a, const char* b, int length) @@ -614,7 +657,15 @@ int StringImpl::find(const char* chs, int index, bool caseSensitive) int StringImpl::find(UChar c, int start) { - return WebCore::find(m_data, m_length, c, start); + unsigned index = start; + if (index >= m_length ) + return -1; + while(index < m_length) { + if (m_data[index] == c) + return index; + index++; + } + return -1; } int StringImpl::find(StringImpl* str, int index, bool caseSensitive) @@ -675,7 +726,18 @@ int StringImpl::find(StringImpl* str, int index, bool caseSensitive) int StringImpl::reverseFind(UChar c, int index) { - return WebCore::reverseFind(m_data, m_length, c, index); + if (index >= (int)m_length || m_length == 0) + return -1; + + if (index < 0) + index += m_length; + while (1) { + if (m_data[index] == c) + return index; + if (index == 0) + return -1; + index--; + } } int StringImpl::reverseFind(StringImpl* str, int index, bool caseSensitive) diff --git a/WebCore/platform/text/StringImpl.h b/WebCore/platform/text/StringImpl.h index 1fb1e95..dd50b2e 100644 --- a/WebCore/platform/text/StringImpl.h +++ b/WebCore/platform/text/StringImpl.h @@ -49,9 +49,6 @@ struct StringHash; struct UCharBufferTranslator; class StringImpl : public RefCounted<StringImpl> { - friend class AtomicString; - friend struct UCharBufferTranslator; - friend struct CStringTranslator; private: StringImpl(); StringImpl(const UChar*, unsigned length); @@ -102,16 +99,9 @@ public: bool containsOnlyWhitespace(); - int toIntStrict(bool* ok = 0, int base = 10); - unsigned toUIntStrict(bool* ok = 0, int base = 10); - int64_t toInt64Strict(bool* ok = 0, int base = 10); - uint64_t toUInt64Strict(bool* ok = 0, int base = 10); - - int toInt(bool* ok = 0); // ignores trailing garbage - unsigned toUInt(bool* ok = 0); // ignores trailing garbage - int64_t toInt64(bool* ok = 0); // ignores trailing garbage - uint64_t toUInt64(bool* ok = 0); // ignores trailing garbage - + int toInt(bool* ok = 0); // ignores trailing garbage, unlike DeprecatedString + int64_t toInt64(bool* ok = 0); // ignores trailing garbage, unlike DeprecatedString + uint64_t toUInt64(bool* ok = 0); // ignores trailing garbage, unlike DeprecatedString double toDouble(bool* ok = 0); float toFloat(bool* ok = 0); @@ -156,6 +146,10 @@ public: #endif private: + friend class AtomicString; + friend struct UCharBufferTranslator; + friend struct CStringTranslator; + unsigned m_length; const UChar* m_data; mutable unsigned m_hash; diff --git a/WebCore/platform/text/TextBreakIteratorICU.cpp b/WebCore/platform/text/TextBreakIteratorICU.cpp index 9941f58..9fd2d0b 100644 --- a/WebCore/platform/text/TextBreakIteratorICU.cpp +++ b/WebCore/platform/text/TextBreakIteratorICU.cpp @@ -25,7 +25,6 @@ #include "TextBreakIteratorInternalICU.h" #include <unicode/ubrk.h> -#include <wtf/Assertions.h> namespace WebCore { @@ -39,7 +38,6 @@ static TextBreakIterator* setUpIterator(bool& createdIterator, TextBreakIterator UErrorCode openStatus = U_ZERO_ERROR; iterator = static_cast<TextBreakIterator*>(ubrk_open(type, currentTextBreakLocaleID(), 0, 0, &openStatus)); createdIterator = true; - ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), "ICU could not open a break iterator: %s (%d)", u_errorName(openStatus), openStatus); } if (!iterator) return 0; diff --git a/WebCore/platform/text/TextCodecICU.cpp b/WebCore/platform/text/TextCodecICU.cpp index 0299b8f..a89a74e 100644 --- a/WebCore/platform/text/TextCodecICU.cpp +++ b/WebCore/platform/text/TextCodecICU.cpp @@ -33,6 +33,7 @@ #include <unicode/ucnv.h> #include <unicode/ucnv_cb.h> #include <wtf/Assertions.h> +#include <wtf/HashMap.h> using std::auto_ptr; using std::min; @@ -85,7 +86,9 @@ void TextCodecICU::registerExtendedEncodingNames(EncodingNameRegistrar registrar // for encoding GB_2312-80 and several others. So, we need to override this behavior, too. if (strcmp(standardName, "GB2312") == 0 || strcmp(standardName, "GB_2312-80") == 0) standardName = "GBK"; +#ifndef ANDROID else +#endif registrar(standardName, standardName); uint16_t numAliases = ucnv_countAliases(name, &error); @@ -262,27 +265,23 @@ String TextCodecICU::decode(const char* bytes, size_t length, bool flush) // We need to apply these fallbacks ourselves as they are not currently supported by ICU and // they were provided by the old TEC encoding path // Needed to fix <rdar://problem/4708689> -static UChar getGbkEscape(UChar32 codePoint) -{ - switch (codePoint) { - case 0x01F9: - return 0xE7C8; - case 0x1E3F: - return 0xE7C7; - case 0x22EF: - return 0x2026; - case 0x301C: - return 0xFF5E; - default: - return 0; +static HashMap<UChar32, UChar>& gbkEscapes() { + static HashMap<UChar32, UChar> escapes; + if (escapes.isEmpty()) { + escapes.add(0x01F9, 0xE7C8); + escapes.add(0x1E3F, 0xE7C7); + escapes.add(0x22EF, 0x2026); + escapes.add(0x301C, 0xFF5E); } + + return escapes; } static void gbkCallbackEscape(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) { - UChar outChar; - if (reason == UCNV_UNASSIGNED && (outChar = getGbkEscape(codePoint))) { + if (codePoint && gbkEscapes().contains(codePoint)) { + UChar outChar = gbkEscapes().get(codePoint); const UChar* source = &outChar; *err = U_ZERO_ERROR; ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); @@ -294,8 +293,8 @@ static void gbkCallbackEscape(const void* context, UConverterFromUnicodeArgs* fr static void gbkCallbackSubstitute(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) { - UChar outChar; - if (reason == UCNV_UNASSIGNED && (outChar = getGbkEscape(codePoint))) { + if (gbkEscapes().contains(codePoint)) { + UChar outChar = gbkEscapes().get(codePoint); const UChar* source = &outChar; *err = U_ZERO_ERROR; ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); diff --git a/WebCore/platform/text/TextCodecLatin1.cpp b/WebCore/platform/text/TextCodecLatin1.cpp index 2e9d116..a687235 100644 --- a/WebCore/platform/text/TextCodecLatin1.cpp +++ b/WebCore/platform/text/TextCodecLatin1.cpp @@ -29,7 +29,6 @@ #include "CString.h" #include "PlatformString.h" #include "StringBuffer.h" -#include <stdio.h> using std::auto_ptr; diff --git a/WebCore/platform/text/TextCodecUserDefined.cpp b/WebCore/platform/text/TextCodecUserDefined.cpp index 3ef1bc9..a420992 100644 --- a/WebCore/platform/text/TextCodecUserDefined.cpp +++ b/WebCore/platform/text/TextCodecUserDefined.cpp @@ -29,7 +29,6 @@ #include "CString.h" #include "PlatformString.h" #include "StringBuffer.h" -#include <stdio.h> using std::auto_ptr; diff --git a/WebCore/platform/text/TextStream.cpp b/WebCore/platform/text/TextStream.cpp index 5aafbc0..b23e769 100644 --- a/WebCore/platform/text/TextStream.cpp +++ b/WebCore/platform/text/TextStream.cpp @@ -26,74 +26,145 @@ #include "config.h" #include "TextStream.h" +#include "DeprecatedString.h" +#include "Logging.h" #include "PlatformString.h" -#include <wtf/StringExtras.h> +#include <wtf/Vector.h> namespace WebCore { -static const size_t printBufferSize = 100; // large enough for any integer or floating point value in string format, including trailing null character +const size_t integerOrPointerAsStringBufferSize = 100; // large enough for any integer or pointer in string format, including trailing null character +const char* const precisionFormats[7] = { "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f"}; +const int maxPrecision = 6; // must match size of precisionFormats +const int defaultPrecision = 6; // matches qt and sprintf(.., "%f", ...) behaviour + +TextStream::TextStream(DeprecatedString* s) + : m_hasByteArray(false), m_string(s), m_precision(defaultPrecision) +{ +} + +TextStream& TextStream::operator<<(char c) +{ + if (m_hasByteArray) + m_byteArray.append(c); + + if (m_string) + m_string->append(DeprecatedChar(c)); + return *this; +} + +TextStream& TextStream::operator<<(short i) +{ + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, "%d", i); + return *this << buffer; +} + +TextStream& TextStream::operator<<(unsigned short i) +{ + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, "%u", i); + return *this << buffer; +} TextStream& TextStream::operator<<(int i) { - char buffer[printBufferSize]; - snprintf(buffer, sizeof(buffer) - 1, "%d", i); + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, "%d", i); return *this << buffer; } TextStream& TextStream::operator<<(unsigned i) { - char buffer[printBufferSize]; - snprintf(buffer, sizeof(buffer) - 1, "%u", i); + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, "%u", i); return *this << buffer; } TextStream& TextStream::operator<<(long i) { - char buffer[printBufferSize]; - snprintf(buffer, sizeof(buffer) - 1, "%ld", i); + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, "%ld", i); return *this << buffer; } TextStream& TextStream::operator<<(unsigned long i) { - char buffer[printBufferSize]; - snprintf(buffer, sizeof(buffer) - 1, "%lu", i); + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, "%lu", i); return *this << buffer; } TextStream& TextStream::operator<<(float f) { - char buffer[printBufferSize]; - snprintf(buffer, sizeof(buffer) - 1, "%.2f", f); + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, precisionFormats[m_precision], f); return *this << buffer; } TextStream& TextStream::operator<<(double d) { - char buffer[printBufferSize]; - snprintf(buffer, sizeof(buffer) - 1, "%.2f", d); + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, precisionFormats[m_precision], d); return *this << buffer; } -TextStream& TextStream::operator<<(const char* string) +TextStream& TextStream::operator<<(const char* s) { - size_t stringLength = strlen(string); - size_t textLength = m_text.size(); - m_text.grow(textLength + stringLength); - for (size_t i = 0; i < stringLength; ++i) - m_text[textLength + i] = string[i]; + if (m_hasByteArray) { + unsigned length = strlen(s); + unsigned oldSize = m_byteArray.size(); + m_byteArray.grow(oldSize + length); + memcpy(m_byteArray.data() + oldSize, s, length); + } + if (m_string) + m_string->append(s); return *this; } -TextStream& TextStream::operator<<(const String& string) +TextStream& TextStream::operator<<(const DeprecatedString& s) { - append(m_text, string); + if (m_hasByteArray) { + unsigned length = s.length(); + unsigned oldSize = m_byteArray.size(); + m_byteArray.grow(oldSize + length); + memcpy(m_byteArray.data() + oldSize, s.latin1(), length); + } + if (m_string) + m_string->append(s); return *this; } -String TextStream::release() +TextStream& TextStream::operator<<(const String& s) +{ + return (*this) << s.deprecatedString(); +} + +TextStream& TextStream::operator<<(void* p) +{ + char buffer[integerOrPointerAsStringBufferSize]; + sprintf(buffer, "%p", p); + return *this << buffer; +} + +TextStream& TextStream::operator<<(const TextStreamManipulator& m) +{ + return m(*this); +} + +int TextStream::precision(int p) +{ + int oldPrecision = m_precision; + + if (p >= 0 && p <= maxPrecision) + m_precision = p; + + return oldPrecision; +} + +TextStream &endl(TextStream& stream) { - return String::adopt(m_text); + return stream << '\n'; } } diff --git a/WebCore/platform/text/TextStream.h b/WebCore/platform/text/TextStream.h index b4b801c..897c267 100644 --- a/WebCore/platform/text/TextStream.h +++ b/WebCore/platform/text/TextStream.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,14 +27,26 @@ #define TextStream_h #include <wtf/Vector.h> -#include <wtf/unicode/Unicode.h> namespace WebCore { +class DeprecatedChar; +class DeprecatedString; class String; +class TextStream; + +typedef TextStream& (*TextStreamManipulator)(TextStream&); + +TextStream& endl(TextStream&); class TextStream { public: + TextStream(DeprecatedString*); + + TextStream& operator<<(char); + TextStream& operator<<(const DeprecatedChar&); + TextStream& operator<<(short); + TextStream& operator<<(unsigned short); TextStream& operator<<(int); TextStream& operator<<(unsigned); TextStream& operator<<(long); @@ -43,11 +55,21 @@ public: TextStream& operator<<(double); TextStream& operator<<(const char*); TextStream& operator<<(const String&); + TextStream& operator<<(const DeprecatedString&); + TextStream& operator<<(void*); - String release(); + TextStream& operator<<(const TextStreamManipulator&); + + int precision(int); private: - Vector<UChar> m_text; + TextStream(const TextStream&); + TextStream& operator=(const TextStream&); + + bool m_hasByteArray; + Vector<char> m_byteArray; + DeprecatedString* m_string; + int m_precision; }; } diff --git a/WebCore/platform/text/mac/ShapeArabic.c b/WebCore/platform/text/mac/ShapeArabic.c index 43e149d..4706e7c 100644 --- a/WebCore/platform/text/mac/ShapeArabic.c +++ b/WebCore/platform/text/mac/ShapeArabic.c @@ -2,28 +2,8 @@ ****************************************************************************** * * Copyright (C) 2000-2004, International Business Machines -* Corporation and others. All Rights Reserved. -* Copyright (C) 2007 Apple Inc. All rights reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining a copy of this -* software and associated documentation files (the "Software"), to deal in the Software -* without restriction, including without limitation the rights to use, copy, modify, -* merge, publish, distribute, and/or sell copies of the Software, and to permit persons -* to whom the Software is furnished to do so, provided that the above copyright notice(s) -* and this permission notice appear in all copies of the Software and that both the above -* copyright notice(s) and this permission notice appear in supporting documentation. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -* PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER -* OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR -* CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR -* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING -* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -* -* Except as contained in this notice, the name of a copyright holder shall not be used in -* advertising or otherwise to promote the sale, use or other dealings in this Software -* without prior written authorization of the copyright holder. +* Corporation and others. All Rights Reserved. +* Copyright (C) 2007 Apple Inc. All rights reserved. * ****************************************************************************** * diff --git a/WebCore/platform/text/qt/StringQt.cpp b/WebCore/platform/text/qt/StringQt.cpp index de9f527..23a684b 100644 --- a/WebCore/platform/text/qt/StringQt.cpp +++ b/WebCore/platform/text/qt/StringQt.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "PlatformString.h" +#include "DeprecatedString.h" #include <QString> @@ -43,14 +44,21 @@ String::String(const QStringRef& ref) { if (!ref.string()) return; - m_impl = StringImpl::create(reinterpret_cast<const UChar*>(ref.unicode()), ref.length()); + m_impl = StringImpl::create(reinterpret_cast<const UChar *>(ref.unicode()), ref.length()); } + String::operator QString() const { return QString(reinterpret_cast<const QChar*>(characters()), length()); } +// DeprecatedString conversions +DeprecatedString::operator QString() const +{ + return QString(reinterpret_cast<const QChar*>(unicode()), length()); +} + } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/text/wx/StringWx.cpp b/WebCore/platform/text/wx/StringWx.cpp index 50919c4..7f91dbf 100644 --- a/WebCore/platform/text/wx/StringWx.cpp +++ b/WebCore/platform/text/wx/StringWx.cpp @@ -27,6 +27,7 @@ #include "PlatformString.h" #include "CString.h" +#include "DeprecatedString.h" #include "unicode/ustring.h" #include <wx/defs.h> @@ -87,6 +88,12 @@ String::operator wxString() const return wxString(utf8().data(), wxConvUTF8); } +// DeprecatedString conversions +DeprecatedString::operator wxString() const +{ + return wxString(utf8().data(), wxConvUTF8); +} + } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/win/BString.cpp b/WebCore/platform/win/BString.cpp index 7d08248..6228dab 100644 --- a/WebCore/platform/win/BString.cpp +++ b/WebCore/platform/win/BString.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,7 +27,7 @@ #include "BString.h" #include "AtomicString.h" -#include "KURL.h" +#include "DeprecatedString.h" #include "PlatformString.h" #include <tchar.h> #include <windows.h> @@ -67,12 +67,12 @@ BString::BString(const String& s) m_bstr = SysAllocStringLen(s.characters(), s.length()); } -BString::BString(const KURL& url) +BString::BString(const DeprecatedString& s) { - if (url.isNull()) + if (s.isNull()) m_bstr = 0; else - m_bstr = SysAllocStringLen(url.string().characters(), url.string().length()); + m_bstr = SysAllocStringLen(String(s).characters(), s.length()); } BString::BString(const AtomicString& s) diff --git a/WebCore/platform/win/BString.h b/WebCore/platform/win/BString.h index 5269491..5f1d75d 100644 --- a/WebCore/platform/win/BString.h +++ b/WebCore/platform/win/BString.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,7 +35,7 @@ typedef wchar_t* BSTR; namespace WebCore { class AtomicString; - class KURL; + class DeprecatedString; class String; class BString { @@ -44,8 +44,8 @@ namespace WebCore { BString(const wchar_t*); BString(const wchar_t*, size_t length); BString(const String&); + BString(const DeprecatedString&); BString(const AtomicString&); - BString(const KURL&); #if PLATFORM(CF) BString(CFStringRef); #endif diff --git a/WebCore/platform/win/ClipboardUtilitiesWin.cpp b/WebCore/platform/win/ClipboardUtilitiesWin.cpp index 7a3e5ff..066e235 100644 --- a/WebCore/platform/win/ClipboardUtilitiesWin.cpp +++ b/WebCore/platform/win/ClipboardUtilitiesWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,12 +26,12 @@ #include "config.h" #include "ClipboardUtilitiesWin.h" +#include "KURL.h" #include "CString.h" #include "DocumentFragment.h" -#include "KURL.h" +#include "markup.h" #include "PlatformString.h" #include "TextEncoding.h" -#include "markup.h" #include <CoreFoundation/CoreFoundation.h> #include <wtf/RetainPtr.h> #include <shlwapi.h> @@ -120,103 +120,93 @@ HGLOBAL createGlobalData(const KURL& url, const String& title) return cbData; } -HGLOBAL createGlobalData(const String& str) -{ - HGLOBAL globalData = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar)); - if (!globalData) - return 0; - UChar* buffer = static_cast<UChar*>(::GlobalLock(globalData)); - memcpy(buffer, str.characters(), str.length() * sizeof(UChar)); - buffer[str.length()] = 0; - ::GlobalUnlock(globalData); - return globalData; -} - -HGLOBAL createGlobalData(const Vector<char>& vector) -{ - HGLOBAL globalData = ::GlobalAlloc(GPTR, vector.size() + 1); - if (!globalData) - return 0; - char* buffer = static_cast<char*>(::GlobalLock(globalData)); - memcpy(buffer, vector.data(), vector.size()); - buffer[vector.size()] = 0; - ::GlobalUnlock(globalData); - return globalData; -} - -static void append(Vector<char>& vector, const char* string) -{ - vector.append(string, strlen(string)); +HGLOBAL createGlobalData(String str) +{ + SIZE_T size = (str.length() + 1) * sizeof(UChar); + HGLOBAL cbData = ::GlobalAlloc(GPTR, size); + if (cbData) { + void* buffer = ::GlobalLock(cbData); + memcpy(buffer, str.charactersWithNullTermination(), size); + ::GlobalUnlock(cbData); + } + return cbData; } -static void append(Vector<char>& vector, const CString& string) +HGLOBAL createGlobalData(CString str) { - vector.append(string.data(), string.length()); + SIZE_T size = str.length() * sizeof(char); + HGLOBAL cbData = ::GlobalAlloc(GPTR, size + 1); + if (cbData) { + char* buffer = static_cast<char*>(::GlobalLock(cbData)); + memcpy(buffer, str.data(), size); + buffer[size] = 0; + ::GlobalUnlock(cbData); + } + return cbData; } // Documentation for the CF_HTML format is available at http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp -void markupToCF_HTML(const String& markup, const String& srcURL, Vector<char>& result) +DeprecatedCString markupToCF_HTML(const String& markup, const String& srcURL) { - if (markup.isEmpty()) - return; - - #define MAX_DIGITS 10 - #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits) - #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u" - #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS) + if (!markup.length()) + return DeprecatedCString(); - const char* header = "Version:0.9\n" - "StartHTML:" NUMBER_FORMAT "\n" - "EndHTML:" NUMBER_FORMAT "\n" - "StartFragment:" NUMBER_FORMAT "\n" - "EndFragment:" NUMBER_FORMAT "\n"; - const char* sourceURLPrefix = "SourceURL:"; + DeprecatedCString cf_html ("Version:0.9"); + DeprecatedCString startHTML ("\nStartHTML:"); + DeprecatedCString endHTML ("\nEndHTML:"); + DeprecatedCString startFragment ("\nStartFragment:"); + DeprecatedCString endFragment ("\nEndFragment:"); + DeprecatedCString sourceURL ("\nSourceURL:"); - const char* startMarkup = "<HTML>\n<BODY>\n<!--StartFragment-->\n"; - const char* endMarkup = "\n<!--EndFragment-->\n</BODY>\n</HTML>"; + bool shouldFillSourceURL = !srcURL.isEmpty() && (srcURL != "about:blank"); + if (shouldFillSourceURL) + sourceURL.append(srcURL.utf8().data()); - CString sourceURLUTF8 = srcURL == blankURL() ? "" : srcURL.utf8(); - CString markupUTF8 = markup.utf8(); + DeprecatedCString startMarkup ("\n<HTML>\n<BODY>\n<!--StartFragment-->\n"); + DeprecatedCString endMarkup ("\n<!--EndFragment-->\n</BODY>\n</HTML>"); // calculate offsets - unsigned startHTMLOffset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4; - if (sourceURLUTF8.length()) - startHTMLOffset += strlen(sourceURLPrefix) + sourceURLUTF8.length() + 1; - unsigned startFragmentOffset = startHTMLOffset + strlen(startMarkup); + const unsigned UINT_MAXdigits = 10; // number of digits in UINT_MAX in base 10 + unsigned startHTMLOffset = cf_html.length() + startHTML.length() + endHTML.length() + startFragment.length() + endFragment.length() + (shouldFillSourceURL ? sourceURL.length() : 0) + (4*UINT_MAXdigits); + unsigned startFragmentOffset = startHTMLOffset + startMarkup.length(); + CString markupUTF8 = markup.utf8(); unsigned endFragmentOffset = startFragmentOffset + markupUTF8.length(); - unsigned endHTMLOffset = endFragmentOffset + strlen(endMarkup); - - append(result, String::format(header, startHTMLOffset, endHTMLOffset, startFragmentOffset, endFragmentOffset).utf8()); - if (sourceURLUTF8.length()) { - append(result, sourceURLPrefix); - append(result, sourceURLUTF8); - result.append('\n'); - } - append(result, startMarkup); - append(result, markupUTF8); - append(result, endMarkup); - - #undef MAX_DIGITS - #undef MAKE_NUMBER_FORMAT_1 - #undef MAKE_NUMBER_FORMAT_2 - #undef NUMBER_FORMAT + unsigned endHTMLOffset = endFragmentOffset + endMarkup.length(); + + // fill in needed data + startHTML.append(String::format("%010u", startHTMLOffset).deprecatedString().utf8()); + endHTML.append(String::format("%010u", endHTMLOffset).deprecatedString().utf8()); + startFragment.append(String::format("%010u", startFragmentOffset).deprecatedString().utf8()); + endFragment.append(String::format("%010u", endFragmentOffset).deprecatedString().utf8()); + startMarkup.append(markupUTF8.data()); + + // create full cf_html string from the fragments + cf_html.append(startHTML); + cf_html.append(endHTML); + cf_html.append(startFragment); + cf_html.append(endFragment); + if (shouldFillSourceURL) + cf_html.append(sourceURL); + cf_html.append(startMarkup); + cf_html.append(endMarkup); + + return cf_html; } String urlToMarkup(const KURL& url, const String& title) { - Vector<UChar> markup; - append(markup, "<a href=\""); - append(markup, url.string()); - append(markup, "\">"); - append(markup, title); - append(markup, "</a>"); - return String::adopt(markup); + String markup("<a href=\""); + markup.append(url.string()); + markup.append("\">"); + markup.append(title); + markup.append("</a>"); + return markup; } void replaceNewlinesWithWindowsStyleNewlines(String& str) { static const UChar Newline = '\n'; - static const char* const WindowsNewline("\r\n"); + static const String WindowsNewline("\r\n"); str.replace(Newline, WindowsNewline); } diff --git a/WebCore/platform/win/ClipboardUtilitiesWin.h b/WebCore/platform/win/ClipboardUtilitiesWin.h index a92a4bf..49898ea 100644 --- a/WebCore/platform/win/ClipboardUtilitiesWin.h +++ b/WebCore/platform/win/ClipboardUtilitiesWin.h @@ -31,12 +31,14 @@ namespace WebCore { +class CString; +class DeprecatedCString; class Document; class KURL; class String; -HGLOBAL createGlobalData(const String&); -HGLOBAL createGlobalData(const Vector<char>&); +HGLOBAL createGlobalData(String str); +HGLOBAL createGlobalData(CString str); HGLOBAL createGlobalData(const KURL& url, const String& title); FORMATETC* urlWFormat(); @@ -49,21 +51,21 @@ FORMATETC* htmlFormat(); FORMATETC* cfHDropFormat(); FORMATETC* smartPasteFormat(); -void markupToCF_HTML(const String& markup, const String& srcURL, Vector<char>& result); +DeprecatedCString markupToCF_HTML(const String& markup, const String& srcURL); String urlToMarkup(const KURL& url, const String& title); -void replaceNewlinesWithWindowsStyleNewlines(String&); -void replaceNBSPWithSpace(String&); +void replaceNewlinesWithWindowsStyleNewlines(String& str); +void replaceNBSPWithSpace(String& str); bool containsFilenames(const IDataObject*); -bool containsHTML(IDataObject*); +bool containsHTML(IDataObject* data); PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*); -PassRefPtr<DocumentFragment> fragmentFromHTML(Document*, IDataObject*); -PassRefPtr<DocumentFragment> fragmentFromCF_HTML(Document*, const String& cf_html); +PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data); +PassRefPtr<DocumentFragment> fragmentFromCF_HTML(Document* doc, const String& cf_html); -String getURL(IDataObject*, bool& success, String* title = 0); -String getPlainText(IDataObject*, bool& success); +String getURL(IDataObject* dataObject, bool& success, String* title = 0); +String getPlainText(IDataObject* dataObject, bool& success); } // namespace WebCore diff --git a/WebCore/platform/win/ClipboardWin.cpp b/WebCore/platform/win/ClipboardWin.cpp index 46e7695..fbbbedb 100644 --- a/WebCore/platform/win/ClipboardWin.cpp +++ b/WebCore/platform/win/ClipboardWin.cpp @@ -26,9 +26,11 @@ #include "config.h" #include "ClipboardWin.h" -#include "CString.h" #include "CachedImage.h" #include "ClipboardUtilitiesWin.h" +#include "csshelper.h" +#include "CString.h" +#include "DeprecatedString.h" #include "Document.h" #include "DragData.h" #include "Editor.h" @@ -40,6 +42,7 @@ #include "HTMLNames.h" #include "Image.h" #include "MIMETypeRegistry.h" +#include "markup.h" #include "Page.h" #include "Pasteboard.h" #include "PlatformMouseEvent.h" @@ -49,16 +52,12 @@ #include "ResourceResponse.h" #include "StringHash.h" #include "WCDataObject.h" -#include "csshelper.h" -#include "markup.h" #include <shlwapi.h> #include <wininet.h> #include <wtf/RefPtr.h> -using namespace std; - namespace WebCore { using namespace HTMLNames; @@ -141,7 +140,7 @@ static String filesystemPathFromUrlOrTitle(const String& url, const String& titl // The filename for any content based drag should be the last element of // the path. If we can't find it, or we're coming up with the name for a link // we just use the entire url. - KURL kurl(url); + KURL kurl(url.deprecatedString()); String lastComponent; if (!isLink && !(lastComponent = kurl.lastPathComponent()).isEmpty()) { len = min<DWORD>(MAX_PATH, lastComponent.length()); @@ -227,10 +226,11 @@ static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, Share WCHAR filePath[MAX_PATH]; if (url.isLocalFile()) { - String localPath = url.path(); + DeprecatedString path = url.path(); // windows does not enjoy a leading slash on paths - if (localPath[0] == '/') - localPath = localPath.substring(1); + if (path[0] == '/') + path = path.mid(1); + String localPath = path.ascii(); LPCTSTR localPathStr = localPath.charactersWithNullTermination(); if (wcslen(localPathStr) + 1 < MAX_PATH) wcscpy_s(filePath, MAX_PATH, localPathStr); @@ -437,9 +437,7 @@ static bool writeURL(WCDataObject *data, const KURL& url, String title, bool wit success = true; if (withHTML) { - Vector<char> cfhtmlData; - markupToCF_HTML(urlToMarkup(url, title), "", cfhtmlData); - medium.hGlobal = createGlobalData(cfhtmlData); + medium.hGlobal = createGlobalData(markupToCF_HTML(urlToMarkup(url, title), "")); if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE))) ::GlobalFree(medium.hGlobal); else @@ -505,7 +503,7 @@ String ClipboardWin::getData(const String& type, bool& success) const return ""; } -bool ClipboardWin::setData(const String& type, const String& data) +bool ClipboardWin::setData(const String &type, const String &data) { // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941> ASSERT(isForDragging()); @@ -515,7 +513,7 @@ bool ClipboardWin::setData(const String& type, const String& data) ClipboardDataType winType = clipboardTypeFromMIMEType(type); if (winType == ClipboardDataTypeURL) - return WebCore::writeURL(m_writableDataObject.get(), KURL(data), String(), false, true); + return WebCore::writeURL(m_writableDataObject.get(), data.deprecatedString(), String(), false, true); if (winType == ClipboardDataTypeText) { STGMEDIUM medium = {0}; @@ -678,7 +676,7 @@ void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, c if (imageURL.isEmpty()) return; - String fullURL = frame->document()->completeURL(parseURL(imageURL)).string(); + String fullURL = frame->document()->completeURL(parseURL(imageURL)); if (fullURL.isEmpty()) return; STGMEDIUM medium = {0}; @@ -686,9 +684,7 @@ void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, c ExceptionCode ec = 0; // Put img tag on the clipboard referencing the image - Vector<char> data; - markupToCF_HTML(imageToMarkup(fullURL), "", data); - medium.hGlobal = createGlobalData(data); + medium.hGlobal = createGlobalData(markupToCF_HTML(imageToMarkup(fullURL), "")); if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE))) ::GlobalFree(medium.hGlobal); } @@ -723,10 +719,7 @@ void ClipboardWin::writeRange(Range* selectedRange, Frame* frame) medium.tymed = TYMED_HGLOBAL; ExceptionCode ec = 0; - Vector<char> data; - markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange), - selectedRange->startContainer(ec)->document()->url().string(), data); - medium.hGlobal = createGlobalData(data); + medium.hGlobal = createGlobalData(markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange), selectedRange->startContainer(ec)->document()->url())); if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE))) ::GlobalFree(medium.hGlobal); diff --git a/WebCore/platform/win/DragImageCairoWin.cpp b/WebCore/platform/win/DragImageCairoWin.cpp index 5fff64f..1c9973d 100644 --- a/WebCore/platform/win/DragImageCairoWin.cpp +++ b/WebCore/platform/win/DragImageCairoWin.cpp @@ -47,7 +47,8 @@ DragImageRef createDragImageFromImage(Image* img) { notImplemented(); - return 0; + DragImageRef temp; + return temp; } } diff --git a/WebCore/platform/win/FileChooserWin.cpp b/WebCore/platform/win/FileChooserWin.cpp index 1bc4681..905bf92 100644 --- a/WebCore/platform/win/FileChooserWin.cpp +++ b/WebCore/platform/win/FileChooserWin.cpp @@ -41,8 +41,7 @@ namespace WebCore { FileChooser::FileChooser(FileChooserClient* client, const String& filename) - : RefCounted<FileChooser>(0) - , m_client(client) + : m_client(client) , m_filename(filename) , m_icon(chooseIcon(filename)) { diff --git a/WebCore/platform/win/FileSystemWin.cpp b/WebCore/platform/win/FileSystemWin.cpp index 2a6c5d1..f721f3e 100644 --- a/WebCore/platform/win/FileSystemWin.cpp +++ b/WebCore/platform/win/FileSystemWin.cpp @@ -126,11 +126,6 @@ String homeDirectoryPath() return ""; } -String pathGetFileName(const String& path) -{ - return String(PathFindFileName(String(path).charactersWithNullTermination())); -} - static String bundleName() { static bool initialized; @@ -219,12 +214,6 @@ int writeToFile(PlatformFileHandle handle, const char* data, int length) return -1; return static_cast<int>(bytesWritten); } - -bool unloadModule(PlatformModule module) -{ - return ::FreeLibrary(module); -} - String localUserSpecificStorageDirectory() { return cachedStorageDirectory(CSIDL_LOCAL_APPDATA); diff --git a/WebCore/platform/win/MutexWin.cpp b/WebCore/platform/win/MutexWin.cpp new file mode 100644 index 0000000..41d7fe4 --- /dev/null +++ b/WebCore/platform/win/MutexWin.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Brent Fulgham + * + * 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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 "Threading.h" + +namespace WebCore { + +Mutex::Mutex() +{ + m_mutex.m_recursionCount = 0; + ::InitializeCriticalSection(&m_mutex.m_internalMutex); +} + +Mutex::~Mutex() +{ + ::DeleteCriticalSection(&m_mutex.m_internalMutex); +} + +void Mutex::lock() +{ + ::EnterCriticalSection(&m_mutex.m_internalMutex); + ++m_mutex.m_recursionCount; +} + +bool Mutex::tryLock() +{ + // This method is modeled after the behavior of pthread_mutex_trylock, + // which will return an error if the lock is already owned by the + // current thread. Since the primitive Win32 'TryEnterCriticalSection' + // treats this as a successful case, it changes the behavior of several + // tests in WebKit that check to see if the current thread already + // owned this mutex (see e.g., IconDatabase::getOrCreateIconRecord) + DWORD result = ::TryEnterCriticalSection(&m_mutex.m_internalMutex); + + if (result != 0) { // We got the lock + // If this thread already had the lock, we must unlock and + // return false so that we mimic the behavior of POSIX's + // pthread_mutex_trylock: + if (m_mutex.m_recursionCount > 0) { + ::LeaveCriticalSection(&m_mutex.m_internalMutex); + return false; + } + + ++m_mutex.m_recursionCount; + return true; + } + + return false; +} + +void Mutex::unlock() +{ + --m_mutex.m_recursionCount; + ::LeaveCriticalSection(&m_mutex.m_internalMutex); +} + +} // namespace WebCore diff --git a/WebCore/platform/win/PasteboardWin.cpp b/WebCore/platform/win/PasteboardWin.cpp index 506cc7b..427c303 100644 --- a/WebCore/platform/win/PasteboardWin.cpp +++ b/WebCore/platform/win/PasteboardWin.cpp @@ -26,10 +26,11 @@ #include "config.h" #include "Pasteboard.h" -#include "CString.h" #include "ClipboardUtilitiesWin.h" -#include "Document.h" +#include "CString.h" +#include "DeprecatedString.h" #include "DocumentFragment.h" +#include "Document.h" #include "Element.h" #include "Frame.h" #include "HitTestResult.h" @@ -115,10 +116,7 @@ void Pasteboard::writeSelection(Range* selectedRange, bool canSmartCopyOrDelete, // Put CF_HTML format on the pasteboard if (::OpenClipboard(m_owner)) { ExceptionCode ec = 0; - Vector<char> data; - markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange), - selectedRange->startContainer(ec)->document()->url().string(), data); - HGLOBAL cbData = createGlobalData(data); + HGLOBAL cbData = createGlobalData(markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange), selectedRange->startContainer(ec)->document()->url())); if (!::SetClipboardData(HTMLClipboardFormat, cbData)) ::GlobalFree(cbData); ::CloseClipboard(); @@ -168,9 +166,7 @@ void Pasteboard::writeURL(const KURL& url, const String& titleStr, Frame* frame) // write to clipboard in format CF_HTML to be able to paste into contenteditable areas as a link if (::OpenClipboard(m_owner)) { - Vector<char> data; - markupToCF_HTML(urlToMarkup(url, title), "", data); - HGLOBAL cbData = createGlobalData(data); + HGLOBAL cbData = createGlobalData(markupToCF_HTML(urlToMarkup(url, title), "")); if (!::SetClipboardData(HTMLClipboardFormat, cbData)) ::GlobalFree(cbData); ::CloseClipboard(); diff --git a/WebCore/platform/win/PlatformScrollBarWin.cpp b/WebCore/platform/win/PlatformScrollBarWin.cpp deleted file mode 100644 index f59f27a..0000000 --- a/WebCore/platform/win/PlatformScrollBarWin.cpp +++ /dev/null @@ -1,772 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * Copyright (C) 2008 Brent Fulgham - * - * 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "PlatformScrollBar.h" - -#include "EventHandler.h" -#include "FrameView.h" -#include "Frame.h" -#include "GraphicsContext.h" -#include "IntRect.h" -#include "PlatformMouseEvent.h" -#include "SoftLinking.h" - -#include <windows.h> -#include "RenderThemeWin.h" - -// Generic state constants -#define TS_NORMAL 1 -#define TS_HOVER 2 -#define TS_ACTIVE 3 -#define TS_DISABLED 4 -#define TS_FOCUSED 5 - -#define ABS_UPNORMAL 1 -#define ABS_DOWNNORMAL 5 -#define ABS_LEFTNORMAL 9 -#define ABS_RIGHTNORMAL 13 - -static const unsigned SP_ABS_HOT_MODIFIER = 1; -static const unsigned SP_ABS_PRESSED_MODIFIER = 2; -static const unsigned SP_ABS_DISABLE_MODIFIER = 3; - -// Scrollbar constants -#define SP_BUTTON 1 -#define SP_THUMBHOR 2 -#define SP_THUMBVERT 3 -#define SP_TRACKSTARTHOR 4 -#define SP_TRACKENDHOR 5 -#define SP_TRACKSTARTVERT 6 -#define SP_TRACKENDVERT 7 -#define SP_GRIPPERHOR 8 -#define SP_GRIPPERVERT 9 - -using namespace std; - -namespace WebCore { - -// FIXME: We should get these numbers from SafariTheme -static int cHorizontalWidth; -static int cHorizontalHeight; -static int cVerticalWidth; -static int cVerticalHeight; -static int cHorizontalButtonWidth; -static int cVerticalButtonHeight; -static int cRealButtonLength = 28; -static int cButtonInset = 14; -static int cButtonHitInset = 3; -// cRealButtonLength - cButtonInset -static int cThumbWidth; -static int cThumbHeight; -static int cThumbMinLength = 26; - -static HANDLE cScrollBarTheme = 0; - -// FIXME: Refactor the soft-linking code so that it can be shared with RenderThemeWin -SOFT_LINK_LIBRARY(uxtheme) -SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) -SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) -SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect)) -SOFT_LINK(uxtheme, DrawThemeEdge, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, unsigned uEdge, unsigned uFlags, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, uEdge, uFlags, pClipRect)) -SOFT_LINK(uxtheme, GetThemeContentRect, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pContentRect), (hTheme, hdc, iPartId, iStateId, pRect, pContentRect)) -SOFT_LINK(uxtheme, GetThemePartSize, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, RECT* pRect, int ts, SIZE* psz), (hTheme, hdc, iPartId, iStateId, pRect, ts, psz)) -SOFT_LINK(uxtheme, GetThemeSysFont, HRESULT, WINAPI, (HANDLE hTheme, int iFontId, OUT LOGFONT* pFont), (hTheme, iFontId, pFont)) -SOFT_LINK(uxtheme, GetThemeColor, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, int iPropId, OUT COLORREF* pColor), (hTheme, hdc, iPartId, iStateId, iPropId, pColor)) - -static void checkAndInitScrollbarTheme() -{ - if (uxthemeLibrary() && !cScrollBarTheme) - cScrollBarTheme = OpenThemeData(0, L"Scrollbar"); -} - -// May need to add stuff to these later, so keep the graphics context retrieval/release in some helpers. -static HDC prepareForDrawing(GraphicsContext* g, const IntRect& r) -{ - return g->getWindowsContext(r); -} - -static void doneDrawing(GraphicsContext* g, HDC hdc, const IntRect& r) -{ - g->releaseWindowsContext(hdc, r); -} -// End Copied from RenderThemeWin - -const double cInitialTimerDelay = 0.25; -const double cNormalTimerDelay = 0.05; - -PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size) - : Scrollbar(client, orientation, size) - , m_hoveredPart(NoPart) - , m_pressedPart(NoPart) - , m_pressedPos(0) - , m_scrollTimer(this, &PlatformScrollbar::autoscrollTimerFired) - , m_overlapsResizer(false) -{ - // Obtain the correct scrollbar sizes from the system. - // FIXME: We should update these on a WM_SETTINGSCHANGE, too. - if (!cHorizontalHeight) { - cHorizontalHeight = ::GetSystemMetrics(SM_CYHSCROLL); - cHorizontalWidth = ::GetSystemMetrics(SM_CXHSCROLL); - cVerticalHeight = ::GetSystemMetrics(SM_CYVSCROLL); - cVerticalWidth = ::GetSystemMetrics(SM_CXVSCROLL); - cThumbWidth = ::GetSystemMetrics(SM_CXHTHUMB); - cThumbHeight = ::GetSystemMetrics(SM_CYVTHUMB); - cHorizontalButtonWidth = ::GetSystemMetrics(SM_CYVSCROLL); - cVerticalButtonHeight = ::GetSystemMetrics(SM_CXHSCROLL); - } - - if (orientation == VerticalScrollbar) - setFrameGeometry(IntRect(0, 0, cVerticalWidth, cVerticalHeight)); - else - setFrameGeometry(IntRect(0, 0, cHorizontalWidth, cHorizontalHeight)); -} - -PlatformScrollbar::~PlatformScrollbar() -{ - stopTimerIfNeeded(); -} - -void PlatformScrollbar::updateThumbPosition() -{ - invalidateTrack(); -} - -void PlatformScrollbar::updateThumbProportion() -{ - invalidateTrack(); -} - -static IntRect trackRepaintRect(const IntRect& trackRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) -{ - const int cButtonLength = (orientation == VerticalScrollbar) ? cVerticalButtonHeight : cHorizontalButtonWidth; - - IntRect paintRect(trackRect); - if (orientation == HorizontalScrollbar) - paintRect.inflateX(cButtonLength); - else - paintRect.inflateY(cButtonLength); - - return paintRect; -} - -static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start) -{ - IntRect paintRect(buttonRect); - if (orientation == HorizontalScrollbar) { - paintRect.setWidth(cRealButtonLength); - if (!start) - paintRect.setX(buttonRect.x() - (cRealButtonLength - buttonRect.width())); - } else { - paintRect.setHeight(cRealButtonLength); - if (!start) - paintRect.setY(buttonRect.y() - (cRealButtonLength - buttonRect.height())); - } - - return paintRect; -} - -void PlatformScrollbar::invalidateTrack() -{ - IntRect rect = trackRepaintRect(trackRect(), m_orientation, controlSize()); - rect.move(-x(), -y()); - invalidateRect(rect); -} - -void PlatformScrollbar::invalidatePart(ScrollbarPart part) -{ - if (part == NoPart) - return; - - IntRect result; - switch (part) { - case BackButtonPart: - result = buttonRepaintRect(backButtonRect(), m_orientation, controlSize(), true); - break; - case ForwardButtonPart: - result = buttonRepaintRect(forwardButtonRect(), m_orientation, controlSize(), false); - break; - default: { - IntRect beforeThumbRect, thumbRect, afterThumbRect; - splitTrack(trackRect(), beforeThumbRect, thumbRect, afterThumbRect); - if (part == BackTrackPart) - result = beforeThumbRect; - else if (part == ForwardTrackPart) - result = afterThumbRect; - else - result = thumbRect; - } - } - result.move(-x(), -y()); - invalidateRect(result); -} - -int PlatformScrollbar::width() const -{ - return Widget::width(); -} - -int PlatformScrollbar::height() const -{ - return Widget::height(); -} - -void PlatformScrollbar::setRect(const IntRect& rect) -{ - // Get our window resizer rect and see if we overlap. Adjust to avoid the overlap - // if necessary. - IntRect adjustedRect(rect); - if (parent() && parent()->isFrameView()) { - bool overlapsResizer = false; - FrameView* view = static_cast<FrameView*>(parent()); - IntRect resizerRect = view->windowResizerRect(); - resizerRect.setLocation(view->convertFromContainingWindow(resizerRect.location())); - if (rect.intersects(resizerRect)) { - if (orientation() == HorizontalScrollbar) { - int overlap = rect.right() - resizerRect.x(); - if (overlap > 0 && resizerRect.right() >= rect.right()) { - adjustedRect.setWidth(rect.width() - overlap); - overlapsResizer = true; - } - } else { - int overlap = rect.bottom() - resizerRect.y(); - if (overlap > 0 && resizerRect.bottom() >= rect.bottom()) { - adjustedRect.setHeight(rect.height() - overlap); - overlapsResizer = true; - } - } - } - - if (overlapsResizer != m_overlapsResizer) { - m_overlapsResizer = overlapsResizer; - view->adjustOverlappingScrollbarCount(m_overlapsResizer ? 1 : -1); - } - } - - setFrameGeometry(adjustedRect); -} - -void PlatformScrollbar::setParent(ScrollView* parentView) -{ - if (!parentView && m_overlapsResizer && parent() && parent()->isFrameView()) - static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(-1); - Widget::setParent(parentView); -} - -void PlatformScrollbar::setEnabled(bool enabled) -{ - if (enabled != isEnabled()) { - Widget::setEnabled(enabled); - invalidate(); - } -} - -void PlatformScrollbar::paint(GraphicsContext* graphicsContext, const IntRect& damageRect) -{ - if (graphicsContext->updatingControlTints()) { - invalidate(); - return; - } - - if (graphicsContext->paintingDisabled()) - return; - - // Don't paint anything if the scrollbar doesn't intersect the damage rect. - if (!frameGeometry().intersects(damageRect)) - return; - - IntRect track = trackRect(); - paintTrack(graphicsContext, track, true, damageRect); - - if (hasButtons()) { - paintButton(graphicsContext, backButtonRect(), true, damageRect); - paintButton(graphicsContext, forwardButtonRect(), false, damageRect); - } - - if (hasThumb() && damageRect.intersects(track)) { - IntRect startTrackRect, thumbRect, endTrackRect; - splitTrack(track, startTrackRect, thumbRect, endTrackRect); - paintThumb(graphicsContext, thumbRect, damageRect); - } -} - -bool PlatformScrollbar::hasButtons() const -{ - return isEnabled() && (m_orientation == HorizontalScrollbar ? width() : height()) >= 2 * (cRealButtonLength - cButtonHitInset); -} - -bool PlatformScrollbar::hasThumb() const -{ - return isEnabled() && (m_orientation == HorizontalScrollbar ? width() : height()) >= 2 * cButtonInset + cThumbMinLength + 1; -} - -IntRect PlatformScrollbar::backButtonRect() const -{ - // Our actual rect will shrink to half the available space when - // we have < 34 pixels left. This allows the scrollbar - // to scale down and function even at tiny sizes. - if (m_orientation == HorizontalScrollbar) - return IntRect(x(), y(), cHorizontalButtonWidth, cHorizontalHeight); - return IntRect(x(), y(), cVerticalWidth, cVerticalButtonHeight); -} - -IntRect PlatformScrollbar::forwardButtonRect() const -{ - // Our desired rect is essentially 17x17. - - // Our actual rect will shrink to half the available space when - // we have < 34 pixels left. This allows the scrollbar - // to scale down and function even at tiny sizes. - if (m_orientation == HorizontalScrollbar) - return IntRect(x() + width() - cHorizontalButtonWidth, y(), cHorizontalButtonWidth, cHorizontalHeight); - return IntRect(x(), y() + height() - cVerticalButtonHeight, cVerticalWidth, cVerticalButtonHeight); -} - -IntRect PlatformScrollbar::trackRect() const -{ - if (m_orientation == HorizontalScrollbar) { - if (!hasButtons()) - return IntRect(x(), y(), width(), cHorizontalHeight); - return IntRect(x() + cHorizontalButtonWidth, y(), width() - 2 * cHorizontalButtonWidth, cHorizontalHeight); - } - - if (!hasButtons()) - return IntRect(x(), y(), cVerticalWidth, height()); - return IntRect(x(), y() + cVerticalButtonHeight, cVerticalWidth, height() - 2 * cVerticalButtonHeight); -} - -IntRect PlatformScrollbar::thumbRect() const -{ - IntRect beforeThumbRect, thumbRect, afterThumbRect; - splitTrack(trackRect(), beforeThumbRect, thumbRect, afterThumbRect); - return thumbRect; -} - -IntRect PlatformScrollbar::gripperRect(const IntRect& thumbRect) const -{ - return IntRect(); -} - -void PlatformScrollbar::splitTrack(const IntRect& trackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect) const -{ - // This function won't even get called unless we're big enough to have some combination of these three rects where at least - // one of them is non-empty. - int thumbPos = thumbPosition(); - if (m_orientation == HorizontalScrollbar) { - thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - cThumbHeight) / 2, thumbLength(), cThumbHeight); - beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos, trackRect.height()); - afterThumbRect = IntRect(thumbRect.x() + thumbRect.width(), trackRect.y(), trackRect.right() - thumbRect.right(), trackRect.height()); - } else { - thumbRect = IntRect(trackRect.x() + (trackRect.width() - cThumbWidth) / 2, trackRect.y() + thumbPos, cThumbWidth, thumbLength()); - beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos); - afterThumbRect = IntRect(trackRect.x(), thumbRect.y() + thumbRect.height(), trackRect.width(), trackRect.bottom() - thumbRect.bottom()); - } -} - -int PlatformScrollbar::thumbPosition() const -{ - if (isEnabled()) - return (float)m_currentPos * (trackLength() - thumbLength()) / (m_totalSize - m_visibleSize); - return 0; -} - -int PlatformScrollbar::thumbLength() const -{ - if (!isEnabled()) - return 0; - - float proportion = (float)(m_visibleSize) / m_totalSize; - int trackLen = trackLength(); - int length = proportion * trackLen; - int minLength = cThumbMinLength; - length = max(length, minLength); - if (length > trackLen) - length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track). - return length; -} - -int PlatformScrollbar::trackLength() const -{ - return (m_orientation == HorizontalScrollbar) ? trackRect().width() : trackRect().height(); -} - -void PlatformScrollbar::paintButton(GraphicsContext* context, const IntRect& rect, bool start, const IntRect& damageRect) const -{ - IntRect paintRect = buttonRepaintRect(rect, m_orientation, controlSize(), start); - - if (!damageRect.intersects(paintRect)) - return; - - unsigned part = 0; - unsigned state = 0; - unsigned classicPart = 0; - unsigned classicState = 0; - - if (m_orientation == HorizontalScrollbar) { - state = start ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL; - classicPart = start ? DFCS_SCROLLLEFT : DFCS_SCROLLRIGHT; - } else { - state = start ? ABS_UPNORMAL : ABS_DOWNNORMAL; - classicPart = start ? DFCS_SCROLLUP : DFCS_SCROLLDOWN; - } - - if (!isEnabled()) { - state += SP_ABS_DISABLE_MODIFIER; - classicState |= DFCS_INACTIVE; - } else if ((m_pressedPart == BackButtonPart && start) - || (m_pressedPart == ForwardButtonPart && !start)) { - state += SP_ABS_PRESSED_MODIFIER; - classicState |= DFCS_PUSHED | DFCS_FLAT; - } else if (m_client->isActive()) { - state += SP_ABS_HOT_MODIFIER; - classicState |= DFCS_HOT; - } - - HDC hdc = prepareForDrawing(context, rect); - RECT widgetRect = rect; - checkAndInitScrollbarTheme(); - - if (cScrollBarTheme) - DrawThemeBackground(cScrollBarTheme, hdc, SP_BUTTON, state, &widgetRect, NULL); - else - DrawFrameControl(hdc, &widgetRect, classicPart, classicState); - - doneDrawing(context, hdc, rect); -} - -void PlatformScrollbar::paintTrack(GraphicsContext* context, const IntRect& rect, bool start, const IntRect& damageRect) const -{ - IntRect paintRect = hasButtons() ? trackRepaintRect(rect, m_orientation, controlSize()) : rect; - - if (!damageRect.intersects(paintRect)) - return; - - unsigned part = 0; - unsigned classicPart = DFC_SCROLL; - if (m_orientation == HorizontalScrollbar) - part = start ? SP_TRACKSTARTHOR : SP_TRACKENDHOR; - else - part = start ? SP_TRACKSTARTVERT : SP_TRACKENDVERT; - - unsigned state = TS_DISABLED; - unsigned classicState = DFCS_MONO; - if (m_client->isActive()) - state |= TS_ACTIVE; - else - classicState |= DFCS_INACTIVE; - - if (hasButtons()) - state |= TS_NORMAL; - - HDC hdc = prepareForDrawing(context, rect); - RECT widgetRect = rect; - checkAndInitScrollbarTheme(); - - if (cScrollBarTheme) - DrawThemeBackground(cScrollBarTheme, hdc, part, state, &widgetRect, NULL); - else - DrawFrameControl(hdc, &widgetRect, DFC_SCROLL, classicState); - - doneDrawing(context, hdc, rect); -} - -void PlatformScrollbar::paintThumb(GraphicsContext* context, const IntRect& rect, const IntRect& damageRect) const -{ - if (!damageRect.intersects(rect)) - return; - - unsigned part = (m_orientation == HorizontalScrollbar) ? SP_THUMBHOR : SP_THUMBVERT; - unsigned state = 0; - - if (!isEnabled()) - state += SP_ABS_DISABLE_MODIFIER; - else if (m_client->isActive()) - state += SP_ABS_HOT_MODIFIER; - - HDC hdc = prepareForDrawing(context, rect); - RECT widgetRect = rect; - checkAndInitScrollbarTheme(); - - if (cScrollBarTheme) { - DrawThemeBackground(cScrollBarTheme, hdc, part, state, &widgetRect, NULL); - paintGripper(hdc, widgetRect); - } else { - HGDIOBJ hSaveBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE)); - DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT); - SelectObject(hdc,hSaveBrush); - } - - doneDrawing(context, hdc, rect); -} - -void PlatformScrollbar::paintGripper(HDC hdc, const IntRect& rect) const -{ - unsigned part = (m_orientation == HorizontalScrollbar) ? SP_GRIPPERHOR : SP_GRIPPERVERT; - unsigned state = 0; - - if (m_client->isActive()) - state |= TS_ACTIVE; - - RECT widgetRect = rect; - checkAndInitScrollbarTheme(); - - if (cScrollBarTheme) - DrawThemeBackground(cScrollBarTheme, hdc, part, state, &widgetRect, NULL); -} - -ScrollbarPart PlatformScrollbar::hitTest(const PlatformMouseEvent& evt) -{ - if (!isEnabled()) - return NoPart; - - IntPoint mousePosition = convertFromContainingWindow(evt.pos()); - mousePosition.move(x(), y()); - - if (hasButtons()) { - if (backButtonRect().contains(mousePosition)) - return BackButtonPart; - - if (forwardButtonRect().contains(mousePosition)) - return ForwardButtonPart; - } - - if (!hasThumb()) - return NoPart; - - IntRect track = trackRect(); - if (track.contains(mousePosition)) { - IntRect beforeThumbRect, thumbRect, afterThumbRect; - splitTrack(track, beforeThumbRect, thumbRect, afterThumbRect); - if (beforeThumbRect.contains(mousePosition)) - return BackTrackPart; - if (thumbRect.contains(mousePosition)) - return ThumbPart; - return ForwardTrackPart; - } - - return NoPart; -} - -bool PlatformScrollbar::handleMouseMoveEvent(const PlatformMouseEvent& evt) -{ - if (m_pressedPart == ThumbPart) { - // Drag the thumb. - int thumbPos = thumbPosition(); - int thumbLen = thumbLength(); - int trackLen = trackLength(); - int maxPos = trackLen - thumbLen; - int delta = 0; - if (m_orientation == HorizontalScrollbar) - delta = convertFromContainingWindow(evt.pos()).x() - m_pressedPos; - else - delta = convertFromContainingWindow(evt.pos()).y() - m_pressedPos; - - if (delta > 0) - // The mouse moved down/right. - delta = min(maxPos - thumbPos, delta); - else if (delta < 0) - // The mouse moved up/left. - delta = max(-thumbPos, delta); - - if (delta != 0) { - setValue((float)(thumbPos + delta) * (m_totalSize - m_visibleSize) / (trackLen - thumbLen)); - m_pressedPos += thumbPosition() - thumbPos; - } - - return true; - } - - if (m_pressedPart != NoPart) - m_pressedPos = (m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : convertFromContainingWindow(evt.pos()).y()); - - ScrollbarPart part = hitTest(evt); - if (part != m_hoveredPart) { - if (m_pressedPart != NoPart) { - if (part == m_pressedPart) { - // The mouse is moving back over the pressed part. We - // need to start up the timer action again. - startTimerIfNeeded(cNormalTimerDelay); - invalidatePart(m_pressedPart); - } else if (m_hoveredPart == m_pressedPart) { - // The mouse is leaving the pressed part. Kill our timer - // if needed. - stopTimerIfNeeded(); - invalidatePart(m_pressedPart); - } - } else { - invalidatePart(part); - invalidatePart(m_hoveredPart); - } - m_hoveredPart = part; - } - - return true; -} - -bool PlatformScrollbar::handleMouseOutEvent(const PlatformMouseEvent& evt) -{ - invalidatePart(m_hoveredPart); - m_hoveredPart = NoPart; - - return true; -} - -bool PlatformScrollbar::handleMousePressEvent(const PlatformMouseEvent& evt) -{ - m_pressedPart = hitTest(evt); - m_pressedPos = (m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : convertFromContainingWindow(evt.pos()).y()); - invalidatePart(m_pressedPart); - autoscrollPressedPart(cInitialTimerDelay); - return true; -} - -bool PlatformScrollbar::handleMouseReleaseEvent(const PlatformMouseEvent& evt) -{ - invalidatePart(m_pressedPart); - m_pressedPart = NoPart; - m_pressedPos = 0; - stopTimerIfNeeded(); - - if (parent() && parent()->isFrameView()) - static_cast<FrameView*>(parent())->frame()->eventHandler()->setMousePressed(false); - - return true; -} - -void PlatformScrollbar::startTimerIfNeeded(double delay) -{ - // Don't do anything for the thumb. - if (m_pressedPart == ThumbPart) - return; - - // Handle the track. We halt track scrolling once the thumb is level - // with us. - if ((m_pressedPart == BackTrackPart || m_pressedPart == ForwardTrackPart) && thumbUnderMouse()) { - invalidatePart(m_pressedPart); - m_hoveredPart = ThumbPart; - return; - } - - // We can't scroll if we've hit the beginning or end. - ScrollDirection dir = pressedPartScrollDirection(); - if (dir == ScrollUp || dir == ScrollLeft) { - if (m_currentPos == 0) - return; - } else { - if (m_currentPos == m_totalSize - m_visibleSize) - return; - } - - m_scrollTimer.startOneShot(delay); -} - -void PlatformScrollbar::stopTimerIfNeeded() -{ - if (m_scrollTimer.isActive()) - m_scrollTimer.stop(); -} - -void PlatformScrollbar::autoscrollPressedPart(double delay) -{ - // Don't do anything for the thumb or if nothing was pressed. - if (m_pressedPart == ThumbPart || m_pressedPart == NoPart) - return; - - // Handle the track. - if ((m_pressedPart == BackTrackPart || m_pressedPart == ForwardTrackPart) && thumbUnderMouse()) { - invalidatePart(m_pressedPart); - m_hoveredPart = ThumbPart; - return; - } - - // Handle the arrows and track. - if (scroll(pressedPartScrollDirection(), pressedPartScrollGranularity())) - startTimerIfNeeded(delay); -} - -void PlatformScrollbar::autoscrollTimerFired(Timer<PlatformScrollbar>*) -{ - autoscrollPressedPart(cNormalTimerDelay); -} - -ScrollDirection PlatformScrollbar::pressedPartScrollDirection() -{ - if (m_orientation == HorizontalScrollbar) { - if (m_pressedPart == BackButtonPart || m_pressedPart == BackTrackPart) - return ScrollLeft; - return ScrollRight; - } else { - if (m_pressedPart == BackButtonPart || m_pressedPart == BackTrackPart) - return ScrollUp; - return ScrollDown; - } -} - -ScrollGranularity PlatformScrollbar::pressedPartScrollGranularity() -{ - if (m_pressedPart == BackButtonPart || m_pressedPart == ForwardButtonPart) - return ScrollByLine; - return ScrollByPage; -} - -bool PlatformScrollbar::thumbUnderMouse() -{ - // Construct a rect. - IntRect thumb = thumbRect(); - thumb.move(-x(), -y()); - int begin = (m_orientation == HorizontalScrollbar) ? thumb.x() : thumb.y(); - int end = (m_orientation == HorizontalScrollbar) ? thumb.right() : thumb.bottom(); - return (begin <= m_pressedPos && m_pressedPos < end); -} - -int PlatformScrollbar::horizontalScrollbarHeight(ScrollbarControlSize controlSize) -{ - return cHorizontalWidth; -} - -int PlatformScrollbar::verticalScrollbarWidth(ScrollbarControlSize controlSize) -{ - return cVerticalHeight; -} - -IntRect PlatformScrollbar::windowClipRect() const -{ - IntRect clipRect(0, 0, width(), height()); - - clipRect = convertToContainingWindow(clipRect); - if (m_client) - clipRect.intersect(m_client->windowClipRect()); - - return clipRect; -} - -void PlatformScrollbar::themeChanged() -{ -} - -} - diff --git a/WebCore/platform/win/PopupMenuWin.cpp b/WebCore/platform/win/PopupMenuWin.cpp index 466d881..c44763d 100644 --- a/WebCore/platform/win/PopupMenuWin.cpp +++ b/WebCore/platform/win/PopupMenuWin.cpp @@ -65,8 +65,7 @@ static inline bool isASCIIPrintable(unsigned c) } PopupMenu::PopupMenu(PopupMenuClient* client) - : RefCounted<PopupMenu>(0) - , m_popupClient(client) + : m_popupClient(client) , m_scrollBar(0) , m_popup(0) , m_DC(0) diff --git a/WebCore/platform/win/SharedTimerWin.cpp b/WebCore/platform/win/SharedTimerWin.cpp index 8f00ad8..8ff7734 100644 --- a/WebCore/platform/win/SharedTimerWin.cpp +++ b/WebCore/platform/win/SharedTimerWin.cpp @@ -40,16 +40,6 @@ #include <windows.h> -// These aren't in winuser.h with the MSVS 2003 Platform SDK, -// so use default values in that case. -#ifndef USER_TIMER_MINIMUM -#define USER_TIMER_MINIMUM 0x0000000A -#endif - -#ifndef USER_TIMER_MAXIMUM -#define USER_TIMER_MAXIMUM 0x7FFFFFFF -#endif - #if PLATFORM(WIN) #include "PluginView.h" #endif diff --git a/WebCore/platform/win/ThreadConditionWin.cpp b/WebCore/platform/win/ThreadConditionWin.cpp new file mode 100644 index 0000000..f2bdb78 --- /dev/null +++ b/WebCore/platform/win/ThreadConditionWin.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2007 Apple 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 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. + * + * ============================================================================= + * Note: The implementation of condition variables under the Windows + * plaform was based on that of the excellent BOOST C++ library. It + * has been rewritten to fit in with the WebKit architecture and to + * use its coding conventions. + * ============================================================================= + * + * The Boost license is virtually identical to the Apple variation at the + * top of this file, but is included here for completeness: + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" +#include "Logging.h" +#include "Threading.h" +#include <limits> +#include <errno.h> + +namespace WebCore { + +static const long MaxSemaphoreCount = static_cast<long>(~0UL >> 1); + +ThreadCondition::ThreadCondition() +{ + m_condition.m_timedOut = 0; + m_condition.m_blocked = 0; + m_condition.m_waitingForRemoval = 0; + m_condition.m_gate = ::CreateSemaphore(0, 1, 1, 0); + m_condition.m_queue = ::CreateSemaphore(0, 0, MaxSemaphoreCount, 0); + m_condition.m_mutex = ::CreateMutex(0, 0, 0); + + if (!m_condition.m_gate || !m_condition.m_queue || !m_condition.m_mutex) { + if (m_condition.m_gate) + ::CloseHandle(m_condition.m_gate); + if (m_condition.m_queue) + ::CloseHandle(m_condition.m_queue); + if (m_condition.m_mutex) + ::CloseHandle(m_condition.m_mutex); + } +} + +ThreadCondition::~ThreadCondition() +{ + ::CloseHandle(m_condition.m_gate); + ::CloseHandle(m_condition.m_queue); + ::CloseHandle(m_condition.m_mutex); +} + +void ThreadCondition::wait(Mutex& mutex) +{ + PlatformMutex& cs = mutex.impl(); + + // Enter the wait state. + DWORD res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + ++m_condition.m_blocked; + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + + ::LeaveCriticalSection(&cs.m_internalMutex); + + res = ::WaitForSingleObject(m_condition.m_queue, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + size_t wasWaiting = m_condition.m_waitingForRemoval; + size_t wasTimedOut = m_condition.m_timedOut; + if (wasWaiting != 0) { + if (--m_condition.m_waitingForRemoval == 0) { + if (m_condition.m_blocked != 0) { + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); // open m_gate + ASSERT(res); + wasWaiting = 0; + } + else if (m_condition.m_timedOut != 0) + m_condition.m_timedOut = 0; + } + } else if (++m_condition.m_timedOut == ((std::numeric_limits<unsigned>::max)() / 2)) { + // timeout occured, normalize the m_condition.m_timedOut count + // this may occur if many calls to wait with a timeout are made and + // no call to notify_* is made + res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + m_condition.m_blocked -= m_condition.m_timedOut; + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + m_condition.m_timedOut = 0; + } + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + + if (wasWaiting == 1) { + for (/**/ ; wasTimedOut; --wasTimedOut) { + // better now than spurious later + res = ::WaitForSingleObject(m_condition.m_queue, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + } + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + } + + ::EnterCriticalSection (&cs.m_internalMutex); +} + +void ThreadCondition::signal() +{ + unsigned signals = 0; + + DWORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed + if (m_condition.m_blocked == 0) { + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + return; + } + + ++m_condition.m_waitingForRemoval; + --m_condition.m_blocked; + + signals = 1; + } else { + res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + if (m_condition.m_blocked > m_condition.m_timedOut) { + if (m_condition.m_timedOut != 0) { + m_condition.m_blocked -= m_condition.m_timedOut; + m_condition.m_timedOut = 0; + } + signals = m_condition.m_waitingForRemoval = 1; + --m_condition.m_blocked; + } else { + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + } + } + + res =::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + + if (signals) { + res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0); + ASSERT(res); + } +} + +void ThreadCondition::broadcast() +{ + unsigned signals = 0; + + WORD res = ::WaitForSingleObject(m_condition.m_mutex, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + + if (m_condition.m_waitingForRemoval != 0) { // the m_gate is already closed + if (m_condition.m_blocked == 0) { + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + return; + } + + m_condition.m_waitingForRemoval += (signals = m_condition.m_blocked); + m_condition.m_blocked = 0; + } else { + res = ::WaitForSingleObject(m_condition.m_gate, INFINITE); + ASSERT(res == WAIT_OBJECT_0); + if (m_condition.m_blocked > m_condition.m_timedOut) { + if (m_condition.m_timedOut != 0) { + m_condition.m_blocked -= m_condition.m_timedOut; + m_condition.m_timedOut = 0; + } + signals = m_condition.m_waitingForRemoval = m_condition.m_blocked; + m_condition.m_blocked = 0; + } else { + res = ::ReleaseSemaphore(m_condition.m_gate, 1, 0); + ASSERT(res); + } + } + + res = ::ReleaseMutex(m_condition.m_mutex); + ASSERT(res); + + if (signals) { + res = ::ReleaseSemaphore(m_condition.m_queue, signals, 0); + ASSERT(res); + } +} + +} // namespace WebCore diff --git a/WebCore/platform/win/MainThreadWin.cpp b/WebCore/platform/win/ThreadingWin.cpp index 6d3493c..0da3d17 100644 --- a/WebCore/platform/win/MainThreadWin.cpp +++ b/WebCore/platform/win/ThreadingWin.cpp @@ -27,11 +27,12 @@ */ #include "config.h" -#include "MainThread.h" - #include "Logging.h" #include "Page.h" +#include "Threading.h" +#include <errno.h> #include <windows.h> +#include <wtf/HashMap.h> namespace WebCore { @@ -48,6 +49,88 @@ static UINT threadingFiredMessage = 0; const LPCWSTR kThreadingWindowClassName = L"ThreadingWindowClass"; static bool processingCustomThreadingMessage = false; +static Mutex& threadMapMutex() +{ + static Mutex mutex; + return mutex; +} + +static HashMap<DWORD, HANDLE>& threadMap() +{ + static HashMap<DWORD, HANDLE> map; + return map; +} + +static void storeThreadHandleByIdentifier(DWORD threadID, HANDLE threadHandle) +{ + MutexLocker locker(threadMapMutex()); + threadMap().add(threadID, threadHandle); +} + +static HANDLE threadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + return threadMap().get(id); +} + +static void clearThreadHandleForIdentifier(ThreadIdentifier id) +{ + MutexLocker locker(threadMapMutex()); + ASSERT(threadMap().contains(id)); + threadMap().remove(id); +} + +ThreadIdentifier createThread(ThreadFunction entryPoint, void* data) +{ + DWORD threadIdentifier = 0; + ThreadIdentifier threadID = 0; + HANDLE hEvent = ::CreateEvent(0, FALSE, FALSE, 0); + HANDLE threadHandle = ::CreateThread(0, 0, (LPTHREAD_START_ROUTINE)entryPoint, data, 0, &threadIdentifier); + if (!threadHandle) { + LOG_ERROR("Failed to create thread at entry point %p with data %p", entryPoint, data); + return 0; + } + + threadID = static_cast<ThreadIdentifier>(threadIdentifier); + storeThreadHandleByIdentifier(threadIdentifier, threadHandle); + + LOG(Threading, "Created thread with thread id %u", threadID); + return threadID; +} + +int waitForThreadCompletion(ThreadIdentifier threadID, void** result) +{ + ASSERT(threadID); + + HANDLE threadHandle = threadHandleForIdentifier(threadID); + if (!threadHandle) + LOG_ERROR("ThreadIdentifier %u did not correspond to an active thread when trying to quit", threadID); + + DWORD joinResult = ::WaitForSingleObject(threadHandle, INFINITE); + if (joinResult == WAIT_FAILED) + LOG_ERROR("ThreadIdentifier %u was found to be deadlocked trying to quit", threadID); + + ::CloseHandle(threadHandle); + clearThreadHandleForIdentifier(threadID); + + return joinResult; +} + +void detachThread(ThreadIdentifier threadID) +{ + ASSERT(threadID); + + HANDLE threadHandle = threadHandleForIdentifier(threadID); + if (threadHandle) + ::CloseHandle(threadHandle); + clearThreadHandleForIdentifier(threadID); +} + +ThreadIdentifier currentThread() +{ + return static_cast<ThreadIdentifier>(::GetCurrentThreadId()); +} + static Mutex& functionQueueMutex() { static Mutex staticFunctionQueueMutex; @@ -84,13 +167,11 @@ LRESULT CALLBACK ThreadingWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, return 0; } -void initializeThreadingAndMainThread() +void initializeThreading() { if (threadingWindowHandle) return; - - WTF::initializeThreading(); - + WNDCLASSEX wcex; memset(&wcex, 0, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); diff --git a/WebCore/platform/wx/FileSystemWx.cpp b/WebCore/platform/wx/FileSystemWx.cpp index 7be985b..28310f2 100644 --- a/WebCore/platform/wx/FileSystemWx.cpp +++ b/WebCore/platform/wx/FileSystemWx.cpp @@ -1,9 +1,7 @@ /* * Copyright (C) 2007 Kevin Ollivier - * Copyright (C) 2008 Collabora, Ltd. - * * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -77,40 +75,4 @@ String pathByAppendingComponent(const String& path, const String& component) return String(); } -String homeDirectoryPath() -{ - notImplemented(); - return String(); -} - -String pathGetFileName(const String&) -{ - notImplemented(); - return String(); -} - -CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) -{ - notImplemented(); - handle = invalidPlatformFileHandle; - return CString(); -} - -void closeFile(PlatformFileHandle&) -{ - notImplemented(); -} - -int writeToFile(PlatformFileHandle, const char* data, int length) -{ - notImplemented(); - return 0; -} - -bool unloadModule(PlatformModule) -{ - notImplemented(); - return false; -} - } diff --git a/WebCore/platform/wx/LocalizedStringsWx.cpp b/WebCore/platform/wx/LocalizedStringsWx.cpp index 8ba5fe4..368f938 100644 --- a/WebCore/platform/wx/LocalizedStringsWx.cpp +++ b/WebCore/platform/wx/LocalizedStringsWx.cpp @@ -25,8 +25,6 @@ */ #include "config.h" -#include "LocalizedStrings.h" - #include "PlatformString.h" namespace WebCore { @@ -266,9 +264,4 @@ String unknownFileSizeText() return String("Unknown"); } -String imageTitle(const String& filename, const IntSize& size) -{ - return String(); -} - } // namespace WebCore diff --git a/WebCore/platform/wx/MouseWheelEventWx.cpp b/WebCore/platform/wx/MouseWheelEventWx.cpp index 6064176..0bfbd4a 100644 --- a/WebCore/platform/wx/MouseWheelEventWx.cpp +++ b/WebCore/platform/wx/MouseWheelEventWx.cpp @@ -40,10 +40,6 @@ PlatformWheelEvent::PlatformWheelEvent(const wxMouseEvent& event, const wxPoint& , m_metaKey(event.MetaDown()) // FIXME: We'll have to test other browsers , m_deltaX(0) // wx doesn't support horizontal mouse wheel scrolling , m_deltaY(event.GetWheelRotation() / event.GetWheelDelta()) - , m_isAccepted(false) - , m_isContinuous(false) - , m_continuousDeltaX(0) - , m_continuousDeltaY(0) { } diff --git a/WebCore/platform/wx/ScrollViewWx.cpp b/WebCore/platform/wx/ScrollViewWx.cpp index 8726fc3..b6c5ae4 100644 --- a/WebCore/platform/wx/ScrollViewWx.cpp +++ b/WebCore/platform/wx/ScrollViewWx.cpp @@ -68,6 +68,7 @@ public: win->Connect(wxEVT_SCROLLWIN_PAGEDOWN, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); win->Connect(wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); + win->Connect(wxEVT_SCROLLWIN_TOP, wxScrollWinEventHandler(ScrollViewPrivate::OnScrollWinEvents), NULL, this); } void OnScrollWinEvents(wxScrollWinEvent& e) @@ -83,30 +84,18 @@ public: else pos.y = e.GetPosition(); } - else if (scrollType == wxEVT_SCROLLWIN_LINEDOWN) { + else if ( scrollType == wxEVT_SCROLLWIN_LINEDOWN ) { if (horiz) pos.x += LINE_STEP; else pos.y += LINE_STEP; } - else if (scrollType == wxEVT_SCROLLWIN_LINEUP) { + else if ( scrollType == wxEVT_SCROLLWIN_LINEUP ) { if (horiz) pos.x -= LINE_STEP; else pos.y -= LINE_STEP; } - else if (scrollType == wxEVT_SCROLLWIN_PAGEUP) { - if (horiz) - pos.x -= m_scrollView->visibleWidth() - PAGE_KEEP; - else - pos.y -= m_scrollView->visibleHeight() - PAGE_KEEP; - } - else if (scrollType == wxEVT_SCROLLWIN_PAGEDOWN) { - if (horiz) - pos.x += m_scrollView->visibleWidth() - PAGE_KEEP; - else - pos.y += m_scrollView->visibleHeight() - PAGE_KEEP; - } else return e.Skip(); diff --git a/WebCore/platform/wx/TemporaryLinkStubs.cpp b/WebCore/platform/wx/TemporaryLinkStubs.cpp index b44ed9a..cd328c6 100755 --- a/WebCore/platform/wx/TemporaryLinkStubs.cpp +++ b/WebCore/platform/wx/TemporaryLinkStubs.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2008 Collabora, Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -64,8 +63,7 @@ #include "PlatformMenuDescription.h" #include "PlatformMouseEvent.h" #include "PlatformScrollBar.h" -#include "PluginDatabase.h" -#include "PluginPackage.h" +#include "PluginInfoStore.h" #include "PopupMenu.h" #include "RenderTheme.h" #include "ResourceHandle.h" @@ -119,14 +117,11 @@ namespace WebCore { void WebCore::findWordBoundary(UChar const* str,int len,int position,int* start, int* end) { notImplemented(); *start=position; *end=position; } -PluginSet PluginDatabase::getPluginsInPaths() const { notImplemented(); return PluginSet(); } -Vector<String> PluginDatabase::defaultPluginPaths() { notImplemented(); return Vector<String>(); } -bool PluginDatabase::isPreferredPluginPath(const String&) { notImplemented(); return false; } -int PluginPackage::compare(const PluginPackage&) const { notImplemented(); return 0; } -bool PluginPackage::fetchInfo() { notImplemented(); return false; } -unsigned PluginPackage::hash() const { notImplemented(); return 0; } -bool PluginPackage::equal(const PluginPackage&, const PluginPackage&) { notImplemented(); return false; } -bool PluginPackage::load() { notImplemented(); return false; } +PluginInfo*PluginInfoStore::createPluginInfoForPluginAtIndex(unsigned) { notImplemented(); return 0;} +unsigned PluginInfoStore::pluginCount() const { notImplemented(); return 0; } +bool WebCore::PluginInfoStore::supportsMIMEType(const WebCore::String&) { notImplemented(); return false; } +String PluginInfoStore::pluginNameForMIMEType(const String& mimeType) { notImplemented(); return String(); } +void WebCore::refreshPlugins(bool) { notImplemented(); } void Widget::setIsSelected(bool) { notImplemented(); } @@ -156,13 +151,13 @@ void PlatformScrollbar::updateThumbPosition() { notImplemented(); } void PlatformScrollbar::updateThumbProportion() { notImplemented(); } void PlatformScrollbar::setRect(const IntRect&) { notImplemented(); } -FileChooser::FileChooser(FileChooserClient*, const String& initialFilename) : RefCounted<FileChooser>(0) { notImplemented(); } +FileChooser::FileChooser(FileChooserClient*, const String& initialFilename) { notImplemented(); } //PassRefPtr<FileChooser> FileChooser::create(FileChooserClient*, const String& initialFilename) { notImplemented(); return PassRefPtr<FileChooser>(); } FileChooser::~FileChooser() { notImplemented(); } void FileChooser::openFileChooser(Document*) { notImplemented(); } String FileChooser::basenameForWidth(const Font&, int width) const { notImplemented(); return String(); } -PopupMenu::PopupMenu(PopupMenuClient*) : RefCounted<PopupMenu>(0) { notImplemented(); } +PopupMenu::PopupMenu(PopupMenuClient*) { notImplemented(); } PopupMenu::~PopupMenu() { notImplemented(); } void PopupMenu::show(const IntRect&, FrameView*, int index) { notImplemented(); } @@ -170,7 +165,7 @@ void PopupMenu::hide() { notImplemented(); } void PopupMenu::updateFromElement() { notImplemented(); } bool PopupMenu::itemWritingDirectionIsNatural() { notImplemented(); return false; } -Icon::Icon() : RefCounted<Icon>(0) { notImplemented(); } +Icon::Icon() { notImplemented(); } Icon::~Icon() { notImplemented(); } PassRefPtr<Icon> Icon::newIconForFile(const String& filename) { notImplemented(); return PassRefPtr<Icon>(new Icon()); } void Icon::paint(GraphicsContext*, const IntRect&) { notImplemented(); } diff --git a/WebCore/platform/wx/MainThreadWx.cpp b/WebCore/platform/wx/ThreadingWx.cpp index b49cb62..7cb90ff 100644 --- a/WebCore/platform/wx/MainThreadWx.cpp +++ b/WebCore/platform/wx/ThreadingWx.cpp @@ -28,7 +28,7 @@ #include "config.h" #include "NotImplemented.h" -#include "MainThread.h" +#include "Threading.h" namespace WebCore { diff --git a/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp b/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp deleted file mode 100644 index b86a9bc..0000000 --- a/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2007 Kevin Watters, Kevin Ollivier. 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "GlyphBuffer.h" -#include "GraphicsContext.h" -#include "SimpleFontData.h" - -#include <wx/defs.h> -#include <wx/dcclient.h> -#include <wx/gdicmn.h> -#include <vector> - -namespace WebCore { - -void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) -{ -#if USE(WXGC) - wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); -#else - wxDC* dc = graphicsContext->platformContext(); -#endif - - wxFont wxfont = font->getWxFont(); - if (wxfont.IsOk()) - dc->SetFont(wxfont); - dc->SetTextForeground(color); - - // convert glyphs to wxString - GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); - int offset = point.x(); - wxString text = wxEmptyString; - for (unsigned i = 0; i < numGlyphs; i++) { - text = text.Append((wxChar)glyphs[i]); - offset += glyphBuffer.advanceAt(from + i); - } - - // the y point is actually the bottom point of the text, turn it into the top - float height = font->ascent() - font->descent(); - wxCoord ypoint = (wxCoord) (point.y() - height); - - dc->DrawText(text, (wxCoord)point.x(), ypoint); -} - -} diff --git a/WebCore/platform/wx/wxcode/win/non-kerned-drawing.cpp b/WebCore/platform/wx/wxcode/win/non-kerned-drawing.cpp deleted file mode 100644 index f05923a..0000000 --- a/WebCore/platform/wx/wxcode/win/non-kerned-drawing.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2007 Kevin Watters, Kevin Ollivier. 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 COMPUTER, INC. ``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 COMPUTER, INC. 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 "GlyphBuffer.h" -#include "GraphicsContext.h" -#include "SimpleFontData.h" - -#include <wx/defs.h> -#include <wx/dcclient.h> -#include <wx/gdicmn.h> -#include <vector> - -using namespace std; - -//----------------------------------------------------------------------------- -// constants -//----------------------------------------------------------------------------- - -const double RAD2DEG = 180.0 / M_PI; - -//----------------------------------------------------------------------------- -// Local functions -//----------------------------------------------------------------------------- - -static inline double dmin(double a, double b) { return a < b ? a : b; } -static inline double dmax(double a, double b) { return a > b ? a : b; } - -static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; } -static inline double RadToDeg(double deg) { return (deg * 180.0) / M_PI; } - -#include "wx/msw/private.h" - -// TODO remove this dependency (gdiplus needs the macros) - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#include "gdiplus.h" - - -namespace WebCore { - -void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) -{ -#if USE(WXGC) - wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); -#else - wxDC* dc = graphicsContext->platformContext(); -#endif - - // get the native HDC handle to draw using native APIs - HDC hdc = 0; -#if USE(WXGC) - wxGraphicsContext* gc = dc->GetGraphicsContext(); - Gdiplus::Graphics* g; - if (gc) { - g = (Gdiplus::Graphics*)gc->GetNativeContext(); - hdc = g->GetHDC(); - } -#else - hdc = static_cast<HDC>(dc->GetHDC()); -#endif - - // ExtTextOut wants the offsets as an array of ints, so extract them - // from the glyph buffer - const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); - const GlyphBufferAdvance* advances = glyphBuffer.advances(from); - - float y = point.y() - font->ascent(); - float x = point.x(); - - int* spacing = new int[numGlyphs - from]; - for (unsigned i = 0; i < numGlyphs; ++i) - spacing[i] = advances[i].width(); - - ::SelectObject(hdc, GetHfontOf(font->getWxFont())); - - if (color.Ok()) - ::SetTextColor(hdc, color.GetPixel()); - - // do not draw background behind characters - ::SetBkMode(hdc, TRANSPARENT); - - // draw text with optional character widths array - wxString string = wxString((wxChar*)(&glyphs[from]), numGlyphs); - ::ExtTextOut(hdc, x, y, 0, NULL, string.c_str(), string.length(), spacing); - - ::SetBkMode(hdc, TRANSPARENT); - - #if USE(WXGC) - g->ReleaseHDC(hdc); - #endif - - delete [] spacing; -} - -} |