2011-02-07 Maciej Stachowiak <mjs@apple.com>
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / InjectedBundlePage.cpp
1 /*
2  * Copyright (C) 2010 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "InjectedBundlePage.h"
27
28 #include "InjectedBundle.h"
29 #include "StringFunctions.h"
30 #include <cmath>
31 #include <JavaScriptCore/JSRetainPtr.h>
32 #include <WebCore/KURL.h>
33 #include <WebKit2/WKArray.h>
34 #include <WebKit2/WKBundle.h>
35 #include <WebKit2/WKBundleBackForwardList.h>
36 #include <WebKit2/WKBundleBackForwardListItem.h>
37 #include <WebKit2/WKBundleFrame.h>
38 #include <WebKit2/WKBundleFramePrivate.h>
39 #include <WebKit2/WKBundlePagePrivate.h>
40 #include <WebKit2/WKURLRequest.h>
41
42 using namespace std;
43
44 namespace WTR {
45
46 template<typename T> static inline WKRetainPtr<T> adoptWK(T item)
47 {
48     return WKRetainPtr<T>(AdoptWK, item);
49 }
50
51 static bool hasPrefix(const string& searchString, const string& prefix)
52 {
53     return searchString.length() >= prefix.length() && searchString.substr(0, prefix.length()) == prefix;
54 }
55
56 static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
57 {
58     if (!object)
59         return 0;
60     JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
61     return JSObjectGetProperty(context, object, propertyNameString.get(), 0);
62 }
63
64 static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName)
65 {
66     JSValueRef value = propertyValue(context, object, propertyName);
67     if (!value)
68         return 0;
69     return JSValueToNumber(context, value, 0);    
70 }
71
72 static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName)
73 {
74     return static_cast<int>(propertyValueDouble(context, object, propertyName));    
75 }
76
77 static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName)
78 {
79     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
80     return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName);
81 }
82
83 static string dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
84 {
85     JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName");
86     JSRetainPtr<JSStringRef> jsStringNodeName(Adopt, JSValueToStringCopy(context, nodeNameValue, 0));
87     WKRetainPtr<WKStringRef> nodeName = toWK(jsStringNodeName);
88
89     JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode");
90
91     ostringstream out;
92     out << nodeName;
93
94     if (parentNode && JSValueIsObject(context, parentNode))
95         out << " > " << dumpPath(context, (JSObjectRef)parentNode);
96
97     return out.str();
98 }
99
100 static string dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
101 {
102     if (!node)
103         return "(null)";
104
105     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
106
107     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
108     JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world);
109     ASSERT(JSValueIsObject(context, nodeValue));
110     JSObjectRef nodeObject = (JSObjectRef)nodeValue;
111
112     return dumpPath(context, nodeObject);
113 }
114
115 static string toStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
116 {
117     if (!rangeRef)
118         return "(null)";
119
120     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
121
122     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
123     JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world);
124     ASSERT(JSValueIsObject(context, rangeValue));
125     JSObjectRef rangeObject = (JSObjectRef)rangeValue;
126
127     JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer");
128     ASSERT(JSValueIsObject(context, startNodeValue));
129     JSObjectRef startNodeObject = (JSObjectRef)startNodeValue;
130
131     JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer");
132     ASSERT(JSValueIsObject(context, endNodeValue));
133     JSObjectRef endNodeObject = (JSObjectRef)endNodeValue;
134
135     int startOffset = propertyValueInt(context, rangeObject, "startOffset");
136     int endOffset = propertyValueInt(context, rangeObject, "endOffset");
137
138     ostringstream out;
139     out << "range from " << startOffset << " of " << dumpPath(context, startNodeObject) << " to " << endOffset << " of " << dumpPath(context, endNodeObject);
140     return out.str();
141 }
142
143 static ostream& operator<<(ostream& out, WKBundleCSSStyleDeclarationRef style)
144 {
145     // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
146     // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
147     // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
148     out << "<DOMCSSStyleDeclaration ADDRESS>";
149     return out;
150 }
151
152 static ostream& operator<<(ostream& out, WKBundleFrameRef frame)
153 {
154     WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
155     if (WKBundleFrameIsMainFrame(frame)) {
156         if (!WKStringIsEmpty(name.get()))
157             out << "main frame \"" << name << "\"";
158         else
159             out << "main frame";
160     } else {
161         if (!WKStringIsEmpty(name.get()))
162             out << "frame \"" << name << "\"";
163         else
164             out << "frame (anonymous)";
165     }
166
167     return out;
168 }
169
170 InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
171     : m_page(page)
172     , m_world(AdoptWK, WKBundleScriptWorldCreateWorld())
173 {
174     WKBundlePageLoaderClient loaderClient = {
175         0,
176         this,
177         didStartProvisionalLoadForFrame,
178         didReceiveServerRedirectForProvisionalLoadForFrame,
179         didFailProvisionalLoadWithErrorForFrame,
180         didCommitLoadForFrame,
181         didFinishDocumentLoadForFrame,
182         didFinishLoadForFrame,
183         didFailLoadWithErrorForFrame,
184         didSameDocumentNavigationForFrame,
185         didReceiveTitleForFrame,
186         0,
187         0,
188         0,
189         didDisplayInsecureContentForFrame,
190         didRunInsecureContentForFrame,
191         didClearWindowForFrame,
192         didCancelClientRedirectForFrame,
193         willPerformClientRedirectForFrame,
194         didHandleOnloadEventsForFrame,
195     };
196     WKBundlePageSetPageLoaderClient(m_page, &loaderClient);
197
198     WKBundlePageResourceLoadClient resourceLoadClient = {
199         0,
200         this,
201         didInitiateLoadForResource,
202         willSendRequestForFrame,
203         didReceiveResponseForResource,
204         didReceiveContentLengthForResource,
205         didFinishLoadForResource,
206         didFailLoadForResource
207     };
208     WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient);
209
210     WKBundlePageUIClient uiClient = {
211         0,
212         this,
213         willAddMessageToConsole,
214         willSetStatusbarText,
215         willRunJavaScriptAlert,
216         willRunJavaScriptConfirm,
217         willRunJavaScriptPrompt,
218         0, /*mouseDidMoveOverElement*/
219         0, /*pageDidScroll*/
220         0, /*paintCustomOverhangArea*/
221     };
222     WKBundlePageSetUIClient(m_page, &uiClient);
223
224     WKBundlePageEditorClient editorClient = {
225         0,
226         this,
227         shouldBeginEditing,
228         shouldEndEditing,
229         shouldInsertNode,
230         shouldInsertText,
231         shouldDeleteRange,
232         shouldChangeSelectedRange,
233         shouldApplyStyle,
234         didBeginEditing,
235         didEndEditing,
236         didChange,
237         didChangeSelection
238     };
239     WKBundlePageSetEditorClient(m_page, &editorClient);
240 }
241
242 InjectedBundlePage::~InjectedBundlePage()
243 {
244 }
245
246 void InjectedBundlePage::stopLoading()
247 {
248     WKBundlePageStopLoading(m_page);
249 }
250
251 void InjectedBundlePage::reset()
252 {
253     WKBundlePageClearMainFrameName(m_page);
254
255     WKBundlePageSetPageZoomFactor(m_page, 1);
256     WKBundlePageSetTextZoomFactor(m_page, 1);
257
258     m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
259 }
260
261 // Loader Client Callbacks
262
263 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
264 {
265     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
266 }
267
268 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
269 {
270     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
271 }
272
273 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
274 {
275     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
276 }
277
278 void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
279 {
280     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
281 }
282
283 void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
284 {
285     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
286 }
287
288 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
289 {
290     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
291 }
292
293 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
294 {
295     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
296 }
297
298 void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
299 {
300     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
301 }
302
303 void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
304 {
305     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
306 }
307
308 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
309 {
310     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
311 }
312
313 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
314 {
315     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(frame, url, delay, date);
316 }
317
318 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
319 {
320     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
321 }
322
323 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
324 {
325     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
326 }
327
328 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
329 {
330     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
331 }
332
333 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
334 {
335     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
336 }
337
338 void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void* clientInfo)
339 {
340     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didInitiateLoadForResource(page, frame, identifier, request, pageLoadIsProvisional);
341 }
342
343 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
344 {
345     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse);
346 }
347
348 void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLResponseRef response, const void* clientInfo)
349 {
350     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveResponseForResource(page, frame, identifier, response);
351 }
352
353 void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, uint64_t length, const void* clientInfo)
354 {
355     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveContentLengthForResource(page, frame, identifier, length);
356 }
357
358 void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
359 {
360     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier);
361 }
362
363 void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKErrorRef error, const void* clientInfo)
364 {
365     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier, error);
366 }
367
368 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
369 {
370     if (!InjectedBundle::shared().isTestRunning())
371         return;
372
373     if (InjectedBundle::shared().topLoadingFrame())
374         return;
375     InjectedBundle::shared().setTopLoadingFrame(frame);
376 }
377
378 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
379 {
380 }
381
382 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
383 {
384     if (!InjectedBundle::shared().isTestRunning())
385         return;
386
387     if (frame != InjectedBundle::shared().topLoadingFrame())
388         return;
389     InjectedBundle::shared().setTopLoadingFrame(0);
390
391     if (InjectedBundle::shared().layoutTestController()->waitToDump())
392         return;
393
394     InjectedBundle::shared().done();
395 }
396
397 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
398 {
399 }
400
401 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
402
403 static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
404 {
405     double x = numericWindowPropertyValue(frame, "pageXOffset");
406     double y = numericWindowPropertyValue(frame, "pageYOffset");
407     if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) {
408         if (shouldIncludeFrameName) {
409             WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
410             InjectedBundle::shared().os() << "frame '" << name << "' ";
411         }
412         InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n";
413     }
414 }
415
416 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame)
417 {
418     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
419     size_t size = WKArrayGetSize(childFrames.get());
420     for (size_t i = 0; i < size; ++i) {
421         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
422         dumpFrameScrollPosition(subframe, ShouldIncludeFrameName);
423         dumpDescendantFrameScrollPositions(subframe);
424     }
425 }
426
427 void InjectedBundlePage::dumpAllFrameScrollPositions()
428 {
429     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
430     dumpFrameScrollPosition(frame);
431     dumpDescendantFrameScrollPositions(frame);
432 }
433
434 static JSRetainPtr<JSStringRef> toJS(const char* string)
435 {
436     return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
437 }
438
439 static bool hasDocumentElement(WKBundleFrameRef frame)
440 {
441     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
442     JSObjectRef globalObject = JSContextGetGlobalObject(context);
443
444     JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
445     if (!documentValue)
446         return false;
447
448     ASSERT(JSValueIsObject(context, documentValue));
449     JSObjectRef document = JSValueToObject(context, documentValue, 0);
450
451     JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
452     if (!documentElementValue)
453         return false;
454
455     return JSValueToBoolean(context, documentElementValue);
456 }
457
458 static void dumpFrameText(WKBundleFrameRef frame)
459 {
460     // If the frame doesn't have a document element, its inner text will be an empty string, so
461     // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
462     // anything in this case, so we shouldn't either.
463     if (!hasDocumentElement(frame))
464         return;
465
466     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
467     InjectedBundle::shared().os() << text << "\n";
468 }
469
470 static void dumpDescendantFramesText(WKBundleFrameRef frame)
471 {
472     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
473     size_t size = WKArrayGetSize(childFrames.get());
474     for (size_t i = 0; i < size; ++i) {
475         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
476         WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
477         InjectedBundle::shared().os() << "\n--------\nFrame: '" << subframeName << "'\n--------\n";
478         dumpFrameText(subframe);
479         dumpDescendantFramesText(subframe);
480     }
481 }
482
483 void InjectedBundlePage::dumpAllFramesText()
484 {
485     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
486     dumpFrameText(frame);
487     dumpDescendantFramesText(frame);
488 }
489
490 void InjectedBundlePage::dump()
491 {
492     ASSERT(InjectedBundle::shared().isTestRunning());
493
494     InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdogTimer();
495
496     switch (InjectedBundle::shared().layoutTestController()->whatToDump()) {
497     case LayoutTestController::RenderTree: {
498         WKRetainPtr<WKStringRef> text(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page));
499         InjectedBundle::shared().os() << text;
500         break;
501     }
502     case LayoutTestController::MainFrameText:
503         dumpFrameText(WKBundlePageGetMainFrame(m_page));
504         break;
505     case LayoutTestController::AllFramesText:
506         dumpAllFramesText();
507         break;
508     }
509
510     if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions())
511         dumpAllFrameScrollPositions();
512     else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition())
513         dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page));
514
515     if (InjectedBundle::shared().layoutTestController()->shouldDumpBackForwardListsForAllWindows())
516         InjectedBundle::shared().dumpBackForwardListsForAllPages();
517
518     InjectedBundle::shared().done();
519 }
520
521 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
522 {
523     if (!InjectedBundle::shared().isTestRunning())
524         return;
525
526     if (frame != InjectedBundle::shared().topLoadingFrame())
527         return;
528     InjectedBundle::shared().setTopLoadingFrame(0);
529
530     if (InjectedBundle::shared().layoutTestController()->waitToDump())
531         return;
532
533     InjectedBundle::shared().page()->dump();
534 }
535
536 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
537 {
538     if (!InjectedBundle::shared().isTestRunning())
539         return;
540
541     if (frame != InjectedBundle::shared().topLoadingFrame())
542         return;
543     InjectedBundle::shared().setTopLoadingFrame(0);
544
545     if (InjectedBundle::shared().layoutTestController()->waitToDump())
546         return;
547
548     InjectedBundle::shared().done();
549 }
550
551 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
552 {
553     if (!InjectedBundle::shared().isTestRunning())
554         return;
555
556     if (!InjectedBundle::shared().layoutTestController()->shouldDumpTitleChanges())
557         return;
558
559     InjectedBundle::shared().os() << "TITLE CHANGED: " << title << "\n";
560 }
561
562 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
563 {
564     if (!InjectedBundle::shared().isTestRunning())
565         return;
566
567     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
568     JSObjectRef window = JSContextGetGlobalObject(context);
569
570     if (WKBundleScriptWorldNormalWorld() != world) {
571         JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, LayoutTestController::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
572         return;
573     }
574
575     JSValueRef exception = 0;
576     InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception);
577     InjectedBundle::shared().gcController()->makeWindowObject(context, window, &exception);
578     InjectedBundle::shared().eventSendingController()->makeWindowObject(context, window, &exception);
579 }
580
581 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
582 {
583 }
584
585 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundleFrameRef frame, WKURLRef url, double delay, double date)
586 {
587 }
588
589 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
590 {
591 }
592
593 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
594 {
595     if (!InjectedBundle::shared().isTestRunning())
596         return;
597
598     unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
599     if (pendingFrameUnloadEvents)
600         InjectedBundle::shared().os() << frame << " - has " << pendingFrameUnloadEvents << " onunload handler(s)\n";
601 }
602
603 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
604 {
605 }
606
607 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef frame)
608 {
609 }
610
611 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef frame)
612 {
613 }
614
615 void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef, bool)
616 {
617 }
618
619 // Resource Load Client Callbacks
620
621 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKURLRequestRef request, WKURLResponseRef)
622 {
623     if (InjectedBundle::shared().isTestRunning() && InjectedBundle::shared().layoutTestController()->willSendRequestReturnsNull())
624         return 0;
625
626     string urlString = toSTD(adoptWK(WKURLCopyString(adoptWK(WKURLRequestCopyURL(request)).get())));
627     WebCore::KURL url(WebCore::ParsedURLString, urlString.c_str());
628
629     if (!url.host().isEmpty()
630         && (equalIgnoringCase(url.protocol(), "http") || (equalIgnoringCase(url.protocol(), "https")))
631         && (url.host() != "127.0.0.1")
632         && (url.host() != "255.255.255.255") // used in some tests that expect to get back an error
633         && (!equalIgnoringCase(url.host(), "localhost"))) {
634         InjectedBundle::shared().os() << "Blocked access to external URL " << urlString << "\n";
635         return 0;
636     }
637
638     return request;
639 }
640
641 void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKURLResponseRef)
642 {
643 }
644
645 void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, uint64_t)
646 {
647 }
648
649 void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t)
650 {
651 }
652
653 void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKErrorRef)
654 {
655 }
656
657 // UI Client Callbacks
658
659 void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
660 {
661     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
662 }
663
664 void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
665 {
666     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
667 }
668
669 void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
670 {
671     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
672 }
673
674 void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
675 {
676     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
677 }
678
679 void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
680 {
681     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
682 }
683
684 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
685 {
686     if (!InjectedBundle::shared().isTestRunning())
687         return;
688
689     // FIXME: Strip file: urls.
690     InjectedBundle::shared().os() << "CONSOLE MESSAGE: line " << lineNumber << ": " << message << "\n";
691 }
692
693 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
694 {
695     if (!InjectedBundle::shared().isTestRunning())
696         return;
697
698     if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks())
699         return;
700
701     InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << statusbarText << "\n";
702 }
703
704 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
705 {
706     if (!InjectedBundle::shared().isTestRunning())
707         return;
708
709     InjectedBundle::shared().os() << "ALERT: " << message << "\n";
710 }
711
712 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
713 {
714     if (!InjectedBundle::shared().isTestRunning())
715         return;
716
717     InjectedBundle::shared().os() << "CONFIRM: " << message << "\n";
718 }
719
720 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
721 {
722     InjectedBundle::shared().os() << "PROMPT: " << message << ", default text: " << defaultValue <<  "\n";
723 }
724
725 // Editor Client Callbacks
726
727 bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
728 {
729     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
730 }
731
732 bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
733 {
734     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
735 }
736
737 bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
738 {
739     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
740 }
741
742 bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
743 {
744     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
745 }
746
747 bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
748 {
749     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
750 }
751
752 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
753 {
754     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
755 }
756
757 bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
758 {
759     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
760 }
761
762 void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
763 {
764     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
765 }
766
767 void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
768 {
769     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
770 }
771
772 void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
773 {
774     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
775 }
776
777 void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
778 {
779     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
780 }
781
782 bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
783 {
784     if (!InjectedBundle::shared().isTestRunning())
785         return true;
786
787     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
788         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldBeginEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
789     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
790 }
791
792 bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
793 {
794     if (!InjectedBundle::shared().isTestRunning())
795         return true;
796
797     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
798         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldEndEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
799     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
800 }
801
802 bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
803 {
804     if (!InjectedBundle::shared().isTestRunning())
805         return true;
806
807     static const char* insertactionstring[] = {
808         "WebViewInsertActionTyped",
809         "WebViewInsertActionPasted",
810         "WebViewInsertActionDropped",
811     };
812
813     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
814         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertNode:" << dumpPath(m_page, m_world.get(), node) << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
815     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
816 }
817
818 bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
819 {
820     if (!InjectedBundle::shared().isTestRunning())
821         return true;
822
823     static const char *insertactionstring[] = {
824         "WebViewInsertActionTyped",
825         "WebViewInsertActionPasted",
826         "WebViewInsertActionDropped",
827     };
828
829     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
830         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertText:" << text << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
831     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
832 }
833
834 bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
835 {
836     if (!InjectedBundle::shared().isTestRunning())
837         return true;
838
839     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
840         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldDeleteDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
841     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
842 }
843
844 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
845 {
846     if (!InjectedBundle::shared().isTestRunning())
847         return true;
848
849     static const char *affinitystring[] = {
850         "NSSelectionAffinityUpstream",
851         "NSSelectionAffinityDownstream"
852     };
853     static const char *boolstring[] = {
854         "FALSE",
855         "TRUE"
856     };
857
858     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
859         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldChangeSelectedDOMRange:" << toStr(m_page, m_world.get(), fromRange) << " toDOMRange:" << toStr(m_page, m_world.get(), toRange) << " affinity:" << affinitystring[affinity] << " stillSelecting:" << boolstring[stillSelecting] << "\n";
860     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
861 }
862
863 bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
864 {
865     if (!InjectedBundle::shared().isTestRunning())
866         return true;
867
868     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
869         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldApplyStyle:" << style << " toElementsInDOMRange:" << toStr(m_page, m_world.get(), range)  << "\n";
870     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
871 }
872
873 void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
874 {
875     if (!InjectedBundle::shared().isTestRunning())
876         return;
877
878     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
879         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidBeginEditing:" << notificationName << "\n";
880 }
881
882 void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
883 {
884     if (!InjectedBundle::shared().isTestRunning())
885         return;
886
887     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
888         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidEndEditing:" << notificationName << "\n";
889 }
890
891 void InjectedBundlePage::didChange(WKStringRef notificationName)
892 {
893     if (!InjectedBundle::shared().isTestRunning())
894         return;
895
896     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
897         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChange:" << notificationName << "\n";
898 }
899
900 void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
901 {
902     if (!InjectedBundle::shared().isTestRunning())
903         return;
904
905     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
906         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChangeSelection:" << notificationName << "\n";
907 }
908
909 static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
910 {
911     return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
912 }
913
914 static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem)
915 {
916     unsigned column = 0;
917     if (isCurrentItem) {
918         InjectedBundle::shared().os() << "curr->";
919         column = 6;
920     }
921     for (unsigned i = column; i < indent; i++)
922         InjectedBundle::shared().os() << ' ';
923
924     string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
925     if (hasPrefix(url, "file:")) {
926         string directoryName = "/LayoutTests/";
927         size_t start = url.find(directoryName);
928         if (start == string::npos)
929             start = 0;
930         else
931             start += directoryName.size();
932         InjectedBundle::shared().os() << "(file test):" << url.substr(start);
933     } else
934         InjectedBundle::shared().os() << url;
935
936     string target = toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
937     if (target.length())
938         InjectedBundle::shared().os() << " (in frame \"" << target << "\")";
939
940     // FIXME: Need WKBackForwardListItemIsTargetItem.
941     if (WKBundleBackForwardListItemIsTargetItem(item))
942         InjectedBundle::shared().os() << "  **nav target**";
943
944     InjectedBundle::shared().os() << '\n';
945
946     if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
947         // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
948         size_t size = WKArrayGetSize(kids.get());
949         Vector<WKBundleBackForwardListItemRef> sortedKids(size);
950         for (size_t i = 0; i < size; ++i)
951             sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
952         stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
953         for (size_t i = 0; i < size; ++i)
954             dumpBackForwardListItem(sortedKids[i], indent + 4, false);
955     }
956 }
957
958 void InjectedBundlePage::dumpBackForwardList()
959 {
960     InjectedBundle::shared().os() << "\n============== Back Forward List ==============\n";
961
962     WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
963
964     // Print out all items in the list after m_previousTestBackForwardListItem.
965     // Gather items from the end of the list, then print them out from oldest to newest.
966     Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
967     for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
968         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
969         // Something is wrong if the item from the last test is in the forward part of the list.
970         ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
971         itemsToPrint.append(item);
972     }
973
974     ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
975
976     itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
977
978     int currentItemIndex = itemsToPrint.size() - 1;
979
980     int backListCount = WKBundleBackForwardListGetBackListCount(list);
981     for (int i = -1; i >= -backListCount; --i) {
982         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
983         if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
984             break;
985         itemsToPrint.append(item);
986     }
987
988     for (int i = itemsToPrint.size() - 1; i >= 0; i--)
989         dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex);
990
991     InjectedBundle::shared().os() << "===============================================\n";
992 }
993
994 } // namespace WTR