[BlackBerry] Upstream DumpRenderTreeBlackBerry
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Feb 2012 18:35:50 +0000 (18:35 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Feb 2012 18:35:50 +0000 (18:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=78042

Patch by Rob Buis <rbuis@rim.com> on 2012-02-08
Reviewed by Antonio Gomes.

Source/WebKit:

Add abstract interface for our DumpRenderTree solution.

* blackberry/Api/DumpRenderTreeClient.h: Added.

Tools:

Add implementation for our DumpRenderTree solution.

* DumpRenderTree/blackberry/DumpRenderTree.cpp: Added.
* DumpRenderTree/blackberry/DumpRenderTreeBlackBerry.h: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@107105 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit/ChangeLog
Source/WebKit/blackberry/Api/DumpRenderTreeClient.h [new file with mode: 0644]
Tools/ChangeLog
Tools/DumpRenderTree/blackberry/DumpRenderTree.cpp [new file with mode: 0644]
Tools/DumpRenderTree/blackberry/DumpRenderTreeBlackBerry.h [new file with mode: 0644]

index 56eedad..9c3a66d 100644 (file)
@@ -1,3 +1,14 @@
+2012-02-08  Rob Buis  <rbuis@rim.com>
+
+        [BlackBerry] Upstream DumpRenderTreeBlackBerry
+        https://bugs.webkit.org/show_bug.cgi?id=78042
+
+        Reviewed by Antonio Gomes.
+
+        Add abstract interface for our DumpRenderTree solution.
+
+        * blackberry/Api/DumpRenderTreeClient.h: Added.
+
 2012-02-08  Nima Ghanavatian  <nghanavatian@rim.com>
 
         Initial upstreaming of input handling for BlackBerry port
diff --git a/Source/WebKit/blackberry/Api/DumpRenderTreeClient.h b/Source/WebKit/blackberry/Api/DumpRenderTreeClient.h
new file mode 100644 (file)
index 0000000..03eb5bb
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009, 2010, 2011 Research In Motion Limited. All rights reserved.
+ *
+ * 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 DumpRenderTreeClient_h
+#define DumpRenderTreeClient_h
+
+#include "BlackBerryGlobal.h"
+
+#include "PlatformString.h"
+#include <JavaScriptCore/JSObjectRef.h>
+
+namespace WebCore {
+class Frame;
+class DOMWrapperWorld;
+class NavigationAction;
+class Node;
+class Range;
+class ResourceRequest;
+class ResourceResponse;
+class SecurityOrigin;
+}
+
+namespace BlackBerry {
+namespace WebKit {
+class WebPage;
+
+class BLACKBERRY_EXPORT DumpRenderTreeClient {
+public:
+    virtual void runTests() = 0;
+
+    // FrameLoaderClient delegates
+    virtual void didStartProvisionalLoadForFrame(WebCore::Frame*) = 0;
+    virtual void didReceiveResponseForFrame(WebCore::Frame*, const WebCore::ResourceResponse&) = 0;
+    virtual void didCommitLoadForFrame(WebCore::Frame*) = 0;
+    virtual void didFailProvisionalLoadForFrame(WebCore::Frame*) = 0;
+    virtual void didFailLoadForFrame(WebCore::Frame*) = 0;
+    virtual void didFinishLoadForFrame(WebCore::Frame*) = 0;
+    virtual void didFinishDocumentLoadForFrame(WebCore::Frame*) = 0;
+    virtual void didClearWindowObjectInWorld(WebCore::DOMWrapperWorld*, JSGlobalContextRef, JSObjectRef windowObject) = 0;
+    virtual void didReceiveTitleForFrame(const WTF::String& title, WebCore::Frame*) = 0;
+    virtual void didDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&) = 0;
+    virtual void didDispatchWillPerformClientRedirect() = 0;
+    virtual void didHandleOnloadEventsForFrame(WebCore::Frame*) = 0;
+
+    // ChromeClient delegates
+    virtual void addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID) = 0;
+    virtual void runJavaScriptAlert(const WTF::String& message) = 0;
+    virtual bool runJavaScriptConfirm(const WTF::String& message) = 0;
+    virtual WTF::String runJavaScriptPrompt(const WTF::String& message, const WTF::String& defaultValue) = 0;
+    virtual bool runBeforeUnloadConfirmPanel(const WTF::String& message) = 0;
+    virtual void setStatusText(const WTF::String&) = 0;
+    virtual void exceededDatabaseQuota(WebCore::SecurityOrigin*, const WTF::String& name) = 0;
+    virtual bool allowsOpeningWindow() = 0;
+    virtual void windowCreated(WebPage*) = 0;
+
+    // EditorClient delegates
+    virtual void setAcceptsEditing(bool) = 0;
+    virtual void didBeginEditing() = 0;
+    virtual void didEndEditing() = 0;
+    virtual void didChange() = 0;
+    virtual void didChangeSelection() = 0;
+    virtual bool shouldBeginEditingInDOMRange(WebCore::Range*) = 0;
+    virtual bool shouldEndEditingInDOMRange(WebCore::Range*) = 0;
+    virtual bool shouldDeleteDOMRange(WebCore::Range*) = 0;
+    virtual bool shouldChangeSelectedDOMRangeToDOMRangeAffinityStillSelecting(WebCore::Range* fromRange, WebCore::Range* toRange, int affinity, bool stillSelecting) = 0;
+    virtual bool shouldInsertNode(WebCore::Node*, WebCore::Range*, int insertAction) = 0;
+    virtual bool shouldInsertText(const WTF::String&, WebCore::Range*, int insertAction) = 0;
+    virtual bool isSelectTrailingWhitespaceEnabled() const = 0;
+
+};
+}
+}
+
+#endif // DumpRenderTreeClient_h
index b413b5a..7453672 100644 (file)
@@ -1,3 +1,15 @@
+2012-02-08  Rob Buis  <rbuis@rim.com>
+
+        [BlackBerry] Upstream DumpRenderTreeBlackBerry
+        https://bugs.webkit.org/show_bug.cgi?id=78042
+
+        Reviewed by Antonio Gomes.
+
+        Add implementation for our DumpRenderTree solution.
+
+        * DumpRenderTree/blackberry/DumpRenderTree.cpp: Added.
+        * DumpRenderTree/blackberry/DumpRenderTreeBlackBerry.h: Added.
+
 2012-02-08  Antti Koivisto  <antti@apple.com>
 
         REGRESSION (r106681): Null check missing in [WebFrame(WebInternal) _typingStyle]
diff --git a/Tools/DumpRenderTree/blackberry/DumpRenderTree.cpp b/Tools/DumpRenderTree/blackberry/DumpRenderTree.cpp
new file mode 100644 (file)
index 0000000..fb52cfd
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * 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 "config.h"
+#include "DumpRenderTree.h"
+
+#include "AccessibilityController.h"
+#include "BackForwardController.h"
+#include "BackForwardListImpl.h"
+#include "CString.h"
+#include "DatabaseTracker.h"
+#include "DocumentLoader.h"
+#include "DumpRenderTree/GCController.h"
+#include "DumpRenderTreeSupport.h"
+#include "EditingBehaviorTypes.h"
+#include "EditorClientBlackBerry.h"
+#include "EditorInsertAction.h"
+#include "Element.h"
+#include "EventSender.h"
+#include "Frame.h"
+#include "FrameLoaderTypes.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "HistoryItem.h"
+#include "IntSize.h"
+#include "LayoutTestController.h"
+#include "NotImplemented.h"
+#include "OwnArrayPtr.h"
+#include "Page.h"
+#include "PageGroup.h"
+#include "PixelDumpSupport.h"
+#include "PixelDumpSupportBlackBerry.h"
+#include "Range.h"
+#include "RenderTreeAsText.h"
+#include "ScriptController.h"
+#include "SecurityOrigin.h"
+#include "Settings.h"
+#include "TextAffinity.h"
+#include "Timer.h"
+#include "Vector.h"
+#include "WebCoreTestSupport.h"
+#include "WebPage.h"
+#include "WebPageClient.h"
+#include "WorkQueue.h"
+#include "WorkQueueItem.h"
+#include <WebSettings.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <wtf/NonCopyingSort.h>
+
+#define SDCARD_PATH "/developer"
+
+volatile bool testDone;
+
+RefPtr<LayoutTestController> gLayoutTestController;
+
+WebCore::Frame* mainFrame = 0;
+WebCore::Frame* topLoadingFrame = 0;
+bool waitForPolicy = false;
+
+// FIXME: Assuming LayoutTests has been copied to /developer/LayoutTests/
+static const char* const kSDCLayoutTestsURI = "file:///developer/LayoutTests/";
+static const char* httpTestSyntax = "http/tests/";
+static const char* httpPrefixURL = "http://127.0.0.1:8000/";
+
+using namespace std;
+
+static WTF::String drtAffinityDescription(WebCore::EAffinity affinity)
+{
+    if (affinity == WebCore::UPSTREAM)
+        return WTF::String("NSSelectionAffinityUpstream");
+    if (affinity == WebCore::DOWNSTREAM)
+        return WTF::String("NSSelectionAffinityDownstream");
+    return "";
+}
+
+static WTF::String drtDumpPath(WebCore::Node* node)
+{
+    WebCore::Node* parent = node->parentNode();
+    WTF::String str = WTF::String::format("%s", node->nodeName().utf8().data());
+    if (parent) {
+        str.append(" > ");
+        str.append(drtDumpPath(parent));
+    }
+    return str;
+}
+
+static WTF::String drtRangeDescription(WebCore::Range* range)
+{
+    if (!range)
+        return "(null)";
+    return WTF::String::format("range from %d of %s to %d of %s", range->startOffset(), drtDumpPath(range->startContainer()).utf8().data(), range->endOffset(), drtDumpPath(range->endContainer()).utf8().data());
+}
+
+static WTF::String drtFrameDescription(WebCore::Frame* frame)
+{
+    WTF::String name = frame->tree()->uniqueName().string();
+    if (frame == mainFrame) {
+        if (!name.isNull() && name.length())
+            return WTF::String::format("main frame \"%s\"", name.utf8().data());
+        return "main frame";
+    }
+    if (!name.isNull())
+        return WTF::String::format("frame \"%s\"", name.utf8().data());
+    return "frame (anonymous)";
+}
+
+static bool shouldLogFrameLoadDelegates(const WTF::String& url)
+{
+    return url.contains("loading/");
+}
+
+namespace BlackBerry {
+namespace WebKit {
+
+DumpRenderTree* DumpRenderTree::s_currentInstance = 0;
+bool DumpRenderTree::s_selectTrailingWhitespaceEnabled = false;
+
+static void createFile(const WTF::String& fileName)
+{
+    FILE* fd = fopen(fileName.utf8().data(), "wb");
+    fclose(fd);
+}
+
+DumpRenderTree::DumpRenderTree(BlackBerry::WebKit::WebPage* page)
+    : m_gcController(0)
+    , m_accessibilityController(0)
+    , m_page(page)
+    , m_dumpPixels(false)
+    , m_waitToDumpWatchdogTimer(this, &DumpRenderTree::waitToDumpWatchdogTimerFired)
+    , m_workTimer(this, &DumpRenderTree::processWork)
+    , m_acceptsEditing(true)
+{
+    WTF::String sdcardPath = SDCARD_PATH;
+    m_resultsDir = sdcardPath + "/results/";
+    m_indexFile = sdcardPath + "/index.drt";
+    m_doneFile = sdcardPath + "/done";
+    m_currentTestFile = sdcardPath + "/current.drt";
+    m_page->resetVirtualViewportOnCommitted(false);
+    m_page->setVirtualViewportSize(800, 600);
+    s_currentInstance = this;
+}
+
+DumpRenderTree::~DumpRenderTree()
+{
+    delete m_gcController;
+    delete m_accessibilityController;
+}
+
+void DumpRenderTree::runTest(const WTF::String& url)
+{
+    createFile(m_resultsDir + *m_currentTest + ".dump.crash");
+
+    mainFrame->loader()->stopForUserCancel();
+    resetToConsistentStateBeforeTesting();
+    if (shouldLogFrameLoadDelegates(url))
+        gLayoutTestController->setDumpFrameLoadCallbacks(true);
+    WTF::String stdoutFile = m_resultsDir + *m_currentTest + ".dump";
+    WTF::String stderrFile = m_resultsDir + *m_currentTest + ".stderr";
+
+    // FIXME: we should preserve the original stdout and stderr here but aren't doing
+    // that yet due to issues with dup, etc.
+    freopen(stdoutFile.utf8().data(), "wb", stdout);
+    freopen(stderrFile.utf8().data(), "wb", stderr);
+
+    FILE* current = fopen(m_currentTestFile.utf8().data(), "w");
+    fwrite(m_currentTest->utf8().data(), 1, m_currentTest->utf8().length(), current);
+    fclose(current);
+    m_page->load(url.utf8().data(), 0, false);
+}
+
+void DumpRenderTree::doneDrt()
+{
+    fclose(stdout);
+    fclose(stderr);
+
+    // Notify the external world that we're done.
+    createFile(m_doneFile);
+    (m_page->client())->notifyRunLayoutTestsFinished();
+}
+
+void DumpRenderTree::runRemainingTests()
+{
+    // FIXME: fflush should not be necessary but is temporarily required due to a bug in stdio output.
+    fflush(stdout);
+    fflush(stderr);
+
+    if (m_currentTest >= m_tests.end() - 1) {
+        doneDrt();
+        return;
+    }
+
+    m_currentTest++;
+    if (isHTTPTest(m_currentTest->utf8().data())) {
+        m_currentHttpTest = m_currentTest->utf8().data();
+        m_currentHttpTest.remove(0, strlen(httpTestSyntax));
+        runTest(httpPrefixURL + m_currentHttpTest);
+    } else
+        runTest(kSDCLayoutTestsURI + *m_currentTest);
+}
+
+void DumpRenderTree::resetToConsistentStateBeforeTesting()
+{
+    if (isHTTPTest(m_currentTest->utf8().data()))
+        gLayoutTestController = LayoutTestController::create(String(httpPrefixURL + *m_currentTest).utf8().data(), "");
+    else
+        gLayoutTestController = LayoutTestController::create(String(kSDCLayoutTestsURI + *m_currentTest).utf8().data(), "");
+
+    gLayoutTestController->setIconDatabaseEnabled(false);
+
+    DumpRenderTreeSupport::resetGeolocationMock(m_page);
+
+    topLoadingFrame = 0;
+    m_loadFinished = false;
+    s_selectTrailingWhitespaceEnabled = false;
+
+    testDone = false;
+    WorkQueue::shared()->clear();
+    WorkQueue::shared()->setFrozen(false);
+
+    WebSettings* settings = m_page->settings();
+
+    settings->setTextReflowMode(WebSettings::TextReflowDisabled);
+    settings->setJavaScriptEnabled(true);
+    settings->setLoadsImagesAutomatically(true);
+    settings->setJavaScriptOpenWindowsAutomatically(true);
+    settings->setZoomToFitOnLoad(false);
+    settings->setDefaultFontSize(16);
+    settings->setDefaultFixedFontSize(13);
+    settings->setMinimumFontSize(1);
+    settings->setSerifFontFamily("Times");
+    settings->setFixedFontFamily("Courier New");
+    settings->setSansSerifFontFamily("Arial");
+    settings->setStandardFontFamily("Times");
+    settings->setXSSAuditorEnabled(false);
+    settings->setFrameFlatteningEnabled(false);
+    settings->setMaximumPagesInCache(0);
+    settings->setPluginsEnabled(true);
+    settings->setUserScalable(true);
+    // Apply new settings to current page, see more in the destructor of WebSettingsTransaction.
+    WebSettingsTransaction webSettingTransaction(settings);
+
+    BlackBerry::WebKit::DumpRenderTree::currentInstance()->page()->clearBackForwardList(false);
+
+    setAcceptsEditing(true);
+    DumpRenderTreeSupport::setLinksIncludedInFocusChain(true);
+
+    m_page->setVirtualViewportSize(800, 600);
+    m_page->resetVirtualViewportOnCommitted(false);
+    m_page->setJavaScriptCanAccessClipboard(true);
+
+    mainFrame = m_page->mainFrame();
+    if (mainFrame) {
+        mainFrame->page()->setTabKeyCyclesThroughElements(true);
+        mainFrame->page()->settings()->setEditingBehaviorType(WebCore::EditingUnixBehavior);
+        mainFrame->page()->settings()->setDOMPasteAllowed(true);
+        mainFrame->page()->settings()->setValidationMessageTimerMagnification(-1);
+        mainFrame->page()->settings()->setInteractiveFormValidationEnabled(true);
+        mainFrame->page()->settings()->setAllowFileAccessFromFileURLs(true);
+        mainFrame->page()->settings()->setAllowUniversalAccessFromFileURLs(true);
+        mainFrame->page()->settings()->setAuthorAndUserStylesEnabled(true);
+        mainFrame->page()->settings()->setUsePreHTML5ParserQuirks(false);
+        mainFrame->tree()->clearName();
+        mainFrame->loader()->setOpener(0);
+        // FIXME: Other ports also clear history/backForwardList allong with visited links.
+        mainFrame->page()->group().removeVisitedLinks();
+    }
+
+    // For now we manually garbage collect between each test to make sure the device won't run out of memory due to lazy collection.
+    DumpRenderTreeSupport::garbageCollectorCollect();
+}
+
+void DumpRenderTree::runTests()
+{
+    m_gcController = new GCController();
+    m_accessibilityController = new AccessibilityController();
+    getTestsToRun();
+
+    mainFrame = m_page->mainFrame();
+
+    m_currentTest = m_tests.begin();
+
+    if (m_currentTest == m_tests.end()) {
+        doneDrt();
+        return;
+    }
+
+    if (isHTTPTest(m_currentTest->utf8().data())) {
+        m_currentHttpTest = m_currentTest->utf8().data();
+        m_currentHttpTest.remove(0, strlen(httpTestSyntax));
+        runTest(httpPrefixURL + m_currentHttpTest);
+    } else
+        runTest(kSDCLayoutTestsURI + *m_currentTest);
+}
+
+
+WTF::String DumpRenderTree::dumpFramesAsText(WebCore::Frame* frame)
+{
+    WTF::String s;
+    WebCore::Element* documentElement = frame->document()->documentElement();
+    if (!documentElement)
+        return s.utf8().data();
+
+    if (frame->tree()->parent())
+        s = WTF::String::format("\n--------\nFrame: '%s'\n--------\n", frame->tree()->uniqueName().string().utf8().data());
+
+    s += documentElement->innerText() + "\n";
+
+    if (gLayoutTestController->dumpChildFramesAsText()) {
+        WebCore::FrameTree* tree = frame->tree();
+        for (WebCore::Frame* child = tree->firstChild(); child; child = child->tree()->nextSibling())
+            s += dumpFramesAsText(child);
+    }
+    return s;
+}
+
+static void dumpToFile(const WTF::String& data)
+{
+    fwrite(data.utf8().data(), 1, data.utf8().length(), stdout);
+}
+
+bool DumpRenderTree::isHTTPTest(const WTF::String& test)
+{
+    if (test.length() < strlen(httpTestSyntax))
+        return false;
+    return test.lower().substring(0, strlen(httpTestSyntax)) == httpTestSyntax;
+}
+
+void DumpRenderTree::getTestsToRun()
+{
+    Vector<WTF::String> files;
+
+    FILE* fd = fopen(m_indexFile.utf8().data(), "r");
+    fseek(fd, 0, SEEK_END);
+    int size = ftell(fd);
+    fseek(fd, 0, SEEK_SET);
+    OwnArrayPtr<char> buf = adoptArrayPtr(new char[size]);
+    fread(buf.get(), 1, size, fd);
+    fclose(fd);
+    WTF::String s(buf.get(), size);
+    s.split("\n", files);
+
+    m_tests = files;
+}
+
+void DumpRenderTree::invalidateAnyPreviousWaitToDumpWatchdog()
+{
+    m_waitToDumpWatchdogTimer.stop();
+    waitForPolicy = false;
+}
+
+WTF::String DumpRenderTree::renderTreeDump() const
+{
+    if (mainFrame) {
+        if (mainFrame->view() && mainFrame->view()->layoutPending())
+            mainFrame->view()->layout();
+
+        return externalRepresentation(mainFrame);
+    }
+    return "";
+}
+
+static bool historyItemCompare(const RefPtr<WebCore::HistoryItem>& a, const RefPtr<WebCore::HistoryItem>& b)
+{
+    return codePointCompare(a->urlString(), b->urlString()) < 0;
+}
+
+static WTF::String dumpHistoryItem(PassRefPtr<WebCore::HistoryItem> item, int indent, bool current)
+{
+    WTF::String result;
+
+    int start = 0;
+    if (current) {
+        result += "curr->";
+        start = 6;
+    }
+    for (int i = start; i < indent; i++)
+        result += " ";
+
+    WTF::String url = item->urlString();
+    if (url.contains("file://")) {
+        static WTF::String layoutTestsString("/LayoutTests/");
+        static WTF::String fileTestString("(file test):");
+
+        WTF::String res = url.substring(url.find(layoutTestsString) + layoutTestsString.length());
+        if (res.isEmpty())
+            return result;
+
+        result += fileTestString;
+        result += res;
+    } else
+        result += url;
+
+    WTF::String target = item->target();
+    if (!target.isEmpty())
+        result += " (in frame \"" + target + "\")";
+
+    if (item->isTargetItem())
+        result += "  **nav target**";
+    result += "\n";
+
+    WebCore::HistoryItemVector children = item->children();
+    // Must sort to eliminate arbitrary result ordering which defeats reproducible testing.
+    nonCopyingSort(children.begin(), children.end(), historyItemCompare);
+    unsigned resultSize = children.size();
+    for (unsigned i = 0; i < resultSize; ++i)
+        result += dumpHistoryItem(children[i], indent + 4, false);
+
+    return result;
+}
+
+static WTF::String dumpBackForwardListForWebView()
+{
+    WTF::String result = "\n============== Back Forward List ==============\n";
+    // FORMAT:
+    // "        (file test):fast/loader/resources/click-fragment-link.html  **nav target**"
+    // "curr->  (file test):fast/loader/resources/click-fragment-link.html#testfragment  **nav target**"
+    WebCore::BackForwardListImpl* bfList = static_cast<WebCore::BackForwardListImpl*>(mainFrame->page()->backForward()->client());
+    int maxItems = bfList->capacity();
+    WebCore::HistoryItemVector entries;
+    bfList->backListWithLimit(maxItems, entries);
+    unsigned resultSize = entries.size();
+    for (unsigned i = 0; i < resultSize; ++i)
+        result += dumpHistoryItem(entries[i], 8, false);
+
+    result += dumpHistoryItem(bfList->currentItem(), 8, true);
+
+    bfList->forwardListWithLimit(maxItems, entries);
+    resultSize = entries.size();
+    for (unsigned i = 0; i < resultSize; ++i)
+        result += dumpHistoryItem(entries[i], 8, false);
+
+    result += "===============================================\n";
+
+    return result;
+}
+
+void DumpRenderTree::dump()
+{
+    invalidateAnyPreviousWaitToDumpWatchdog();
+
+    WTF::String dumpFile = m_resultsDir + *m_currentTest + ".dump";
+
+    WTF::String resultMimeType = "text/plain";
+    WTF::String responseMimeType = mainFrame->loader()->documentLoader()->responseMIMEType();
+
+    bool dumpAsText = gLayoutTestController->dumpAsText() || responseMimeType == "text/plain";
+    WTF::String data = dumpAsText ? dumpFramesAsText(mainFrame) : renderTreeDump();
+
+    if (gLayoutTestController->dumpBackForwardList())
+        data += dumpBackForwardListForWebView();
+
+    WTF::String result = "Content-Type: " + resultMimeType + "\n" + data;
+
+    dumpToFile(result);
+    if (m_dumpPixels && !dumpAsText && gLayoutTestController->generatePixelResults())
+        dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
+
+    WTF::String crashFile = dumpFile + ".crash";
+    unlink(crashFile.utf8().data());
+
+    testDone = true;
+    runRemainingTests();
+}
+
+void DumpRenderTree::setWaitToDumpWatchdog(double interval)
+{
+    invalidateAnyPreviousWaitToDumpWatchdog();
+    m_waitToDumpWatchdogTimer.startOneShot(interval);
+}
+
+void DumpRenderTree::waitToDumpWatchdogTimerFired(WebCore::Timer<DumpRenderTree>*)
+{
+    gLayoutTestController->waitToDumpWatchdogTimerFired();
+}
+
+void DumpRenderTree::processWork(WebCore::Timer<DumpRenderTree>*)
+{
+    if (topLoadingFrame)
+        return;
+
+    if (WorkQueue::shared()->processWork() && !gLayoutTestController->waitToDump())
+        dump();
+}
+
+void DumpRenderTree::locationChangeForFrame(WebCore::Frame* frame)
+{
+    if (frame != topLoadingFrame)
+        return;
+
+    topLoadingFrame = 0;
+    WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue
+    if (gLayoutTestController->waitToDump())
+        return;
+
+    if (WorkQueue::shared()->count())
+        m_workTimer.startOneShot(0);
+    else
+        dump();
+}
+
+// FrameLoadClient delegates.
+void DumpRenderTree::didStartProvisionalLoadForFrame(WebCore::Frame* frame)
+{
+    if (!testDone && gLayoutTestController->dumpFrameLoadCallbacks())
+        printf("%s - didStartProvisionalLoadForFrame\n", drtFrameDescription(frame).utf8().data());
+
+    if (!testDone && gLayoutTestController->dumpUserGestureInFrameLoadCallbacks())
+        printf("Frame with user gesture \"%s\" - in didStartProvisionalLoadForFrame\n", WebCore::ScriptController::processingUserGesture() ? "true" : "false");
+
+    if (!topLoadingFrame && !testDone)
+        topLoadingFrame = frame;
+
+    if (!testDone && gLayoutTestController->stopProvisionalFrameLoads()) {
+        printf("%s - stopping load in didStartProvisionalLoadForFrame callback\n", drtFrameDescription(frame).utf8().data());
+        frame->loader()->stopForUserCancel();
+    }
+}
+
+void DumpRenderTree::didCommitLoadForFrame(WebCore::Frame* frame)
+{
+    if (!testDone && gLayoutTestController->dumpFrameLoadCallbacks())
+        printf("%s - didCommitLoadForFrame\n", drtFrameDescription(frame).utf8().data());
+
+    gLayoutTestController->setWindowIsKey(true);
+}
+
+void DumpRenderTree::didFailProvisionalLoadForFrame(WebCore::Frame* frame)
+{
+    if (!testDone && gLayoutTestController->dumpFrameLoadCallbacks())
+        printf("%s - didFailProvisionalLoadWithError\n", drtFrameDescription(frame).utf8().data());
+
+    locationChangeForFrame(frame);
+}
+
+void DumpRenderTree::didFailLoadForFrame(WebCore::Frame* frame)
+{
+    if (!testDone && gLayoutTestController->dumpFrameLoadCallbacks())
+        printf("%s - didFailLoadWithError\n", drtFrameDescription(frame).utf8().data());
+
+    locationChangeForFrame(frame);
+}
+
+void DumpRenderTree::didFinishLoadForFrame(WebCore::Frame* frame)
+{
+    if (!testDone && gLayoutTestController->dumpFrameLoadCallbacks())
+        printf("%s - didFinishLoadForFrame\n", drtFrameDescription(frame).utf8().data());
+
+    if (frame == topLoadingFrame)
+        m_loadFinished = true;
+    locationChangeForFrame(frame);
+}
+
+void DumpRenderTree::didFinishDocumentLoadForFrame(WebCore::Frame* frame)
+{
+    if (!testDone) {
+        if (gLayoutTestController->dumpFrameLoadCallbacks())
+            printf("%s - didFinishDocumentLoadForFrame\n", drtFrameDescription(frame).utf8().data());
+        else {
+            unsigned pendingFrameUnloadEvents = frame->domWindow()->pendingUnloadEventListeners();
+            if (pendingFrameUnloadEvents)
+                printf("%s - has %u onunload handler(s)\n", drtFrameDescription(frame).utf8().data(), pendingFrameUnloadEvents);
+        }
+    }
+}
+
+void DumpRenderTree::didClearWindowObjectInWorld(WebCore::DOMWrapperWorld*, JSGlobalContextRef context, JSObjectRef windowObject)
+{
+    JSValueRef exception = 0;
+
+    gLayoutTestController->makeWindowObject(context, windowObject, &exception);
+    ASSERT(!exception);
+
+    m_gcController->makeWindowObject(context, windowObject, &exception);
+    ASSERT(!exception);
+
+    m_accessibilityController->makeWindowObject(context, windowObject, &exception);
+    ASSERT(!exception);
+
+    JSStringRef eventSenderStr = JSStringCreateWithUTF8CString("eventSender");
+    JSValueRef eventSender = makeEventSender(context);
+    JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0);
+    JSStringRelease(eventSenderStr);
+    WebCoreTestSupport::injectInternalsObject(context);
+}
+
+void DumpRenderTree::didReceiveTitleForFrame(const WTF::String& title, WebCore::Frame* frame)
+{
+    if (!testDone && gLayoutTestController->dumpFrameLoadCallbacks())
+        printf("%s - didReceiveTitle: %s\n", drtFrameDescription(frame).utf8().data(), title.utf8().data());
+
+    if (gLayoutTestController->dumpTitleChanges())
+        printf("TITLE CHANGED: %s\n", title.utf8().data());
+}
+
+// ChromeClient delegates.
+void DumpRenderTree::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID)
+{
+    printf("CONSOLE MESSAGE: line %d: %s\n", lineNumber, message.utf8().data());
+}
+
+void DumpRenderTree::runJavaScriptAlert(const WTF::String& message)
+{
+    if (!testDone)
+        printf("ALERT: %s\n", message.utf8().data());
+}
+
+bool DumpRenderTree::runJavaScriptConfirm(const WTF::String& message)
+{
+    if (!testDone)
+        printf("CONFIRM: %s\n", message.utf8().data());
+    return true;
+}
+
+WTF::String DumpRenderTree::runJavaScriptPrompt(const WTF::String& message, const WTF::String& defaultValue)
+{
+    if (!testDone)
+        printf("PROMPT: %s, default text: %s\n", message.utf8().data(), defaultValue.utf8().data());
+    return defaultValue;
+}
+
+bool DumpRenderTree::runBeforeUnloadConfirmPanel(const WTF::String& message)
+{
+    if (!testDone)
+        printf("CONFIRM NAVIGATION: %s\n", message.utf8().data());
+    return true;
+}
+
+void DumpRenderTree::setStatusText(const WTF::String& status)
+{
+    if (gLayoutTestController->dumpStatusCallbacks())
+        printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", status.utf8().data());
+}
+
+void DumpRenderTree::exceededDatabaseQuota(WebCore::SecurityOrigin* origin, const WTF::String& name)
+{
+    if (!testDone && gLayoutTestController->dumpDatabaseCallbacks())
+        printf("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:{%s, %s, %i} database:%s\n", origin->protocol().utf8().data(), origin->host().utf8().data(), origin->port(), name.utf8().data());
+
+    WebCore::DatabaseTracker::tracker().setQuota(mainFrame->document()->securityOrigin(), 5 * 1024 * 1024);
+}
+
+bool DumpRenderTree::allowsOpeningWindow()
+{
+    return gLayoutTestController->canOpenWindows();
+}
+
+void DumpRenderTree::windowCreated(BlackBerry::WebKit::WebPage* page)
+{
+    page->settings()->setJavaScriptOpenWindowsAutomatically(true);
+}
+
+// EditorClient delegates.
+void DumpRenderTree::didBeginEditing()
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: webViewDidBeginEditing:%s\n", "WebViewDidBeginEditingNotification");
+}
+
+void DumpRenderTree::didEndEditing()
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: webViewDidEndEditing:%s\n", "WebViewDidEndEditingNotification");
+}
+
+void DumpRenderTree::didChange()
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: webViewDidChange:%s\n", "WebViewDidChangeNotification");
+}
+
+void DumpRenderTree::didChangeSelection()
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: webViewDidChangeSelection:%s\n", "WebViewDidChangeSelectionNotification");
+}
+
+bool DumpRenderTree::findString(const WTF::String& string, WebCore::FindOptions options)
+{
+    WebCore::Page* page = mainFrame ? mainFrame->page() : 0;
+    return page && page->findString(string, options);
+}
+
+bool DumpRenderTree::shouldBeginEditingInDOMRange(WebCore::Range* range)
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n", drtRangeDescription(range).utf8().data());
+    return m_acceptsEditing;
+}
+
+bool DumpRenderTree::shouldEndEditingInDOMRange(WebCore::Range* range)
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n", drtRangeDescription(range).utf8().data());
+    return m_acceptsEditing;
+}
+
+bool DumpRenderTree::shouldDeleteDOMRange(WebCore::Range* range)
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: shouldDeleteDOMRange:%s\n", drtRangeDescription(range).utf8().data());
+    return m_acceptsEditing;
+}
+
+bool DumpRenderTree::shouldChangeSelectedDOMRangeToDOMRangeAffinityStillSelecting(WebCore::Range* fromRange, WebCore::Range* toRange, int affinity, bool stillSelecting)
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s toDOMRange:%s affinity:%s stillSelecting:%s\n", drtRangeDescription(fromRange).utf8().data(), drtRangeDescription(toRange).utf8().data(), drtAffinityDescription(static_cast<WebCore::EAffinity>(affinity)).utf8().data(), stillSelecting ? "TRUE" : "FALSE");
+    return m_acceptsEditing;
+}
+
+static const char* insertActionString(WebCore::EditorInsertAction action)
+{
+    switch (action) {
+    case WebCore::EditorInsertActionTyped:
+        return "WebViewInsertActionTyped";
+    case WebCore::EditorInsertActionPasted:
+        return "WebViewInsertActionPasted";
+    case WebCore::EditorInsertActionDropped:
+        return "WebViewInsertActionDropped";
+    }
+    ASSERT_NOT_REACHED();
+    return "WebViewInsertActionTyped";
+}
+
+bool DumpRenderTree::shouldInsertNode(WebCore::Node* node, WebCore::Range* range, int action)
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: shouldInsertNode:%s replacingDOMRange:%s givenAction:%s\n", drtDumpPath(node).utf8().data(), drtRangeDescription(range).utf8().data(), insertActionString((WebCore::EditorInsertAction)action));
+    return m_acceptsEditing;
+}
+
+bool DumpRenderTree::shouldInsertText(const WTF::String& text, WebCore::Range* range, int action)
+{
+    if (!testDone && gLayoutTestController->dumpEditingCallbacks())
+        printf("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:%s givenAction:%s\n", text.utf8().data(), drtRangeDescription(range).utf8().data(), insertActionString((WebCore::EditorInsertAction)action));
+    return m_acceptsEditing;
+}
+
+void DumpRenderTree::didDecidePolicyForNavigationAction(const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request)
+{
+    if (!waitForPolicy)
+        return;
+
+    const char* typeDescription;
+    switch (action.type()) {
+    case WebCore::NavigationTypeLinkClicked:
+        typeDescription = "link clicked";
+        break;
+    case WebCore::NavigationTypeFormSubmitted:
+        typeDescription = "form submitted";
+        break;
+    case WebCore::NavigationTypeBackForward:
+        typeDescription = "back/forward";
+        break;
+    case WebCore::NavigationTypeReload:
+        typeDescription = "reload";
+        break;
+    case WebCore::NavigationTypeFormResubmitted:
+        typeDescription = "form resubmitted";
+        break;
+    case WebCore::NavigationTypeOther:
+        typeDescription = "other";
+        break;
+    default:
+        typeDescription = "illegal value";
+    }
+
+    printf("Policy delegate: attempt to load %s with navigation type '%s'\n", request.url().string().utf8().data(), typeDescription);
+    // FIXME: do originating part.
+
+    gLayoutTestController->notifyDone();
+}
+
+void DumpRenderTree::didDispatchWillPerformClientRedirect()
+{
+    if (!testDone && gLayoutTestController->dumpUserGestureInFrameLoadCallbacks())
+        printf("Frame with user gesture \"%s\" - in willPerformClientRedirect\n", WebCore::ScriptController::processingUserGesture() ? "true" : "false");
+}
+
+void DumpRenderTree::didHandleOnloadEventsForFrame(WebCore::Frame* frame)
+{
+    if (!testDone && gLayoutTestController->dumpFrameLoadCallbacks())
+        printf("%s - didHandleOnloadEventsForFrame\n", drtFrameDescription(frame).utf8().data());
+}
+
+void DumpRenderTree::didReceiveResponseForFrame(WebCore::Frame* frame, const WebCore::ResourceResponse& response)
+{
+    if (!testDone && gLayoutTestController->dumpResourceResponseMIMETypes())
+        printf("%s has MIME type %s\n", response.url().lastPathComponent().utf8().data(), response.mimeType().utf8().data());
+}
+
+}
+}
+
+// Static dump() function required by cross-platform DRT code.
+void dump()
+{
+    BlackBerry::WebKit::DumpRenderTree* dumper = BlackBerry::WebKit::DumpRenderTree::currentInstance();
+    if (!dumper)
+        return;
+
+    dumper->dump();
+}
diff --git a/Tools/DumpRenderTree/blackberry/DumpRenderTreeBlackBerry.h b/Tools/DumpRenderTree/blackberry/DumpRenderTreeBlackBerry.h
new file mode 100644 (file)
index 0000000..db2f69a
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
+ *
+ * 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 DumpRenderTreeBlackBerry_h
+#define DumpRenderTreeBlackBerry_h
+
+#include "BlackBerryGlobal.h"
+
+#include "DumpRenderTreeClient.h"
+#include "PlatformString.h"
+#include "Timer.h"
+#include <FindOptions.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+class Frame;
+class DOMWrapperWorld;
+class Range;
+}
+
+extern WebCore::Frame* mainFrame;
+extern WebCore::Frame* topLoadingFrame;
+extern bool waitForPolicy;
+
+class AccessibilityController;
+class GCController;
+
+namespace BlackBerry {
+namespace WebKit {
+class WebPage;
+
+class DumpRenderTree : public BlackBerry::WebKit::DumpRenderTreeClient {
+public:
+    DumpRenderTree(WebPage*);
+    virtual ~DumpRenderTree();
+
+    static DumpRenderTree* currentInstance() { return s_currentInstance; }
+
+    void dump();
+
+    void setWaitToDumpWatchdog(double interval);
+
+    WebPage* page() { return m_page; }
+
+    bool loadFinished() const { return m_loadFinished; }
+
+    // FrameLoaderClient delegates
+    void didStartProvisionalLoadForFrame(WebCore::Frame*);
+    void didCommitLoadForFrame(WebCore::Frame*);
+    void didFailProvisionalLoadForFrame(WebCore::Frame*);
+    void didFailLoadForFrame(WebCore::Frame*);
+    void didFinishLoadForFrame(WebCore::Frame*);
+    void didFinishDocumentLoadForFrame(WebCore::Frame*);
+    void didClearWindowObjectInWorld(WebCore::DOMWrapperWorld*, JSGlobalContextRef, JSObjectRef windowObject);
+    void didReceiveTitleForFrame(const WTF::String& title, WebCore::Frame*);
+    void didDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&);
+    void didDispatchWillPerformClientRedirect();
+    void didHandleOnloadEventsForFrame(WebCore::Frame*);
+    void didReceiveResponseForFrame(WebCore::Frame*, const WebCore::ResourceResponse&);
+
+    // ChromeClient delegates
+    void addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID);
+    void runJavaScriptAlert(const WTF::String& message);
+    bool runJavaScriptConfirm(const WTF::String& message);
+    WTF::String runJavaScriptPrompt(const WTF::String& message, const WTF::String& defaultValue);
+    bool runBeforeUnloadConfirmPanel(const WTF::String& message);
+    void setStatusText(const WTF::String&);
+    void exceededDatabaseQuota(WebCore::SecurityOrigin*, const WTF::String& name);
+    bool allowsOpeningWindow();
+    void windowCreated(WebPage*);
+
+    // EditorClient delegates
+    void setAcceptsEditing(bool acceptsEditing) { m_acceptsEditing = acceptsEditing; }
+
+    void didBeginEditing();
+    void didEndEditing();
+    void didChange();
+    void didChangeSelection();
+    bool findString(const WTF::String&, WebCore::FindOptions);
+    bool shouldBeginEditingInDOMRange(WebCore::Range*);
+    bool shouldEndEditingInDOMRange(WebCore::Range*);
+    bool shouldDeleteDOMRange(WebCore::Range*);
+    bool shouldChangeSelectedDOMRangeToDOMRangeAffinityStillSelecting(WebCore::Range* fromRange, WebCore::Range* toRange, int affinity, bool stillSelecting);
+    bool shouldInsertNode(WebCore::Node*, WebCore::Range*, int insertAction);
+    bool shouldInsertText(const WTF::String&, WebCore::Range*, int insertAction);
+
+    bool isSelectTrailingWhitespaceEnabled() const { return s_selectTrailingWhitespaceEnabled; }
+    void setSelectTrailingWhitespaceEnabled(bool enabled) { s_selectTrailingWhitespaceEnabled = enabled; }
+
+private:
+    void runTest(const WTF::String& url);
+    void runTests();
+
+    void processWork(WebCore::Timer<DumpRenderTree>*);
+
+private:
+    static DumpRenderTree* s_currentInstance;
+
+    WTF::String dumpFramesAsText(WebCore::Frame*);
+    void locationChangeForFrame(WebCore::Frame*);
+
+    void doneDrt();
+    void getTestsToRun();
+    bool isHTTPTest(const WTF::String& test);
+    WTF::String renderTreeDump() const;
+    void resetToConsistentStateBeforeTesting();
+    void runRemainingTests();
+    void invalidateAnyPreviousWaitToDumpWatchdog();
+    void waitToDumpWatchdogTimerFired(WebCore::Timer<DumpRenderTree>*);
+
+    Vector<WTF::String> m_tests;
+    Vector<WTF::String>::iterator m_currentTest;
+
+    WTF::String m_resultsDir;
+    WTF::String m_indexFile;
+    WTF::String m_doneFile;
+    WTF::String m_currentHttpTest;
+    WTF::String m_currentTestFile;
+
+    GCController* m_gcController;
+    AccessibilityController* m_accessibilityController;
+    WebPage* m_page;
+    bool m_dumpPixels;
+    WebCore::Timer<DumpRenderTree> m_waitToDumpWatchdogTimer;
+    WebCore::Timer<DumpRenderTree> m_workTimer;
+
+    bool m_acceptsEditing;
+    bool m_loadFinished;
+    static bool s_selectTrailingWhitespaceEnabled;
+};
+}
+}
+
+#endif // DumpRenderTreeBlackBerry_h