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