parseHTMLInteger() should take a StringView in parameter
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / InjectedBundlePage.cpp
1 /*
2  * Copyright (C) 2010-2016 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 "config.h"
27 #include "InjectedBundlePage.h"
28
29 #include "InjectedBundle.h"
30 #include "StringFunctions.h"
31 #include "WebCoreTestSupport.h"
32 #include <cmath>
33 #include <JavaScriptCore/JSRetainPtr.h>
34 #include <WebKit/WKArray.h>
35 #include <WebKit/WKBundle.h>
36 #include <WebKit/WKBundleBackForwardList.h>
37 #include <WebKit/WKBundleBackForwardListItem.h>
38 #include <WebKit/WKBundleFrame.h>
39 #include <WebKit/WKBundleFramePrivate.h>
40 #include <WebKit/WKBundleHitTestResult.h>
41 #include <WebKit/WKBundleNavigationAction.h>
42 #include <WebKit/WKBundleNavigationActionPrivate.h>
43 #include <WebKit/WKBundleNodeHandlePrivate.h>
44 #include <WebKit/WKBundlePagePrivate.h>
45 #include <WebKit/WKBundlePrivate.h>
46 #include <WebKit/WKSecurityOriginRef.h>
47 #include <WebKit/WKURLRequest.h>
48 #include <wtf/HashMap.h>
49 #include <wtf/text/CString.h>
50 #include <wtf/text/StringBuilder.h>
51
52 #if USE(CF)
53 #include "WebArchiveDumpSupport.h"
54 #endif
55
56 using namespace std;
57
58 namespace WTR {
59
60 static bool hasPrefix(const WTF::String& searchString, const WTF::String& prefix)
61 {
62     return searchString.length() >= prefix.length() && searchString.substring(0, prefix.length()) == prefix;
63 }
64
65 static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
66 {
67     if (!object)
68         return 0;
69     JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
70     return JSObjectGetProperty(context, object, propertyNameString.get(), 0);
71 }
72
73 static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName)
74 {
75     JSValueRef value = propertyValue(context, object, propertyName);
76     if (!value)
77         return 0;
78     return JSValueToNumber(context, value, 0);    
79 }
80
81 static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName)
82 {
83     return static_cast<int>(propertyValueDouble(context, object, propertyName));    
84 }
85
86 static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName)
87 {
88     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
89     return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName);
90 }
91
92 static WTF::String dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
93 {
94     JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName");
95     JSRetainPtr<JSStringRef> jsStringNodeName(Adopt, JSValueToStringCopy(context, nodeNameValue, 0));
96     WKRetainPtr<WKStringRef> nodeName = toWK(jsStringNodeName);
97
98     JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode");
99
100     StringBuilder stringBuilder;
101     stringBuilder.append(toWTFString(nodeName));
102
103     if (parentNode && JSValueIsObject(context, parentNode)) {
104         stringBuilder.appendLiteral(" > ");
105         stringBuilder.append(dumpPath(context, (JSObjectRef)parentNode));
106     }
107
108     return stringBuilder.toString();
109 }
110
111 static WTF::String dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
112 {
113     if (!node)
114         return "(null)";
115
116     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
117
118     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
119     JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world);
120     ASSERT(JSValueIsObject(context, nodeValue));
121     JSObjectRef nodeObject = (JSObjectRef)nodeValue;
122
123     return dumpPath(context, nodeObject);
124 }
125
126 static WTF::String rangeToStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
127 {
128     if (!rangeRef)
129         return "(null)";
130  
131     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
132
133     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
134     JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world);
135     ASSERT(JSValueIsObject(context, rangeValue));
136     JSObjectRef rangeObject = (JSObjectRef)rangeValue;
137
138     JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer");
139     ASSERT(JSValueIsObject(context, startNodeValue));
140     JSObjectRef startNodeObject = (JSObjectRef)startNodeValue;
141
142     JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer");
143     ASSERT(JSValueIsObject(context, endNodeValue));
144     JSObjectRef endNodeObject = (JSObjectRef)endNodeValue;
145
146     int startOffset = propertyValueInt(context, rangeObject, "startOffset");
147     int endOffset = propertyValueInt(context, rangeObject, "endOffset");
148
149     StringBuilder stringBuilder;
150     stringBuilder.appendLiteral("range from ");
151     stringBuilder.appendNumber(startOffset);
152     stringBuilder.appendLiteral(" of ");
153     stringBuilder.append(dumpPath(context, startNodeObject));
154     stringBuilder.appendLiteral(" to ");
155     stringBuilder.appendNumber(endOffset);
156     stringBuilder.appendLiteral(" of ");
157     stringBuilder.append(dumpPath(context, endNodeObject));
158     return stringBuilder.toString();
159 }
160
161 static WKRetainPtr<WKStringRef> NavigationTypeToString(WKFrameNavigationType type)
162 {
163     switch (type) {
164     case kWKFrameNavigationTypeLinkClicked:
165         return adoptWK(WKStringCreateWithUTF8CString("link clicked"));
166     case kWKFrameNavigationTypeFormSubmitted:
167         return adoptWK(WKStringCreateWithUTF8CString("form submitted"));
168     case kWKFrameNavigationTypeBackForward:
169         return adoptWK(WKStringCreateWithUTF8CString("back/forward"));
170     case kWKFrameNavigationTypeReload:
171         return adoptWK(WKStringCreateWithUTF8CString("reload"));
172     case kWKFrameNavigationTypeFormResubmitted:
173         return adoptWK(WKStringCreateWithUTF8CString("form resubmitted"));
174     case kWKFrameNavigationTypeOther:
175         return adoptWK(WKStringCreateWithUTF8CString("other"));
176     }
177     return adoptWK(WKStringCreateWithUTF8CString("illegal value"));
178 }
179
180 static WTF::String styleDecToStr(WKBundleCSSStyleDeclarationRef style)
181 {
182     // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
183     // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
184     // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
185     StringBuilder stringBuilder;
186     stringBuilder.appendLiteral("<DOMCSSStyleDeclaration ADDRESS>");
187     return stringBuilder.toString();
188 }
189
190 static WTF::String securityOriginToStr(WKSecurityOriginRef origin)
191 {
192     StringBuilder stringBuilder;
193     stringBuilder.append('{');
194     stringBuilder.append(toWTFString(adoptWK(WKSecurityOriginCopyProtocol(origin))));
195     stringBuilder.appendLiteral(", ");
196     stringBuilder.append(toWTFString(adoptWK(WKSecurityOriginCopyHost(origin))));
197     stringBuilder.appendLiteral(", ");
198     stringBuilder.appendNumber(WKSecurityOriginGetPort(origin));
199     stringBuilder.append('}');
200
201     return stringBuilder.toString();
202 }
203
204 static WTF::String frameToStr(WKBundleFrameRef frame)
205 {
206     WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
207     StringBuilder stringBuilder;
208     if (WKBundleFrameIsMainFrame(frame)) {
209         if (!WKStringIsEmpty(name.get())) {
210             stringBuilder.appendLiteral("main frame \"");
211             stringBuilder.append(toWTFString(name));
212             stringBuilder.append('"');
213         } else
214             stringBuilder.appendLiteral("main frame");
215     } else {
216         if (!WKStringIsEmpty(name.get())) {
217             stringBuilder.appendLiteral("frame \"");
218             stringBuilder.append(toWTFString(name));
219             stringBuilder.append('"');
220         }
221         else
222             stringBuilder.appendLiteral("frame (anonymous)");
223     }
224     
225     return stringBuilder.toString();
226 }
227
228 static inline bool isLocalFileScheme(WKStringRef scheme)
229 {
230     return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "file");
231 }
232
233 static const char divider = '/';
234
235 static inline WTF::String pathSuitableForTestResult(WKURLRef fileUrl)
236 {
237     if (!fileUrl)
238         return "(null)";
239
240     WKRetainPtr<WKStringRef> schemeString = adoptWK(WKURLCopyScheme(fileUrl));
241     if (!isLocalFileScheme(schemeString.get()))
242         return toWTFString(adoptWK(WKURLCopyString(fileUrl)));
243
244     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
245     WKRetainPtr<WKURLRef> mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
246     if (!mainFrameURL)
247         mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
248
249     String pathString = toWTFString(adoptWK(WKURLCopyPath(fileUrl)));
250     String mainFrameURLPathString = toWTFString(adoptWK(WKURLCopyPath(mainFrameURL.get())));
251     String basePath = mainFrameURLPathString.substring(0, mainFrameURLPathString.reverseFind(divider) + 1);
252     
253     if (!basePath.isEmpty() && pathString.startsWith(basePath))
254         return pathString.substring(basePath.length());
255     return toWTFString(adoptWK(WKURLCopyLastPathComponent(fileUrl))); // We lose some information here, but it's better than exposing a full path, which is always machine specific.
256 }
257
258 static HashMap<uint64_t, String> assignedUrlsCache;
259
260 static inline void dumpResourceURL(uint64_t identifier, StringBuilder& stringBuilder)
261 {
262     if (assignedUrlsCache.contains(identifier))
263         stringBuilder.append(assignedUrlsCache.get(identifier));
264     else
265         stringBuilder.appendLiteral("<unknown>");
266 }
267
268 InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
269     : m_page(page)
270     , m_world(AdoptWK, WKBundleScriptWorldCreateWorld())
271 {
272     WKBundlePageLoaderClientV8 loaderClient = {
273         { 8, this },
274         didStartProvisionalLoadForFrame,
275         didReceiveServerRedirectForProvisionalLoadForFrame,
276         didFailProvisionalLoadWithErrorForFrame,
277         didCommitLoadForFrame,
278         didFinishDocumentLoadForFrame,
279         didFinishLoadForFrame,
280         didFailLoadWithErrorForFrame,
281         didSameDocumentNavigationForFrame,
282         didReceiveTitleForFrame,
283         0, // didFirstLayoutForFrame
284         0, // didFirstVisuallyNonEmptyLayoutForFrame
285         0, // didRemoveFrameFromHierarchy
286         didDisplayInsecureContentForFrame,
287         didRunInsecureContentForFrame,
288         didClearWindowForFrame,
289         didCancelClientRedirectForFrame,
290         willPerformClientRedirectForFrame,
291         didHandleOnloadEventsForFrame,
292         0, // didLayoutForFrame
293         0, // didNewFirstVisuallyNonEmptyLayout_unavailable
294         didDetectXSSForFrame,
295         0, // shouldGoToBackForwardListItem
296         0, // didCreateGlobalObjectForFrame
297         0, // willDisconnectDOMWindowExtensionFromGlobalObject
298         0, // didReconnectDOMWindowExtensionToGlobalObject
299         0, // willDestroyGlobalObjectForDOMWindowExtension
300         didFinishProgress, // didFinishProgress
301         0, // shouldForceUniversalAccessFromLocalURL
302         0, // didReceiveIntentForFrame
303         0, // registerIntentServiceForFrame
304         0, // didLayout
305         0, // featuresUsedInPage
306         0, // willLoadURLRequest
307         0, // willLoadDataRequest
308         0, // willDestroyFrame_unavailable
309         0, // userAgentForURL
310     };
311     WKBundlePageSetPageLoaderClient(m_page, &loaderClient.base);
312
313     WKBundlePageResourceLoadClientV1 resourceLoadClient = {
314         { 1, this },
315         didInitiateLoadForResource,
316         willSendRequestForFrame,
317         didReceiveResponseForResource,
318         didReceiveContentLengthForResource,
319         didFinishLoadForResource,
320         didFailLoadForResource,
321         shouldCacheResponse,
322         0 // shouldUseCredentialStorage
323     };
324     WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient.base);
325
326     WKBundlePagePolicyClientV0 policyClient = {
327         { 0, this },
328         decidePolicyForNavigationAction,
329         decidePolicyForNewWindowAction,
330         decidePolicyForResponse,
331         unableToImplementPolicy
332     };
333     WKBundlePageSetPolicyClient(m_page, &policyClient.base);
334
335     WKBundlePageUIClientV2 uiClient = {
336         { 2, this },
337         willAddMessageToConsole,
338         willSetStatusbarText,
339         willRunJavaScriptAlert,
340         willRunJavaScriptConfirm,
341         willRunJavaScriptPrompt,
342         0, /*mouseDidMoveOverElement*/
343         0, /*pageDidScroll*/
344         0, /*paintCustomOverhangArea*/
345         0, /*shouldGenerateFileForUpload*/
346         0, /*generateFileForUpload*/
347         0, /*shouldRubberBandInDirection*/
348         0, /*statusBarIsVisible*/
349         0, /*menuBarIsVisible*/
350         0, /*toolbarsAreVisible*/
351         didReachApplicationCacheOriginQuota,
352         didExceedDatabaseQuota,
353         0, /*plugInStartLabelTitle*/
354         0, /*plugInStartLabelSubtitle*/
355         0, /*plugInExtraStyleSheet*/
356         0, /*plugInExtraScript*/
357     };
358     WKBundlePageSetUIClient(m_page, &uiClient.base);
359
360     WKBundlePageEditorClientV1 editorClient = {
361         { 1, this },
362         shouldBeginEditing,
363         shouldEndEditing,
364         shouldInsertNode,
365         shouldInsertText,
366         shouldDeleteRange,
367         shouldChangeSelectedRange,
368         shouldApplyStyle,
369         didBeginEditing,
370         didEndEditing,
371         didChange,
372         didChangeSelection,
373         0, /* willWriteToPasteboard */
374         0, /* getPasteboardDataForRange */
375         0  /* didWriteToPasteboard */
376     };
377     WKBundlePageSetEditorClient(m_page, &editorClient.base);
378
379 #if ENABLE(FULLSCREEN_API)
380     WKBundlePageFullScreenClientV1 fullScreenClient = {
381         { 1, this },
382         supportsFullScreen,
383         enterFullScreenForElement,
384         exitFullScreenForElement,
385         beganEnterFullScreen,
386         beganExitFullScreen,
387         closeFullScreen,
388     };
389     WKBundlePageSetFullScreenClient(m_page, &fullScreenClient.base);
390 #endif
391 }
392
393 InjectedBundlePage::~InjectedBundlePage()
394 {
395 }
396
397 void InjectedBundlePage::stopLoading()
398 {
399     WKBundlePageStopLoading(m_page);
400 }
401
402 void InjectedBundlePage::prepare()
403 {
404     WKBundlePageClearMainFrameName(m_page);
405
406     WKBundlePageSetPageZoomFactor(m_page, 1);
407     WKBundlePageSetTextZoomFactor(m_page, 1);
408
409     WKPoint origin = { 0, 0 };
410     WKBundlePageSetScaleAtOrigin(m_page, 1, origin);
411
412     m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
413
414     WKBundleFrameClearOpener(WKBundlePageGetMainFrame(m_page));
415     
416     WKBundlePageSetTracksRepaints(m_page, false);
417 }
418
419 void InjectedBundlePage::resetAfterTest()
420 {
421     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
422
423     // WebKit currently doesn't reset focus even when navigating to a new page. This may or may not be a bug
424     // (see <https://bugs.webkit.org/show_bug.cgi?id=138334>), however for tests, we want to start each one with a clean state.
425     WKBundleFrameFocus(frame);
426
427     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
428     WebCoreTestSupport::resetInternalsObject(context);
429     assignedUrlsCache.clear();
430
431     // User scripts need to be removed after the test and before loading about:blank, as otherwise they would run in about:blank, and potentially leak results into a subsequest test.
432     WKBundlePageRemoveAllUserContent(m_page);
433 }
434
435 // Loader Client Callbacks
436
437 // String output must be identical to -[WebFrame _drt_descriptionSuitableForTestResult].
438 static void dumpFrameDescriptionSuitableForTestResult(WKBundleFrameRef frame, StringBuilder& stringBuilder)
439 {
440     WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
441     if (WKBundleFrameIsMainFrame(frame)) {
442         if (WKStringIsEmpty(name.get())) {
443             stringBuilder.appendLiteral("main frame");
444             return;
445         }
446
447         stringBuilder.appendLiteral("main frame \"");
448         stringBuilder.append(toWTFString(name));
449         stringBuilder.append('"');
450         return;
451     }
452
453     if (WKStringIsEmpty(name.get())) {
454         stringBuilder.appendLiteral("frame (anonymous)");
455         return;
456     }
457
458     stringBuilder.appendLiteral("frame \"");
459     stringBuilder.append(toWTFString(name));
460     stringBuilder.append('"');
461 }
462
463 static void dumpLoadEvent(WKBundleFrameRef frame, const char* eventName)
464 {
465     StringBuilder stringBuilder;
466     dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
467     stringBuilder.appendLiteral(" - ");
468     stringBuilder.append(eventName);
469     stringBuilder.append('\n');
470     InjectedBundle::singleton().outputText(stringBuilder.toString());
471 }
472
473 static inline void dumpRequestDescriptionSuitableForTestResult(WKURLRequestRef request, StringBuilder& stringBuilder)
474 {
475     WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
476     WKRetainPtr<WKURLRef> firstParty = adoptWK(WKURLRequestCopyFirstPartyForCookies(request));
477     WKRetainPtr<WKStringRef> httpMethod = adoptWK(WKURLRequestCopyHTTPMethod(request));
478
479     stringBuilder.appendLiteral("<NSURLRequest URL ");
480     stringBuilder.append(pathSuitableForTestResult(url.get()));
481     stringBuilder.appendLiteral(", main document URL ");
482     stringBuilder.append(pathSuitableForTestResult(firstParty.get()));
483     stringBuilder.appendLiteral(", http method ");
484
485     if (WKStringIsEmpty(httpMethod.get()))
486         stringBuilder.appendLiteral("(none)");
487     else
488         stringBuilder.append(toWTFString(httpMethod));
489
490     stringBuilder.append('>');
491 }
492
493 static inline void dumpResponseDescriptionSuitableForTestResult(WKURLResponseRef response, StringBuilder& stringBuilder)
494 {
495     WKRetainPtr<WKURLRef> url = adoptWK(WKURLResponseCopyURL(response));
496     if (!url) {
497         stringBuilder.appendLiteral("(null)");
498         return;
499     }
500     stringBuilder.appendLiteral("<NSURLResponse ");
501     stringBuilder.append(pathSuitableForTestResult(url.get()));
502     stringBuilder.appendLiteral(", http status code ");
503     stringBuilder.appendNumber(WKURLResponseHTTPStatusCode(response));
504     stringBuilder.append('>');
505 }
506
507 static inline void dumpErrorDescriptionSuitableForTestResult(WKErrorRef error, StringBuilder& stringBuilder)
508 {
509     WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
510     int errorCode = WKErrorGetErrorCode(error);
511
512     // We need to do some error mapping here to match the test expectations (Mac error names are expected).
513     if (WKStringIsEqualToUTF8CString(errorDomain.get(), "WebKitNetworkError")) {
514         errorDomain = adoptWK(WKStringCreateWithUTF8CString("NSURLErrorDomain"));
515         errorCode = -999;
516     }
517
518     if (WKStringIsEqualToUTF8CString(errorDomain.get(), "WebKitPolicyError"))
519         errorDomain = adoptWK(WKStringCreateWithUTF8CString("WebKitErrorDomain"));
520
521     stringBuilder.appendLiteral("<NSError domain ");
522     stringBuilder.append(toWTFString(errorDomain));
523     stringBuilder.appendLiteral(", code ");
524     stringBuilder.appendNumber(errorCode);
525
526     WKRetainPtr<WKURLRef> url = adoptWK(WKErrorCopyFailingURL(error));
527     if (url.get()) {
528         WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
529         stringBuilder.appendLiteral(", failing URL \"");
530         stringBuilder.append(toWTFString(urlString));
531         stringBuilder.append('"');
532     }
533
534     stringBuilder.append('>');
535 }
536
537 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
538 {
539     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
540 }
541
542 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
543 {
544     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
545 }
546
547 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
548 {
549     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
550 }
551
552 void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
553 {
554     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
555 }
556
557 void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
558 {
559     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
560 }
561
562 void InjectedBundlePage::didFinishProgress(WKBundlePageRef, const void *clientInfo)
563 {
564     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishProgress();
565 }
566
567 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
568 {
569     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
570 }
571
572 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
573 {
574     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
575 }
576
577 void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
578 {
579     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
580 }
581
582 void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
583 {
584     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
585 }
586
587 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
588 {
589     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
590 }
591
592 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
593 {
594     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(page, frame, url, delay, date);
595 }
596
597 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
598 {
599     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
600 }
601
602 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
603 {
604     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
605 }
606
607 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
608 {
609     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
610 }
611
612 void InjectedBundlePage::didDetectXSSForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
613 {
614     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDetectXSSForFrame(frame);
615 }
616
617 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
618 {
619     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
620 }
621
622 void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void* clientInfo)
623 {
624     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didInitiateLoadForResource(page, frame, identifier, request, pageLoadIsProvisional);
625 }
626
627 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
628 {
629     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse);
630 }
631
632 void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLResponseRef response, const void* clientInfo)
633 {
634     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveResponseForResource(page, frame, identifier, response);
635 }
636
637 void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, uint64_t length, const void* clientInfo)
638 {
639     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveContentLengthForResource(page, frame, identifier, length);
640 }
641
642 void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
643 {
644     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier);
645 }
646
647 void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKErrorRef error, const void* clientInfo)
648 {
649     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadForResource(page, frame, identifier, error);
650 }
651
652 bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
653 {
654     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldCacheResponse(page, frame, identifier);
655 }
656
657 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
658 {
659     auto& injectedBundle = InjectedBundle::singleton();
660     if (!injectedBundle.isTestRunning())
661         return;
662
663     if (!injectedBundle.testRunner()->testURL()) {
664         WKRetainPtr<WKURLRef> testURL = adoptWK(WKBundleFrameCopyProvisionalURL(frame));
665         injectedBundle.testRunner()->setTestURL(testURL.get());
666     }
667
668     platformDidStartProvisionalLoadForFrame(frame);
669
670     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
671         dumpLoadEvent(frame, "didStartProvisionalLoadForFrame");
672
673     if (!injectedBundle.topLoadingFrame())
674         injectedBundle.setTopLoadingFrame(frame);
675
676     if (injectedBundle.testRunner()->shouldStopProvisionalFrameLoads())
677         dumpLoadEvent(frame, "stopping load in didStartProvisionalLoadForFrame callback");
678 }
679
680 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
681 {
682     auto& injectedBundle = InjectedBundle::singleton();
683     if (!injectedBundle.isTestRunning())
684         return;
685
686     if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
687         return;
688
689     dumpLoadEvent(frame, "didReceiveServerRedirectForProvisionalLoadForFrame");
690 }
691
692 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
693 {
694     auto& injectedBundle = InjectedBundle::singleton();
695     if (!injectedBundle.isTestRunning())
696         return;
697
698     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks()) {
699         dumpLoadEvent(frame, "didFailProvisionalLoadWithError");
700         auto code = WKErrorGetErrorCode(error);
701         if (code == kWKErrorCodeCannotShowURL)
702             dumpLoadEvent(frame, "(kWKErrorCodeCannotShowURL)");
703         else if (code == kWKErrorCodeFrameLoadBlockedByContentBlocker)
704             dumpLoadEvent(frame, "(kWKErrorCodeFrameLoadBlockedByContentBlocker)");
705     }
706
707     frameDidChangeLocation(frame);
708 }
709
710 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
711 {
712     auto& injectedBundle = InjectedBundle::singleton();
713     if (!injectedBundle.isTestRunning())
714         return;
715
716     if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
717         return;
718
719     dumpLoadEvent(frame, "didCommitLoadForFrame");
720 }
721
722 void InjectedBundlePage::didFinishProgress()
723 {
724     auto& injectedBundle = InjectedBundle::singleton();
725     if (!injectedBundle.isTestRunning())
726         return;
727
728     if (!injectedBundle.testRunner()->shouldDumpProgressFinishedCallback())
729         return;
730
731     injectedBundle.outputText("postProgressFinishedNotification\n");
732 }
733
734 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
735
736 static void dumpFrameScrollPosition(WKBundleFrameRef frame, StringBuilder& stringBuilder, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
737 {
738     double x = numericWindowPropertyValue(frame, "pageXOffset");
739     double y = numericWindowPropertyValue(frame, "pageYOffset");
740     if (fabs(x) <= 0.00000001 && fabs(y) <= 0.00000001)
741         return;
742
743     if (shouldIncludeFrameName) {
744         WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
745         stringBuilder.appendLiteral("frame '");
746         stringBuilder.append(toWTFString(name));
747         stringBuilder.appendLiteral("' ");
748     }
749     stringBuilder.appendLiteral("scrolled to ");
750     stringBuilder.append(WTF::String::number(x));
751     stringBuilder.append(',');
752     stringBuilder.append(WTF::String::number(y));
753     stringBuilder.append('\n');
754 }
755
756 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame, StringBuilder& stringBuilder)
757 {
758     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
759     size_t size = WKArrayGetSize(childFrames.get());
760     for (size_t i = 0; i < size; ++i) {
761         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
762         dumpFrameScrollPosition(subframe, stringBuilder, ShouldIncludeFrameName);
763         dumpDescendantFrameScrollPositions(subframe, stringBuilder);
764     }
765 }
766
767 void InjectedBundlePage::dumpAllFrameScrollPositions(StringBuilder& stringBuilder)
768 {
769     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
770     dumpFrameScrollPosition(frame, stringBuilder);
771     dumpDescendantFrameScrollPositions(frame, stringBuilder);
772 }
773
774 static JSRetainPtr<JSStringRef> toJS(const char* string)
775 {
776     return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
777 }
778
779 static bool hasDocumentElement(WKBundleFrameRef frame)
780 {
781     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
782     JSObjectRef globalObject = JSContextGetGlobalObject(context);
783
784     JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
785     if (!documentValue)
786         return false;
787
788     ASSERT(JSValueIsObject(context, documentValue));
789     JSObjectRef document = JSValueToObject(context, documentValue, 0);
790
791     JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
792     if (!documentElementValue)
793         return false;
794
795     return JSValueToBoolean(context, documentElementValue);
796 }
797
798 static void dumpFrameText(WKBundleFrameRef frame, StringBuilder& stringBuilder)
799 {
800     // If the frame doesn't have a document element, its inner text will be an empty string, so
801     // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
802     // anything in this case, so we shouldn't either.
803     if (!hasDocumentElement(frame))
804         return;
805
806     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
807     stringBuilder.append(toWTFString(text));
808     stringBuilder.append('\n');
809 }
810
811 static void dumpDescendantFramesText(WKBundleFrameRef frame, StringBuilder& stringBuilder)
812 {
813     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
814     size_t size = WKArrayGetSize(childFrames.get());
815     for (size_t i = 0; i < size; ++i) {
816         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
817         WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
818
819         // DumpRenderTree ignores empty frames, so do the same thing here.
820         if (!hasDocumentElement(subframe))
821             continue;
822
823         stringBuilder.appendLiteral("\n--------\nFrame: '");
824         stringBuilder.append(toWTFString(subframeName));
825         stringBuilder.appendLiteral("'\n--------\n");
826
827         dumpFrameText(subframe, stringBuilder);
828         dumpDescendantFramesText(subframe, stringBuilder);
829     }
830 }
831
832 void InjectedBundlePage::dumpAllFramesText(StringBuilder& stringBuilder)
833 {
834     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
835     dumpFrameText(frame, stringBuilder);
836     dumpDescendantFramesText(frame, stringBuilder);
837 }
838
839
840 void InjectedBundlePage::dumpDOMAsWebArchive(WKBundleFrameRef frame, StringBuilder& stringBuilder)
841 {
842 #if USE(CF)
843     WKRetainPtr<WKDataRef> wkData = adoptWK(WKBundleFrameCopyWebArchive(frame));
844     RetainPtr<CFDataRef> cfData = adoptCF(CFDataCreate(0, WKDataGetBytes(wkData.get()), WKDataGetSize(wkData.get())));
845     RetainPtr<CFStringRef> cfString = adoptCF(createXMLStringFromWebArchiveData(cfData.get()));
846     stringBuilder.append(String(cfString.get()));
847 #endif
848 }
849
850 void InjectedBundlePage::dump()
851 {
852     auto& injectedBundle = InjectedBundle::singleton();
853     ASSERT(injectedBundle.isTestRunning());
854
855     // Force a paint before dumping. This matches DumpRenderTree on Windows. (DumpRenderTree on Mac
856     // does this at a slightly different time.) See <http://webkit.org/b/55469> for details.
857     WKBundlePageForceRepaint(m_page);
858
859     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
860     WKRetainPtr<WKURLRef> urlRef = adoptWK(WKBundleFrameCopyURL(frame));
861     String url = toWTFString(adoptWK(WKURLCopyString(urlRef.get())));
862     WKRetainPtr<WKStringRef> mimeType = adoptWK(WKBundleFrameCopyMIMETypeForResourceWithURL(frame, urlRef.get()));
863     if (url.find("dumpAsText/") != notFound || WKStringIsEqualToUTF8CString(mimeType.get(), "text/plain"))
864         injectedBundle.testRunner()->dumpAsText(false);
865
866     StringBuilder stringBuilder;
867
868     switch (injectedBundle.testRunner()->whatToDump()) {
869     case TestRunner::RenderTree: {
870         if (injectedBundle.testRunner()->isPrinting())
871             stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentationForPrinting(m_page)).get()));
872         else
873             stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentation(m_page)).get()));
874         break;
875     }
876     case TestRunner::MainFrameText:
877         dumpFrameText(WKBundlePageGetMainFrame(m_page), stringBuilder);
878         break;
879     case TestRunner::AllFramesText:
880         dumpAllFramesText(stringBuilder);
881         break;
882     case TestRunner::Audio:
883         break;
884     case TestRunner::DOMAsWebArchive:
885         dumpDOMAsWebArchive(frame, stringBuilder);
886         break;
887     }
888
889     if (injectedBundle.testRunner()->shouldDumpAllFrameScrollPositions())
890         dumpAllFrameScrollPositions(stringBuilder);
891     else if (injectedBundle.testRunner()->shouldDumpMainFrameScrollPosition())
892         dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page), stringBuilder);
893
894     if (injectedBundle.testRunner()->shouldDumpBackForwardListsForAllWindows())
895         injectedBundle.dumpBackForwardListsForAllPages(stringBuilder);
896
897     if (injectedBundle.shouldDumpPixels() && injectedBundle.testRunner()->shouldDumpPixels()) {
898         bool shouldCreateSnapshot = injectedBundle.testRunner()->isPrinting();
899         if (shouldCreateSnapshot) {
900             WKSnapshotOptions options = kWKSnapshotOptionsShareable;
901             WKRect snapshotRect = WKBundleFrameGetVisibleContentBounds(WKBundlePageGetMainFrame(m_page));
902
903             if (injectedBundle.testRunner()->isPrinting())
904                 options |= kWKSnapshotOptionsPrinting;
905             else {
906                 options |= kWKSnapshotOptionsInViewCoordinates;
907                 if (injectedBundle.testRunner()->shouldDumpSelectionRect())
908                     options |= kWKSnapshotOptionsPaintSelectionRectangle;
909             }
910
911             injectedBundle.setPixelResult(adoptWK(WKBundlePageCreateSnapshotWithOptions(m_page, snapshotRect, options)).get());
912         } else
913             injectedBundle.setPixelResultIsPending(true);
914
915         if (WKBundlePageIsTrackingRepaints(m_page) && !injectedBundle.testRunner()->isPrinting())
916             injectedBundle.setRepaintRects(adoptWK(WKBundlePageCopyTrackedRepaintRects(m_page)).get());
917     }
918
919     injectedBundle.outputText(stringBuilder.toString());
920     injectedBundle.done();
921 }
922
923 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
924 {
925     auto& injectedBundle = InjectedBundle::singleton();
926     if (!injectedBundle.isTestRunning())
927         return;
928
929     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
930         dumpLoadEvent(frame, "didFinishLoadForFrame");
931
932     frameDidChangeLocation(frame, /*shouldDump*/ true);
933 }
934
935 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
936 {
937     auto& injectedBundle = InjectedBundle::singleton();
938     if (!injectedBundle.isTestRunning())
939         return;
940
941     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
942         dumpLoadEvent(frame, "didFailLoadWithError");
943
944     frameDidChangeLocation(frame);
945 }
946
947 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
948 {
949     auto& injectedBundle = InjectedBundle::singleton();
950     if (!injectedBundle.isTestRunning())
951         return;
952
953     StringBuilder stringBuilder;
954     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks()) {
955         dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
956         stringBuilder.appendLiteral(" - didReceiveTitle: ");
957         stringBuilder.append(toWTFString(title));
958         stringBuilder.append('\n');
959     }
960
961     if (injectedBundle.testRunner()->shouldDumpTitleChanges()) {
962         stringBuilder.appendLiteral("TITLE CHANGED: '");
963         stringBuilder.append(toWTFString(title));
964         stringBuilder.appendLiteral("'\n");
965     }
966
967     injectedBundle.outputText(stringBuilder.toString());
968 }
969
970 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
971 {
972     auto& injectedBundle = InjectedBundle::singleton();
973     if (!injectedBundle.isTestRunning())
974         return;
975
976     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
977     JSObjectRef window = JSContextGetGlobalObject(context);
978
979     if (WKBundleScriptWorldNormalWorld() != world) {
980         JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, TestRunner::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
981         return;
982     }
983
984     JSValueRef exception = nullptr;
985     injectedBundle.testRunner()->makeWindowObject(context, window, &exception);
986     injectedBundle.gcController()->makeWindowObject(context, window, &exception);
987     injectedBundle.eventSendingController()->makeWindowObject(context, window, &exception);
988     injectedBundle.textInputController()->makeWindowObject(context, window, &exception);
989     injectedBundle.accessibilityController()->makeWindowObject(context, window, &exception);
990
991     WebCoreTestSupport::injectInternalsObject(context);
992 }
993
994 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
995 {
996     auto& injectedBundle = InjectedBundle::singleton();
997     if (!injectedBundle.isTestRunning())
998         return;
999
1000     if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1001         return;
1002
1003     dumpLoadEvent(frame, "didCancelClientRedirectForFrame");
1004 }
1005
1006 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKURLRef url, double delay, double date)
1007 {
1008     auto& injectedBundle = InjectedBundle::singleton();
1009     if (!injectedBundle.isTestRunning())
1010         return;
1011
1012     if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1013         return;
1014
1015     StringBuilder stringBuilder;
1016     dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
1017     stringBuilder.appendLiteral(" - willPerformClientRedirectToURL: ");
1018     stringBuilder.append(pathSuitableForTestResult(url));
1019     stringBuilder.appendLiteral(" \n");
1020     injectedBundle.outputText(stringBuilder.toString());
1021 }
1022
1023 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
1024 {
1025     auto& injectedBundle = InjectedBundle::singleton();
1026     if (!injectedBundle.isTestRunning())
1027         return;
1028
1029     if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1030         return;
1031
1032     if (type != kWKSameDocumentNavigationAnchorNavigation)
1033         return;
1034
1035     dumpLoadEvent(frame, "didChangeLocationWithinPageForFrame");
1036 }
1037
1038 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
1039 {
1040     auto& injectedBundle = InjectedBundle::singleton();
1041     if (!injectedBundle.isTestRunning())
1042         return;
1043
1044     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1045         dumpLoadEvent(frame, "didFinishDocumentLoadForFrame");
1046
1047     unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
1048     if (pendingFrameUnloadEvents) {
1049         StringBuilder stringBuilder;
1050         stringBuilder.append(frameToStr(frame));
1051         stringBuilder.appendLiteral(" - has ");
1052         stringBuilder.appendNumber(pendingFrameUnloadEvents);
1053         stringBuilder.appendLiteral(" onunload handler(s)\n");
1054         injectedBundle.outputText(stringBuilder.toString());
1055     }
1056 }
1057
1058 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
1059 {
1060     auto& injectedBundle = InjectedBundle::singleton();
1061     if (!injectedBundle.isTestRunning())
1062         return;
1063
1064     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1065         dumpLoadEvent(frame, "didHandleOnloadEventsForFrame");
1066 }
1067
1068 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef)
1069 {
1070     auto& injectedBundle = InjectedBundle::singleton();
1071     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1072         injectedBundle.outputText("didDisplayInsecureContent\n");
1073 }
1074
1075 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef)
1076 {
1077     auto& injectedBundle = InjectedBundle::singleton();
1078     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1079         injectedBundle.outputText("didRunInsecureContent\n");
1080 }
1081
1082 void InjectedBundlePage::didDetectXSSForFrame(WKBundleFrameRef)
1083 {
1084     auto& injectedBundle = InjectedBundle::singleton();
1085     if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1086         injectedBundle.outputText("didDetectXSS\n");
1087 }
1088
1089 void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef request, bool)
1090 {
1091     if (!InjectedBundle::singleton().isTestRunning())
1092         return;
1093
1094     WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
1095     assignedUrlsCache.add(identifier, pathSuitableForTestResult(url.get()));
1096 }
1097
1098 // Resource Load Client Callbacks
1099
1100 static inline bool isLocalHost(WKStringRef host)
1101 {
1102     return WKStringIsEqualToUTF8CString(host, "127.0.0.1") || WKStringIsEqualToUTF8CString(host, "localhost");
1103 }
1104
1105 static inline bool isHTTPOrHTTPSScheme(WKStringRef scheme)
1106 {
1107     return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "http") || WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "https");
1108 }
1109
1110 static inline bool isAllowedHost(WKStringRef host)
1111 {
1112     return InjectedBundle::singleton().isAllowedHost(host);
1113 }
1114
1115 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef response)
1116 {
1117     auto& injectedBundle = InjectedBundle::singleton();
1118     if (injectedBundle.isTestRunning()
1119         && injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks()) {
1120         StringBuilder stringBuilder;
1121         dumpResourceURL(identifier, stringBuilder);
1122         stringBuilder.appendLiteral(" - willSendRequest ");
1123         dumpRequestDescriptionSuitableForTestResult(request, stringBuilder);
1124         stringBuilder.appendLiteral(" redirectResponse ");
1125         dumpResponseDescriptionSuitableForTestResult(response, stringBuilder);
1126         stringBuilder.append('\n');
1127         injectedBundle.outputText(stringBuilder.toString());
1128     }
1129
1130     if (injectedBundle.isTestRunning() && injectedBundle.testRunner()->willSendRequestReturnsNull())
1131         return nullptr;
1132
1133     WKRetainPtr<WKURLRef> redirectURL = adoptWK(WKURLResponseCopyURL(response));
1134     if (injectedBundle.isTestRunning() && injectedBundle.testRunner()->willSendRequestReturnsNullOnRedirect() && redirectURL) {
1135         injectedBundle.outputText("Returning null for this redirect\n");
1136         return nullptr;
1137     }
1138
1139     WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
1140     WKRetainPtr<WKStringRef> host = adoptWK(WKURLCopyHostName(url.get()));
1141     WKRetainPtr<WKStringRef> scheme = adoptWK(WKURLCopyScheme(url.get()));
1142     WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
1143     if (host && !WKStringIsEmpty(host.get())
1144         && isHTTPOrHTTPSScheme(scheme.get())
1145         && !WKStringIsEqualToUTF8CString(host.get(), "255.255.255.255") // Used in some tests that expect to get back an error.
1146         && !isLocalHost(host.get())) {
1147         bool mainFrameIsExternal = false;
1148         if (injectedBundle.isTestRunning()) {
1149             WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(m_page);
1150             WKRetainPtr<WKURLRef> mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
1151             if (!mainFrameURL || WKStringIsEqualToUTF8CString(adoptWK(WKURLCopyString(mainFrameURL.get())).get(), "about:blank"))
1152                 mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
1153
1154             WKRetainPtr<WKStringRef> mainFrameHost = adoptWK(WKURLCopyHostName(mainFrameURL.get()));
1155             WKRetainPtr<WKStringRef> mainFrameScheme = adoptWK(WKURLCopyScheme(mainFrameURL.get()));
1156             mainFrameIsExternal = isHTTPOrHTTPSScheme(mainFrameScheme.get()) && !isLocalHost(mainFrameHost.get());
1157         }
1158         if (!mainFrameIsExternal && !isAllowedHost(host.get())) {
1159             StringBuilder stringBuilder;
1160             stringBuilder.appendLiteral("Blocked access to external URL ");
1161             stringBuilder.append(toWTFString(urlString));
1162             stringBuilder.append('\n');
1163             injectedBundle.outputText(stringBuilder.toString());
1164             return nullptr;
1165         }
1166     }
1167     
1168     if (injectedBundle.isTestRunning()) {
1169         String body = injectedBundle.testRunner()->willSendRequestHTTPBody();
1170         if (!body.isEmpty()) {
1171             CString cBody = body.utf8();
1172             return WKURLRequestCopySettingHTTPBody(request, WKDataCreate(reinterpret_cast<const unsigned char*>(cBody.data()), cBody.length()));
1173         }
1174     }
1175
1176     WKRetain(request);
1177     return request;
1178 }
1179
1180 void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef response)
1181 {
1182     auto& injectedBundle = InjectedBundle::singleton();
1183     if (!injectedBundle.isTestRunning())
1184         return;
1185
1186     if (injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks()) {
1187         StringBuilder stringBuilder;
1188         dumpResourceURL(identifier, stringBuilder);
1189         stringBuilder.appendLiteral(" - didReceiveResponse ");
1190         dumpResponseDescriptionSuitableForTestResult(response, stringBuilder);
1191         stringBuilder.append('\n');
1192         injectedBundle.outputText(stringBuilder.toString());
1193     }
1194
1195
1196     if (!injectedBundle.testRunner()->shouldDumpResourceResponseMIMETypes())
1197         return;
1198
1199     WKRetainPtr<WKURLRef> url = adoptWK(WKURLResponseCopyURL(response));
1200     WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyLastPathComponent(url.get()));
1201     WKRetainPtr<WKStringRef> mimeTypeString = adoptWK(WKURLResponseCopyMIMEType(response));
1202
1203     StringBuilder stringBuilder;
1204     stringBuilder.append(toWTFString(urlString));
1205     stringBuilder.appendLiteral(" has MIME type ");
1206     stringBuilder.append(toWTFString(mimeTypeString));
1207
1208     String platformMimeType = platformResponseMimeType(response);
1209     if (!platformMimeType.isEmpty() && platformMimeType != toWTFString(mimeTypeString)) {
1210         stringBuilder.appendLiteral(" but platform response has ");
1211         stringBuilder.append(platformMimeType);
1212     }
1213
1214     stringBuilder.append('\n');
1215
1216     injectedBundle.outputText(stringBuilder.toString());
1217 }
1218
1219 void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, uint64_t)
1220 {
1221 }
1222
1223 void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
1224 {
1225     auto& injectedBundle = InjectedBundle::singleton();
1226     if (!injectedBundle.isTestRunning())
1227         return;
1228
1229     if (!injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks())
1230         return;
1231
1232     StringBuilder stringBuilder;
1233     dumpResourceURL(identifier, stringBuilder);
1234     stringBuilder.appendLiteral(" - didFinishLoading\n");
1235     injectedBundle.outputText(stringBuilder.toString());
1236 }
1237
1238 void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKErrorRef error)
1239 {
1240     auto& injectedBundle = InjectedBundle::singleton();
1241     if (!injectedBundle.isTestRunning())
1242         return;
1243
1244     if (!injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks())
1245         return;
1246
1247     StringBuilder stringBuilder;
1248     dumpResourceURL(identifier, stringBuilder);
1249     stringBuilder.appendLiteral(" - didFailLoadingWithError: ");
1250
1251     dumpErrorDescriptionSuitableForTestResult(error, stringBuilder);
1252     stringBuilder.append('\n');
1253     injectedBundle.outputText(stringBuilder.toString());
1254 }
1255
1256 bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
1257 {
1258     auto& injectedBundle = InjectedBundle::singleton();
1259     if (!injectedBundle.isTestRunning())
1260         return true;
1261
1262     if (!injectedBundle.testRunner()->shouldDumpWillCacheResponse())
1263         return true;
1264
1265     StringBuilder stringBuilder;
1266     stringBuilder.appendNumber(identifier);
1267     stringBuilder.appendLiteral(" - willCacheResponse: called\n");
1268     injectedBundle.outputText(stringBuilder.toString());
1269
1270     // The default behavior is the cache the response.
1271     return true;
1272 }
1273
1274
1275 // Policy Client Callbacks
1276
1277 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
1278 {
1279     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(page, frame, navigationAction, request, userData);
1280 }
1281
1282 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKStringRef frameName, WKTypeRef* userData, const void* clientInfo)
1283 {
1284     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNewWindowAction(page, frame, navigationAction, request, frameName, userData);
1285 }
1286
1287 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
1288 {
1289     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForResponse(page, frame, response, request, userData);
1290 }
1291
1292 void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef* userData, const void* clientInfo)
1293 {
1294     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->unableToImplementPolicy(page, frame, error, userData);
1295 }
1296
1297 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData)
1298 {
1299     auto& injectedBundle = InjectedBundle::singleton();
1300     if (!injectedBundle.isTestRunning())
1301         return WKBundlePagePolicyActionUse;
1302
1303     if (injectedBundle.testRunner()->shouldDumpPolicyCallbacks()) {
1304         StringBuilder stringBuilder;
1305         stringBuilder.appendLiteral(" - decidePolicyForNavigationAction \n");
1306         dumpRequestDescriptionSuitableForTestResult(request, stringBuilder);
1307         stringBuilder.appendLiteral(" is main frame - ");
1308         stringBuilder.append(WKBundleFrameIsMainFrame(frame) ? "yes" : "no");
1309         stringBuilder.appendLiteral(" should open URLs externally - ");
1310         stringBuilder.append(WKBundleNavigationActionGetShouldOpenExternalURLs(navigationAction) ? "yes" : "no");
1311         stringBuilder.append('\n');
1312         injectedBundle.outputText(stringBuilder.toString());
1313     }
1314
1315     if (injectedBundle.testRunner()->shouldDecideNavigationPolicyAfterDelay())
1316         return WKBundlePagePolicyActionPassThrough;
1317
1318     if (!injectedBundle.testRunner()->isPolicyDelegateEnabled()) {
1319         WKRetainPtr<WKStringRef> downloadAttributeRef(AdoptWK, WKBundleNavigationActionCopyDownloadAttribute(navigationAction));
1320         String downloadAttribute = toWTFString(downloadAttributeRef);
1321         return downloadAttribute.isNull() ? WKBundlePagePolicyActionUse : WKBundlePagePolicyActionPassThrough;
1322     }
1323
1324     WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
1325     WKRetainPtr<WKStringRef> urlScheme = adoptWK(WKURLCopyScheme(url.get()));
1326
1327     StringBuilder stringBuilder;
1328     stringBuilder.appendLiteral("Policy delegate: attempt to load ");
1329     if (isLocalFileScheme(urlScheme.get())) {
1330         WKRetainPtr<WKStringRef> filename = adoptWK(WKURLCopyLastPathComponent(url.get()));
1331         stringBuilder.append(toWTFString(filename));
1332     } else {
1333         WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
1334         stringBuilder.append(toWTFString(urlString));
1335     }
1336     stringBuilder.appendLiteral(" with navigation type \'");
1337     stringBuilder.append(toWTFString(NavigationTypeToString(WKBundleNavigationActionGetNavigationType(navigationAction))));
1338     stringBuilder.appendLiteral("\'");
1339     WKBundleHitTestResultRef hitTestResultRef = WKBundleNavigationActionCopyHitTestResult(navigationAction);
1340     if (hitTestResultRef) {
1341         stringBuilder.appendLiteral(" originating from ");
1342         stringBuilder.append(dumpPath(m_page, m_world.get(), WKBundleHitTestResultCopyNodeHandle(hitTestResultRef)));
1343     }
1344
1345     stringBuilder.append('\n');
1346     injectedBundle.outputText(stringBuilder.toString());
1347     injectedBundle.testRunner()->notifyDone();
1348
1349     if (injectedBundle.testRunner()->isPolicyDelegatePermissive())
1350         return WKBundlePagePolicyActionUse;
1351     return WKBundlePagePolicyActionPassThrough;
1352 }
1353
1354 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKStringRef, WKTypeRef*)
1355 {
1356     return WKBundlePagePolicyActionUse;
1357 }
1358
1359 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef, WKURLResponseRef response, WKURLRequestRef, WKTypeRef*)
1360 {
1361     if (InjectedBundle::singleton().testRunner()->isPolicyDelegateEnabled() && WKURLResponseIsAttachment(response)) {
1362         StringBuilder stringBuilder;
1363         WKRetainPtr<WKStringRef> filename = adoptWK(WKURLResponseCopySuggestedFilename(response));
1364         stringBuilder.appendLiteral("Policy delegate: resource is an attachment, suggested file name \'");
1365         stringBuilder.append(toWTFString(filename));
1366         stringBuilder.appendLiteral("\'\n");
1367         InjectedBundle::singleton().outputText(stringBuilder.toString());
1368     }
1369
1370     WKRetainPtr<WKStringRef> mimeType = adoptWK(WKURLResponseCopyMIMEType(response));
1371     return WKBundlePageCanShowMIMEType(page, mimeType.get()) ? WKBundlePagePolicyActionUse : WKBundlePagePolicyActionPassThrough;
1372 }
1373
1374 void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*)
1375 {
1376 }
1377
1378 // UI Client Callbacks
1379
1380 void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
1381 {
1382     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
1383 }
1384
1385 void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
1386 {
1387     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
1388 }
1389
1390 void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
1391 {
1392     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
1393 }
1394
1395 void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
1396 {
1397     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
1398 }
1399
1400 void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
1401 {
1402     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
1403 }
1404
1405 void InjectedBundlePage::didReachApplicationCacheOriginQuota(WKBundlePageRef page, WKSecurityOriginRef origin, int64_t totalBytesNeeded, const void* clientInfo)
1406 {
1407     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReachApplicationCacheOriginQuota(origin, totalBytesNeeded);
1408 }
1409
1410 uint64_t InjectedBundlePage::didExceedDatabaseQuota(WKBundlePageRef page, WKSecurityOriginRef origin, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes, const void* clientInfo)
1411 {
1412     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didExceedDatabaseQuota(origin, databaseName, databaseDisplayName, currentQuotaBytes, currentOriginUsageBytes, currentDatabaseUsageBytes, expectedUsageBytes);
1413 }
1414
1415 static WTF::String lastFileURLPathComponent(const WTF::String& path)
1416 {
1417     size_t pos = path.find("file://");
1418     ASSERT(WTF::notFound != pos);
1419
1420     WTF::String tmpPath = path.substring(pos + 7);
1421     if (tmpPath.length() < 2) // Keep the lone slash to avoid empty output.
1422         return tmpPath;
1423
1424     // Remove the trailing delimiter
1425     if (tmpPath[tmpPath.length() - 1] == '/')
1426         tmpPath.remove(tmpPath.length() - 1);
1427
1428     pos = tmpPath.reverseFind('/');
1429     if (WTF::notFound != pos)
1430         return tmpPath.substring(pos + 1);
1431
1432     return tmpPath;
1433 }
1434
1435 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
1436 {
1437     auto& injectedBundle = InjectedBundle::singleton();
1438     if (!injectedBundle.isTestRunning())
1439         return;
1440
1441     WTF::String messageString = toWTFString(message);
1442     size_t nullCharPos = messageString.find(UChar(0));
1443     if (nullCharPos != WTF::notFound)
1444         messageString.truncate(nullCharPos);
1445
1446     size_t fileProtocolStart = messageString.find("file://");
1447     if (fileProtocolStart != WTF::notFound)
1448         // FIXME: The code below does not handle additional text after url nor multiple urls. This matches DumpRenderTree implementation.
1449         messageString = messageString.substring(0, fileProtocolStart) + lastFileURLPathComponent(messageString.substring(fileProtocolStart));
1450
1451     StringBuilder stringBuilder;
1452     stringBuilder.appendLiteral("CONSOLE MESSAGE: ");
1453     if (lineNumber) {
1454         stringBuilder.appendLiteral("line ");
1455         stringBuilder.appendNumber(lineNumber);
1456         stringBuilder.appendLiteral(": ");
1457     }
1458     stringBuilder.append(messageString);
1459     stringBuilder.append('\n');
1460     injectedBundle.outputText(stringBuilder.toString());
1461 }
1462
1463 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
1464 {
1465     auto& injectedBundle = InjectedBundle::singleton();
1466     if (!injectedBundle.isTestRunning())
1467         return;
1468
1469     if (!injectedBundle.testRunner()->shouldDumpStatusCallbacks())
1470         return;
1471
1472     StringBuilder stringBuilder;
1473     stringBuilder.appendLiteral("UI DELEGATE STATUS CALLBACK: setStatusText:");
1474     stringBuilder.append(toWTFString(statusbarText));
1475     stringBuilder.append('\n');
1476     injectedBundle.outputText(stringBuilder.toString());
1477 }
1478
1479 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
1480 {
1481     auto& injectedBundle = InjectedBundle::singleton();
1482     if (!injectedBundle.isTestRunning())
1483         return;
1484
1485     StringBuilder stringBuilder;
1486     stringBuilder.appendLiteral("ALERT: ");
1487     stringBuilder.append(toWTFString(message));
1488     stringBuilder.append('\n');
1489     injectedBundle.outputText(stringBuilder.toString());
1490 }
1491
1492 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
1493 {
1494     auto& injectedBundle = InjectedBundle::singleton();
1495     if (!injectedBundle.isTestRunning())
1496         return;
1497
1498     StringBuilder stringBuilder;
1499     stringBuilder.appendLiteral("CONFIRM: ");
1500     stringBuilder.append(toWTFString(message));
1501     stringBuilder.append('\n');
1502     injectedBundle.outputText(stringBuilder.toString());
1503 }
1504
1505 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
1506 {
1507     StringBuilder stringBuilder;
1508     stringBuilder.appendLiteral("PROMPT: ");
1509     stringBuilder.append(toWTFString(message));
1510     stringBuilder.appendLiteral(", default text: ");
1511     stringBuilder.append(toWTFString(defaultValue));
1512     stringBuilder.append('\n');
1513     InjectedBundle::singleton().outputText(stringBuilder.toString());
1514 }
1515
1516 void InjectedBundlePage::didReachApplicationCacheOriginQuota(WKSecurityOriginRef origin, int64_t totalBytesNeeded)
1517 {
1518     auto& injectedBundle = InjectedBundle::singleton();
1519     if (injectedBundle.testRunner()->shouldDumpApplicationCacheDelegateCallbacks()) {
1520         // For example, numbers from 30000 - 39999 will output as 30000.
1521         // Rounding up or down does not really matter for these tests. It's
1522         // sufficient to just get a range of 10000 to determine if we were
1523         // above or below a threshold.
1524         int64_t truncatedSpaceNeeded = (totalBytesNeeded / 10000) * 10000;
1525
1526         StringBuilder stringBuilder;
1527         stringBuilder.appendLiteral("UI DELEGATE APPLICATION CACHE CALLBACK: exceededApplicationCacheOriginQuotaForSecurityOrigin:");
1528         stringBuilder.append(securityOriginToStr(origin));
1529         stringBuilder.appendLiteral(" totalSpaceNeeded:~");
1530         stringBuilder.appendNumber(truncatedSpaceNeeded);
1531         stringBuilder.append('\n');
1532         injectedBundle.outputText(stringBuilder.toString());
1533     }
1534
1535     if (injectedBundle.testRunner()->shouldDisallowIncreaseForApplicationCacheQuota())
1536         return;
1537
1538     // Reset default application cache quota.
1539     WKBundlePageResetApplicationCacheOriginQuota(injectedBundle.page()->page(), adoptWK(WKSecurityOriginCopyToString(origin)).get());
1540 }
1541
1542 uint64_t InjectedBundlePage::didExceedDatabaseQuota(WKSecurityOriginRef origin, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes)
1543 {
1544     auto& injectedBundle = InjectedBundle::singleton();
1545     if (injectedBundle.testRunner()->shouldDumpDatabaseCallbacks()) {
1546         StringBuilder stringBuilder;
1547         stringBuilder.appendLiteral("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:");
1548         stringBuilder.append(securityOriginToStr(origin));
1549         stringBuilder.appendLiteral(" database:");
1550         stringBuilder.append(toWTFString(databaseName));
1551         stringBuilder.append('\n');
1552         injectedBundle.outputText(stringBuilder.toString());
1553     }
1554
1555     uint64_t defaultQuota = 5 * 1024 * 1024;
1556     double testDefaultQuota = injectedBundle.testRunner()->databaseDefaultQuota();
1557     if (testDefaultQuota >= 0)
1558         defaultQuota = testDefaultQuota;
1559
1560     unsigned long long newQuota = defaultQuota;
1561
1562     double maxQuota = injectedBundle.testRunner()->databaseMaxQuota();
1563     if (maxQuota >= 0) {
1564         if (defaultQuota < expectedUsageBytes && expectedUsageBytes <= maxQuota) {
1565             newQuota = expectedUsageBytes;
1566
1567             StringBuilder stringBuilder;
1568             stringBuilder.appendLiteral("UI DELEGATE DATABASE CALLBACK: increased quota to ");
1569             stringBuilder.appendNumber(newQuota);
1570             stringBuilder.append('\n');
1571             injectedBundle.outputText(stringBuilder.toString());
1572         }
1573     }
1574     return newQuota;
1575 }
1576
1577 // Editor Client Callbacks
1578
1579 bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
1580 {
1581     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
1582 }
1583
1584 bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
1585 {
1586     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
1587 }
1588
1589 bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
1590 {
1591     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
1592 }
1593
1594 bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
1595 {
1596     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
1597 }
1598
1599 bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
1600 {
1601     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
1602 }
1603
1604 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
1605 {
1606     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
1607 }
1608
1609 bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
1610 {
1611     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
1612 }
1613
1614 void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1615 {
1616     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
1617 }
1618
1619 void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1620 {
1621     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
1622 }
1623
1624 void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1625 {
1626     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
1627 }
1628
1629 void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1630 {
1631     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
1632 }
1633
1634 bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
1635 {
1636     auto& injectedBundle = InjectedBundle::singleton();
1637     if (!injectedBundle.isTestRunning())
1638         return true;
1639
1640     if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1641         StringBuilder stringBuilder;
1642         stringBuilder.appendLiteral("EDITING DELEGATE: shouldBeginEditingInDOMRange:");
1643         stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1644         stringBuilder.append('\n');
1645         injectedBundle.outputText(stringBuilder.toString());
1646     }
1647     return injectedBundle.testRunner()->shouldAllowEditing();
1648 }
1649
1650 bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
1651 {
1652     auto& injectedBundle = InjectedBundle::singleton();
1653     if (!injectedBundle.isTestRunning())
1654         return true;
1655
1656     if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1657         StringBuilder stringBuilder;
1658         stringBuilder.appendLiteral("EDITING DELEGATE: shouldEndEditingInDOMRange:");
1659         stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1660         stringBuilder.append('\n');
1661         injectedBundle.outputText(stringBuilder.toString());
1662     }
1663     return injectedBundle.testRunner()->shouldAllowEditing();
1664 }
1665
1666 bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
1667 {
1668     auto& injectedBundle = InjectedBundle::singleton();
1669     if (!injectedBundle.isTestRunning())
1670         return true;
1671
1672     static const char* insertactionstring[] = {
1673         "WebViewInsertActionTyped",
1674         "WebViewInsertActionPasted",
1675         "WebViewInsertActionDropped",
1676     };
1677
1678     if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1679         StringBuilder stringBuilder;
1680         stringBuilder.appendLiteral("EDITING DELEGATE: shouldInsertNode:");
1681         stringBuilder.append(dumpPath(m_page, m_world.get(), node));
1682         stringBuilder.appendLiteral(" replacingDOMRange:");
1683         stringBuilder.append(rangeToStr(m_page, m_world.get(), rangeToReplace));
1684         stringBuilder.appendLiteral(" givenAction:");
1685         stringBuilder.append(insertactionstring[action]);
1686         stringBuilder.append('\n');
1687         injectedBundle.outputText(stringBuilder.toString());
1688     }
1689     return injectedBundle.testRunner()->shouldAllowEditing();
1690 }
1691
1692 bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
1693 {
1694     auto& injectedBundle = InjectedBundle::singleton();
1695     if (!injectedBundle.isTestRunning())
1696         return true;
1697
1698     static const char *insertactionstring[] = {
1699         "WebViewInsertActionTyped",
1700         "WebViewInsertActionPasted",
1701         "WebViewInsertActionDropped",
1702     };
1703
1704     if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1705         StringBuilder stringBuilder;
1706         stringBuilder.appendLiteral("EDITING DELEGATE: shouldInsertText:");
1707         stringBuilder.append(toWTFString(text));
1708         stringBuilder.appendLiteral(" replacingDOMRange:");
1709         stringBuilder.append(rangeToStr(m_page, m_world.get(), rangeToReplace));
1710         stringBuilder.appendLiteral(" givenAction:");
1711         stringBuilder.append(insertactionstring[action]);
1712         stringBuilder.append('\n');
1713         injectedBundle.outputText(stringBuilder.toString());
1714     }
1715     return injectedBundle.testRunner()->shouldAllowEditing();
1716 }
1717
1718 bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
1719 {
1720     auto& injectedBundle = InjectedBundle::singleton();
1721     if (!injectedBundle.isTestRunning())
1722         return true;
1723
1724     if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1725         StringBuilder stringBuilder;
1726         stringBuilder.appendLiteral("EDITING DELEGATE: shouldDeleteDOMRange:");
1727         stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1728         stringBuilder.append('\n');
1729         injectedBundle.outputText(stringBuilder.toString());
1730     }
1731     return injectedBundle.testRunner()->shouldAllowEditing();
1732 }
1733
1734 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
1735 {
1736     auto& injectedBundle = InjectedBundle::singleton();
1737     if (!injectedBundle.isTestRunning())
1738         return true;
1739
1740     static const char *affinitystring[] = {
1741         "NSSelectionAffinityUpstream",
1742         "NSSelectionAffinityDownstream"
1743     };
1744     static const char *boolstring[] = {
1745         "FALSE",
1746         "TRUE"
1747     };
1748
1749     if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1750         StringBuilder stringBuilder;
1751         stringBuilder.appendLiteral("EDITING DELEGATE: shouldChangeSelectedDOMRange:");
1752         stringBuilder.append(rangeToStr(m_page, m_world.get(), fromRange));
1753         stringBuilder.appendLiteral(" toDOMRange:");
1754         stringBuilder.append(rangeToStr(m_page, m_world.get(), toRange));
1755         stringBuilder.appendLiteral(" affinity:");
1756         stringBuilder.append(affinitystring[affinity]); 
1757         stringBuilder.appendLiteral(" stillSelecting:");
1758         stringBuilder.append(boolstring[stillSelecting]); 
1759         stringBuilder.append('\n');
1760         injectedBundle.outputText(stringBuilder.toString());
1761     }
1762     return injectedBundle.testRunner()->shouldAllowEditing();
1763 }
1764
1765 bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
1766 {
1767     auto& injectedBundle = InjectedBundle::singleton();
1768     if (!injectedBundle.isTestRunning())
1769         return true;
1770
1771     if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1772         StringBuilder stringBuilder;
1773         stringBuilder.appendLiteral("EDITING DELEGATE: shouldApplyStyle:");
1774         stringBuilder.append(styleDecToStr(style));
1775         stringBuilder.appendLiteral(" toElementsInDOMRange:");
1776         stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1777         stringBuilder.append('\n');
1778         injectedBundle.outputText(stringBuilder.toString());
1779     }
1780     return injectedBundle.testRunner()->shouldAllowEditing();
1781 }
1782
1783 void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
1784 {
1785     auto& injectedBundle = InjectedBundle::singleton();
1786     if (!injectedBundle.isTestRunning())
1787         return;
1788     if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1789         return;
1790
1791     StringBuilder stringBuilder;
1792     stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidBeginEditing:");
1793     stringBuilder.append(toWTFString(notificationName));
1794     stringBuilder.append('\n');
1795     injectedBundle.outputText(stringBuilder.toString());
1796 }
1797
1798 void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
1799 {
1800     auto& injectedBundle = InjectedBundle::singleton();
1801     if (!injectedBundle.isTestRunning())
1802         return;
1803     if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1804         return;
1805
1806     StringBuilder stringBuilder;
1807     stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidEndEditing:");
1808     stringBuilder.append(toWTFString(notificationName));
1809     stringBuilder.append('\n');
1810     injectedBundle.outputText(stringBuilder.toString());
1811 }
1812
1813 void InjectedBundlePage::didChange(WKStringRef notificationName)
1814 {
1815     auto& injectedBundle = InjectedBundle::singleton();
1816     if (!injectedBundle.isTestRunning())
1817         return;
1818     if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1819         return;
1820
1821     StringBuilder stringBuilder;
1822     stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidChange:");
1823     stringBuilder.append(toWTFString(notificationName));
1824     stringBuilder.append('\n');
1825     injectedBundle.outputText(stringBuilder.toString());
1826 }
1827
1828 void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
1829 {
1830     auto& injectedBundle = InjectedBundle::singleton();
1831     if (!injectedBundle.isTestRunning())
1832         return;
1833     if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1834         return;
1835
1836     StringBuilder stringBuilder;
1837     stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidChangeSelection:");
1838     stringBuilder.append(toWTFString(notificationName));
1839     stringBuilder.append('\n');
1840     injectedBundle.outputText(stringBuilder.toString());
1841 }
1842
1843 #if ENABLE(FULLSCREEN_API)
1844 bool InjectedBundlePage::supportsFullScreen(WKBundlePageRef pageRef, WKFullScreenKeyboardRequestType requestType)
1845 {
1846     auto& injectedBundle = InjectedBundle::singleton();
1847     if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1848         injectedBundle.outputText("supportsFullScreen() == true\n");
1849     return true;
1850 }
1851
1852 void InjectedBundlePage::enterFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
1853 {
1854     auto& injectedBundle = InjectedBundle::singleton();
1855     if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1856         injectedBundle.outputText("enterFullScreenForElement()\n");
1857
1858     if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
1859         WKBundlePageWillEnterFullScreen(pageRef);
1860         WKBundlePageDidEnterFullScreen(pageRef);
1861     }
1862 }
1863
1864 void InjectedBundlePage::exitFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
1865 {
1866     auto& injectedBundle = InjectedBundle::singleton();
1867     if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1868         injectedBundle.outputText("exitFullScreenForElement()\n");
1869
1870     if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
1871         WKBundlePageWillExitFullScreen(pageRef);
1872         WKBundlePageDidExitFullScreen(pageRef);
1873     }
1874 }
1875
1876 void InjectedBundlePage::beganEnterFullScreen(WKBundlePageRef, WKRect, WKRect)
1877 {
1878     auto& injectedBundle = InjectedBundle::singleton();
1879     if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1880         injectedBundle.outputText("beganEnterFullScreen()\n");
1881 }
1882
1883 void InjectedBundlePage::beganExitFullScreen(WKBundlePageRef, WKRect, WKRect)
1884 {
1885     auto& injectedBundle = InjectedBundle::singleton();
1886     if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1887         injectedBundle.outputText("beganExitFullScreen()\n");
1888 }
1889
1890 void InjectedBundlePage::closeFullScreen(WKBundlePageRef pageRef)
1891 {
1892     auto& injectedBundle = InjectedBundle::singleton();
1893     if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1894         injectedBundle.outputText("closeFullScreen()\n");
1895
1896     if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
1897         WKBundlePageWillExitFullScreen(pageRef);
1898         WKBundlePageDidExitFullScreen(pageRef);
1899     }
1900 }
1901 #endif
1902
1903 static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
1904 {
1905     return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
1906 }
1907
1908 static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem, StringBuilder& stringBuilder)
1909 {
1910     unsigned column = 0;
1911     if (isCurrentItem) {
1912         stringBuilder.appendLiteral("curr->");
1913         column = 6;
1914     }
1915     for (unsigned i = column; i < indent; i++)
1916         stringBuilder.append(' ');
1917
1918     WTF::String url = toWTFString(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
1919     if (hasPrefix(url, "file:")) {
1920         WTF::String directoryName = "/LayoutTests/";
1921         size_t start = url.find(directoryName);
1922         if (start == WTF::notFound)
1923             start = 0;
1924         else
1925             start += directoryName.length();
1926         stringBuilder.appendLiteral("(file test):");
1927         stringBuilder.append(url.substring(start));
1928     } else
1929         stringBuilder.append(url);
1930
1931     WTF::String target = toWTFString(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
1932     if (target.length()) {
1933         stringBuilder.appendLiteral(" (in frame \"");
1934         stringBuilder.append(target);
1935         stringBuilder.appendLiteral("\")");
1936     }
1937
1938     // FIXME: Need WKBackForwardListItemIsTargetItem.
1939     if (WKBundleBackForwardListItemIsTargetItem(item))
1940         stringBuilder.appendLiteral("  **nav target**");
1941
1942     stringBuilder.append('\n');
1943
1944     if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
1945         // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
1946         size_t size = WKArrayGetSize(kids.get());
1947         Vector<WKBundleBackForwardListItemRef> sortedKids(size);
1948         for (size_t i = 0; i < size; ++i)
1949             sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
1950         stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
1951         for (size_t i = 0; i < size; ++i)
1952             dumpBackForwardListItem(sortedKids[i], indent + 4, false, stringBuilder);
1953     }
1954 }
1955
1956 void InjectedBundlePage::dumpBackForwardList(StringBuilder& stringBuilder)
1957 {
1958     stringBuilder.appendLiteral("\n============== Back Forward List ==============\n");
1959
1960     WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
1961
1962     // Print out all items in the list after m_previousTestBackForwardListItem.
1963     // Gather items from the end of the list, then print them out from oldest to newest.
1964     Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
1965     for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
1966         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
1967         // Something is wrong if the item from the last test is in the forward part of the list.
1968         ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
1969         itemsToPrint.append(item);
1970     }
1971
1972     ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
1973
1974     itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
1975
1976     int currentItemIndex = itemsToPrint.size() - 1;
1977
1978     int backListCount = WKBundleBackForwardListGetBackListCount(list);
1979     for (int i = -1; i >= -backListCount; --i) {
1980         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
1981         if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
1982             break;
1983         itemsToPrint.append(item);
1984     }
1985
1986     for (int i = itemsToPrint.size() - 1; i >= 0; i--)
1987         dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex, stringBuilder);
1988
1989     stringBuilder.appendLiteral("===============================================\n");
1990 }
1991
1992 #if !PLATFORM(COCOA)
1993 void InjectedBundlePage::platformDidStartProvisionalLoadForFrame(WKBundleFrameRef)
1994 {
1995 }
1996
1997 String InjectedBundlePage::platformResponseMimeType(WKURLResponseRef)
1998 {
1999     return String();
2000 }
2001 #endif
2002
2003 void InjectedBundlePage::frameDidChangeLocation(WKBundleFrameRef frame, bool shouldDump)
2004 {
2005     auto& injectedBundle = InjectedBundle::singleton();
2006     if (frame != injectedBundle.topLoadingFrame())
2007         return;
2008
2009     injectedBundle.setTopLoadingFrame(nullptr);
2010
2011     if (injectedBundle.testRunner()->waitToDump())
2012         return;
2013
2014     if (injectedBundle.shouldProcessWorkQueue()) {
2015         injectedBundle.processWorkQueue();
2016         return;
2017     }
2018
2019     if (shouldDump)
2020         injectedBundle.page()->dump();
2021     else
2022         injectedBundle.done();
2023 }
2024
2025 } // namespace WTR