diff options
Diffstat (limited to 'Source/WebKit/qt/tests')
119 files changed, 13177 insertions, 0 deletions
diff --git a/Source/WebKit/qt/tests/MIMESniffing/MIMESniffing.pro b/Source/WebKit/qt/tests/MIMESniffing/MIMESniffing.pro new file mode 100644 index 0000000..53d80c8 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/MIMESniffing.pro @@ -0,0 +1,19 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +TARGET = MIMESniffing +CONFIG += console + +SOURCES += ../../../../WebCore/platform/network/MIMESniffing.cpp +HEADERS += \ + ../../../../WebCore/platform/network/MIMESniffing.h \ + TestData.h + +INCLUDEPATH += \ + ../../../../WebCore/platform/network \ + ../../../../JavaScriptCore + +debug { + SOURCES += ../../../../JavaScriptCore/wtf/Assertions.cpp +} + +RESOURCES += resources.qrc diff --git a/Source/WebKit/qt/tests/MIMESniffing/TestData.h b/Source/WebKit/qt/tests/MIMESniffing/TestData.h new file mode 100644 index 0000000..c04bd6b --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/TestData.h @@ -0,0 +1,984 @@ +/* + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + + 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 TestData_h +#define TestData_h + +typedef struct _TestData { + const char* file; + const char* advertisedType; + bool isImage; + const char* sniffedType; +} TestData; + +static const TestData testList[] = { + {":/application_atom+xml", "text/plain", false, "text/plain"}, + {":/application_atom+xml", "text/plain", true, "text/plain"}, + {":/application_atom+xml", "unknown/unknown", false, "text/xml"}, + {":/application_atom+xml", "unknown/unknown", true, "text/xml"}, + {":/application_atom+xml", "application/unknown", false, "text/xml"}, + {":/application_atom+xml", "application/unknown", true, "text/xml"}, + {":/application_atom+xml", "*/*", false, "text/xml"}, + {":/application_atom+xml", "*/*", true, "text/xml"}, + {":/application_atom+xml", "text/xml", false, 0}, + {":/application_atom+xml", "text/xml", true, 0}, + {":/application_atom+xml", "application/xml", false, 0}, + {":/application_atom+xml", "application/xml", true, 0}, + {":/application_atom+xml", "text/html", false, "application/atom+xml"}, + {":/application_atom+xml", "text/html", true, 0}, + {":/application_atom+xml", "text/xml", false, 0}, + {":/application_atom+xml", "text/xml", true, 0}, + {":/application_atom+xml", "application/pdf", false, 0}, + {":/application_atom+xml", "application/pdf", true, 0}, + {":/application_atom+xml", "application/postscript", false, 0}, + {":/application_atom+xml", "application/postscript", true, 0}, + {":/application_atom+xml", "application/ogg", false, 0}, + {":/application_atom+xml", "application/ogg", true, 0}, + {":/application_atom+xml", "video/webm", false, 0}, + {":/application_atom+xml", "video/webm", true, 0}, + {":/application_atom+xml", "application/x-rar-compressed", false, 0}, + {":/application_atom+xml", "application/x-rar-compressed", true, 0}, + {":/application_atom+xml", "application/zip", false, 0}, + {":/application_atom+xml", "application/zip", true, 0}, + {":/application_atom+xml", "application/x-gzip", false, 0}, + {":/application_atom+xml", "application/x-gzip", true, 0}, + {":/application_atom+xml", "audio/x-wave", false, 0}, + {":/application_atom+xml", "audio/x-wave", true, 0}, + {":/application_atom+xml", "image/webp", false, 0}, + {":/application_atom+xml", "image/webp", true, 0}, + {":/application_atom+xml", "image/gif", false, 0}, + {":/application_atom+xml", "image/gif", true, 0}, + {":/application_atom+xml", "image/png", false, 0}, + {":/application_atom+xml", "image/png", true, 0}, + {":/application_atom+xml", "image/jpeg", false, 0}, + {":/application_atom+xml", "image/jpeg", true, 0}, + {":/application_atom+xml", "image/bmp", false, 0}, + {":/application_atom+xml", "image/bmp", true, 0}, + {":/application_atom+xml", "image/vnd.microsoft.icon", false, 0}, + {":/application_atom+xml", "image/vnd.microsoft.icon", true, 0}, + {":/application_atom+xml", "application/rdf+xml", false, 0}, + {":/application_atom+xml", "application/rdf+xml", true, 0}, + {":/application_atom+xml", "application/rss+xml", false, 0}, + {":/application_atom+xml", "application/rss+xml", true, 0}, + {":/application_atom+xml", "application/atom+xml", false, 0}, + {":/application_atom+xml", "application/atom+xml", true, 0}, + {":/application_ogg", "text/plain", false, "application/ogg"}, + {":/application_ogg", "text/plain", true, "application/ogg"}, + {":/application_ogg", "unknown/unknown", false, "application/ogg"}, + {":/application_ogg", "unknown/unknown", true, "application/ogg"}, + {":/application_ogg", "application/unknown", false, "application/ogg"}, + {":/application_ogg", "application/unknown", true, "application/ogg"}, + {":/application_ogg", "*/*", false, "application/ogg"}, + {":/application_ogg", "*/*", true, "application/ogg"}, + {":/application_ogg", "text/xml", false, 0}, + {":/application_ogg", "text/xml", true, 0}, + {":/application_ogg", "application/xml", false, 0}, + {":/application_ogg", "application/xml", true, 0}, + {":/application_ogg", "text/html", false, 0}, + {":/application_ogg", "text/html", true, 0}, + {":/application_ogg", "text/xml", false, 0}, + {":/application_ogg", "text/xml", true, 0}, + {":/application_ogg", "application/pdf", false, 0}, + {":/application_ogg", "application/pdf", true, 0}, + {":/application_ogg", "application/postscript", false, 0}, + {":/application_ogg", "application/postscript", true, 0}, + {":/application_ogg", "application/ogg", false, 0}, + {":/application_ogg", "application/ogg", true, 0}, + {":/application_ogg", "video/webm", false, 0}, + {":/application_ogg", "video/webm", true, 0}, + {":/application_ogg", "application/x-rar-compressed", false, 0}, + {":/application_ogg", "application/x-rar-compressed", true, 0}, + {":/application_ogg", "application/zip", false, 0}, + {":/application_ogg", "application/zip", true, 0}, + {":/application_ogg", "application/x-gzip", false, 0}, + {":/application_ogg", "application/x-gzip", true, 0}, + {":/application_ogg", "audio/x-wave", false, 0}, + {":/application_ogg", "audio/x-wave", true, 0}, + {":/application_ogg", "image/webp", false, 0}, + {":/application_ogg", "image/webp", true, 0}, + {":/application_ogg", "image/gif", false, 0}, + {":/application_ogg", "image/gif", true, 0}, + {":/application_ogg", "image/png", false, 0}, + {":/application_ogg", "image/png", true, 0}, + {":/application_ogg", "image/jpeg", false, 0}, + {":/application_ogg", "image/jpeg", true, 0}, + {":/application_ogg", "image/bmp", false, 0}, + {":/application_ogg", "image/bmp", true, 0}, + {":/application_ogg", "image/vnd.microsoft.icon", false, 0}, + {":/application_ogg", "image/vnd.microsoft.icon", true, 0}, + {":/application_ogg", "application/rdf+xml", false, 0}, + {":/application_ogg", "application/rdf+xml", true, 0}, + {":/application_ogg", "application/rss+xml", false, 0}, + {":/application_ogg", "application/rss+xml", true, 0}, + {":/application_ogg", "application/atom+xml", false, 0}, + {":/application_ogg", "application/atom+xml", true, 0}, + {":/application_pdf", "text/plain", false, "application/octet-stream"}, + {":/application_pdf", "text/plain", true, "application/octet-stream"}, + {":/application_pdf", "unknown/unknown", false, "application/pdf"}, + {":/application_pdf", "unknown/unknown", true, "application/pdf"}, + {":/application_pdf", "application/unknown", false, "application/pdf"}, + {":/application_pdf", "application/unknown", true, "application/pdf"}, + {":/application_pdf", "*/*", false, "application/pdf"}, + {":/application_pdf", "*/*", true, "application/pdf"}, + {":/application_pdf", "text/xml", false, 0}, + {":/application_pdf", "text/xml", true, 0}, + {":/application_pdf", "application/xml", false, 0}, + {":/application_pdf", "application/xml", true, 0}, + {":/application_pdf", "text/html", false, 0}, + {":/application_pdf", "text/html", true, 0}, + {":/application_pdf", "text/xml", false, 0}, + {":/application_pdf", "text/xml", true, 0}, + {":/application_pdf", "application/pdf", false, 0}, + {":/application_pdf", "application/pdf", true, 0}, + {":/application_pdf", "application/postscript", false, 0}, + {":/application_pdf", "application/postscript", true, 0}, + {":/application_pdf", "application/ogg", false, 0}, + {":/application_pdf", "application/ogg", true, 0}, + {":/application_pdf", "video/webm", false, 0}, + {":/application_pdf", "video/webm", true, 0}, + {":/application_pdf", "application/x-rar-compressed", false, 0}, + {":/application_pdf", "application/x-rar-compressed", true, 0}, + {":/application_pdf", "application/zip", false, 0}, + {":/application_pdf", "application/zip", true, 0}, + {":/application_pdf", "application/x-gzip", false, 0}, + {":/application_pdf", "application/x-gzip", true, 0}, + {":/application_pdf", "audio/x-wave", false, 0}, + {":/application_pdf", "audio/x-wave", true, 0}, + {":/application_pdf", "image/webp", false, 0}, + {":/application_pdf", "image/webp", true, 0}, + {":/application_pdf", "image/gif", false, 0}, + {":/application_pdf", "image/gif", true, 0}, + {":/application_pdf", "image/png", false, 0}, + {":/application_pdf", "image/png", true, 0}, + {":/application_pdf", "image/jpeg", false, 0}, + {":/application_pdf", "image/jpeg", true, 0}, + {":/application_pdf", "image/bmp", false, 0}, + {":/application_pdf", "image/bmp", true, 0}, + {":/application_pdf", "image/vnd.microsoft.icon", false, 0}, + {":/application_pdf", "image/vnd.microsoft.icon", true, 0}, + {":/application_pdf", "application/rdf+xml", false, 0}, + {":/application_pdf", "application/rdf+xml", true, 0}, + {":/application_pdf", "application/rss+xml", false, 0}, + {":/application_pdf", "application/rss+xml", true, 0}, + {":/application_pdf", "application/atom+xml", false, 0}, + {":/application_pdf", "application/atom+xml", true, 0}, + {":/application_postscript", "text/plain", false, "text/plain"}, + {":/application_postscript", "text/plain", true, "text/plain"}, + {":/application_postscript", "unknown/unknown", false, "application/postscript"}, + {":/application_postscript", "unknown/unknown", true, "application/postscript"}, + {":/application_postscript", "application/unknown", false, "application/postscript"}, + {":/application_postscript", "application/unknown", true, "application/postscript"}, + {":/application_postscript", "*/*", false, "application/postscript"}, + {":/application_postscript", "*/*", true, "application/postscript"}, + {":/application_postscript", "text/xml", false, 0}, + {":/application_postscript", "text/xml", true, 0}, + {":/application_postscript", "application/xml", false, 0}, + {":/application_postscript", "application/xml", true, 0}, + {":/application_postscript", "text/html", false, 0}, + {":/application_postscript", "text/html", true, 0}, + {":/application_postscript", "text/xml", false, 0}, + {":/application_postscript", "text/xml", true, 0}, + {":/application_postscript", "application/pdf", false, 0}, + {":/application_postscript", "application/pdf", true, 0}, + {":/application_postscript", "application/postscript", false, 0}, + {":/application_postscript", "application/postscript", true, 0}, + {":/application_postscript", "application/ogg", false, 0}, + {":/application_postscript", "application/ogg", true, 0}, + {":/application_postscript", "video/webm", false, 0}, + {":/application_postscript", "video/webm", true, 0}, + {":/application_postscript", "application/x-rar-compressed", false, 0}, + {":/application_postscript", "application/x-rar-compressed", true, 0}, + {":/application_postscript", "application/zip", false, 0}, + {":/application_postscript", "application/zip", true, 0}, + {":/application_postscript", "application/x-gzip", false, 0}, + {":/application_postscript", "application/x-gzip", true, 0}, + {":/application_postscript", "audio/x-wave", false, 0}, + {":/application_postscript", "audio/x-wave", true, 0}, + {":/application_postscript", "image/webp", false, 0}, + {":/application_postscript", "image/webp", true, 0}, + {":/application_postscript", "image/gif", false, 0}, + {":/application_postscript", "image/gif", true, 0}, + {":/application_postscript", "image/png", false, 0}, + {":/application_postscript", "image/png", true, 0}, + {":/application_postscript", "image/jpeg", false, 0}, + {":/application_postscript", "image/jpeg", true, 0}, + {":/application_postscript", "image/bmp", false, 0}, + {":/application_postscript", "image/bmp", true, 0}, + {":/application_postscript", "image/vnd.microsoft.icon", false, 0}, + {":/application_postscript", "image/vnd.microsoft.icon", true, 0}, + {":/application_postscript", "application/rdf+xml", false, 0}, + {":/application_postscript", "application/rdf+xml", true, 0}, + {":/application_postscript", "application/rss+xml", false, 0}, + {":/application_postscript", "application/rss+xml", true, 0}, + {":/application_postscript", "application/atom+xml", false, 0}, + {":/application_postscript", "application/atom+xml", true, 0}, + {":/application_rdf+xml", "text/plain", false, "text/plain"}, + {":/application_rdf+xml", "text/plain", true, "text/plain"}, + {":/application_rdf+xml", "unknown/unknown", false, "text/xml"}, + {":/application_rdf+xml", "unknown/unknown", true, "text/xml"}, + {":/application_rdf+xml", "application/unknown", false, "text/xml"}, + {":/application_rdf+xml", "application/unknown", true, "text/xml"}, + {":/application_rdf+xml", "*/*", false, "text/xml"}, + {":/application_rdf+xml", "*/*", true, "text/xml"}, + {":/application_rdf+xml", "text/xml", false, 0}, + {":/application_rdf+xml", "text/xml", true, 0}, + {":/application_rdf+xml", "application/xml", false, 0}, + {":/application_rdf+xml", "application/xml", true, 0}, + {":/application_rdf+xml", "text/html", false, "application/rdf+xml"}, + {":/application_rdf+xml", "text/html", true, 0}, + {":/application_rdf+xml", "text/xml", false, 0}, + {":/application_rdf+xml", "text/xml", true, 0}, + {":/application_rdf+xml", "application/pdf", false, 0}, + {":/application_rdf+xml", "application/pdf", true, 0}, + {":/application_rdf+xml", "application/postscript", false, 0}, + {":/application_rdf+xml", "application/postscript", true, 0}, + {":/application_rdf+xml", "application/ogg", false, 0}, + {":/application_rdf+xml", "application/ogg", true, 0}, + {":/application_rdf+xml", "video/webm", false, 0}, + {":/application_rdf+xml", "video/webm", true, 0}, + {":/application_rdf+xml", "application/x-rar-compressed", false, 0}, + {":/application_rdf+xml", "application/x-rar-compressed", true, 0}, + {":/application_rdf+xml", "application/zip", false, 0}, + {":/application_rdf+xml", "application/zip", true, 0}, + {":/application_rdf+xml", "application/x-gzip", false, 0}, + {":/application_rdf+xml", "application/x-gzip", true, 0}, + {":/application_rdf+xml", "audio/x-wave", false, 0}, + {":/application_rdf+xml", "audio/x-wave", true, 0}, + {":/application_rdf+xml", "image/webp", false, 0}, + {":/application_rdf+xml", "image/webp", true, 0}, + {":/application_rdf+xml", "image/gif", false, 0}, + {":/application_rdf+xml", "image/gif", true, 0}, + {":/application_rdf+xml", "image/png", false, 0}, + {":/application_rdf+xml", "image/png", true, 0}, + {":/application_rdf+xml", "image/jpeg", false, 0}, + {":/application_rdf+xml", "image/jpeg", true, 0}, + {":/application_rdf+xml", "image/bmp", false, 0}, + {":/application_rdf+xml", "image/bmp", true, 0}, + {":/application_rdf+xml", "image/vnd.microsoft.icon", false, 0}, + {":/application_rdf+xml", "image/vnd.microsoft.icon", true, 0}, + {":/application_rdf+xml", "application/rdf+xml", false, 0}, + {":/application_rdf+xml", "application/rdf+xml", true, 0}, + {":/application_rdf+xml", "application/rss+xml", false, 0}, + {":/application_rdf+xml", "application/rss+xml", true, 0}, + {":/application_rdf+xml", "application/atom+xml", false, 0}, + {":/application_rdf+xml", "application/atom+xml", true, 0}, + {":/application_rss+xml", "text/plain", false, "text/plain"}, + {":/application_rss+xml", "text/plain", true, "text/plain"}, + {":/application_rss+xml", "unknown/unknown", false, "text/xml"}, + {":/application_rss+xml", "unknown/unknown", true, "text/xml"}, + {":/application_rss+xml", "application/unknown", false, "text/xml"}, + {":/application_rss+xml", "application/unknown", true, "text/xml"}, + {":/application_rss+xml", "*/*", false, "text/xml"}, + {":/application_rss+xml", "*/*", true, "text/xml"}, + {":/application_rss+xml", "text/xml", false, 0}, + {":/application_rss+xml", "text/xml", true, 0}, + {":/application_rss+xml", "application/xml", false, 0}, + {":/application_rss+xml", "application/xml", true, 0}, + {":/application_rss+xml", "text/html", false, "application/rss+xml"}, + {":/application_rss+xml", "text/html", true, 0}, + {":/application_rss+xml", "text/xml", false, 0}, + {":/application_rss+xml", "text/xml", true, 0}, + {":/application_rss+xml", "application/pdf", false, 0}, + {":/application_rss+xml", "application/pdf", true, 0}, + {":/application_rss+xml", "application/postscript", false, 0}, + {":/application_rss+xml", "application/postscript", true, 0}, + {":/application_rss+xml", "application/ogg", false, 0}, + {":/application_rss+xml", "application/ogg", true, 0}, + {":/application_rss+xml", "video/webm", false, 0}, + {":/application_rss+xml", "video/webm", true, 0}, + {":/application_rss+xml", "application/x-rar-compressed", false, 0}, + {":/application_rss+xml", "application/x-rar-compressed", true, 0}, + {":/application_rss+xml", "application/zip", false, 0}, + {":/application_rss+xml", "application/zip", true, 0}, + {":/application_rss+xml", "application/x-gzip", false, 0}, + {":/application_rss+xml", "application/x-gzip", true, 0}, + {":/application_rss+xml", "audio/x-wave", false, 0}, + {":/application_rss+xml", "audio/x-wave", true, 0}, + {":/application_rss+xml", "image/webp", false, 0}, + {":/application_rss+xml", "image/webp", true, 0}, + {":/application_rss+xml", "image/gif", false, 0}, + {":/application_rss+xml", "image/gif", true, 0}, + {":/application_rss+xml", "image/png", false, 0}, + {":/application_rss+xml", "image/png", true, 0}, + {":/application_rss+xml", "image/jpeg", false, 0}, + {":/application_rss+xml", "image/jpeg", true, 0}, + {":/application_rss+xml", "image/bmp", false, 0}, + {":/application_rss+xml", "image/bmp", true, 0}, + {":/application_rss+xml", "image/vnd.microsoft.icon", false, 0}, + {":/application_rss+xml", "image/vnd.microsoft.icon", true, 0}, + {":/application_rss+xml", "application/rdf+xml", false, 0}, + {":/application_rss+xml", "application/rdf+xml", true, 0}, + {":/application_rss+xml", "application/rss+xml", false, 0}, + {":/application_rss+xml", "application/rss+xml", true, 0}, + {":/application_rss+xml", "application/atom+xml", false, 0}, + {":/application_rss+xml", "application/atom+xml", true, 0}, + {":/application_x-gzip", "text/plain", false, "application/x-gzip"}, + {":/application_x-gzip", "text/plain", true, "application/x-gzip"}, + {":/application_x-gzip", "unknown/unknown", false, "application/x-gzip"}, + {":/application_x-gzip", "unknown/unknown", true, "application/x-gzip"}, + {":/application_x-gzip", "application/unknown", false, "application/x-gzip"}, + {":/application_x-gzip", "application/unknown", true, "application/x-gzip"}, + {":/application_x-gzip", "*/*", false, "application/x-gzip"}, + {":/application_x-gzip", "*/*", true, "application/x-gzip"}, + {":/application_x-gzip", "text/xml", false, 0}, + {":/application_x-gzip", "text/xml", true, 0}, + {":/application_x-gzip", "application/xml", false, 0}, + {":/application_x-gzip", "application/xml", true, 0}, + {":/application_x-gzip", "text/html", false, 0}, + {":/application_x-gzip", "text/html", true, 0}, + {":/application_x-gzip", "text/xml", false, 0}, + {":/application_x-gzip", "text/xml", true, 0}, + {":/application_x-gzip", "application/pdf", false, 0}, + {":/application_x-gzip", "application/pdf", true, 0}, + {":/application_x-gzip", "application/postscript", false, 0}, + {":/application_x-gzip", "application/postscript", true, 0}, + {":/application_x-gzip", "application/ogg", false, 0}, + {":/application_x-gzip", "application/ogg", true, 0}, + {":/application_x-gzip", "video/webm", false, 0}, + {":/application_x-gzip", "video/webm", true, 0}, + {":/application_x-gzip", "application/x-rar-compressed", false, 0}, + {":/application_x-gzip", "application/x-rar-compressed", true, 0}, + {":/application_x-gzip", "application/zip", false, 0}, + {":/application_x-gzip", "application/zip", true, 0}, + {":/application_x-gzip", "application/x-gzip", false, 0}, + {":/application_x-gzip", "application/x-gzip", true, 0}, + {":/application_x-gzip", "audio/x-wave", false, 0}, + {":/application_x-gzip", "audio/x-wave", true, 0}, + {":/application_x-gzip", "image/webp", false, 0}, + {":/application_x-gzip", "image/webp", true, 0}, + {":/application_x-gzip", "image/gif", false, 0}, + {":/application_x-gzip", "image/gif", true, 0}, + {":/application_x-gzip", "image/png", false, 0}, + {":/application_x-gzip", "image/png", true, 0}, + {":/application_x-gzip", "image/jpeg", false, 0}, + {":/application_x-gzip", "image/jpeg", true, 0}, + {":/application_x-gzip", "image/bmp", false, 0}, + {":/application_x-gzip", "image/bmp", true, 0}, + {":/application_x-gzip", "image/vnd.microsoft.icon", false, 0}, + {":/application_x-gzip", "image/vnd.microsoft.icon", true, 0}, + {":/application_x-gzip", "application/rdf+xml", false, 0}, + {":/application_x-gzip", "application/rdf+xml", true, 0}, + {":/application_x-gzip", "application/rss+xml", false, 0}, + {":/application_x-gzip", "application/rss+xml", true, 0}, + {":/application_x-gzip", "application/atom+xml", false, 0}, + {":/application_x-gzip", "application/atom+xml", true, 0}, + {":/application_x-rar-compressed", "text/plain", false, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "text/plain", true, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "unknown/unknown", false, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "unknown/unknown", true, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "application/unknown", false, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "application/unknown", true, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "*/*", false, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "*/*", true, "application/x-rar-compressed"}, + {":/application_x-rar-compressed", "text/xml", false, 0}, + {":/application_x-rar-compressed", "text/xml", true, 0}, + {":/application_x-rar-compressed", "application/xml", false, 0}, + {":/application_x-rar-compressed", "application/xml", true, 0}, + {":/application_x-rar-compressed", "text/html", false, 0}, + {":/application_x-rar-compressed", "text/html", true, 0}, + {":/application_x-rar-compressed", "text/xml", false, 0}, + {":/application_x-rar-compressed", "text/xml", true, 0}, + {":/application_x-rar-compressed", "application/pdf", false, 0}, + {":/application_x-rar-compressed", "application/pdf", true, 0}, + {":/application_x-rar-compressed", "application/postscript", false, 0}, + {":/application_x-rar-compressed", "application/postscript", true, 0}, + {":/application_x-rar-compressed", "application/ogg", false, 0}, + {":/application_x-rar-compressed", "application/ogg", true, 0}, + {":/application_x-rar-compressed", "video/webm", false, 0}, + {":/application_x-rar-compressed", "video/webm", true, 0}, + {":/application_x-rar-compressed", "application/x-rar-compressed", false, 0}, + {":/application_x-rar-compressed", "application/x-rar-compressed", true, 0}, + {":/application_x-rar-compressed", "application/zip", false, 0}, + {":/application_x-rar-compressed", "application/zip", true, 0}, + {":/application_x-rar-compressed", "application/x-gzip", false, 0}, + {":/application_x-rar-compressed", "application/x-gzip", true, 0}, + {":/application_x-rar-compressed", "audio/x-wave", false, 0}, + {":/application_x-rar-compressed", "audio/x-wave", true, 0}, + {":/application_x-rar-compressed", "image/webp", false, 0}, + {":/application_x-rar-compressed", "image/webp", true, 0}, + {":/application_x-rar-compressed", "image/gif", false, 0}, + {":/application_x-rar-compressed", "image/gif", true, 0}, + {":/application_x-rar-compressed", "image/png", false, 0}, + {":/application_x-rar-compressed", "image/png", true, 0}, + {":/application_x-rar-compressed", "image/jpeg", false, 0}, + {":/application_x-rar-compressed", "image/jpeg", true, 0}, + {":/application_x-rar-compressed", "image/bmp", false, 0}, + {":/application_x-rar-compressed", "image/bmp", true, 0}, + {":/application_x-rar-compressed", "image/vnd.microsoft.icon", false, 0}, + {":/application_x-rar-compressed", "image/vnd.microsoft.icon", true, 0}, + {":/application_x-rar-compressed", "application/rdf+xml", false, 0}, + {":/application_x-rar-compressed", "application/rdf+xml", true, 0}, + {":/application_x-rar-compressed", "application/rss+xml", false, 0}, + {":/application_x-rar-compressed", "application/rss+xml", true, 0}, + {":/application_x-rar-compressed", "application/atom+xml", false, 0}, + {":/application_x-rar-compressed", "application/atom+xml", true, 0}, + {":/application_zip", "text/plain", false, "application/zip"}, + {":/application_zip", "text/plain", true, "application/zip"}, + {":/application_zip", "unknown/unknown", false, "application/zip"}, + {":/application_zip", "unknown/unknown", true, "application/zip"}, + {":/application_zip", "application/unknown", false, "application/zip"}, + {":/application_zip", "application/unknown", true, "application/zip"}, + {":/application_zip", "*/*", false, "application/zip"}, + {":/application_zip", "*/*", true, "application/zip"}, + {":/application_zip", "text/xml", false, 0}, + {":/application_zip", "text/xml", true, 0}, + {":/application_zip", "application/xml", false, 0}, + {":/application_zip", "application/xml", true, 0}, + {":/application_zip", "text/html", false, 0}, + {":/application_zip", "text/html", true, 0}, + {":/application_zip", "text/xml", false, 0}, + {":/application_zip", "text/xml", true, 0}, + {":/application_zip", "application/pdf", false, 0}, + {":/application_zip", "application/pdf", true, 0}, + {":/application_zip", "application/postscript", false, 0}, + {":/application_zip", "application/postscript", true, 0}, + {":/application_zip", "application/ogg", false, 0}, + {":/application_zip", "application/ogg", true, 0}, + {":/application_zip", "video/webm", false, 0}, + {":/application_zip", "video/webm", true, 0}, + {":/application_zip", "application/x-rar-compressed", false, 0}, + {":/application_zip", "application/x-rar-compressed", true, 0}, + {":/application_zip", "application/zip", false, 0}, + {":/application_zip", "application/zip", true, 0}, + {":/application_zip", "application/x-gzip", false, 0}, + {":/application_zip", "application/x-gzip", true, 0}, + {":/application_zip", "audio/x-wave", false, 0}, + {":/application_zip", "audio/x-wave", true, 0}, + {":/application_zip", "image/webp", false, 0}, + {":/application_zip", "image/webp", true, 0}, + {":/application_zip", "image/gif", false, 0}, + {":/application_zip", "image/gif", true, 0}, + {":/application_zip", "image/png", false, 0}, + {":/application_zip", "image/png", true, 0}, + {":/application_zip", "image/jpeg", false, 0}, + {":/application_zip", "image/jpeg", true, 0}, + {":/application_zip", "image/bmp", false, 0}, + {":/application_zip", "image/bmp", true, 0}, + {":/application_zip", "image/vnd.microsoft.icon", false, 0}, + {":/application_zip", "image/vnd.microsoft.icon", true, 0}, + {":/application_zip", "application/rdf+xml", false, 0}, + {":/application_zip", "application/rdf+xml", true, 0}, + {":/application_zip", "application/rss+xml", false, 0}, + {":/application_zip", "application/rss+xml", true, 0}, + {":/application_zip", "application/atom+xml", false, 0}, + {":/application_zip", "application/atom+xml", true, 0}, + {":/audio_x-wave", "text/plain", false, "audio/x-wave"}, + {":/audio_x-wave", "text/plain", true, "audio/x-wave"}, + {":/audio_x-wave", "unknown/unknown", false, "audio/x-wave"}, + {":/audio_x-wave", "unknown/unknown", true, "audio/x-wave"}, + {":/audio_x-wave", "application/unknown", false, "audio/x-wave"}, + {":/audio_x-wave", "application/unknown", true, "audio/x-wave"}, + {":/audio_x-wave", "*/*", false, "audio/x-wave"}, + {":/audio_x-wave", "*/*", true, "audio/x-wave"}, + {":/audio_x-wave", "text/xml", false, 0}, + {":/audio_x-wave", "text/xml", true, 0}, + {":/audio_x-wave", "application/xml", false, 0}, + {":/audio_x-wave", "application/xml", true, 0}, + {":/audio_x-wave", "text/html", false, 0}, + {":/audio_x-wave", "text/html", true, 0}, + {":/audio_x-wave", "text/xml", false, 0}, + {":/audio_x-wave", "text/xml", true, 0}, + {":/audio_x-wave", "application/pdf", false, 0}, + {":/audio_x-wave", "application/pdf", true, 0}, + {":/audio_x-wave", "application/postscript", false, 0}, + {":/audio_x-wave", "application/postscript", true, 0}, + {":/audio_x-wave", "application/ogg", false, 0}, + {":/audio_x-wave", "application/ogg", true, 0}, + {":/audio_x-wave", "video/webm", false, 0}, + {":/audio_x-wave", "video/webm", true, 0}, + {":/audio_x-wave", "application/x-rar-compressed", false, 0}, + {":/audio_x-wave", "application/x-rar-compressed", true, 0}, + {":/audio_x-wave", "application/zip", false, 0}, + {":/audio_x-wave", "application/zip", true, 0}, + {":/audio_x-wave", "application/x-gzip", false, 0}, + {":/audio_x-wave", "application/x-gzip", true, 0}, + {":/audio_x-wave", "audio/x-wave", false, 0}, + {":/audio_x-wave", "audio/x-wave", true, 0}, + {":/audio_x-wave", "image/webp", false, 0}, + {":/audio_x-wave", "image/webp", true, 0}, + {":/audio_x-wave", "image/gif", false, 0}, + {":/audio_x-wave", "image/gif", true, 0}, + {":/audio_x-wave", "image/png", false, 0}, + {":/audio_x-wave", "image/png", true, 0}, + {":/audio_x-wave", "image/jpeg", false, 0}, + {":/audio_x-wave", "image/jpeg", true, 0}, + {":/audio_x-wave", "image/bmp", false, 0}, + {":/audio_x-wave", "image/bmp", true, 0}, + {":/audio_x-wave", "image/vnd.microsoft.icon", false, 0}, + {":/audio_x-wave", "image/vnd.microsoft.icon", true, 0}, + {":/audio_x-wave", "application/rdf+xml", false, 0}, + {":/audio_x-wave", "application/rdf+xml", true, 0}, + {":/audio_x-wave", "application/rss+xml", false, 0}, + {":/audio_x-wave", "application/rss+xml", true, 0}, + {":/audio_x-wave", "application/atom+xml", false, 0}, + {":/audio_x-wave", "application/atom+xml", true, 0}, + {":/image_bmp", "text/plain", false, "image/bmp"}, + {":/image_bmp", "text/plain", true, "image/bmp"}, + {":/image_bmp", "unknown/unknown", false, "image/bmp"}, + {":/image_bmp", "unknown/unknown", true, "image/bmp"}, + {":/image_bmp", "application/unknown", false, "image/bmp"}, + {":/image_bmp", "application/unknown", true, "image/bmp"}, + {":/image_bmp", "*/*", false, "image/bmp"}, + {":/image_bmp", "*/*", true, "image/bmp"}, + {":/image_bmp", "text/xml", false, 0}, + {":/image_bmp", "text/xml", true, 0}, + {":/image_bmp", "application/xml", false, 0}, + {":/image_bmp", "application/xml", true, 0}, + {":/image_bmp", "text/html", false, 0}, + {":/image_bmp", "text/html", true, "image/bmp"}, + {":/image_bmp", "text/xml", false, 0}, + {":/image_bmp", "text/xml", true, 0}, + {":/image_bmp", "application/pdf", false, 0}, + {":/image_bmp", "application/pdf", true, "image/bmp"}, + {":/image_bmp", "application/postscript", false, 0}, + {":/image_bmp", "application/postscript", true, "image/bmp"}, + {":/image_bmp", "application/ogg", false, 0}, + {":/image_bmp", "application/ogg", true, "image/bmp"}, + {":/image_bmp", "video/webm", false, 0}, + {":/image_bmp", "video/webm", true, "image/bmp"}, + {":/image_bmp", "application/x-rar-compressed", false, 0}, + {":/image_bmp", "application/x-rar-compressed", true, "image/bmp"}, + {":/image_bmp", "application/zip", false, 0}, + {":/image_bmp", "application/zip", true, "image/bmp"}, + {":/image_bmp", "application/x-gzip", false, 0}, + {":/image_bmp", "application/x-gzip", true, "image/bmp"}, + {":/image_bmp", "audio/x-wave", false, 0}, + {":/image_bmp", "audio/x-wave", true, "image/bmp"}, + {":/image_bmp", "image/webp", false, 0}, + {":/image_bmp", "image/webp", true, "image/bmp"}, + {":/image_bmp", "image/gif", false, 0}, + {":/image_bmp", "image/gif", true, "image/bmp"}, + {":/image_bmp", "image/png", false, 0}, + {":/image_bmp", "image/png", true, "image/bmp"}, + {":/image_bmp", "image/jpeg", false, 0}, + {":/image_bmp", "image/jpeg", true, "image/bmp"}, + {":/image_bmp", "image/bmp", false, 0}, + {":/image_bmp", "image/bmp", true, "image/bmp"}, + {":/image_bmp", "image/vnd.microsoft.icon", false, 0}, + {":/image_bmp", "image/vnd.microsoft.icon", true, "image/bmp"}, + {":/image_bmp", "application/rdf+xml", false, 0}, + {":/image_bmp", "application/rdf+xml", true, 0}, + {":/image_bmp", "application/rss+xml", false, 0}, + {":/image_bmp", "application/rss+xml", true, 0}, + {":/image_bmp", "application/atom+xml", false, 0}, + {":/image_bmp", "application/atom+xml", true, 0}, + {":/image_gif", "text/plain", false, "image/gif"}, + {":/image_gif", "text/plain", true, "image/gif"}, + {":/image_gif", "unknown/unknown", false, "image/gif"}, + {":/image_gif", "unknown/unknown", true, "image/gif"}, + {":/image_gif", "application/unknown", false, "image/gif"}, + {":/image_gif", "application/unknown", true, "image/gif"}, + {":/image_gif", "*/*", false, "image/gif"}, + {":/image_gif", "*/*", true, "image/gif"}, + {":/image_gif", "text/xml", false, 0}, + {":/image_gif", "text/xml", true, 0}, + {":/image_gif", "application/xml", false, 0}, + {":/image_gif", "application/xml", true, 0}, + {":/image_gif", "text/html", false, 0}, + {":/image_gif", "text/html", true, "image/gif"}, + {":/image_gif", "text/xml", false, 0}, + {":/image_gif", "text/xml", true, 0}, + {":/image_gif", "application/pdf", false, 0}, + {":/image_gif", "application/pdf", true, "image/gif"}, + {":/image_gif", "application/postscript", false, 0}, + {":/image_gif", "application/postscript", true, "image/gif"}, + {":/image_gif", "application/ogg", false, 0}, + {":/image_gif", "application/ogg", true, "image/gif"}, + {":/image_gif", "video/webm", false, 0}, + {":/image_gif", "video/webm", true, "image/gif"}, + {":/image_gif", "application/x-rar-compressed", false, 0}, + {":/image_gif", "application/x-rar-compressed", true, "image/gif"}, + {":/image_gif", "application/zip", false, 0}, + {":/image_gif", "application/zip", true, "image/gif"}, + {":/image_gif", "application/x-gzip", false, 0}, + {":/image_gif", "application/x-gzip", true, "image/gif"}, + {":/image_gif", "audio/x-wave", false, 0}, + {":/image_gif", "audio/x-wave", true, "image/gif"}, + {":/image_gif", "image/webp", false, 0}, + {":/image_gif", "image/webp", true, "image/gif"}, + {":/image_gif", "image/gif", false, 0}, + {":/image_gif", "image/gif", true, "image/gif"}, + {":/image_gif", "image/png", false, 0}, + {":/image_gif", "image/png", true, "image/gif"}, + {":/image_gif", "image/jpeg", false, 0}, + {":/image_gif", "image/jpeg", true, "image/gif"}, + {":/image_gif", "image/bmp", false, 0}, + {":/image_gif", "image/bmp", true, "image/gif"}, + {":/image_gif", "image/vnd.microsoft.icon", false, 0}, + {":/image_gif", "image/vnd.microsoft.icon", true, "image/gif"}, + {":/image_gif", "application/rdf+xml", false, 0}, + {":/image_gif", "application/rdf+xml", true, 0}, + {":/image_gif", "application/rss+xml", false, 0}, + {":/image_gif", "application/rss+xml", true, 0}, + {":/image_gif", "application/atom+xml", false, 0}, + {":/image_gif", "application/atom+xml", true, 0}, + {":/image_jpeg", "text/plain", false, "image/jpeg"}, + {":/image_jpeg", "text/plain", true, "image/jpeg"}, + {":/image_jpeg", "unknown/unknown", false, "image/jpeg"}, + {":/image_jpeg", "unknown/unknown", true, "image/jpeg"}, + {":/image_jpeg", "application/unknown", false, "image/jpeg"}, + {":/image_jpeg", "application/unknown", true, "image/jpeg"}, + {":/image_jpeg", "*/*", false, "image/jpeg"}, + {":/image_jpeg", "*/*", true, "image/jpeg"}, + {":/image_jpeg", "text/xml", false, 0}, + {":/image_jpeg", "text/xml", true, 0}, + {":/image_jpeg", "application/xml", false, 0}, + {":/image_jpeg", "application/xml", true, 0}, + {":/image_jpeg", "text/html", false, 0}, + {":/image_jpeg", "text/html", true, "image/jpeg"}, + {":/image_jpeg", "text/xml", false, 0}, + {":/image_jpeg", "text/xml", true, 0}, + {":/image_jpeg", "application/pdf", false, 0}, + {":/image_jpeg", "application/pdf", true, "image/jpeg"}, + {":/image_jpeg", "application/postscript", false, 0}, + {":/image_jpeg", "application/postscript", true, "image/jpeg"}, + {":/image_jpeg", "application/ogg", false, 0}, + {":/image_jpeg", "application/ogg", true, "image/jpeg"}, + {":/image_jpeg", "video/webm", false, 0}, + {":/image_jpeg", "video/webm", true, "image/jpeg"}, + {":/image_jpeg", "application/x-rar-compressed", false, 0}, + {":/image_jpeg", "application/x-rar-compressed", true, "image/jpeg"}, + {":/image_jpeg", "application/zip", false, 0}, + {":/image_jpeg", "application/zip", true, "image/jpeg"}, + {":/image_jpeg", "application/x-gzip", false, 0}, + {":/image_jpeg", "application/x-gzip", true, "image/jpeg"}, + {":/image_jpeg", "audio/x-wave", false, 0}, + {":/image_jpeg", "audio/x-wave", true, "image/jpeg"}, + {":/image_jpeg", "image/webp", false, 0}, + {":/image_jpeg", "image/webp", true, "image/jpeg"}, + {":/image_jpeg", "image/gif", false, 0}, + {":/image_jpeg", "image/gif", true, "image/jpeg"}, + {":/image_jpeg", "image/png", false, 0}, + {":/image_jpeg", "image/png", true, "image/jpeg"}, + {":/image_jpeg", "image/jpeg", false, 0}, + {":/image_jpeg", "image/jpeg", true, "image/jpeg"}, + {":/image_jpeg", "image/bmp", false, 0}, + {":/image_jpeg", "image/bmp", true, "image/jpeg"}, + {":/image_jpeg", "image/vnd.microsoft.icon", false, 0}, + {":/image_jpeg", "image/vnd.microsoft.icon", true, "image/jpeg"}, + {":/image_jpeg", "application/rdf+xml", false, 0}, + {":/image_jpeg", "application/rdf+xml", true, 0}, + {":/image_jpeg", "application/rss+xml", false, 0}, + {":/image_jpeg", "application/rss+xml", true, 0}, + {":/image_jpeg", "application/atom+xml", false, 0}, + {":/image_jpeg", "application/atom+xml", true, 0}, + {":/image_png", "text/plain", false, "image/png"}, + {":/image_png", "text/plain", true, "image/png"}, + {":/image_png", "unknown/unknown", false, "image/png"}, + {":/image_png", "unknown/unknown", true, "image/png"}, + {":/image_png", "application/unknown", false, "image/png"}, + {":/image_png", "application/unknown", true, "image/png"}, + {":/image_png", "*/*", false, "image/png"}, + {":/image_png", "*/*", true, "image/png"}, + {":/image_png", "text/xml", false, 0}, + {":/image_png", "text/xml", true, 0}, + {":/image_png", "application/xml", false, 0}, + {":/image_png", "application/xml", true, 0}, + {":/image_png", "text/html", false, 0}, + {":/image_png", "text/html", true, "image/png"}, + {":/image_png", "text/xml", false, 0}, + {":/image_png", "text/xml", true, 0}, + {":/image_png", "application/pdf", false, 0}, + {":/image_png", "application/pdf", true, "image/png"}, + {":/image_png", "application/postscript", false, 0}, + {":/image_png", "application/postscript", true, "image/png"}, + {":/image_png", "application/ogg", false, 0}, + {":/image_png", "application/ogg", true, "image/png"}, + {":/image_png", "video/webm", false, 0}, + {":/image_png", "video/webm", true, "image/png"}, + {":/image_png", "application/x-rar-compressed", false, 0}, + {":/image_png", "application/x-rar-compressed", true, "image/png"}, + {":/image_png", "application/zip", false, 0}, + {":/image_png", "application/zip", true, "image/png"}, + {":/image_png", "application/x-gzip", false, 0}, + {":/image_png", "application/x-gzip", true, "image/png"}, + {":/image_png", "audio/x-wave", false, 0}, + {":/image_png", "audio/x-wave", true, "image/png"}, + {":/image_png", "image/webp", false, 0}, + {":/image_png", "image/webp", true, "image/png"}, + {":/image_png", "image/gif", false, 0}, + {":/image_png", "image/gif", true, "image/png"}, + {":/image_png", "image/png", false, 0}, + {":/image_png", "image/png", true, "image/png"}, + {":/image_png", "image/jpeg", false, 0}, + {":/image_png", "image/jpeg", true, "image/png"}, + {":/image_png", "image/bmp", false, 0}, + {":/image_png", "image/bmp", true, "image/png"}, + {":/image_png", "image/vnd.microsoft.icon", false, 0}, + {":/image_png", "image/vnd.microsoft.icon", true, "image/png"}, + {":/image_png", "application/rdf+xml", false, 0}, + {":/image_png", "application/rdf+xml", true, 0}, + {":/image_png", "application/rss+xml", false, 0}, + {":/image_png", "application/rss+xml", true, 0}, + {":/image_png", "application/atom+xml", false, 0}, + {":/image_png", "application/atom+xml", true, 0}, + {":/image_vnd.microsoft.icon", "text/plain", false, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "text/plain", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "unknown/unknown", false, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "unknown/unknown", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/unknown", false, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/unknown", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "*/*", false, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "*/*", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "text/xml", false, 0}, + {":/image_vnd.microsoft.icon", "text/xml", true, 0}, + {":/image_vnd.microsoft.icon", "application/xml", false, 0}, + {":/image_vnd.microsoft.icon", "application/xml", true, 0}, + {":/image_vnd.microsoft.icon", "text/html", false, 0}, + {":/image_vnd.microsoft.icon", "text/html", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "text/xml", false, 0}, + {":/image_vnd.microsoft.icon", "text/xml", true, 0}, + {":/image_vnd.microsoft.icon", "application/pdf", false, 0}, + {":/image_vnd.microsoft.icon", "application/pdf", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/postscript", false, 0}, + {":/image_vnd.microsoft.icon", "application/postscript", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/ogg", false, 0}, + {":/image_vnd.microsoft.icon", "application/ogg", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "video/webm", false, 0}, + {":/image_vnd.microsoft.icon", "video/webm", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/x-rar-compressed", false, 0}, + {":/image_vnd.microsoft.icon", "application/x-rar-compressed", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/zip", false, 0}, + {":/image_vnd.microsoft.icon", "application/zip", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/x-gzip", false, 0}, + {":/image_vnd.microsoft.icon", "application/x-gzip", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "audio/x-wave", false, 0}, + {":/image_vnd.microsoft.icon", "audio/x-wave", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "image/webp", false, 0}, + {":/image_vnd.microsoft.icon", "image/webp", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "image/gif", false, 0}, + {":/image_vnd.microsoft.icon", "image/gif", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "image/png", false, 0}, + {":/image_vnd.microsoft.icon", "image/png", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "image/jpeg", false, 0}, + {":/image_vnd.microsoft.icon", "image/jpeg", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "image/bmp", false, 0}, + {":/image_vnd.microsoft.icon", "image/bmp", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "image/vnd.microsoft.icon", false, 0}, + {":/image_vnd.microsoft.icon", "image/vnd.microsoft.icon", true, "image/vnd.microsoft.icon"}, + {":/image_vnd.microsoft.icon", "application/rdf+xml", false, 0}, + {":/image_vnd.microsoft.icon", "application/rdf+xml", true, 0}, + {":/image_vnd.microsoft.icon", "application/rss+xml", false, 0}, + {":/image_vnd.microsoft.icon", "application/rss+xml", true, 0}, + {":/image_vnd.microsoft.icon", "application/atom+xml", false, 0}, + {":/image_vnd.microsoft.icon", "application/atom+xml", true, 0}, + {":/image_webp", "text/plain", false, "image/webp"}, + {":/image_webp", "text/plain", true, "image/webp"}, + {":/image_webp", "unknown/unknown", false, "image/webp"}, + {":/image_webp", "unknown/unknown", true, "image/webp"}, + {":/image_webp", "application/unknown", false, "image/webp"}, + {":/image_webp", "application/unknown", true, "image/webp"}, + {":/image_webp", "*/*", false, "image/webp"}, + {":/image_webp", "*/*", true, "image/webp"}, + {":/image_webp", "text/xml", false, 0}, + {":/image_webp", "text/xml", true, 0}, + {":/image_webp", "application/xml", false, 0}, + {":/image_webp", "application/xml", true, 0}, + {":/image_webp", "text/html", false, 0}, + {":/image_webp", "text/html", true, "image/webp"}, + {":/image_webp", "text/xml", false, 0}, + {":/image_webp", "text/xml", true, 0}, + {":/image_webp", "application/pdf", false, 0}, + {":/image_webp", "application/pdf", true, "image/webp"}, + {":/image_webp", "application/postscript", false, 0}, + {":/image_webp", "application/postscript", true, "image/webp"}, + {":/image_webp", "application/ogg", false, 0}, + {":/image_webp", "application/ogg", true, "image/webp"}, + {":/image_webp", "video/webm", false, 0}, + {":/image_webp", "video/webm", true, "image/webp"}, + {":/image_webp", "application/x-rar-compressed", false, 0}, + {":/image_webp", "application/x-rar-compressed", true, "image/webp"}, + {":/image_webp", "application/zip", false, 0}, + {":/image_webp", "application/zip", true, "image/webp"}, + {":/image_webp", "application/x-gzip", false, 0}, + {":/image_webp", "application/x-gzip", true, "image/webp"}, + {":/image_webp", "audio/x-wave", false, 0}, + {":/image_webp", "audio/x-wave", true, "image/webp"}, + {":/image_webp", "image/webp", false, 0}, + {":/image_webp", "image/webp", true, "image/webp"}, + {":/image_webp", "image/gif", false, 0}, + {":/image_webp", "image/gif", true, "image/webp"}, + {":/image_webp", "image/png", false, 0}, + {":/image_webp", "image/png", true, "image/webp"}, + {":/image_webp", "image/jpeg", false, 0}, + {":/image_webp", "image/jpeg", true, "image/webp"}, + {":/image_webp", "image/bmp", false, 0}, + {":/image_webp", "image/bmp", true, "image/webp"}, + {":/image_webp", "image/vnd.microsoft.icon", false, 0}, + {":/image_webp", "image/vnd.microsoft.icon", true, "image/webp"}, + {":/image_webp", "application/rdf+xml", false, 0}, + {":/image_webp", "application/rdf+xml", true, 0}, + {":/image_webp", "application/rss+xml", false, 0}, + {":/image_webp", "application/rss+xml", true, 0}, + {":/image_webp", "application/atom+xml", false, 0}, + {":/image_webp", "application/atom+xml", true, 0}, + {":/text_html", "text/plain", false, "text/plain"}, + {":/text_html", "text/plain", true, "text/plain"}, + {":/text_html", "unknown/unknown", false, "text/html"}, + {":/text_html", "unknown/unknown", true, "text/html"}, + {":/text_html", "application/unknown", false, "text/html"}, + {":/text_html", "application/unknown", true, "text/html"}, + {":/text_html", "*/*", false, "text/html"}, + {":/text_html", "*/*", true, "text/html"}, + {":/text_html", "text/xml", false, 0}, + {":/text_html", "text/xml", true, 0}, + {":/text_html", "application/xml", false, 0}, + {":/text_html", "application/xml", true, 0}, + {":/text_html", "text/html", false, 0}, + {":/text_html", "text/html", true, 0}, + {":/text_html", "text/xml", false, 0}, + {":/text_html", "text/xml", true, 0}, + {":/text_html", "application/pdf", false, 0}, + {":/text_html", "application/pdf", true, 0}, + {":/text_html", "application/postscript", false, 0}, + {":/text_html", "application/postscript", true, 0}, + {":/text_html", "application/ogg", false, 0}, + {":/text_html", "application/ogg", true, 0}, + {":/text_html", "video/webm", false, 0}, + {":/text_html", "video/webm", true, 0}, + {":/text_html", "application/x-rar-compressed", false, 0}, + {":/text_html", "application/x-rar-compressed", true, 0}, + {":/text_html", "application/zip", false, 0}, + {":/text_html", "application/zip", true, 0}, + {":/text_html", "application/x-gzip", false, 0}, + {":/text_html", "application/x-gzip", true, 0}, + {":/text_html", "audio/x-wave", false, 0}, + {":/text_html", "audio/x-wave", true, 0}, + {":/text_html", "image/webp", false, 0}, + {":/text_html", "image/webp", true, 0}, + {":/text_html", "image/gif", false, 0}, + {":/text_html", "image/gif", true, 0}, + {":/text_html", "image/png", false, 0}, + {":/text_html", "image/png", true, 0}, + {":/text_html", "image/jpeg", false, 0}, + {":/text_html", "image/jpeg", true, 0}, + {":/text_html", "image/bmp", false, 0}, + {":/text_html", "image/bmp", true, 0}, + {":/text_html", "image/vnd.microsoft.icon", false, 0}, + {":/text_html", "image/vnd.microsoft.icon", true, 0}, + {":/text_html", "application/rdf+xml", false, 0}, + {":/text_html", "application/rdf+xml", true, 0}, + {":/text_html", "application/rss+xml", false, 0}, + {":/text_html", "application/rss+xml", true, 0}, + {":/text_html", "application/atom+xml", false, 0}, + {":/text_html", "application/atom+xml", true, 0}, + {":/text_xml", "text/plain", false, "text/plain"}, + {":/text_xml", "text/plain", true, "text/plain"}, + {":/text_xml", "unknown/unknown", false, "text/xml"}, + {":/text_xml", "unknown/unknown", true, "text/xml"}, + {":/text_xml", "application/unknown", false, "text/xml"}, + {":/text_xml", "application/unknown", true, "text/xml"}, + {":/text_xml", "*/*", false, "text/xml"}, + {":/text_xml", "*/*", true, "text/xml"}, + {":/text_xml", "text/xml", false, 0}, + {":/text_xml", "text/xml", true, 0}, + {":/text_xml", "application/xml", false, 0}, + {":/text_xml", "application/xml", true, 0}, + {":/text_xml", "text/html", false, 0}, + {":/text_xml", "text/html", true, 0}, + {":/text_xml", "text/xml", false, 0}, + {":/text_xml", "text/xml", true, 0}, + {":/text_xml", "application/pdf", false, 0}, + {":/text_xml", "application/pdf", true, 0}, + {":/text_xml", "application/postscript", false, 0}, + {":/text_xml", "application/postscript", true, 0}, + {":/text_xml", "application/ogg", false, 0}, + {":/text_xml", "application/ogg", true, 0}, + {":/text_xml", "video/webm", false, 0}, + {":/text_xml", "video/webm", true, 0}, + {":/text_xml", "application/x-rar-compressed", false, 0}, + {":/text_xml", "application/x-rar-compressed", true, 0}, + {":/text_xml", "application/zip", false, 0}, + {":/text_xml", "application/zip", true, 0}, + {":/text_xml", "application/x-gzip", false, 0}, + {":/text_xml", "application/x-gzip", true, 0}, + {":/text_xml", "audio/x-wave", false, 0}, + {":/text_xml", "audio/x-wave", true, 0}, + {":/text_xml", "image/webp", false, 0}, + {":/text_xml", "image/webp", true, 0}, + {":/text_xml", "image/gif", false, 0}, + {":/text_xml", "image/gif", true, 0}, + {":/text_xml", "image/png", false, 0}, + {":/text_xml", "image/png", true, 0}, + {":/text_xml", "image/jpeg", false, 0}, + {":/text_xml", "image/jpeg", true, 0}, + {":/text_xml", "image/bmp", false, 0}, + {":/text_xml", "image/bmp", true, 0}, + {":/text_xml", "image/vnd.microsoft.icon", false, 0}, + {":/text_xml", "image/vnd.microsoft.icon", true, 0}, + {":/text_xml", "application/rdf+xml", false, 0}, + {":/text_xml", "application/rdf+xml", true, 0}, + {":/text_xml", "application/rss+xml", false, 0}, + {":/text_xml", "application/rss+xml", true, 0}, + {":/text_xml", "application/atom+xml", false, 0}, + {":/text_xml", "application/atom+xml", true, 0}, + {":/video_webm", "text/plain", false, "video/webm"}, + {":/video_webm", "text/plain", true, "video/webm"}, + {":/video_webm", "unknown/unknown", false, "video/webm"}, + {":/video_webm", "unknown/unknown", true, "video/webm"}, + {":/video_webm", "application/unknown", false, "video/webm"}, + {":/video_webm", "application/unknown", true, "video/webm"}, + {":/video_webm", "*/*", false, "video/webm"}, + {":/video_webm", "*/*", true, "video/webm"}, + {":/video_webm", "text/xml", false, 0}, + {":/video_webm", "text/xml", true, 0}, + {":/video_webm", "application/xml", false, 0}, + {":/video_webm", "application/xml", true, 0}, + {":/video_webm", "text/html", false, 0}, + {":/video_webm", "text/html", true, 0}, + {":/video_webm", "text/xml", false, 0}, + {":/video_webm", "text/xml", true, 0}, + {":/video_webm", "application/pdf", false, 0}, + {":/video_webm", "application/pdf", true, 0}, + {":/video_webm", "application/postscript", false, 0}, + {":/video_webm", "application/postscript", true, 0}, + {":/video_webm", "application/ogg", false, 0}, + {":/video_webm", "application/ogg", true, 0}, + {":/video_webm", "video/webm", false, 0}, + {":/video_webm", "video/webm", true, 0}, + {":/video_webm", "application/x-rar-compressed", false, 0}, + {":/video_webm", "application/x-rar-compressed", true, 0}, + {":/video_webm", "application/zip", false, 0}, + {":/video_webm", "application/zip", true, 0}, + {":/video_webm", "application/x-gzip", false, 0}, + {":/video_webm", "application/x-gzip", true, 0}, + {":/video_webm", "audio/x-wave", false, 0}, + {":/video_webm", "audio/x-wave", true, 0}, + {":/video_webm", "image/webp", false, 0}, + {":/video_webm", "image/webp", true, 0}, + {":/video_webm", "image/gif", false, 0}, + {":/video_webm", "image/gif", true, 0}, + {":/video_webm", "image/png", false, 0}, + {":/video_webm", "image/png", true, 0}, + {":/video_webm", "image/jpeg", false, 0}, + {":/video_webm", "image/jpeg", true, 0}, + {":/video_webm", "image/bmp", false, 0}, + {":/video_webm", "image/bmp", true, 0}, + {":/video_webm", "image/vnd.microsoft.icon", false, 0}, + {":/video_webm", "image/vnd.microsoft.icon", true, 0}, + {":/video_webm", "application/rdf+xml", false, 0}, + {":/video_webm", "application/rdf+xml", true, 0}, + {":/video_webm", "application/rss+xml", false, 0}, + {":/video_webm", "application/rss+xml", true, 0}, + {":/video_webm", "application/atom+xml", false, 0}, + {":/video_webm", "application/atom+xml", true, 0} +}; +static const size_t testListSize = sizeof(testList) / sizeof(testList[0]); + +#endif // TestData_h diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources.qrc b/Source/WebKit/qt/tests/MIMESniffing/resources.qrc new file mode 100644 index 0000000..b4afb32 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources.qrc @@ -0,0 +1,23 @@ +<RCC> + <qresource prefix="/"> + <file alias="application_atom+xml">resources/application_atom+xml</file> + <file alias="application_ogg">resources/application_ogg</file> + <file alias="application_pdf">resources/application_pdf</file> + <file alias="application_postscript">resources/application_postscript</file> + <file alias="application_rdf+xml">resources/application_rdf+xml</file> + <file alias="application_rss+xml">resources/application_rss+xml</file> + <file alias="application_x-gzip">resources/application_x-gzip</file> + <file alias="application_x-rar-compressed">resources/application_x-rar-compressed</file> + <file alias="application_zip">resources/application_zip</file> + <file alias="audio_x-wave">resources/audio_x-wave</file> + <file alias="image_bmp">resources/image_bmp</file> + <file alias="image_gif">resources/image_gif</file> + <file alias="image_jpeg">resources/image_jpeg</file> + <file alias="image_png">resources/image_png</file> + <file alias="image_vnd.microsoft.icon">resources/image_vnd.microsoft.icon</file> + <file alias="image_webp">resources/image_webp</file> + <file alias="text_html">resources/text_html</file> + <file alias="text_xml">resources/text_xml</file> + <file alias="video_webm">resources/video_webm</file> + </qresource> +</RCC> diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_atom+xml b/Source/WebKit/qt/tests/MIMESniffing/resources/application_atom+xml new file mode 100644 index 0000000..54086a7 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_atom+xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"> + <title>Example Feed</title> + <link href="http://example.org/"/> + <updated>2003-12-13T18:30:02Z</updated> + <author> + <name>John Doe</name> + </author> + <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id> + <entry> + <title>Atom-Powered Robots Run Amok</title> + <link href="http://example.org/2003/12/13/atom03"/> + <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + <updated>2003-12-13T18:30:02Z</updated> + <summary>Some text.</summary> + </entry> +</feed> diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_ogg b/Source/WebKit/qt/tests/MIMESniffing/resources/application_ogg Binary files differnew file mode 100644 index 0000000..b9cc1b2 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_ogg diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_pdf b/Source/WebKit/qt/tests/MIMESniffing/resources/application_pdf Binary files differnew file mode 100644 index 0000000..7f8996f --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_pdf diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_postscript b/Source/WebKit/qt/tests/MIMESniffing/resources/application_postscript new file mode 100644 index 0000000..c4b9ae6 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_postscript @@ -0,0 +1,137 @@ +%!PS-Adobe-2.0 EPSF-1.2
+%%Creator: HiJaak 2.1
+%%CreationDate: 12/29/93 13:52:08
+%%BoundingBox:126 216 486 576
+%%EndComments
+/ld {load def} bind def
+/s /stroke ld /f /fill ld /m /moveto ld /l /lineto ld /c /curveto ld /rgb {255 div 3 1 roll 255 div 3 1 roll 255 div 3 1 roll setrgbcolor} def
+126 216 translate
+360.0000 360.0000 scale
+/picstr 124 string def
+124 124 8 [124 0 0 -124 0 124] {currentfile picstr readhexstring pop} image
+65656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565ADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADAD1B1B1B1B1B1B1B1B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565006565656565006565656500656565656565000000656565656565656565656565656565656565656565656565656565656565ADADAD00ADADAD000000ADAD00ADADADADAD00AD00ADADADAD00ADADADAD00000000001B1B001B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565000065656500006565650065006565656500656565006565656565656565656565656565656565656565656565656565656565ADADAD00ADAD00ADADAD00ADAD00ADADAD00ADAD0000ADADAD00ADADADAD001B1B1B1B001B001B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565000065656500006565650065006565650065656565650065656565656565656565656565656565656565656565656565656565ADADAD00AD00ADADADADADADAD00ADADAD00ADAD00AD00ADAD00ADADADAD001B1B1B1B001B001B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565006500650065006565650065006565650065656565656565656565656565656565656565656565656565656565656565656565ADADAD00AD00ADADADADADADADAD00AD00ADADAD00AD00ADAD00ADADADAD00000000001B1B001B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565006500650065006565006565650065650065656500000065656565656565656565656565656565656565656565656565656565ADADAD00AD00ADADADADADADADADAD00ADADADAD00ADAD00AD00ADADADAD001B1B1B1B001B001B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565006500650065006565000000000065650065656565650065656565656565656565656565656565656565656565656565656565ADADAD00AD00ADADADADADADADADAD00ADADADAD00ADAD00AD00ADADADAD001B1B1B1B001B001B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565006565006565006500656565656500656500656565006565656565656565656565656565656565656565656565656565656565ADADAD00ADAD00ADADAD00ADADADAD00ADADADAD00ADADAD0000ADADADAD001B1B1B1B001B001B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565006565006565006500656565656500656565000000656565656565656565656565656565656565656565656565656565656565ADADAD00ADADAD000000ADADADADAD00ADADADAD00ADADADAD00ADADADAD00000000001B1B00000000FF
+65656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565ADADAD00ADADADADADADADADADADADADADADADADADADADADADADADADADADAD1B1B1B1B1B1B1B1B1B1BFF
+65656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565656565ADADAD00ADADADADADADADADADADADADADADADADADADADADADADADADADADAD1B1B1B1B1B1B1B1B1B1BFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC0000DCDC0000DC0000000000DC0000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292000000009292000000000092920000929200009292929292ADADADAD00000000ADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC0000DCDC0000DC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929200009292000092000092920000920000929200009292929292ADADAD0000ADAD0000ADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC00DCDC00DCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292920000929292929292000092920000920000009200009292929292ADAD0000ADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC00000000DCDC0000000000DC0000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292920000929292929292000092920000920000009200009292929292ADAD0000ADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC0000DCDCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292920000929200000092000000000092920000920000009292929292ADAD0000ADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC0000DCDCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292920000929292000092000092000092920000920000009292929292ADAD0000ADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC0000DCDCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929200009292000092000092920000920000929200009292929292ADADAD0000ADAD0000ADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC0000DCDCDC0000000000DC000000000000DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292000000009292000092929200000000929200009292929292ADADADAD00000000ADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+DCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC92929292929292929292929292929292929292929292929292929292929292ADADADADADADADADADADFF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00000000004A4A00000000004A00000000004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDC0000DCDC0000DC0000000000DC0000DCDCDCDCDCDCDCDCDCDCDC00000065656500000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00004A4A00004A00004A4A4A4A00004A4A00004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDC0000DCDC0000DC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDC00000065656500000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00004A4A00004A00004A4A4A4A00004A4A00004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDC00DCDC00DCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDC00000000650000000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00004A4A00004A00000000004A00004A4A00004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDC00000000DCDC0000000000DC0000DCDCDCDCDCDCDCDCDCDCDC00000000650000000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00000000004A4A00004A4A4A4A00004A4A00004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDC0000DCDCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDC00006500650065000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00004A00004A4A00004A4A4A4A00004A4A00004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDC0000DCDCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDC00006500000065000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00004A4A00004A00004A4A4A4A00004A4A00004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDC0000DCDCDC0000DCDCDCDC0000DCDCDCDCDCDCDCDCDCDCDC00006500000065000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A00004A4A4A000000000000004A00000000004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDC0000DCDCDC0000000000DC000000000000DCDCDCDCDCDCDC00006565006565000065FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A00000000004A4A4A4A0000004A4A4A00000000004A4A4A4A004A4A4A4A4A00000000004A00000000004A00000000004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A004A4A4A4A004A4A004A4A4A004A4A4A4A004A4A4A4A4A4A004A4A4A4A4A004A4A4A4A4A004A4A4A4A4A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A004A4A4A4A004A004A4A4A4A4A004A4A4A004A4A4A4A4A4A004A4A4A4A4A004A4A4A4A4A004A4A4A4A4A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A00000000004A4A004A4A4A4A4A004A4A4A004A4A4A4A4A4A004A4A4A4A4A00000000004A000000004A4A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A004A4A4A4A004A004A4A4A4A4A004A4A4A004A4A4A4A4A4A004A4A4A4A4A004A4A4A4A4A004A4A4A4A4A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A004A4A4A4A004A004A4A4A4A4A004A4A4A004A4A4A4A4A4A004A4A4A4A4A004A4A4A4A4A004A4A4A4A4A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A004A4A4A4A004A4A004A4A4A004A4A4A4A004A4A4A4A4A4A004A4A4A4A4A004A4A4A4A4A004A4A4A4A4A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A00000000004A4A4A4A0000004A4A4A4A4A004A4A4A4A4A4A00000000004A00000000004A004A4A4A4A4A4A4A004A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4ADCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDCDC65656565656565656565FF
+showpage
+
\ No newline at end of file diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_rdf+xml b/Source/WebKit/qt/tests/MIMESniffing/resources/application_rdf+xml new file mode 100644 index 0000000..e214145 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_rdf+xml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> + +<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/"> + + <channel rdf:about="http://www.xml.com/xml/news.rss"> + <title>XML.com</title> + <link>http://xml.com/pub</link> + <description> + XML.com features a rich mix of information and services + for the XML community. + </description> + + <image rdf:resource="http://xml.com/universal/images/xml_tiny.gif" /> + + <items> + <rdf:Seq> + <rdf:li resource="http://xml.com/pub/2000/08/09/xslt/xslt.html" /> + <rdf:li resource="http://xml.com/pub/2000/08/09/rdfdb/index.html" /> + </rdf:Seq> + </items> + + </channel> + + <image rdf:about="http://xml.com/universal/images/xml_tiny.gif"> + <title>XML.com</title> + <link>http://www.xml.com</link> + <url>http://xml.com/universal/images/xml_tiny.gif</url> + </image> + + <item rdf:about="http://xml.com/pub/2000/08/09/xslt/xslt.html"> + <title>Processing Inclusions with XSLT</title> + <link>http://xml.com/pub/2000/08/09/xslt/xslt.html</link> + <description> + Processing document inclusions with general XML tools can be + problematic. This article proposes a way of preserving inclusion + information through SAX-based processing. + </description> + </item> + + <item rdf:about="http://xml.com/pub/2000/08/09/rdfdb/index.html"> + <title>Putting RDF to Work</title> + <link>http://xml.com/pub/2000/08/09/rdfdb/index.html</link> + <description> + Tool and API support for the Resource Description Framework + is slowly coming of age. Edd Dumbill takes a look at RDFDB, + one of the most exciting new RDF toolkits. + </description> + </item> + +</rdf:RDF> diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_rss+xml b/Source/WebKit/qt/tests/MIMESniffing/resources/application_rss+xml new file mode 100644 index 0000000..3537c41 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_rss+xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<rss version="0.91"> +<channel> + +<title>Folha.com - Ambiente - Principal</title> +<link>http://redir.folha.com.br/redir/online/ambiente/rss091/*http://www1.folha.uol.com.br/ambiente/</link> +<description>Primeiro jornal em tempo real em língua portuguesa</description> +<language>pt-br</language> +<copyright>Copyright Folha.com. Todos os direitos reservados.</copyright> +<docs>http://redir.folha.com.br/redir/online/ambiente/rss091/*http://www1.folha.uol.com.br/folha/conheca/arquivo_e_copyright.shtml</docs> +<webMaster>webmaster@grupofolha.com.br (Webmaster Folha.com)</webMaster> + +<image> +<title>Folha.com - Ambiente - Principal</title> +<url>http://www1.folha.uol.com.br/folha/images/lgo-folha_com-88x31.gif</url> +<link>http://redir.folha.com.br/redir/online/ambiente/rss091/*http://www1.folha.uol.com.br/ambiente/</link> +<width>88</width> +<height>31</height> +<description>Primeiro jornal em tempo real em língua portuguesa</description> +</image> + +<item> +<title>Nasa dimensiona danos da seca na Amazônia em 2,4 mi de km2</title> +<link>http://redir.folha.com.br/redir/online/ambiente/rss091/*http://www1.folha.uol.com.br/ambiente/895526-nasa-dimensiona-danos-da-seca-na-amazonia-em-24-mi-de-km2.shtml</link> +<description> +Os satélites da Nasa (agência espacial americana) forneceram material para uma análise dos estragos provocados pela <a href="http://www1.folha.uol.com.br/ambiente/870588-amazonia-teve-a-pior-seca-dos-ultimos-cem-anos.shtml">pior seca a atingir a Amazônia em 2010</a>. +Pela tomada aérea, estima-se que foram 2,5 milhões de quilômetros quadrados afetados --pouco menos da metade do ecossistema amazônico. +<table class="articleGraphic"> +<tr> +<td rowspan="3" class="articleGraphicSpace"></td> +<td class="articleGraphicCredit">Universidade de Boston/Nasa</td> +<td rowspan="3" class="articleGraphicSpace"></td> +</tr> +<tr> +<td class="articleGraphicImage"><img src="http://f.i.uol.com.br/folha/ambiente/images/11090120.jpeg" alt="Área (vermelho) mostra redução do índice do verdor, onde a vegetação ficou menos verde e mais seca" border="0" /></td> +</tr> +<tr> +<td class="articleGraphicCaption">Área (vermelho) mostra redução do índice do verdor, onde a vegetação ficou menos verde e mais seca</td> +</tr> +</table> +<a href="http://redir.folha.com.br/redir/online/ambiente/rss091/*http://www1.folha.uol.com.br/ambiente/895526-nasa-dimensiona-danos-da-seca-na-amazonia-em-24-mi-de-km2.shtml">Leia mais</a> (29/03/2011 - 18h04)</description> +<pubDate>29 Mar 2011 18:04:00 -0300</pubDate> +</item> +</channel> +</rss> diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_x-gzip b/Source/WebKit/qt/tests/MIMESniffing/resources/application_x-gzip Binary files differnew file mode 100644 index 0000000..a5e7d31 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_x-gzip diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_x-rar-compressed b/Source/WebKit/qt/tests/MIMESniffing/resources/application_x-rar-compressed Binary files differnew file mode 100644 index 0000000..d9cb6ae --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_x-rar-compressed diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/application_zip b/Source/WebKit/qt/tests/MIMESniffing/resources/application_zip Binary files differnew file mode 100644 index 0000000..8aec52f --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/application_zip diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/audio_x-wave b/Source/WebKit/qt/tests/MIMESniffing/resources/audio_x-wave Binary files differnew file mode 100644 index 0000000..a0f9b85 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/audio_x-wave diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/image_bmp b/Source/WebKit/qt/tests/MIMESniffing/resources/image_bmp Binary files differnew file mode 100644 index 0000000..b61d368 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/image_bmp diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/image_gif b/Source/WebKit/qt/tests/MIMESniffing/resources/image_gif Binary files differnew file mode 100644 index 0000000..32b1ea2 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/image_gif diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/image_jpeg b/Source/WebKit/qt/tests/MIMESniffing/resources/image_jpeg Binary files differnew file mode 100644 index 0000000..1874576 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/image_jpeg diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/image_png b/Source/WebKit/qt/tests/MIMESniffing/resources/image_png Binary files differnew file mode 100644 index 0000000..bef59c7 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/image_png diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/image_vnd.microsoft.icon b/Source/WebKit/qt/tests/MIMESniffing/resources/image_vnd.microsoft.icon Binary files differnew file mode 100644 index 0000000..58921b8 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/image_vnd.microsoft.icon diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/image_webp b/Source/WebKit/qt/tests/MIMESniffing/resources/image_webp Binary files differnew file mode 100644 index 0000000..0da983e --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/image_webp diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/text_html b/Source/WebKit/qt/tests/MIMESniffing/resources/text_html new file mode 100644 index 0000000..21eeee3 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/text_html @@ -0,0 +1,3 @@ + +<!-- saved from url=(0017)http://127.0.0.1/ --> +<HTML><HEAD><META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"></HEAD><BODY><H1>It works!</H1></BODY></HTML>
\ No newline at end of file diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/text_xml b/Source/WebKit/qt/tests/MIMESniffing/resources/text_xml new file mode 100644 index 0000000..38a9fe5 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/text_xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="ISO-8859-1"?>
+<CATALOG>
+ <CD>
+ <TITLE>Empire Burlesque</TITLE>
+ <ARTIST>Bob Dylan</ARTIST>
+ <COUNTRY>USA</COUNTRY>
+ <COMPANY>Columbia</COMPANY>
+ <PRICE>10.90</PRICE>
+ <YEAR>1985</YEAR>
+ </CD>
+ <CD>
+ <TITLE>Unchain my heart</TITLE>
+ <ARTIST>Joe Cocker</ARTIST>
+ <COUNTRY>USA</COUNTRY>
+ <COMPANY>EMI</COMPANY>
+ <PRICE>8.20</PRICE>
+ <YEAR>1987</YEAR>
+ </CD>
+</CATALOG>
diff --git a/Source/WebKit/qt/tests/MIMESniffing/resources/video_webm b/Source/WebKit/qt/tests/MIMESniffing/resources/video_webm Binary files differnew file mode 100644 index 0000000..95d5031 --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/resources/video_webm diff --git a/Source/WebKit/qt/tests/MIMESniffing/tst_MIMESniffing.cpp b/Source/WebKit/qt/tests/MIMESniffing/tst_MIMESniffing.cpp new file mode 100644 index 0000000..8c5417f --- /dev/null +++ b/Source/WebKit/qt/tests/MIMESniffing/tst_MIMESniffing.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + + 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 "MIMESniffing.h" + +#include "TestData.h" + +#include <QtCore/QFile> +#include <QtCore/QString> +#include <QtTest/QtTest> + +class tst_MIMESniffing : public QObject { + Q_OBJECT + +public: + tst_MIMESniffing(); + +private Q_SLOTS: + void testCase1(); +}; + +tst_MIMESniffing::tst_MIMESniffing() +{ +} + +static inline const char* errorText(const TestData& data, const char* sniffedType) +{ + return QString("file: %1, advertised: %2, image: %3. sniffed mime type was expected to be \"%4\" but instead was \"%5\"").arg(data.file).arg(data.advertisedType).arg(data.isImage).arg(data.sniffedType).arg(sniffedType).toLatin1(); +} + +void tst_MIMESniffing::testCase1() +{ + + for (int i = 0; i < testListSize; ++i) { + QFile file(testList[i].file); + QVERIFY2(file.open(QIODevice::ReadOnly), QString("unable to open file %1").arg(file.fileName()).toLatin1()); + + MIMESniffer sniffer(testList[i].advertisedType, testList[i].isImage); + QByteArray data = file.peek(sniffer.dataSize()); + + const char* sniffedType = sniffer.sniff(data.constData(), data.size()); + + QVERIFY2(!(sniffedType || testList[i].sniffedType) || (sniffedType && testList[i].sniffedType), errorText(testList[i], sniffedType)); + + if (sniffedType) + QVERIFY2(!strcmp(sniffedType, testList[i].sniffedType), errorText(testList[i], sniffedType)); + + } + + QVERIFY2(true, "Failure"); +} + +QTEST_APPLESS_MAIN(tst_MIMESniffing); + +#include "tst_MIMESniffing.moc" diff --git a/Source/WebKit/qt/tests/benchmarks/loading/loading.pro b/Source/WebKit/qt/tests/benchmarks/loading/loading.pro new file mode 100644 index 0000000..99c64a5 --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/loading/loading.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../../.. +include(../../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/benchmarks/loading/tst_loading.cpp b/Source/WebKit/qt/tests/benchmarks/loading/tst_loading.cpp new file mode 100644 index 0000000..0bc87f7 --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/loading/tst_loading.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2009 Holger Hans Peter Freyther + * + * 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 <QtTest/QtTest> + +#include <qwebframe.h> +#include <qwebview.h> +#include <qpainter.h> + +/** + * Starts an event loop that runs until the given signal is received. + Optionally the event loop + * can return earlier on a timeout. + * + * \return \p true if the requested signal was received + * \p false on timeout + */ +static bool waitForSignal(QObject* obj, const char* signal, int timeout = 0) +{ + QEventLoop loop; + QObject::connect(obj, signal, &loop, SLOT(quit())); + QTimer timer; + QSignalSpy timeoutSpy(&timer, SIGNAL(timeout())); + if (timeout > 0) { + QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.setSingleShot(true); + timer.start(timeout); + } + loop.exec(); + return timeoutSpy.isEmpty(); +} + +class tst_Loading : public QObject +{ + Q_OBJECT + +public: + +public Q_SLOTS: + void init(); + void cleanup(); + +private Q_SLOTS: + void load_data(); + void load(); + +private: + QWebView* m_view; + QWebPage* m_page; +}; + +void tst_Loading::init() +{ + m_view = new QWebView; + m_page = m_view->page(); + + QSize viewportSize(1024, 768); + m_view->setFixedSize(viewportSize); + m_page->setViewportSize(viewportSize); +} + +void tst_Loading::cleanup() +{ + delete m_view; +} + +void tst_Loading::load_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::newRow("amazon") << QUrl("http://www.amazon.com"); + QTest::newRow("kde") << QUrl("http://www.kde.org"); + QTest::newRow("apple") << QUrl("http://www.apple.com"); +} + +void tst_Loading::load() +{ + QFETCH(QUrl, url); + + + QBENCHMARK { + m_view->load(url); + + // really wait for loading, painting is in another test + ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); + } +} + +QTEST_MAIN(tst_Loading) +#include "tst_loading.moc" diff --git a/Source/WebKit/qt/tests/benchmarks/painting/painting.pro b/Source/WebKit/qt/tests/benchmarks/painting/painting.pro new file mode 100644 index 0000000..99c64a5 --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/painting/painting.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../../.. +include(../../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/benchmarks/painting/tst_painting.cpp b/Source/WebKit/qt/tests/benchmarks/painting/tst_painting.cpp new file mode 100644 index 0000000..fc5b8e3 --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/painting/tst_painting.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2009 Holger Hans Peter Freyther + * + * 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 <QtTest/QtTest> + +#include <qwebelement.h> +#include <qwebframe.h> +#include <qwebview.h> +#include <qpainter.h> + +/** + * Starts an event loop that runs until the given signal is received. + Optionally the event loop + * can return earlier on a timeout. + * + * \return \p true if the requested signal was received + * \p false on timeout + */ +static bool waitForSignal(QObject* obj, const char* signal, int timeout = 0) +{ + QEventLoop loop; + QObject::connect(obj, signal, &loop, SLOT(quit())); + QTimer timer; + QSignalSpy timeoutSpy(&timer, SIGNAL(timeout())); + if (timeout > 0) { + QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.setSingleShot(true); + timer.start(timeout); + } + loop.exec(); + return timeoutSpy.isEmpty(); +} + +class tst_Painting : public QObject +{ + Q_OBJECT + +public: + +public Q_SLOTS: + void init(); + void cleanup(); + +private Q_SLOTS: + void paint_data(); + void paint(); + void textAreas(); + +private: + QWebView* m_view; + QWebPage* m_page; +}; + +void tst_Painting::init() +{ + m_view = new QWebView; + m_page = m_view->page(); + + QSize viewportSize(1024, 768); + m_view->setFixedSize(viewportSize); + m_page->setViewportSize(viewportSize); +} + +void tst_Painting::cleanup() +{ + delete m_view; +} + +void tst_Painting::paint_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::newRow("amazon") << QUrl("http://www.amazon.com"); +} + +void tst_Painting::paint() +{ + QFETCH(QUrl, url); + + m_view->load(url); + ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); + + /* force a layout */ + QWebFrame* mainFrame = m_page->mainFrame(); + mainFrame->toPlainText(); + + QPixmap pixmap(m_page->viewportSize()); + QBENCHMARK { + QPainter painter(&pixmap); + mainFrame->render(&painter, QRect(QPoint(0, 0), m_page->viewportSize())); + painter.end(); + } +} + +void tst_Painting::textAreas() +{ + m_view->load(QUrl("data:text/html;<html><body></body></html>")); + ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); + + QWebElement bodyElement = m_page->mainFrame()->findFirstElement("body"); + + int count = 100; + while (count--) { + QString markup("<textarea cols='1' rows='1'></textarea>"); + bodyElement.appendInside(markup); + } + + /* force a layout */ + QWebFrame* mainFrame = m_page->mainFrame(); + mainFrame->toPlainText(); + + QPixmap pixmap(mainFrame->contentsSize()); + QBENCHMARK { + QPainter painter(&pixmap); + mainFrame->render(&painter, QRect(QPoint(0, 0), mainFrame->contentsSize())); + painter.end(); + } +} + +QTEST_MAIN(tst_Painting) +#include "tst_painting.moc" diff --git a/Source/WebKit/qt/tests/benchmarks/webgl/10000_triangles.html b/Source/WebKit/qt/tests/benchmarks/webgl/10000_triangles.html new file mode 100644 index 0000000..fd061aa --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/webgl/10000_triangles.html @@ -0,0 +1,59 @@ +<html> + <body style="margin: 0"> + <canvas width="1000" height="1000"></canvas> + </body> +</html> +<script> + var canvas = document.getElementsByTagName("canvas")[0]; + gl = canvas.getContext("experimental-webgl"); + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.viewport(0, 0, canvas.width, canvas.height); + + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, "attribute vec4 vPosition;\nvoid main() { gl_Position = vPosition; }"); + gl.compileShader(vertexShader); + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }"); + gl.compileShader(fragmentShader); + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.bindAttribLocation(shaderProgram, 0, "vPosition"); + gl.linkProgram(shaderProgram); + + gl.useProgram(shaderProgram); + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + + var vertices = []; + var seedX = -1.0; + var seedY = 1.0; + for (var i = 1; i <= 10000; ++i) { + vertices.push(seedX); + vertices.push(seedY); + vertices.push(0); + seedX += 0.01; + vertices.push(seedX); + vertices.push(seedY - 0.02); + vertices.push(0); + seedX += 0.01; + vertices.push(seedX); + vertices.push(seedY); + vertices.push(0); + if (!(i % 100)) { + seedX = -1.0; + seedY -= 0.02; + } + } + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + + + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 30000); + gl.flush(); +</script> diff --git a/Source/WebKit/qt/tests/benchmarks/webgl/tst_webgl.cpp b/Source/WebKit/qt/tests/benchmarks/webgl/tst_webgl.cpp new file mode 100644 index 0000000..bd865a2 --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/webgl/tst_webgl.cpp @@ -0,0 +1,130 @@ +/* + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + + 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 "../../util.h" +#include <QGLWidget> +#include <QGraphicsView> +#include <QGraphicsWebView> +#include <QScopedPointer> +#include <QWebFrame> +#include <QtTest/QtTest> + +class GraphicsView; + +class tst_WebGlPerformance : public QObject { + Q_OBJECT + +private slots: + void init(); + void cleanup(); + + void benchSoftwareFallbackRgb16(); + void benchSoftwareFallbackRgb32(); + void benchSoftwareFallbackArgb32(); + void benchSoftwareFallbackArgb32Premultiplied(); + +private: + void benchmarkFrameRenderingOnImage(QImage::Format); + + QScopedPointer<GraphicsView> m_view; +}; + +class GraphicsView : public QGraphicsView { +public: + GraphicsView(); + QGraphicsWebView* m_webView; + +protected: + void resizeEvent(QResizeEvent*); +}; + +GraphicsView::GraphicsView() +{ + QGraphicsScene* const scene = new QGraphicsScene(this); + setScene(scene); + + m_webView = new QGraphicsWebView; + scene->addItem(m_webView); + + m_webView->page()->settings()->setAttribute(QWebSettings::WebGLEnabled, true); + + resize(800, 600); + setFrameShape(QFrame::NoFrame); + setViewport(new QGLWidget); +} + +void GraphicsView::resizeEvent(QResizeEvent* event) +{ + QGraphicsView::resizeEvent(event); + QRectF rect(QPoint(0, 0), event->size()); + m_webView->setGeometry(rect); + scene()->setSceneRect(rect); +} + +void tst_WebGlPerformance::init() +{ + m_view.reset(new GraphicsView); + m_view->showMaximized(); + QTest::qWaitForWindowShown(m_view.data()); +} + +void tst_WebGlPerformance::cleanup() +{ + m_view.reset(); +} + +void tst_WebGlPerformance::benchSoftwareFallbackRgb16() +{ + benchmarkFrameRenderingOnImage(QImage::Format_RGB16); +} + +void tst_WebGlPerformance::benchSoftwareFallbackRgb32() +{ + benchmarkFrameRenderingOnImage(QImage::Format_RGB32); +} + +void tst_WebGlPerformance::benchSoftwareFallbackArgb32() +{ + benchmarkFrameRenderingOnImage(QImage::Format_ARGB32); +} + +void tst_WebGlPerformance::benchSoftwareFallbackArgb32Premultiplied() +{ + benchmarkFrameRenderingOnImage(QImage::Format_ARGB32_Premultiplied); +} + +void tst_WebGlPerformance::benchmarkFrameRenderingOnImage(QImage::Format format) +{ + m_view->m_webView->load(QUrl(QLatin1String("qrc:///testcases/10000_triangles.html"))); + const bool pageLoaded = waitForSignal(m_view->m_webView, SIGNAL(loadFinished(bool))); + Q_ASSERT(pageLoaded); + Q_UNUSED(pageLoaded); + + QImage target(m_view->size(), format); + QBENCHMARK { + QPainter painter(&target); + m_view->render(&painter); + painter.end(); + } +} + +QTEST_MAIN(tst_WebGlPerformance) + +#include "tst_webgl.moc" diff --git a/Source/WebKit/qt/tests/benchmarks/webgl/tst_webgl.qrc b/Source/WebKit/qt/tests/benchmarks/webgl/tst_webgl.qrc new file mode 100644 index 0000000..b849448 --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/webgl/tst_webgl.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/testcases"> + <file>10000_triangles.html</file> + </qresource> +</RCC> diff --git a/Source/WebKit/qt/tests/benchmarks/webgl/webgl.pro b/Source/WebKit/qt/tests/benchmarks/webgl/webgl.pro new file mode 100644 index 0000000..fb21bc8 --- /dev/null +++ b/Source/WebKit/qt/tests/benchmarks/webgl/webgl.pro @@ -0,0 +1,4 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../../.. +include(../../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc +QT += opengl diff --git a/Source/WebKit/qt/tests/hybridPixmap/hybridPixmap.pro b/Source/WebKit/qt/tests/hybridPixmap/hybridPixmap.pro new file mode 100644 index 0000000..9e80870 --- /dev/null +++ b/Source/WebKit/qt/tests/hybridPixmap/hybridPixmap.pro @@ -0,0 +1,11 @@ +# ------------------------------------------------- +# Project created by QtCreator 2009-12-10T11:25:02 +# ------------------------------------------------- +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +TARGET = hybridPixmap +SOURCES += widget.cpp +HEADERS += widget.h +FORMS += widget.ui +RESOURCES += resources.qrc +CONFIG += console diff --git a/Source/WebKit/qt/tests/hybridPixmap/resources.qrc b/Source/WebKit/qt/tests/hybridPixmap/resources.qrc new file mode 100644 index 0000000..5fd47e3 --- /dev/null +++ b/Source/WebKit/qt/tests/hybridPixmap/resources.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>test.html</file> + </qresource> +</RCC> diff --git a/Source/WebKit/qt/tests/hybridPixmap/test.html b/Source/WebKit/qt/tests/hybridPixmap/test.html new file mode 100644 index 0000000..0f2c345 --- /dev/null +++ b/Source/WebKit/qt/tests/hybridPixmap/test.html @@ -0,0 +1,65 @@ +<html> + <head> + <style> + img { display: block; border-style: groove} + </style> + <script> + function startTest() + { + var obj = myWidget.image; + var pxm = myWidget.pixmap; + + var img = new Image; + obj.assignToHTMLImageElement(img); + var img1 = document.getElementById("img1"); + var img2 = document.getElementById("img2"); + var img3 = document.getElementById("img3"); + var img4 = document.getElementById("img4"); + document.body.appendChild(img); + obj.assignToHTMLImageElement(img3); + pxm.assignToHTMLImageElement(img4); + myWidget.compare(pxm.width, img4.width); + myWidget.compare(obj.width, img3.width); + var signalsFired = 0; + myWidget.compare(obj.toString(),"[Qt Native Pixmap "+obj.width+","+obj.height+"]"); + myWidget.compare(String(pxm),"[Qt Native Pixmap "+pxm.width+","+pxm.height+"]"); + + // this shouldn't work but shouldn't crash + myWidget.randomSlot("foobar"); + + myWidget.pixmapSignal.connect(function(imgFromSignal) { + myWidget.compare(imgFromSignal.height, img2.height); + if (++signalsFired == 2) + myWidget.completeTest(); + }); + + myWidget.imageSignal.connect(function(imgFromSignal) { + myWidget.compare(pxm.height, img2.height); + if (++signalsFired == 2) + myWidget.completeTest(); + }); + + function continueTestAfterImagesAreLoaded() + { + if (img1.complete && img2.complete) { + myWidget.compare(pxm.height, img2.height); + myWidget.pixmapSlot(img); + myWidget.imageSlot(pxm); + } + } + img1.onload = continueTestAfterImagesAreLoaded; + img2.onload = continueTestAfterImagesAreLoaded; + img1.src = obj.toDataUrl(); + img2.src = myWidget.pixmap.toDataUrl(); + myWidget.image = pxm; + myWidget.pixmap = img; + } + </script> + </head> + <body onload="startTest()"> + <img id="img1" /> + <img id="img2" /> + <img id="img3" /> + <img id="img4" /> + </body> +</html> diff --git a/Source/WebKit/qt/tests/hybridPixmap/tst_hybridPixmap.cpp b/Source/WebKit/qt/tests/hybridPixmap/tst_hybridPixmap.cpp new file mode 100644 index 0000000..72dbb3b --- /dev/null +++ b/Source/WebKit/qt/tests/hybridPixmap/tst_hybridPixmap.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "../util.h" + +#include "widget.h" +#include <QtTest/QtTest> + +class tst_hybridPixmap : public QObject { + Q_OBJECT + +public: + tst_hybridPixmap(QObject* o = 0) : QObject(o) {} + +public slots: + void init() + { + } + + void cleanup() + { + } + +private slots: + void hybridPixmap() + { + Widget widget; + widget.show(); + widget.start(); + waitForSignal(&widget, SIGNAL(testComplete())); + } +}; + +QTEST_MAIN(tst_hybridPixmap) + +#include <tst_hybridPixmap.moc> diff --git a/Source/WebKit/qt/tests/hybridPixmap/widget.cpp b/Source/WebKit/qt/tests/hybridPixmap/widget.cpp new file mode 100644 index 0000000..cfdb1d6 --- /dev/null +++ b/Source/WebKit/qt/tests/hybridPixmap/widget.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "widget.h" + +#include "qwebelement.h" +#include "qwebframe.h" +#include "ui_widget.h" +#include <QPainter> +#include <QtTest/QtTest> + +Widget::Widget(QWidget* parent) : + QWidget(parent), + ui(new Ui::Widget) +{ + ui->setupUi(this); +} + +void Widget::refreshJS() +{ + ui->webView->page()->mainFrame()->addToJavaScriptWindowObject("myWidget", this); +} +void Widget::start() +{ + ui->webView->load(QUrl("qrc:///test.html")); + connect(ui->webView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(refreshJS())); + ui->webView->page()->mainFrame()->addToJavaScriptWindowObject("myWidget", this); +} + +void Widget::completeTest() +{ + QCOMPARE(ui->lbl1->pixmap()->size(), ui->lbl2->size()); + QCOMPARE(ui->lbl3->size(), ui->lbl4->pixmap()->size()); + QCOMPARE(ui->lbl2->size().width(), ui->webView->page()->mainFrame()->findFirstElement("#img1").evaluateJavaScript("this.width").toInt()); + QCOMPARE(ui->lbl3->size().width(), ui->webView->page()->mainFrame()->findFirstElement("#img2").evaluateJavaScript("this.width").toInt()); + emit testComplete(); +} + +void Widget::setPixmap(const QPixmap& p) +{ + ui->lbl1->setPixmap(p); +} +QPixmap Widget::pixmap() const +{ + QPixmap px(ui->lbl3->size()); + { + QPainter p(&px); + ui->lbl3->render(&p); + } + return px; +} +void Widget::setImage(const QImage& img) +{ + ui->lbl4->setPixmap(QPixmap::fromImage(img)); +} + +QImage Widget::image() const +{ + QImage img(ui->lbl2->size(), QImage::Format_ARGB32); + { + QPainter p(&img); + ui->lbl2->render(&p); + } + return img; +} + +Widget::~Widget() +{ + delete ui; +} + +void Widget::changeEvent(QEvent* e) +{ + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} +void Widget::compare(const QVariant& a, const QVariant& b) +{ + QCOMPARE(a, b); +} + +void Widget::imageSlot(const QImage& img) +{ + QCOMPARE(img.size(), ui->lbl3->size()); + emit pixmapSignal(QPixmap::fromImage(img)); +} + +void Widget::pixmapSlot(const QPixmap& pxm) +{ + QCOMPARE(pxm.size(), ui->lbl2->size()); + emit imageSignal(ui->lbl4->pixmap()->toImage()); +} + +void Widget::randomSlot(const QPixmap& pxm) +{ + QVERIFY(pxm.isNull()); +} diff --git a/Source/WebKit/qt/tests/hybridPixmap/widget.h b/Source/WebKit/qt/tests/hybridPixmap/widget.h new file mode 100644 index 0000000..2ac8a06 --- /dev/null +++ b/Source/WebKit/qt/tests/hybridPixmap/widget.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef widget_h +#define widget_h + +#include <QImage> +#include <QPixmap> +#include <QWidget> +#include "qwebview.h" + +typedef QWebView WebView; + +QT_BEGIN_NAMESPACE +namespace Ui { +class Widget; +} +QT_END_NAMESPACE + +class Widget : public QWidget { + Q_OBJECT + Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) + Q_PROPERTY(QImage image READ image WRITE setImage) + +public: + Widget(QWidget* parent = 0); + ~Widget(); + void setPixmap(const QPixmap&); + QPixmap pixmap() const; + void setImage(const QImage&); + QImage image() const; + +private slots: + void refreshJS(); + +public slots: + void completeTest(); + void start(); + void compare(const QVariant& a, const QVariant& b); + void imageSlot(const QImage&); + void pixmapSlot(const QPixmap&); + void randomSlot(const QPixmap&); + +signals: + void testComplete(); + void imageSignal(const QImage&); + void pixmapSignal(const QPixmap&); + +protected: + void changeEvent(QEvent* e); + +private: + Ui::Widget* ui; +}; + +#endif // widget_h diff --git a/Source/WebKit/qt/tests/hybridPixmap/widget.ui b/Source/WebKit/qt/tests/hybridPixmap/widget.ui new file mode 100644 index 0000000..272d6a7 --- /dev/null +++ b/Source/WebKit/qt/tests/hybridPixmap/widget.ui @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Widget</class> + <widget class="QWidget" name="Widget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle"> + <string notr="true">Widget</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="WebView" name="webView" native="true"> + <property name="url" stdset="0"> + <url> + <string notr="true">about:blank</string> + </url> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="lbl1"> + <property name="text"> + <string notr="true"/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lbl2"> + <property name="minimumSize"> + <size> + <width>120</width> + <height>30</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>120</width> + <height>30</height> + </size> + </property> + <property name="text"> + <string notr="true">Image from Qt to HTML</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lbl3"> + <property name="text"> + <string notr="true">Pixmap from Qt to HTML</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="lbl4"> + <property name="text"> + <string notr="true"/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11"/> + <customwidgets> + <customwidget> + <class>WebView</class> + <extends>QWidget</extends> + <header>widget.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/qdeclarativewebview.pro b/Source/WebKit/qt/tests/qdeclarativewebview/qdeclarativewebview.pro new file mode 100644 index 0000000..488fa84 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/qdeclarativewebview.pro @@ -0,0 +1,6 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc +INCLUDEPATH += \ + $$PWD/../../declarative + diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.html b/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.html new file mode 100644 index 0000000..22e3e24 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.html @@ -0,0 +1,17 @@ +<html> +<head><title>Basic</title> +<link rel="icon" sizes="48x48" href="basic.png"> +<script type="text/javascript"> +<!-- +window.onload = function(){ window.status = "status here"; } +// --> +</script> +</head> +<body leftmargin="0" marginwidth="0"> +<table width="123"> +<tbody> +<tr><td>This is a basic test.</td></tr> +</tbody> +</table> +</body> +</html> diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.png b/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.png Binary files differnew file mode 100644 index 0000000..35717cc --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.png diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.qml new file mode 100644 index 0000000..b5208d0 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/basic.qml @@ -0,0 +1,5 @@ +import QtWebKit 1.0 + +WebView { + url: "basic.html" +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/elements.html b/Source/WebKit/qt/tests/qdeclarativewebview/resources/elements.html new file mode 100644 index 0000000..9236867 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/elements.html @@ -0,0 +1,14 @@ +<body leftmargin=0 topmargin=0> +<table width="300px" border=1 cellpadding=0 cellspacing=0> +<tr> +<td align=center width=25%%><p>A</p></td> +<td width=75% height=50px> + <table width=100% border=1 cellpadding=0 cellspacing=0> + <tr> + <td align=center width=50% height=50px><p>B</p></td> + <td align=center width=50% height=50px><p>C</p></td> + </tr> + </table> +</td> +</tr> +</table> diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/elements.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/elements.qml new file mode 100644 index 0000000..8fef8c9 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/elements.qml @@ -0,0 +1,7 @@ +import QtWebKit 1.0 + +WebView { + url: "elements.html" + width: 310 + height: 100 +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/forward.html b/Source/WebKit/qt/tests/qdeclarativewebview/resources/forward.html new file mode 100644 index 0000000..62ab62d --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/forward.html @@ -0,0 +1,12 @@ +<html> +<head><title>Forward</title> +<link rel="icon" sizes="32x32" href="forward.png"> +</head> +<body leftmargin="0" marginwidth="0"> +<table width="123"> +<tbody> +<tr><td>This is more.</td></tr> +</tbody> +</table> +</body> +</html> diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/forward.png b/Source/WebKit/qt/tests/qdeclarativewebview/resources/forward.png Binary files differnew file mode 100644 index 0000000..a82533e --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/forward.png diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/javaScript.html b/Source/WebKit/qt/tests/qdeclarativewebview/resources/javaScript.html new file mode 100644 index 0000000..35270bc --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/javaScript.html @@ -0,0 +1,11 @@ +<html> +<head><title>JavaScript</title> +<link rel="icon" sizes="48x48" href="basic.png"> +<script type="text/javascript"> +<!-- +window.onload = function(){ window.status = "status here"; } +// --> +</script> +</head> +<body> +This is a JS test. diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/javaScript.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/javaScript.qml new file mode 100644 index 0000000..527e3b9 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/javaScript.qml @@ -0,0 +1,12 @@ +import QtQuick 1.0 +import QtWebKit 1.0 + +WebView { + url: "javaScript.html" + javaScriptWindowObjects: [ + QtObject { + property string qmlprop: "qmlvalue" + WebView.windowObjectName: "myjsname" + } + ] +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/loadError.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/loadError.qml new file mode 100644 index 0000000..26cec8f --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/loadError.qml @@ -0,0 +1,5 @@ +import QtWebKit 1.0 + +WebView { + url: "does-not-exist.html" +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/newwindows.html b/Source/WebKit/qt/tests/qdeclarativewebview/resources/newwindows.html new file mode 100644 index 0000000..dd541f9 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/newwindows.html @@ -0,0 +1,16 @@ +<html> +<head> +<script type="text/javascript"> +<!-- +function clickTheLink() +{ + var ev = document.createEvent('MouseEvents'); + ev.initEvent( "click", true, false ); + document.getElementById('thelink').dispatchEvent(ev); +} +// --> +</script> +</head> +<h1>Multiple windows...</h1> + +<a id=thelink target="_blank" href="newwindows.html">Popup!</a> diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/newwindows.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/newwindows.qml new file mode 100644 index 0000000..e66631d --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/newwindows.qml @@ -0,0 +1,34 @@ +// Demonstrates opening new WebViews from HTML + +import QtQuick 1.0 +import QtWebKit 1.0 + +Grid { + columns: 3 + id: pages + height: 300; width: 600 + property int total: 0 + + Component { + id: webViewPage + Rectangle { + width: webView.width + height: webView.height + border.color: "gray" + + WebView { + id: webView + width: 150 // force predictable for test + newWindowComponent: webViewPage + newWindowParent: pages + url: "newwindows.html" + Timer { + interval: 10; running: total<4; repeat: false; + onTriggered: { if (webView.status==WebView.Ready) { total++; webView.evaluateJavaScript("clickTheLink()") } } + } + } + } + } + + Loader { sourceComponent: webViewPage } +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/propertychanges.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/propertychanges.qml new file mode 100644 index 0000000..db06887 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/propertychanges.qml @@ -0,0 +1,34 @@ +import QtQuick 1.0 +import QtWebKit 1.0 + +Item { + width: 240 + height: 160 + Grid { + anchors.fill: parent + objectName: "newWindowParent" + id: newWindowParent + } + + Row { + anchors.fill: parent + id: oldWindowParent + objectName: "oldWindowParent" + } + + Loader { + sourceComponent: webViewComponent + } + Component { + id: webViewComponent + WebView { + id: webView + objectName: "webView" + newWindowComponent: webViewComponent + newWindowParent: oldWindowParent + url: "basic.html" + renderingEnabled: true + pressGrabTime: 200 + } + } +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/sample.html b/Source/WebKit/qt/tests/qdeclarativewebview/resources/sample.html new file mode 100644 index 0000000..cc9bffa --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/sample.html @@ -0,0 +1,6 @@ +<html> +<head></head> +<body width=400 height=400> +Here is a sample text +</body> +</html> diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/sethtml.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/sethtml.qml new file mode 100644 index 0000000..5bff442 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/sethtml.qml @@ -0,0 +1,5 @@ +import QtWebKit 1.0 + +WebView { + html: "<p>This is a <b>string</b> set on the WebView" +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewbackgroundcolor.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewbackgroundcolor.qml new file mode 100644 index 0000000..2edc794 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewbackgroundcolor.qml @@ -0,0 +1,10 @@ +import Qt 4.7 +import QtWebKit 1.1 + +WebView { + id: myweb + height: 300 + width: 300 + focus: true + backgroundColor : "red" +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewtest.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewtest.qml new file mode 100644 index 0000000..ae8a42b --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewtest.qml @@ -0,0 +1,24 @@ +import Qt 4.7 +import QtWebKit 1.0 + +Flickable { + id: flick + width: 640 + height: 400 + clip: true + contentWidth: myweb.width; contentHeight: myweb.height + property alias myurl: myweb.url + property alias prefHeight: myweb.preferredHeight + property alias prefWidth: myweb.preferredWidth + property url testUrl; + WebView { + id: myweb + url: testUrl + smooth: false + scale: 1.0 + preferredHeight: 500 + preferredWidth: 600 + pressGrabTime: 1000 + focus: true + } +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewtestdefault.qml b/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewtestdefault.qml new file mode 100644 index 0000000..3f659f6 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/resources/webviewtestdefault.qml @@ -0,0 +1,22 @@ +import Qt 4.7 +import QtWebKit 1.0 + +Flickable { + id: flick + width: 640 + height: 400 + clip: true + contentWidth: myweb.width; contentHeight: myweb.height + property alias myurl: myweb.url + property alias prefHeight: myweb.preferredHeight + property alias prefWidth: myweb.preferredWidth + property url testUrl; + WebView { + id: myweb + url: testUrl; + smooth: false + scale: 1.0 + pressGrabTime: 1000 + focus: true + } +} diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/tst_qdeclarativewebview.cpp b/Source/WebKit/qt/tests/qdeclarativewebview/tst_qdeclarativewebview.cpp new file mode 100644 index 0000000..23e8bfb --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/tst_qdeclarativewebview.cpp @@ -0,0 +1,544 @@ +#include "../util.h" +#include <QAction> +#include <QColor> +#include <QDebug> +#include <QDeclarativeComponent> +#include <QDeclarativeEngine> +#include <QDeclarativeItem> +#include <QDeclarativeProperty> +#include <QDeclarativeView> +#include <QDir> +#include <QGraphicsWebView> +#include <QTest> +#include <QVariant> +#include <QWebFrame> +#include "qdeclarativewebview_p.h" + +QT_BEGIN_NAMESPACE + +class tst_QDeclarativeWebView : public QObject { + Q_OBJECT + +public: + tst_QDeclarativeWebView(); + +private slots: + void cleanupTestCase(); + + void basicProperties(); + void elementAreaAt(); + void historyNav(); + void javaScript(); + void loadError(); + void multipleWindows(); + void newWindowComponent(); + void newWindowParent(); + void preferredWidthTest(); + void preferredHeightTest(); + void preferredWidthDefaultTest(); + void preferredHeightDefaultTest(); + void pressGrabTime(); + void renderingEnabled(); + void setHtml(); + void settings(); +#if QT_VERSION >= 0x040703 + void backgroundColor(); +#endif + +private: + void checkNoErrors(const QDeclarativeComponent&); + QString tmpDir() const + { + static QString tmpd = QDir::tempPath() + "/tst_qdeclarativewebview-" + + QDateTime::currentDateTime().toString(QLatin1String("yyyyMMddhhmmss")); + return tmpd; + } +}; + +tst_QDeclarativeWebView::tst_QDeclarativeWebView() +{ + Q_UNUSED(waitForSignal) +} + +static QString strippedHtml(QString html) +{ + html.replace(QRegExp("\\s+"), ""); + return html; +} + +static QString fileContents(const QString& filename) +{ + QFile file(filename); + file.open(QIODevice::ReadOnly); + return QString::fromUtf8(file.readAll()); +} + +static void removeRecursive(const QString& dirname) +{ + QDir dir(dirname); + QFileInfoList entries(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)); + for (int i = 0; i < entries.count(); ++i) + if (entries[i].isDir()) + removeRecursive(entries[i].filePath()); + else + dir.remove(entries[i].fileName()); + QDir().rmdir(dirname); +} + +void tst_QDeclarativeWebView::cleanupTestCase() +{ + removeRecursive(tmpDir()); +} + +void tst_QDeclarativeWebView::basicProperties() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/basic.qml")); + checkNoErrors(component); + QWebSettings::enablePersistentStorage(tmpDir()); + + QObject* wv = component.create(); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + QCOMPARE(wv->property("title").toString(), QLatin1String("Basic")); + QTRY_COMPARE(qvariant_cast<QPixmap>(wv->property("icon")).width(), 48); + QEXPECT_FAIL("", "'icon' property isn't working", Continue); + QCOMPARE(qvariant_cast<QPixmap>(wv->property("icon")), QPixmap("qrc:///resources/basic.png")); + QCOMPARE(wv->property("statusText").toString(), QLatin1String("status here")); + QCOMPARE(strippedHtml(fileContents(":/resources/basic.html")), strippedHtml(wv->property("html").toString())); + QEXPECT_FAIL("", "TODO: get preferred width from QGraphicsWebView result", Continue); + QCOMPARE(wv->property("preferredWidth").toInt(), 0); + QEXPECT_FAIL("", "TODO: get preferred height from QGraphicsWebView result", Continue); + QCOMPARE(wv->property("preferredHeight").toInt(), 0); + QCOMPARE(wv->property("url").toUrl(), QUrl("qrc:///resources/basic.html")); + QCOMPARE(wv->property("status").toInt(), int(QDeclarativeWebView::Ready)); + + QAction* reloadAction = wv->property("reload").value<QAction*>(); + QVERIFY(reloadAction); + QVERIFY(reloadAction->isEnabled()); + QAction* backAction = wv->property("back").value<QAction*>(); + QVERIFY(backAction); + QVERIFY(!backAction->isEnabled()); + QAction* forwardAction = wv->property("forward").value<QAction*>(); + QVERIFY(forwardAction); + QVERIFY(!forwardAction->isEnabled()); + QAction* stopAction = wv->property("stop").value<QAction*>(); + QVERIFY(stopAction); + QVERIFY(!stopAction->isEnabled()); + + wv->setProperty("pixelCacheSize", 0); // mainly testing that it doesn't crash or anything! + QCOMPARE(wv->property("pixelCacheSize").toInt(), 0); + reloadAction->trigger(); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); +} + +void tst_QDeclarativeWebView::elementAreaAt() +{ + QSKIP("This test should be changed to test 'heuristicZoom' instead.", SkipAll); + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/elements.qml")); + checkNoErrors(component); + QDeclarativeWebView* wv = qobject_cast<QDeclarativeWebView*>(component.create()); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + + // Areas from elements.html. +// const QRect areaA(1, 1, 75, 54); +// const QRect areaB(78, 3, 110, 50); +// const QRect wholeView(0, 0, 310, 100); +// const QRect areaBC(76, 1, 223, 54); + +// QCOMPARE(wv->elementAreaAt(40, 30, 100, 100), areaA); +// QCOMPARE(wv->elementAreaAt(130, 30, 200, 100), areaB); +// QCOMPARE(wv->elementAreaAt(40, 30, 400, 400), wholeView); +// QCOMPARE(wv->elementAreaAt(130, 30, 280, 280), areaBC); +// QCOMPARE(wv->elementAreaAt(130, 30, 400, 400), wholeView); +} + +void tst_QDeclarativeWebView::historyNav() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/basic.qml")); + checkNoErrors(component); + QWebSettings::enablePersistentStorage(tmpDir()); + + QObject* wv = component.create(); + QVERIFY(wv); + + QAction* reloadAction = wv->property("reload").value<QAction*>(); + QVERIFY(reloadAction); + QAction* backAction = wv->property("back").value<QAction*>(); + QVERIFY(backAction); + QAction* forwardAction = wv->property("forward").value<QAction*>(); + QVERIFY(forwardAction); + QAction* stopAction = wv->property("stop").value<QAction*>(); + QVERIFY(stopAction); + + for (int i = 1; i <= 2; ++i) { + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + QCOMPARE(wv->property("title").toString(), QLatin1String("Basic")); + QTRY_COMPARE(qvariant_cast<QPixmap>(wv->property("icon")).width(), 48); + QEXPECT_FAIL("", "'icon' property isn't working", Continue); + QCOMPARE(qvariant_cast<QPixmap>(wv->property("icon")), QPixmap("qrc:///data/basic.png")); + QCOMPARE(wv->property("statusText").toString(), QLatin1String("status here")); + QCOMPARE(strippedHtml(fileContents(":/resources/basic.html")), strippedHtml(wv->property("html").toString())); + QEXPECT_FAIL("", "TODO: get preferred width from QGraphicsWebView result", Continue); + QCOMPARE(wv->property("preferredWidth").toDouble(), 0.0); + QCOMPARE(wv->property("url").toUrl(), QUrl("qrc:///resources/basic.html")); + QCOMPARE(wv->property("status").toInt(), int(QDeclarativeWebView::Ready)); + QVERIFY(reloadAction->isEnabled()); + QVERIFY(!backAction->isEnabled()); + QVERIFY(!forwardAction->isEnabled()); + QVERIFY(!stopAction->isEnabled()); + reloadAction->trigger(); + } + + wv->setProperty("url", QUrl("qrc:///resources/forward.html")); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + QCOMPARE(wv->property("title").toString(), QLatin1String("Forward")); + QTRY_COMPARE(qvariant_cast<QPixmap>(wv->property("icon")).width(), 32); + QEXPECT_FAIL("", "'icon' property isn't working", Continue); + QCOMPARE(qvariant_cast<QPixmap>(wv->property("icon")), QPixmap("qrc:///resources/forward.png")); + QCOMPARE(strippedHtml(fileContents(":/resources/forward.html")), strippedHtml(wv->property("html").toString())); + QCOMPARE(wv->property("url").toUrl(), QUrl("qrc:///resources/forward.html")); + QCOMPARE(wv->property("status").toInt(), int(QDeclarativeWebView::Ready)); + QCOMPARE(wv->property("statusText").toString(), QString()); + + QVERIFY(reloadAction->isEnabled()); + QVERIFY(backAction->isEnabled()); + QVERIFY(!forwardAction->isEnabled()); + QVERIFY(!stopAction->isEnabled()); + + backAction->trigger(); + + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + QCOMPARE(wv->property("title").toString(), QLatin1String("Basic")); + QCOMPARE(strippedHtml(fileContents(":/resources/basic.html")), strippedHtml(wv->property("html").toString())); + QCOMPARE(wv->property("url").toUrl(), QUrl("qrc:///resources/basic.html")); + QCOMPARE(wv->property("status").toInt(), int(QDeclarativeWebView::Ready)); + + QVERIFY(reloadAction->isEnabled()); + QVERIFY(!backAction->isEnabled()); + QVERIFY(forwardAction->isEnabled()); + QVERIFY(!stopAction->isEnabled()); +} + +static inline QVariant callEvaluateJavaScript(QObject *object, const QString& snippet) +{ + QVariant result; + QMetaObject::invokeMethod(object, "evaluateJavaScript", Q_RETURN_ARG(QVariant, result), Q_ARG(QString, snippet)); + return result; +} + +void tst_QDeclarativeWebView::javaScript() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/javaScript.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + + QCOMPARE(callEvaluateJavaScript(wv, "123").toInt(), 123); + QCOMPARE(callEvaluateJavaScript(wv, "window.status").toString(), QLatin1String("status here")); + QCOMPARE(callEvaluateJavaScript(wv, "window.myjsname.qmlprop").toString(), QLatin1String("qmlvalue")); +} + +void tst_QDeclarativeWebView::loadError() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/loadError.qml")); + checkNoErrors(component); + QWebSettings::enablePersistentStorage(tmpDir()); + + QObject* wv = component.create(); + QVERIFY(wv); + QAction* reloadAction = wv->property("reload").value<QAction*>(); + QVERIFY(reloadAction); + + for (int i = 1; i <= 2; ++i) { + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + QCOMPARE(wv->property("title").toString(), QString()); + QCOMPARE(wv->property("statusText").toString(), QString()); // HTML 'status bar' text, not error message + QCOMPARE(wv->property("url").toUrl(), QUrl("qrc:///resources/does-not-exist.html")); // Unlike QWebPage, which loses url + QCOMPARE(wv->property("status").toInt(), int(QDeclarativeWebView::Error)); + reloadAction->trigger(); + } +} + +void tst_QDeclarativeWebView::multipleWindows() +{ + QSKIP("Rework this test to not depend on QDeclarativeGrid", SkipAll); + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/newwindows.qml")); + checkNoErrors(component); + +// QDeclarativeGrid *grid = qobject_cast<QDeclarativeGrid*>(component.create()); +// QVERIFY(grid != 0); +// QTRY_COMPARE(grid->children().count(), 2+4); // Component, Loader (with 1 WebView), 4 new-window WebViews +// QDeclarativeItem* popup = qobject_cast<QDeclarativeItem*>(grid->children().at(2)); // first popup after Component and Loader. +// QVERIFY(popup != 0); +// QTRY_COMPARE(popup->x(), 150.0); +} + +void tst_QDeclarativeWebView::newWindowComponent() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/propertychanges.qml")); + checkNoErrors(component); + QDeclarativeItem* rootItem = qobject_cast<QDeclarativeItem*>(component.create()); + QVERIFY(rootItem); + QObject* wv = rootItem->findChild<QObject*>("webView"); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + + QDeclarativeComponent substituteComponent(&engine); + substituteComponent.setData("import QtQuick 1.0; WebView { objectName: 'newWebView'; url: 'basic.html'; }", QUrl::fromLocalFile("")); + QSignalSpy newWindowComponentSpy(wv, SIGNAL(newWindowComponentChanged())); + + wv->setProperty("newWindowComponent", QVariant::fromValue(&substituteComponent)); + QCOMPARE(wv->property("newWindowComponent"), QVariant::fromValue(&substituteComponent)); + QCOMPARE(newWindowComponentSpy.count(), 1); + + wv->setProperty("newWindowComponent", QVariant::fromValue(&substituteComponent)); + QCOMPARE(newWindowComponentSpy.count(), 1); + + wv->setProperty("newWindowComponent", QVariant::fromValue((QDeclarativeComponent*)0)); + QCOMPARE(newWindowComponentSpy.count(), 2); +} + +void tst_QDeclarativeWebView::newWindowParent() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/propertychanges.qml")); + checkNoErrors(component); + QDeclarativeItem* rootItem = qobject_cast<QDeclarativeItem*>(component.create()); + QVERIFY(rootItem); + QObject* wv = rootItem->findChild<QObject*>("webView"); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + + QDeclarativeItem* oldWindowParent = rootItem->findChild<QDeclarativeItem*>("oldWindowParent"); + QCOMPARE(qvariant_cast<QDeclarativeItem*>(wv->property("newWindowParent")), oldWindowParent); + QSignalSpy newWindowParentSpy(wv, SIGNAL(newWindowParentChanged())); + + QDeclarativeItem* newWindowParent = rootItem->findChild<QDeclarativeItem*>("newWindowParent"); + wv->setProperty("newWindowParent", QVariant::fromValue(newWindowParent)); + QVERIFY(newWindowParent); + QVERIFY(oldWindowParent); + QCOMPARE(oldWindowParent->childItems().count(), 0); + QCOMPARE(wv->property("newWindowParent"), QVariant::fromValue(newWindowParent)); + QCOMPARE(newWindowParentSpy.count(), 1); + + wv->setProperty("newWindowParent", QVariant::fromValue(newWindowParent)); + QCOMPARE(newWindowParentSpy.count(), 1); + + wv->setProperty("newWindowParent", QVariant::fromValue((QDeclarativeItem*)0)); + QCOMPARE(newWindowParentSpy.count(), 2); +} + +void tst_QDeclarativeWebView::preferredWidthTest() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/webviewtest.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + wv->setProperty("testUrl", QUrl("qrc:///resources/sample.html")); + QCOMPARE(wv->property("prefWidth").toInt(), 600); +} + +void tst_QDeclarativeWebView::preferredHeightTest() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/webviewtest.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + wv->setProperty("testUrl", QUrl("qrc:///resources/sample.html")); + QCOMPARE(wv->property("prefHeight").toInt(), 500); +} + +void tst_QDeclarativeWebView::preferredWidthDefaultTest() +{ + QGraphicsWebView view; + view.load(QUrl("qrc:///resources/sample.html")); + + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/webviewtestdefault.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + wv->setProperty("testUrl", QUrl("qrc:///resources/sample.html")); + QCOMPARE(wv->property("prefWidth").toDouble(), view.preferredWidth()); +} + +void tst_QDeclarativeWebView::preferredHeightDefaultTest() +{ + QGraphicsWebView view; + view.load(QUrl("qrc:///resources/sample.html")); + + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/webviewtestdefault.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + wv->setProperty("testUrl", QUrl("qrc:///resources/sample.html")); + QCOMPARE(wv->property("prefHeight").toDouble(), view.preferredHeight()); +} + +void tst_QDeclarativeWebView::pressGrabTime() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/propertychanges.qml")); + checkNoErrors(component); + QDeclarativeItem* rootItem = qobject_cast<QDeclarativeItem*>(component.create()); + QVERIFY(rootItem); + QObject* wv = rootItem->findChild<QObject*>("webView"); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + QCOMPARE(wv->property("pressGrabTime").toInt(), 200); + QSignalSpy pressGrabTimeSpy(wv, SIGNAL(pressGrabTimeChanged())); + + wv->setProperty("pressGrabTime", 100); + QCOMPARE(wv->property("pressGrabTime").toInt(), 100); + QCOMPARE(pressGrabTimeSpy.count(), 1); + + wv->setProperty("pressGrabTime", 100); + QCOMPARE(pressGrabTimeSpy.count(), 1); + + wv->setProperty("pressGrabTime", 0); + QCOMPARE(pressGrabTimeSpy.count(), 2); +} + +void tst_QDeclarativeWebView::renderingEnabled() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/propertychanges.qml")); + checkNoErrors(component); + QDeclarativeItem* rootItem = qobject_cast<QDeclarativeItem*>(component.create()); + QVERIFY(rootItem); + QObject* wv = rootItem->findChild<QObject*>("webView"); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + + QVERIFY(wv->property("renderingEnabled").toBool()); + QSignalSpy renderingEnabledSpy(wv, SIGNAL(renderingEnabledChanged())); + + wv->setProperty("renderingEnabled", false); + QVERIFY(!wv->property("renderingEnabled").toBool()); + QCOMPARE(renderingEnabledSpy.count(), 1); + + wv->setProperty("renderingEnabled", false); + QCOMPARE(renderingEnabledSpy.count(), 1); + + wv->setProperty("renderingEnabled", true); + QCOMPARE(renderingEnabledSpy.count(), 2); +} + +void tst_QDeclarativeWebView::setHtml() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/sethtml.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + QCOMPARE(wv->property("html").toString(), QLatin1String("<html><head></head><body><p>This is a <b>string</b> set on the WebView</p></body></html>")); + + QSignalSpy spy(wv, SIGNAL(htmlChanged())); + wv->setProperty("html", QLatin1String("<html><head><title>Basic</title></head><body><p>text</p></body></html>")); + QCOMPARE(spy.count(), 1); +} + +void tst_QDeclarativeWebView::settings() +{ + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/basic.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + QTRY_COMPARE(wv->property("progress").toDouble(), 1.0); + + QObject* s = QDeclarativeProperty(wv, "settings").object(); + QVERIFY(s); + + QStringList settingsList; + settingsList << QString::fromAscii("autoLoadImages") + << QString::fromAscii("developerExtrasEnabled") + << QString::fromAscii("javaEnabled") + << QString::fromAscii("javascriptCanAccessClipboard") + << QString::fromAscii("javascriptCanOpenWindows") + << QString::fromAscii("javascriptEnabled") + << QString::fromAscii("linksIncludedInFocusChain") + << QString::fromAscii("localContentCanAccessRemoteUrls") + << QString::fromAscii("localStorageDatabaseEnabled") + << QString::fromAscii("offlineStorageDatabaseEnabled") + << QString::fromAscii("offlineWebApplicationCacheEnabled") + << QString::fromAscii("pluginsEnabled") + << QString::fromAscii("printElementBackgrounds") + << QString::fromAscii("privateBrowsingEnabled") + << QString::fromAscii("zoomTextOnly"); + + // Merely tests that setting gets stored (in QWebSettings), behavioural tests are in WebKit. + for (int b = 0; b <= 1; b++) { + bool value = !!b; + foreach (const QString& name, settingsList) + s->setProperty(name.toAscii().data(), value); + for (int i = 0; i < 2; i++) { + foreach (const QString& name, settingsList) + QCOMPARE(s->property(name.toAscii().data()).toBool(), value); + } + } +} + +#if QT_VERSION >= 0x040703 +void tst_QDeclarativeWebView::backgroundColor() +{ + // We test here the rendering of the background. + QDeclarativeEngine engine; + QDeclarativeComponent component(&engine, QUrl("qrc:///resources/webviewbackgroundcolor.qml")); + checkNoErrors(component); + QObject* wv = component.create(); + QVERIFY(wv); + QCOMPARE(wv->property("backgroundColor").value<QColor>(), QColor(Qt::red)); + QDeclarativeView view; + view.setSource(QUrl("qrc:///resources/webviewbackgroundcolor.qml")); + view.show(); + QTest::qWaitForWindowShown(&view); + QPixmap result(view.width(), view.height()); + QPainter painter(&result); + view.render(&painter); + QPixmap reference(view.width(), view.height()); + reference.fill(Qt::red); + QCOMPARE(reference, result); + + // We test the emission of the backgroundColorChanged signal. + QSignalSpy spyColorChanged(wv, SIGNAL(backgroundColorChanged())); + wv->setProperty("backgroundColor", Qt::red); + QCOMPARE(spyColorChanged.count(), 0); + wv->setProperty("backgroundColor", Qt::green); + QCOMPARE(spyColorChanged.count(), 1); +} +#endif + +void tst_QDeclarativeWebView::checkNoErrors(const QDeclarativeComponent& component) +{ + // Wait until the component is ready + QTRY_VERIFY(component.isReady() || component.isError()); + if (component.isError()) { + QList<QDeclarativeError> errors = component.errors(); + for (int ii = 0; ii < errors.count(); ++ii) { + const QDeclarativeError &error = errors.at(ii); + QByteArray errorStr = QByteArray::number(error.line()) + ":" + + QByteArray::number(error.column()) + ":" + + error.description().toUtf8(); + qWarning() << errorStr; + } + } + QVERIFY(!component.isError()); +} + +QTEST_MAIN(tst_QDeclarativeWebView) +#include "tst_qdeclarativewebview.moc" + +QT_END_NAMESPACE diff --git a/Source/WebKit/qt/tests/qdeclarativewebview/tst_qdeclarativewebview.qrc b/Source/WebKit/qt/tests/qdeclarativewebview/tst_qdeclarativewebview.qrc new file mode 100644 index 0000000..fb66ad8 --- /dev/null +++ b/Source/WebKit/qt/tests/qdeclarativewebview/tst_qdeclarativewebview.qrc @@ -0,0 +1,22 @@ +<RCC> + <qresource prefix="/"> + <file>resources/basic.html</file> + <file>resources/basic.png</file> + <file>resources/basic.qml</file> + <file>resources/elements.html</file> + <file>resources/elements.qml</file> + <file>resources/forward.html</file> + <file>resources/forward.png</file> + <file>resources/javaScript.html</file> + <file>resources/javaScript.qml</file> + <file>resources/loadError.qml</file> + <file>resources/newwindows.html</file> + <file>resources/newwindows.qml</file> + <file>resources/propertychanges.qml</file> + <file>resources/sample.html</file> + <file>resources/sethtml.qml</file> + <file>resources/webviewbackgroundcolor.qml</file> + <file>resources/webviewtestdefault.qml</file> + <file>resources/webviewtest.qml</file> + </qresource> +</RCC> diff --git a/Source/WebKit/qt/tests/qgraphicswebview/qgraphicswebview.pro b/Source/WebKit/qt/tests/qgraphicswebview/qgraphicswebview.pro new file mode 100644 index 0000000..e5494ae --- /dev/null +++ b/Source/WebKit/qt/tests/qgraphicswebview/qgraphicswebview.pro @@ -0,0 +1,6 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc +contains(DEFINES, ENABLE_WEBGL=1) { + QT += opengl +} diff --git a/Source/WebKit/qt/tests/qgraphicswebview/resources/56929.html b/Source/WebKit/qt/tests/qgraphicswebview/resources/56929.html new file mode 100644 index 0000000..2f7fa97 --- /dev/null +++ b/Source/WebKit/qt/tests/qgraphicswebview/resources/56929.html @@ -0,0 +1,8 @@ +<body style="background-color: white"> + <div id="1" style="width: 50%; height: 50%; background-color: green"/> + <script> + function resizeDiv() { + document.getElementById("1").setAttribute("style", "width: 150%; height: 150%; background-color: green"); + } + </script> +</body> diff --git a/Source/WebKit/qt/tests/qgraphicswebview/resources/input_types.html b/Source/WebKit/qt/tests/qgraphicswebview/resources/input_types.html new file mode 100644 index 0000000..18ab314 --- /dev/null +++ b/Source/WebKit/qt/tests/qgraphicswebview/resources/input_types.html @@ -0,0 +1,8 @@ +<html><body> +<input type='text' maxlength='20' style='position: absolute; left: 10px; top: 0px; height: 50px; width: 100px;'/><br> +<input type='password' style='position: absolute; left: 10px; top: 50px; height: 50px; width: 100px;'/><br> +<input type='tel' style='position: absolute; left: 10px; top: 100px; height: 50px; width: 100px;'/><br> +<input type='number' style='position: absolute; left: 10px; top: 150px; height: 50px; width: 100px;'/><br> +<input type='email' style='position: absolute; left: 10px; top: 200px; height: 50px; width: 100px;'/><br> +<input type='url' style='position: absolute; left: 10px; top: 250px; height: 50px; width: 100px;'/><br>" +</body></html>
\ No newline at end of file diff --git a/Source/WebKit/qt/tests/qgraphicswebview/resources/pointing_right.html b/Source/WebKit/qt/tests/qgraphicswebview/resources/pointing_right.html new file mode 100644 index 0000000..bc592fb --- /dev/null +++ b/Source/WebKit/qt/tests/qgraphicswebview/resources/pointing_right.html @@ -0,0 +1,45 @@ +<html> + <body style="margin: 0"> + <canvas width="100" height="100"></canvas> + </body> +</html> +<script> + var canvas = document.getElementsByTagName("canvas")[0]; + gl = canvas.getContext("experimental-webgl"); + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.viewport(0, 0, canvas.width, canvas.height); + + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, "attribute vec4 vPosition;\nvoid main() { gl_Position = vPosition; }"); + gl.compileShader(vertexShader); + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }"); + gl.compileShader(fragmentShader); + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.bindAttribLocation(shaderProgram, 0, "vPosition"); + gl.linkProgram(shaderProgram); + + gl.useProgram(shaderProgram); + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + + var vertices = [-1.0, -1.0, + 0.0, 0.0, + -1.0, 1.0]; + var seedX = -1.0; + var seedY = 1.0; + vertices + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + + + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 3); + gl.flush(); +</script> diff --git a/Source/WebKit/qt/tests/qgraphicswebview/resources/pointing_up.html b/Source/WebKit/qt/tests/qgraphicswebview/resources/pointing_up.html new file mode 100644 index 0000000..474a56d --- /dev/null +++ b/Source/WebKit/qt/tests/qgraphicswebview/resources/pointing_up.html @@ -0,0 +1,46 @@ +<html> + <body style="margin: 0"> + <canvas width="100" height="100"></canvas> + </body> +</html> +<script> + var canvas = document.getElementsByTagName("canvas")[0]; + gl = canvas.getContext("experimental-webgl"); + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.viewport(0, 0, canvas.width, canvas.height); + + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, "attribute vec4 vPosition;\nvoid main() { gl_Position = vPosition; }"); + gl.compileShader(vertexShader); + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }"); + gl.compileShader(fragmentShader); + + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.bindAttribLocation(shaderProgram, 0, "vPosition"); + gl.linkProgram(shaderProgram); + + gl.useProgram(shaderProgram); + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + + var vertices = [-1.0, -1.0, + 0.0, 0.0, + 1.0, -1.0]; + var seedX = -1.0; + var seedY = 1.0; + vertices + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); + + + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 3); + gl.flush(); + gl.finish(); +</script> diff --git a/Source/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.cpp b/Source/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.cpp new file mode 100644 index 0000000..ed162c4 --- /dev/null +++ b/Source/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.cpp @@ -0,0 +1,602 @@ +/* + Copyright (C) 2009 Jakub Wieczorek <faw217@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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "../util.h" +#include <QtTest/QtTest> +#include <QGraphicsSceneMouseEvent> +#include <QGraphicsView> +#include <QStyleOptionGraphicsItem> +#include <qgraphicswebview.h> +#include <qwebpage.h> +#include <qwebframe.h> + +#if defined(ENABLE_WEBGL) && ENABLE_WEBGL +#include <QGLWidget> +#endif + +class tst_QGraphicsWebView : public QObject +{ + Q_OBJECT + +private slots: + void qgraphicswebview(); + void crashOnViewlessWebPages(); + void microFocusCoordinates(); + void focusInputTypes(); + void crashOnSetScaleBeforeSetUrl(); + void widgetsRenderingThroughCache(); + void setPalette_data(); + void setPalette(); + void renderHints(); +#if defined(ENABLE_TILED_BACKING_STORE) && ENABLE_TILED_BACKING_STORE + void bug56929(); +#endif +#if defined(ENABLE_WEBGL) && ENABLE_WEBGL + void webglSoftwareFallbackVerticalOrientation(); + void webglSoftwareFallbackHorizontalOrientation(); + +private: + void compareCanvasToImage(const QUrl&, const QImage&); +#endif +}; + +void tst_QGraphicsWebView::qgraphicswebview() +{ + QGraphicsWebView item; + item.url(); + item.title(); + item.icon(); + item.zoomFactor(); + item.history(); + item.settings(); + item.page(); + item.setPage(0); + item.page(); + item.setUrl(QUrl()); + item.setZoomFactor(0); + item.load(QUrl()); + item.setHtml(QString()); + item.setContent(QByteArray()); + item.isModified(); +} + +class WebPage : public QWebPage +{ + Q_OBJECT + +public: + WebPage(QObject* parent = 0): QWebPage(parent) + { + } + + QGraphicsWebView* webView; + +private slots: + // Force a webview deletion during the load. + // It should not cause WebPage to crash due to + // it accessing invalid pageClient pointer. + void aborting() + { + delete webView; + } +}; + +class GraphicsWebView : public QGraphicsWebView +{ + Q_OBJECT + +public: + GraphicsWebView(QGraphicsItem* parent = 0): QGraphicsWebView(parent) + { + } + + void fireMouseClick(QPointF point) { + QGraphicsSceneMouseEvent presEv(QEvent::GraphicsSceneMousePress); + presEv.setPos(point); + presEv.setButton(Qt::LeftButton); + presEv.setButtons(Qt::LeftButton); + QGraphicsSceneMouseEvent relEv(QEvent::GraphicsSceneMouseRelease); + relEv.setPos(point); + relEv.setButton(Qt::LeftButton); + relEv.setButtons(Qt::LeftButton); + QGraphicsWebView::sceneEvent(&presEv); + QGraphicsWebView::sceneEvent(&relEv); + } +}; + +void tst_QGraphicsWebView::crashOnViewlessWebPages() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + + QGraphicsWebView* webView = new QGraphicsWebView; + WebPage* page = new WebPage; + webView->setPage(page); + page->webView = webView; + scene.addItem(webView); + + view.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + view.resize(600, 480); + webView->resize(view.geometry().size()); + + QCoreApplication::processEvents(); + view.show(); + + // Resizing the page will resize and layout the empty "about:blank" + // page, so we first connect the signal afterward. + connect(page->mainFrame(), SIGNAL(initialLayoutCompleted()), page, SLOT(aborting())); + + page->mainFrame()->load(QUrl("data:text/html," + "<frameset cols=\"25%,75%\">" + "<frame src=\"data:text/html,foo \">" + "<frame src=\"data:text/html,bar\">" + "</frameset>")); + + QVERIFY(waitForSignal(page, SIGNAL(loadFinished(bool)))); + delete page; +} + +void tst_QGraphicsWebView::crashOnSetScaleBeforeSetUrl() +{ + QGraphicsWebView* webView = new QGraphicsWebView; + webView->setScale(2.0); + delete webView; +} + +void tst_QGraphicsWebView::widgetsRenderingThroughCache() +{ + // Widgets should be rendered the same way with and without + // intermediate cache (tiling for example). + // See bug https://bugs.webkit.org/show_bug.cgi?id=47767 where + // widget are rendered as disabled when caching is using. + + QGraphicsWebView* webView = new QGraphicsWebView; + webView->setHtml(QLatin1String("<body style=\"background-color: white\"><input type=range></input><input type=checkbox></input><input type=radio></input><input type=file></input></body>")); + + QGraphicsView view; + view.show(); + QGraphicsScene* scene = new QGraphicsScene(&view); + view.setScene(scene); + scene->addItem(webView); + view.setGeometry(QRect(0, 0, 500, 500)); + QWidget *const widget = &view; + QTest::qWaitForWindowShown(widget); + + // 1. Reference without tiling. + webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, false); + QPixmap referencePixmap(view.size()); + widget->render(&referencePixmap); + + // 2. With tiling. + webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true); + QPixmap viewWithTiling(view.size()); + widget->render(&viewWithTiling); + QApplication::processEvents(); + viewWithTiling.fill(); + widget->render(&viewWithTiling); + + QCOMPARE(referencePixmap.toImage(), viewWithTiling.toImage()); +} + +#if defined(ENABLE_TILED_BACKING_STORE) && ENABLE_TILED_BACKING_STORE +void tst_QGraphicsWebView::bug56929() +{ + // When rendering from tiles sychronous layout should not be triggered + // and scrollbars should be in sync with the size of the document in the displayed state. + + QGraphicsWebView* webView = new QGraphicsWebView(); + webView->setGeometry(QRectF(0.0, 0.0, 100.0, 100.0)); + QGraphicsView view(new QGraphicsScene()); + view.scene()->setParent(&view); + view.scene()->addItem(webView); + webView->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true); + QUrl url("qrc:///resources/56929.html"); + webView->load(url); + QVERIFY(waitForSignal(webView, SIGNAL(loadFinished(bool)))); + QStyleOptionGraphicsItem option; + option.exposedRect = webView->geometry(); + QImage img(option.exposedRect.width(), option.exposedRect.height(), QImage::Format_ARGB32_Premultiplied); + QPainter painter(&img); + // This will not paint anything as the tiles are not ready, yet. + webView->paint(&painter, &option); + QApplication::processEvents(); + webView->paint(&painter, &option); + QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(255, 255, 255, 255)); + painter.fillRect(option.exposedRect, Qt::black); + QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(0, 0, 0, 255)); + webView->page()->mainFrame()->evaluateJavaScript(QString("resizeDiv();")); + webView->paint(&painter, &option); + QCOMPARE(img.pixel(option.exposedRect.width() - 2, option.exposedRect.height() / 2), qRgba(255, 255, 255, 255)); +} +#endif + +void tst_QGraphicsWebView::microFocusCoordinates() +{ + QWebPage* page = new QWebPage; + QGraphicsWebView* webView = new QGraphicsWebView; + webView->setPage( page ); + QGraphicsView* view = new QGraphicsView; + QGraphicsScene* scene = new QGraphicsScene(view); + view->setScene(scene); + scene->addItem(webView); + view->setGeometry(QRect(0,0,500,500)); + + page->mainFrame()->setHtml("<html><body>" \ + "<input type='text' id='input1' style='font--family: serif' value='' maxlength='20'/><br>" \ + "<canvas id='canvas1' width='500' height='500'></canvas>" \ + "<input type='password'/><br>" \ + "<canvas id='canvas2' width='500' height='500'></canvas>" \ + "</body></html>"); + + page->mainFrame()->setFocus(); + + QVariant initialMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus); + QVERIFY(initialMicroFocus.isValid()); + + page->mainFrame()->scroll(0,300); + + QVariant currentMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus); + QVERIFY(currentMicroFocus.isValid()); + + QCOMPARE(initialMicroFocus.toRect().translated(QPoint(0,-300)), currentMicroFocus.toRect()); + + delete view; +} + +void tst_QGraphicsWebView::focusInputTypes() +{ + QWebPage* page = new QWebPage; + GraphicsWebView* webView = new GraphicsWebView; + webView->setPage( page ); + QGraphicsView* view = new QGraphicsView; + QGraphicsScene* scene = new QGraphicsScene(view); + view->setScene(scene); + scene->addItem(webView); + view->setGeometry(QRect(0,0,500,500)); + QCoreApplication::processEvents(); + QUrl url("qrc:///resources/input_types.html"); + page->mainFrame()->load(url); + page->mainFrame()->setFocus(); + + QVERIFY(waitForSignal(page, SIGNAL(loadFinished(bool)))); + + // 'text' type + webView->fireMouseClick(QPointF(20.0, 10.0)); +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) || defined(Q_OS_SYMBIAN) + QVERIFY(webView->inputMethodHints() & Qt::ImhNoAutoUppercase); + QVERIFY(webView->inputMethodHints() & Qt::ImhNoPredictiveText); +#else + QVERIFY(webView->inputMethodHints() == Qt::ImhNone); +#endif + + // 'password' field + webView->fireMouseClick(QPointF(20.0, 60.0)); + QVERIFY(webView->inputMethodHints() & Qt::ImhHiddenText); + + // 'tel' field + webView->fireMouseClick(QPointF(20.0, 110.0)); + QVERIFY(webView->inputMethodHints() & Qt::ImhDialableCharactersOnly); + + // 'number' field + webView->fireMouseClick(QPointF(20.0, 160.0)); + QVERIFY(webView->inputMethodHints() & Qt::ImhDigitsOnly); + + // 'email' field + webView->fireMouseClick(QPointF(20.0, 210.0)); + QVERIFY(webView->inputMethodHints() & Qt::ImhEmailCharactersOnly); + + // 'url' field + webView->fireMouseClick(QPointF(20.0, 260.0)); + QVERIFY(webView->inputMethodHints() & Qt::ImhUrlCharactersOnly); + + delete webView; + delete view; +} + +void tst_QGraphicsWebView::setPalette_data() +{ + QTest::addColumn<bool>("active"); + QTest::addColumn<bool>("background"); + QTest::newRow("activeBG") << true << true; + QTest::newRow("activeFG") << true << false; + QTest::newRow("inactiveBG") << false << true; + QTest::newRow("inactiveFG") << false << false; +} + +// Render a QGraphicsWebView to a QImage twice, each time with a different palette set, +// verify that images rendered are not the same, confirming WebCore usage of +// custom palette on selections. +void tst_QGraphicsWebView::setPalette() +{ + QString html = "<html><head></head>" + "<body>" + "Some text here" + "</body>" + "</html>"; + + QFETCH(bool, active); + QFETCH(bool, background); + + QWidget* activeView = 0; + + // Use controlView to manage active/inactive state of test views by raising + // or lowering their position in the window stack. + QGraphicsScene controlScene; + QGraphicsView controlView(&controlScene); + QGraphicsWebView controlWebView; + controlScene.addItem(&controlWebView); + controlWebView.setHtml(html); + controlWebView.setGeometry(QRectF(0, 0, 200, 200)); + + QGraphicsScene scene1; + QGraphicsView view1(&scene1); + view1.setSceneRect(0, 0, 300, 300); + QGraphicsWebView webView1; + webView1.setResizesToContents(true); + scene1.addItem(&webView1); + webView1.setFocus(); + + QPalette palette1; + QBrush brush1(Qt::red); + brush1.setStyle(Qt::SolidPattern); + if (active && background) { + // Rendered image must have red background on an active QGraphicsWebView. + palette1.setBrush(QPalette::Active, QPalette::Highlight, brush1); + } else if (active && !background) { + // Rendered image must have red foreground on an active QGraphicsWebView. + palette1.setBrush(QPalette::Active, QPalette::HighlightedText, brush1); + } else if (!active && background) { + // Rendered image must have red background on an inactive QGraphicsWebView. + palette1.setBrush(QPalette::Inactive, QPalette::Highlight, brush1); + } else if (!active && !background) { + // Rendered image must have red foreground on an inactive QGraphicsWebView. + palette1.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush1); + } + + webView1.setHtml(html); + view1.resize(webView1.page()->viewportSize()); + webView1.setPalette(palette1); + view1.show(); + + QVERIFY(webView1.palette() == palette1); + QVERIFY(webView1.page()->palette() == palette1); + + QTest::qWaitForWindowShown(&view1); + + if (!active) { + controlView.show(); + QTest::qWaitForWindowShown(&controlView); + activeView = &controlView; + controlView.activateWindow(); + } else { + view1.activateWindow(); + activeView = &view1; + } + + QTRY_COMPARE(QApplication::activeWindow(), activeView); + + webView1.page()->triggerAction(QWebPage::SelectAll); + + QImage img1(webView1.page()->viewportSize(), QImage::Format_ARGB32); + QPainter painter1(&img1); + webView1.page()->currentFrame()->render(&painter1); + painter1.end(); + view1.close(); + controlView.close(); + + QGraphicsScene scene2; + QGraphicsView view2(&scene2); + view2.setSceneRect(0, 0, 300, 300); + QGraphicsWebView webView2; + webView2.setResizesToContents(true); + scene2.addItem(&webView2); + webView2.setFocus(); + + QPalette palette2; + QBrush brush2(Qt::blue); + brush2.setStyle(Qt::SolidPattern); + if (active && background) { + // Rendered image must have blue background on an active QGraphicsWebView. + palette2.setBrush(QPalette::Active, QPalette::Highlight, brush2); + } else if (active && !background) { + // Rendered image must have blue foreground on an active QGraphicsWebView. + palette2.setBrush(QPalette::Active, QPalette::HighlightedText, brush2); + } else if (!active && background) { + // Rendered image must have blue background on an inactive QGraphicsWebView. + palette2.setBrush(QPalette::Inactive, QPalette::Highlight, brush2); + } else if (!active && !background) { + // Rendered image must have blue foreground on an inactive QGraphicsWebView. + palette2.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush2); + } + + webView2.setHtml(html); + view2.resize(webView2.page()->viewportSize()); + webView2.setPalette(palette2); + view2.show(); + + QTest::qWaitForWindowShown(&view2); + + if (!active) { + controlView.show(); + QTest::qWaitForWindowShown(&controlView); + activeView = &controlView; + controlView.activateWindow(); + } else { + view2.activateWindow(); + activeView = &view2; + } + + QTRY_COMPARE(QApplication::activeWindow(), activeView); + + webView2.page()->triggerAction(QWebPage::SelectAll); + + QImage img2(webView2.page()->viewportSize(), QImage::Format_ARGB32); + QPainter painter2(&img2); + webView2.page()->currentFrame()->render(&painter2); + painter2.end(); + + view2.close(); + controlView.close(); + + QVERIFY(img1 != img2); +} + +void tst_QGraphicsWebView::renderHints() +{ + QGraphicsWebView webView; + + // default is only text antialiasing + smooth pixmap transform + QVERIFY(!(webView.renderHints() & QPainter::Antialiasing)); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::Antialiasing, true); + QVERIFY(webView.renderHints() & QPainter::Antialiasing); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::Antialiasing, false); + QVERIFY(!(webView.renderHints() & QPainter::Antialiasing)); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::SmoothPixmapTransform, true); + QVERIFY(!(webView.renderHints() & QPainter::Antialiasing)); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::SmoothPixmapTransform, false); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(!(webView.renderHints() & QPainter::SmoothPixmapTransform)); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); +} + +class GraphicsView : public QGraphicsView { +public: + GraphicsView(); + QGraphicsWebView* m_webView; +}; + +#if defined(ENABLE_WEBGL) && ENABLE_WEBGL +bool compareImagesFuzzyPixelCount(const QImage& image1, const QImage& image2, qreal tolerance = 0.05) +{ + if (image1.size() != image2.size()) + return false; + + unsigned diffPixelCount = 0; + for (int row = 0; row < image1.size().width(); ++row) { + for (int column = 0; column < image1.size().height(); ++column) + if (image1.pixel(row, column) != image2.pixel(row, column)) + ++diffPixelCount; + } + + if (diffPixelCount > (image1.size().width() * image1.size().height()) * tolerance) + return false; + + return true; +} + +GraphicsView::GraphicsView() +{ + QGraphicsScene* const scene = new QGraphicsScene(this); + setScene(scene); + + m_webView = new QGraphicsWebView; + scene->addItem(m_webView); + + m_webView->page()->settings()->setAttribute(QWebSettings::WebGLEnabled, true); + m_webView->setResizesToContents(true); + + setFrameShape(QFrame::NoFrame); + setViewport(new QGLWidget); +} + +void tst_QGraphicsWebView::webglSoftwareFallbackVerticalOrientation() +{ + QSize canvasSize(100, 100); + QImage reference(canvasSize, QImage::Format_ARGB32); + reference.fill(0xFF00FF00); + { // Reference. + QPainter painter(&reference); + QPolygonF triangleUp; + triangleUp << QPointF(0, canvasSize.height()) + << QPointF(canvasSize.width(), canvasSize.height()) + << QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0); + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::red); + painter.drawPolygon(triangleUp); + } + + compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_up.html")), reference); +} + +void tst_QGraphicsWebView::webglSoftwareFallbackHorizontalOrientation() +{ + QSize canvasSize(100, 100); + QImage reference(canvasSize, QImage::Format_ARGB32); + reference.fill(0xFF00FF00); + { // Reference. + QPainter painter(&reference); + QPolygonF triangleUp; + triangleUp << QPointF(0, 0) + << QPointF(0, canvasSize.height()) + << QPointF(canvasSize.width() / 2.0, canvasSize.height() / 2.0); + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::red); + painter.drawPolygon(triangleUp); + } + + compareCanvasToImage(QUrl(QLatin1String("qrc:///resources/pointing_right.html")), reference); +} + +void tst_QGraphicsWebView::compareCanvasToImage(const QUrl& url, const QImage& reference) +{ + GraphicsView view; + view.show(); + QTest::qWaitForWindowShown(&view); + + QGraphicsWebView* const graphicsWebView = view.m_webView; + graphicsWebView->load(url); + QVERIFY(waitForSignal(graphicsWebView, SIGNAL(loadFinished(bool)))); + { // Force a render, to create the accelerated compositing tree. + QPixmap pixmap(view.size()); + QPainter painter(&pixmap); + view.render(&painter); + } + QApplication::syncX(); + + const QSize imageSize = reference.size(); + + QImage target(imageSize, QImage::Format_ARGB32); + { // Web page content. + QPainter painter(&target); + QRectF renderRect(0, 0, imageSize.width(), imageSize.height()); + view.scene()->render(&painter, renderRect, renderRect); + } + QVERIFY(compareImagesFuzzyPixelCount(target, reference, 0.01)); +} +#endif + +QTEST_MAIN(tst_QGraphicsWebView) + +#include "tst_qgraphicswebview.moc" diff --git a/Source/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.qrc b/Source/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.qrc new file mode 100644 index 0000000..f929fe7 --- /dev/null +++ b/Source/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/"> + <file>resources/input_types.html</file> + <file>resources/pointing_right.html</file> + <file>resources/pointing_up.html</file> + <file>resources/56929.html</file> + </qresource> +</RCC> diff --git a/Source/WebKit/qt/tests/qwebelement/qwebelement.pro b/Source/WebKit/qt/tests/qwebelement/qwebelement.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebelement/qwebelement.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebelement/resources/image.png b/Source/WebKit/qt/tests/qwebelement/resources/image.png Binary files differnew file mode 100644 index 0000000..8d70364 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebelement/resources/image.png diff --git a/Source/WebKit/qt/tests/qwebelement/resources/style.css b/Source/WebKit/qt/tests/qwebelement/resources/style.css new file mode 100644 index 0000000..2713dfd --- /dev/null +++ b/Source/WebKit/qt/tests/qwebelement/resources/style.css @@ -0,0 +1 @@ +#idP {color: black !important} diff --git a/Source/WebKit/qt/tests/qwebelement/resources/style2.css b/Source/WebKit/qt/tests/qwebelement/resources/style2.css new file mode 100644 index 0000000..6575dcb --- /dev/null +++ b/Source/WebKit/qt/tests/qwebelement/resources/style2.css @@ -0,0 +1 @@ +#idP {color: green ! important} diff --git a/Source/WebKit/qt/tests/qwebelement/tst_qwebelement.cpp b/Source/WebKit/qt/tests/qwebelement/tst_qwebelement.cpp new file mode 100644 index 0000000..9e9948a --- /dev/null +++ b/Source/WebKit/qt/tests/qwebelement/tst_qwebelement.cpp @@ -0,0 +1,1043 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 <QtTest/QtTest> +#include <qwebpage.h> +#include <qwidget.h> +#include <qwebview.h> +#include <qwebframe.h> +#include <qwebelement.h> +#include <util.h> +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QWebElement : public QObject +{ + Q_OBJECT + +public: + tst_QWebElement(); + virtual ~tst_QWebElement(); + +public slots: + void init(); + void cleanup(); + +private slots: + void textHtml(); + void simpleCollection(); + void attributes(); + void attributesNS(); + void listAttributes(); + void classes(); + void namespaceURI(); + void iteration(); + void nonConstIterator(); + void constIterator(); + void foreachManipulation(); + void emptyCollection(); + void appendCollection(); + void evaluateJavaScript(); + void documentElement(); + void frame(); + void style(); + void computedStyle(); + void appendAndPrepend(); + void insertBeforeAndAfter(); + void remove(); + void clear(); + void replaceWith(); + void encloseWith(); + void encloseContentsWith(); + void nullSelect(); + void firstChildNextSibling(); + void lastChildPreviousSibling(); + void hasSetFocus(); + void render(); + void addElementToHead(); + +private: + QWebView* m_view; + QWebPage* m_page; + QWebFrame* m_mainFrame; +}; + +tst_QWebElement::tst_QWebElement() +{ +} + +tst_QWebElement::~tst_QWebElement() +{ +} + +void tst_QWebElement::init() +{ + m_view = new QWebView(); + m_page = m_view->page(); + m_mainFrame = m_page->mainFrame(); +} + +void tst_QWebElement::cleanup() +{ + delete m_view; +} + +void tst_QWebElement::textHtml() +{ + QString html = "<head></head><body><p>test</p></body>"; + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement(); + QVERIFY(!body.isNull()); + + QCOMPARE(body.toPlainText(), QString("test")); + QCOMPARE(body.toPlainText(), m_mainFrame->toPlainText()); + + QCOMPARE(body.toInnerXml(), html); +} + +void tst_QWebElement::simpleCollection() +{ + QString html = "<body><p>first para</p><p>second para</p></body>"; + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement(); + + QWebElementCollection list = body.findAll("p"); + QCOMPARE(list.count(), 2); + QCOMPARE(list.at(0).toPlainText(), QString("first para")); + QCOMPARE(list.at(1).toPlainText(), QString("second para")); +} + +void tst_QWebElement::attributes() +{ + m_mainFrame->setHtml("<body><p>Test"); + QWebElement body = m_mainFrame->documentElement(); + + QVERIFY(!body.hasAttribute("title")); + QVERIFY(!body.hasAttributes()); + + body.setAttribute("title", "test title"); + + QVERIFY(body.hasAttributes()); + QVERIFY(body.hasAttribute("title")); + + QCOMPARE(body.attribute("title"), QString("test title")); + + body.removeAttribute("title"); + + QVERIFY(!body.hasAttribute("title")); + QVERIFY(!body.hasAttributes()); + + QCOMPARE(body.attribute("does-not-exist", "testvalue"), QString("testvalue")); +} + +void tst_QWebElement::attributesNS() +{ + QString content = "<html xmlns=\"http://www.w3.org/1999/xhtml\" " + "xmlns:svg=\"http://www.w3.org/2000/svg\">" + "<body><svg:svg id=\"foobar\" width=\"400px\" height=\"300px\">" + "</svg:svg></body></html>"; + + m_mainFrame->setContent(content.toUtf8(), "application/xhtml+xml"); + + QWebElement svg = m_mainFrame->findFirstElement("svg"); + QVERIFY(!svg.isNull()); + + QVERIFY(!svg.hasAttributeNS("http://www.w3.org/2000/svg", "foobar")); + QCOMPARE(svg.attributeNS("http://www.w3.org/2000/svg", "foobar", "defaultblah"), QString("defaultblah")); + svg.setAttributeNS("http://www.w3.org/2000/svg", "svg:foobar", "true"); + QVERIFY(svg.hasAttributeNS("http://www.w3.org/2000/svg", "foobar")); + QCOMPARE(svg.attributeNS("http://www.w3.org/2000/svg", "foobar", "defaultblah"), QString("true")); +} + +void tst_QWebElement::listAttributes() +{ + QString content = "<html xmlns=\"http://www.w3.org/1999/xhtml\" " + "xmlns:svg=\"http://www.w3.org/2000/svg\">" + "<body><svg:svg foo=\"\" svg:bar=\"\">" + "</svg:svg></body></html>"; + + m_mainFrame->setContent(content.toUtf8(), "application/xhtml+xml"); + + QWebElement svg = m_mainFrame->findFirstElement("svg"); + QVERIFY(!svg.isNull()); + + QVERIFY(svg.attributeNames().contains("foo")); + QVERIFY(svg.attributeNames("http://www.w3.org/2000/svg").contains("bar")); + + svg.setAttributeNS("http://www.w3.org/2000/svg", "svg:foobar", "true"); + QVERIFY(svg.attributeNames().contains("foo")); + QStringList attributes = svg.attributeNames("http://www.w3.org/2000/svg"); + QCOMPARE(attributes.size(), 2); + QVERIFY(attributes.contains("bar")); + QVERIFY(attributes.contains("foobar")); +} + +void tst_QWebElement::classes() +{ + m_mainFrame->setHtml("<body><p class=\"a b c d a c\">Test"); + + QWebElement body = m_mainFrame->documentElement(); + QCOMPARE(body.classes().count(), 0); + + QWebElement p = m_mainFrame->documentElement().findAll("p").at(0); + QStringList classes = p.classes(); + QCOMPARE(classes.count(), 4); + QCOMPARE(classes[0], QLatin1String("a")); + QCOMPARE(classes[1], QLatin1String("b")); + QCOMPARE(classes[2], QLatin1String("c")); + QCOMPARE(classes[3], QLatin1String("d")); + QVERIFY(p.hasClass("a")); + QVERIFY(p.hasClass("b")); + QVERIFY(p.hasClass("c")); + QVERIFY(p.hasClass("d")); + QVERIFY(!p.hasClass("e")); + + p.addClass("f"); + QVERIFY(p.hasClass("f")); + p.addClass("a"); + QCOMPARE(p.classes().count(), 5); + QVERIFY(p.hasClass("a")); + QVERIFY(p.hasClass("b")); + QVERIFY(p.hasClass("c")); + QVERIFY(p.hasClass("d")); + + p.toggleClass("a"); + QVERIFY(!p.hasClass("a")); + QVERIFY(p.hasClass("b")); + QVERIFY(p.hasClass("c")); + QVERIFY(p.hasClass("d")); + QVERIFY(p.hasClass("f")); + QCOMPARE(p.classes().count(), 4); + p.toggleClass("f"); + QVERIFY(!p.hasClass("f")); + QCOMPARE(p.classes().count(), 3); + p.toggleClass("a"); + p.toggleClass("f"); + QVERIFY(p.hasClass("a")); + QVERIFY(p.hasClass("f")); + QCOMPARE(p.classes().count(), 5); + + p.removeClass("f"); + QVERIFY(!p.hasClass("f")); + QCOMPARE(p.classes().count(), 4); + p.removeClass("d"); + QVERIFY(!p.hasClass("d")); + QCOMPARE(p.classes().count(), 3); + p.removeClass("not-exist"); + QCOMPARE(p.classes().count(), 3); + p.removeClass("c"); + QVERIFY(!p.hasClass("c")); + QCOMPARE(p.classes().count(), 2); + p.removeClass("b"); + QVERIFY(!p.hasClass("b")); + QCOMPARE(p.classes().count(), 1); + p.removeClass("a"); + QVERIFY(!p.hasClass("a")); + QCOMPARE(p.classes().count(), 0); + p.removeClass("foobar"); + QCOMPARE(p.classes().count(), 0); +} + +void tst_QWebElement::namespaceURI() +{ + QString content = "<html xmlns=\"http://www.w3.org/1999/xhtml\" " + "xmlns:svg=\"http://www.w3.org/2000/svg\">" + "<body><svg:svg id=\"foobar\" width=\"400px\" height=\"300px\">" + "</svg:svg></body></html>"; + + m_mainFrame->setContent(content.toUtf8(), "application/xhtml+xml"); + QWebElement body = m_mainFrame->documentElement(); + QCOMPARE(body.namespaceUri(), QLatin1String("http://www.w3.org/1999/xhtml")); + + QWebElement svg = body.findAll("*#foobar").at(0); + QCOMPARE(svg.prefix(), QLatin1String("svg")); + QCOMPARE(svg.localName(), QLatin1String("svg")); + QCOMPARE(svg.tagName(), QLatin1String("svg:svg")); + QCOMPARE(svg.namespaceUri(), QLatin1String("http://www.w3.org/2000/svg")); + +} + +void tst_QWebElement::iteration() +{ + QString html = "<body><p>first para</p><p>second para</p></body>"; + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement(); + + QWebElementCollection paras = body.findAll("p"); + QList<QWebElement> referenceList = paras.toList(); + + QList<QWebElement> foreachList; + foreach(QWebElement p, paras) { + foreachList.append(p); + } + QVERIFY(foreachList.count() == 2); + QCOMPARE(foreachList.count(), referenceList.count()); + QCOMPARE(foreachList.at(0), referenceList.at(0)); + QCOMPARE(foreachList.at(1), referenceList.at(1)); + + QList<QWebElement> forLoopList; + for (int i = 0; i < paras.count(); ++i) { + forLoopList.append(paras.at(i)); + } + QVERIFY(foreachList.count() == 2); + QCOMPARE(foreachList.count(), referenceList.count()); + QCOMPARE(foreachList.at(0), referenceList.at(0)); + QCOMPARE(foreachList.at(1), referenceList.at(1)); + + for (int i = 0; i < paras.count(); ++i) { + QCOMPARE(paras.at(i), paras[i]); + } + + QCOMPARE(paras.at(0), paras.first()); + QCOMPARE(paras.at(1), paras.last()); +} + +void tst_QWebElement::nonConstIterator() +{ + QString html = "<body><p>first para</p><p>second para</p></body>"; + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement(); + QWebElementCollection paras = body.findAll("p"); + + QWebElementCollection::iterator it = paras.begin(); + QCOMPARE(*it, paras.at(0)); + ++it; + (*it).encloseWith("<div>"); + QCOMPARE(*it, paras.at(1)); + ++it; + QCOMPARE(it, paras.end()); +} + +void tst_QWebElement::constIterator() +{ + QString html = "<body><p>first para</p><p>second para</p></body>"; + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement(); + const QWebElementCollection paras = body.findAll("p"); + + QWebElementCollection::const_iterator it = paras.begin(); + QCOMPARE(*it, paras.at(0)); + ++it; + QCOMPARE(*it, paras.at(1)); + ++it; + QCOMPARE(it, paras.end()); +} + +void tst_QWebElement::foreachManipulation() +{ + QString html = "<body><p>first para</p><p>second para</p></body>"; + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement(); + + foreach(QWebElement p, body.findAll("p")) { + p.setInnerXml("<div>foo</div><div>bar</div>"); + } + + QCOMPARE(body.findAll("div").count(), 4); +} + +void tst_QWebElement::emptyCollection() +{ + QWebElementCollection emptyCollection; + QCOMPARE(emptyCollection.count(), 0); +} + +void tst_QWebElement::appendCollection() +{ + QString html = "<body><span class='a'>aaa</span><p>first para</p><div>foo</div>" + "<span class='b'>bbb</span><p>second para</p><div>bar</div></body>"; + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement(); + + QWebElementCollection collection = body.findAll("p"); + QCOMPARE(collection.count(), 2); + + collection.append(body.findAll("div")); + QCOMPARE(collection.count(), 4); + + collection += body.findAll("span.a"); + QCOMPARE(collection.count(), 5); + + QWebElementCollection all = collection + body.findAll("span.b"); + QCOMPARE(all.count(), 6); + QCOMPARE(collection.count(), 5); + + all += collection; + QCOMPARE(all.count(), 11); + + QCOMPARE(collection.count(), 5); + QWebElementCollection test; + test.append(collection); + QCOMPARE(test.count(), 5); + test.append(QWebElementCollection()); + QCOMPARE(test.count(), 5); +} + +void tst_QWebElement::evaluateJavaScript() +{ + QVariant result; + m_mainFrame->setHtml("<body><p>test"); + QWebElement para = m_mainFrame->findFirstElement("p"); + + result = para.evaluateJavaScript("this.tagName"); + QVERIFY(result.isValid()); + QVERIFY(result.type() == QVariant::String); + QCOMPARE(result.toString(), QLatin1String("P")); + + result = para.evaluateJavaScript("this.hasAttributes()"); + QVERIFY(result.isValid()); + QVERIFY(result.type() == QVariant::Bool); + QVERIFY(!result.toBool()); + + para.evaluateJavaScript("this.setAttribute('align', 'left');"); + QCOMPARE(para.attribute("align"), QLatin1String("left")); + + result = para.evaluateJavaScript("this.hasAttributes()"); + QVERIFY(result.isValid()); + QVERIFY(result.type() == QVariant::Bool); + QVERIFY(result.toBool()); +} + +void tst_QWebElement::documentElement() +{ + m_mainFrame->setHtml("<body><p>Test"); + + QWebElement para = m_mainFrame->documentElement().findAll("p").at(0); + QVERIFY(para.parent().parent() == m_mainFrame->documentElement()); + QVERIFY(para.document() == m_mainFrame->documentElement()); +} + +void tst_QWebElement::frame() +{ + m_mainFrame->setHtml("<body><p>test"); + + QWebElement doc = m_mainFrame->documentElement(); + QVERIFY(doc.webFrame() == m_mainFrame); + + m_mainFrame->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," + "<p>frame1\">" + "<frame src=\"data:text/html,<p>frame2\"></frameset>")); + + waitForSignal(m_page, SIGNAL(loadFinished(bool))); + + QCOMPARE(m_mainFrame->childFrames().count(), 2); + + QWebFrame* firstFrame = m_mainFrame->childFrames().at(0); + QWebFrame* secondFrame = m_mainFrame->childFrames().at(1); + + QCOMPARE(firstFrame->toPlainText(), QString("frame1")); + QCOMPARE(secondFrame->toPlainText(), QString("frame2")); + + QWebElement firstPara = firstFrame->documentElement().findAll("p").at(0); + QWebElement secondPara = secondFrame->documentElement().findAll("p").at(0); + + QVERIFY(firstPara.webFrame() == firstFrame); + QVERIFY(secondPara.webFrame() == secondFrame); +} + +void tst_QWebElement::style() +{ + QString html = "<head>" + "<style type='text/css'>" + "p { color: green !important }" + "#idP { color: red }" + ".classP { color : yellow ! important }" + "</style>" + "</head>" + "<body>" + "<p id='idP' class='classP' style='color: blue;'>some text</p>" + "</body>"; + + m_mainFrame->setHtml(html); + + QWebElement p = m_mainFrame->documentElement().findAll("p").at(0); + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("blue")); + QVERIFY(p.styleProperty("cursor", QWebElement::InlineStyle).isEmpty()); + + p.setStyleProperty("color", "red"); + p.setStyleProperty("cursor", "auto"); + + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("red")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("yellow")); + QCOMPARE(p.styleProperty("cursor", QWebElement::InlineStyle), QLatin1String("auto")); + + p.setStyleProperty("color", "green !important"); + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("green")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("green")); + + p.setStyleProperty("color", "blue"); + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("blue")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("green")); + + p.setStyleProperty("color", "blue !important"); + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("blue")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("blue")); + + QString html2 = "<head>" + "<style type='text/css'>" + "p { color: green }" + "#idP { color: red }" + ".classP { color: yellow }" + "</style>" + "</head>" + "<body>" + "<p id='idP' class='classP' style='color: blue;'>some text</p>" + "</body>"; + + m_mainFrame->setHtml(html2); + p = m_mainFrame->documentElement().findAll("p").at(0); + + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("blue")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("blue")); + + QString html3 = "<head>" + "<style type='text/css'>" + "p { color: green !important }" + "#idP { color: red !important}" + ".classP { color: yellow !important}" + "</style>" + "</head>" + "<body>" + "<p id='idP' class='classP' style='color: blue !important;'>some text</p>" + "</body>"; + + m_mainFrame->setHtml(html3); + p = m_mainFrame->documentElement().findAll("p").at(0); + + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("blue")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("blue")); + + QString html5 = "<head>" + "<style type='text/css'>" + "p { color: green }" + "#idP { color: red }" + ".classP { color: yellow }" + "</style>" + "</head>" + "<body>" + "<p id='idP' class='classP'>some text</p>" + "</body>"; + + m_mainFrame->setHtml(html5); + p = m_mainFrame->documentElement().findAll("p").at(0); + + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red")); + + QString html6 = "<head>" + "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" + "<style type='text/css'>" + "p { color: green }" + "#idP { color: red }" + ".classP { color: yellow ! important}" + "</style>" + "</head>" + "<body>" + "<p id='idP' class='classP' style='color: blue;'>some text</p>" + "</body>"; + + // in few seconds, the CSS should be completey loaded + m_mainFrame->setHtml(html6); + waitForSignal(m_page, SIGNAL(loadFinished(bool)), 200); + + p = m_mainFrame->documentElement().findAll("p").at(0); + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("blue")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("black")); + + QString html7 = "<head>" + "<style type='text/css'>" + "@import url(qrc:/style2.css);" + "</style>" + "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" + "</head>" + "<body>" + "<p id='idP' style='color: blue;'>some text</p>" + "</body>"; + + // in few seconds, the style should be completey loaded + m_mainFrame->setHtml(html7); + waitForSignal(m_page, SIGNAL(loadFinished(bool)), 200); + + p = m_mainFrame->documentElement().findAll("p").at(0); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("black")); + + QString html8 = "<body><p>some text</p></body>"; + + m_mainFrame->setHtml(html8); + p = m_mainFrame->documentElement().findAll("p").at(0); + + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("")); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("")); +} + +void tst_QWebElement::computedStyle() +{ + QString html = "<body><p>some text</p></body>"; + m_mainFrame->setHtml(html); + + QWebElement p = m_mainFrame->documentElement().findAll("p").at(0); + QCOMPARE(p.styleProperty("cursor", QWebElement::ComputedStyle), QLatin1String("auto")); + QVERIFY(!p.styleProperty("cursor", QWebElement::ComputedStyle).isEmpty()); + QVERIFY(p.styleProperty("cursor", QWebElement::InlineStyle).isEmpty()); + + p.setStyleProperty("cursor", "text"); + p.setStyleProperty("color", "red"); + + QCOMPARE(p.styleProperty("cursor", QWebElement::ComputedStyle), QLatin1String("text")); + QCOMPARE(p.styleProperty("color", QWebElement::ComputedStyle), QLatin1String("rgb(255, 0, 0)")); + QCOMPARE(p.styleProperty("color", QWebElement::InlineStyle), QLatin1String("red")); +} + +void tst_QWebElement::appendAndPrepend() +{ + QString html = "<body>" + "<p>" + "foo" + "</p>" + "<p>" + "bar" + "</p>" + "</body>"; + + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement().findFirst("body"); + + QCOMPARE(body.findAll("p").count(), 2); + body.appendInside(body.findFirst("p")); + QCOMPARE(body.findAll("p").count(), 2); + QCOMPARE(body.findFirst("p").toPlainText(), QString("bar")); + QCOMPARE(body.findAll("p").last().toPlainText(), QString("foo")); + + body.appendInside(body.findFirst("p").clone()); + QCOMPARE(body.findAll("p").count(), 3); + QCOMPARE(body.findFirst("p").toPlainText(), QString("bar")); + QCOMPARE(body.findAll("p").last().toPlainText(), QString("bar")); + + body.prependInside(body.findAll("p").at(1).clone()); + QCOMPARE(body.findAll("p").count(), 4); + QCOMPARE(body.findFirst("p").toPlainText(), QString("foo")); + + body.findFirst("p").appendInside("<div>booyakasha</div>"); + QCOMPARE(body.findAll("p div").count(), 1); + QCOMPARE(body.findFirst("p div").toPlainText(), QString("booyakasha")); + + body.findFirst("div").prependInside("<code>yepp</code>"); + QCOMPARE(body.findAll("p div code").count(), 1); + QCOMPARE(body.findFirst("p div code").toPlainText(), QString("yepp")); +} + +void tst_QWebElement::insertBeforeAndAfter() +{ + QString html = "<body>" + "<p>" + "foo" + "</p>" + "<div>" + "yeah" + "</div>" + "<p>" + "bar" + "</p>" + "</body>"; + + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement().findFirst("body"); + QWebElement div = body.findFirst("div"); + + QCOMPARE(body.findAll("p").count(), 2); + QCOMPARE(body.findAll("div").count(), 1); + + div.prependOutside(body.findAll("p").last().clone()); + QCOMPARE(body.findAll("p").count(), 3); + QCOMPARE(body.findAll("p").at(0).toPlainText(), QString("foo")); + QCOMPARE(body.findAll("p").at(1).toPlainText(), QString("bar")); + QCOMPARE(body.findAll("p").at(2).toPlainText(), QString("bar")); + + div.appendOutside(body.findFirst("p").clone()); + QCOMPARE(body.findAll("p").count(), 4); + QCOMPARE(body.findAll("p").at(0).toPlainText(), QString("foo")); + QCOMPARE(body.findAll("p").at(1).toPlainText(), QString("bar")); + QCOMPARE(body.findAll("p").at(2).toPlainText(), QString("foo")); + QCOMPARE(body.findAll("p").at(3).toPlainText(), QString("bar")); + + div.prependOutside("<span>hey</span>"); + QCOMPARE(body.findAll("span").count(), 1); + + div.appendOutside("<span>there</span>"); + QCOMPARE(body.findAll("span").count(), 2); + QCOMPARE(body.findAll("span").at(0).toPlainText(), QString("hey")); + QCOMPARE(body.findAll("span").at(1).toPlainText(), QString("there")); +} + +void tst_QWebElement::remove() +{ + QString html = "<body>" + "<p>" + "foo" + "</p>" + "<div>" + "<p>yeah</p>" + "</div>" + "<p>" + "bar" + "</p>" + "</body>"; + + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement().findFirst("body"); + + QCOMPARE(body.findAll("div").count(), 1); + QCOMPARE(body.findAll("p").count(), 3); + + QWebElement div = body.findFirst("div"); + div.takeFromDocument(); + + QCOMPARE(div.isNull(), false); + QCOMPARE(body.findAll("div").count(), 0); + QCOMPARE(body.findAll("p").count(), 2); + + body.appendInside(div); + + QCOMPARE(body.findAll("div").count(), 1); + QCOMPARE(body.findAll("p").count(), 3); +} + +void tst_QWebElement::clear() +{ + QString html = "<body>" + "<p>" + "foo" + "</p>" + "<div>" + "<p>yeah</p>" + "</div>" + "<p>" + "bar" + "</p>" + "</body>"; + + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement().findFirst("body"); + + QCOMPARE(body.findAll("div").count(), 1); + QCOMPARE(body.findAll("p").count(), 3); + body.findFirst("div").removeAllChildren(); + QCOMPARE(body.findAll("div").count(), 1); + QCOMPARE(body.findAll("p").count(), 2); +} + + +void tst_QWebElement::replaceWith() +{ + QString html = "<body>" + "<p>" + "foo" + "</p>" + "<div>" + "yeah" + "</div>" + "<p>" + "<span>haba</span>" + "</p>" + "</body>"; + + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement().findFirst("body"); + + QCOMPARE(body.findAll("div").count(), 1); + QCOMPARE(body.findAll("span").count(), 1); + body.findFirst("div").replace(body.findFirst("span").clone()); + QCOMPARE(body.findAll("div").count(), 0); + QCOMPARE(body.findAll("span").count(), 2); + QCOMPARE(body.findAll("p").count(), 2); + + body.findFirst("span").replace("<p><code>wow</code></p>"); + QCOMPARE(body.findAll("p").count(), 3); + QCOMPARE(body.findAll("p code").count(), 1); + QCOMPARE(body.findFirst("p code").toPlainText(), QString("wow")); +} + +void tst_QWebElement::encloseContentsWith() +{ + QString html = "<body>" + "<div>" + "<i>" + "yeah" + "</i>" + "<i>" + "hello" + "</i>" + "</div>" + "<p>" + "<span>foo</span>" + "<span>bar</span>" + "</p>" + "<u></u>" + "<b></b>" + "<em>hey</em>" + "</body>"; + + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement().findFirst("body"); + + body.findFirst("p").encloseContentsWith(body.findFirst("b")); + QCOMPARE(body.findAll("p b span").count(), 2); + QCOMPARE(body.findFirst("p b span").toPlainText(), QString("foo")); + + body.findFirst("u").encloseContentsWith("<i></i>"); + QCOMPARE(body.findAll("u i").count(), 1); + QCOMPARE(body.findFirst("u i").toPlainText(), QString()); + + body.findFirst("div").encloseContentsWith("<span></span>"); + QCOMPARE(body.findAll("div span i").count(), 2); + QCOMPARE(body.findFirst("div span i").toPlainText(), QString("yeah")); + + QString snippet = "" + "<table>" + "<tbody>" + "<tr>" + "<td></td>" + "<td></td>" + "</tr>" + "<tr>" + "<td></td>" + "<td></td>" + "<tr>" + "</tbody>" + "</table>"; + + body.findFirst("em").encloseContentsWith(snippet); + QCOMPARE(body.findFirst("em table tbody tr td").toPlainText(), QString("hey")); +} + +void tst_QWebElement::encloseWith() +{ + QString html = "<body>" + "<p>" + "foo" + "</p>" + "<div>" + "yeah" + "</div>" + "<p>" + "<span>bar</span>" + "</p>" + "<em>hey</em>" + "<h1>hello</h1>" + "</body>"; + + m_mainFrame->setHtml(html); + QWebElement body = m_mainFrame->documentElement().findFirst("body"); + + body.findFirst("p").encloseWith("<br>"); + QCOMPARE(body.findAll("br").count(), 0); + + QCOMPARE(body.findAll("div").count(), 1); + body.findFirst("div").encloseWith(body.findFirst("span").clone()); + QCOMPARE(body.findAll("div").count(), 1); + QCOMPARE(body.findAll("span").count(), 2); + QCOMPARE(body.findAll("p").count(), 2); + + body.findFirst("div").encloseWith("<code></code>"); + QCOMPARE(body.findAll("code").count(), 1); + QCOMPARE(body.findAll("code div").count(), 1); + QCOMPARE(body.findFirst("code div").toPlainText(), QString("yeah")); + + QString snippet = "" + "<table>" + "<tbody>" + "<tr>" + "<td></td>" + "<td></td>" + "</tr>" + "<tr>" + "<td></td>" + "<td></td>" + "<tr>" + "</tbody>" + "</table>"; + + body.findFirst("em").encloseWith(snippet); + QCOMPARE(body.findFirst("table tbody tr td em").toPlainText(), QString("hey")); +} + +void tst_QWebElement::nullSelect() +{ + m_mainFrame->setHtml("<body><p>Test"); + + QWebElementCollection collection = m_mainFrame->findAllElements("invalid{syn(tax;;%#$f223e>>"); + QVERIFY(collection.count() == 0); +} + +void tst_QWebElement::firstChildNextSibling() +{ + m_mainFrame->setHtml("<body><!--comment--><p>Test</p><!--another comment--><table>"); + + QWebElement body = m_mainFrame->findFirstElement("body"); + QVERIFY(!body.isNull()); + QWebElement p = body.firstChild(); + QVERIFY(!p.isNull()); + QCOMPARE(p.tagName(), QString("P")); + QWebElement table = p.nextSibling(); + QVERIFY(!table.isNull()); + QCOMPARE(table.tagName(), QString("TABLE")); + QVERIFY(table.nextSibling().isNull()); +} + +void tst_QWebElement::lastChildPreviousSibling() +{ + m_mainFrame->setHtml("<body><!--comment--><p>Test</p><!--another comment--><table>"); + + QWebElement body = m_mainFrame->findFirstElement("body"); + QVERIFY(!body.isNull()); + QWebElement table = body.lastChild(); + QVERIFY(!table.isNull()); + QCOMPARE(table.tagName(), QString("TABLE")); + QWebElement p = table.previousSibling(); + QVERIFY(!p.isNull()); + QCOMPARE(p.tagName(), QString("P")); + QVERIFY(p.previousSibling().isNull()); +} + +void tst_QWebElement::hasSetFocus() +{ + m_mainFrame->setHtml("<html><body>" \ + "<input type='text' id='input1'/>" \ + "<br>"\ + "<input type='text' id='input2'/>" \ + "</body></html>"); + + QWebElementCollection inputs = m_mainFrame->documentElement().findAll("input"); + QWebElement input1 = inputs.at(0); + input1.setFocus(); + QVERIFY(input1.hasFocus()); + + QWebElement input2 = inputs.at(1); + input2.setFocus(); + QVERIFY(!input1.hasFocus()); + QVERIFY(input2.hasFocus()); +} + +void tst_QWebElement::render() +{ + QString html( "<html>" + "<head><style>" + "body, iframe { margin: 0px; border: none; background: white; }" + "</style></head>" + "<body><table width='300px' height='300px' border='1'>" + "<tr>" + "<td>test" + "</td>" + "<td><img src='qrc:///image.png'>" + "</td>" + "</tr>" + "</table>" + "</body>" + "</html>" + ); + + QWebPage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + page.mainFrame()->setHtml(html); + + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(loadSpy.count(), 1); + + QSize size = page.mainFrame()->contentsSize(); + page.setViewportSize(size); + + QWebElementCollection imgs = page.mainFrame()->findAllElements("img"); + QCOMPARE(imgs.count(), 1); + + QImage resource(":/image.png"); + QRect imageRect(0, 0, resource.width(), resource.height()); + + QImage testImage(resource.width(), resource.height(), QImage::Format_ARGB32); + QPainter painter0(&testImage); + painter0.fillRect(imageRect, Qt::white); + // render() uses pixmaps internally, and pixmaps might have bit depths + // other than 32, giving different pixel values due to rounding. + QPixmap pix = QPixmap::fromImage(resource); + painter0.drawPixmap(0, 0, pix); + painter0.end(); + + QImage image1(resource.width(), resource.height(), QImage::Format_ARGB32); + QPainter painter1(&image1); + painter1.fillRect(imageRect, Qt::white); + imgs[0].render(&painter1); + painter1.end(); + + QVERIFY(image1 == testImage); + + // render image 2nd time to make sure that cached rendering works fine + QImage image2(resource.width(), resource.height(), QImage::Format_ARGB32); + QPainter painter2(&image2); + painter2.fillRect(imageRect, Qt::white); + imgs[0].render(&painter2); + painter2.end(); + + QVERIFY(image2 == testImage); + + // compare table rendered through QWebElement::render to whole page table rendering + QRect tableRect(0, 0, 300, 300); + QWebElementCollection tables = page.mainFrame()->findAllElements("table"); + QCOMPARE(tables.count(), 1); + + QImage image3(300, 300, QImage::Format_ARGB32); + QPainter painter3(&image3); + painter3.fillRect(tableRect, Qt::white); + tables[0].render(&painter3); + painter3.end(); + + QImage image4(300, 300, QImage::Format_ARGB32); + QPainter painter4(&image4); + page.mainFrame()->render(&painter4, tableRect); + painter4.end(); + + QVERIFY(image3 == image4); + + // Chunked render test reuses page rendered in image4 in previous test + const int chunkHeight = tableRect.height(); + const int chunkWidth = tableRect.width() / 3; + QImage chunk(chunkWidth, chunkHeight, QImage::Format_ARGB32); + QRect chunkRect(0, 0, chunkWidth, chunkHeight); + for (int x = 0; x < tableRect.width(); x += chunkWidth) { + QPainter painter(&chunk); + painter.fillRect(chunkRect, Qt::white); + QRect chunkPaintRect(x, 0, chunkWidth, chunkHeight); + tables[0].render(&painter, chunkPaintRect); + painter.end(); + + QVERIFY(chunk == image4.copy(chunkPaintRect)); + } +} + +void tst_QWebElement::addElementToHead() +{ + m_mainFrame->setHtml("<html><head></head><body></body></html>"); + QWebElement head = m_mainFrame->findFirstElement("head"); + QVERIFY(!head.isNull()); + QString append = "<script type=\"text/javascript\">var t = 0;</script>"; + head.appendInside(append); + QCOMPARE(head.toInnerXml(), append); +} + +QTEST_MAIN(tst_QWebElement) +#include "tst_qwebelement.moc" diff --git a/Source/WebKit/qt/tests/qwebelement/tst_qwebelement.qrc b/Source/WebKit/qt/tests/qwebelement/tst_qwebelement.qrc new file mode 100644 index 0000000..7384c76 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebelement/tst_qwebelement.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> +<file alias="style.css">resources/style.css</file> +<file alias="style2.css">resources/style2.css</file> +<file alias="image.png">resources/image.png</file> +</qresource> +</RCC> diff --git a/Source/WebKit/qt/tests/qwebframe/qwebframe.pro b/Source/WebKit/qt/tests/qwebframe/qwebframe.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/qwebframe.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebframe/resources/image.png b/Source/WebKit/qt/tests/qwebframe/resources/image.png Binary files differnew file mode 100644 index 0000000..8d70364 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/resources/image.png diff --git a/Source/WebKit/qt/tests/qwebframe/resources/style.css b/Source/WebKit/qt/tests/qwebframe/resources/style.css new file mode 100644 index 0000000..c05b747 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/resources/style.css @@ -0,0 +1 @@ +#idP {color: red !important} diff --git a/Source/WebKit/qt/tests/qwebframe/resources/test1.html b/Source/WebKit/qt/tests/qwebframe/resources/test1.html new file mode 100644 index 0000000..b323f96 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/resources/test1.html @@ -0,0 +1 @@ +<html><body><p>Some text 1</p></body></html> diff --git a/Source/WebKit/qt/tests/qwebframe/resources/test2.html b/Source/WebKit/qt/tests/qwebframe/resources/test2.html new file mode 100644 index 0000000..63ac1f6 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/resources/test2.html @@ -0,0 +1 @@ +<html><body> <p>Some text 2</p></body></html> diff --git a/Source/WebKit/qt/tests/qwebframe/resources/testiframe.html b/Source/WebKit/qt/tests/qwebframe/resources/testiframe.html new file mode 100644 index 0000000..203d3d3 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/resources/testiframe.html @@ -0,0 +1,54 @@ +</html>
+<html>
+<head>
+<title></title>
+<style type="text/css">
+<!--
+#header {
+ background: #0f0;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 800px;
+ height: 100px;
+}
+#content1 {
+ background: #ff0;
+ position: absolute;
+ top: 101px;
+ left: 0px;
+ width: 400px;
+ height: 400px;
+ overflow: scroll;
+}
+#content2 {
+ background: #ff7;
+ position: absolute;
+ top: 101px;
+ left: 401px;
+ width: 400px;
+ height: 400px;
+}
+#footer {
+ background: #0f0;
+ position: absolute;
+ top: 502px;
+ left: 0px;
+ width: 800px;
+ height: 200px;
+}
+-->
+</style>
+</head>
+<body>
+<div id="header"></div>
+<div id="content1">You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.
+You can use the overflow property when you want to have better control of the layout. Try to change the overflow property to: visible, hidden, auto, or inherit and see what happens. The default value is visible.</div>
+<iframe id="content2" name="control" src="testiframe2.html"> </iframe>
+<div id="footer"></div>
+</body>
+</html>
\ No newline at end of file diff --git a/Source/WebKit/qt/tests/qwebframe/resources/testiframe2.html b/Source/WebKit/qt/tests/qwebframe/resources/testiframe2.html new file mode 100644 index 0000000..0d3a22f --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/resources/testiframe2.html @@ -0,0 +1,21 @@ +</html>
+<html>
+<head>
+<title></title>
+<style type="text/css">
+<!--
+#content {
+ background: #fff;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 800px;
+ height: 800px;
+}
+-->
+</style>
+</head>
+<body>
+<div id="content"> </div>
+</body>
+</html>
\ No newline at end of file diff --git a/Source/WebKit/qt/tests/qwebframe/tst_qwebframe.cpp b/Source/WebKit/qt/tests/qwebframe/tst_qwebframe.cpp new file mode 100644 index 0000000..3b9324d --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/tst_qwebframe.cpp @@ -0,0 +1,3645 @@ +/* + Copyright (C) 2008,2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 <QtTest/QtTest> + +#include <qwebpage.h> +#include <qwebelement.h> +#include <qwidget.h> +#include <qwebview.h> +#include <qwebframe.h> +#include <qwebhistory.h> +#include <QAbstractItemView> +#include <QApplication> +#include <QComboBox> +#include <QPaintEngine> +#include <QPicture> +#include <QRegExp> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QTextCodec> +#ifndef QT_NO_OPENSSL +#include <qsslerror.h> +#endif +#include "../util.h" + +struct CustomType { + QString string; +}; +Q_DECLARE_METATYPE(CustomType) + +Q_DECLARE_METATYPE(QBrush*) +Q_DECLARE_METATYPE(QObjectList) +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(Qt::BrushStyle) +Q_DECLARE_METATYPE(QVariantList) +Q_DECLARE_METATYPE(QVariantMap) + +class MyQObject : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty) + Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty) + Q_PROPERTY(QVariantList variantListProperty READ variantListProperty WRITE setVariantListProperty) + Q_PROPERTY(QVariantMap variantMapProperty READ variantMapProperty WRITE setVariantMapProperty) + Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty) + Q_PROPERTY(QStringList stringListProperty READ stringListProperty WRITE setStringListProperty) + Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty) + Q_PROPERTY(QBrush brushProperty READ brushProperty WRITE setBrushProperty) + Q_PROPERTY(double hiddenProperty READ hiddenProperty WRITE setHiddenProperty SCRIPTABLE false) + Q_PROPERTY(int writeOnlyProperty WRITE setWriteOnlyProperty) + Q_PROPERTY(int readOnlyProperty READ readOnlyProperty) + Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) + Q_PROPERTY(CustomType propWithCustomType READ propWithCustomType WRITE setPropWithCustomType) + Q_PROPERTY(QWebElement webElementProperty READ webElementProperty WRITE setWebElementProperty) + Q_PROPERTY(QObject* objectStarProperty READ objectStarProperty WRITE setObjectStarProperty) + Q_ENUMS(Policy Strategy) + Q_FLAGS(Ability) + +public: + enum Policy { + FooPolicy = 0, + BarPolicy, + BazPolicy + }; + + enum Strategy { + FooStrategy = 10, + BarStrategy, + BazStrategy + }; + + enum AbilityFlag { + NoAbility = 0x000, + FooAbility = 0x001, + BarAbility = 0x080, + BazAbility = 0x200, + AllAbility = FooAbility | BarAbility | BazAbility + }; + + Q_DECLARE_FLAGS(Ability, AbilityFlag) + + MyQObject(QObject* parent = 0) + : QObject(parent), + m_intValue(123), + m_variantValue(QLatin1String("foo")), + m_variantListValue(QVariantList() << QVariant(123) << QVariant(QLatin1String("foo"))), + m_stringValue(QLatin1String("bar")), + m_stringListValue(QStringList() << QLatin1String("zig") << QLatin1String("zag")), + m_brushValue(QColor(10, 20, 30, 40)), + m_hiddenValue(456.0), + m_writeOnlyValue(789), + m_readOnlyValue(987), + m_objectStar(0), + m_qtFunctionInvoked(-1) + { + m_variantMapValue.insert("a", QVariant(123)); + m_variantMapValue.insert("b", QVariant(QLatin1String("foo"))); + m_variantMapValue.insert("c", QVariant::fromValue<QObject*>(this)); + } + + ~MyQObject() { } + + int intProperty() const { + return m_intValue; + } + void setIntProperty(int value) { + m_intValue = value; + } + + QVariant variantProperty() const { + return m_variantValue; + } + void setVariantProperty(const QVariant &value) { + m_variantValue = value; + } + + QVariantList variantListProperty() const { + return m_variantListValue; + } + void setVariantListProperty(const QVariantList &value) { + m_variantListValue = value; + } + + QVariantMap variantMapProperty() const { + return m_variantMapValue; + } + void setVariantMapProperty(const QVariantMap &value) { + m_variantMapValue = value; + } + + QString stringProperty() const { + return m_stringValue; + } + void setStringProperty(const QString &value) { + m_stringValue = value; + } + + QStringList stringListProperty() const { + return m_stringListValue; + } + void setStringListProperty(const QStringList &value) { + m_stringListValue = value; + } + + QByteArray byteArrayProperty() const { + return m_byteArrayValue; + } + void setByteArrayProperty(const QByteArray &value) { + m_byteArrayValue = value; + } + + QBrush brushProperty() const { + return m_brushValue; + } + Q_INVOKABLE void setBrushProperty(const QBrush &value) { + m_brushValue = value; + } + + double hiddenProperty() const { + return m_hiddenValue; + } + void setHiddenProperty(double value) { + m_hiddenValue = value; + } + + int writeOnlyProperty() const { + return m_writeOnlyValue; + } + void setWriteOnlyProperty(int value) { + m_writeOnlyValue = value; + } + + int readOnlyProperty() const { + return m_readOnlyValue; + } + + QKeySequence shortcut() const { + return m_shortcut; + } + void setShortcut(const QKeySequence &seq) { + m_shortcut = seq; + } + + QWebElement webElementProperty() const { + return m_webElement; + } + + void setWebElementProperty(const QWebElement& element) { + m_webElement = element; + } + + CustomType propWithCustomType() const { + return m_customType; + } + void setPropWithCustomType(const CustomType &c) { + m_customType = c; + } + + QObject* objectStarProperty() const { + return m_objectStar; + } + + void setObjectStarProperty(QObject* object) { + m_objectStar = object; + } + + + int qtFunctionInvoked() const { + return m_qtFunctionInvoked; + } + + QVariantList qtFunctionActuals() const { + return m_actuals; + } + + void resetQtFunctionInvoked() { + m_qtFunctionInvoked = -1; + m_actuals.clear(); + } + + Q_INVOKABLE void myInvokable() { + m_qtFunctionInvoked = 0; + } + Q_INVOKABLE void myInvokableWithIntArg(int arg) { + m_qtFunctionInvoked = 1; + m_actuals << arg; + } + Q_INVOKABLE void myInvokableWithLonglongArg(qlonglong arg) { + m_qtFunctionInvoked = 2; + m_actuals << arg; + } + Q_INVOKABLE void myInvokableWithFloatArg(float arg) { + m_qtFunctionInvoked = 3; + m_actuals << arg; + } + Q_INVOKABLE void myInvokableWithDoubleArg(double arg) { + m_qtFunctionInvoked = 4; + m_actuals << arg; + } + Q_INVOKABLE void myInvokableWithStringArg(const QString &arg) { + m_qtFunctionInvoked = 5; + m_actuals << arg; + } + Q_INVOKABLE void myInvokableWithIntArgs(int arg1, int arg2) { + m_qtFunctionInvoked = 6; + m_actuals << arg1 << arg2; + } + Q_INVOKABLE int myInvokableReturningInt() { + m_qtFunctionInvoked = 7; + return 123; + } + Q_INVOKABLE qlonglong myInvokableReturningLongLong() { + m_qtFunctionInvoked = 39; + return 456; + } + Q_INVOKABLE QString myInvokableReturningString() { + m_qtFunctionInvoked = 8; + return QLatin1String("ciao"); + } + Q_INVOKABLE void myInvokableWithIntArg(int arg1, int arg2) { // overload + m_qtFunctionInvoked = 9; + m_actuals << arg1 << arg2; + } + Q_INVOKABLE void myInvokableWithEnumArg(Policy policy) { + m_qtFunctionInvoked = 10; + m_actuals << policy; + } + Q_INVOKABLE void myInvokableWithQualifiedEnumArg(MyQObject::Policy policy) { + m_qtFunctionInvoked = 36; + m_actuals << policy; + } + Q_INVOKABLE Policy myInvokableReturningEnum() { + m_qtFunctionInvoked = 37; + return BazPolicy; + } + Q_INVOKABLE MyQObject::Policy myInvokableReturningQualifiedEnum() { + m_qtFunctionInvoked = 38; + return BazPolicy; + } + Q_INVOKABLE QVector<int> myInvokableReturningVectorOfInt() { + m_qtFunctionInvoked = 11; + return QVector<int>(); + } + Q_INVOKABLE void myInvokableWithVectorOfIntArg(const QVector<int> &) { + m_qtFunctionInvoked = 12; + } + Q_INVOKABLE QObject* myInvokableReturningQObjectStar() { + m_qtFunctionInvoked = 13; + return this; + } + Q_INVOKABLE QObjectList myInvokableWithQObjectListArg(const QObjectList &lst) { + m_qtFunctionInvoked = 14; + m_actuals << QVariant::fromValue(lst); + return lst; + } + Q_INVOKABLE QVariant myInvokableWithVariantArg(const QVariant &v) { + m_qtFunctionInvoked = 15; + m_actuals << v; + return v; + } + Q_INVOKABLE QVariantMap myInvokableWithVariantMapArg(const QVariantMap &vm) { + m_qtFunctionInvoked = 16; + m_actuals << vm; + return vm; + } + Q_INVOKABLE QList<int> myInvokableWithListOfIntArg(const QList<int> &lst) { + m_qtFunctionInvoked = 17; + m_actuals << QVariant::fromValue(lst); + return lst; + } + Q_INVOKABLE QObject* myInvokableWithQObjectStarArg(QObject* obj) { + m_qtFunctionInvoked = 18; + m_actuals << QVariant::fromValue(obj); + return obj; + } + Q_INVOKABLE QBrush myInvokableWithQBrushArg(const QBrush &brush) { + m_qtFunctionInvoked = 19; + m_actuals << QVariant::fromValue(brush); + return brush; + } + Q_INVOKABLE void myInvokableWithBrushStyleArg(Qt::BrushStyle style) { + m_qtFunctionInvoked = 43; + m_actuals << QVariant::fromValue(style); + } + Q_INVOKABLE void myInvokableWithVoidStarArg(void* arg) { + m_qtFunctionInvoked = 44; + m_actuals << QVariant::fromValue(arg); + } + Q_INVOKABLE void myInvokableWithAmbiguousArg(int arg) { + m_qtFunctionInvoked = 45; + m_actuals << QVariant::fromValue(arg); + } + Q_INVOKABLE void myInvokableWithAmbiguousArg(uint arg) { + m_qtFunctionInvoked = 46; + m_actuals << QVariant::fromValue(arg); + } + Q_INVOKABLE void myInvokableWithDefaultArgs(int arg1, const QString &arg2 = "") { + m_qtFunctionInvoked = 47; + m_actuals << QVariant::fromValue(arg1) << qVariantFromValue(arg2); + } + Q_INVOKABLE QObject& myInvokableReturningRef() { + m_qtFunctionInvoked = 48; + return *this; + } + Q_INVOKABLE const QObject& myInvokableReturningConstRef() const { + const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 49; + return *this; + } + Q_INVOKABLE void myInvokableWithPointArg(const QPoint &arg) { + const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 50; + m_actuals << QVariant::fromValue(arg); + } + Q_INVOKABLE void myInvokableWithPointArg(const QPointF &arg) { + const_cast<MyQObject*>(this)->m_qtFunctionInvoked = 51; + m_actuals << QVariant::fromValue(arg); + } + Q_INVOKABLE void myInvokableWithBoolArg(bool arg) { + m_qtFunctionInvoked = 52; + m_actuals << arg; + } + + void emitMySignal() { + emit mySignal(); + } + void emitMySignalWithIntArg(int arg) { + emit mySignalWithIntArg(arg); + } + void emitMySignal2(bool arg) { + emit mySignal2(arg); + } + void emitMySignal2() { + emit mySignal2(); + } + void emitMySignalWithDateTimeArg(QDateTime dt) { + emit mySignalWithDateTimeArg(dt); + } + void emitMySignalWithRegexArg(QRegExp r) { + emit mySignalWithRegexArg(r); + } + +public Q_SLOTS: + void mySlot() { + m_qtFunctionInvoked = 20; + } + void mySlotWithIntArg(int arg) { + m_qtFunctionInvoked = 21; + m_actuals << arg; + } + void mySlotWithDoubleArg(double arg) { + m_qtFunctionInvoked = 22; + m_actuals << arg; + } + void mySlotWithStringArg(const QString &arg) { + m_qtFunctionInvoked = 23; + m_actuals << arg; + } + + void myOverloadedSlot() { + m_qtFunctionInvoked = 24; + } + void myOverloadedSlot(QObject* arg) { + m_qtFunctionInvoked = 41; + m_actuals << QVariant::fromValue(arg); + } + void myOverloadedSlot(bool arg) { + m_qtFunctionInvoked = 25; + m_actuals << arg; + } + void myOverloadedSlot(const QStringList &arg) { + m_qtFunctionInvoked = 42; + m_actuals << arg; + } + void myOverloadedSlot(double arg) { + m_qtFunctionInvoked = 26; + m_actuals << arg; + } + void myOverloadedSlot(float arg) { + m_qtFunctionInvoked = 27; + m_actuals << arg; + } + void myOverloadedSlot(int arg) { + m_qtFunctionInvoked = 28; + m_actuals << arg; + } + void myOverloadedSlot(const QString &arg) { + m_qtFunctionInvoked = 29; + m_actuals << arg; + } + void myOverloadedSlot(const QColor &arg) { + m_qtFunctionInvoked = 30; + m_actuals << arg; + } + void myOverloadedSlot(const QBrush &arg) { + m_qtFunctionInvoked = 31; + m_actuals << arg; + } + void myOverloadedSlot(const QDateTime &arg) { + m_qtFunctionInvoked = 32; + m_actuals << arg; + } + void myOverloadedSlot(const QDate &arg) { + m_qtFunctionInvoked = 33; + m_actuals << arg; + } + void myOverloadedSlot(const QRegExp &arg) { + m_qtFunctionInvoked = 34; + m_actuals << arg; + } + void myOverloadedSlot(const QVariant &arg) { + m_qtFunctionInvoked = 35; + m_actuals << arg; + } + void myOverloadedSlot(const QWebElement &arg) { + m_qtFunctionInvoked = 36; + m_actuals << QVariant::fromValue<QWebElement>(arg); + } + + void qscript_call(int arg) { + m_qtFunctionInvoked = 40; + m_actuals << arg; + } + +protected Q_SLOTS: + void myProtectedSlot() { + m_qtFunctionInvoked = 36; + } + +private Q_SLOTS: + void myPrivateSlot() { } + +Q_SIGNALS: + void mySignal(); + void mySignalWithIntArg(int arg); + void mySignalWithDoubleArg(double arg); + void mySignal2(bool arg = false); + void mySignalWithDateTimeArg(QDateTime dt); + void mySignalWithRegexArg(QRegExp r); + +private: + int m_intValue; + QVariant m_variantValue; + QVariantList m_variantListValue; + QVariantMap m_variantMapValue; + QString m_stringValue; + QStringList m_stringListValue; + QByteArray m_byteArrayValue; + QBrush m_brushValue; + double m_hiddenValue; + int m_writeOnlyValue; + int m_readOnlyValue; + QKeySequence m_shortcut; + QWebElement m_webElement; + CustomType m_customType; + QObject* m_objectStar; + int m_qtFunctionInvoked; + QVariantList m_actuals; +}; + +class MyWebElementSlotOnlyObject : public QObject { + Q_OBJECT + Q_PROPERTY(QString tagName READ tagName) +public slots: + void doSomethingWithWebElement(const QWebElement& element) + { + m_tagName = element.tagName(); + } + +public: + QString tagName() const + { + return m_tagName; + } +private: + QString m_tagName; +}; + +class MyOtherQObject : public MyQObject +{ +public: + MyOtherQObject(QObject* parent = 0) + : MyQObject(parent) { } +}; + +class MyEnumTestQObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString p1 READ p1) + Q_PROPERTY(QString p2 READ p2) + Q_PROPERTY(QString p3 READ p3 SCRIPTABLE false) + Q_PROPERTY(QString p4 READ p4) + Q_PROPERTY(QString p5 READ p5 SCRIPTABLE false) + Q_PROPERTY(QString p6 READ p6) +public: + MyEnumTestQObject(QObject* parent = 0) + : QObject(parent) { } + QString p1() const { + return QLatin1String("p1"); + } + QString p2() const { + return QLatin1String("p2"); + } + QString p3() const { + return QLatin1String("p3"); + } + QString p4() const { + return QLatin1String("p4"); + } + QString p5() const { + return QLatin1String("p5"); + } + QString p6() const { + return QLatin1String("p5"); + } +public Q_SLOTS: + void mySlot() { } + void myOtherSlot() { } +Q_SIGNALS: + void mySignal(); +}; + +class tst_QWebFrame : public QObject +{ + Q_OBJECT + +public: + tst_QWebFrame(); + virtual ~tst_QWebFrame(); + bool eventFilter(QObject* watched, QEvent* event); + +public slots: + void init(); + void cleanup(); + +private slots: + void horizontalScrollAfterBack(); + void getSetStaticProperty(); + void getSetDynamicProperty(); + void getSetChildren(); + void callQtInvokable(); + void connectAndDisconnect(); + void classEnums(); + void classConstructor(); + void overrideInvokable(); + void transferInvokable(); + void findChild(); + void findChildren(); + void overloadedSlots(); + void webElementSlotOnly(); + void enumerate_data(); + void enumerate(); + void objectDeleted(); + void typeConversion(); + void arrayObjectEnumerable(); + void symmetricUrl(); + void progressSignal(); + void urlChange(); + void domCycles(); + void requestedUrl(); + void requestedUrlAfterSetAndLoadFailures(); + void javaScriptWindowObjectCleared_data(); + void javaScriptWindowObjectCleared(); + void javaScriptWindowObjectClearedOnEvaluate(); + void setHtml(); + void setHtmlWithResource(); + void setHtmlWithBaseURL(); + void setHtmlWithJSAlert(); + void ipv6HostEncoding(); + void metaData(); +#if !defined(Q_WS_MAEMO_5) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_COMBOBOX) + // as maemo 5 && symbian do not use QComboBoxes to implement the popups + // this test does not make sense for it. + void popupFocus(); +#endif + void inputFieldFocus(); + void hitTestContent(); + void jsByteArray(); + void ownership(); + void nullValue(); + void baseUrl_data(); + void baseUrl(); + void hasSetFocus(); + void render(); + void renderHints(); + void scrollPosition(); + void scrollToAnchor(); + void scrollbarsOff(); + void evaluateWillCauseRepaint(); + void qObjectWrapperWithSameIdentity(); + void introspectQtMethods_data(); + void introspectQtMethods(); + void setContent_data(); + void setContent(); + void setCacheLoadControlAttribute(); + void setUrlWithPendingLoads(); + void setUrlWithFragment_data(); + void setUrlWithFragment(); + void setUrlToEmpty(); + void setUrlToInvalid(); + void setUrlHistory(); + void setUrlSameUrl(); + void setUrlThenLoads_data(); + void setUrlThenLoads(); + +private: + QString evalJS(const QString&s) { + // Convert an undefined return variant to the string "undefined" + QVariant ret = evalJSV(s); + if (ret.userType() == QMetaType::Void) + return "undefined"; + else + return ret.toString(); + } + QVariant evalJSV(const QString &s) { + return m_page->mainFrame()->evaluateJavaScript(s); + } + + QString evalJS(const QString&s, QString& type) { + return evalJSV(s, type).toString(); + } + QVariant evalJSV(const QString &s, QString& type) { + // As a special measure, if we get an exception we set the type to 'error' + // (in ecma, an Error object has typeof object, but qtscript has a convenience function) + // Similarly, an array is an object, but we'd prefer to have a type of 'array' + // Also, consider a QMetaType::Void QVariant to be undefined + QString escaped = s; + escaped.replace('\'', "\\'"); // Don't preescape your single quotes! + evalJS("var retvalue;\ + var typevalue; \ + try {\ + retvalue = eval('" + escaped + "'); \ + typevalue = typeof retvalue; \ + if (retvalue instanceof Array) \ + typevalue = 'array'; \ + } \ + catch(e) {\ + retvalue = e.name + ': ' + e.message;\ + typevalue = 'error';\ + }"); + QVariant ret = evalJSV("retvalue"); + if (ret.userType() != QMetaType::Void) + type = evalJS("typevalue"); + else { + ret = QString("undefined"); + type = sUndefined; + } + evalJS("delete retvalue; delete typevalue"); + return ret; + } + QObject* firstChildByClassName(QObject* parent, const char* className) { + const QObjectList & children = parent->children(); + foreach (QObject* child, children) { + if (!strcmp(child->metaObject()->className(), className)) { + return child; + } + } + return 0; + } + + const QString sTrue; + const QString sFalse; + const QString sUndefined; + const QString sArray; + const QString sFunction; + const QString sError; + const QString sString; + const QString sObject; + const QString sNumber; + +private: + QWebView* m_view; + QWebPage* m_page; + MyQObject* m_myObject; + QWebView* m_inputFieldsTestView; + int m_inputFieldTestPaintCount; +}; + +tst_QWebFrame::tst_QWebFrame() + : sTrue("true"), sFalse("false"), sUndefined("undefined"), sArray("array"), sFunction("function"), sError("error"), + sString("string"), sObject("object"), sNumber("number"), m_inputFieldsTestView(0), m_inputFieldTestPaintCount(0) +{ +} + +tst_QWebFrame::~tst_QWebFrame() +{ +} + +bool tst_QWebFrame::eventFilter(QObject* watched, QEvent* event) +{ + // used on the inputFieldFocus test + if (watched == m_inputFieldsTestView) { + if (event->type() == QEvent::Paint) + m_inputFieldTestPaintCount++; + } + return QObject::eventFilter(watched, event); +} + +void tst_QWebFrame::init() +{ + m_view = new QWebView(); + m_page = m_view->page(); + m_myObject = new MyQObject(); + m_page->mainFrame()->addToJavaScriptWindowObject("myObject", m_myObject); +} + +void tst_QWebFrame::cleanup() +{ + delete m_view; + delete m_myObject; +} + +void tst_QWebFrame::getSetStaticProperty() +{ + m_page->mainFrame()->setHtml("<html><head><body></body></html>"); + QCOMPARE(evalJS("typeof myObject.noSuchProperty"), sUndefined); + + // initial value (set in MyQObject constructor) + { + QString type; + QVariant ret = evalJSV("myObject.intProperty", type); + QCOMPARE(type, sNumber); + QCOMPARE(ret.type(), QVariant::Double); + QCOMPARE(ret.toInt(), 123); + } + QCOMPARE(evalJS("myObject.intProperty === 123.0"), sTrue); + + { + QString type; + QVariant ret = evalJSV("myObject.variantProperty", type); + QCOMPARE(type, sString); + QCOMPARE(ret.type(), QVariant::String); + QCOMPARE(ret.toString(), QLatin1String("foo")); + } + QCOMPARE(evalJS("myObject.variantProperty == 'foo'"), sTrue); + + { + QString type; + QVariant ret = evalJSV("myObject.stringProperty", type); + QCOMPARE(type, sString); + QCOMPARE(ret.type(), QVariant::String); + QCOMPARE(ret.toString(), QLatin1String("bar")); + } + QCOMPARE(evalJS("myObject.stringProperty === 'bar'"), sTrue); + + { + QString type; + QVariant ret = evalJSV("myObject.variantListProperty", type); + QCOMPARE(type, sArray); + QCOMPARE(ret.type(), QVariant::List); + QVariantList vl = ret.value<QVariantList>(); + QCOMPARE(vl.size(), 2); + QCOMPARE(vl.at(0).toInt(), 123); + QCOMPARE(vl.at(1).toString(), QLatin1String("foo")); + } + QCOMPARE(evalJS("myObject.variantListProperty.length === 2"), sTrue); + QCOMPARE(evalJS("myObject.variantListProperty[0] === 123"), sTrue); + QCOMPARE(evalJS("myObject.variantListProperty[1] === 'foo'"), sTrue); + + { + QString type; + QVariant ret = evalJSV("myObject.variantMapProperty", type); + QCOMPARE(type, sObject); + QCOMPARE(ret.type(), QVariant::Map); + QVariantMap vm = ret.value<QVariantMap>(); + QCOMPARE(vm.size(), 3); + QCOMPARE(vm.value("a").toInt(), 123); + QCOMPARE(vm.value("b").toString(), QLatin1String("foo")); + QCOMPARE(vm.value("c").value<QObject*>(), static_cast<QObject*>(m_myObject)); + } + QCOMPARE(evalJS("myObject.variantMapProperty.a === 123"), sTrue); + QCOMPARE(evalJS("myObject.variantMapProperty.b === 'foo'"), sTrue); + QCOMPARE(evalJS("myObject.variantMapProperty.c.variantMapProperty.b === 'foo'"), sTrue); + + { + QString type; + QVariant ret = evalJSV("myObject.stringListProperty", type); + QCOMPARE(type, sArray); + QCOMPARE(ret.type(), QVariant::List); + QVariantList vl = ret.value<QVariantList>(); + QCOMPARE(vl.size(), 2); + QCOMPARE(vl.at(0).toString(), QLatin1String("zig")); + QCOMPARE(vl.at(1).toString(), QLatin1String("zag")); + } + QCOMPARE(evalJS("myObject.stringListProperty.length === 2"), sTrue); + QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString); + QCOMPARE(evalJS("myObject.stringListProperty[0]"), QLatin1String("zig")); + QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString); + QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("zag")); + + // property change in C++ should be reflected in script + m_myObject->setIntProperty(456); + QCOMPARE(evalJS("myObject.intProperty == 456"), sTrue); + m_myObject->setIntProperty(789); + QCOMPARE(evalJS("myObject.intProperty == 789"), sTrue); + + m_myObject->setVariantProperty(QLatin1String("bar")); + QCOMPARE(evalJS("myObject.variantProperty === 'bar'"), sTrue); + m_myObject->setVariantProperty(42); + QCOMPARE(evalJS("myObject.variantProperty === 42"), sTrue); + m_myObject->setVariantProperty(QVariant::fromValue(QBrush())); +//XFAIL +// QCOMPARE(evalJS("typeof myObject.variantProperty"), sVariant); + + m_myObject->setStringProperty(QLatin1String("baz")); + QCOMPARE(evalJS("myObject.stringProperty === 'baz'"), sTrue); + m_myObject->setStringProperty(QLatin1String("zab")); + QCOMPARE(evalJS("myObject.stringProperty === 'zab'"), sTrue); + + // property change in script should be reflected in C++ + QCOMPARE(evalJS("myObject.intProperty = 123"), QLatin1String("123")); + QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue); + QCOMPARE(m_myObject->intProperty(), 123); + QCOMPARE(evalJS("myObject.intProperty = 'ciao!';" + "myObject.intProperty == 0"), sTrue); + QCOMPARE(m_myObject->intProperty(), 0); + QCOMPARE(evalJS("myObject.intProperty = '123';" + "myObject.intProperty == 123"), sTrue); + QCOMPARE(m_myObject->intProperty(), 123); + + QCOMPARE(evalJS("myObject.stringProperty = 'ciao'"), QLatin1String("ciao")); + QCOMPARE(evalJS("myObject.stringProperty"), QLatin1String("ciao")); + QCOMPARE(m_myObject->stringProperty(), QLatin1String("ciao")); + QCOMPARE(evalJS("myObject.stringProperty = 123;" + "myObject.stringProperty"), QLatin1String("123")); + QCOMPARE(m_myObject->stringProperty(), QLatin1String("123")); + QCOMPARE(evalJS("myObject.stringProperty = null"), QString()); + QCOMPARE(evalJS("myObject.stringProperty"), QString()); + QCOMPARE(m_myObject->stringProperty(), QString()); + QCOMPARE(evalJS("myObject.stringProperty = undefined"), sUndefined); + QCOMPARE(evalJS("myObject.stringProperty"), QString()); + QCOMPARE(m_myObject->stringProperty(), QString()); + + QCOMPARE(evalJS("myObject.variantProperty = new Number(1234);" + "myObject.variantProperty").toDouble(), 1234.0); + QCOMPARE(m_myObject->variantProperty().toDouble(), 1234.0); + + QCOMPARE(evalJS("myObject.variantProperty = new Boolean(1234);" + "myObject.variantProperty"), sTrue); + QCOMPARE(m_myObject->variantProperty().toBool(), true); + + QCOMPARE(evalJS("myObject.variantProperty = null;" + "myObject.variantProperty.valueOf()"), sUndefined); + QCOMPARE(m_myObject->variantProperty(), QVariant()); + QCOMPARE(evalJS("myObject.variantProperty = undefined;" + "myObject.variantProperty.valueOf()"), sUndefined); + QCOMPARE(m_myObject->variantProperty(), QVariant()); + + QCOMPARE(evalJS("myObject.variantProperty = 'foo';" + "myObject.variantProperty.valueOf()"), QLatin1String("foo")); + QCOMPARE(m_myObject->variantProperty(), QVariant(QLatin1String("foo"))); + QCOMPARE(evalJS("myObject.variantProperty = 42;" + "myObject.variantProperty").toDouble(), 42.0); + QCOMPARE(m_myObject->variantProperty().toDouble(), 42.0); + + QCOMPARE(evalJS("myObject.variantListProperty = [1, 'two', true];" + "myObject.variantListProperty.length == 3"), sTrue); + QCOMPARE(evalJS("myObject.variantListProperty[0] === 1"), sTrue); + QCOMPARE(evalJS("myObject.variantListProperty[1]"), QLatin1String("two")); + QCOMPARE(evalJS("myObject.variantListProperty[2] === true"), sTrue); + + QCOMPARE(evalJS("myObject.stringListProperty = [1, 'two', true];" + "myObject.stringListProperty.length == 3"), sTrue); + QCOMPARE(evalJS("typeof myObject.stringListProperty[0]"), sString); + QCOMPARE(evalJS("myObject.stringListProperty[0] == '1'"), sTrue); + QCOMPARE(evalJS("typeof myObject.stringListProperty[1]"), sString); + QCOMPARE(evalJS("myObject.stringListProperty[1]"), QLatin1String("two")); + QCOMPARE(evalJS("typeof myObject.stringListProperty[2]"), sString); + QCOMPARE(evalJS("myObject.stringListProperty[2]"), QLatin1String("true")); + evalJS("myObject.webElementProperty=document.body;"); + QCOMPARE(evalJS("myObject.webElementProperty.tagName"), QLatin1String("BODY")); + + // try to delete + QCOMPARE(evalJS("delete myObject.intProperty"), sFalse); + QCOMPARE(evalJS("myObject.intProperty == 123"), sTrue); + + QCOMPARE(evalJS("delete myObject.variantProperty"), sFalse); + QCOMPARE(evalJS("myObject.variantProperty").toDouble(), 42.0); + + // custom property + QCOMPARE(evalJS("myObject.customProperty"), sUndefined); + QCOMPARE(evalJS("myObject.customProperty = 123;" + "myObject.customProperty == 123"), sTrue); + QVariant v = m_page->mainFrame()->evaluateJavaScript("myObject.customProperty"); + QCOMPARE(v.type(), QVariant::Double); + QCOMPARE(v.toInt(), 123); + + // non-scriptable property + QCOMPARE(m_myObject->hiddenProperty(), 456.0); + QCOMPARE(evalJS("myObject.hiddenProperty"), sUndefined); + QCOMPARE(evalJS("myObject.hiddenProperty = 123;" + "myObject.hiddenProperty == 123"), sTrue); + QCOMPARE(m_myObject->hiddenProperty(), 456.0); + + // write-only property + QCOMPARE(m_myObject->writeOnlyProperty(), 789); + QCOMPARE(evalJS("typeof myObject.writeOnlyProperty"), sUndefined); + QCOMPARE(evalJS("myObject.writeOnlyProperty = 123;" + "typeof myObject.writeOnlyProperty"), sUndefined); + QCOMPARE(m_myObject->writeOnlyProperty(), 123); + + // read-only property + QCOMPARE(m_myObject->readOnlyProperty(), 987); + QCOMPARE(evalJS("myObject.readOnlyProperty == 987"), sTrue); + QCOMPARE(evalJS("myObject.readOnlyProperty = 654;" + "myObject.readOnlyProperty == 987"), sTrue); + QCOMPARE(m_myObject->readOnlyProperty(), 987); + + // QObject* property + m_myObject->setObjectStarProperty(0); + QCOMPARE(m_myObject->objectStarProperty(), (QObject*)0); + QCOMPARE(evalJS("myObject.objectStarProperty == null"), sTrue); + QCOMPARE(evalJS("typeof myObject.objectStarProperty"), sObject); + QCOMPARE(evalJS("Boolean(myObject.objectStarProperty)"), sFalse); + QCOMPARE(evalJS("String(myObject.objectStarProperty) == 'null'"), sTrue); + QCOMPARE(evalJS("myObject.objectStarProperty.objectStarProperty"), + sUndefined); + m_myObject->setObjectStarProperty(this); + QCOMPARE(evalJS("myObject.objectStarProperty != null"), sTrue); + QCOMPARE(evalJS("typeof myObject.objectStarProperty"), sObject); + QCOMPARE(evalJS("Boolean(myObject.objectStarProperty)"), sTrue); + QCOMPARE(evalJS("String(myObject.objectStarProperty) != 'null'"), sTrue); +} + +void tst_QWebFrame::getSetDynamicProperty() +{ + // initially the object does not have the property + // In WebKit, RuntimeObjects do not inherit Object, so don't have hasOwnProperty + + //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse); + QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); + + // add a dynamic property in C++ + QCOMPARE(m_myObject->setProperty("dynamicProperty", 123), false); + //QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sTrue); + QCOMPARE(evalJS("typeof myObject.dynamicProperty != 'undefined'"), sTrue); + QCOMPARE(evalJS("myObject.dynamicProperty == 123"), sTrue); + + // property change in script should be reflected in C++ + QCOMPARE(evalJS("myObject.dynamicProperty = 'foo';" + "myObject.dynamicProperty"), QLatin1String("foo")); + QCOMPARE(m_myObject->property("dynamicProperty").toString(), QLatin1String("foo")); + + // delete the property (XFAIL - can't delete properties) + QEXPECT_FAIL("", "can't delete properties", Continue); + QCOMPARE(evalJS("delete myObject.dynamicProperty"), sTrue); + /* + QCOMPARE(m_myObject->property("dynamicProperty").isValid(), false); + QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); + // QCOMPARE(evalJS("myObject.hasOwnProperty('dynamicProperty')"), sFalse); + QCOMPARE(evalJS("typeof myObject.dynamicProperty"), sUndefined); + */ +} + +void tst_QWebFrame::getSetChildren() +{ + // initially the object does not have the child + // (again, no hasOwnProperty) + + //QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse); + QCOMPARE(evalJS("typeof myObject.child"), sUndefined); + + // add a child + MyQObject* child = new MyQObject(m_myObject); + child->setObjectName("child"); +// QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sTrue); + QCOMPARE(evalJS("typeof myObject.child != 'undefined'"), sTrue); + + // add a grandchild + MyQObject* grandChild = new MyQObject(child); + grandChild->setObjectName("grandChild"); +// QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sTrue); + QCOMPARE(evalJS("typeof myObject.child.grandChild != 'undefined'"), sTrue); + + // delete grandchild + delete grandChild; +// QCOMPARE(evalJS("myObject.child.hasOwnProperty('grandChild')"), sFalse); + QCOMPARE(evalJS("typeof myObject.child.grandChild == 'undefined'"), sTrue); + + // delete child + delete child; +// QCOMPARE(evalJS("myObject.hasOwnProperty('child')"), sFalse); + QCOMPARE(evalJS("typeof myObject.child == 'undefined'"), sTrue); +} + +Q_DECLARE_METATYPE(QVector<int>) +Q_DECLARE_METATYPE(QVector<double>) +Q_DECLARE_METATYPE(QVector<QString>) + +void tst_QWebFrame::callQtInvokable() +{ + qRegisterMetaType<QObjectList>(); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokable()"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 0); + QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); + + // extra arguments should silently be ignored + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokable(10, 20, 30)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 0); + QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg('123')"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithLonglongArg(123)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 2); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toLongLong(), qlonglong(123)); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithFloatArg(123.5)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 3); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(123.5)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 4); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.5); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithDoubleArg(new Number(1234.5))"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 4); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 1234.5); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithBoolArg(new Boolean(true))"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 52); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toBool(), true); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg('ciao')"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 5); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("ciao")); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(123)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 5); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(null)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 5); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); + QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty()); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithStringArg(undefined)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 5); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QString()); + QVERIFY(m_myObject->qtFunctionActuals().at(0).toString().isEmpty()); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArgs(123, 456)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 6); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); + QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.myInvokableReturningInt()"), QLatin1String("123")); + QCOMPARE(m_myObject->qtFunctionInvoked(), 7); + QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.myInvokableReturningLongLong()"), QLatin1String("456")); + QCOMPARE(m_myObject->qtFunctionInvoked(), 39); + QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.myInvokableReturningString()"), QLatin1String("ciao")); + QCOMPARE(m_myObject->qtFunctionInvoked(), 8); + QCOMPARE(m_myObject->qtFunctionActuals(), QVariantList()); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithIntArg(123, 456)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 9); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); + QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("typeof myObject.myInvokableWithVoidStarArg(null)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 44); + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("myObject.myInvokableWithVoidStarArg(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithVoidStarArg(); candidates were\n myInvokableWithVoidStarArg(void*)")); + QCOMPARE(m_myObject->qtFunctionInvoked(), -1); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("myObject.myInvokableWithAmbiguousArg(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: ambiguous call of overloaded function myInvokableWithAmbiguousArg(); candidates were\n myInvokableWithAmbiguousArg(int)\n myInvokableWithAmbiguousArg(uint)")); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("myObject.myInvokableWithDefaultArgs(123, 'hello')", type); + QCOMPARE(type, sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 47); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); + QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QLatin1String("hello")); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("myObject.myInvokableWithDefaultArgs(456)", type); + QCOMPARE(type, sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 47); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); + QCOMPARE(m_myObject->qtFunctionActuals().at(1).toString(), QString()); + } + + // calling function that returns (const)ref + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("typeof myObject.myInvokableReturningRef()"); + QCOMPARE(ret, sUndefined); + //QVERIFY(!m_engine->hasUncaughtException()); + QCOMPARE(m_myObject->qtFunctionInvoked(), 48); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("typeof myObject.myInvokableReturningConstRef()"); + QCOMPARE(ret, sUndefined); + //QVERIFY(!m_engine->hasUncaughtException()); + QCOMPARE(m_myObject->qtFunctionInvoked(), 49); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableReturningQObjectStar()", type); + QCOMPARE(m_myObject->qtFunctionInvoked(), 13); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); + QCOMPARE(type, sObject); + QCOMPARE(ret.userType(), int(QMetaType::QObjectStar)); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableWithQObjectListArg([myObject])", type); + QCOMPARE(m_myObject->qtFunctionInvoked(), 14); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(type, sArray); + QCOMPARE(ret.userType(), int(QVariant::List)); // All lists get downgraded to QVariantList + QVariantList vl = qvariant_cast<QVariantList>(ret); + QCOMPARE(vl.count(), 1); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + m_myObject->setVariantProperty(QVariant(123)); + QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(myObject.variantProperty)", type); + QCOMPARE(type, sNumber); + QCOMPARE(m_myObject->qtFunctionInvoked(), 15); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0), m_myObject->variantProperty()); + QCOMPARE(ret.userType(), int(QMetaType::Double)); // all JS numbers are doubles, even though this started as an int + QCOMPARE(ret.toInt(),123); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(null)", type); + QCOMPARE(type, sObject); + QCOMPARE(m_myObject->qtFunctionInvoked(), 15); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); + QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid()); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(undefined)", type); + QCOMPARE(type, sObject); + QCOMPARE(m_myObject->qtFunctionInvoked(), 15); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant()); + QVERIFY(!m_myObject->qtFunctionActuals().at(0).isValid()); + } + + /* XFAIL - variant support + m_myObject->resetQtFunctionInvoked(); + { + m_myObject->setVariantProperty(QVariant::fromValue(QBrush())); + QVariant ret = evalJS("myObject.myInvokableWithVariantArg(myObject.variantProperty)"); + QVERIFY(ret.isVariant()); + QCOMPARE(m_myObject->qtFunctionInvoked(), 15); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(ret.toVariant(), m_myObject->qtFunctionActuals().at(0)); + QCOMPARE(ret.toVariant(), m_myObject->variantProperty()); + } + */ + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableWithVariantArg(123)", type); + QCOMPARE(type, sNumber); + QCOMPARE(m_myObject->qtFunctionInvoked(), 15); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0), QVariant(123)); + QCOMPARE(ret.userType(), int(QMetaType::Double)); + QCOMPARE(ret.toInt(),123); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableWithVariantMapArg({ a:123, b:'ciao' })", type); + QCOMPARE(m_myObject->qtFunctionInvoked(), 16); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + + QVariant v = m_myObject->qtFunctionActuals().at(0); + QCOMPARE(v.userType(), int(QMetaType::QVariantMap)); + + QVariantMap vmap = qvariant_cast<QVariantMap>(v); + QCOMPARE(vmap.keys().size(), 2); + QCOMPARE(vmap.keys().at(0), QLatin1String("a")); + QCOMPARE(vmap.value("a"), QVariant(123)); + QCOMPARE(vmap.keys().at(1), QLatin1String("b")); + QCOMPARE(vmap.value("b"), QVariant("ciao")); + + QCOMPARE(type, sObject); + + QCOMPARE(ret.userType(), int(QMetaType::QVariantMap)); + vmap = qvariant_cast<QVariantMap>(ret); + QCOMPARE(vmap.keys().size(), 2); + QCOMPARE(vmap.keys().at(0), QLatin1String("a")); + QCOMPARE(vmap.value("a"), QVariant(123)); + QCOMPARE(vmap.keys().at(1), QLatin1String("b")); + QCOMPARE(vmap.value("b"), QVariant("ciao")); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableWithListOfIntArg([1, 5])", type); + QCOMPARE(m_myObject->qtFunctionInvoked(), 17); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QVariant v = m_myObject->qtFunctionActuals().at(0); + QCOMPARE(v.userType(), qMetaTypeId<QList<int> >()); + QList<int> ilst = qvariant_cast<QList<int> >(v); + QCOMPARE(ilst.size(), 2); + QCOMPARE(ilst.at(0), 1); + QCOMPARE(ilst.at(1), 5); + + QCOMPARE(type, sArray); + QCOMPARE(ret.userType(), int(QMetaType::QVariantList)); // ints get converted to doubles, so this is a qvariantlist + QVariantList vlst = qvariant_cast<QVariantList>(ret); + QCOMPARE(vlst.size(), 2); + QCOMPARE(vlst.at(0).toInt(), 1); + QCOMPARE(vlst.at(1).toInt(), 5); + } + + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QVariant ret = evalJSV("myObject.myInvokableWithQObjectStarArg(myObject)", type); + QCOMPARE(m_myObject->qtFunctionInvoked(), 18); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QVariant v = m_myObject->qtFunctionActuals().at(0); + QCOMPARE(v.userType(), int(QMetaType::QObjectStar)); + QCOMPARE(qvariant_cast<QObject*>(v), (QObject*)m_myObject); + + QCOMPARE(ret.userType(), int(QMetaType::QObjectStar)); + QCOMPARE(qvariant_cast<QObject*>(ret), (QObject*)m_myObject); + + QCOMPARE(type, sObject); + } + + m_myObject->resetQtFunctionInvoked(); + { + // no implicit conversion from integer to QObject* + QString type; + evalJS("myObject.myInvokableWithQObjectStarArg(123)", type); + QCOMPARE(type, sError); + } + + /* + m_myObject->resetQtFunctionInvoked(); + { + QString fun = evalJS("myObject.myInvokableWithQBrushArg"); + Q_ASSERT(fun.isFunction()); + QColor color(10, 20, 30, 40); + // QColor should be converted to a QBrush + QVariant ret = fun.call(QString(), QStringList() + << qScriptValueFromValue(m_engine, color)); + QCOMPARE(m_myObject->qtFunctionInvoked(), 19); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QVariant v = m_myObject->qtFunctionActuals().at(0); + QCOMPARE(v.userType(), int(QMetaType::QBrush)); + QCOMPARE(qvariant_cast<QColor>(v), color); + + QCOMPARE(qscriptvalue_cast<QColor>(ret), color); + } + */ + + // private slots should not be part of the QObject binding + QCOMPARE(evalJS("typeof myObject.myPrivateSlot"), sUndefined); + + // protected slots should be fine + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myProtectedSlot()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 36); + + // call with too few arguments + { + QString type; + QString ret = evalJS("myObject.myInvokableWithIntArg()", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("SyntaxError: too few arguments in call to myInvokableWithIntArg(); candidates are\n myInvokableWithIntArg(int,int)\n myInvokableWithIntArg(int)")); + } + + // call function where not all types have been registered + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("myObject.myInvokableWithBrushStyleArg(0)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: cannot call myInvokableWithBrushStyleArg(): unknown type `Qt::BrushStyle'")); + QCOMPARE(m_myObject->qtFunctionInvoked(), -1); + } + + // call function with incompatible argument type + m_myObject->resetQtFunctionInvoked(); + { + QString type; + QString ret = evalJS("myObject.myInvokableWithQBrushArg(null)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: incompatible type of argument(s) in call to myInvokableWithQBrushArg(); candidates were\n myInvokableWithQBrushArg(QBrush)")); + QCOMPARE(m_myObject->qtFunctionInvoked(), -1); + } +} + +void tst_QWebFrame::connectAndDisconnect() +{ + // connect(function) + QCOMPARE(evalJS("typeof myObject.mySignal"), sFunction); + QCOMPARE(evalJS("typeof myObject.mySignal.connect"), sFunction); + QCOMPARE(evalJS("typeof myObject.mySignal.disconnect"), sFunction); + + { + QString type; + evalJS("myObject.mySignal.connect(123)", type); + QCOMPARE(type, sError); + } + + evalJS("myHandler = function() { window.gotSignal = true; window.signalArgs = arguments; window.slotThisObject = this; window.signalSender = __qt_sender__; }"); + + QCOMPARE(evalJS("myObject.mySignal.connect(myHandler)"), sUndefined); + + evalJS("gotSignal = false"); + evalJS("myObject.mySignal()"); + QCOMPARE(evalJS("gotSignal"), sTrue); + QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); + QCOMPARE(evalJS("signalSender"),evalJS("myObject")); + QCOMPARE(evalJS("slotThisObject == window"), sTrue); + + evalJS("gotSignal = false"); + m_myObject->emitMySignal(); + QCOMPARE(evalJS("gotSignal"), sTrue); + QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); + + QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myHandler)"), sUndefined); + + evalJS("gotSignal = false"); + m_myObject->emitMySignalWithIntArg(123); + QCOMPARE(evalJS("gotSignal"), sTrue); + QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); + QCOMPARE(evalJS("signalArgs[0] == 123.0"), sTrue); + + QCOMPARE(evalJS("myObject.mySignal.disconnect(myHandler)"), sUndefined); + { + QString type; + evalJS("myObject.mySignal.disconnect(myHandler)", type); + QCOMPARE(type, sError); + } + + evalJS("gotSignal = false"); + QCOMPARE(evalJS("myObject.mySignal2.connect(myHandler)"), sUndefined); + m_myObject->emitMySignal2(true); + QCOMPARE(evalJS("gotSignal"), sTrue); + QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); + QCOMPARE(evalJS("signalArgs[0]"), sTrue); + + QCOMPARE(evalJS("myObject.mySignal2.disconnect(myHandler)"), sUndefined); + + QCOMPARE(evalJS("typeof myObject['mySignal2()']"), sFunction); + QCOMPARE(evalJS("typeof myObject['mySignal2()'].connect"), sFunction); + QCOMPARE(evalJS("typeof myObject['mySignal2()'].disconnect"), sFunction); + + QCOMPARE(evalJS("myObject['mySignal2()'].connect(myHandler)"), sUndefined); + + evalJS("gotSignal = false"); + m_myObject->emitMySignal2(); + QCOMPARE(evalJS("gotSignal"), sTrue); + + QCOMPARE(evalJS("myObject['mySignal2()'].disconnect(myHandler)"), sUndefined); + + // connect(object, function) + evalJS("otherObject = { name:'foo' }"); + QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined); + QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined); + evalJS("gotSignal = false"); + m_myObject->emitMySignal(); + QCOMPARE(evalJS("gotSignal"), sFalse); + + { + QString type; + evalJS("myObject.mySignal.disconnect(otherObject, myHandler)", type); + QCOMPARE(type, sError); + } + + QCOMPARE(evalJS("myObject.mySignal.connect(otherObject, myHandler)"), sUndefined); + evalJS("gotSignal = false"); + m_myObject->emitMySignal(); + QCOMPARE(evalJS("gotSignal"), sTrue); + QCOMPARE(evalJS("signalArgs.length == 0"), sTrue); + QCOMPARE(evalJS("slotThisObject"),evalJS("otherObject")); + QCOMPARE(evalJS("signalSender"),evalJS("myObject")); + QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("foo")); + QCOMPARE(evalJS("myObject.mySignal.disconnect(otherObject, myHandler)"), sUndefined); + + evalJS("yetAnotherObject = { name:'bar', func : function() { } }"); + QCOMPARE(evalJS("myObject.mySignal2.connect(yetAnotherObject, myHandler)"), sUndefined); + evalJS("gotSignal = false"); + m_myObject->emitMySignal2(true); + QCOMPARE(evalJS("gotSignal"), sTrue); + QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); + QCOMPARE(evalJS("slotThisObject == yetAnotherObject"), sTrue); + QCOMPARE(evalJS("signalSender == myObject"), sTrue); + QCOMPARE(evalJS("slotThisObject.name"), QLatin1String("bar")); + QCOMPARE(evalJS("myObject.mySignal2.disconnect(yetAnotherObject, myHandler)"), sUndefined); + + QCOMPARE(evalJS("myObject.mySignal2.connect(myObject, myHandler)"), sUndefined); + evalJS("gotSignal = false"); + m_myObject->emitMySignal2(true); + QCOMPARE(evalJS("gotSignal"), sTrue); + QCOMPARE(evalJS("signalArgs.length == 1"), sTrue); + QCOMPARE(evalJS("slotThisObject == myObject"), sTrue); + QCOMPARE(evalJS("signalSender == myObject"), sTrue); + QCOMPARE(evalJS("myObject.mySignal2.disconnect(myObject, myHandler)"), sUndefined); + + // connect(obj, string) + QCOMPARE(evalJS("myObject.mySignal.connect(yetAnotherObject, 'func')"), sUndefined); + QCOMPARE(evalJS("myObject.mySignal.connect(myObject, 'mySlot')"), sUndefined); + QCOMPARE(evalJS("myObject.mySignal.disconnect(yetAnotherObject, 'func')"), sUndefined); + QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject, 'mySlot')"), sUndefined); + + // check that emitting signals from script works + + // no arguments + QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined); + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.mySignal()"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 20); + QCOMPARE(evalJS("myObject.mySignal.disconnect(myObject.mySlot)"), sUndefined); + + // one argument + QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithIntArg)"), sUndefined); + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 21); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); + QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithIntArg)"), sUndefined); + + QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithDoubleArg)"), sUndefined); + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 22); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDouble(), 123.0); + QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithDoubleArg)"), sUndefined); + + QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.mySlotWithStringArg)"), sUndefined); + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 23); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toString(), QLatin1String("123")); + QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.mySlotWithStringArg)"), sUndefined); + + // connecting to overloaded slot + QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject.myOverloadedSlot)"), sUndefined); + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.mySignalWithIntArg(123)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 26); // double overload + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); + QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject.myOverloadedSlot)"), sUndefined); + + QCOMPARE(evalJS("myObject.mySignalWithIntArg.connect(myObject['myOverloadedSlot(int)'])"), sUndefined); + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.mySignalWithIntArg(456)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 28); // int overload + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); + QCOMPARE(evalJS("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])"), sUndefined); + + // erroneous input + { + // ### QtScript adds .connect to all functions, WebKit does only to signals/slots + QString type; + QString ret = evalJS("(function() { }).connect()", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); + } + { + QString type; + QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect()", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); + } + + { + QString type; + QString ret = evalJS("(function() { }).connect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); + } + { + QString type; + QString ret = evalJS("var o = { }; o.connect = Function.prototype.connect; o.connect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: 'undefined' is not a function")); + } + + { + QString type; + QString ret = evalJS("myObject.myInvokable.connect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal")); + } + { + QString type; + QString ret = evalJS("myObject.myInvokable.connect(function() { })", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: MyQObject::myInvokable() is not a signal")); + } + + { + QString type; + QString ret = evalJS("myObject.mySignal.connect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.connect: target is not a function")); + } + + { + QString type; + QString ret = evalJS("myObject.mySignal.disconnect()", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given")); + } + { + QString type; + QString ret = evalJS("var o = { }; o.disconnect = myObject.mySignal.disconnect; o.disconnect()", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: no arguments given")); + } + + /* XFAIL - Function.prototype doesn't get connect/disconnect, just signals/slots + { + QString type; + QString ret = evalJS("(function() { }).disconnect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: this object is not a signal")); + } + */ + + { + QString type; + QString ret = evalJS("var o = { }; o.disconnect = myObject.myInvokable.disconnect; o.disconnect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); + } + + { + QString type; + QString ret = evalJS("myObject.myInvokable.disconnect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); + } + { + QString type; + QString ret = evalJS("myObject.myInvokable.disconnect(function() { })", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: MyQObject::myInvokable() is not a signal")); + } + + { + QString type; + QString ret = evalJS("myObject.mySignal.disconnect(123)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("TypeError: QtMetaMethod.disconnect: target is not a function")); + } + + { + QString type; + QString ret = evalJS("myObject.mySignal.disconnect(function() { })", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: QtMetaMethod.disconnect: failed to disconnect from MyQObject::mySignal()")); + } + + // when the wrapper dies, the connection stays alive + QCOMPARE(evalJS("myObject.mySignal.connect(myObject.mySlot)"), sUndefined); + m_myObject->resetQtFunctionInvoked(); + m_myObject->emitMySignal(); + QCOMPARE(m_myObject->qtFunctionInvoked(), 20); + evalJS("myObject = null"); + evalJS("gc()"); + m_myObject->resetQtFunctionInvoked(); + m_myObject->emitMySignal(); + QCOMPARE(m_myObject->qtFunctionInvoked(), 20); +} + +void tst_QWebFrame::classEnums() +{ + // We don't do the meta thing currently, unfortunately!!! + /* + QString myClass = m_engine->newQMetaObject(m_myObject->metaObject(), m_engine->undefinedValue()); + m_engine->globalObject().setProperty("MyQObject", myClass); + + QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.FooPolicy").toInt()), + MyQObject::FooPolicy); + QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BarPolicy").toInt()), + MyQObject::BarPolicy); + QCOMPARE(static_cast<MyQObject::Policy>(evalJS("MyQObject.BazPolicy").toInt()), + MyQObject::BazPolicy); + + QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.FooStrategy").toInt()), + MyQObject::FooStrategy); + QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BarStrategy").toInt()), + MyQObject::BarStrategy); + QCOMPARE(static_cast<MyQObject::Strategy>(evalJS("MyQObject.BazStrategy").toInt()), + MyQObject::BazStrategy); + + QCOMPARE(MyQObject::Ability(evalJS("MyQObject.NoAbility").toInt()), + MyQObject::NoAbility); + QCOMPARE(MyQObject::Ability(evalJS("MyQObject.FooAbility").toInt()), + MyQObject::FooAbility); + QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BarAbility").toInt()), + MyQObject::BarAbility); + QCOMPARE(MyQObject::Ability(evalJS("MyQObject.BazAbility").toInt()), + MyQObject::BazAbility); + QCOMPARE(MyQObject::Ability(evalJS("MyQObject.AllAbility").toInt()), + MyQObject::AllAbility); + + // enums from Qt are inherited through prototype + QCOMPARE(static_cast<Qt::FocusPolicy>(evalJS("MyQObject.StrongFocus").toInt()), + Qt::StrongFocus); + QCOMPARE(static_cast<Qt::Key>(evalJS("MyQObject.Key_Left").toInt()), + Qt::Key_Left); + + QCOMPARE(evalJS("MyQObject.className()"), QLatin1String("MyQObject")); + + qRegisterMetaType<MyQObject::Policy>("Policy"); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.myInvokableWithEnumArg(MyQObject.BazPolicy)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 10); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); + + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.myInvokableWithQualifiedEnumArg(MyQObject.BazPolicy)"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 36); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), int(MyQObject::BazPolicy)); + + m_myObject->resetQtFunctionInvoked(); + { + QVariant ret = evalJS("myObject.myInvokableReturningEnum()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 37); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); + QCOMPARE(ret.isVariant()); + } + m_myObject->resetQtFunctionInvoked(); + { + QVariant ret = evalJS("myObject.myInvokableReturningQualifiedEnum()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 38); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 0); + QCOMPARE(ret.isNumber()); + } + */ +} + +void tst_QWebFrame::classConstructor() +{ + /* + QString myClass = qScriptValueFromQMetaObject<MyQObject>(m_engine); + m_engine->globalObject().setProperty("MyQObject", myClass); + + QString myObj = evalJS("myObj = MyQObject()"); + QObject* qobj = myObj.toQObject(); + QVERIFY(qobj != 0); + QCOMPARE(qobj->metaObject()->className(), "MyQObject"); + QCOMPARE(qobj->parent(), (QObject*)0); + + QString qobjectClass = qScriptValueFromQMetaObject<QObject>(m_engine); + m_engine->globalObject().setProperty("QObject", qobjectClass); + + QString otherObj = evalJS("otherObj = QObject(myObj)"); + QObject* qqobj = otherObj.toQObject(); + QVERIFY(qqobj != 0); + QCOMPARE(qqobj->metaObject()->className(), "QObject"); + QCOMPARE(qqobj->parent(), qobj); + + delete qobj; + */ +} + +void tst_QWebFrame::overrideInvokable() +{ + m_myObject->resetQtFunctionInvoked(); + QCOMPARE(evalJS("myObject.myInvokable()"), sUndefined); + QCOMPARE(m_myObject->qtFunctionInvoked(), 0); + + /* XFAIL - can't write to functions with RuntimeObject + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myInvokable = function() { window.a = 123; }"); + evalJS("myObject.myInvokable()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), -1); + QCOMPARE(evalJS("window.a").toDouble(), 123.0); + + evalJS("myObject.myInvokable = function() { window.a = 456; }"); + evalJS("myObject.myInvokable()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), -1); + QCOMPARE(evalJS("window.a").toDouble(), 456.0); + */ + + evalJS("delete myObject.myInvokable"); + evalJS("myObject.myInvokable()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 0); + + /* XFAIL - ditto + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg"); + evalJS("myObject.myInvokable(123)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 1); + */ + + evalJS("delete myObject.myInvokable"); + m_myObject->resetQtFunctionInvoked(); + // this form (with the '()') is read-only + evalJS("myObject['myInvokable()'] = function() { window.a = 123; }"); + evalJS("myObject.myInvokable()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 0); +} + +void tst_QWebFrame::transferInvokable() +{ + /* XFAIL - can't put to functions with RuntimeObject + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.foozball = myObject.myInvokable"); + evalJS("myObject.foozball()"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 0); + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.foozball = myObject.myInvokableWithIntArg"); + evalJS("myObject.foozball(123)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 1); + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myInvokable = myObject.myInvokableWithIntArg"); + evalJS("myObject.myInvokable(123)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 1); + + MyOtherQObject other; + m_page->mainFrame()->addToJSWindowObject("myOtherObject", &other); + evalJS("myOtherObject.foo = myObject.foozball"); + other.resetQtFunctionInvoked(); + evalJS("myOtherObject.foo(456)"); + QCOMPARE(other.qtFunctionInvoked(), 1); + */ +} + +void tst_QWebFrame::findChild() +{ + /* + QObject* child = new QObject(m_myObject); + child->setObjectName(QLatin1String("myChildObject")); + + { + QString result = evalJS("myObject.findChild('noSuchChild')"); + QCOMPARE(result.isNull()); + } + + { + QString result = evalJS("myObject.findChild('myChildObject')"); + QCOMPARE(result.isQObject()); + QCOMPARE(result.toQObject(), child); + } + + delete child; + */ +} + +void tst_QWebFrame::findChildren() +{ + /* + QObject* child = new QObject(m_myObject); + child->setObjectName(QLatin1String("myChildObject")); + + { + QString result = evalJS("myObject.findChildren('noSuchChild')"); + QCOMPARE(result.isArray()); + QCOMPARE(result.property(QLatin1String("length")).toDouble(), 0.0); + } + + { + QString result = evalJS("myObject.findChildren('myChildObject')"); + QCOMPARE(result.isArray()); + QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); + QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); + } + + QObject* namelessChild = new QObject(m_myObject); + + { + QString result = evalJS("myObject.findChildren('myChildObject')"); + QCOMPARE(result.isArray()); + QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); + QCOMPARE(result.property(QLatin1String("0")).toQObject(), child); + } + + QObject* anotherChild = new QObject(m_myObject); + anotherChild->setObjectName(QLatin1String("anotherChildObject")); + + { + QString result = evalJS("myObject.findChildren('anotherChildObject')"); + QCOMPARE(result.isArray()); + QCOMPARE(result.property(QLatin1String("length")).toDouble(), 1.0); + QCOMPARE(result.property(QLatin1String("0")).toQObject(), anotherChild); + } + + anotherChild->setObjectName(QLatin1String("myChildObject")); + { + QString result = evalJS("myObject.findChildren('myChildObject')"); + QCOMPARE(result.isArray()); + QCOMPARE(result.property(QLatin1String("length")).toDouble(), 2.0); + QObject* o1 = result.property(QLatin1String("0")).toQObject(); + QObject* o2 = result.property(QLatin1String("1")).toQObject(); + if (o1 != child) { + QCOMPARE(o1, anotherChild); + QCOMPARE(o2, child); + } else { + QCOMPARE(o1, child); + QCOMPARE(o2, anotherChild); + } + } + + // find all + { + QString result = evalJS("myObject.findChildren()"); + QVERIFY(result.isArray()); + int count = 3; + QCOMPARE(result.property("length"), QLatin1String(count); + for (int i = 0; i < 3; ++i) { + QObject* o = result.property(i).toQObject(); + if (o == namelessChild || o == child || o == anotherChild) + --count; + } + QVERIFY(count == 0); + } + + delete anotherChild; + delete namelessChild; + delete child; + */ +} + +void tst_QWebFrame::overloadedSlots() +{ + // should pick myOverloadedSlot(double) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(10)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 26); + + // should pick myOverloadedSlot(double) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(10.0)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 26); + + // should pick myOverloadedSlot(QString) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot('10')"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 29); + + // should pick myOverloadedSlot(bool) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(true)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 25); + + // should pick myOverloadedSlot(QDateTime) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(new Date())"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 32); + + // should pick myOverloadedSlot(QRegExp) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(new RegExp())"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 34); + + // should pick myOverloadedSlot(QVariant) + /* XFAIL + m_myObject->resetQtFunctionInvoked(); + QString f = evalJS("myObject.myOverloadedSlot"); + f.call(QString(), QStringList() << m_engine->newVariant(QVariant("ciao"))); + QCOMPARE(m_myObject->qtFunctionInvoked(), 35); + */ + + // should pick myOverloadedSlot(QRegExp) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(document.body)"); + QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=37319", Continue); + QCOMPARE(m_myObject->qtFunctionInvoked(), 36); + + // should pick myOverloadedSlot(QObject*) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(myObject)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 41); + + // should pick myOverloadedSlot(QObject*) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(null)"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 41); + + // should pick myOverloadedSlot(QStringList) + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(['hello'])"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 42); +} + +void tst_QWebFrame::enumerate_data() +{ + QTest::addColumn<QStringList>("expectedNames"); + + QTest::newRow("enumerate all") + << (QStringList() + // meta-object-defined properties: + // inherited + << "objectName" + // non-inherited + << "p1" << "p2" << "p4" << "p6" + // dynamic properties + << "dp1" << "dp2" << "dp3" + // inherited slots + << "destroyed(QObject*)" << "destroyed()" + << "deleteLater()" + // not included because it's private: + // << "_q_reregisterTimers(void*)" + // signals + << "mySignal()" + // slots + << "mySlot()" << "myOtherSlot()"); +} + +void tst_QWebFrame::enumerate() +{ + QFETCH(QStringList, expectedNames); + + MyEnumTestQObject enumQObject; + // give it some dynamic properties + enumQObject.setProperty("dp1", "dp1"); + enumQObject.setProperty("dp2", "dp2"); + enumQObject.setProperty("dp3", "dp3"); + m_page->mainFrame()->addToJavaScriptWindowObject("myEnumObject", &enumQObject); + + // enumerate in script + { + evalJS("var enumeratedProperties = []"); + evalJS("for (var p in myEnumObject) { enumeratedProperties.push(p); }"); + QStringList result = evalJSV("enumeratedProperties").toStringList(); + QCOMPARE(result.size(), expectedNames.size()); + for (int i = 0; i < expectedNames.size(); ++i) + QCOMPARE(result.at(i), expectedNames.at(i)); + } +} + +void tst_QWebFrame::objectDeleted() +{ + MyQObject* qobj = new MyQObject(); + m_page->mainFrame()->addToJavaScriptWindowObject("bar", qobj); + evalJS("bar.objectName = 'foo';"); + QCOMPARE(qobj->objectName(), QLatin1String("foo")); + evalJS("bar.intProperty = 123;"); + QCOMPARE(qobj->intProperty(), 123); + qobj->resetQtFunctionInvoked(); + evalJS("bar.myInvokable.call(bar);"); + QCOMPARE(qobj->qtFunctionInvoked(), 0); + + // do this, to ensure that we cache that it implements call + evalJS("bar()"); + + // now delete the object + delete qobj; + + QCOMPARE(evalJS("typeof bar"), sObject); + + // any attempt to access properties of the object should result in an exception + { + QString type; + QString ret = evalJS("bar.objectName", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); + } + { + QString type; + QString ret = evalJS("bar.objectName = 'foo'", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); + } + + // myInvokable is stored in member table (since we've accessed it before deletion) + { + QString type; + evalJS("bar.myInvokable", type); + QCOMPARE(type, sFunction); + } + + { + QString type; + QString ret = evalJS("bar.myInvokable.call(bar);", type); + ret = evalJS("bar.myInvokable(bar)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject")); + } + // myInvokableWithIntArg is not stored in member table (since we've not accessed it) + { + QString type; + QString ret = evalJS("bar.myInvokableWithIntArg", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); + } + + // access from script + evalJS("window.o = bar;"); + { + QString type; + QString ret = evalJS("o.objectName", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: cannot access member `objectName' of deleted QObject")); + } + { + QString type; + QString ret = evalJS("o.myInvokable()", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: cannot call function of deleted QObject")); + } + { + QString type; + QString ret = evalJS("o.myInvokableWithIntArg(10)", type); + QCOMPARE(type, sError); + QCOMPARE(ret, QLatin1String("Error: cannot access member `myInvokableWithIntArg' of deleted QObject")); + } +} + +void tst_QWebFrame::typeConversion() +{ + m_myObject->resetQtFunctionInvoked(); + + QDateTime localdt(QDate(2008,1,18), QTime(12,31,0)); + QDateTime utclocaldt = localdt.toUTC(); + QDateTime utcdt(QDate(2008,1,18), QTime(12,31,0), Qt::UTC); + + // Dates in JS (default to local) + evalJS("myObject.myOverloadedSlot(new Date(2008,0,18,12,31,0))"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 32); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utclocaldt); + + m_myObject->resetQtFunctionInvoked(); + evalJS("myObject.myOverloadedSlot(new Date(Date.UTC(2008,0,18,12,31,0)))"); + QCOMPARE(m_myObject->qtFunctionInvoked(), 32); + QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); + QCOMPARE(m_myObject->qtFunctionActuals().at(0).toDateTime().toUTC(), utcdt); + + // Pushing QDateTimes into JS + // Local + evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(2008,0,18,12,31,0))?true:false;}"); + evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)"); + m_myObject->emitMySignalWithDateTimeArg(localdt); + QCOMPARE(evalJS("window.__date_equals"), sTrue); + evalJS("delete window.__date_equals"); + m_myObject->emitMySignalWithDateTimeArg(utclocaldt); + QCOMPARE(evalJS("window.__date_equals"), sTrue); + evalJS("delete window.__date_equals"); + evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;"); + + // UTC + evalJS("function checkDate(d) {window.__date_equals = (d.toString() == new Date(Date.UTC(2008,0,18,12,31,0)))?true:false; }"); + evalJS("myObject.mySignalWithDateTimeArg.connect(checkDate)"); + m_myObject->emitMySignalWithDateTimeArg(utcdt); + QCOMPARE(evalJS("window.__date_equals"), sTrue); + evalJS("delete window.__date_equals"); + evalJS("myObject.mySignalWithDateTimeArg.disconnect(checkDate); delete checkDate;"); + + // ### RegExps +} + +class StringListTestObject : public QObject { + Q_OBJECT +public Q_SLOTS: + QVariant stringList() + { + return QStringList() << "Q" << "t"; + }; +}; + +void tst_QWebFrame::arrayObjectEnumerable() +{ + QWebPage page; + QWebFrame* frame = page.mainFrame(); + QObject* qobject = new StringListTestObject(); + frame->addToJavaScriptWindowObject("test", qobject, QScriptEngine::ScriptOwnership); + + const QString script("var stringArray = test.stringList();" + "var result = '';" + "for (var i in stringArray) {" + " result += stringArray[i];" + "}" + "result;"); + QCOMPARE(frame->evaluateJavaScript(script).toString(), QString::fromLatin1("Qt")); +} + +void tst_QWebFrame::symmetricUrl() +{ + QVERIFY(m_view->url().isEmpty()); + + QCOMPARE(m_view->history()->count(), 0); + + QUrl dataUrl("data:text/html,<h1>Test"); + + m_view->setUrl(dataUrl); + QCOMPARE(m_view->url(), dataUrl); + QCOMPARE(m_view->history()->count(), 0); + + // loading is _not_ immediate, so the text isn't set just yet. + QVERIFY(m_view->page()->mainFrame()->toPlainText().isEmpty()); + + ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); + + QCOMPARE(m_view->history()->count(), 1); + QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test")); + + QUrl dataUrl2("data:text/html,<h1>Test2"); + QUrl dataUrl3("data:text/html,<h1>Test3"); + + m_view->setUrl(dataUrl2); + m_view->setUrl(dataUrl3); + + QCOMPARE(m_view->url(), dataUrl3); + + ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); + + QCOMPARE(m_view->history()->count(), 2); + + QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("Test3")); +} + +void tst_QWebFrame::progressSignal() +{ + QSignalSpy progressSpy(m_view, SIGNAL(loadProgress(int))); + + QUrl dataUrl("data:text/html,<h1>Test"); + m_view->setUrl(dataUrl); + + ::waitForSignal(m_view, SIGNAL(loadFinished(bool))); + + QVERIFY(progressSpy.size() >= 2); + + // WebKit defines initialProgressValue as 10%, not 0% + QCOMPARE(progressSpy.first().first().toInt(), 10); + + // But we always end at 100% + QCOMPARE(progressSpy.last().first().toInt(), 100); +} + +void tst_QWebFrame::urlChange() +{ + QSignalSpy urlSpy(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); + + QUrl dataUrl("data:text/html,<h1>Test"); + m_view->setUrl(dataUrl); + + ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); + + QCOMPARE(urlSpy.size(), 1); + + QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>"); + m_view->setUrl(dataUrl2); + + ::waitForSignal(m_page->mainFrame(), SIGNAL(urlChanged(QUrl))); + + QCOMPARE(urlSpy.size(), 2); +} + + +void tst_QWebFrame::domCycles() +{ + m_view->setHtml("<html><body>"); + QVariant v = m_page->mainFrame()->evaluateJavaScript("document"); + QVERIFY(v.type() == QVariant::Map); +} + +class FakeReply : public QNetworkReply { + Q_OBJECT + +public: + FakeReply(const QNetworkRequest& request, QObject* parent = 0) + : QNetworkReply(parent) + { + setOperation(QNetworkAccessManager::GetOperation); + setRequest(request); + if (request.url() == QUrl("qrc:/test1.html")) { + setHeader(QNetworkRequest::LocationHeader, QString("qrc:/test2.html")); + setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html")); + } +#ifndef QT_NO_OPENSSL + else if (request.url() == QUrl("qrc:/fake-ssl-error.html")) + setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error !")); // force a ssl error +#endif + else if (request.url().host() == QLatin1String("abcdef.abcdef")) + setError(QNetworkReply::HostNotFoundError, tr("Invalid URL")); + + open(QIODevice::ReadOnly); + QTimer::singleShot(0, this, SLOT(timeout())); + } + ~FakeReply() + { + close(); + } + virtual void abort() {} + virtual void close() {} + +protected: + qint64 readData(char*, qint64) + { + return 0; + } + +private slots: + void timeout() + { + if (request().url() == QUrl("qrc://test1.html")) + emit error(this->error()); + else if (request().url() == QUrl("http://abcdef.abcdef/")) + emit metaDataChanged(); +#ifndef QT_NO_OPENSSL + else if (request().url() == QUrl("qrc:/fake-ssl-error.html")) + return; +#endif + + emit readyRead(); + emit finished(); + } +}; + +class FakeNetworkManager : public QNetworkAccessManager { + Q_OBJECT + +public: + FakeNetworkManager(QObject* parent) : QNetworkAccessManager(parent) { } + +protected: + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) + { + QString url = request.url().toString(); + if (op == QNetworkAccessManager::GetOperation) { + if (url == "qrc:/test1.html" || url == "http://abcdef.abcdef/") + return new FakeReply(request, this); +#ifndef QT_NO_OPENSSL + else if (url == "qrc:/fake-ssl-error.html") { + FakeReply* reply = new FakeReply(request, this); + QList<QSslError> errors; + emit sslErrors(reply, errors << QSslError(QSslError::UnspecifiedError)); + return reply; + } +#endif + } + + return QNetworkAccessManager::createRequest(op, request, outgoingData); + } +}; + +void tst_QWebFrame::requestedUrl() +{ + QWebPage page; + QWebFrame* frame = page.mainFrame(); + + // in few seconds, the image should be completely loaded + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + FakeNetworkManager* networkManager = new FakeNetworkManager(&page); + page.setNetworkAccessManager(networkManager); + + frame->setUrl(QUrl("qrc:/test1.html")); + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy.count(), 1); + QCOMPARE(frame->requestedUrl(), QUrl("qrc:/test1.html")); + QCOMPARE(frame->url(), QUrl("qrc:/test2.html")); + + frame->setUrl(QUrl("qrc:/non-existent.html")); + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy.count(), 2); + QCOMPARE(frame->requestedUrl(), QUrl("qrc:/non-existent.html")); + QCOMPARE(frame->url(), QUrl("qrc:/non-existent.html")); + + frame->setUrl(QUrl("http://abcdef.abcdef")); + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy.count(), 3); + QCOMPARE(frame->requestedUrl(), QUrl("http://abcdef.abcdef/")); + QCOMPARE(frame->url(), QUrl("http://abcdef.abcdef/")); + +#ifndef QT_NO_OPENSSL + qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); + qRegisterMetaType<QNetworkReply* >("QNetworkReply*"); + + QSignalSpy spy2(page.networkAccessManager(), SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>))); + frame->setUrl(QUrl("qrc:/fake-ssl-error.html")); + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy2.count(), 1); + QCOMPARE(frame->requestedUrl(), QUrl("qrc:/fake-ssl-error.html")); + QCOMPARE(frame->url(), QUrl("qrc:/fake-ssl-error.html")); +#endif +} + +void tst_QWebFrame::requestedUrlAfterSetAndLoadFailures() +{ + QWebPage page; + QWebFrame* frame = page.mainFrame(); + + QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); + + const QUrl first("http://abcdef.abcdef/"); + frame->setUrl(first); + ::waitForSignal(frame, SIGNAL(loadFinished(bool))); + QCOMPARE(frame->url(), first); + QCOMPARE(frame->requestedUrl(), first); + QVERIFY(!spy.at(0).first().toBool()); + + const QUrl second("http://abcdef.abcdef/another_page.html"); + QVERIFY(first != second); + + frame->load(second); + ::waitForSignal(frame, SIGNAL(loadFinished(bool))); + QCOMPARE(frame->url(), first); + QCOMPARE(frame->requestedUrl(), second); + QVERIFY(!spy.at(1).first().toBool()); +} + +void tst_QWebFrame::javaScriptWindowObjectCleared_data() +{ + QTest::addColumn<QString>("html"); + QTest::addColumn<int>("signalCount"); + QTest::newRow("with <script>") << "<html><body><script>i=0</script><p>hello world</p></body></html>" << 1; + // NOTE: Empty scripts no longer cause this signal to be emitted. + QTest::newRow("with empty <script>") << "<html><body><script></script><p>hello world</p></body></html>" << 0; + QTest::newRow("without <script>") << "<html><body><p>hello world</p></body></html>" << 0; +} + +void tst_QWebFrame::javaScriptWindowObjectCleared() +{ + QWebPage page; + QWebFrame* frame = page.mainFrame(); + QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); + QFETCH(QString, html); + frame->setHtml(html); + + QFETCH(int, signalCount); + QCOMPARE(spy.count(), signalCount); +} + +void tst_QWebFrame::javaScriptWindowObjectClearedOnEvaluate() +{ + QWebPage page; + QWebFrame* frame = page.mainFrame(); + QSignalSpy spy(frame, SIGNAL(javaScriptWindowObjectCleared())); + frame->setHtml("<html></html>"); + QCOMPARE(spy.count(), 0); + frame->evaluateJavaScript("var a = 'a';"); + QCOMPARE(spy.count(), 1); + // no new clear for a new script: + frame->evaluateJavaScript("var a = 1;"); + QCOMPARE(spy.count(), 1); +} + +void tst_QWebFrame::setHtml() +{ + QString html("<html><head></head><body><p>hello world</p></body></html>"); + QSignalSpy spy(m_view->page(), SIGNAL(loadFinished(bool))); + m_view->page()->mainFrame()->setHtml(html); + QCOMPARE(m_view->page()->mainFrame()->toHtml(), html); + QCOMPARE(spy.count(), 1); +} + +void tst_QWebFrame::setHtmlWithResource() +{ + QString html("<html><body><p>hello world</p><img src='qrc:/image.png'/></body></html>"); + + QWebPage page; + QWebFrame* frame = page.mainFrame(); + + // in few seconds, the image should be completey loaded + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + frame->setHtml(html); + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy.count(), 1); + + QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); + QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); + QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); + + QString html2 = + "<html>" + "<head>" + "<link rel='stylesheet' href='qrc:/style.css' type='text/css' />" + "</head>" + "<body>" + "<p id='idP'>some text</p>" + "</body>" + "</html>"; + + // in few seconds, the CSS should be completey loaded + frame->setHtml(html2); + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy.size(), 2); + + QWebElement p = frame->documentElement().findAll("p").at(0); + QCOMPARE(p.styleProperty("color", QWebElement::CascadedStyle), QLatin1String("red")); +} + +void tst_QWebFrame::setHtmlWithBaseURL() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QDir::setCurrent(TESTS_SOURCE_DIR); + + QString html("<html><body><p>hello world</p><img src='resources/image2.png'/></body></html>"); + + QWebPage page; + QWebFrame* frame = page.mainFrame(); + + // in few seconds, the image should be completey loaded + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + frame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(spy.count(), 1); + + QCOMPARE(frame->evaluateJavaScript("document.images.length").toInt(), 1); + QCOMPARE(frame->evaluateJavaScript("document.images[0].width").toInt(), 128); + QCOMPARE(frame->evaluateJavaScript("document.images[0].height").toInt(), 128); + + // no history item has to be added. + QCOMPARE(m_view->page()->history()->count(), 0); +} + +class MyPage : public QWebPage +{ +public: + MyPage() : QWebPage(), alerts(0) {} + int alerts; + +protected: + virtual void javaScriptAlert(QWebFrame*, const QString& msg) + { + alerts++; + QCOMPARE(msg, QString("foo")); + // Should not be enough to trigger deferred loading, since we've upped the HTML + // tokenizer delay in the Qt frameloader. See HTMLTokenizer::continueProcessing() + QTest::qWait(1000); + } +}; + +void tst_QWebFrame::setHtmlWithJSAlert() +{ + QString html("<html><head></head><body><script>alert('foo');</script><p>hello world</p></body></html>"); + MyPage page; + m_view->setPage(&page); + page.mainFrame()->setHtml(html); + QCOMPARE(page.alerts, 1); + QCOMPARE(m_view->page()->mainFrame()->toHtml(), html); +} + +class TestNetworkManager : public QNetworkAccessManager +{ +public: + TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} + + QList<QUrl> requestedUrls; + +protected: + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { + requestedUrls.append(request.url()); + QNetworkRequest redirectedRequest = request; + redirectedRequest.setUrl(QUrl("data:text/html,<p>hello")); + return QNetworkAccessManager::createRequest(op, redirectedRequest, outgoingData); + } +}; + +void tst_QWebFrame::ipv6HostEncoding() +{ + TestNetworkManager* networkManager = new TestNetworkManager(m_page); + m_page->setNetworkAccessManager(networkManager); + networkManager->requestedUrls.clear(); + + QUrl baseUrl = QUrl::fromEncoded("http://[::1]/index.html"); + m_view->setHtml("<p>Hi", baseUrl); + m_view->page()->mainFrame()->evaluateJavaScript("var r = new XMLHttpRequest();" + "r.open('GET', 'http://[::1]/test.xml', false);" + "r.send(null);" + ); + QCOMPARE(networkManager->requestedUrls.count(), 1); + QCOMPARE(networkManager->requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); +} + +void tst_QWebFrame::metaData() +{ + m_view->setHtml("<html>" + " <head>" + " <meta name=\"description\" content=\"Test description\">" + " <meta name=\"keywords\" content=\"HTML, JavaScript, Css\">" + " </head>" + "</html>"); + + QMultiMap<QString, QString> metaData = m_view->page()->mainFrame()->metaData(); + + QCOMPARE(metaData.count(), 2); + + QCOMPARE(metaData.value("description"), QString("Test description")); + QCOMPARE(metaData.value("keywords"), QString("HTML, JavaScript, Css")); + QCOMPARE(metaData.value("nonexistant"), QString()); + + m_view->setHtml("<html>" + " <head>" + " <meta name=\"samekey\" content=\"FirstValue\">" + " <meta name=\"samekey\" content=\"SecondValue\">" + " </head>" + "</html>"); + + metaData = m_view->page()->mainFrame()->metaData(); + + QCOMPARE(metaData.count(), 2); + + QStringList values = metaData.values("samekey"); + QCOMPARE(values.count(), 2); + + QVERIFY(values.contains("FirstValue")); + QVERIFY(values.contains("SecondValue")); + + QCOMPARE(metaData.value("nonexistant"), QString()); +} + +#if !defined(Q_WS_MAEMO_5) && !defined(Q_OS_SYMBIAN) && !defined(QT_NO_COMBOBOX) +void tst_QWebFrame::popupFocus() +{ + QWebView view; + view.setHtml("<html>" + " <body>" + " <select name=\"select\">" + " <option>1</option>" + " <option>2</option>" + " </select>" + " <input type=\"text\"> </input>" + " <textarea name=\"text_area\" rows=\"3\" cols=\"40\">" + "This test checks whether showing and hiding a popup" + "takes the focus away from the webpage." + " </textarea>" + " </body>" + "</html>"); + view.resize(400, 100); + // Call setFocus before show to work around http://bugreports.qt.nokia.com/browse/QTBUG-14762 + view.setFocus(); + view.show(); + QTest::qWaitForWindowShown(&view); + view.activateWindow(); + QTRY_VERIFY(view.hasFocus()); + + // open the popup by clicking. check if focus is on the popup + const QWebElement webCombo = view.page()->mainFrame()->documentElement().findFirst(QLatin1String("select[name=select]")); + QTest::mouseClick(&view, Qt::LeftButton, 0, webCombo.geometry().center()); + QObject* webpopup = firstChildByClassName(&view, "QComboBox"); + QComboBox* combo = qobject_cast<QComboBox*>(webpopup); + QVERIFY(combo != 0); + QTRY_VERIFY(!view.hasFocus() && combo->view()->hasFocus()); // Focus should be on the popup + + // hide the popup and check if focus is on the page + combo->hidePopup(); + QTRY_VERIFY(view.hasFocus()); // Focus should be back on the WebView +} +#endif + +void tst_QWebFrame::inputFieldFocus() +{ + QWebView view; + view.setHtml("<html><body><input type=\"text\"></input></body></html>"); + view.resize(400, 100); + view.show(); + QTest::qWaitForWindowShown(&view); + view.activateWindow(); + view.setFocus(); + QTRY_VERIFY(view.hasFocus()); + + // double the flashing time, should at least blink once already + int delay = qApp->cursorFlashTime() * 2; + + // focus the lineedit and check if it blinks + bool autoSipEnabled = qApp->autoSipEnabled(); + qApp->setAutoSipEnabled(false); + const QWebElement inputElement = view.page()->mainFrame()->documentElement().findFirst(QLatin1String("input[type=text]")); + QTest::mouseClick(&view, Qt::LeftButton, 0, inputElement.geometry().center()); + m_inputFieldsTestView = &view; + view.installEventFilter( this ); + QTest::qWait(delay); + QVERIFY2(m_inputFieldTestPaintCount >= 3, + "The input field should have a blinking caret"); + qApp->setAutoSipEnabled(autoSipEnabled); +} + +void tst_QWebFrame::hitTestContent() +{ + QString html("<html><body><p>A paragraph</p><br/><br/><br/><a href=\"about:blank\" target=\"_foo\" id=\"link\">link text</a></body></html>"); + + QWebPage page; + QWebFrame* frame = page.mainFrame(); + frame->setHtml(html); + page.setViewportSize(QSize(200, 0)); //no height so link is not visible + const QWebElement linkElement = frame->documentElement().findFirst(QLatin1String("a#link")); + QWebHitTestResult result = frame->hitTestContent(linkElement.geometry().center()); + QCOMPARE(result.linkText(), QString("link text")); + QWebElement link = result.linkElement(); + QCOMPARE(link.attribute("target"), QString("_foo")); +} + +void tst_QWebFrame::jsByteArray() +{ + QByteArray ba("hello world"); + m_myObject->setByteArrayProperty(ba); + + // read-only property + QCOMPARE(m_myObject->byteArrayProperty(), ba); + QString type; + QVariant v = evalJSV("myObject.byteArrayProperty"); + QCOMPARE(int(v.type()), int(QVariant::ByteArray)); + + QCOMPARE(v.toByteArray(), ba); +} + +void tst_QWebFrame::ownership() +{ + // test ownership + { + QPointer<QObject> ptr = new QObject(); + QVERIFY(ptr != 0); + { + QWebPage page; + QWebFrame* frame = page.mainFrame(); + frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::ScriptOwnership); + } + QVERIFY(ptr == 0); + } + { + QPointer<QObject> ptr = new QObject(); + QVERIFY(ptr != 0); + QObject* before = ptr; + { + QWebPage page; + QWebFrame* frame = page.mainFrame(); + frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::QtOwnership); + } + QVERIFY(ptr == before); + delete ptr; + } + { + QObject* parent = new QObject(); + QObject* child = new QObject(parent); + QWebPage page; + QWebFrame* frame = page.mainFrame(); + frame->addToJavaScriptWindowObject("test", child, QScriptEngine::QtOwnership); + QVariant v = frame->evaluateJavaScript("test"); + QCOMPARE(qvariant_cast<QObject*>(v), child); + delete parent; + v = frame->evaluateJavaScript("test"); + QCOMPARE(qvariant_cast<QObject*>(v), (QObject *)0); + } + { + QPointer<QObject> ptr = new QObject(); + QVERIFY(ptr != 0); + { + QWebPage page; + QWebFrame* frame = page.mainFrame(); + frame->addToJavaScriptWindowObject("test", ptr, QScriptEngine::AutoOwnership); + } + // no parent, so it should be like ScriptOwnership + QVERIFY(ptr == 0); + } + { + QObject* parent = new QObject(); + QPointer<QObject> child = new QObject(parent); + QVERIFY(child != 0); + { + QWebPage page; + QWebFrame* frame = page.mainFrame(); + frame->addToJavaScriptWindowObject("test", child, QScriptEngine::AutoOwnership); + } + // has parent, so it should be like QtOwnership + QVERIFY(child != 0); + delete parent; + } +} + +void tst_QWebFrame::nullValue() +{ + QVariant v = m_view->page()->mainFrame()->evaluateJavaScript("null"); + QVERIFY(v.isNull()); +} + +void tst_QWebFrame::baseUrl_data() +{ + QTest::addColumn<QString>("html"); + QTest::addColumn<QUrl>("loadUrl"); + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QUrl>("baseUrl"); + + QTest::newRow("null") << QString() << QUrl() + << QUrl("about:blank") << QUrl("about:blank"); + + QTest::newRow("foo") << QString() << QUrl("http://foobar.baz/") + << QUrl("http://foobar.baz/") << QUrl("http://foobar.baz/"); + + QString html = "<html>" + "<head>" + "<base href=\"http://foobaz.bar/\" />" + "</head>" + "</html>"; + QTest::newRow("customBaseUrl") << html << QUrl("http://foobar.baz/") + << QUrl("http://foobar.baz/") << QUrl("http://foobaz.bar/"); +} + +void tst_QWebFrame::baseUrl() +{ + QFETCH(QString, html); + QFETCH(QUrl, loadUrl); + QFETCH(QUrl, url); + QFETCH(QUrl, baseUrl); + + m_page->mainFrame()->setHtml(html, loadUrl); + QCOMPARE(m_page->mainFrame()->url(), url); + QCOMPARE(m_page->mainFrame()->baseUrl(), baseUrl); +} + +void tst_QWebFrame::hasSetFocus() +{ + QString html("<html><body><p>top</p>" \ + "<iframe width='80%' height='30%'/>" \ + "</body></html>"); + + QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); + m_page->mainFrame()->setHtml(html); + + waitForSignal(m_page->mainFrame(), SIGNAL(loadFinished(bool)), 200); + QCOMPARE(loadSpy.size(), 1); + + QList<QWebFrame*> children = m_page->mainFrame()->childFrames(); + QWebFrame* frame = children.at(0); + QString innerHtml("<html><body><p>another iframe</p>" \ + "<iframe width='80%' height='30%'/>" \ + "</body></html>"); + frame->setHtml(innerHtml); + + waitForSignal(frame, SIGNAL(loadFinished(bool)), 200); + QCOMPARE(loadSpy.size(), 2); + + m_page->mainFrame()->setFocus(); + QTRY_VERIFY(m_page->mainFrame()->hasFocus()); + + for (int i = 0; i < children.size(); ++i) { + children.at(i)->setFocus(); + QTRY_VERIFY(children.at(i)->hasFocus()); + QVERIFY(!m_page->mainFrame()->hasFocus()); + } + + m_page->mainFrame()->setFocus(); + QTRY_VERIFY(m_page->mainFrame()->hasFocus()); +} + +void tst_QWebFrame::render() +{ + QString html("<html>" \ + "<head><style>" \ + "body, iframe { margin: 0px; border: none; }" \ + "</style></head>" \ + "<body><iframe width='100px' height='100px'/></body>" \ + "</html>"); + + QWebPage page; + page.mainFrame()->setHtml(html); + + QList<QWebFrame*> frames = page.mainFrame()->childFrames(); + QWebFrame *frame = frames.at(0); + QString innerHtml("<body style='margin: 0px;'><img src='qrc:/image.png'/></body>"); + frame->setHtml(innerHtml); + + QPicture picture; + + QSize size = page.mainFrame()->contentsSize(); + page.setViewportSize(size); + + // render contents layer only (the iframe is smaller than the image, so it will have scrollbars) + QPainter painter1(&picture); + frame->render(&painter1, QWebFrame::ContentsLayer); + painter1.end(); + + QCOMPARE(size.width(), picture.boundingRect().width() + frame->scrollBarGeometry(Qt::Vertical).width()); + QCOMPARE(size.height(), picture.boundingRect().height() + frame->scrollBarGeometry(Qt::Horizontal).height()); + + // render everything, should be the size of the iframe + QPainter painter2(&picture); + frame->render(&painter2, QWebFrame::AllLayers); + painter2.end(); + + QCOMPARE(size.width(), picture.boundingRect().width()); // width: 100px + QCOMPARE(size.height(), picture.boundingRect().height()); // height: 100px +} + + +class DummyPaintEngine: public QPaintEngine { +public: + + DummyPaintEngine() + : QPaintEngine(QPaintEngine::AllFeatures) + , renderHints(0) + { + } + + bool begin(QPaintDevice*) + { + setActive(true); + return true; + } + + bool end() + { + setActive(false); + return false; + } + + void updateState(const QPaintEngineState& state) + { + renderHints = state.renderHints(); + } + + void drawPath(const QPainterPath&) { } + void drawPixmap(const QRectF&, const QPixmap&, const QRectF&) { } + + QPaintEngine::Type type() const + { + return static_cast<QPaintEngine::Type>(QPaintEngine::User + 2); + } + + QPainter::RenderHints renderHints; +}; + +class DummyPaintDevice: public QPaintDevice { +public: + DummyPaintDevice() + : QPaintDevice() + , m_engine(new DummyPaintEngine) + { + } + + ~DummyPaintDevice() + { + delete m_engine; + } + + QPaintEngine* paintEngine() const + { + return m_engine; + } + + QPainter::RenderHints renderHints() const + { + return m_engine->renderHints; + } + +protected: + int metric(PaintDeviceMetric metric) const; + +private: + DummyPaintEngine* m_engine; + friend class DummyPaintEngine; +}; + + +int DummyPaintDevice::metric(PaintDeviceMetric metric) const +{ + switch (metric) { + case PdmWidth: + return 400; + break; + + case PdmHeight: + return 200; + break; + + case PdmNumColors: + return INT_MAX; + break; + + case PdmDepth: + return 32; + break; + + default: + break; + } + return 0; +} + +void tst_QWebFrame::renderHints() +{ + QString html("<html><body><p>Hello, world!</p></body></html>"); + + QWebPage page; + page.mainFrame()->setHtml(html); + page.setViewportSize(page.mainFrame()->contentsSize()); + + // We will call frame->render and trap the paint engine state changes + // to ensure that GraphicsContext does not clobber the render hints. + DummyPaintDevice buffer; + QPainter painter(&buffer); + + painter.setRenderHint(QPainter::TextAntialiasing, false); + page.mainFrame()->render(&painter); + QVERIFY(!(buffer.renderHints() & QPainter::TextAntialiasing)); + QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); + QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); + + painter.setRenderHint(QPainter::TextAntialiasing, true); + page.mainFrame()->render(&painter); + QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); + QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); + QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); + + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + page.mainFrame()->render(&painter); + QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); + QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); + + painter.setRenderHint(QPainter::HighQualityAntialiasing, true); + page.mainFrame()->render(&painter); + QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); + QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(buffer.renderHints() & QPainter::HighQualityAntialiasing); +} + +void tst_QWebFrame::scrollPosition() +{ + // enlarged image in a small viewport, to provoke the scrollbars to appear + QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); + + QWebPage page; + page.setViewportSize(QSize(200, 200)); + + QWebFrame* frame = page.mainFrame(); + frame->setHtml(html); + frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); + frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); + + // try to set the scroll offset programmatically + frame->setScrollPosition(QPoint(23, 29)); + QCOMPARE(frame->scrollPosition().x(), 23); + QCOMPARE(frame->scrollPosition().y(), 29); + + int x = frame->evaluateJavaScript("window.scrollX").toInt(); + int y = frame->evaluateJavaScript("window.scrollY").toInt(); + QCOMPARE(x, 23); + QCOMPARE(y, 29); +} + +void tst_QWebFrame::scrollToAnchor() +{ + QWebPage page; + page.setViewportSize(QSize(480, 800)); + QWebFrame* frame = page.mainFrame(); + + QString html("<html><body><p style=\"margin-bottom: 1500px;\">Hello.</p>" + "<p><a id=\"foo\">This</a> is an anchor</p>" + "<p style=\"margin-bottom: 1500px;\"><a id=\"bar\">This</a> is another anchor</p>" + "</body></html>"); + frame->setHtml(html); + frame->setScrollPosition(QPoint(0, 0)); + QCOMPARE(frame->scrollPosition().x(), 0); + QCOMPARE(frame->scrollPosition().y(), 0); + + QWebElement fooAnchor = frame->findFirstElement("a[id=foo]"); + + frame->scrollToAnchor("foo"); + QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top()); + + frame->scrollToAnchor("bar"); + frame->scrollToAnchor("foo"); + QCOMPARE(frame->scrollPosition().y(), fooAnchor.geometry().top()); + + frame->scrollToAnchor("top"); + QCOMPARE(frame->scrollPosition().y(), 0); + + frame->scrollToAnchor("bar"); + frame->scrollToAnchor("notexist"); + QVERIFY(frame->scrollPosition().y() != 0); +} + + +void tst_QWebFrame::scrollbarsOff() +{ + QWebView view; + QWebFrame* mainFrame = view.page()->mainFrame(); + + mainFrame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); + mainFrame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); + + QString html("<script>" \ + " function checkScrollbar() {" \ + " if (innerWidth === document.documentElement.offsetWidth)" \ + " document.getElementById('span1').innerText = 'SUCCESS';" \ + " else" \ + " document.getElementById('span1').innerText = 'FAIL';" \ + " }" \ + "</script>" \ + "<body>" \ + " <div style='margin-top:1000px ; margin-left:1000px'>" \ + " <a id='offscreen' href='a'>End</a>" \ + " </div>" \ + "<span id='span1'></span>" \ + "</body>"); + + + view.setHtml(html); + ::waitForSignal(&view, SIGNAL(loadFinished(bool))); + + mainFrame->evaluateJavaScript("checkScrollbar();"); + QCOMPARE(mainFrame->documentElement().findAll("span").at(0).toPlainText(), QString("SUCCESS")); +} + +void tst_QWebFrame::horizontalScrollAfterBack() +{ + QWebView view; + QWebFrame* frame = view.page()->mainFrame(); + QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); + + view.page()->settings()->setMaximumPagesInCache(2); + frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); + frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); + + view.load(QUrl("qrc:/testiframe2.html")); + view.resize(200, 200); + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height()); + + view.load(QUrl("qrc:/testiframe.html")); + QTRY_COMPARE(loadSpy.count(), 2); + + view.page()->triggerAction(QWebPage::Back); + QTRY_COMPARE(loadSpy.count(), 3); + QTRY_VERIFY((frame->scrollBarGeometry(Qt::Horizontal)).height()); +} + +void tst_QWebFrame::evaluateWillCauseRepaint() +{ + QWebView view; + QString html("<html><body>top<div id=\"junk\" style=\"display: block;\">" + "junk</div>bottom</body></html>"); + view.setHtml(html); + view.show(); + + QTest::qWaitForWindowShown(&view); + view.page()->mainFrame()->evaluateJavaScript( + "document.getElementById('junk').style.display = 'none';"); + + ::waitForSignal(view.page(), SIGNAL(repaintRequested(QRect))); +} + +class TestFactory : public QObject +{ + Q_OBJECT +public: + TestFactory() + : obj(0), counter(0) + {} + + Q_INVOKABLE QObject* getNewObject() + { + delete obj; + obj = new QObject(this); + obj->setObjectName(QLatin1String("test") + QString::number(++counter)); + return obj; + + } + + QObject* obj; + int counter; +}; + +void tst_QWebFrame::qObjectWrapperWithSameIdentity() +{ + m_view->setHtml("<script>function triggerBug() { document.getElementById('span1').innerText = test.getNewObject().objectName; }</script>" + "<body><span id='span1'>test</span></body>"); + + QWebFrame* mainFrame = m_view->page()->mainFrame(); + QCOMPARE(mainFrame->toPlainText(), QString("test")); + + mainFrame->addToJavaScriptWindowObject("test", new TestFactory, QScriptEngine::ScriptOwnership); + + mainFrame->evaluateJavaScript("triggerBug();"); + QCOMPARE(mainFrame->toPlainText(), QString("test1")); + + mainFrame->evaluateJavaScript("triggerBug();"); + QCOMPARE(mainFrame->toPlainText(), QString("test2")); +} + +void tst_QWebFrame::introspectQtMethods_data() +{ + QTest::addColumn<QString>("objectExpression"); + QTest::addColumn<QString>("methodName"); + QTest::addColumn<QStringList>("expectedPropertyNames"); + + QTest::newRow("myObject.mySignal") + << "myObject" << "mySignal" << (QStringList() << "connect" << "disconnect" << "length" << "name"); + QTest::newRow("myObject.mySlot") + << "myObject" << "mySlot" << (QStringList() << "connect" << "disconnect" << "length" << "name"); + QTest::newRow("myObject.myInvokable") + << "myObject" << "myInvokable" << (QStringList() << "connect" << "disconnect" << "length" << "name"); + QTest::newRow("myObject.mySignal.connect") + << "myObject.mySignal" << "connect" << (QStringList() << "length" << "name"); + QTest::newRow("myObject.mySignal.disconnect") + << "myObject.mySignal" << "disconnect" << (QStringList() << "length" << "name"); +} + +void tst_QWebFrame::introspectQtMethods() +{ + QFETCH(QString, objectExpression); + QFETCH(QString, methodName); + QFETCH(QStringList, expectedPropertyNames); + + QString methodLookup = QString::fromLatin1("%0['%1']").arg(objectExpression).arg(methodName); + QCOMPARE(evalJSV(QString::fromLatin1("Object.getOwnPropertyNames(%0).sort()").arg(methodLookup)).toStringList(), expectedPropertyNames); + + for (int i = 0; i < expectedPropertyNames.size(); ++i) { + QString name = expectedPropertyNames.at(i); + QCOMPARE(evalJS(QString::fromLatin1("%0.hasOwnProperty('%1')").arg(methodLookup).arg(name)), sTrue); + evalJS(QString::fromLatin1("var descriptor = Object.getOwnPropertyDescriptor(%0, '%1')").arg(methodLookup).arg(name)); + QCOMPARE(evalJS("typeof descriptor"), QString::fromLatin1("object")); + QCOMPARE(evalJS("descriptor.get"), sUndefined); + QCOMPARE(evalJS("descriptor.set"), sUndefined); + QCOMPARE(evalJS(QString::fromLatin1("descriptor.value === %0['%1']").arg(methodLookup).arg(name)), sTrue); + QCOMPARE(evalJS(QString::fromLatin1("descriptor.enumerable")), sFalse); + QCOMPARE(evalJS(QString::fromLatin1("descriptor.configurable")), sFalse); + } + + QVERIFY(evalJSV("var props=[]; for (var p in myObject.deleteLater) {props.push(p);}; props.sort()").toStringList().isEmpty()); +} + +void tst_QWebFrame::setContent_data() +{ + QTest::addColumn<QString>("mimeType"); + QTest::addColumn<QByteArray>("testContents"); + QTest::addColumn<QString>("expected"); + + QString str = QString::fromUtf8("ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει"); + QTest::newRow("UTF-8 plain text") << "text/plain; charset=utf-8" << str.toUtf8() << str; + + QTextCodec *utf16 = QTextCodec::codecForName("UTF-16"); + if (utf16) + QTest::newRow("UTF-16 plain text") << "text/plain; charset=utf-16" << utf16->fromUnicode(str) << str; + + str = QString::fromUtf8("Une chaîne de caractères à sa façon."); + QTest::newRow("latin-1 plain text") << "text/plain; charset=iso-8859-1" << str.toLatin1() << str; + + +} + +void tst_QWebFrame::setContent() +{ + QFETCH(QString, mimeType); + QFETCH(QByteArray, testContents); + QFETCH(QString, expected); + m_view->setContent(testContents, mimeType); + QWebFrame* mainFrame = m_view->page()->mainFrame(); + QCOMPARE(expected , mainFrame->toPlainText()); +} + +class CacheNetworkAccessManager : public QNetworkAccessManager { +public: + CacheNetworkAccessManager(QObject* parent = 0) + : QNetworkAccessManager(parent) + , m_lastCacheLoad(QNetworkRequest::PreferNetwork) + { + } + + virtual QNetworkReply* createRequest(Operation, const QNetworkRequest& request, QIODevice*) + { + QVariant cacheLoad = request.attribute(QNetworkRequest::CacheLoadControlAttribute); + if (cacheLoad.isValid()) + m_lastCacheLoad = static_cast<QNetworkRequest::CacheLoadControl>(cacheLoad.toUInt()); + else + m_lastCacheLoad = QNetworkRequest::PreferNetwork; // default value + return new FakeReply(request, this); + } + + QNetworkRequest::CacheLoadControl lastCacheLoad() const + { + return m_lastCacheLoad; + } + +private: + QNetworkRequest::CacheLoadControl m_lastCacheLoad; +}; + +void tst_QWebFrame::setCacheLoadControlAttribute() +{ + QWebPage page; + CacheNetworkAccessManager* manager = new CacheNetworkAccessManager(&page); + page.setNetworkAccessManager(manager); + QWebFrame* frame = page.mainFrame(); + + QNetworkRequest request(QUrl("http://abcdef.abcdef/")); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); + frame->load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysCache); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + frame->load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferCache); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + frame->load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::AlwaysNetwork); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); + frame->load(request); + QCOMPARE(manager->lastCacheLoad(), QNetworkRequest::PreferNetwork); +} + +void tst_QWebFrame::webElementSlotOnly() +{ + MyWebElementSlotOnlyObject object; + m_page->mainFrame()->setHtml("<html><head><body></body></html>"); + m_page->mainFrame()->addToJavaScriptWindowObject("myWebElementSlotObject", &object); + evalJS("myWebElementSlotObject.doSomethingWithWebElement(document.body)"); + QCOMPARE(evalJS("myWebElementSlotObject.tagName"), QString("BODY")); +} + +void tst_QWebFrame::setUrlWithPendingLoads() +{ + QWebPage page; + page.mainFrame()->setHtml("<img src='dummy:'/>"); + page.mainFrame()->setUrl(QUrl("about:blank")); +} + +void tst_QWebFrame::setUrlWithFragment_data() +{ + QTest::addColumn<QUrl>("previousUrl"); + QTest::newRow("empty") << QUrl(); + QTest::newRow("same URL no fragment") << QUrl("qrc:/test1.html"); + // See comments in setUrlSameUrl about using setUrl() with the same url(). + QTest::newRow("same URL with same fragment") << QUrl("qrc:/test1.html#"); + QTest::newRow("same URL with different fragment") << QUrl("qrc:/test1.html#anotherFragment"); + QTest::newRow("another URL") << QUrl("qrc:/test2.html"); +} + +// Based on bug report https://bugs.webkit.org/show_bug.cgi?id=32723 +void tst_QWebFrame::setUrlWithFragment() +{ + QFETCH(QUrl, previousUrl); + + QWebPage page; + QWebFrame* frame = page.mainFrame(); + + if (!previousUrl.isEmpty()) { + frame->load(previousUrl); + ::waitForSignal(frame, SIGNAL(loadFinished(bool))); + QCOMPARE(frame->url(), previousUrl); + } + + QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); + const QUrl url("qrc:/test1.html#"); + QVERIFY(!url.fragment().isNull()); + + frame->setUrl(url); + ::waitForSignal(frame, SIGNAL(loadFinished(bool))); + + QCOMPARE(spy.count(), 1); + QVERIFY(!frame->toPlainText().isEmpty()); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(frame->url(), url); +} + +void tst_QWebFrame::setUrlToEmpty() +{ + int expectedLoadFinishedCount = 0; + const QUrl aboutBlank("about:blank"); + const QUrl url("qrc:/test2.html"); + + QWebPage page; + QWebFrame* frame = page.mainFrame(); + QCOMPARE(frame->url(), QUrl()); + QCOMPARE(frame->requestedUrl(), QUrl()); + QCOMPARE(frame->baseUrl(), QUrl()); + + QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); + + // Set existing url + frame->setUrl(url); + expectedLoadFinishedCount++; + ::waitForSignal(frame, SIGNAL(loadFinished(bool))); + + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(frame->baseUrl(), url); + + // Set empty url + frame->setUrl(QUrl()); + expectedLoadFinishedCount++; + + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), aboutBlank); + QCOMPARE(frame->requestedUrl(), QUrl()); + QCOMPARE(frame->baseUrl(), aboutBlank); + + // Set existing url + frame->setUrl(url); + expectedLoadFinishedCount++; + ::waitForSignal(frame, SIGNAL(loadFinished(bool))); + + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(frame->baseUrl(), url); + + // Load empty url + frame->load(QUrl()); + expectedLoadFinishedCount++; + + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), aboutBlank); + QCOMPARE(frame->requestedUrl(), QUrl()); + QCOMPARE(frame->baseUrl(), aboutBlank); +} + +void tst_QWebFrame::setUrlToInvalid() +{ + QWebPage page; + QWebFrame* frame = page.mainFrame(); + + const QUrl invalidUrl("http://strange;hostname/here"); + QVERIFY(!invalidUrl.isEmpty()); + QVERIFY(!invalidUrl.isValid()); + QVERIFY(invalidUrl != QUrl()); + + frame->setUrl(invalidUrl); + QCOMPARE(frame->url(), invalidUrl); + QCOMPARE(frame->requestedUrl(), invalidUrl); + QCOMPARE(frame->baseUrl(), invalidUrl); + + // QUrls equivalent to QUrl() will be treated as such. + const QUrl aboutBlank("about:blank"); + const QUrl anotherInvalidUrl("1http://bugs.webkit.org"); + QVERIFY(!anotherInvalidUrl.isEmpty()); // and they are not necessarily empty. + QVERIFY(!anotherInvalidUrl.isValid()); + QCOMPARE(anotherInvalidUrl, QUrl()); + + frame->setUrl(anotherInvalidUrl); + QCOMPARE(frame->url(), aboutBlank); + QCOMPARE(frame->requestedUrl(), anotherInvalidUrl); + QCOMPARE(frame->baseUrl(), aboutBlank); +} + +void tst_QWebFrame::setUrlHistory() +{ + const QUrl aboutBlank("about:blank"); + QUrl url; + int expectedLoadFinishedCount = 0; + QWebFrame* frame = m_page->mainFrame(); + QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); + + QCOMPARE(m_page->history()->count(), 0); + + frame->setUrl(QUrl()); + expectedLoadFinishedCount++; + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), aboutBlank); + QCOMPARE(frame->requestedUrl(), QUrl()); + QCOMPARE(m_page->history()->count(), 0); + + url = QUrl("http://non.existant/"); + frame->setUrl(url); + ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); + expectedLoadFinishedCount++; + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(m_page->history()->count(), 0); + + url = QUrl("qrc:/test1.html"); + frame->setUrl(url); + ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); + expectedLoadFinishedCount++; + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(m_page->history()->count(), 1); + + frame->setUrl(QUrl()); + expectedLoadFinishedCount++; + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), aboutBlank); + QCOMPARE(frame->requestedUrl(), QUrl()); + QCOMPARE(m_page->history()->count(), 1); + + // Loading same page as current in history, so history count doesn't change. + url = QUrl("qrc:/test1.html"); + frame->setUrl(url); + ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); + expectedLoadFinishedCount++; + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(m_page->history()->count(), 1); + + url = QUrl("qrc:/test2.html"); + frame->setUrl(url); + ::waitForSignal(m_page, SIGNAL(loadFinished(bool))); + expectedLoadFinishedCount++; + QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(m_page->history()->count(), 2); +} + +void tst_QWebFrame::setUrlSameUrl() +{ + const QUrl url1("qrc:/test1.html"); + const QUrl url2("qrc:/test2.html"); + + QWebPage page; + QWebFrame* frame = page.mainFrame(); + FakeNetworkManager* networkManager = new FakeNetworkManager(&page); + page.setNetworkAccessManager(networkManager); + + QSignalSpy spy(frame, SIGNAL(loadFinished(bool))); + + frame->setUrl(url1); + waitForSignal(frame, SIGNAL(loadFinished(bool))); + QVERIFY(frame->url() != url1); // Nota bene: our QNAM redirects url1 to url2 + QCOMPARE(frame->url(), url2); + QCOMPARE(spy.count(), 1); + + frame->setUrl(url1); + waitForSignal(frame, SIGNAL(loadFinished(bool))); + QVERIFY(frame->url() != url1); + QCOMPARE(frame->url(), url2); + QCOMPARE(spy.count(), 2); + + // Now a case without redirect. The existing behavior we have for setUrl() + // is more like a "clear(); load()", so the page will be loaded again, even + // if urlToBeLoaded == url(). This test should be changed if we want to + // make setUrl() early return in this case. + frame->setUrl(url2); + waitForSignal(frame, SIGNAL(loadFinished(bool))); + QCOMPARE(frame->url(), url2); + QCOMPARE(spy.count(), 3); + + frame->setUrl(url1); + waitForSignal(frame, SIGNAL(loadFinished(bool))); + QCOMPARE(frame->url(), url2); + QCOMPARE(spy.count(), 4); +} + +static inline QUrl extractBaseUrl(const QUrl& url) +{ + return url.resolved(QUrl()); +} + +void tst_QWebFrame::setUrlThenLoads_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QUrl>("baseUrl"); + + QTest::newRow("resource file") << QUrl("qrc:/test1.html") << extractBaseUrl(QUrl("qrc:/test1.html")); + QTest::newRow("base specified in HTML") << QUrl("data:text/html,<head><base href=\"http://different.base/\"></head>") << QUrl("http://different.base/"); +} + +void tst_QWebFrame::setUrlThenLoads() +{ + QFETCH(QUrl, url); + QFETCH(QUrl, baseUrl); + QWebFrame* frame = m_page->mainFrame(); + QSignalSpy urlChangedSpy(frame, SIGNAL(urlChanged(QUrl))); + QSignalSpy startedSpy(frame, SIGNAL(loadStarted())); + QSignalSpy finishedSpy(frame, SIGNAL(loadFinished(bool))); + + frame->setUrl(url); + QCOMPARE(startedSpy.count(), 1); + ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); + QCOMPARE(urlChangedSpy.count(), 1); + QVERIFY(finishedSpy.at(0).first().toBool()); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), url); + QCOMPARE(frame->baseUrl(), baseUrl); + + const QUrl urlToLoad1("qrc:/test2.html"); + const QUrl urlToLoad2("qrc:/test1.html"); + + // Just after first load. URL didn't changed yet. + frame->load(urlToLoad1); + QCOMPARE(startedSpy.count(), 2); + QCOMPARE(frame->url(), url); + QCOMPARE(frame->requestedUrl(), urlToLoad1); + QCOMPARE(frame->baseUrl(), baseUrl); + + // After first URL changed. + ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); + QCOMPARE(urlChangedSpy.count(), 2); + QVERIFY(finishedSpy.at(1).first().toBool()); + QCOMPARE(frame->url(), urlToLoad1); + QCOMPARE(frame->requestedUrl(), urlToLoad1); + QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1)); + + // Just after second load. URL didn't changed yet. + frame->load(urlToLoad2); + QCOMPARE(startedSpy.count(), 3); + QCOMPARE(frame->url(), urlToLoad1); + QCOMPARE(frame->requestedUrl(), urlToLoad2); + QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad1)); + + // After second URL changed. + ::waitForSignal(frame, SIGNAL(urlChanged(QUrl))); + QCOMPARE(urlChangedSpy.count(), 3); + QVERIFY(finishedSpy.at(2).first().toBool()); + QCOMPARE(frame->url(), urlToLoad2); + QCOMPARE(frame->requestedUrl(), urlToLoad2); + QCOMPARE(frame->baseUrl(), extractBaseUrl(urlToLoad2)); +} + +QTEST_MAIN(tst_QWebFrame) +#include "tst_qwebframe.moc" diff --git a/Source/WebKit/qt/tests/qwebframe/tst_qwebframe.qrc b/Source/WebKit/qt/tests/qwebframe/tst_qwebframe.qrc new file mode 100644 index 0000000..2a7d0b9 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebframe/tst_qwebframe.qrc @@ -0,0 +1,10 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> +<file alias="image.png">resources/image.png</file> +<file alias="style.css">resources/style.css</file> +<file alias="test1.html">resources/test1.html</file> +<file alias="test2.html">resources/test2.html</file> +<file alias="testiframe.html">resources/testiframe.html</file> +<file alias="testiframe2.html">resources/testiframe2.html</file> +</qresource> +</RCC> diff --git a/Source/WebKit/qt/tests/qwebhistory/qwebhistory.pro b/Source/WebKit/qt/tests/qwebhistory/qwebhistory.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/qwebhistory.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebhistory/resources/page1.html b/Source/WebKit/qt/tests/qwebhistory/resources/page1.html new file mode 100644 index 0000000..82fa4af --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/resources/page1.html @@ -0,0 +1 @@ +<title>page1</title><body><h1>page1</h1></body> diff --git a/Source/WebKit/qt/tests/qwebhistory/resources/page2.html b/Source/WebKit/qt/tests/qwebhistory/resources/page2.html new file mode 100644 index 0000000..5307bdc --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/resources/page2.html @@ -0,0 +1 @@ +<title>page2</title><body><h1>page2</h1></body> diff --git a/Source/WebKit/qt/tests/qwebhistory/resources/page3.html b/Source/WebKit/qt/tests/qwebhistory/resources/page3.html new file mode 100644 index 0000000..4e5547c --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/resources/page3.html @@ -0,0 +1 @@ +<title>page3</title><body><h1>page3</h1></body> diff --git a/Source/WebKit/qt/tests/qwebhistory/resources/page4.html b/Source/WebKit/qt/tests/qwebhistory/resources/page4.html new file mode 100644 index 0000000..3c57aed --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/resources/page4.html @@ -0,0 +1 @@ +<title>page4</title><body><h1>page4</h1></body> diff --git a/Source/WebKit/qt/tests/qwebhistory/resources/page5.html b/Source/WebKit/qt/tests/qwebhistory/resources/page5.html new file mode 100644 index 0000000..8593552 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/resources/page5.html @@ -0,0 +1 @@ +<title>page5</title><body><h1>page5</h1></body> diff --git a/Source/WebKit/qt/tests/qwebhistory/resources/page6.html b/Source/WebKit/qt/tests/qwebhistory/resources/page6.html new file mode 100644 index 0000000..c5bbc6f --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/resources/page6.html @@ -0,0 +1 @@ +<title>page6</title><body><h1>page6</h1></body> diff --git a/Source/WebKit/qt/tests/qwebhistory/tst_qwebhistory.cpp b/Source/WebKit/qt/tests/qwebhistory/tst_qwebhistory.cpp new file mode 100644 index 0000000..783214c --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/tst_qwebhistory.cpp @@ -0,0 +1,397 @@ +/* + Copyright (C) 2008 Holger Hans Peter Freyther + + 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 <QtTest/QtTest> +#include <QAction> + +#include "qwebpage.h" +#include "qwebview.h" +#include "qwebframe.h" +#include "qwebhistory.h" +#include "qdebug.h" + +class tst_QWebHistory : public QObject +{ + Q_OBJECT + +public: + tst_QWebHistory(); + virtual ~tst_QWebHistory(); + +protected : + void loadPage(int nr) + { + frame->load(QUrl("qrc:/resources/page" + QString::number(nr) + ".html")); + waitForLoadFinished.exec(); + } + +public slots: + void init(); + void cleanup(); + +private slots: + void title(); + void count(); + void back(); + void forward(); + void itemAt(); + void goToItem(); + void items(); + void serialize_1(); //QWebHistory countity + void serialize_2(); //QWebHistory index + void serialize_3(); //QWebHistoryItem + void saveAndRestore_crash_1(); + void saveAndRestore_crash_2(); + void saveAndRestore_crash_3(); + void popPushState_data(); + void popPushState(); + void clear(); + + +private: + QWebPage* page; + QWebFrame* frame; + QWebHistory* hist; + QEventLoop waitForLoadFinished; //operation on history are asynchronous! + int histsize; +}; + +tst_QWebHistory::tst_QWebHistory() +{ +} + +tst_QWebHistory::~tst_QWebHistory() +{ +} + +void tst_QWebHistory::init() +{ + page = new QWebPage(this); + frame = page->mainFrame(); + connect(page, SIGNAL(loadFinished(bool)), &waitForLoadFinished, SLOT(quit()), Qt::QueuedConnection); + + for (int i = 1;i < 6;i++) { + loadPage(i); + } + hist = page->history(); + histsize = 5; +} + +void tst_QWebHistory::cleanup() +{ + delete page; +} + +/** + * Check QWebHistoryItem::title() method + */ +void tst_QWebHistory::title() +{ + QCOMPARE(hist->currentItem().title(), QString("page5")); +} + +/** + * Check QWebHistory::count() method + */ +void tst_QWebHistory::count() +{ + QCOMPARE(hist->count(), histsize); +} + +/** + * Check QWebHistory::back() method + */ +void tst_QWebHistory::back() +{ + for (int i = histsize;i > 1;i--) { + QCOMPARE(page->mainFrame()->toPlainText(), QString("page") + QString::number(i)); + hist->back(); + waitForLoadFinished.exec(); + } + //try one more time (too many). crash test + hist->back(); + QCOMPARE(page->mainFrame()->toPlainText(), QString("page1")); +} + +/** + * Check QWebHistory::forward() method + */ +void tst_QWebHistory::forward() +{ + //rewind history :-) + while (hist->canGoBack()) { + hist->back(); + waitForLoadFinished.exec(); + } + + for (int i = 1;i < histsize;i++) { + QCOMPARE(page->mainFrame()->toPlainText(), QString("page") + QString::number(i)); + hist->forward(); + waitForLoadFinished.exec(); + } + //try one more time (too many). crash test + hist->forward(); + QCOMPARE(page->mainFrame()->toPlainText(), QString("page") + QString::number(histsize)); +} + +/** + * Check QWebHistory::itemAt() method + */ +void tst_QWebHistory::itemAt() +{ + for (int i = 1;i < histsize;i++) { + QCOMPARE(hist->itemAt(i - 1).title(), QString("page") + QString::number(i)); + QVERIFY(hist->itemAt(i - 1).isValid()); + } + //check out of range values + QVERIFY(!hist->itemAt(-1).isValid()); + QVERIFY(!hist->itemAt(histsize).isValid()); +} + +/** + * Check QWebHistory::goToItem() method + */ +void tst_QWebHistory::goToItem() +{ + QWebHistoryItem current = hist->currentItem(); + hist->back(); + waitForLoadFinished.exec(); + hist->back(); + waitForLoadFinished.exec(); + QVERIFY(hist->currentItem().title() != current.title()); + hist->goToItem(current); + waitForLoadFinished.exec(); + QCOMPARE(hist->currentItem().title(), current.title()); +} + +/** + * Check QWebHistory::items() method + */ +void tst_QWebHistory::items() +{ + QList<QWebHistoryItem> items = hist->items(); + //check count + QCOMPARE(histsize, items.count()); + + //check order + for (int i = 1;i <= histsize;i++) { + QCOMPARE(items.at(i - 1).title(), QString("page") + QString::number(i)); + } +} + +/** + * Check history state after serialization (pickle, persistent..) method + * Checks history size, history order + */ +void tst_QWebHistory::serialize_1() +{ + QByteArray tmp; //buffer + QDataStream save(&tmp, QIODevice::WriteOnly); //here data will be saved + QDataStream load(&tmp, QIODevice::ReadOnly); //from here data will be loaded + + save << *hist; + QVERIFY(save.status() == QDataStream::Ok); + QCOMPARE(hist->count(), histsize); + + //check size of history + //load next page to find differences + loadPage(6); + QCOMPARE(hist->count(), histsize + 1); + load >> *hist; + QVERIFY(load.status() == QDataStream::Ok); + QCOMPARE(hist->count(), histsize); + + //check order of historyItems + QList<QWebHistoryItem> items = hist->items(); + for (int i = 1;i <= histsize;i++) { + QCOMPARE(items.at(i - 1).title(), QString("page") + QString::number(i)); + } +} + +/** + * Check history state after serialization (pickle, persistent..) method + * Checks history currentIndex value + */ +void tst_QWebHistory::serialize_2() +{ + QByteArray tmp; //buffer + QDataStream save(&tmp, QIODevice::WriteOnly); //here data will be saved + QDataStream load(&tmp, QIODevice::ReadOnly); //from here data will be loaded + + int oldCurrentIndex = hist->currentItemIndex(); + + hist->back(); + waitForLoadFinished.exec(); + hist->back(); + waitForLoadFinished.exec(); + //check if current index was changed (make sure that it is not last item) + QVERIFY(hist->currentItemIndex() != oldCurrentIndex); + //save current index + oldCurrentIndex = hist->currentItemIndex(); + + save << *hist; + QVERIFY(save.status() == QDataStream::Ok); + load >> *hist; + QVERIFY(load.status() == QDataStream::Ok); + + //check current index + QCOMPARE(hist->currentItemIndex(), oldCurrentIndex); +} + +/** + * Check history state after serialization (pickle, persistent..) method + * Checks QWebHistoryItem public property after serialization + */ +void tst_QWebHistory::serialize_3() +{ + QByteArray tmp; //buffer + QDataStream save(&tmp, QIODevice::WriteOnly); //here data will be saved + QDataStream load(&tmp, QIODevice::ReadOnly); //from here data will be loaded + + //prepare two different history items + QWebHistoryItem a = hist->currentItem(); + a.setUserData("A - user data"); + + //check properties BEFORE serialization + QString title(a.title()); + QDateTime lastVisited(a.lastVisited()); + QUrl originalUrl(a.originalUrl()); + QUrl url(a.url()); + QVariant userData(a.userData()); + + save << *hist; + QVERIFY(save.status() == QDataStream::Ok); + QVERIFY(!load.atEnd()); + hist->clear(); + QVERIFY(hist->count() == 1); + load >> *hist; + QVERIFY(load.status() == QDataStream::Ok); + QWebHistoryItem b = hist->currentItem(); + + //check properties AFTER serialization + QCOMPARE(b.title(), title); + QCOMPARE(b.lastVisited(), lastVisited); + QCOMPARE(b.originalUrl(), originalUrl); + QCOMPARE(b.url(), url); + QCOMPARE(b.userData(), userData); + + //Check if all data was read + QVERIFY(load.atEnd()); +} + +static void saveHistory(QWebHistory* history, QByteArray* in) +{ + in->clear(); + QDataStream save(in, QIODevice::WriteOnly); + save << *history; +} + +static void restoreHistory(QWebHistory* history, QByteArray* out) +{ + QDataStream load(out, QIODevice::ReadOnly); + load >> *history; +} + +/** The test shouldn't crash */ +void tst_QWebHistory::saveAndRestore_crash_1() +{ + QByteArray buffer; + saveHistory(hist, &buffer); + for (unsigned i = 0; i < 5; i++) { + restoreHistory(hist, &buffer); + saveHistory(hist, &buffer); + } +} + +/** The test shouldn't crash */ +void tst_QWebHistory::saveAndRestore_crash_2() +{ + QByteArray buffer; + saveHistory(hist, &buffer); + QWebPage* page2 = new QWebPage(this); + QWebHistory* hist2 = page2->history(); + for (unsigned i = 0; i < 5; i++) { + restoreHistory(hist2, &buffer); + saveHistory(hist2, &buffer); + } + delete page2; +} + +/** The test shouldn't crash */ +void tst_QWebHistory::saveAndRestore_crash_3() +{ + QByteArray buffer; + saveHistory(hist, &buffer); + QWebPage* page2 = new QWebPage(this); + QWebHistory* hist1 = hist; + QWebHistory* hist2 = page2->history(); + for (unsigned i = 0; i < 5; i++) { + restoreHistory(hist1, &buffer); + restoreHistory(hist2, &buffer); + QVERIFY(hist1->count() == hist2->count()); + QVERIFY(hist1->count() == histsize); + hist2->back(); + saveHistory(hist2, &buffer); + hist2->clear(); + } + delete page2; +} + +void tst_QWebHistory::popPushState_data() +{ + QTest::addColumn<QString>("script"); + QTest::newRow("pushState") << "history.pushState(123, \"foo\");"; + QTest::newRow("replaceState") << "history.replaceState(\"a\", \"b\");"; + QTest::newRow("back") << "history.back();"; + QTest::newRow("forward") << "history.forward();"; + QTest::newRow("clearState") << "history.clearState();"; +} + +/** Crash test, WebKit bug 38840 (https://bugs.webkit.org/show_bug.cgi?id=38840) */ +void tst_QWebHistory::popPushState() +{ + QFETCH(QString, script); + QWebPage page; + page.mainFrame()->setHtml("<html><body>long live Qt!</body></html>"); + page.mainFrame()->evaluateJavaScript(script); +} + +/** ::clear */ +void tst_QWebHistory::clear() +{ + QByteArray buffer; + + QAction* actionBack = page->action(QWebPage::Back); + QVERIFY(actionBack->isEnabled()); + saveHistory(hist, &buffer); + QVERIFY(hist->count() > 1); + hist->clear(); + QVERIFY(hist->count() == 1); // Leave current item. + QVERIFY(!actionBack->isEnabled()); + + QWebPage* page2 = new QWebPage(this); + QWebHistory* hist2 = page2->history(); + QVERIFY(hist2->count() == 0); + hist2->clear(); + QVERIFY(hist2->count() == 0); // Do not change anything. + delete page2; +} + +QTEST_MAIN(tst_QWebHistory) +#include "tst_qwebhistory.moc" diff --git a/Source/WebKit/qt/tests/qwebhistory/tst_qwebhistory.qrc b/Source/WebKit/qt/tests/qwebhistory/tst_qwebhistory.qrc new file mode 100644 index 0000000..6e2f50a --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistory/tst_qwebhistory.qrc @@ -0,0 +1,11 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>resources/page1.html</file> + <file>resources/page2.html</file> + <file>resources/page3.html</file> + <file>resources/page4.html</file> + <file>resources/page5.html</file> + <file>resources/page6.html</file> +</qresource> +</RCC> + diff --git a/Source/WebKit/qt/tests/qwebhistoryinterface/qwebhistoryinterface.pro b/Source/WebKit/qt/tests/qwebhistoryinterface/qwebhistoryinterface.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistoryinterface/qwebhistoryinterface.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebhistoryinterface/tst_qwebhistoryinterface.cpp b/Source/WebKit/qt/tests/qwebhistoryinterface/tst_qwebhistoryinterface.cpp new file mode 100644 index 0000000..84b12d2 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebhistoryinterface/tst_qwebhistoryinterface.cpp @@ -0,0 +1,96 @@ +/* + Copyright (C) 2008 Holger Hans Peter Freyther + + 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 <QtTest/QtTest> + +#include <qwebpage.h> +#include <qwebview.h> +#include <qwebframe.h> +#include <qwebelement.h> +#include <qwebhistoryinterface.h> +#include <QDebug> + +class tst_QWebHistoryInterface : public QObject +{ + Q_OBJECT + +public: + tst_QWebHistoryInterface(); + virtual ~tst_QWebHistoryInterface(); + +public slots: + void init(); + void cleanup(); + +private slots: + void visitedLinks(); + +private: + + +private: + QWebView* m_view; + QWebPage* m_page; +}; + +tst_QWebHistoryInterface::tst_QWebHistoryInterface() +{ +} + +tst_QWebHistoryInterface::~tst_QWebHistoryInterface() +{ +} + +void tst_QWebHistoryInterface::init() +{ + m_view = new QWebView(); + m_page = m_view->page(); +} + +void tst_QWebHistoryInterface::cleanup() +{ + delete m_view; +} + +class FakeHistoryImplementation : public QWebHistoryInterface { +public: + void addHistoryEntry(const QString&) {} + bool historyContains(const QString& url) const { + return url == QLatin1String("http://www.trolltech.com/"); + } +}; + + +/* + * Test that visited links are properly colored. http://www.trolltech.com is marked + * as visited, so the below website should have exactly one element in the a:visited + * state. + */ +void tst_QWebHistoryInterface::visitedLinks() +{ + QWebHistoryInterface::setDefaultInterface(new FakeHistoryImplementation); + m_view->setHtml("<html><style>:link{color:green}:visited{color:red}</style><body><a href='http://www.trolltech.com' id='vlink'>Trolltech</a></body></html>"); + QWebElement anchor = m_view->page()->mainFrame()->findFirstElement("a[id=vlink]"); + QString linkColor = anchor.styleProperty("color", QWebElement::ComputedStyle); + QCOMPARE(linkColor, QString::fromLatin1("rgb(255, 0, 0)")); +} + +QTEST_MAIN(tst_QWebHistoryInterface) +#include "tst_qwebhistoryinterface.moc" diff --git a/Source/WebKit/qt/tests/qwebinspector/qwebinspector.pro b/Source/WebKit/qt/tests/qwebinspector/qwebinspector.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebinspector/qwebinspector.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebinspector/tst_qwebinspector.cpp b/Source/WebKit/qt/tests/qwebinspector/tst_qwebinspector.cpp new file mode 100644 index 0000000..12cd630 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebinspector/tst_qwebinspector.cpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 <QtTest/QtTest> + +#include <qdir.h> +#include <qwebinspector.h> +#include <qwebpage.h> +#include <qwebsettings.h> + +class tst_QWebInspector : public QObject { + Q_OBJECT + +private slots: + void attachAndDestroy(); +}; + +void tst_QWebInspector::attachAndDestroy() +{ + { // External inspector + manual destruction of page first + QWebPage* page = new QWebPage(); + page->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); + QWebInspector* inspector = new QWebInspector(); + inspector->setPage(page); + page->updatePositionDependentActions(QPoint(0, 0)); + page->triggerAction(QWebPage::InspectElement); + + delete page; + delete inspector; + } + { // External inspector + manual destruction of inspector first + QWebPage* page = new QWebPage(); + page->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); + QWebInspector* inspector = new QWebInspector(); + inspector->setPage(page); + page->updatePositionDependentActions(QPoint(0, 0)); + page->triggerAction(QWebPage::InspectElement); + + delete inspector; + delete page; + } + { // Internal inspector + QWebPage page; + page.settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); + page.updatePositionDependentActions(QPoint(0, 0)); + page.triggerAction(QWebPage::InspectElement); + } +} + +QTEST_MAIN(tst_QWebInspector) + +#include "tst_qwebinspector.moc" diff --git a/Source/WebKit/qt/tests/qwebpage/qwebpage.pro b/Source/WebKit/qt/tests/qwebpage/qwebpage.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/qwebpage.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebpage/resources/content.html b/Source/WebKit/qt/tests/qwebpage/resources/content.html new file mode 100644 index 0000000..823a983 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/content.html @@ -0,0 +1,6 @@ +<html> +<body> +<a>This is test content</a> +</body> +</html> + diff --git a/Source/WebKit/qt/tests/qwebpage/resources/frame_a.html b/Source/WebKit/qt/tests/qwebpage/resources/frame_a.html new file mode 100644 index 0000000..9ff68f1 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/frame_a.html @@ -0,0 +1,2 @@ +<a href="http://google.com" target="frame_b"><img src="" width=100 height=100 alt="Google"></a> +<a href="http://yahoo.com" target="frame_b"><img src="" width=100 height=100 alt="Yahoo"></a> diff --git a/Source/WebKit/qt/tests/qwebpage/resources/frame_c.html b/Source/WebKit/qt/tests/qwebpage/resources/frame_c.html new file mode 100644 index 0000000..eba9ca0 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/frame_c.html @@ -0,0 +1 @@ +<a href="content.html" target="frame_b"><img src="" width=100 height=100 alt="Google"></a> diff --git a/Source/WebKit/qt/tests/qwebpage/resources/framedindex.html b/Source/WebKit/qt/tests/qwebpage/resources/framedindex.html new file mode 100644 index 0000000..465c6f0 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/framedindex.html @@ -0,0 +1,4 @@ +<frameset cols="25%,75%"> + <frame src="frame_c.html" name="frame_c"> + <frame src="frame_b.html" name="frame_b"> +</frameset> diff --git a/Source/WebKit/qt/tests/qwebpage/resources/iframe.html b/Source/WebKit/qt/tests/qwebpage/resources/iframe.html new file mode 100644 index 0000000..f17027c --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/iframe.html @@ -0,0 +1,6 @@ +<html> +<body> +<p>top</p> +<iframe src="iframe2.html" width="80%" height="30%"/> +</body> +</html> diff --git a/Source/WebKit/qt/tests/qwebpage/resources/iframe2.html b/Source/WebKit/qt/tests/qwebpage/resources/iframe2.html new file mode 100644 index 0000000..5017435 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/iframe2.html @@ -0,0 +1,7 @@ +<html> +<body> +<p>another iframe</p> +<iframe src="iframe3.html" width="80%" height="30%"></iframe> +</body> +</html> + diff --git a/Source/WebKit/qt/tests/qwebpage/resources/iframe3.html b/Source/WebKit/qt/tests/qwebpage/resources/iframe3.html new file mode 100644 index 0000000..ed6ac5b --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/iframe3.html @@ -0,0 +1,5 @@ +<html> +<body> +<p>inner</p> +</body> +</html> diff --git a/Source/WebKit/qt/tests/qwebpage/resources/index.html b/Source/WebKit/qt/tests/qwebpage/resources/index.html new file mode 100644 index 0000000..c53ad09 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/resources/index.html @@ -0,0 +1,4 @@ +<frameset cols="25%,75%"> + <frame src="frame_a.html" name="frame_a"> + <frame src="frame_b.html" name="frame_b"> +</frameset> diff --git a/Source/WebKit/qt/tests/qwebpage/tst_qwebpage.cpp b/Source/WebKit/qt/tests/qwebpage/tst_qwebpage.cpp new file mode 100644 index 0000000..9e3337b --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/tst_qwebpage.cpp @@ -0,0 +1,2881 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> + Copyright (C) 2010 Holger Hans Peter Freyther + + 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 "../util.h" +#include "../WebCoreSupport/DumpRenderTreeSupportQt.h" +#include <QClipboard> +#include <QDir> +#include <QGraphicsWidget> +#include <QLineEdit> +#include <QMainWindow> +#include <QMenu> +#include <QPushButton> +#include <QStyle> +#include <QtTest/QtTest> +#include <QTextCharFormat> +#include <qgraphicsscene.h> +#include <qgraphicsview.h> +#include <qgraphicswebview.h> +#include <qnetworkcookiejar.h> +#include <qnetworkrequest.h> +#include <qwebdatabase.h> +#include <qwebelement.h> +#include <qwebframe.h> +#include <qwebhistory.h> +#include <qwebpage.h> +#include <qwebsecurityorigin.h> +#include <qwebview.h> +#include <qimagewriter.h> + +class EventSpy : public QObject, public QList<QEvent::Type> +{ + Q_OBJECT +public: + EventSpy(QObject* objectToSpy) + { + objectToSpy->installEventFilter(this); + } + + virtual bool eventFilter(QObject* receiver, QEvent* event) + { + append(event->type()); + return false; + } +}; + +class tst_QWebPage : public QObject +{ + Q_OBJECT + +public: + tst_QWebPage(); + virtual ~tst_QWebPage(); + +public slots: + void init(); + void cleanup(); + void cleanupFiles(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void contextMenuCopy(); + void acceptNavigationRequest(); + void geolocationRequestJS(); + void loadFinished(); + void acceptNavigationRequestWithNewWindow(); + void userStyleSheet(); + void loadHtml5Video(); + void modified(); + void contextMenuCrash(); + void updatePositionDependentActionsCrash(); + void database(); + void createPluginWithPluginsEnabled(); + void createPluginWithPluginsDisabled(); + void destroyPlugin_data(); + void destroyPlugin(); + void createViewlessPlugin_data(); + void createViewlessPlugin(); + void graphicsWidgetPlugin(); + void multiplePageGroupsAndLocalStorage(); + void cursorMovements(); + void textSelection(); + void textEditing(); + void backActionUpdate(); + void frameAt(); + void requestCache(); + void loadCachedPage(); + void protectBindingsRuntimeObjectsFromCollector(); + void localURLSchemes(); + void testOptionalJSObjects(); + void testEnablePersistentStorage(); + void consoleOutput(); + void inputMethods_data(); + void inputMethods(); + void inputMethodsTextFormat_data(); + void inputMethodsTextFormat(); + void defaultTextEncoding(); + void errorPageExtension(); + void errorPageExtensionInIFrames(); + void errorPageExtensionInFrameset(); + void userAgentApplicationName(); + + void viewModes(); + + void crashTests_LazyInitializationOfMainFrame(); + + void screenshot_data(); + void screenshot(); + +#if defined(ENABLE_WEBGL) && ENABLE_WEBGL + void acceleratedWebGLScreenshotWithoutView(); + void unacceleratedWebGLScreenshotWithoutView(); +#endif + + void originatingObjectInNetworkRequests(); + void testJSPrompt(); + void showModalDialog(); + void testStopScheduledPageRefresh(); + void findText(); + void supportedContentType(); + void infiniteLoopJS(); + void navigatorCookieEnabled(); + void deleteQWebViewTwice(); + void renderOnRepaintRequestedShouldNotRecurse(); + +#ifdef Q_OS_MAC + void macCopyUnicodeToClipboard(); +#endif + +private: + QWebView* m_view; + QWebPage* m_page; +}; + +tst_QWebPage::tst_QWebPage() +{ +} + +tst_QWebPage::~tst_QWebPage() +{ +} + +void tst_QWebPage::init() +{ + m_view = new QWebView(); + m_page = m_view->page(); +} + +void tst_QWebPage::cleanup() +{ + delete m_view; +} + +void tst_QWebPage::cleanupFiles() +{ + QFile::remove("Databases.db"); + QDir::current().rmdir("http_www.myexample.com_0"); + QFile::remove("http_www.myexample.com_0.localstorage"); +} + +void tst_QWebPage::initTestCase() +{ + cleanupFiles(); // In case there are old files from previous runs +} + +void tst_QWebPage::cleanupTestCase() +{ + cleanupFiles(); // Be nice +} + +class NavigationRequestOverride : public QWebPage +{ +public: + NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {} + + bool m_acceptNavigationRequest; +protected: + virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) { + Q_UNUSED(frame); + Q_UNUSED(request); + Q_UNUSED(type); + + return m_acceptNavigationRequest; + } +}; + +void tst_QWebPage::acceptNavigationRequest() +{ + QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); + + NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false); + m_view->setPage(newPage); + + m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>" + "<input type='text'><input type='submit'></form></body></html>"), QUrl()); + QTRY_COMPARE(loadSpy.count(), 1); + + m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();"); + + newPage->m_acceptNavigationRequest = true; + m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();"); + QTRY_COMPARE(loadSpy.count(), 2); + + QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?")); + + // Restore default page + m_view->setPage(0); +} + +class JSTestPage : public QWebPage +{ +Q_OBJECT +public: + JSTestPage(QObject* parent = 0) + : QWebPage(parent) {} + +public slots: + bool shouldInterruptJavaScript() { + return true; + } + void requestPermission(QWebFrame* frame, QWebPage::Feature feature) + { + if (m_allowGeolocation) + setFeaturePermission(frame, feature, PermissionGrantedByUser); + else + setFeaturePermission(frame, feature, PermissionDeniedByUser); + } + +public: + void setGeolocationPermission(bool allow) + { + m_allowGeolocation = allow; + } + +private: + bool m_allowGeolocation; +}; + +void tst_QWebPage::infiniteLoopJS() +{ + JSTestPage* newPage = new JSTestPage(m_view); + m_view->setPage(newPage); + m_view->setHtml(QString("<html><body>test</body></html>"), QUrl()); + m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}"); + delete newPage; +} + +void tst_QWebPage::geolocationRequestJS() +{ + JSTestPage* newPage = new JSTestPage(m_view); + + if (newPage->mainFrame()->evaluateJavaScript(QLatin1String("!navigator.geolocation")).toBool()) { + delete newPage; + QSKIP("Geolocation is not supported.", SkipSingle); + } + + connect(newPage, SIGNAL(featurePermissionRequested(QWebFrame*, QWebPage::Feature)), + newPage, SLOT(requestPermission(QWebFrame*, QWebPage::Feature))); + + newPage->setGeolocationPermission(false); + m_view->setPage(newPage); + m_view->setHtml(QString("<html><body>test</body></html>"), QUrl()); + m_view->page()->mainFrame()->evaluateJavaScript("var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)"); + QTest::qWait(2000); + QVariant empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode"); + + QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0); + + newPage->setGeolocationPermission(true); + m_view->page()->mainFrame()->evaluateJavaScript("errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);"); + empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode"); + + //http://dev.w3.org/geo/api/spec-source.html#position + //PositionError: const unsigned short PERMISSION_DENIED = 1; + QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 1); + delete newPage; +} + +void tst_QWebPage::loadFinished() +{ + qRegisterMetaType<QWebFrame*>("QWebFrame*"); + qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*"); + QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted())); + QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); + + m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," + "<head><meta http-equiv='refresh' content='1'></head>foo \">" + "<frame src=\"data:text/html,bar\"></frameset>")); + QTRY_COMPARE(spyLoadFinished.count(), 1); + + QTRY_VERIFY(spyLoadStarted.count() > 1); + QTRY_VERIFY(spyLoadFinished.count() > 1); + + spyLoadFinished.clear(); + + m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," + "foo \"><frame src=\"data:text/html,bar\"></frameset>")); + QTRY_COMPARE(spyLoadFinished.count(), 1); + QCOMPARE(spyLoadFinished.count(), 1); +} + +class ConsolePage : public QWebPage +{ +public: + ConsolePage(QObject* parent = 0) : QWebPage(parent) {} + + virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) + { + messages.append(message); + lineNumbers.append(lineNumber); + sourceIDs.append(sourceID); + } + + QStringList messages; + QList<int> lineNumbers; + QStringList sourceIDs; +}; + +void tst_QWebPage::consoleOutput() +{ + ConsolePage page; + page.mainFrame()->evaluateJavaScript("this is not valid JavaScript"); + QCOMPARE(page.messages.count(), 1); + QCOMPARE(page.lineNumbers.at(0), 1); +} + +class TestPage : public QWebPage +{ +public: + TestPage(QObject* parent = 0) : QWebPage(parent) {} + + struct Navigation { + QPointer<QWebFrame> frame; + QNetworkRequest request; + NavigationType type; + }; + + QList<Navigation> navigations; + QList<QWebPage*> createdWindows; + + virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) { + Navigation n; + n.frame = frame; + n.request = request; + n.type = type; + navigations.append(n); + return true; + } + + virtual QWebPage* createWindow(WebWindowType) { + QWebPage* page = new TestPage(this); + createdWindows.append(page); + return page; + } +}; + +void tst_QWebPage::acceptNavigationRequestWithNewWindow() +{ + TestPage* page = new TestPage(m_view); + page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true); + m_page = page; + m_view->setPage(m_page); + + m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>")); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QFocusEvent fe(QEvent::FocusIn); + m_page->event(&fe); + + QVERIFY(m_page->focusNextPrevChild(/*next*/ true)); + + QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); + m_page->event(&keyEnter); + + QCOMPARE(page->navigations.count(), 2); + + TestPage::Navigation n = page->navigations.at(1); + QVERIFY(n.frame.isNull()); + QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached")); + QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked); + + QCOMPARE(page->createdWindows.count(), 1); +} + +class TestNetworkManager : public QNetworkAccessManager +{ +public: + TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} + + QList<QUrl> requestedUrls; + QList<QNetworkRequest> requests; + +protected: + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { + requests.append(request); + requestedUrls.append(request.url()); + return QNetworkAccessManager::createRequest(op, request, outgoingData); + } +}; + +void tst_QWebPage::userStyleSheet() +{ + TestNetworkManager* networkManager = new TestNetworkManager(m_page); + m_page->setNetworkAccessManager(networkManager); + networkManager->requestedUrls.clear(); + + m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64," + + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64())); + m_view->setHtml("<p>hello world</p>"); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(networkManager->requestedUrls.count() >= 1); + QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); +} + +void tst_QWebPage::loadHtml5Video() +{ +#if defined(WTF_USE_QT_MULTIMEDIA) && WTF_USE_QT_MULTIMEDIA + QByteArray url("http://does.not/exist?a=1%2Cb=2"); + m_view->setHtml("<p><video id ='video' src='" + url + "' autoplay/></p>"); + QTest::qWait(2000); + QUrl mUrl = DumpRenderTreeSupportQt::mediaContentUrlByElementId(m_page->mainFrame(), "video"); + QCOMPARE(mUrl.toEncoded(), url); +#else + QSKIP("This test requires Qt Multimedia", SkipAll); +#endif +} + +void tst_QWebPage::viewModes() +{ + m_view->setHtml("<body></body>"); + m_page->setProperty("_q_viewMode", "minimized"); + + QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")"); + QVERIFY(empty.type() == QVariant::Bool && empty.toBool()); + + QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")"); + QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool()); + + QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")"); + QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool()); +} + +void tst_QWebPage::modified() +{ + m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub")); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah")); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(!m_page->isModified()); + +// m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))"); + m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()"); + m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');"); + + QVERIFY(m_page->isModified()); + + m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);"); + + QVERIFY(!m_page->isModified()); + + m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);"); + + QVERIFY(m_page->isModified()); + + QVERIFY(m_page->history()->canGoBack()); + QVERIFY(!m_page->history()->canGoForward()); + QCOMPARE(m_page->history()->count(), 2); + QVERIFY(m_page->history()->backItem().isValid()); + QVERIFY(!m_page->history()->forwardItem().isValid()); + + m_page->history()->back(); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(!m_page->history()->canGoBack()); + QVERIFY(m_page->history()->canGoForward()); + + QVERIFY(!m_page->isModified()); + + QVERIFY(m_page->history()->currentItemIndex() == 0); + + m_page->history()->setMaximumItemCount(3); + QVERIFY(m_page->history()->maximumItemCount() == 3); + + QVariant variant("string test"); + m_page->history()->currentItem().setUserData(variant); + QVERIFY(m_page->history()->currentItem().userData().toString() == "string test"); + + m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page")); + m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page")); + QVERIFY(m_page->history()->count() == 2); + m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page")); + QVERIFY(m_page->history()->count() == 2); + m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page")); + QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*)))); +} + +// https://bugs.webkit.org/show_bug.cgi?id=51331 +void tst_QWebPage::updatePositionDependentActionsCrash() +{ + QWebView view; + view.setHtml("<p>test"); + QPoint pos(0, 0); + view.page()->updatePositionDependentActions(pos); + QMenu* contextMenu = 0; + foreach (QObject* child, view.children()) { + contextMenu = qobject_cast<QMenu*>(child); + if (contextMenu) + break; + } + QVERIFY(!contextMenu); +} + +// https://bugs.webkit.org/show_bug.cgi?id=20357 +void tst_QWebPage::contextMenuCrash() +{ + QWebView view; + view.setHtml("<p>test"); + QPoint pos(0, 0); + QContextMenuEvent event(QContextMenuEvent::Mouse, pos); + view.page()->swallowContextMenuEvent(&event); + view.page()->updatePositionDependentActions(pos); + QMenu* contextMenu = 0; + foreach (QObject* child, view.children()) { + contextMenu = qobject_cast<QMenu*>(child); + if (contextMenu) + break; + } + QVERIFY(contextMenu); + delete contextMenu; +} + +void tst_QWebPage::database() +{ + QString path = QDir::currentPath(); + m_page->settings()->setOfflineStoragePath(path); + QVERIFY(m_page->settings()->offlineStoragePath() == path); + + QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024); + QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024); + + m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); + m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); + + QString dbFileName = path + "Databases.db"; + + if (QFile::exists(dbFileName)) + QFile::remove(dbFileName); + + qRegisterMetaType<QWebFrame*>("QWebFrame*"); + QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString))); + m_view->setHtml(QString("<html><head><script>var db; db=openDatabase('testdb', '1.0', 'test database API', 50000); </script></head><body><div></div></body></html>"), QUrl("http://www.myexample.com")); + QTRY_COMPARE(spy.count(), 1); + m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);"); + QTRY_COMPARE(spy.count(),1); + + m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';"); + m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com")); + + QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test"); + QCOMPARE(s1.toString(), QString("This is a test for local storage")); + + m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';"); + m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com")); + QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test"); + QCOMPARE(s2.toString(), QString("This is a test for session storage")); + + m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com")); + m_page->mainFrame()->evaluateJavaScript("var db3; db3=openDatabase('testdb', '1.0', 'test database API', 50000);db3.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS Test (text TEXT)', []); }, function(tx, result) { }, function(tx, error) { });"); + QTest::qWait(200); + + // Remove all databases. + QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin(); + QList<QWebDatabase> dbs = origin.databases(); + for (int i = 0; i < dbs.count(); i++) { + QString fileName = dbs[i].fileName(); + QVERIFY(QFile::exists(fileName)); + QWebDatabase::removeDatabase(dbs[i]); + QVERIFY(!QFile::exists(fileName)); + } + QVERIFY(!origin.databases().size()); + // Remove removed test :-) + QWebDatabase::removeAllDatabases(); + QVERIFY(!origin.databases().size()); +} + +class PluginPage : public QWebPage +{ +public: + PluginPage(QObject *parent = 0) + : QWebPage(parent) {} + + struct CallInfo + { + CallInfo(const QString &c, const QUrl &u, + const QStringList &pn, const QStringList &pv, + QObject *r) + : classid(c), url(u), paramNames(pn), + paramValues(pv), returnValue(r) + {} + QString classid; + QUrl url; + QStringList paramNames; + QStringList paramValues; + QObject *returnValue; + }; + + QList<CallInfo> calls; + +protected: + virtual QObject *createPlugin(const QString &classid, const QUrl &url, + const QStringList ¶mNames, + const QStringList ¶mValues) + { + QObject *result = 0; + if (classid == "pushbutton") + result = new QPushButton(); +#ifndef QT_NO_INPUTDIALOG + else if (classid == "lineedit") + result = new QLineEdit(); +#endif + else if (classid == "graphicswidget") + result = new QGraphicsWidget(); + if (result) + result->setObjectName(classid); + calls.append(CallInfo(classid, url, paramNames, paramValues, result)); + return result; + } +}; + +static void createPlugin(QWebView *view) +{ + QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool))); + + PluginPage* newPage = new PluginPage(view); + view->setPage(newPage); + + // type has to be application/x-qt-plugin + view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(newPage->calls.count(), 0); + + view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>")); + QTRY_COMPARE(loadSpy.count(), 2); + QCOMPARE(newPage->calls.count(), 1); + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); + QVERIFY(ci.returnValue != 0); + QVERIFY(ci.returnValue->inherits("QPushButton")); + } + // test JS bindings + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(), + QString::fromLatin1("string")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(), + QString::fromLatin1("pushbutton")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(), + QString::fromLatin1("function")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(), + QString::fromLatin1("function clicked() {\n [native code]\n}")); + + view->setHtml(QString("<html><body><table>" + "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>" + "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>" + "</table></body></html>"), QUrl("http://foo.bar.baz")); + QTRY_COMPARE(loadSpy.count(), 3); + QCOMPARE(newPage->calls.count(), 2); + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("lineedit")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit")); + QVERIFY(ci.returnValue != 0); + QVERIFY(ci.returnValue->inherits("QLineEdit")); + } + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); + QVERIFY(ci.returnValue != 0); + QVERIFY(ci.returnValue->inherits("QPushButton")); + } +} + +void tst_QWebPage::graphicsWidgetPlugin() +{ + m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); + QGraphicsWebView webView; + + QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool))); + + PluginPage* newPage = new PluginPage(&webView); + webView.setPage(newPage); + + // type has to be application/x-qt-plugin + webView.setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='graphicswidget' id='mygraphicswidget'/></body></html>")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(newPage->calls.count(), 0); + + webView.setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='graphicswidget' id='mygraphicswidget'/></body></html>")); + QTRY_COMPARE(loadSpy.count(), 2); + QCOMPARE(newPage->calls.count(), 1); + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("graphicswidget")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("graphicswidget")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mygraphicswidget")); + QVERIFY(ci.returnValue); + QVERIFY(ci.returnValue->inherits("QGraphicsWidget")); + } + // test JS bindings + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mygraphicswidget').toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.objectName").toString(), + QString::fromLatin1("string")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.objectName").toString(), + QString::fromLatin1("graphicswidget")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.geometryChanged").toString(), + QString::fromLatin1("function")); + QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.geometryChanged.toString()").toString(), + QString::fromLatin1("function geometryChanged() {\n [native code]\n}")); +} + +void tst_QWebPage::createPluginWithPluginsEnabled() +{ + m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); + createPlugin(m_view); +} + +void tst_QWebPage::createPluginWithPluginsDisabled() +{ + // Qt Plugins should be loaded by QtWebKit even when PluginsEnabled is + // false. The client decides whether a Qt plugin is enabled or not when + // it decides whether or not to instantiate it. + m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false); + createPlugin(m_view); +} + +// Standard base class for template PluginTracerPage. In tests it is used as interface. +class PluginCounterPage : public QWebPage { +public: + int m_count; + QPointer<QObject> m_widget; + QObject* m_pluginParent; + PluginCounterPage(QObject* parent = 0) + : QWebPage(parent) + , m_count(0) + , m_widget(0) + , m_pluginParent(0) + { + settings()->setAttribute(QWebSettings::PluginsEnabled, true); + } + ~PluginCounterPage() + { + if (m_pluginParent) + m_pluginParent->deleteLater(); + } +}; + +template<class T> +class PluginTracerPage : public PluginCounterPage { +public: + PluginTracerPage(QObject* parent = 0) + : PluginCounterPage(parent) + { + // this is a dummy parent object for the created plugin + m_pluginParent = new T; + } + virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&) + { + m_count++; + m_widget = new T; + // need a cast to the specific type, as QObject::setParent cannot be called, + // because it is not virtual. Instead it is necesary to call QWidget::setParent, + // which also takes a QWidget* instead of a QObject*. Therefore we need to + // upcast to T*, which is a QWidget. + static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent)); + return m_widget; + } +}; + +class PluginFactory { +public: + enum FactoredType {QWidgetType, QGraphicsWidgetType}; + static PluginCounterPage* create(FactoredType type, QObject* parent = 0) + { + PluginCounterPage* result = 0; + switch (type) { + case QWidgetType: + result = new PluginTracerPage<QWidget>(parent); + break; + case QGraphicsWidgetType: + result = new PluginTracerPage<QGraphicsWidget>(parent); + break; + default: {/*Oops*/}; + } + return result; + } + + static void prepareTestData() + { + QTest::addColumn<int>("type"); + QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType; + QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType; + } +}; + +void tst_QWebPage::destroyPlugin_data() +{ + PluginFactory::prepareTestData(); +} + +void tst_QWebPage::destroyPlugin() +{ + QFETCH(int, type); + PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view); + m_view->setPage(page); + + // we create the plugin, so the widget should be constructed + QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>"); + m_view->setHtml(content); + QVERIFY(page->m_widget); + QCOMPARE(page->m_count, 1); + + // navigate away, the plugin widget should be destructed + m_view->setHtml("<html><body>Hi</body></html>"); + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!page->m_widget); +} + +void tst_QWebPage::createViewlessPlugin_data() +{ + PluginFactory::prepareTestData(); +} + +void tst_QWebPage::createViewlessPlugin() +{ + QFETCH(int, type); + PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type); + QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>"); + page->mainFrame()->setHtml(content); + QCOMPARE(page->m_count, 1); + QVERIFY(page->m_widget); + QVERIFY(page->m_pluginParent); + QVERIFY(page->m_widget->parent() == page->m_pluginParent); + delete page; + +} + +void tst_QWebPage::multiplePageGroupsAndLocalStorage() +{ + QDir dir(QDir::currentPath()); + dir.mkdir("path1"); + dir.mkdir("path2"); + + QWebView view1; + QWebView view2; + + view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); + view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1")); + DumpRenderTreeSupportQt::webPageSetGroupName(view1.page(), "group1"); + view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); + view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2")); + DumpRenderTreeSupportQt::webPageSetGroupName(view2.page(), "group2"); + QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()), QString("group1")); + QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()), QString("group2")); + + + view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); + view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); + + view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';"); + view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';"); + + view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); + view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com")); + + QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test"); + QCOMPARE(s1.toString(), QString("value1")); + + QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test"); + QCOMPARE(s2.toString(), QString("value2")); + + QTest::qWait(1000); + + QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage")); + QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage")); + dir.rmdir(QDir::toNativeSeparators("./path1")); + dir.rmdir(QDir::toNativeSeparators("./path2")); +} + +class CursorTrackedPage : public QWebPage +{ +public: + + CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) { + setViewportSize(QSize(1024, 768)); // big space + } + + QString selectedText() { + return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString(); + } + + int selectionStartOffset() { + return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt(); + } + + int selectionEndOffset() { + return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt(); + } + + // true if start offset == end offset, i.e. no selected text + int isSelectionCollapsed() { + return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool(); + } +}; + +void tst_QWebPage::cursorMovements() +{ + CursorTrackedPage* page = new CursorTrackedPage; + QString content("<html><body><p id=one>The quick brown fox</p><p id=two>jumps over the lazy dog</p><p>May the source<br/>be with you!</p></body></html>"); + page->mainFrame()->setHtml(content); + + // this will select the first paragraph + QString script = "var range = document.createRange(); " \ + "var node = document.getElementById(\"one\"); " \ + "range.selectNode(node); " \ + "getSelection().addRange(range);"; + page->mainFrame()->evaluateJavaScript(script); + QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); + + QRegExp regExp(" style=\".*\""); + regExp.setMinimal(true); + QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<span class=\"Apple-style-span\"><p id=\"one\">The quick brown fox</p></span>")); + + // these actions must exist + QVERIFY(page->action(QWebPage::MoveToNextChar) != 0); + QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0); + QVERIFY(page->action(QWebPage::MoveToNextWord) != 0); + QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0); + QVERIFY(page->action(QWebPage::MoveToNextLine) != 0); + QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0); + QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0); + QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0); + QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0); + QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0); + QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0); + QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0); + + // right now they are disabled because contentEditable is false + QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false); + + // make it editable before navigating the cursor + page->setContentEditable(true); + + // here the actions are enabled after contentEditable is true + QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true); + + // cursor will be before the word "jump" + page->triggerAction(QWebPage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be between 'j' and 'u' in the word "jump" + page->triggerAction(QWebPage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 1); + + // cursor will be between 'u' and 'm' in the word "jump" + page->triggerAction(QWebPage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 2); + + // cursor will be after the word "jump" + page->triggerAction(QWebPage::MoveToNextWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 5); + + // cursor will be after the word "lazy" + page->triggerAction(QWebPage::MoveToNextWord); + page->triggerAction(QWebPage::MoveToNextWord); + page->triggerAction(QWebPage::MoveToNextWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 19); + + // cursor will be between 'z' and 'y' in "lazy" + page->triggerAction(QWebPage::MoveToPreviousChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 18); + + // cursor will be between 'a' and 'z' in "lazy" + page->triggerAction(QWebPage::MoveToPreviousChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 17); + + // cursor will be before the word "lazy" + page->triggerAction(QWebPage::MoveToPreviousWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 15); + + // cursor will be before the word "quick" + page->triggerAction(QWebPage::MoveToPreviousWord); + page->triggerAction(QWebPage::MoveToPreviousWord); + page->triggerAction(QWebPage::MoveToPreviousWord); + page->triggerAction(QWebPage::MoveToPreviousWord); + page->triggerAction(QWebPage::MoveToPreviousWord); + page->triggerAction(QWebPage::MoveToPreviousWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 4); + + // cursor will be between 'p' and 's' in the word "jumps" + page->triggerAction(QWebPage::MoveToNextWord); + page->triggerAction(QWebPage::MoveToNextWord); + page->triggerAction(QWebPage::MoveToNextWord); + page->triggerAction(QWebPage::MoveToNextChar); + page->triggerAction(QWebPage::MoveToNextChar); + page->triggerAction(QWebPage::MoveToNextChar); + page->triggerAction(QWebPage::MoveToNextChar); + page->triggerAction(QWebPage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 4); + + // cursor will be before the word "jumps" + page->triggerAction(QWebPage::MoveToStartOfLine); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be after the word "dog" + page->triggerAction(QWebPage::MoveToEndOfLine); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 23); + + // cursor will be between 'w' and 'n' in "brown" + page->triggerAction(QWebPage::MoveToStartOfLine); + page->triggerAction(QWebPage::MoveToPreviousWord); + page->triggerAction(QWebPage::MoveToPreviousWord); + page->triggerAction(QWebPage::MoveToNextChar); + page->triggerAction(QWebPage::MoveToNextChar); + page->triggerAction(QWebPage::MoveToNextChar); + page->triggerAction(QWebPage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 14); + + // cursor will be after the word "fox" + page->triggerAction(QWebPage::MoveToEndOfLine); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 19); + + // cursor will be before the word "The" + page->triggerAction(QWebPage::MoveToStartOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be after the word "you!" + page->triggerAction(QWebPage::MoveToEndOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + + // cursor will be before the word "be" + page->triggerAction(QWebPage::MoveToStartOfBlock); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be after the word "you!" + page->triggerAction(QWebPage::MoveToEndOfBlock); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + + // try to move before the document start + page->triggerAction(QWebPage::MoveToStartOfDocument); + page->triggerAction(QWebPage::MoveToPreviousChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + page->triggerAction(QWebPage::MoveToStartOfDocument); + page->triggerAction(QWebPage::MoveToPreviousWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // try to move past the document end + page->triggerAction(QWebPage::MoveToEndOfDocument); + page->triggerAction(QWebPage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + page->triggerAction(QWebPage::MoveToEndOfDocument); + page->triggerAction(QWebPage::MoveToNextWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + + delete page; +} + +void tst_QWebPage::textSelection() +{ + CursorTrackedPage* page = new CursorTrackedPage; + QString content("<html><body><p id=one>The quick brown fox</p>" \ + "<p id=two>jumps over the lazy dog</p>" \ + "<p>May the source<br/>be with you!</p></body></html>"); + page->mainFrame()->setHtml(content); + + // these actions must exist + QVERIFY(page->action(QWebPage::SelectAll) != 0); + QVERIFY(page->action(QWebPage::SelectNextChar) != 0); + QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0); + QVERIFY(page->action(QWebPage::SelectNextWord) != 0); + QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0); + QVERIFY(page->action(QWebPage::SelectNextLine) != 0); + QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0); + QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0); + QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0); + QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0); + QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0); + QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0); + QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0); + + // right now they are disabled because contentEditable is false and + // there isn't an existing selection to modify + QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false); + + // ..but SelectAll is awalys enabled + QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true); + + // Verify hasSelection returns false since there is no selection yet... + QCOMPARE(page->hasSelection(), false); + + // this will select the first paragraph + QString selectScript = "var range = document.createRange(); " \ + "var node = document.getElementById(\"one\"); " \ + "range.selectNode(node); " \ + "getSelection().addRange(range);"; + page->mainFrame()->evaluateJavaScript(selectScript); + QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); + QRegExp regExp(" style=\".*\""); + regExp.setMinimal(true); + QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("<span class=\"Apple-style-span\"><p id=\"one\">The quick brown fox</p></span>")); + + // Make sure hasSelection returns true, since there is selected text now... + QCOMPARE(page->hasSelection(), true); + + // here the actions are enabled after a selection has been created + QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true); + + // make it editable before navigating the cursor + page->setContentEditable(true); + + // cursor will be before the word "The", this makes sure there is a charet + page->triggerAction(QWebPage::MoveToStartOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // here the actions are enabled after contentEditable is true + QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true); + + delete page; +} + +void tst_QWebPage::textEditing() +{ + CursorTrackedPage* page = new CursorTrackedPage; + QString content("<html><body><p id=one>The quick brown fox</p>" \ + "<p id=two>jumps over the lazy dog</p>" \ + "<p>May the source<br/>be with you!</p></body></html>"); + page->mainFrame()->setHtml(content); + + // these actions must exist + QVERIFY(page->action(QWebPage::Cut) != 0); + QVERIFY(page->action(QWebPage::Copy) != 0); + QVERIFY(page->action(QWebPage::Paste) != 0); + QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0); + QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0); + QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0); + QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0); + QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0); + QVERIFY(page->action(QWebPage::ToggleBold) != 0); + QVERIFY(page->action(QWebPage::ToggleItalic) != 0); + QVERIFY(page->action(QWebPage::ToggleUnderline) != 0); + QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0); + QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0); + QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0); + QVERIFY(page->action(QWebPage::RemoveFormat) != 0); + QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0); + QVERIFY(page->action(QWebPage::ToggleSubscript) != 0); + QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0); + QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0); + QVERIFY(page->action(QWebPage::InsertOrderedList) != 0); + QVERIFY(page->action(QWebPage::Indent) != 0); + QVERIFY(page->action(QWebPage::Outdent) != 0); + QVERIFY(page->action(QWebPage::AlignCenter) != 0); + QVERIFY(page->action(QWebPage::AlignJustified) != 0); + QVERIFY(page->action(QWebPage::AlignLeft) != 0); + QVERIFY(page->action(QWebPage::AlignRight) != 0); + + // right now they are disabled because contentEditable is false + QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false); + + // Select everything + page->triggerAction(QWebPage::SelectAll); + + // make sure it is enabled since there is a selection + QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true); + + // make it editable before navigating the cursor + page->setContentEditable(true); + + // clear the selection + page->triggerAction(QWebPage::MoveToStartOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // make sure it is disabled since there isn't a selection + QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false); + + // here the actions are enabled after contentEditable is true + QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true); + + // make sure these are disabled since there isn't a selection + QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false); + QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false); + + // make sure everything is selected + page->triggerAction(QWebPage::SelectAll); + + // this is only true if there is an editable selection + QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true); + QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true); + + delete page; +} + +void tst_QWebPage::requestCache() +{ + TestPage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>")); + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(page.navigations.count(), 1); + + page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>")); + QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(page.navigations.count(), 2); + + page.triggerAction(QWebPage::Stop); + QVERIFY(page.history()->canGoBack()); + page.triggerAction(QWebPage::Back); + + QTRY_COMPARE(loadSpy.count(), 3); + QTRY_COMPARE(page.navigations.count(), 3); + QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), + (int)QNetworkRequest::PreferNetwork); + QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), + (int)QNetworkRequest::PreferNetwork); + QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), + (int)QNetworkRequest::PreferCache); +} + +void tst_QWebPage::loadCachedPage() +{ + TestPage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setMaximumPagesInCache(3); + + page.mainFrame()->load(QUrl("data:text/html,This is first page")); + + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(page.navigations.count(), 1); + + QUrl firstPageUrl = page.mainFrame()->url(); + page.mainFrame()->load(QUrl("data:text/html,This is second page")); + + QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(page.navigations.count(), 2); + + page.triggerAction(QWebPage::Stop); + QVERIFY(page.history()->canGoBack()); + + QSignalSpy urlSpy(page.mainFrame(), SIGNAL(urlChanged(QUrl))); + QVERIFY(urlSpy.isValid()); + + page.triggerAction(QWebPage::Back); + ::waitForSignal(page.mainFrame(), SIGNAL(urlChanged(QUrl))); + QCOMPARE(urlSpy.size(), 1); + + QList<QVariant> arguments1 = urlSpy.takeFirst(); + QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl); + +} +void tst_QWebPage::backActionUpdate() +{ + QWebView view; + QWebPage *page = view.page(); + QAction *action = page->action(QWebPage::Back); + QVERIFY(!action->isEnabled()); + QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool))); + QUrl url = QUrl("qrc:///resources/framedindex.html"); + page->mainFrame()->load(url); + QTRY_COMPARE(loadSpy.count(), 1); + QVERIFY(!action->isEnabled()); + QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10)); + QTRY_COMPARE(loadSpy.count(), 2); + + QVERIFY(action->isEnabled()); +} + +void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition) +{ + if (!webFrame) + return; + + framePosition += QPoint(webFrame->pos()); + QList<QWebFrame*> children = webFrame->childFrames(); + for (int i = 0; i < children.size(); ++i) { + if (children.at(i)->childFrames().size() > 0) + frameAtHelper(webPage, children.at(i), framePosition); + + QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size()); + QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft())); + } +} + +void tst_QWebPage::frameAt() +{ + QWebView webView; + QWebPage* webPage = webView.page(); + QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool))); + QUrl url = QUrl("qrc:///resources/iframe.html"); + webPage->mainFrame()->load(url); + QTRY_COMPARE(loadSpy.count(), 1); + frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos()); +} + +void tst_QWebPage::inputMethods_data() +{ + QTest::addColumn<QString>("viewType"); + QTest::newRow("QWebView") << "QWebView"; + QTest::newRow("QGraphicsWebView") << "QGraphicsWebView"; +} + +static Qt::InputMethodHints inputMethodHints(QObject* object) +{ + if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object)) + return o->inputMethodHints(); + if (QWidget* w = qobject_cast<QWidget*>(object)) + return w->inputMethodHints(); + return Qt::InputMethodHints(); +} + +static bool inputMethodEnabled(QObject* object) +{ + if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object)) + return o->flags() & QGraphicsItem::ItemAcceptsInputMethod; + if (QWidget* w = qobject_cast<QWidget*>(object)) + return w->testAttribute(Qt::WA_InputMethodEnabled); + return false; +} + +static void clickOnPage(QWebPage* page, const QPoint& position) +{ + QMouseEvent evpres(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + page->event(&evpres); + QMouseEvent evrel(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + page->event(&evrel); +} + +void tst_QWebPage::inputMethods() +{ + QFETCH(QString, viewType); + QWebPage* page = new QWebPage; + QObject* view = 0; + QObject* container = 0; + if (viewType == "QWebView") { + QWebView* wv = new QWebView; + wv->setPage(page); + view = wv; + container = view; + } else if (viewType == "QGraphicsWebView") { + QGraphicsWebView* wv = new QGraphicsWebView; + wv->setPage(page); + view = wv; + + QGraphicsView* gv = new QGraphicsView; + QGraphicsScene* scene = new QGraphicsScene(gv); + gv->setScene(scene); + scene->addItem(wv); + wv->setGeometry(QRect(0, 0, 500, 500)); + + container = gv; + } else + QVERIFY2(false, "Unknown view type"); + + page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont"); + page->mainFrame()->setHtml("<html><body>" \ + "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \ + "<input type='password'/>" \ + "</body></html>"); + page->mainFrame()->setFocus(); + + EventSpy viewEventSpy(container); + + QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input"); + QPoint textInputCenter = inputs.at(0).geometry().center(); + + clickOnPage(page, textInputCenter); + + // This part of the test checks if the SIP (Software Input Panel) is triggered, + // which normally happens on mobile platforms, when a user input form receives + // a mouse click. + int inputPanel = 0; + if (viewType == "QWebView") { + if (QWebView* wv = qobject_cast<QWebView*>(view)) + inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); + } else if (viewType == "QGraphicsWebView") { + if (QGraphicsWebView* wv = qobject_cast<QGraphicsWebView*>(view)) + inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); + } + + // For non-mobile platforms RequestSoftwareInputPanel event is not called + // because there is no SIP (Software Input Panel) triggered. In the case of a + // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked + // and the RequestSoftwareInputPanel event is called. For these two situations + // this part of the test can verified as the checks below. + if (inputPanel) + QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); + else + QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); + viewEventSpy.clear(); + + clickOnPage(page, textInputCenter); + QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); + + //ImMicroFocus + QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus); + QRect focusRect = variant.toRect(); + QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft())); + + //ImFont + variant = page->inputMethodQuery(Qt::ImFont); + QFont font = variant.value<QFont>(); + QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family()); + + QList<QInputMethodEvent::Attribute> inputAttributes; + + //Insert text. + { + QInputMethodEvent eventText("QtWebKit", inputAttributes); + QSignalSpy signalSpy(page, SIGNAL(microFocusChanged())); + page->event(&eventText); + QCOMPARE(signalSpy.count(), 0); + } + + { + QInputMethodEvent eventText("", inputAttributes); + eventText.setCommitString(QString("QtWebKit"), 0, 0); + page->event(&eventText); + } + + //ImMaximumTextLength + variant = page->inputMethodQuery(Qt::ImMaximumTextLength); + QCOMPARE(20, variant.toInt()); + + //Set selection + inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant()); + QInputMethodEvent eventSelection("",inputAttributes); + page->event(&eventSelection); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + int anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 3); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + int cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 5); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + QString selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("eb")); + + //Set selection with negative length + inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant()); + QInputMethodEvent eventSelection3("",inputAttributes); + page->event(&eventSelection3); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 1); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 6); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("tWebK")); + + //ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + QString value = variant.value<QString>(); + QCOMPARE(value, QString("QtWebKit")); + + { + QList<QInputMethodEvent::Attribute> attributes; + // Clear the selection, so the next test does not clear any contents. + QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant()); + attributes.append(newSelection); + QInputMethodEvent event("composition", attributes); + page->event(&event); + } + + // A ongoing composition should not change the surrounding text before it is committed. + variant = page->inputMethodQuery(Qt::ImSurroundingText); + value = variant.value<QString>(); + QCOMPARE(value, QString("QtWebKit")); + + // Cancel current composition first + inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant()); + QInputMethodEvent eventSelection4("", inputAttributes); + page->event(&eventSelection4); + + // START - Tests for Selection when the Editor is NOT in Composition mode + + // LEFT to RIGHT selection + // Deselect the selection by sending MouseButtonPress events + // This moves the current cursor to the end of the text + clickOnPage(page, textInputCenter); + + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event(QString(), attributes); + event.setCommitString("XXX", 0, 0); + page->event(&event); + event.setCommitString(QString(), -2, 2); // Erase two characters. + page->event(&event); + event.setCommitString(QString(), -1, 1); // Erase one character. + page->event(&event); + variant = page->inputMethodQuery(Qt::ImSurroundingText); + value = variant.value<QString>(); + QCOMPARE(value, QString("QtWebKit")); + } + + //Move to the start of the line + page->triggerAction(QWebPage::MoveToStartOfLine); + + QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); + QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier); + + //Move 2 characters RIGHT + for (int j = 0; j < 2; ++j) { + page->event(&keyRightEventPress); + page->event(&keyRightEventRelease); + } + + //Select to the end of the line + page->triggerAction(QWebPage::SelectEndOfLine); + + //ImAnchorPosition QtWebKit + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 2); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 8); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("WebKit")); + + //RIGHT to LEFT selection + //Deselect the selection (this moves the current cursor to the end of the text) + clickOnPage(page, textInputCenter); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 8); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 8); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); + QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier); + + //Move 2 characters LEFT + for (int i = 0; i < 2; ++i) { + page->event(&keyLeftEventPress); + page->event(&keyLeftEventRelease); + } + + //Select to the start of the line + page->triggerAction(QWebPage::SelectStartOfLine); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 6); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("QtWebK")); + + //END - Tests for Selection when the Editor is not in Composition mode + + //ImhHiddenText + QPoint passwordInputCenter = inputs.at(1).geometry().center(); + clickOnPage(page, passwordInputCenter); + + QVERIFY(inputMethodEnabled(view)); + QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText); + + clickOnPage(page, textInputCenter); + QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText)); + + page->mainFrame()->setHtml("<html><body><p>nothing to input here"); + viewEventSpy.clear(); + + QWebElement para = page->mainFrame()->findFirstElement("p"); + clickOnPage(page, para.geometry().center()); + + QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); + + //START - Test for sending empty QInputMethodEvent + page->mainFrame()->setHtml("<html><body>" \ + "<input type='text' id='input3' value='QtWebKit2'/>" \ + "</body></html>"); + page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();"); + + //Send empty QInputMethodEvent + QInputMethodEvent emptyEvent; + page->event(&emptyEvent); + + QString inputValue = page->mainFrame()->evaluateJavaScript("document.getElementById('input3').value").toString(); + QCOMPARE(inputValue, QString("QtWebKit2")); + //END - Test for sending empty QInputMethodEvent + + page->mainFrame()->setHtml("<html><body>" \ + "<input type='text' id='input4' value='QtWebKit inputMethod'/>" \ + "</body></html>"); + page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();"); + + // Clear the selection, also cancel the ongoing composition if there is one. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant()); + attributes.append(newSelection); + QInputMethodEvent event("", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + variant = page->inputMethodQuery(Qt::ImSurroundingText); + QString surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("QtWebKit inputMethod")); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 0); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + // 1. Insert a character to the begining of the line. + // Send temporary text, which makes the editor has composition 'm'. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("m", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("QtWebKit inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 0); + + // Send temporary text, which makes the editor has composition 'n'. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("n", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("QtWebKit inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 0); + + // Send commit text, which makes the editor conforms composition. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("o"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 1); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 1); + + // 2. insert a character to the middle of the line. + // Send temporary text, which makes the editor has composition 'd'. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("d", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 1); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 1); + + // Send commit text, which makes the editor conforms composition. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("e"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 2); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 2); + + // 3. Insert a character to the end of the line. + page->triggerAction(QWebPage::MoveToEndOfLine); + + // Send temporary text, which makes the editor has composition 't'. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("t", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 22); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 22); + + // Send commit text, which makes the editor conforms composition. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("t"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 23); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 23); + + // 4. Replace the selection. + page->triggerAction(QWebPage::SelectPreviousWord); + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("inputMethodt")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 11); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 23); + + // Send temporary text, which makes the editor has composition 'w'. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("w", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oeQtWebKit ")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 11); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 11); + + // Send commit text, which makes the editor conforms composition. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("2"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value<QString>(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value<QString>(); + QCOMPARE(surroundingValue, QString("oeQtWebKit 2")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 12); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 12); + + // Check sending RequestSoftwareInputPanel event + page->mainFrame()->setHtml("<html><body>" \ + "<input type='text' id='input5' value='QtWebKit inputMethod'/>" \ + "<div id='btnDiv' onclick='i=document.getElementById("input5"); i.focus();'>abc</div>"\ + "</body></html>"); + QWebElement inputElement = page->mainFrame()->findFirstElement("div"); + clickOnPage(page, inputElement.geometry().center()); + + QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); + + // START - Newline test for textarea + qApp->processEvents(); + page->mainFrame()->setHtml("<html><body>" \ + "<textarea rows='5' cols='1' id='input5' value=''/>" \ + "</body></html>"); + page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input5'); inputEle.focus(); inputEle.select();"); + QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); + page->event(&keyEnter); + QList<QInputMethodEvent::Attribute> attribs; + + QInputMethodEvent eventText("\n", attribs); + page->event(&eventText); + + QInputMethodEvent eventText2("third line", attribs); + page->event(&eventText2); + qApp->processEvents(); + + QString inputValue2 = page->mainFrame()->evaluateJavaScript("document.getElementById('input5').value").toString(); + QCOMPARE(inputValue2, QString("\n\nthird line")); + // END - Newline test for textarea + + delete container; +} + +void tst_QWebPage::inputMethodsTextFormat_data() +{ + QTest::addColumn<QString>("string"); + QTest::addColumn<int>("start"); + QTest::addColumn<int>("length"); + + QTest::newRow("") << QString("") << 0 << 0; + QTest::newRow("Q") << QString("Q") << 0 << 1; + QTest::newRow("Qt") << QString("Qt") << 0 << 1; + QTest::newRow("Qt") << QString("Qt") << 0 << 2; + QTest::newRow("Qt") << QString("Qt") << 1 << 1; + QTest::newRow("Qt ") << QString("Qt ") << 0 << 1; + QTest::newRow("Qt ") << QString("Qt ") << 1 << 1; + QTest::newRow("Qt ") << QString("Qt ") << 2 << 1; + QTest::newRow("Qt ") << QString("Qt ") << 2 << -1; + QTest::newRow("Qt ") << QString("Qt ") << -2 << 3; + QTest::newRow("Qt ") << QString("Qt ") << 0 << 3; + QTest::newRow("Qt by") << QString("Qt by") << 0 << 1; + QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1; +} + + +void tst_QWebPage::inputMethodsTextFormat() +{ + QWebPage* page = new QWebPage; + QWebView* view = new QWebView; + view->setPage(page); + page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont"); + page->mainFrame()->setHtml("<html><body>" \ + "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>"); + page->mainFrame()->evaluateJavaScript("document.getElementById('input1').focus()"); + page->mainFrame()->setFocus(); + view->show(); + + QFETCH(QString, string); + QFETCH(int, start); + QFETCH(int, length); + + QList<QInputMethodEvent::Attribute> attrs; + QTextCharFormat format; + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + format.setUnderlineColor(Qt::red); + attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format)); + QInputMethodEvent im(string, attrs); + page->event(&im); + + QTest::qWait(1000); + + delete view; +} + +void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector() +{ + QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); + + PluginPage* newPage = new PluginPage(m_view); + m_view->setPage(newPage); + + m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true); + + m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>")); + QTRY_COMPARE(loadSpy.count(), 1); + + newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }"); + + newPage->mainFrame()->evaluateJavaScript("testme('foo')"); + + DumpRenderTreeSupportQt::garbageCollectorCollect(); + + // don't crash! + newPage->mainFrame()->evaluateJavaScript("testme('bar')"); +} + +void tst_QWebPage::localURLSchemes() +{ + int i = QWebSecurityOrigin::localSchemes().size(); + + QWebSecurityOrigin::removeLocalScheme("file"); + QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); + QWebSecurityOrigin::addLocalScheme("file"); + QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); + + QWebSecurityOrigin::removeLocalScheme("qrc"); + QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1); + QWebSecurityOrigin::addLocalScheme("qrc"); + QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); + + QString myscheme = "myscheme"; + QWebSecurityOrigin::addLocalScheme(myscheme); + QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1); + QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme)); + QWebSecurityOrigin::removeLocalScheme(myscheme); + QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); + QWebSecurityOrigin::removeLocalScheme(myscheme); + QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i); +} + +static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue) +{ + webPage.settings()->setAttribute(settingAttribute, settingValue); + return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool(); +} + +void tst_QWebPage::testOptionalJSObjects() +{ + // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off + // the visibility of the JS object any more. For this reason this test uses two QWebPage instances. + // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on + // a feature for one instance will not turn it on for another. + + QWebPage webPage1; + QWebPage webPage2; + + webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); + webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl()); + + QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); + QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); + QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true); + QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue); + QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false); + QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true); + + QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false); + QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true), true); + QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false); + QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true); +} + +void tst_QWebPage::testEnablePersistentStorage() +{ + QWebPage webPage; + + // By default all persistent options should be disabled + QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false); + QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false); + QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false); + QVERIFY(webPage.settings()->iconDatabasePath().isEmpty()); + + QWebSettings::enablePersistentStorage(); + + + QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true); + QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true); + QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true); + + QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty()); + QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty()); + QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty()); +} + +void tst_QWebPage::defaultTextEncoding() +{ + QWebFrame* mainFrame = m_page->mainFrame(); + + QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); + QVERIFY(!defaultCharset.isEmpty()); + QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset); + + m_page->settings()->setDefaultTextEncoding(QString("utf-8")); + QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); + QCOMPARE(charset, QString("utf-8")); + QCOMPARE(m_page->settings()->defaultTextEncoding(), charset); + + m_page->settings()->setDefaultTextEncoding(QString()); + charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); + QVERIFY(!charset.isEmpty()); + QCOMPARE(charset, defaultCharset); + + QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8")); + charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString(); + QCOMPARE(charset, QString("utf-8")); + QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset); +} + +class ErrorPage : public QWebPage +{ +public: + + ErrorPage(QWidget* parent = 0): QWebPage(parent) + { + } + + virtual bool supportsExtension(Extension extension) const + { + return extension == ErrorPageExtension; + } + + virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output) + { + ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output); + + errorPage->contentType = "text/html"; + errorPage->content = "error"; + return true; + } +}; + +void tst_QWebPage::errorPageExtension() +{ + ErrorPage* page = new ErrorPage; + m_view->setPage(page); + + QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); + + m_view->setUrl(QUrl("data:text/html,foo")); + QTRY_COMPARE(spyLoadFinished.count(), 1); + + page->mainFrame()->setUrl(QUrl("http://non.existent/url")); + QTRY_COMPARE(spyLoadFinished.count(), 2); + QCOMPARE(page->mainFrame()->toPlainText(), QString("error")); + QCOMPARE(page->history()->count(), 2); + QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url")); + QCOMPARE(page->history()->canGoBack(), true); + QCOMPARE(page->history()->canGoForward(), false); + + page->triggerAction(QWebPage::Back); + QTRY_COMPARE(page->history()->canGoBack(), false); + QTRY_COMPARE(page->history()->canGoForward(), true); + + page->triggerAction(QWebPage::Forward); + QTRY_COMPARE(page->history()->canGoBack(), true); + QTRY_COMPARE(page->history()->canGoForward(), false); + + page->triggerAction(QWebPage::Back); + QTRY_COMPARE(page->history()->canGoBack(), false); + QTRY_COMPARE(page->history()->canGoForward(), true); + QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo")); + + m_view->setPage(0); +} + +void tst_QWebPage::errorPageExtensionInIFrames() +{ + ErrorPage* page = new ErrorPage; + m_view->setPage(page); + + m_view->page()->mainFrame()->load(QUrl( + "data:text/html," + "<h1>h1</h1>" + "<iframe src='data:text/html,<p/>p'></iframe>" + "<iframe src='http://non.existent/url'></iframe>")); + QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); + QTRY_COMPARE(spyLoadFinished.count(), 1); + + QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error")); + + m_view->setPage(0); +} + +void tst_QWebPage::errorPageExtensionInFrameset() +{ + ErrorPage* page = new ErrorPage; + m_view->setPage(page); + + m_view->load(QUrl("qrc:///resources/index.html")); + + QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); + QTRY_COMPARE(spyLoadFinished.count(), 1); + QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error")); + + m_view->setPage(0); +} + +class FriendlyWebPage : public QWebPage +{ +public: + friend class tst_QWebPage; +}; + +void tst_QWebPage::userAgentApplicationName() +{ + const QString oldApplicationName = QCoreApplication::applicationName(); + FriendlyWebPage page; + + const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236"); + QCoreApplication::setApplicationName(applicationNameMarker); + QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker)); + + QCoreApplication::setApplicationName(oldApplicationName); +} + +void tst_QWebPage::crashTests_LazyInitializationOfMainFrame() +{ + { + QWebPage webPage; + } + { + QWebPage webPage; + webPage.selectedText(); + } + { + QWebPage webPage; + webPage.selectedHtml(); + } + { + QWebPage webPage; + webPage.triggerAction(QWebPage::Back, true); + } + { + QWebPage webPage; + QPoint pos(10,10); + webPage.updatePositionDependentActions(pos); + } +} + +static void takeScreenshot(QWebPage* page) +{ + QWebFrame* mainFrame = page->mainFrame(); + page->setViewportSize(mainFrame->contentsSize()); + QImage image(page->viewportSize(), QImage::Format_ARGB32); + QPainter painter(&image); + mainFrame->render(&painter); + painter.end(); +} + +void tst_QWebPage::screenshot_data() +{ + QTest::addColumn<QString>("html"); + QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>"; + QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>"); + QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>"); +} + +void tst_QWebPage::screenshot() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QDir::setCurrent(TESTS_SOURCE_DIR); + + QFETCH(QString, html); + QWebPage* page = new QWebPage; + page->settings()->setAttribute(QWebSettings::PluginsEnabled, true); + QWebFrame* mainFrame = page->mainFrame(); + mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); + ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000); + + // take screenshot without a view + takeScreenshot(page); + + QWebView* view = new QWebView; + view->setPage(page); + + // take screenshot when attached to a view + takeScreenshot(page); + + delete page; + delete view; + + QDir::setCurrent(QApplication::applicationDirPath()); +} + +#if defined(ENABLE_WEBGL) && ENABLE_WEBGL +// https://bugs.webkit.org/show_bug.cgi?id=54138 +static void webGLScreenshotWithoutView(bool accelerated) +{ + QWebPage page; + page.settings()->setAttribute(QWebSettings::WebGLEnabled, true); + page.settings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, accelerated); + QWebFrame* mainFrame = page.mainFrame(); + mainFrame->setHtml("<html><body>" + "<canvas id='webgl' width='300' height='300'></canvas>" + "<script>document.getElementById('webgl').getContext('experimental-webgl')</script>" + "</body></html>"); + + takeScreenshot(&page); +} + +void tst_QWebPage::acceleratedWebGLScreenshotWithoutView() +{ + webGLScreenshotWithoutView(true); +} + +void tst_QWebPage::unacceleratedWebGLScreenshotWithoutView() +{ + webGLScreenshotWithoutView(false); +} +#endif + +void tst_QWebPage::originatingObjectInNetworkRequests() +{ + TestNetworkManager* networkManager = new TestNetworkManager(m_page); + m_page->setNetworkAccessManager(networkManager); + networkManager->requests.clear(); + + m_view->setHtml(QString("<frameset cols=\"25%,75%\"><frame src=\"data:text/html," + "<head><meta http-equiv='refresh' content='1'></head>foo \">" + "<frame src=\"data:text/html,bar\"></frameset>"), QUrl()); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QCOMPARE(networkManager->requests.count(), 2); + + QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames(); + QCOMPARE(childFrames.count(), 2); + + for (int i = 0; i < 2; ++i) + QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i)); +} + +/** + * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914 + * + * From JS we test the following conditions. + * + * OK + QString() => SUCCESS, empty string (but not null) + * OK + "text" => SUCCESS, "text" + * CANCEL + QString() => CANCEL, null string + * CANCEL + "text" => CANCEL, null string + */ +class JSPromptPage : public QWebPage { + Q_OBJECT +public: + JSPromptPage() + {} + + bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result) + { + if (msg == QLatin1String("test1")) { + *result = QString(); + return true; + } else if (msg == QLatin1String("test2")) { + *result = QLatin1String("text"); + return true; + } else if (msg == QLatin1String("test3")) { + *result = QString(); + return false; + } else if (msg == QLatin1String("test4")) { + *result = QLatin1String("text"); + return false; + } + + qFatal("Unknown msg."); + return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result); + } +}; + +void tst_QWebPage::testJSPrompt() +{ + JSPromptPage page; + bool res; + + // OK + QString() + res = page.mainFrame()->evaluateJavaScript( + "var retval = prompt('test1');" + "retval=='' && retval.length == 0;").toBool(); + QVERIFY(res); + + // OK + "text" + res = page.mainFrame()->evaluateJavaScript( + "var retval = prompt('test2');" + "retval=='text' && retval.length == 4;").toBool(); + QVERIFY(res); + + // Cancel + QString() + res = page.mainFrame()->evaluateJavaScript( + "var retval = prompt('test3');" + "retval===null;").toBool(); + QVERIFY(res); + + // Cancel + "text" + res = page.mainFrame()->evaluateJavaScript( + "var retval = prompt('test4');" + "retval===null;").toBool(); + QVERIFY(res); +} + +class TestModalPage : public QWebPage +{ + Q_OBJECT +public: + TestModalPage(QObject* parent = 0) : QWebPage(parent) { + } + virtual QWebPage* createWindow(WebWindowType) { + QWebPage* page = new TestModalPage(); + connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater())); + return page; + } +}; + +void tst_QWebPage::showModalDialog() +{ + TestModalPage page; + page.mainFrame()->setHtml(QString("<html></html>")); + QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString(); + QCOMPARE(res, QString("This is a test")); +} + +void tst_QWebPage::testStopScheduledPageRefresh() +{ + // Without QWebPage::StopScheduledPageRefresh + QWebPage page1; + page1.setNetworkAccessManager(new TestNetworkManager(&page1)); + page1.mainFrame()->setHtml("<html><head>" + "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">" + "</head><body><h1>Page redirects immediately...</h1>" + "</body></html>"); + QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool)))); + QTest::qWait(500); + QCOMPARE(page1.mainFrame()->url(), QUrl(QLatin1String("qrc:///resources/index.html"))); + + // With QWebPage::StopScheduledPageRefresh + QWebPage page2; + page2.setNetworkAccessManager(new TestNetworkManager(&page2)); + page2.mainFrame()->setHtml("<html><head>" + "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">" + "</head><body><h1>Page redirect test with 1 sec timeout...</h1>" + "</body></html>"); + page2.triggerAction(QWebPage::StopScheduledPageRefresh); + QTest::qWait(1500); + QCOMPARE(page2.mainFrame()->url().toString(), QLatin1String("about:blank")); +} + +void tst_QWebPage::findText() +{ + m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); + m_page->triggerAction(QWebPage::SelectAll); + QVERIFY(!m_page->selectedText().isEmpty()); + QVERIFY(!m_page->selectedHtml().isEmpty()); + m_page->findText(""); + QVERIFY(m_page->selectedText().isEmpty()); + QVERIFY(m_page->selectedHtml().isEmpty()); + QStringList words = (QStringList() << "foo" << "bar"); + QRegExp regExp(" style=\".*\""); + regExp.setMinimal(true); + foreach (QString subString, words) { + m_page->findText(subString, QWebPage::FindWrapsAroundDocument); + QCOMPARE(m_page->selectedText(), subString); + QCOMPARE(m_page->selectedHtml().trimmed().replace(regExp, ""), QString("<span class=\"Apple-style-span\">%1</span>").arg(subString)); + m_page->findText(""); + QVERIFY(m_page->selectedText().isEmpty()); + QVERIFY(m_page->selectedHtml().isEmpty()); + } +} + +struct ImageExtensionMap { + const char* extension; + const char* mimeType; +}; + +static const ImageExtensionMap extensionMap[] = { + { "bmp", "image/bmp" }, + { "css", "text/css" }, + { "gif", "image/gif" }, + { "html", "text/html" }, + { "htm", "text/html" }, + { "ico", "image/x-icon" }, + { "jpeg", "image/jpeg" }, + { "jpg", "image/jpeg" }, + { "js", "application/x-javascript" }, + { "mng", "video/x-mng" }, + { "pbm", "image/x-portable-bitmap" }, + { "pgm", "image/x-portable-graymap" }, + { "pdf", "application/pdf" }, + { "png", "image/png" }, + { "ppm", "image/x-portable-pixmap" }, + { "rss", "application/rss+xml" }, + { "svg", "image/svg+xml" }, + { "text", "text/plain" }, + { "tif", "image/tiff" }, + { "tiff", "image/tiff" }, + { "txt", "text/plain" }, + { "xbm", "image/x-xbitmap" }, + { "xml", "text/xml" }, + { "xpm", "image/x-xpm" }, + { "xsl", "text/xsl" }, + { "xhtml", "application/xhtml+xml" }, + { "wml", "text/vnd.wap.wml" }, + { "wmlc", "application/vnd.wap.wmlc" }, + { 0, 0 } +}; + +static QString getMimeTypeForExtension(const QString &ext) +{ + const ImageExtensionMap *e = extensionMap; + while (e->extension) { + if (ext.compare(QLatin1String(e->extension), Qt::CaseInsensitive) == 0) + return QLatin1String(e->mimeType); + ++e; + } + + return QString(); +} + +void tst_QWebPage::supportedContentType() +{ + QStringList contentTypes; + + // Add supported non image types... + contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/" + << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml" + << "application/rss+xml" << "application/atom+xml" << "application/json"; + + // Add supported image types... + Q_FOREACH(const QByteArray& imageType, QImageWriter::supportedImageFormats()) { + const QString mimeType = getMimeTypeForExtension(imageType); + if (!mimeType.isEmpty()) + contentTypes << mimeType; + } + + // Get the mime types supported by webkit... + const QStringList supportedContentTypes = m_page->supportedContentTypes(); + + Q_FOREACH(const QString& mimeType, contentTypes) + QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1()); + + Q_FOREACH(const QString& mimeType, contentTypes) + QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1()); +} + + +void tst_QWebPage::navigatorCookieEnabled() +{ + m_page->networkAccessManager()->setCookieJar(0); + QVERIFY(!m_page->networkAccessManager()->cookieJar()); + QVERIFY(!m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool()); + + m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); + QVERIFY(m_page->networkAccessManager()->cookieJar()); + QVERIFY(m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool()); +} + +#ifdef Q_OS_MAC +void tst_QWebPage::macCopyUnicodeToClipboard() +{ + QString unicodeText = QString::fromUtf8("αβγδεζηθικλμπ"); + m_page->mainFrame()->setHtml(QString("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head><body>%1</body></html>").arg(unicodeText)); + m_page->triggerAction(QWebPage::SelectAll); + m_page->triggerAction(QWebPage::Copy); + + QString clipboardData = QString::fromUtf8(QApplication::clipboard()->mimeData()->data(QLatin1String("text/html"))); + + QVERIFY(clipboardData.contains(QLatin1String("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"))); + QVERIFY(clipboardData.contains(unicodeText)); +} +#endif + +void tst_QWebPage::contextMenuCopy() +{ + QWebView view; + + view.setHtml("<a href=\"http://www.google.com\">You cant miss this</a>"); + + view.page()->triggerAction(QWebPage::SelectAll); + QVERIFY(!view.page()->selectedText().isEmpty()); + + QWebElement link = view.page()->mainFrame()->findFirstElement("a"); + QPoint pos(link.geometry().center()); + QContextMenuEvent event(QContextMenuEvent::Mouse, pos); + view.page()->swallowContextMenuEvent(&event); + view.page()->updatePositionDependentActions(pos); + + QList<QMenu*> contextMenus = view.findChildren<QMenu*>(); + QVERIFY(!contextMenus.isEmpty()); + QMenu* contextMenu = contextMenus.first(); + QVERIFY(contextMenu); + + QList<QAction *> list = contextMenu->actions(); + int index = list.indexOf(view.page()->action(QWebPage::Copy)); + QVERIFY(index != -1); +} + +void tst_QWebPage::deleteQWebViewTwice() +{ + for (int i = 0; i < 2; ++i) { + QMainWindow mainWindow; + QWebView* webView = new QWebView(&mainWindow); + mainWindow.setCentralWidget(webView); + webView->load(QUrl("qrc:///resources/frame_a.html")); + mainWindow.show(); + connect(webView, SIGNAL(loadFinished(bool)), &mainWindow, SLOT(close())); + QApplication::instance()->exec(); + } +} + +class RepaintRequestedRenderer : public QObject { + Q_OBJECT +public: + RepaintRequestedRenderer(QWebPage* page, QPainter* painter) + : m_page(page) + , m_painter(painter) + , m_recursionCount(0) + { + connect(m_page, SIGNAL(repaintRequested(QRect)), this, SLOT(onRepaintRequested(QRect))); + } + +signals: + void finished(); + +private slots: + void onRepaintRequested(const QRect& rect) + { + QCOMPARE(m_recursionCount, 0); + + m_recursionCount++; + m_page->mainFrame()->render(m_painter, rect); + m_recursionCount--; + + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + } + +private: + QWebPage* m_page; + QPainter* m_painter; + int m_recursionCount; +}; + +void tst_QWebPage::renderOnRepaintRequestedShouldNotRecurse() +{ + QSize viewportSize(720, 576); + QWebPage page; + + QImage image(viewportSize, QImage::Format_ARGB32); + QPainter painter(&image); + + page.setPreferredContentsSize(viewportSize); + page.setViewportSize(viewportSize); + RepaintRequestedRenderer r(&page, &painter); + + page.mainFrame()->setHtml("zalan loves trunk", QUrl()); + + QVERIFY(::waitForSignal(&r, SIGNAL(finished()))); +} + +QTEST_MAIN(tst_QWebPage) +#include "tst_qwebpage.moc" diff --git a/Source/WebKit/qt/tests/qwebpage/tst_qwebpage.qrc b/Source/WebKit/qt/tests/qwebpage/tst_qwebpage.qrc new file mode 100644 index 0000000..a0efabe --- /dev/null +++ b/Source/WebKit/qt/tests/qwebpage/tst_qwebpage.qrc @@ -0,0 +1,13 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>resources/index.html</file> + <file>resources/frame_a.html</file> + <file>resources/frame_c.html</file> + <file>resources/iframe.html</file> + <file>resources/iframe2.html</file> + <file>resources/iframe3.html</file> + <file>resources/framedindex.html</file> + <file>resources/content.html</file> +</qresource> +</RCC> + diff --git a/Source/WebKit/qt/tests/qwebplugindatabase/qwebplugindatabase.pro b/Source/WebKit/qt/tests/qwebplugindatabase/qwebplugindatabase.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebplugindatabase/qwebplugindatabase.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebplugindatabase/tst_qwebplugindatabase.cpp b/Source/WebKit/qt/tests/qwebplugindatabase/tst_qwebplugindatabase.cpp new file mode 100644 index 0000000..1ee6206 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebplugindatabase/tst_qwebplugindatabase.cpp @@ -0,0 +1,437 @@ +/* + Copyright (C) 2009 Jakub Wieczorek <faw217@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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <QtTest/QtTest> + +#include <qdir.h> +#include <qwebframe.h> +#include <qwebpage.h> +#include <qwebplugindatabase.h> +#include <qwebsettings.h> +#include <qvariant.h> + +class tst_QWebPluginDatabase : public QObject +{ + Q_OBJECT + +private slots: + void installedPlugins(); + void searchPaths(); + void null_data(); + void null(); + void pluginForMimeType(); + void enabled(); + void operatorequal_data(); + void operatorequal(); + void preferredPlugin(); + void operatorassign_data(); + void operatorassign(); +}; + +typedef QWebPluginInfo::MimeType MimeType; + +void tst_QWebPluginDatabase::installedPlugins() +{ + QWebPage page; + page.settings()->setAttribute(QWebSettings::PluginsEnabled, true); + QWebFrame* frame = page.mainFrame(); + + QVariantMap jsPluginsMap = frame->evaluateJavaScript("window.navigator.plugins").toMap(); + QList<QWebPluginInfo> plugins = QWebSettings::pluginDatabase()->plugins(); + QCOMPARE(plugins, QWebSettings::pluginDatabase()->plugins()); + + int length = jsPluginsMap["length"].toInt(); + QCOMPARE(length, plugins.count()); + + for (int i = 0; i < length; ++i) { + QWebPluginInfo plugin = plugins.at(i); + + QVariantMap jsPlugin = frame->evaluateJavaScript(QString("window.navigator.plugins[%1]").arg(i)).toMap(); + QString name = jsPlugin["name"].toString(); + QString description = jsPlugin["description"].toString(); + QString fileName = jsPlugin["filename"].toString(); + + QCOMPARE(plugin.name(), name); + QCOMPARE(plugin.description(), description); + QCOMPARE(QFileInfo(plugin.path()).fileName(), fileName); + + QList<MimeType> mimeTypes; + int mimeTypesCount = jsPlugin["length"].toInt(); + + for (int j = 0; j < mimeTypesCount; ++j) { + QVariantMap jsMimeType = frame->evaluateJavaScript(QString("window.navigator.plugins[%1][%2]").arg(i).arg(j)).toMap(); + + MimeType mimeType; + mimeType.name = jsMimeType["type"].toString(); + mimeType.description = jsMimeType["description"].toString(); + mimeType.fileExtensions = jsMimeType["suffixes"].toString().split(',', QString::SkipEmptyParts); + + mimeTypes.append(mimeType); + QVERIFY(plugin.supportsMimeType(mimeType.name)); + } + + QCOMPARE(plugin.mimeTypes(), mimeTypes); + + QVERIFY(!plugin.isNull()); + QVERIFY(plugin.isEnabled()); + } +} + +void tst_QWebPluginDatabase::searchPaths() +{ + QWebPluginDatabase* database = QWebSettings::pluginDatabase(); + QList<QWebPluginInfo> plugins = database->plugins(); + QStringList directories = database->searchPaths(); + QCOMPARE(QWebPluginDatabase::defaultSearchPaths(), directories); + + database->setSearchPaths(directories); + QCOMPARE(QWebPluginDatabase::defaultSearchPaths(), directories); + QCOMPARE(database->searchPaths(), directories); + QCOMPARE(database->plugins(), plugins); + database->refresh(); + QCOMPARE(database->plugins(), plugins); + + database->setSearchPaths(QStringList()); + QCOMPARE(QWebPluginDatabase::defaultSearchPaths(), directories); + QCOMPARE(database->searchPaths(), QStringList()); + QCOMPARE(database->plugins().count(), 0); + + database->setSearchPaths(directories); + QCOMPARE(database->searchPaths(), directories); + database->addSearchPath(QDir::tempPath()); + QCOMPARE(database->searchPaths().count(), directories.count() + 1); + QVERIFY(database->searchPaths().contains(QDir::tempPath())); + directories.append(QDir::tempPath()); + QCOMPARE(database->searchPaths(), directories); + + // As an empty set of search paths has been set, the database has been rebuilt + // from scratch after bringing the old path set back. + // Because the QWebPlugins no longer point to the same PluginPackages, + // the list is also no longer equal to the older one, even though it contains + // the same information. + QCOMPARE(database->plugins().count(), plugins.count()); + plugins = database->plugins(); + QCOMPARE(database->plugins(), plugins); + + for (int i = (directories.count() - 1); i >= 0; --i) { + QDir directory(directories.at(i)); + if (!directory.exists() || !directory.count()) + directories.removeAt(i); + } + + database->setSearchPaths(directories); + QCOMPARE(database->plugins(), plugins); + database->refresh(); + QCOMPARE(database->plugins(), plugins); + + database->setSearchPaths(QWebPluginDatabase::defaultSearchPaths()); + directories = QWebPluginDatabase::defaultSearchPaths(); + QCOMPARE(QWebPluginDatabase::defaultSearchPaths(), directories); + QCOMPARE(database->searchPaths(), directories); + QCOMPARE(database->plugins(), plugins); +} + +Q_DECLARE_METATYPE(QWebPluginInfo) +void tst_QWebPluginDatabase::null_data() +{ + QTest::addColumn<QWebPluginInfo>("plugin"); + QTest::addColumn<bool>("null"); + + QTest::newRow("null") << QWebPluginInfo() << true; + QTest::newRow("foo") << QWebSettings::pluginDatabase()->pluginForMimeType("foobarbaz") << true; + + QList<QWebPluginInfo> plugins = QWebSettings::pluginDatabase()->plugins(); + for (int i = 0; i < plugins.count(); ++i) + QTest::newRow(QString::number(i).toUtf8().constData()) << plugins.at(i) << false; +} + +void tst_QWebPluginDatabase::null() +{ + QFETCH(QWebPluginInfo, plugin); + QFETCH(bool, null); + + QCOMPARE(plugin.isNull(), null); +} + +void tst_QWebPluginDatabase::pluginForMimeType() +{ + QMultiMap<QString, QWebPluginInfo> pluginsMap; + QWebPluginDatabase* database = QWebSettings::pluginDatabase(); + QList<QWebPluginInfo> plugins = database->plugins(); + + for (int i = 0; i < plugins.count(); ++i) { + QWebPluginInfo plugin = plugins.at(i); + + QList<MimeType> mimeTypes = plugin.mimeTypes(); + for (int j = 0; j < mimeTypes.count(); ++j) { + QString mimeType = mimeTypes.at(j).name; + pluginsMap.insert(mimeType, plugin); + QVERIFY(plugin.supportsMimeType(mimeType)); + } + } + + for (int i = 0; i < plugins.count(); ++i) { + QWebPluginInfo plugin = plugins.at(i); + + QList<MimeType> mimeTypes = plugin.mimeTypes(); + for (int j = 0; j < mimeTypes.count(); ++j) { + QString mimeType = mimeTypes.at(j).name; + + QVERIFY(pluginsMap.count(mimeType) > 0); + if (pluginsMap.count(mimeType) > 1) + continue; + + QWebPluginInfo pluginForMimeType = database->pluginForMimeType(mimeType); + QCOMPARE(pluginForMimeType, plugin); + database->setSearchPaths(database->searchPaths()); + QCOMPARE(pluginForMimeType, plugin); + QCOMPARE(pluginForMimeType, database->pluginForMimeType(mimeType.toUpper())); + QCOMPARE(pluginForMimeType, database->pluginForMimeType(mimeType.toLower())); + QVERIFY(plugin.supportsMimeType(mimeType)); + QVERIFY(!pluginForMimeType.isNull()); + QVERIFY(!plugin.isNull()); + } + } +} + +void tst_QWebPluginDatabase::enabled() +{ + QMultiMap<QString, QWebPluginInfo> pluginsMap; + QWebPluginDatabase* database = QWebSettings::pluginDatabase(); + QList<QWebPluginInfo> plugins = database->plugins(); + + for (int i = 0; i < plugins.count(); ++i) { + QWebPluginInfo plugin = plugins.at(i); + + QList<MimeType> mimeTypes = plugin.mimeTypes(); + for (int j = 0; j < mimeTypes.count(); ++j) { + QString mimeType = mimeTypes.at(j).name; + pluginsMap.insert(mimeType, plugin); + QVERIFY(plugin.supportsMimeType(mimeType)); + } + } + + QMultiMap<QString, QWebPluginInfo>::iterator it = pluginsMap.begin(); + while (it != pluginsMap.end()) { + QString mimeType = it.key(); + QWebPluginInfo plugin = it.value(); + QWebPluginInfo pluginForMimeType = database->pluginForMimeType(mimeType); + + QVERIFY(pluginsMap.count(mimeType) > 0); + + if (pluginsMap.count(mimeType) == 1) { + QCOMPARE(plugin, pluginForMimeType); + + QVERIFY(plugin.isEnabled()); + QVERIFY(pluginForMimeType.isEnabled()); + plugin.setEnabled(false); + QVERIFY(!plugin.isEnabled()); + QVERIFY(!pluginForMimeType.isEnabled()); + } else { + QVERIFY(plugin.isEnabled()); + QVERIFY(pluginForMimeType.isEnabled()); + plugin.setEnabled(false); + QVERIFY(!plugin.isEnabled()); + } + + QVERIFY(!plugin.isNull()); + QVERIFY(!pluginForMimeType.isNull()); + + QWebPluginInfo pluginForMimeType2 = database->pluginForMimeType(mimeType); + if (pluginsMap.count(mimeType) == 1) { + QVERIFY(pluginForMimeType2 != plugin); + QVERIFY(pluginForMimeType2.isNull()); + } else { + QVERIFY(pluginForMimeType2 != plugin); + QVERIFY(!pluginForMimeType2.isNull()); + } + + plugin.setEnabled(true); + + ++it; + } +} + +void tst_QWebPluginDatabase::operatorequal_data() +{ + QTest::addColumn<QWebPluginInfo>("first"); + QTest::addColumn<QWebPluginInfo>("second"); + QTest::addColumn<bool>("equal"); + + QWebPluginDatabase* database = QWebSettings::pluginDatabase(); + QTest::newRow("null") << QWebPluginInfo() << QWebPluginInfo() << true; + QTest::newRow("application/x-shockwave-flash") << database->pluginForMimeType("application/x-shockwave-flash") + << database->pluginForMimeType("application/x-shockwave-flash") << true; + QTest::newRow("foo/bar-baz") << database->pluginForMimeType("foo/bar-baz") + << database->pluginForMimeType("foo/bar-baz") << true; + + QList<QWebPluginInfo> plugins = database->plugins(); + for (int i = 0; i < (plugins.count() - 1); ++i) { + QWebPluginInfo first = plugins.at(i); + QWebPluginInfo second = plugins.at(i + 1); + + QTest::newRow(QString("%1==%2").arg(first.name(), second.name()).toUtf8().constData()) + << first << second << false; + } +} + +void tst_QWebPluginDatabase::operatorequal() +{ + QFETCH(QWebPluginInfo, first); + QFETCH(QWebPluginInfo, second); + QFETCH(bool, equal); + + QCOMPARE(first == second, equal); +} + +void tst_QWebPluginDatabase::preferredPlugin() +{ + QMultiMap<QString, QWebPluginInfo> pluginsMap; + QWebPluginDatabase* database = QWebSettings::pluginDatabase(); + QList<QWebPluginInfo> plugins = database->plugins(); + + for (int i = 0; i < plugins.count(); ++i) { + QWebPluginInfo plugin = plugins.at(i); + + QList<MimeType> mimeTypes = plugin.mimeTypes(); + for (int j = 0; j < mimeTypes.count(); ++j) { + QString mimeType = mimeTypes.at(j).name; + pluginsMap.insert(mimeType, plugin); + } + } + + QMultiMap<QString, QWebPluginInfo>::iterator it = pluginsMap.begin(); + while (it != pluginsMap.end()) { + QString mimeType = it.key(); + + if (pluginsMap.count(mimeType) > 1) { + QList<QWebPluginInfo> pluginsForMimeType = pluginsMap.values(mimeType); + QWebPluginInfo plugin = database->pluginForMimeType(mimeType); + QVERIFY(plugin.supportsMimeType(mimeType)); + + pluginsForMimeType.removeAll(plugin); + for (int i = 0; i < pluginsForMimeType.count(); ++i) { + QWebPluginInfo anotherPlugin = pluginsForMimeType.at(i); + QVERIFY(plugin.supportsMimeType(mimeType)); + QVERIFY(plugin != anotherPlugin); + + QCOMPARE(database->pluginForMimeType(mimeType), plugin); + database->setPreferredPluginForMimeType(mimeType, anotherPlugin); + QCOMPARE(database->pluginForMimeType(mimeType), anotherPlugin); + + anotherPlugin.setEnabled(false); + QCOMPARE(database->pluginForMimeType(mimeType), plugin); + + anotherPlugin.setEnabled(true); + QCOMPARE(database->pluginForMimeType(mimeType), anotherPlugin); + database->setSearchPaths(database->searchPaths()); + QCOMPARE(database->pluginForMimeType(mimeType), anotherPlugin); + + database->setPreferredPluginForMimeType(mimeType, QWebPluginInfo()); + QCOMPARE(database->pluginForMimeType(mimeType), plugin); + } + } else { + QWebPluginInfo plugin = database->pluginForMimeType(mimeType); + QCOMPARE(pluginsMap.value(mimeType), plugin); + + database->setPreferredPluginForMimeType(mimeType, plugin); + QCOMPARE(database->pluginForMimeType(mimeType), plugin); + + plugin.setEnabled(false); + QCOMPARE(database->pluginForMimeType(mimeType), QWebPluginInfo()); + plugin.setEnabled(true); + + database->setPreferredPluginForMimeType(mimeType, QWebPluginInfo()); + QCOMPARE(database->pluginForMimeType(mimeType), plugin); + } + + ++it; + } + + if (pluginsMap.keys().count() >= 2) { + QStringList mimeTypes = pluginsMap.uniqueKeys(); + + QString mimeType1 = mimeTypes.at(0); + QString mimeType2 = mimeTypes.at(1); + QWebPluginInfo plugin1 = database->pluginForMimeType(mimeType1); + QWebPluginInfo plugin2 = database->pluginForMimeType(mimeType2); + + int i = 2; + while (plugin2.supportsMimeType(mimeType1) + && !mimeType2.isEmpty() + && i < mimeTypes.count()) { + mimeType2 = mimeTypes.at(i); + plugin2 = database->pluginForMimeType(mimeType2); + ++i; + } + + plugin1 = database->pluginForMimeType(mimeType1); + QVERIFY(plugin1.supportsMimeType(mimeType1)); + QVERIFY(!plugin1.isNull()); + plugin2 = database->pluginForMimeType(mimeType2); + QVERIFY(plugin2.supportsMimeType(mimeType2)); + QVERIFY(!plugin2.isNull()); + + database->setPreferredPluginForMimeType(mimeType2, plugin1); + QVERIFY(!plugin1.supportsMimeType(mimeType2)); + QCOMPARE(database->pluginForMimeType(mimeType2), plugin2); + + database->setPreferredPluginForMimeType(mimeType1, plugin1); + QVERIFY(!plugin2.supportsMimeType(mimeType1)); + QCOMPARE(database->pluginForMimeType(mimeType2), plugin2); + } +} + +void tst_QWebPluginDatabase::operatorassign_data() +{ + QTest::addColumn<QWebPluginInfo>("first"); + QTest::addColumn<QWebPluginInfo>("second"); + + QWebPluginDatabase* database = QWebSettings::pluginDatabase(); + QTest::newRow("null") << QWebPluginInfo() << QWebPluginInfo(); + + QList<QWebPluginInfo> plugins = database->plugins(); + for (int i = 0; i < (plugins.count() - 1); ++i) { + QWebPluginInfo first = plugins.at(i); + QWebPluginInfo second = plugins.at(i + 1); + + QTest::newRow(QString("%1=%2").arg(first.name(), second.name()).toUtf8().constData()) << first << second; + } +} + +void tst_QWebPluginDatabase::operatorassign() +{ + QFETCH(QWebPluginInfo, first); + QFETCH(QWebPluginInfo, second); + + QWebPluginInfo info; + QCOMPARE(info.mimeTypes(), QList<MimeType>()); + QCOMPARE(info = first, first); + QCOMPARE(info, first); + QCOMPARE(info.mimeTypes(), first.mimeTypes()); + QCOMPARE(info = second, second); + QCOMPARE(info, second); + QCOMPARE(info.mimeTypes(), second.mimeTypes()); + QCOMPARE(info = QWebPluginInfo(), QWebPluginInfo()); + QCOMPARE(info.mimeTypes(), QList<MimeType>()); +} + +QTEST_MAIN(tst_QWebPluginDatabase) + +#include "tst_qwebplugindatabase.moc" diff --git a/Source/WebKit/qt/tests/qwebview/.gitignore b/Source/WebKit/qt/tests/qwebview/.gitignore new file mode 100644 index 0000000..444afe6 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebview/.gitignore @@ -0,0 +1 @@ +qwebview diff --git a/Source/WebKit/qt/tests/qwebview/qwebview.pro b/Source/WebKit/qt/tests/qwebview/qwebview.pro new file mode 100644 index 0000000..e915d60 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebview/qwebview.pro @@ -0,0 +1,3 @@ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc diff --git a/Source/WebKit/qt/tests/qwebview/resources/frame_a.html b/Source/WebKit/qt/tests/qwebview/resources/frame_a.html new file mode 100644 index 0000000..9ff68f1 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebview/resources/frame_a.html @@ -0,0 +1,2 @@ +<a href="http://google.com" target="frame_b"><img src="" width=100 height=100 alt="Google"></a> +<a href="http://yahoo.com" target="frame_b"><img src="" width=100 height=100 alt="Yahoo"></a> diff --git a/Source/WebKit/qt/tests/qwebview/resources/index.html b/Source/WebKit/qt/tests/qwebview/resources/index.html new file mode 100644 index 0000000..c53ad09 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebview/resources/index.html @@ -0,0 +1,4 @@ +<frameset cols="25%,75%"> + <frame src="frame_a.html" name="frame_a"> + <frame src="frame_b.html" name="frame_b"> +</frameset> diff --git a/Source/WebKit/qt/tests/qwebview/resources/input_types.html b/Source/WebKit/qt/tests/qwebview/resources/input_types.html new file mode 100644 index 0000000..2e893af --- /dev/null +++ b/Source/WebKit/qt/tests/qwebview/resources/input_types.html @@ -0,0 +1,9 @@ +<html><body> +<input type='text' maxlength='20' style='position: absolute; left: 10px; top: 0px; height: 50px; width: 100px;'/><br> +<input type='password' style='position: absolute; left: 10px; top: 50px; height: 50px; width: 100px;'/><br> +<input type='tel' style='position: absolute; left: 10px; top: 100px; height: 50px; width: 100px;'/><br> +<input type='number' style='position: absolute; left: 10px; top: 150px; height: 50px; width: 100px;'/><br> +<input type='email' style='position: absolute; left: 10px; top: 200px; height: 50px; width: 100px;'/><br> +<input type='url' style='position: absolute; left: 10px; top: 250px; height: 50px; width: 100px;'/><br> +<textarea style='position: absolute; left: 10px; top: 310px; height: 50px; width: 100px;' rows="2" cols="20">blah blah blah blah</textarea><br> +</body></html> diff --git a/Source/WebKit/qt/tests/qwebview/tst_qwebview.cpp b/Source/WebKit/qt/tests/qwebview/tst_qwebview.cpp new file mode 100644 index 0000000..f557dc1 --- /dev/null +++ b/Source/WebKit/qt/tests/qwebview/tst_qwebview.cpp @@ -0,0 +1,516 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Torch Mobile Inc. + Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> + + 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 <qtest.h> +#include "../util.h" + +#include <qpainter.h> +#include <qwebview.h> +#include <qwebpage.h> +#include <qnetworkrequest.h> +#include <qdiriterator.h> +#include <qwebkitversion.h> +#include <qwebelement.h> +#include <qwebframe.h> + +#ifdef Q_OS_SYMBIAN +#define VERIFY_INPUTMETHOD_HINTS(actual, expect) \ + QVERIFY(actual & Qt::ImhNoAutoUppercase); \ + QVERIFY(actual & Qt::ImhNoPredictiveText); \ + QVERIFY(actual & expect); +#else +#define VERIFY_INPUTMETHOD_HINTS(actual, expect) \ + QVERIFY(actual == expect); +#endif + +class tst_QWebView : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void renderingAfterMaxAndBack(); + void renderHints(); + void getWebKitVersion(); + + void reusePage_data(); + void reusePage(); + void microFocusCoordinates(); + void focusInputTypes(); + + void crashTests(); + + void setPalette_data(); + void setPalette(); +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_QWebView::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_QWebView::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_QWebView::init() +{ +} + +// This will be called after every test function. +void tst_QWebView::cleanup() +{ +} + +void tst_QWebView::renderHints() +{ + QWebView webView; + + // default is only text antialiasing + smooth pixmap transform + QVERIFY(!(webView.renderHints() & QPainter::Antialiasing)); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::Antialiasing, true); + QVERIFY(webView.renderHints() & QPainter::Antialiasing); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::Antialiasing, false); + QVERIFY(!(webView.renderHints() & QPainter::Antialiasing)); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::SmoothPixmapTransform, true); + QVERIFY(!(webView.renderHints() & QPainter::Antialiasing)); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(webView.renderHints() & QPainter::SmoothPixmapTransform); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); + + webView.setRenderHint(QPainter::SmoothPixmapTransform, false); + QVERIFY(webView.renderHints() & QPainter::TextAntialiasing); + QVERIFY(!(webView.renderHints() & QPainter::SmoothPixmapTransform)); + QVERIFY(!(webView.renderHints() & QPainter::HighQualityAntialiasing)); +} + +void tst_QWebView::getWebKitVersion() +{ + QVERIFY(qWebKitVersion().toDouble() > 0); +} + +void tst_QWebView::reusePage_data() +{ + QTest::addColumn<QString>("html"); + QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>"; + QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>"); + QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode=\"transparent\"></embed></body></html>"); +} + +void tst_QWebView::reusePage() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QDir::setCurrent(TESTS_SOURCE_DIR); + + QFETCH(QString, html); + QWebView* view1 = new QWebView; + QPointer<QWebPage> page = new QWebPage; + view1->setPage(page); + page->settings()->setAttribute(QWebSettings::PluginsEnabled, true); + QWebFrame* mainFrame = page->mainFrame(); + mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR)); + if (html.contains("</embed>")) { + // some reasonable time for the PluginStream to feed test.swf to flash and start painting + waitForSignal(view1, SIGNAL(loadFinished(bool)), 2000); + } + + view1->show(); + QTest::qWaitForWindowShown(view1); + delete view1; + QVERIFY(page != 0); // deleting view must not have deleted the page, since it's not a child of view + + QWebView *view2 = new QWebView; + view2->setPage(page); + view2->show(); // in Windowless mode, you should still be able to see the plugin here + QTest::qWaitForWindowShown(view2); + delete view2; + + delete page; // must not crash + + QDir::setCurrent(QApplication::applicationDirPath()); +} + +// Class used in crashTests +class WebViewCrashTest : public QObject { + Q_OBJECT + QWebView* m_view; +public: + bool m_executed; + + + WebViewCrashTest(QWebView* view) + : m_view(view) + , m_executed(false) + { + view->connect(view, SIGNAL(loadProgress(int)), this, SLOT(loading(int))); + } + +private slots: + void loading(int progress) + { + if (progress >= 20 && progress < 90) { + QVERIFY(!m_executed); + m_view->stop(); + m_executed = true; + } + } +}; + + +// Should not crash. +void tst_QWebView::crashTests() +{ + // Test if loading can be stopped in loadProgress handler without crash. + // Test page should have frames. + QWebView view; + WebViewCrashTest tester(&view); + QUrl url("qrc:///resources/index.html"); + view.load(url); + QTRY_VERIFY(tester.m_executed); // If fail it means that the test wasn't executed. +} + +void tst_QWebView::microFocusCoordinates() +{ + QWebPage* page = new QWebPage; + QWebView* webView = new QWebView; + webView->setPage( page ); + + page->mainFrame()->setHtml("<html><body>" \ + "<input type='text' id='input1' style='font--family: serif' value='' maxlength='20'/><br>" \ + "<canvas id='canvas1' width='500' height='500'></canvas>" \ + "<input type='password'/><br>" \ + "<canvas id='canvas2' width='500' height='500'></canvas>" \ + "</body></html>"); + + page->mainFrame()->setFocus(); + + QVariant initialMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus); + QVERIFY(initialMicroFocus.isValid()); + + page->mainFrame()->scroll(0,50); + + QVariant currentMicroFocus = page->inputMethodQuery(Qt::ImMicroFocus); + QVERIFY(currentMicroFocus.isValid()); + + QCOMPARE(initialMicroFocus.toRect().translated(QPoint(0,-50)), currentMicroFocus.toRect()); +} + +void tst_QWebView::focusInputTypes() +{ + QWebView webView; + webView.show(); + QTest::qWaitForWindowShown(&webView); + + QUrl url("qrc:///resources/input_types.html"); + QWebFrame* const mainFrame = webView.page()->mainFrame(); + mainFrame->load(url); + mainFrame->setFocus(); + + QVERIFY(waitForSignal(&webView, SIGNAL(loadFinished(bool)))); + + // 'text' type + QWebElement inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=text]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) || defined(Q_OS_SYMBIAN) + QVERIFY(webView.inputMethodHints() & Qt::ImhNoAutoUppercase); + QVERIFY(webView.inputMethodHints() & Qt::ImhNoPredictiveText); +#else + QVERIFY(webView.inputMethodHints() == Qt::ImhNone); +#endif + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'password' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); + VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText); + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'tel' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=tel]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); + VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhDialableCharactersOnly); + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'number' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=number]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); + VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhDigitsOnly); + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'email' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=email]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); + VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhEmailCharactersOnly); + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'url' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=url]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); + VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhUrlCharactersOnly); + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'password' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); + VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText); + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'text' type + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=text]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) || defined(Q_OS_SYMBIAN) + QVERIFY(webView.inputMethodHints() & Qt::ImhNoAutoUppercase); + QVERIFY(webView.inputMethodHints() & Qt::ImhNoPredictiveText); +#else + QVERIFY(webView.inputMethodHints() == Qt::ImhNone); +#endif + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'password' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("input[type=password]")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); + VERIFY_INPUTMETHOD_HINTS(webView.inputMethodHints(), Qt::ImhHiddenText); + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); + + // 'text area' field + inputElement = mainFrame->documentElement().findFirst(QLatin1String("textarea")); + QTest::mouseClick(&webView, Qt::LeftButton, 0, inputElement.geometry().center()); +#if defined(Q_OS_SYMBIAN) + QVERIFY(webView.inputMethodHints() & Qt::ImhNoAutoUppercase); + QVERIFY(webView.inputMethodHints() & Qt::ImhNoPredictiveText); +#else + QVERIFY(webView.inputMethodHints() == Qt::ImhNone); +#endif + QVERIFY(webView.testAttribute(Qt::WA_InputMethodEnabled)); +} + +void tst_QWebView::setPalette_data() +{ + QTest::addColumn<bool>("active"); + QTest::addColumn<bool>("background"); + QTest::newRow("activeBG") << true << true; + QTest::newRow("activeFG") << true << false; + QTest::newRow("inactiveBG") << false << true; + QTest::newRow("inactiveFG") << false << false; +} + +// Render a QWebView to a QImage twice, each time with a different palette set, +// verify that images rendered are not the same, confirming WebCore usage of +// custom palette on selections. +void tst_QWebView::setPalette() +{ + QString html = "<html><head></head>" + "<body>" + "Some text here" + "</body>" + "</html>"; + + QFETCH(bool, active); + QFETCH(bool, background); + + QWidget* activeView = 0; + + // Use controlView to manage active/inactive state of test views by raising + // or lowering their position in the window stack. + QWebView controlView; + controlView.setHtml(html); + + QWebView view1; + + QPalette palette1; + QBrush brush1(Qt::red); + brush1.setStyle(Qt::SolidPattern); + if (active && background) { + // Rendered image must have red background on an active QWebView. + palette1.setBrush(QPalette::Active, QPalette::Highlight, brush1); + } else if (active && !background) { + // Rendered image must have red foreground on an active QWebView. + palette1.setBrush(QPalette::Active, QPalette::HighlightedText, brush1); + } else if (!active && background) { + // Rendered image must have red background on an inactive QWebView. + palette1.setBrush(QPalette::Inactive, QPalette::Highlight, brush1); + } else if (!active && !background) { + // Rendered image must have red foreground on an inactive QWebView. + palette1.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush1); + } + + view1.setPalette(palette1); + view1.setHtml(html); + view1.page()->setViewportSize(view1.page()->currentFrame()->contentsSize()); + view1.show(); + + QTest::qWaitForWindowShown(&view1); + + if (!active) { + controlView.show(); + QTest::qWaitForWindowShown(&controlView); + activeView = &controlView; + controlView.activateWindow(); + } else { + view1.activateWindow(); + activeView = &view1; + } + + QTRY_COMPARE(QApplication::activeWindow(), activeView); + + view1.page()->triggerAction(QWebPage::SelectAll); + + QImage img1(view1.page()->viewportSize(), QImage::Format_ARGB32); + QPainter painter1(&img1); + view1.page()->currentFrame()->render(&painter1); + painter1.end(); + view1.close(); + controlView.close(); + + QWebView view2; + + QPalette palette2; + QBrush brush2(Qt::blue); + brush2.setStyle(Qt::SolidPattern); + if (active && background) { + // Rendered image must have blue background on an active QWebView. + palette2.setBrush(QPalette::Active, QPalette::Highlight, brush2); + } else if (active && !background) { + // Rendered image must have blue foreground on an active QWebView. + palette2.setBrush(QPalette::Active, QPalette::HighlightedText, brush2); + } else if (!active && background) { + // Rendered image must have blue background on an inactive QWebView. + palette2.setBrush(QPalette::Inactive, QPalette::Highlight, brush2); + } else if (!active && !background) { + // Rendered image must have blue foreground on an inactive QWebView. + palette2.setBrush(QPalette::Inactive, QPalette::HighlightedText, brush2); + } + + view2.setPalette(palette2); + view2.setHtml(html); + view2.page()->setViewportSize(view2.page()->currentFrame()->contentsSize()); + view2.show(); + + QTest::qWaitForWindowShown(&view2); + + if (!active) { + controlView.show(); + QTest::qWaitForWindowShown(&controlView); + activeView = &controlView; + controlView.activateWindow(); + } else { + view2.activateWindow(); + activeView = &view2; + } + + QTRY_COMPARE(QApplication::activeWindow(), activeView); + + view2.page()->triggerAction(QWebPage::SelectAll); + + QImage img2(view2.page()->viewportSize(), QImage::Format_ARGB32); + QPainter painter2(&img2); + view2.page()->currentFrame()->render(&painter2); + painter2.end(); + + view2.close(); + controlView.close(); + + QVERIFY(img1 != img2); +} + +void tst_QWebView::renderingAfterMaxAndBack() +{ + QUrl url = QUrl("data:text/html,<html><head></head>" + "<body width=1024 height=768 bgcolor=red>" + "</body>" + "</html>"); + + QWebView view; + view.page()->mainFrame()->load(url); + QVERIFY(waitForSignal(&view, SIGNAL(loadFinished(bool)))); + view.show(); + + view.page()->settings()->setMaximumPagesInCache(3); + + QTest::qWaitForWindowShown(&view); + + QPixmap reference(view.page()->viewportSize()); + reference.fill(Qt::red); + + QPixmap image(view.page()->viewportSize()); + QPainter painter(&image); + view.page()->currentFrame()->render(&painter); + + QCOMPARE(image, reference); + + QUrl url2 = QUrl("data:text/html,<html><head></head>" + "<body width=1024 height=768 bgcolor=blue>" + "</body>" + "</html>"); + view.page()->mainFrame()->load(url2); + + QVERIFY(waitForSignal(&view, SIGNAL(loadFinished(bool)))); + + view.showMaximized(); + + QTest::qWaitForWindowShown(&view); + + QPixmap reference2(view.page()->viewportSize()); + reference2.fill(Qt::blue); + + QPixmap image2(view.page()->viewportSize()); + QPainter painter2(&image2); + view.page()->currentFrame()->render(&painter2); + + QCOMPARE(image2, reference2); + + view.back(); + + QPixmap reference3(view.page()->viewportSize()); + reference3.fill(Qt::red); + QPixmap image3(view.page()->viewportSize()); + QPainter painter3(&image3); + view.page()->currentFrame()->render(&painter3); + + QCOMPARE(image3, reference3); +} + +QTEST_MAIN(tst_QWebView) +#include "tst_qwebview.moc" + diff --git a/Source/WebKit/qt/tests/qwebview/tst_qwebview.qrc b/Source/WebKit/qt/tests/qwebview/tst_qwebview.qrc new file mode 100644 index 0000000..8710a9a --- /dev/null +++ b/Source/WebKit/qt/tests/qwebview/tst_qwebview.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>resources/index.html</file> + <file>resources/frame_a.html</file> + <file>resources/input_types.html</file> +</qresource> +</RCC> + diff --git a/Source/WebKit/qt/tests/resources/image2.png b/Source/WebKit/qt/tests/resources/image2.png Binary files differnew file mode 100644 index 0000000..8d70364 --- /dev/null +++ b/Source/WebKit/qt/tests/resources/image2.png diff --git a/Source/WebKit/qt/tests/resources/test.swf b/Source/WebKit/qt/tests/resources/test.swf Binary files differnew file mode 100644 index 0000000..8952982 --- /dev/null +++ b/Source/WebKit/qt/tests/resources/test.swf diff --git a/Source/WebKit/qt/tests/tests.pri b/Source/WebKit/qt/tests/tests.pri new file mode 100644 index 0000000..3b0c358 --- /dev/null +++ b/Source/WebKit/qt/tests/tests.pri @@ -0,0 +1,36 @@ +TEMPLATE = app +CONFIG -= app_bundle + +VPATH += $$_PRO_FILE_PWD_ +# Add the tst_ prefix, In QTDIR_build it's done by qttest_p4.prf +CONFIG(QTDIR_build) { load(qttest_p4) } +ELSE { TARGET = tst_$$TARGET } + +# Load mobilityconfig if Qt Mobility is available +load(mobilityconfig, true) +contains(MOBILITY_CONFIG, multimedia) { + # This define is used by tests depending on Qt Multimedia + DEFINES -= WTF_USE_QT_MULTIMEDIA=0 + DEFINES += WTF_USE_QT_MULTIMEDIA=1 +} + +SOURCES += $${TARGET}.cpp +INCLUDEPATH += \ + $$PWD \ + $$PWD/../Api + +include(../../../WebKit.pri) +QT += testlib network +contains(QT_CONFIG, declarative): QT += declarative + +QMAKE_RPATHDIR = $$OUTPUT_DIR/lib $$QMAKE_RPATHDIR + +symbian { + TARGET.CAPABILITY = ReadUserData WriteUserData NetworkServices +} + +# This define is used by some tests to look up resources in the source tree +!symbian: DEFINES += TESTS_SOURCE_DIR=\\\"$$PWD/\\\" + +DEFINES -= QT_ASCII_CAST_WARNINGS + diff --git a/Source/WebKit/qt/tests/tests.pro b/Source/WebKit/qt/tests/tests.pro new file mode 100644 index 0000000..5fffd7b --- /dev/null +++ b/Source/WebKit/qt/tests/tests.pro @@ -0,0 +1,8 @@ + +TEMPLATE = subdirs +SUBDIRS = qwebframe qwebpage qwebelement qgraphicswebview qwebhistoryinterface qwebview qwebhistory qwebinspector hybridPixmap MIMESniffing +contains(QT_CONFIG, declarative): SUBDIRS += qdeclarativewebview +SUBDIRS += benchmarks/painting benchmarks/loading +contains(DEFINES, ENABLE_WEBGL=1) { + SUBDIRS += benchmarks/webgl +} diff --git a/Source/WebKit/qt/tests/util.h b/Source/WebKit/qt/tests/util.h new file mode 100644 index 0000000..c61bc6b --- /dev/null +++ b/Source/WebKit/qt/tests/util.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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. +*/ +// Functions and macros that really need to be in QTestLib + +#include <QEventLoop> +#include <QSignalSpy> +#include <QTimer> + +#if !defined(TESTS_SOURCE_DIR) +#define TESTS_SOURCE_DIR "" +#endif + +/** + * Starts an event loop that runs until the given signal is received. + * Optionally the event loop + * can return earlier on a timeout. + * + * \return \p true if the requested signal was received + * \p false on timeout + */ +static bool waitForSignal(QObject* obj, const char* signal, int timeout = 10000) +{ + QEventLoop loop; + QObject::connect(obj, signal, &loop, SLOT(quit())); + QTimer timer; + QSignalSpy timeoutSpy(&timer, SIGNAL(timeout())); + if (timeout > 0) { + QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.setSingleShot(true); + timer.start(timeout); + } + loop.exec(); + return timeoutSpy.isEmpty(); +} + +// Will try to wait for the condition while allowing event processing +#define QTRY_VERIFY(__expr) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if (!(__expr)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QVERIFY(__expr); \ + } while(0) + +// Will try to wait for the condition while allowing event processing +#define QTRY_COMPARE(__expr, __expected) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if ((__expr) != (__expected)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QCOMPARE(__expr, __expected); \ + } while(0) |
