1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "public/testing/WebTestProxy.h"
32
33#include "AccessibilityControllerChromium.h"
34#include "EventSender.h"
35#include "MockColorChooser.h"
36#include "MockWebSpeechInputController.h"
37#include "MockWebSpeechRecognizer.h"
38#include "MockWebValidationMessageClient.h"
39#include "SpellCheckClient.h"
40#include "TestCommon.h"
41#include "TestInterfaces.h"
42#include "TestPlugin.h"
43#include "TestRunner.h"
44#include "WebUserMediaClientMock.h"
45#include "public/platform/WebCString.h"
46#include "public/platform/WebURLError.h"
47#include "public/platform/WebURLRequest.h"
48#include "public/platform/WebURLResponse.h"
49#include "public/testing/WebTestDelegate.h"
50#include "public/testing/WebTestInterfaces.h"
51#include "public/testing/WebTestRunner.h"
52#include "public/web/WebAccessibilityNotification.h"
53#include "public/web/WebAccessibilityObject.h"
54#include "public/web/WebCachedURLRequest.h"
55#include "public/web/WebConsoleMessage.h"
56#include "public/web/WebDataSource.h"
57#include "public/web/WebDeviceOrientationClientMock.h"
58#include "public/web/WebDocument.h"
59#include "public/web/WebElement.h"
60#include "public/web/WebFrame.h"
61#include "public/web/WebGeolocationClientMock.h"
62#include "public/web/WebHistoryItem.h"
63#include "public/web/WebNode.h"
64#include "public/web/WebPluginParams.h"
65#include "public/web/WebPrintParams.h"
66#include "public/web/WebRange.h"
67#include "public/web/WebScriptController.h"
68#include "public/web/WebUserGestureIndicator.h"
69#include "public/web/WebView.h"
70
71// FIXME: Including platform_canvas.h here is a layering violation.
72#include <cctype>
73#include "skia/ext/platform_canvas.h"
74
75using namespace WebKit;
76using namespace std;
77
78namespace WebTestRunner {
79
80namespace {
81
82class HostMethodTask : public WebMethodTask<WebTestProxyBase> {
83public:
84    typedef void (WebTestProxyBase::*CallbackMethodType)();
85    HostMethodTask(WebTestProxyBase* object, CallbackMethodType callback)
86        : WebMethodTask<WebTestProxyBase>(object)
87        , m_callback(callback)
88    { }
89
90    virtual void runIfValid() { (m_object->*m_callback)(); }
91
92private:
93    CallbackMethodType m_callback;
94};
95
96void printNodeDescription(WebTestDelegate* delegate, const WebNode& node, int exception)
97{
98    if (exception) {
99        delegate->printMessage("ERROR");
100        return;
101    }
102    if (node.isNull()) {
103        delegate->printMessage("(null)");
104        return;
105    }
106    delegate->printMessage(node.nodeName().utf8().data());
107    const WebNode& parent = node.parentNode();
108    if (!parent.isNull()) {
109        delegate->printMessage(" > ");
110        printNodeDescription(delegate, parent, 0);
111    }
112}
113
114void printRangeDescription(WebTestDelegate* delegate, const WebRange& range)
115{
116    if (range.isNull()) {
117        delegate->printMessage("(null)");
118        return;
119    }
120    char buffer[100];
121    snprintf(buffer, sizeof(buffer), "range from %d of ", range.startOffset());
122    delegate->printMessage(buffer);
123    int exception = 0;
124    WebNode startNode = range.startContainer(exception);
125    printNodeDescription(delegate, startNode, exception);
126    snprintf(buffer, sizeof(buffer), " to %d of ", range.endOffset());
127    delegate->printMessage(buffer);
128    WebNode endNode = range.endContainer(exception);
129    printNodeDescription(delegate, endNode, exception);
130}
131
132string editingActionDescription(WebEditingAction action)
133{
134    switch (action) {
135    case WebKit::WebEditingActionTyped:
136        return "WebViewInsertActionTyped";
137    case WebKit::WebEditingActionPasted:
138        return "WebViewInsertActionPasted";
139    case WebKit::WebEditingActionDropped:
140        return "WebViewInsertActionDropped";
141    }
142    return "(UNKNOWN ACTION)";
143}
144
145string textAffinityDescription(WebTextAffinity affinity)
146{
147    switch (affinity) {
148    case WebKit::WebTextAffinityUpstream:
149        return "NSSelectionAffinityUpstream";
150    case WebKit::WebTextAffinityDownstream:
151        return "NSSelectionAffinityDownstream";
152    }
153    return "(UNKNOWN AFFINITY)";
154}
155
156void printFrameDescription(WebTestDelegate* delegate, WebFrame* frame)
157{
158    string name8 = frame->uniqueName().utf8();
159    if (frame == frame->view()->mainFrame()) {
160        if (!name8.length()) {
161            delegate->printMessage("main frame");
162            return;
163        }
164        delegate->printMessage(string("main frame \"") + name8 + "\"");
165        return;
166    }
167    if (!name8.length()) {
168        delegate->printMessage("frame (anonymous)");
169        return;
170    }
171    delegate->printMessage(string("frame \"") + name8 + "\"");
172}
173
174void printFrameUserGestureStatus(WebTestDelegate* delegate, WebFrame* frame, const char* msg)
175{
176    bool isUserGesture = WebUserGestureIndicator::isProcessingUserGesture();
177    delegate->printMessage(string("Frame with user gesture \"") + (isUserGesture ? "true" : "false") + "\"" + msg);
178}
179
180// Used to write a platform neutral file:/// URL by taking the
181// filename and its directory. (e.g., converts
182// "file:///tmp/foo/bar.txt" to just "bar.txt").
183string descriptionSuitableForTestResult(const string& url)
184{
185    if (url.empty() || string::npos == url.find("file://"))
186        return url;
187
188    size_t pos = url.rfind('/');
189    if (pos == string::npos || !pos)
190        return "ERROR:" + url;
191    pos = url.rfind('/', pos - 1);
192    if (pos == string::npos)
193        return "ERROR:" + url;
194
195    return url.substr(pos + 1);
196}
197
198void printResponseDescription(WebTestDelegate* delegate, const WebURLResponse& response)
199{
200    if (response.isNull()) {
201        delegate->printMessage("(null)");
202        return;
203    }
204    string url = response.url().spec();
205    char data[100];
206    snprintf(data, sizeof(data), "%d", response. httpStatusCode());
207    delegate->printMessage(string("<NSURLResponse ") + descriptionSuitableForTestResult(url) + ", http status code " + data + ">");
208}
209
210string URLDescription(const GURL& url)
211{
212    if (url.SchemeIs("file"))
213        return url.ExtractFileName();
214    return url.possibly_invalid_spec();
215}
216
217string PriorityDescription(const WebURLRequest::Priority& priority)
218{
219    switch (priority) {
220    case WebURLRequest::PriorityVeryLow:
221        return "VeryLow";
222    case WebURLRequest::PriorityLow:
223        return "Low";
224    case WebURLRequest::PriorityMedium:
225        return "Medium";
226    case WebURLRequest::PriorityHigh:
227        return "High";
228    case WebURLRequest::PriorityVeryHigh:
229        return "VeryHigh";
230    case WebURLRequest::PriorityUnresolved:
231    default:
232        return "Unresolved";
233    }
234}
235
236void blockRequest(WebURLRequest& request)
237{
238    request.setURL(GURL("255.255.255.255"));
239}
240
241bool isLocalhost(const string& host)
242{
243    return host == "127.0.0.1" || host == "localhost";
244}
245
246bool hostIsUsedBySomeTestsToGenerateError(const string& host)
247{
248    return host == "255.255.255.255";
249}
250
251// Used to write a platform neutral file:/// URL by only taking the filename
252// (e.g., converts "file:///tmp/foo.txt" to just "foo.txt").
253string urlSuitableForTestResult(const string& url)
254{
255    if (url.empty() || string::npos == url.find("file://"))
256        return url;
257
258    size_t pos = url.rfind('/');
259    if (pos == string::npos) {
260#ifdef WIN32
261        pos = url.rfind('\\');
262        if (pos == string::npos)
263            pos = 0;
264#else
265        pos = 0;
266#endif
267    }
268    string filename = url.substr(pos + 1);
269    if (filename.empty())
270        return "file:"; // A WebKit test has this in its expected output.
271    return filename;
272}
273
274// WebNavigationType debugging strings taken from PolicyDelegate.mm.
275const char* linkClickedString = "link clicked";
276const char* formSubmittedString = "form submitted";
277const char* backForwardString = "back/forward";
278const char* reloadString = "reload";
279const char* formResubmittedString = "form resubmitted";
280const char* otherString = "other";
281const char* illegalString = "illegal value";
282
283// Get a debugging string from a WebNavigationType.
284const char* webNavigationTypeToString(WebNavigationType type)
285{
286    switch (type) {
287    case WebKit::WebNavigationTypeLinkClicked:
288        return linkClickedString;
289    case WebKit::WebNavigationTypeFormSubmitted:
290        return formSubmittedString;
291    case WebKit::WebNavigationTypeBackForward:
292        return backForwardString;
293    case WebKit::WebNavigationTypeReload:
294        return reloadString;
295    case WebKit::WebNavigationTypeFormResubmitted:
296        return formResubmittedString;
297    case WebKit::WebNavigationTypeOther:
298        return otherString;
299    }
300    return illegalString;
301}
302
303string dumpDocumentText(WebFrame* frame)
304{
305    // We use the document element's text instead of the body text here because
306    // not all documents have a body, such as XML documents.
307    WebElement documentElement = frame->document().documentElement();
308    if (documentElement.isNull())
309        return string();
310    return documentElement.innerText().utf8();
311}
312
313string dumpFramesAsText(WebFrame* frame, bool recursive)
314{
315    string result;
316
317    // Add header for all but the main frame. Skip empty frames.
318    if (frame->parent() && !frame->document().documentElement().isNull()) {
319        result.append("\n--------\nFrame: '");
320        result.append(frame->uniqueName().utf8().data());
321        result.append("'\n--------\n");
322    }
323
324    result.append(dumpDocumentText(frame));
325    result.append("\n");
326
327    if (recursive) {
328        for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
329            result.append(dumpFramesAsText(child, recursive));
330    }
331
332    return result;
333}
334
335string dumpFramesAsPrintedText(WebFrame* frame, bool recursive)
336{
337    string result;
338
339    // Cannot do printed format for anything other than HTML
340    if (!frame->document().isHTMLDocument())
341        return string();
342
343    // Add header for all but the main frame. Skip empty frames.
344    if (frame->parent() && !frame->document().documentElement().isNull()) {
345        result.append("\n--------\nFrame: '");
346        result.append(frame->uniqueName().utf8().data());
347        result.append("'\n--------\n");
348    }
349
350    result.append(frame->renderTreeAsText(WebFrame::RenderAsTextPrinting).utf8());
351    result.append("\n");
352
353    if (recursive) {
354        for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
355            result.append(dumpFramesAsPrintedText(child, recursive));
356    }
357
358    return result;
359}
360
361string dumpFrameScrollPosition(WebFrame* frame, bool recursive)
362{
363    string result;
364    WebSize offset = frame->scrollOffset();
365    if (offset.width > 0 || offset.height > 0) {
366        if (frame->parent())
367            result = string("frame '") + frame->uniqueName().utf8().data() + "' ";
368        char data[100];
369        snprintf(data, sizeof(data), "scrolled to %d,%d\n", offset.width, offset.height);
370        result += data;
371    }
372
373    if (!recursive)
374        return result;
375    for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
376        result += dumpFrameScrollPosition(child, recursive);
377    return result;
378}
379
380struct ToLower {
381    char16 operator()(char16 c) { return tolower(c); }
382};
383
384// Returns True if item1 < item2.
385bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2)
386{
387    string16 target1 = item1.target();
388    string16 target2 = item2.target();
389    std::transform(target1.begin(), target1.end(), target1.begin(), ToLower());
390    std::transform(target2.begin(), target2.end(), target2.begin(), ToLower());
391    return target1 < target2;
392}
393
394string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent)
395{
396    string result;
397
398    if (isCurrent) {
399        result.append("curr->");
400        result.append(indent - 6, ' '); // 6 == "curr->".length()
401    } else
402        result.append(indent, ' ');
403
404    string url = normalizeLayoutTestURL(item.urlString().utf8());
405    result.append(url);
406    if (!item.target().isEmpty()) {
407        result.append(" (in frame \"");
408        result.append(item.target().utf8());
409        result.append("\")");
410    }
411    if (item.isTargetItem())
412        result.append("  **nav target**");
413    result.append("\n");
414
415    const WebVector<WebHistoryItem>& children = item.children();
416    if (!children.isEmpty()) {
417        // Must sort to eliminate arbitrary result ordering which defeats
418        // reproducible testing.
419        // FIXME: WebVector should probably just be a std::vector!!
420        std::vector<WebHistoryItem> sortedChildren;
421        for (size_t i = 0; i < children.size(); ++i)
422            sortedChildren.push_back(children[i]);
423        std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess);
424        for (size_t i = 0; i < sortedChildren.size(); ++i)
425            result += dumpHistoryItem(sortedChildren[i], indent + 4, false);
426    }
427
428    return result;
429}
430
431void dumpBackForwardList(const WebVector<WebHistoryItem>& history, size_t currentEntryIndex, string& result)
432{
433    result.append("\n============== Back Forward List ==============\n");
434    for (size_t index = 0; index < history.size(); ++index)
435        result.append(dumpHistoryItem(history[index], 8, index == currentEntryIndex));
436    result.append("===============================================\n");
437}
438
439string dumpAllBackForwardLists(TestInterfaces* interfaces, WebTestDelegate* delegate)
440{
441    string result;
442    const vector<WebTestProxyBase*>& windowList = interfaces->windowList();
443    for (unsigned i = 0; i < windowList.size(); ++i) {
444        size_t currentEntryIndex = 0;
445        WebVector<WebHistoryItem> history;
446        delegate->captureHistoryForWindow(windowList.at(i), &history, &currentEntryIndex);
447        dumpBackForwardList(history, currentEntryIndex, result);
448    }
449    return result;
450}
451
452}
453
454WebTestProxyBase::WebTestProxyBase()
455    : m_testInterfaces(0)
456    , m_delegate(0)
457    , m_webWidget(0)
458    , m_spellcheck(new SpellCheckClient)
459    , m_chooserCount(0)
460    , m_validationMessageClient(new MockWebValidationMessageClient())
461{
462    reset();
463}
464
465WebTestProxyBase::~WebTestProxyBase()
466{
467    m_testInterfaces->windowClosed(this);
468}
469
470void WebTestProxyBase::setInterfaces(WebTestInterfaces* interfaces)
471{
472    m_testInterfaces = interfaces->testInterfaces();
473    m_testInterfaces->windowOpened(this);
474}
475
476void WebTestProxyBase::setDelegate(WebTestDelegate* delegate)
477{
478    m_delegate = delegate;
479    m_spellcheck->setDelegate(delegate);
480    m_validationMessageClient->setDelegate(delegate);
481#if ENABLE_INPUT_SPEECH
482    if (m_speechInputController.get())
483        m_speechInputController->setDelegate(delegate);
484#endif
485    if (m_speechRecognizer.get())
486        m_speechRecognizer->setDelegate(delegate);
487}
488
489void WebTestProxyBase::setWidget(WebWidget* widget)
490{
491    m_webWidget = widget;
492}
493
494WebWidget* WebTestProxyBase::webWidget()
495{
496    return m_webWidget;
497}
498
499WebView* WebTestProxyBase::webView()
500{
501    WEBKIT_ASSERT(m_webWidget);
502    // TestRunner does not support popup widgets. So m_webWidget is always a WebView.
503    return static_cast<WebView*>(m_webWidget);
504}
505
506void WebTestProxyBase::reset()
507{
508    m_paintRect = WebRect();
509    m_canvas.reset();
510    m_isPainting = false;
511    m_animateScheduled = false;
512    m_resourceIdentifierMap.clear();
513    m_logConsoleOutput = true;
514    if (m_geolocationClient.get())
515        m_geolocationClient->resetMock();
516#if ENABLE_INPUT_SPEECH
517    if (m_speechInputController.get())
518        m_speechInputController->clearResults();
519#endif
520}
521
522WebSpellCheckClient* WebTestProxyBase::spellCheckClient() const
523{
524    return m_spellcheck.get();
525}
526
527WebValidationMessageClient* WebTestProxyBase::validationMessageClient()
528{
529    return m_validationMessageClient.get();
530}
531
532WebColorChooser* WebTestProxyBase::createColorChooser(WebColorChooserClient* client, const WebKit::WebColor& color)
533{
534    // This instance is deleted by WebCore::ColorInputType
535    return new MockColorChooser(client, m_delegate, this);
536}
537
538string WebTestProxyBase::captureTree(bool debugRenderTree)
539{
540    WebScriptController::flushConsoleMessages();
541
542    bool shouldDumpAsText = m_testInterfaces->testRunner()->shouldDumpAsText();
543    bool shouldDumpAsPrinted = m_testInterfaces->testRunner()->isPrinting();
544    WebFrame* frame = webView()->mainFrame();
545    string dataUtf8;
546    if (shouldDumpAsText) {
547        bool recursive = m_testInterfaces->testRunner()->shouldDumpChildFramesAsText();
548        dataUtf8 = shouldDumpAsPrinted ? dumpFramesAsPrintedText(frame, recursive) : dumpFramesAsText(frame, recursive);
549    } else {
550        bool recursive = m_testInterfaces->testRunner()->shouldDumpChildFrameScrollPositions();
551        WebFrame::RenderAsTextControls renderTextBehavior = WebFrame::RenderAsTextNormal;
552        if (shouldDumpAsPrinted)
553            renderTextBehavior |= WebFrame::RenderAsTextPrinting;
554        if (debugRenderTree)
555            renderTextBehavior |= WebFrame::RenderAsTextDebug;
556        dataUtf8 = frame->renderTreeAsText(renderTextBehavior).utf8();
557        dataUtf8 += dumpFrameScrollPosition(frame, recursive);
558    }
559
560    if (m_testInterfaces->testRunner()->shouldDumpBackForwardList())
561        dataUtf8 += dumpAllBackForwardLists(m_testInterfaces, m_delegate);
562
563    return dataUtf8;
564}
565
566SkCanvas* WebTestProxyBase::capturePixels()
567{
568    webWidget()->layout();
569    if (m_testInterfaces->testRunner()->testRepaint()) {
570        WebSize viewSize = webWidget()->size();
571        int width = viewSize.width;
572        int height = viewSize.height;
573        if (m_testInterfaces->testRunner()->sweepHorizontally()) {
574            for (WebRect column(0, 0, 1, height); column.x < width; column.x++)
575                paintRect(column);
576        } else {
577            for (WebRect line(0, 0, width, 1); line.y < height; line.y++)
578                paintRect(line);
579        }
580    } else if (m_testInterfaces->testRunner()->isPrinting())
581        paintPagesWithBoundaries();
582    else
583        paintInvalidatedRegion();
584
585    // See if we need to draw the selection bounds rect. Selection bounds
586    // rect is the rect enclosing the (possibly transformed) selection.
587    // The rect should be drawn after everything is laid out and painted.
588    if (m_testInterfaces->testRunner()->shouldDumpSelectionRect()) {
589        // If there is a selection rect - draw a red 1px border enclosing rect
590        WebRect wr = webView()->mainFrame()->selectionBoundsRect();
591        if (!wr.isEmpty()) {
592            // Render a red rectangle bounding selection rect
593            SkPaint paint;
594            paint.setColor(0xFFFF0000); // Fully opaque red
595            paint.setStyle(SkPaint::kStroke_Style);
596            paint.setFlags(SkPaint::kAntiAlias_Flag);
597            paint.setStrokeWidth(1.0f);
598            SkIRect rect; // Bounding rect
599            rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height);
600            canvas()->drawIRect(rect, paint);
601        }
602    }
603
604    return canvas();
605}
606
607void WebTestProxyBase::setLogConsoleOutput(bool enabled)
608{
609    m_logConsoleOutput = enabled;
610}
611
612void WebTestProxyBase::paintRect(const WebRect& rect)
613{
614    WEBKIT_ASSERT(!m_isPainting);
615    WEBKIT_ASSERT(canvas());
616    m_isPainting = true;
617    float deviceScaleFactor = webView()->deviceScaleFactor();
618    int scaledX = static_cast<int>(static_cast<float>(rect.x) * deviceScaleFactor);
619    int scaledY = static_cast<int>(static_cast<float>(rect.y) * deviceScaleFactor);
620    int scaledWidth = static_cast<int>(ceil(static_cast<float>(rect.width) * deviceScaleFactor));
621    int scaledHeight = static_cast<int>(ceil(static_cast<float>(rect.height) * deviceScaleFactor));
622    WebRect deviceRect(scaledX, scaledY, scaledWidth, scaledHeight);
623    webWidget()->paint(canvas(), deviceRect);
624    m_isPainting = false;
625}
626
627void WebTestProxyBase::paintInvalidatedRegion()
628{
629    webWidget()->animate(0.0);
630    webWidget()->layout();
631    WebSize widgetSize = webWidget()->size();
632    WebRect clientRect(0, 0, widgetSize.width, widgetSize.height);
633
634    // Paint the canvas if necessary. Allow painting to generate extra rects
635    // for the first two calls. This is necessary because some WebCore rendering
636    // objects update their layout only when painted.
637    // Store the total area painted in total_paint. Then tell the gdk window
638    // to update that area after we're done painting it.
639    for (int i = 0; i < 3; ++i) {
640        // rect = intersect(m_paintRect , clientRect)
641        WebRect damageRect = m_paintRect;
642        int left = max(damageRect.x, clientRect.x);
643        int top = max(damageRect.y, clientRect.y);
644        int right = min(damageRect.x + damageRect.width, clientRect.x + clientRect.width);
645        int bottom = min(damageRect.y + damageRect.height, clientRect.y + clientRect.height);
646        WebRect rect;
647        if (left < right && top < bottom)
648            rect = WebRect(left, top, right - left, bottom - top);
649
650        m_paintRect = WebRect();
651        if (rect.isEmpty())
652            continue;
653        paintRect(rect);
654    }
655    WEBKIT_ASSERT(m_paintRect.isEmpty());
656}
657
658void WebTestProxyBase::paintPagesWithBoundaries()
659{
660    WEBKIT_ASSERT(!m_isPainting);
661    WEBKIT_ASSERT(canvas());
662    m_isPainting = true;
663
664    WebSize pageSizeInPixels = webWidget()->size();
665    WebFrame* webFrame = webView()->mainFrame();
666
667    int pageCount = webFrame->printBegin(pageSizeInPixels);
668    int totalHeight = pageCount * (pageSizeInPixels.height + 1) - 1;
669
670    SkCanvas* testCanvas = skia::TryCreateBitmapCanvas(pageSizeInPixels.width, totalHeight, true);
671    if (testCanvas) {
672        discardBackingStore();
673        m_canvas.reset(testCanvas);
674    } else {
675        webFrame->printEnd();
676        return;
677    }
678
679    webFrame->printPagesWithBoundaries(canvas(), pageSizeInPixels);
680    webFrame->printEnd();
681
682    m_isPainting = false;
683}
684
685SkCanvas* WebTestProxyBase::canvas()
686{
687    if (m_canvas.get())
688        return m_canvas.get();
689    WebSize widgetSize = webWidget()->size();
690    float deviceScaleFactor = webView()->deviceScaleFactor();
691    int scaledWidth = static_cast<int>(ceil(static_cast<float>(widgetSize.width) * deviceScaleFactor));
692    int scaledHeight = static_cast<int>(ceil(static_cast<float>(widgetSize.height) * deviceScaleFactor));
693    m_canvas.reset(skia::CreateBitmapCanvas(scaledWidth, scaledHeight, true));
694    return m_canvas.get();
695}
696
697// Paints the entire canvas a semi-transparent black (grayish). This is used
698// by the layout tests in fast/repaint. The alpha value matches upstream.
699void WebTestProxyBase::displayRepaintMask()
700{
701    canvas()->drawARGB(167, 0, 0, 0);
702}
703
704void WebTestProxyBase::display()
705{
706    const WebKit::WebSize& size = webWidget()->size();
707    WebRect rect(0, 0, size.width, size.height);
708    m_paintRect = rect;
709    paintInvalidatedRegion();
710    displayRepaintMask();
711}
712
713void WebTestProxyBase::displayInvalidatedRegion()
714{
715    paintInvalidatedRegion();
716    displayRepaintMask();
717}
718
719void WebTestProxyBase::discardBackingStore()
720{
721    m_canvas.reset();
722}
723
724WebGeolocationClientMock* WebTestProxyBase::geolocationClientMock()
725{
726    if (!m_geolocationClient.get())
727        m_geolocationClient.reset(WebGeolocationClientMock::create());
728    return m_geolocationClient.get();
729}
730
731WebDeviceOrientationClientMock* WebTestProxyBase::deviceOrientationClientMock()
732{
733    if (!m_deviceOrientationClient.get())
734        m_deviceOrientationClient.reset(WebDeviceOrientationClientMock::create());
735    return m_deviceOrientationClient.get();
736}
737
738#if ENABLE_INPUT_SPEECH
739MockWebSpeechInputController* WebTestProxyBase::speechInputControllerMock()
740{
741    WEBKIT_ASSERT(m_speechInputController.get());
742    return m_speechInputController.get();
743}
744#endif
745
746MockWebSpeechRecognizer* WebTestProxyBase::speechRecognizerMock()
747{
748    if (!m_speechRecognizer.get()) {
749        m_speechRecognizer.reset(new MockWebSpeechRecognizer());
750        m_speechRecognizer->setDelegate(m_delegate);
751    }
752    return m_speechRecognizer.get();
753}
754
755void WebTestProxyBase::didInvalidateRect(const WebRect& rect)
756{
757    // m_paintRect = m_paintRect U rect
758    if (rect.isEmpty())
759        return;
760    if (m_paintRect.isEmpty()) {
761        m_paintRect = rect;
762        return;
763    }
764    int left = min(m_paintRect.x, rect.x);
765    int top = min(m_paintRect.y, rect.y);
766    int right = max(m_paintRect.x + m_paintRect.width, rect.x + rect.width);
767    int bottom = max(m_paintRect.y + m_paintRect.height, rect.y + rect.height);
768    m_paintRect = WebRect(left, top, right - left, bottom - top);
769}
770
771void WebTestProxyBase::didScrollRect(int, int, const WebRect& clipRect)
772{
773    didInvalidateRect(clipRect);
774}
775
776void WebTestProxyBase::invalidateAll()
777{
778    m_paintRect = WebRect(0, 0, INT_MAX, INT_MAX);
779}
780
781void WebTestProxyBase::scheduleComposite()
782{
783    invalidateAll();
784}
785
786void WebTestProxyBase::scheduleAnimation()
787{
788    if (!m_testInterfaces->testRunner()->testIsRunning())
789        return;
790
791    if (!m_animateScheduled) {
792        m_animateScheduled = true;
793        m_delegate->postDelayedTask(new HostMethodTask(this, &WebTestProxyBase::animateNow), 1);
794    }
795}
796
797void WebTestProxyBase::animateNow()
798{
799    if (m_animateScheduled) {
800        m_animateScheduled = false;
801        webWidget()->animate(0.0);
802    }
803}
804
805void WebTestProxyBase::show(WebNavigationPolicy)
806{
807    invalidateAll();
808}
809
810void WebTestProxyBase::setWindowRect(const WebRect& rect)
811{
812    invalidateAll();
813    discardBackingStore();
814}
815
816void WebTestProxyBase::didAutoResize(const WebSize&)
817{
818    invalidateAll();
819}
820
821void WebTestProxyBase::postAccessibilityNotification(const WebKit::WebAccessibilityObject& obj, WebKit::WebAccessibilityNotification notification)
822{
823    if (notification == WebKit::WebAccessibilityNotificationFocusedUIElementChanged)
824        m_testInterfaces->accessibilityController()->setFocusedElement(obj);
825
826    const char* notificationName;
827    switch (notification) {
828    case WebKit::WebAccessibilityNotificationActiveDescendantChanged:
829        notificationName = "ActiveDescendantChanged";
830        break;
831    case WebKit::WebAccessibilityNotificationAutocorrectionOccured:
832        notificationName = "AutocorrectionOccured";
833        break;
834    case WebKit::WebAccessibilityNotificationCheckedStateChanged:
835        notificationName = "CheckedStateChanged";
836        break;
837    case WebKit::WebAccessibilityNotificationChildrenChanged:
838        notificationName = "ChildrenChanged";
839        break;
840    case WebKit::WebAccessibilityNotificationFocusedUIElementChanged:
841        notificationName = "FocusedUIElementChanged";
842        break;
843    case WebKit::WebAccessibilityNotificationLayoutComplete:
844        notificationName = "LayoutComplete";
845        break;
846    case WebKit::WebAccessibilityNotificationLoadComplete:
847        notificationName = "LoadComplete";
848        break;
849    case WebKit::WebAccessibilityNotificationSelectedChildrenChanged:
850        notificationName = "SelectedChildrenChanged";
851        break;
852    case WebKit::WebAccessibilityNotificationSelectedTextChanged:
853        notificationName = "SelectedTextChanged";
854        break;
855    case WebKit::WebAccessibilityNotificationValueChanged:
856        notificationName = "ValueChanged";
857        break;
858    case WebKit::WebAccessibilityNotificationScrolledToAnchor:
859        notificationName = "ScrolledToAnchor";
860        break;
861    case WebKit::WebAccessibilityNotificationLiveRegionChanged:
862        notificationName = "LiveRegionChanged";
863        break;
864    case WebKit::WebAccessibilityNotificationMenuListItemSelected:
865        notificationName = "MenuListItemSelected";
866        break;
867    case WebKit::WebAccessibilityNotificationMenuListValueChanged:
868        notificationName = "MenuListValueChanged";
869        break;
870    case WebKit::WebAccessibilityNotificationRowCountChanged:
871        notificationName = "RowCountChanged";
872        break;
873    case WebKit::WebAccessibilityNotificationRowCollapsed:
874        notificationName = "RowCollapsed";
875        break;
876    case WebKit::WebAccessibilityNotificationRowExpanded:
877        notificationName = "RowExpanded";
878        break;
879    case WebKit::WebAccessibilityNotificationInvalidStatusChanged:
880        notificationName = "InvalidStatusChanged";
881        break;
882    case WebKit::WebAccessibilityNotificationTextChanged:
883        notificationName = "TextChanged";
884        break;
885    case WebKit::WebAccessibilityNotificationAriaAttributeChanged:
886        notificationName = "AriaAttributeChanged";
887        break;
888    default:
889        notificationName = "UnknownNotification";
890        break;
891    }
892
893    m_testInterfaces->accessibilityController()->notificationReceived(obj, notificationName);
894
895    if (m_testInterfaces->accessibilityController()->shouldLogAccessibilityEvents()) {
896        string message("AccessibilityNotification - ");
897        message += notificationName;
898
899        WebKit::WebNode node = obj.node();
900        if (!node.isNull() && node.isElementNode()) {
901            WebKit::WebElement element = node.to<WebKit::WebElement>();
902            if (element.hasAttribute("id")) {
903                message += " - id:";
904                message += element.getAttribute("id").utf8().data();
905            }
906        }
907
908        m_delegate->printMessage(message + "\n");
909    }
910}
911
912void WebTestProxyBase::startDragging(WebFrame*, const WebDragData& data, WebDragOperationsMask mask, const WebImage&, const WebPoint&)
913{
914    // When running a test, we need to fake a drag drop operation otherwise
915    // Windows waits for real mouse events to know when the drag is over.
916    m_testInterfaces->eventSender()->doDragDrop(data, mask);
917}
918
919// The output from these methods in layout test mode should match that
920// expected by the layout tests. See EditingDelegate.m in DumpRenderTree.
921
922bool WebTestProxyBase::shouldBeginEditing(const WebRange& range)
923{
924    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) {
925        m_delegate->printMessage("EDITING DELEGATE: shouldBeginEditingInDOMRange:");
926        printRangeDescription(m_delegate, range);
927        m_delegate->printMessage("\n");
928    }
929    return true;
930}
931
932bool WebTestProxyBase::shouldEndEditing(const WebRange& range)
933{
934    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) {
935        m_delegate->printMessage("EDITING DELEGATE: shouldEndEditingInDOMRange:");
936        printRangeDescription(m_delegate, range);
937        m_delegate->printMessage("\n");
938    }
939    return true;
940}
941
942bool WebTestProxyBase::shouldInsertNode(const WebNode& node, const WebRange& range, WebEditingAction action)
943{
944    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) {
945        m_delegate->printMessage("EDITING DELEGATE: shouldInsertNode:");
946        printNodeDescription(m_delegate, node, 0);
947        m_delegate->printMessage(" replacingDOMRange:");
948        printRangeDescription(m_delegate, range);
949        m_delegate->printMessage(string(" givenAction:") + editingActionDescription(action) + "\n");
950    }
951    return true;
952}
953
954bool WebTestProxyBase::shouldInsertText(const WebString& text, const WebRange& range, WebEditingAction action)
955{
956    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) {
957        m_delegate->printMessage(string("EDITING DELEGATE: shouldInsertText:") + text.utf8().data() + " replacingDOMRange:");
958        printRangeDescription(m_delegate, range);
959        m_delegate->printMessage(string(" givenAction:") + editingActionDescription(action) + "\n");
960    }
961    return true;
962}
963
964bool WebTestProxyBase::shouldChangeSelectedRange(
965    const WebRange& fromRange, const WebRange& toRange, WebTextAffinity affinity, bool stillSelecting)
966{
967    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) {
968        m_delegate->printMessage("EDITING DELEGATE: shouldChangeSelectedDOMRange:");
969        printRangeDescription(m_delegate, fromRange);
970        m_delegate->printMessage(" toDOMRange:");
971        printRangeDescription(m_delegate, toRange);
972        m_delegate->printMessage(string(" affinity:") + textAffinityDescription(affinity) + " stillSelecting:" + (stillSelecting ? "TRUE" : "FALSE") + "\n");
973    }
974    return true;
975}
976
977bool WebTestProxyBase::shouldDeleteRange(const WebRange& range)
978{
979    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) {
980        m_delegate->printMessage("EDITING DELEGATE: shouldDeleteDOMRange:");
981        printRangeDescription(m_delegate, range);
982        m_delegate->printMessage("\n");
983    }
984    return true;
985}
986
987bool WebTestProxyBase::shouldApplyStyle(const WebString& style, const WebRange& range)
988{
989    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks()) {
990        m_delegate->printMessage(string("EDITING DELEGATE: shouldApplyStyle:") + style.utf8().data() + " toElementsInDOMRange:");
991        printRangeDescription(m_delegate, range);
992        m_delegate->printMessage("\n");
993    }
994    return true;
995}
996
997void WebTestProxyBase::didBeginEditing()
998{
999    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks())
1000        m_delegate->printMessage("EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification\n");
1001}
1002
1003void WebTestProxyBase::didChangeSelection(bool isEmptySelection)
1004{
1005    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks())
1006        m_delegate->printMessage("EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n");
1007}
1008
1009void WebTestProxyBase::didChangeContents()
1010{
1011    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks())
1012        m_delegate->printMessage("EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n");
1013}
1014
1015void WebTestProxyBase::didEndEditing()
1016{
1017    if (m_testInterfaces->testRunner()->shouldDumpEditingCallbacks())
1018        m_delegate->printMessage("EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification\n");
1019}
1020
1021bool WebTestProxyBase::createView(WebFrame*, const WebURLRequest& request, const WebWindowFeatures&, const WebString&, WebNavigationPolicy)
1022{
1023    if (!m_testInterfaces->testRunner()->canOpenWindows())
1024        return false;
1025    if (m_testInterfaces->testRunner()->shouldDumpCreateView())
1026        m_delegate->printMessage(string("createView(") + URLDescription(request.url()) + ")\n");
1027    return true;
1028}
1029
1030WebPlugin* WebTestProxyBase::createPlugin(WebFrame* frame, const WebPluginParams& params)
1031{
1032    if (params.mimeType == TestPlugin::mimeType())
1033        return TestPlugin::create(frame, params, m_delegate);
1034    return 0;
1035}
1036
1037void WebTestProxyBase::setStatusText(const WebString& text)
1038{
1039    if (!m_testInterfaces->testRunner()->shouldDumpStatusCallbacks())
1040        return;
1041    m_delegate->printMessage(string("UI DELEGATE STATUS CALLBACK: setStatusText:") + text.utf8().data() + "\n");
1042}
1043
1044void WebTestProxyBase::didStopLoading()
1045{
1046    if (m_testInterfaces->testRunner()->shouldDumpProgressFinishedCallback())
1047        m_delegate->printMessage("postProgressFinishedNotification\n");
1048}
1049
1050void WebTestProxyBase::showContextMenu(WebFrame*, const WebContextMenuData& contextMenuData)
1051{
1052    m_testInterfaces->eventSender()->setContextMenuData(contextMenuData);
1053}
1054
1055WebUserMediaClient* WebTestProxyBase::userMediaClient()
1056{
1057    if (!m_userMediaClient.get())
1058        m_userMediaClient.reset(new WebUserMediaClientMock(m_delegate));
1059    return m_userMediaClient.get();
1060}
1061
1062// Simulate a print by going into print mode and then exit straight away.
1063void WebTestProxyBase::printPage(WebFrame* frame)
1064{
1065    WebSize pageSizeInPixels = webWidget()->size();
1066    if (pageSizeInPixels.isEmpty())
1067        return;
1068    WebPrintParams printParams(pageSizeInPixels);
1069    frame->printBegin(printParams);
1070    frame->printEnd();
1071}
1072
1073WebNotificationPresenter* WebTestProxyBase::notificationPresenter()
1074{
1075#if ENABLE_NOTIFICATIONS
1076    return m_testInterfaces->testRunner()->notificationPresenter();
1077#else
1078    return 0;
1079#endif
1080}
1081
1082WebGeolocationClient* WebTestProxyBase::geolocationClient()
1083{
1084    return geolocationClientMock();
1085}
1086
1087WebSpeechInputController* WebTestProxyBase::speechInputController(WebSpeechInputListener* listener)
1088{
1089#if ENABLE_INPUT_SPEECH
1090    if (!m_speechInputController.get()) {
1091        m_speechInputController.reset(new MockWebSpeechInputController(listener));
1092        m_speechInputController->setDelegate(m_delegate);
1093    }
1094    return m_speechInputController.get();
1095#else
1096    WEBKIT_ASSERT(listener);
1097    return 0;
1098#endif
1099}
1100
1101WebSpeechRecognizer* WebTestProxyBase::speechRecognizer()
1102{
1103    return speechRecognizerMock();
1104}
1105
1106WebDeviceOrientationClient* WebTestProxyBase::deviceOrientationClient()
1107{
1108    return deviceOrientationClientMock();
1109}
1110
1111bool WebTestProxyBase::requestPointerLock()
1112{
1113    return m_testInterfaces->testRunner()->requestPointerLock();
1114}
1115
1116void WebTestProxyBase::requestPointerUnlock()
1117{
1118    m_testInterfaces->testRunner()->requestPointerUnlock();
1119}
1120
1121bool WebTestProxyBase::isPointerLocked()
1122{
1123    return m_testInterfaces->testRunner()->isPointerLocked();
1124}
1125
1126void WebTestProxyBase::didFocus()
1127{
1128    m_delegate->setFocus(this, true);
1129}
1130
1131void WebTestProxyBase::didBlur()
1132{
1133    m_delegate->setFocus(this, false);
1134}
1135
1136void WebTestProxyBase::setToolTipText(const WebString& text, WebTextDirection)
1137{
1138    m_testInterfaces->testRunner()->setToolTipText(text);
1139}
1140
1141void WebTestProxyBase::didOpenChooser()
1142{
1143    m_chooserCount++;
1144}
1145
1146void WebTestProxyBase::didCloseChooser()
1147{
1148    m_chooserCount--;
1149}
1150
1151bool WebTestProxyBase::isChooserShown()
1152{
1153    return 0 < m_chooserCount;
1154}
1155
1156void WebTestProxyBase::didStartProvisionalLoad(WebFrame* frame)
1157{
1158    if (!m_testInterfaces->testRunner()->topLoadingFrame())
1159        m_testInterfaces->testRunner()->setTopLoadingFrame(frame, false);
1160
1161    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1162        printFrameDescription(m_delegate, frame);
1163        m_delegate->printMessage(" - didStartProvisionalLoadForFrame\n");
1164    }
1165
1166    if (m_testInterfaces->testRunner()->shouldDumpUserGestureInFrameLoadCallbacks())
1167        printFrameUserGestureStatus(m_delegate, frame, " - in didStartProvisionalLoadForFrame\n");
1168}
1169
1170void WebTestProxyBase::didReceiveServerRedirectForProvisionalLoad(WebFrame* frame)
1171{
1172    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1173        printFrameDescription(m_delegate, frame);
1174        m_delegate->printMessage(" - didReceiveServerRedirectForProvisionalLoadForFrame\n");
1175    }
1176}
1177
1178bool WebTestProxyBase::didFailProvisionalLoad(WebFrame* frame, const WebURLError&)
1179{
1180    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1181        printFrameDescription(m_delegate, frame);
1182        m_delegate->printMessage(" - didFailProvisionalLoadWithError\n");
1183    }
1184    locationChangeDone(frame);
1185    return !frame->provisionalDataSource();
1186}
1187
1188void WebTestProxyBase::didCommitProvisionalLoad(WebFrame* frame, bool)
1189{
1190    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1191        printFrameDescription(m_delegate, frame);
1192        m_delegate->printMessage(" - didCommitLoadForFrame\n");
1193    }
1194}
1195
1196void WebTestProxyBase::didReceiveTitle(WebFrame* frame, const WebString& title, WebTextDirection direction)
1197{
1198    WebCString title8 = title.utf8();
1199
1200    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1201        printFrameDescription(m_delegate, frame);
1202        m_delegate->printMessage(string(" - didReceiveTitle: ") + title8.data() + "\n");
1203    }
1204
1205    if (m_testInterfaces->testRunner()->shouldDumpTitleChanges())
1206        m_delegate->printMessage(string("TITLE CHANGED: '") + title8.data() + "'\n");
1207
1208    m_testInterfaces->testRunner()->setTitleTextDirection(direction);
1209}
1210
1211void WebTestProxyBase::didChangeIcon(WebFrame* frame, WebIconURL::Type)
1212{
1213    if (m_testInterfaces->testRunner()->shouldDumpIconChanges()) {
1214        printFrameDescription(m_delegate, frame);
1215        m_delegate->printMessage(string(" - didChangeIcons\n"));
1216    }
1217}
1218
1219void WebTestProxyBase::didFinishDocumentLoad(WebFrame* frame)
1220{
1221    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1222        printFrameDescription(m_delegate, frame);
1223        m_delegate->printMessage(" - didFinishDocumentLoadForFrame\n");
1224    } else {
1225        unsigned pendingUnloadEvents = frame->unloadListenerCount();
1226        if (pendingUnloadEvents) {
1227            printFrameDescription(m_delegate, frame);
1228            char buffer[100];
1229            snprintf(buffer, sizeof(buffer), " - has %u onunload handler(s)\n", pendingUnloadEvents);
1230            m_delegate->printMessage(buffer);
1231        }
1232    }
1233}
1234
1235void WebTestProxyBase::didHandleOnloadEvents(WebFrame* frame)
1236{
1237    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1238        printFrameDescription(m_delegate, frame);
1239        m_delegate->printMessage(" - didHandleOnloadEventsForFrame\n");
1240    }
1241}
1242
1243void WebTestProxyBase::didFailLoad(WebFrame* frame, const WebURLError&)
1244{
1245    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1246        printFrameDescription(m_delegate, frame);
1247        m_delegate->printMessage(" - didFailLoadWithError\n");
1248    }
1249    locationChangeDone(frame);
1250}
1251
1252void WebTestProxyBase::didFinishLoad(WebFrame* frame)
1253{
1254    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks()) {
1255        printFrameDescription(m_delegate, frame);
1256        m_delegate->printMessage(" - didFinishLoadForFrame\n");
1257    }
1258    locationChangeDone(frame);
1259}
1260
1261void WebTestProxyBase::didDetectXSS(WebFrame*, const WebURL&, bool)
1262{
1263    if (m_testInterfaces->testRunner()->shouldDumpFrameLoadCallbacks())
1264        m_delegate->printMessage("didDetectXSS\n");
1265}
1266
1267void WebTestProxyBase::didDispatchPingLoader(WebFrame*, const WebURL& url)
1268{
1269    if (m_testInterfaces->testRunner()->shouldDumpPingLoaderCallbacks())
1270        m_delegate->printMessage(string("PingLoader dispatched to '") + URLDescription(url).c_str() + "'.\n");
1271}
1272
1273void WebTestProxyBase::willRequestResource(WebFrame* frame, const WebKit::WebCachedURLRequest& request)
1274{
1275    if (m_testInterfaces->testRunner()->shouldDumpResourceRequestCallbacks()) {
1276        printFrameDescription(m_delegate, frame);
1277        m_delegate->printMessage(string(" - ") + request.initiatorName().utf8().data());
1278        m_delegate->printMessage(string(" requested '") + URLDescription(request.urlRequest().url()).c_str() + "'\n");
1279    }
1280}
1281
1282void WebTestProxyBase::didCreateDataSource(WebFrame*, WebDataSource* ds)
1283{
1284    if (!m_testInterfaces->testRunner()->deferMainResourceDataLoad())
1285        ds->setDeferMainResourceDataLoad(false);
1286}
1287
1288void WebTestProxyBase::willSendRequest(WebFrame*, unsigned identifier, WebKit::WebURLRequest& request, const WebKit::WebURLResponse& redirectResponse)
1289{
1290    // Need to use GURL for host() and SchemeIs()
1291    GURL url = request.url();
1292    string requestURL = url.possibly_invalid_spec();
1293
1294    GURL mainDocumentURL = request.firstPartyForCookies();
1295
1296    if (redirectResponse.isNull() && (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks() || m_testInterfaces->testRunner()->shouldDumpResourcePriorities())) {
1297        WEBKIT_ASSERT(m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end());
1298        m_resourceIdentifierMap[identifier] = descriptionSuitableForTestResult(requestURL);
1299    }
1300
1301    if (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks()) {
1302        if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end())
1303            m_delegate->printMessage("<unknown>");
1304        else
1305            m_delegate->printMessage(m_resourceIdentifierMap[identifier]);
1306        m_delegate->printMessage(" - willSendRequest <NSURLRequest URL ");
1307        m_delegate->printMessage(descriptionSuitableForTestResult(requestURL).c_str());
1308        m_delegate->printMessage(", main document URL ");
1309        m_delegate->printMessage(URLDescription(mainDocumentURL).c_str());
1310        m_delegate->printMessage(", http method ");
1311        m_delegate->printMessage(request.httpMethod().utf8().data());
1312        m_delegate->printMessage("> redirectResponse ");
1313        printResponseDescription(m_delegate, redirectResponse);
1314        m_delegate->printMessage("\n");
1315    }
1316
1317    if (m_testInterfaces->testRunner()->shouldDumpResourcePriorities()) {
1318        m_delegate->printMessage(descriptionSuitableForTestResult(requestURL).c_str());
1319        m_delegate->printMessage(" has priority ");
1320        m_delegate->printMessage(PriorityDescription(request.priority()));
1321        m_delegate->printMessage("\n");
1322    }
1323
1324    if (m_testInterfaces->testRunner()->httpHeadersToClear()) {
1325        const set<string> *clearHeaders = m_testInterfaces->testRunner()->httpHeadersToClear();
1326        for (set<string>::const_iterator header = clearHeaders->begin(); header != clearHeaders->end(); ++header)
1327            request.clearHTTPHeaderField(WebString::fromUTF8(*header));
1328    }
1329
1330    string host = url.host();
1331    if (!host.empty() && (url.SchemeIs("http") || url.SchemeIs("https"))) {
1332        if (!isLocalhost(host) && !hostIsUsedBySomeTestsToGenerateError(host)
1333            && ((!mainDocumentURL.SchemeIs("http") && !mainDocumentURL.SchemeIs("https")) || isLocalhost(mainDocumentURL.host()))
1334            && !m_delegate->allowExternalPages()) {
1335            m_delegate->printMessage(string("Blocked access to external URL ") + requestURL + "\n");
1336            blockRequest(request);
1337            return;
1338        }
1339    }
1340
1341    // Set the new substituted URL.
1342    request.setURL(m_delegate->rewriteLayoutTestsURL(request.url().spec()));
1343}
1344
1345void WebTestProxyBase::didReceiveResponse(WebFrame*, unsigned identifier, const WebKit::WebURLResponse& response)
1346{
1347    if (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks()) {
1348        if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end())
1349            m_delegate->printMessage("<unknown>");
1350        else
1351            m_delegate->printMessage(m_resourceIdentifierMap[identifier]);
1352        m_delegate->printMessage(" - didReceiveResponse ");
1353        printResponseDescription(m_delegate, response);
1354        m_delegate->printMessage("\n");
1355    }
1356    if (m_testInterfaces->testRunner()->shouldDumpResourceResponseMIMETypes()) {
1357        GURL url = response.url();
1358        WebString mimeType = response.mimeType();
1359        m_delegate->printMessage(url.ExtractFileName());
1360        m_delegate->printMessage(" has MIME type ");
1361        // Simulate NSURLResponse's mapping of empty/unknown MIME types to application/octet-stream
1362        m_delegate->printMessage(mimeType.isEmpty() ? "application/octet-stream" : mimeType.utf8().data());
1363        m_delegate->printMessage("\n");
1364    }
1365}
1366
1367void WebTestProxyBase::didChangeResourcePriority(WebFrame*, unsigned identifier, const WebKit::WebURLRequest::Priority& priority)
1368{
1369    if (m_testInterfaces->testRunner()->shouldDumpResourcePriorities()) {
1370        if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end())
1371            m_delegate->printMessage("<unknown>");
1372        else
1373            m_delegate->printMessage(m_resourceIdentifierMap[identifier]);
1374        m_delegate->printMessage(" changed priority to ");
1375        m_delegate->printMessage(PriorityDescription(priority));
1376        m_delegate->printMessage("\n");
1377    }
1378}
1379
1380void WebTestProxyBase::didFinishResourceLoad(WebFrame*, unsigned identifier)
1381{
1382    if (m_testInterfaces->testRunner()->shouldDumpResourceLoadCallbacks()) {
1383        if (m_resourceIdentifierMap.find(identifier) == m_resourceIdentifierMap.end())
1384            m_delegate->printMessage("<unknown>");
1385        else
1386            m_delegate->printMessage(m_resourceIdentifierMap[identifier]);
1387        m_delegate->printMessage(" - didFinishLoading\n");
1388    }
1389    m_resourceIdentifierMap.erase(identifier);
1390}
1391
1392void WebTestProxyBase::didAddMessageToConsole(const WebConsoleMessage& message, const WebString& sourceName, unsigned sourceLine)
1393{
1394    // This matches win DumpRenderTree's UIDelegate.cpp.
1395    if (!m_logConsoleOutput)
1396        return;
1397    m_delegate->printMessage(string("CONSOLE MESSAGE: "));
1398    if (sourceLine) {
1399        char buffer[40];
1400        snprintf(buffer, sizeof(buffer), "line %d: ", sourceLine);
1401        m_delegate->printMessage(buffer);
1402    }
1403    if (!message.text.isEmpty()) {
1404        string newMessage;
1405        newMessage = message.text.utf8();
1406        size_t fileProtocol = newMessage.find("file://");
1407        if (fileProtocol != string::npos) {
1408            newMessage = newMessage.substr(0, fileProtocol)
1409                + urlSuitableForTestResult(newMessage.substr(fileProtocol));
1410        }
1411        m_delegate->printMessage(newMessage);
1412    }
1413    m_delegate->printMessage(string("\n"));
1414}
1415
1416void WebTestProxyBase::runModalAlertDialog(WebFrame*, const WebString& message)
1417{
1418    m_delegate->printMessage(string("ALERT: ") + message.utf8().data() + "\n");
1419}
1420
1421bool WebTestProxyBase::runModalConfirmDialog(WebFrame*, const WebString& message)
1422{
1423    m_delegate->printMessage(string("CONFIRM: ") + message.utf8().data() + "\n");
1424    return true;
1425}
1426
1427bool WebTestProxyBase::runModalPromptDialog(WebFrame* frame, const WebString& message, const WebString& defaultValue, WebString*)
1428{
1429    m_delegate->printMessage(string("PROMPT: ") + message.utf8().data() + ", default text: " + defaultValue.utf8().data() + "\n");
1430    return true;
1431}
1432
1433bool WebTestProxyBase::runModalBeforeUnloadDialog(WebFrame*, const WebString& message)
1434{
1435    m_delegate->printMessage(string("CONFIRM NAVIGATION: ") + message.utf8().data() + "\n");
1436    return !m_testInterfaces->testRunner()->shouldStayOnPageAfterHandlingBeforeUnload();
1437}
1438
1439void WebTestProxyBase::locationChangeDone(WebFrame* frame)
1440{
1441    if (frame != m_testInterfaces->testRunner()->topLoadingFrame())
1442        return;
1443    m_testInterfaces->testRunner()->setTopLoadingFrame(frame, true);
1444}
1445
1446WebNavigationPolicy WebTestProxyBase::decidePolicyForNavigation(WebFrame*, WebDataSource::ExtraData*, const WebURLRequest& request, WebNavigationType type, WebNavigationPolicy defaultPolicy, bool isRedirect)
1447{
1448    WebNavigationPolicy result;
1449    if (!m_testInterfaces->testRunner()->policyDelegateEnabled())
1450        return defaultPolicy;
1451
1452    m_delegate->printMessage(string("Policy delegate: attempt to load ") + URLDescription(request.url()) + " with navigation type '" + webNavigationTypeToString(type) + "'\n");
1453    if (m_testInterfaces->testRunner()->policyDelegateIsPermissive())
1454        result = WebKit::WebNavigationPolicyCurrentTab;
1455    else
1456        result = WebKit::WebNavigationPolicyIgnore;
1457
1458    if (m_testInterfaces->testRunner()->policyDelegateShouldNotifyDone())
1459        m_testInterfaces->testRunner()->policyDelegateDone();
1460    return result;
1461}
1462
1463bool WebTestProxyBase::willCheckAndDispatchMessageEvent(WebFrame*, WebFrame*, WebSecurityOrigin, WebDOMMessageEvent)
1464{
1465    if (m_testInterfaces->testRunner()->shouldInterceptPostMessage()) {
1466        m_delegate->printMessage("intercepted postMessage\n");
1467        return true;
1468    }
1469
1470    return false;
1471}
1472
1473void WebTestProxyBase::resetInputMethod()
1474{
1475    // If a composition text exists, then we need to let the browser process
1476    // to cancel the input method's ongoing composition session.
1477    if (m_webWidget)
1478        m_webWidget->confirmComposition();
1479}
1480
1481}
1482