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