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