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