blob: ef210e07e42b782f1fa8ca8d097a3c31e5ce50e9 [file] [log] [blame]
/*
* Copyright (C) 2011 ProFUSION Embedded Systems
* Copyright (C) 2011 Samsung Electronics
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "DumpRenderTree.h"
#include "DumpHistoryItem.h"
#include "DumpRenderTreeChrome.h"
#include "DumpRenderTreeView.h"
#include "EventSender.h"
#include "FontManagement.h"
#include "NotImplemented.h"
#include "PixelDumpSupport.h"
#include "TestRunner.h"
#include "WebCoreSupport/DumpRenderTreeSupportEfl.h"
#include "WebCoreTestSupport.h"
#include "WorkQueue.h"
#include "ewk_private.h"
#include <EWebKit.h>
#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Ecore_File.h>
#include <Edje.h>
#include <Evas.h>
#include <fontconfig/fontconfig.h>
#include <getopt.h>
#include <stdlib.h>
#include <unistd.h>
#include <wtf/Assertions.h>
#include <wtf/OwnPtr.h>
#include <wtf/text/CString.h>
#include <wtf/text/StringBuilder.h>
OwnPtr<DumpRenderTreeChrome> browser;
Evas_Object* topLoadingFrame = 0;
bool waitForPolicy = false;
bool policyDelegateEnabled = false;
bool policyDelegatePermissive = false;
Ecore_Timer* waitToDumpWatchdog = 0;
extern Ewk_History_Item* prevTestBFItem;
// From the top-level DumpRenderTree.h
RefPtr<TestRunner> gTestRunner;
volatile bool done = false;
static bool dumpPixelsForCurrentTest;
static int dumpPixelsForAllTests = false;
static int dumpTree = true;
static int printSeparators = true;
static String dumpFramesAsText(Evas_Object* frame)
{
String result;
if (browser->mainFrame() != frame) {
result.append("\n--------\nFrame: '");
result.append(String::fromUTF8(ewk_frame_name_get(frame)));
result.append("'\n--------\n");
}
const char* frameContents = ewk_frame_plain_text_get(frame);
result.append(String::fromUTF8(frameContents));
result.append("\n");
eina_stringshare_del(frameContents);
if (gTestRunner->dumpChildFramesAsText()) {
Eina_List* children = DumpRenderTreeSupportEfl::frameChildren(frame);
void* iterator;
EINA_LIST_FREE(children, iterator) {
Evas_Object* currentFrame = static_cast<Evas_Object*>(iterator);
String tempText(dumpFramesAsText(currentFrame));
if (tempText.isEmpty())
continue;
result.append(tempText);
}
}
return result;
}
static void dumpFrameScrollPosition(Evas_Object* frame)
{
int x, y;
ewk_frame_scroll_pos_get(frame, &x, &y);
if (abs(x) > 0 || abs(y) > 0) {
StringBuilder result;
Evas_Object* parent = evas_object_smart_parent_get(frame);
// smart parent of main frame is view object.
if (parent != browser->mainView()) {
result.append("frame '");
result.append(ewk_frame_name_get(frame));
result.append("' ");
}
result.append("scrolled to ");
result.append(WTF::String::number(x));
result.append(",");
result.append(WTF::String::number(y));
result.append("\n");
printf("%s", result.toString().utf8().data());
}
if (gTestRunner->dumpChildFrameScrollPositions()) {
Eina_List* children = DumpRenderTreeSupportEfl::frameChildren(frame);
void* iterator;
EINA_LIST_FREE(children, iterator) {
Evas_Object* currentFrame = static_cast<Evas_Object*>(iterator);
dumpFrameScrollPosition(currentFrame);
}
}
}
static bool shouldLogFrameLoadDelegates(const String& pathOrURL)
{
return pathOrURL.contains("loading/");
}
static bool shouldDumpAsText(const String& pathOrURL)
{
return pathOrURL.contains("dumpAsText/");
}
static bool shouldOpenWebInspector(const String& pathOrURL)
{
return pathOrURL.contains("inspector/");
}
static void sendPixelResultsEOF()
{
puts("#EOF");
fflush(stdout);
fflush(stderr);
}
static void invalidateAnyPreviousWaitToDumpWatchdog()
{
if (waitToDumpWatchdog) {
ecore_timer_del(waitToDumpWatchdog);
waitToDumpWatchdog = 0;
}
waitForPolicy = false;
}
static void onEcoreEvasResize(Ecore_Evas* ecoreEvas)
{
int width, height;
ecore_evas_geometry_get(ecoreEvas, 0, 0, &width, &height);
evas_object_move(browser->mainView(), 0, 0);
evas_object_resize(browser->mainView(), width, height);
}
static void onCloseWindow(Ecore_Evas*)
{
notImplemented();
}
static Eina_Bool useLongRunningServerMode(int argc, char** argv)
{
return (argc == optind + 1 && !strcmp(argv[optind], "-"));
}
static bool parseCommandLineOptions(int argc, char** argv)
{
static const option options[] = {
{"notree", no_argument, &dumpTree, false},
{"pixel-tests", no_argument, &dumpPixelsForAllTests, true},
{"tree", no_argument, &dumpTree, true},
{0, 0, 0, 0}
};
int option;
while ((option = getopt_long(argc, (char* const*)argv, "", options, 0)) != -1) {
switch (option) {
case '?':
case ':':
return false;
}
}
return true;
}
static inline bool isGlobalHistoryTest(const String& cTestPathOrURL)
{
return cTestPathOrURL.contains("/globalhistory/");
}
static void createTestRunner(const String& testURL, const String& expectedPixelHash)
{
gTestRunner =
TestRunner::create(std::string(testURL.utf8().data()),
std::string(expectedPixelHash.utf8().data()));
topLoadingFrame = 0;
done = false;
gTestRunner->setIconDatabaseEnabled(false);
if (shouldLogFrameLoadDelegates(testURL))
gTestRunner->setDumpFrameLoadCallbacks(true);
gTestRunner->setDeveloperExtrasEnabled(true);
if (shouldOpenWebInspector(testURL))
gTestRunner->showWebInspector();
gTestRunner->setDumpHistoryDelegateCallbacks(isGlobalHistoryTest(testURL));
if (shouldDumpAsText(testURL)) {
gTestRunner->setDumpAsText(true);
gTestRunner->setGeneratePixelResults(false);
}
}
static String getFinalTestURL(const String& testURL)
{
if (!testURL.startsWith("http://") && !testURL.startsWith("https://")) {
char* cFilePath = ecore_file_realpath(testURL.utf8().data());
const String filePath = String::fromUTF8(cFilePath);
free(cFilePath);
if (ecore_file_exists(filePath.utf8().data()))
return String("file://") + filePath;
}
return testURL;
}
static void runTest(const char* inputLine)
{
TestCommand command = parseInputLine(inputLine);
const String testPathOrURL(command.pathOrURL.c_str());
ASSERT(!testPathOrURL.isEmpty());
dumpPixelsForCurrentTest = command.shouldDumpPixels || dumpPixelsForAllTests;
const String expectedPixelHash(command.expectedPixelHash.c_str());
// Convert the path into a full file URL if it does not look
// like an HTTP/S URL (doesn't start with http:// or https://).
const String testURL = getFinalTestURL(testPathOrURL);
browser->resetDefaultsToConsistentValues();
createTestRunner(testURL, expectedPixelHash);
WorkQueue::shared()->clear();
WorkQueue::shared()->setFrozen(false);
const bool isSVGW3CTest = testURL.contains("svg/W3C-SVG-1.1");
const int width = isSVGW3CTest ? 480 : TestRunner::maxViewWidth;
const int height = isSVGW3CTest ? 360 : TestRunner::maxViewHeight;
evas_object_resize(browser->mainView(), width, height);
if (prevTestBFItem)
ewk_history_item_free(prevTestBFItem);
const Ewk_History* history = ewk_view_history_get(browser->mainView());
prevTestBFItem = ewk_history_history_item_current_get(history);
evas_object_focus_set(browser->mainView(), EINA_TRUE);
ewk_view_uri_set(browser->mainView(), testURL.utf8().data());
ecore_main_loop_begin();
gTestRunner->closeWebInspector();
gTestRunner->setDeveloperExtrasEnabled(false);
browser->clearExtraViews();
// FIXME: Move to DRTChrome::resetDefaultsToConsistentValues() after bug 85209 lands.
WebCoreTestSupport::resetInternalsObject(DumpRenderTreeSupportEfl::globalContextRefForFrame(browser->mainFrame()));
ewk_view_uri_set(browser->mainView(), "about:blank");
gTestRunner.clear();
sendPixelResultsEOF();
}
static void runTestingServerLoop()
{
char filename[PATH_MAX];
while (fgets(filename, sizeof(filename), stdin)) {
char* newLine = strrchr(filename, '\n');
if (newLine)
*newLine = '\0';
if (filename[0] != '\0')
runTest(filename);
}
}
static void adjustOutputTypeByMimeType(const Evas_Object* frame)
{
const String responseMimeType(DumpRenderTreeSupportEfl::responseMimeType(frame));
if (responseMimeType == "text/plain") {
gTestRunner->setDumpAsText(true);
gTestRunner->setGeneratePixelResults(false);
}
}
static void dumpFrameContentsAsText(Evas_Object* frame)
{
String result;
if (gTestRunner->dumpAsText())
result = dumpFramesAsText(frame);
else
result = DumpRenderTreeSupportEfl::renderTreeDump(frame);
printf("%s", result.utf8().data());
}
static bool shouldDumpFrameScrollPosition()
{
return !gTestRunner->dumpAsText() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive();
}
static bool shouldDumpPixelsAndCompareWithExpected()
{
return dumpPixelsForCurrentTest && gTestRunner->generatePixelResults() && !gTestRunner->dumpDOMAsWebArchive() && !gTestRunner->dumpSourceAsWebArchive();
}
static bool shouldDumpBackForwardList()
{
return gTestRunner->dumpBackForwardList();
}
static bool initEfl()
{
if (!ecore_evas_init())
return false;
if (!ecore_file_init()) {
ecore_evas_shutdown();
return false;
}
if (!edje_init()) {
ecore_file_shutdown();
ecore_evas_shutdown();
return false;
}
if (!ewk_init()) {
edje_shutdown();
ecore_file_shutdown();
ecore_evas_shutdown();
return false;
}
return true;
}
static void shutdownEfl()
{
ewk_shutdown();
edje_shutdown();
ecore_file_shutdown();
ecore_evas_shutdown();
}
void displayWebView()
{
DumpRenderTreeSupportEfl::forceLayout(browser->mainFrame());
DumpRenderTreeSupportEfl::setTracksRepaints(browser->mainFrame(), true);
DumpRenderTreeSupportEfl::resetTrackedRepaints(browser->mainFrame());
}
void dump()
{
Evas_Object* frame = browser->mainFrame();
invalidateAnyPreviousWaitToDumpWatchdog();
if (dumpTree) {
adjustOutputTypeByMimeType(frame);
dumpFrameContentsAsText(frame);
if (shouldDumpFrameScrollPosition())
dumpFrameScrollPosition(frame);
if (shouldDumpBackForwardList())
dumpBackForwardListForWebViews();
if (printSeparators) {
puts("#EOF");
fputs("#EOF\n", stderr);
fflush(stdout);
fflush(stderr);
}
}
if (shouldDumpPixelsAndCompareWithExpected())
dumpWebViewAsPixelsAndCompareWithExpected(gTestRunner->expectedPixelHash());
done = true;
ecore_main_loop_quit();
}
static Ecore_Evas* initEcoreEvas()
{
const char* engine = 0;
#if defined(WTF_USE_ACCELERATED_COMPOSITING) && defined(HAVE_ECORE_X)
engine = "opengl_x11";
#endif
Ecore_Evas* ecoreEvas = ecore_evas_new(engine, 0, 0, 800, 600, 0);
if (!ecoreEvas) {
shutdownEfl();
exit(EXIT_FAILURE);
}
ecore_evas_title_set(ecoreEvas, "EFL DumpRenderTree");
ecore_evas_callback_resize_set(ecoreEvas, onEcoreEvasResize);
ecore_evas_callback_delete_request_set(ecoreEvas, onCloseWindow);
ecore_evas_show(ecoreEvas);
return ecoreEvas;
}
int main(int argc, char** argv)
{
if (!parseCommandLineOptions(argc, argv))
return EXIT_FAILURE;
if (!initEfl())
return EXIT_FAILURE;
WTFInstallReportBacktraceOnCrashHook();
OwnPtr<Ecore_Evas> ecoreEvas = adoptPtr(initEcoreEvas());
browser = DumpRenderTreeChrome::create(ecore_evas_get(ecoreEvas.get()));
addFontsToEnvironment();
if (useLongRunningServerMode(argc, argv)) {
printSeparators = true;
runTestingServerLoop();
} else {
printSeparators = (optind < argc - 1 || (dumpPixelsForCurrentTest && dumpTree));
for (int i = optind; i != argc; ++i)
runTest(argv[i]);
}
ecoreEvas.clear();
shutdownEfl();
return EXIT_SUCCESS;
}