656f97ae786cb3eb9e8e74543499df89fb359977
[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 "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         shouldLoadResourceForFrame
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     };
208     WKBundlePageSetUIClient(m_page, &uiClient);
209
210     WKBundlePageEditorClient editorClient = {
211         0,
212         this,
213         shouldBeginEditing,
214         shouldEndEditing,
215         shouldInsertNode,
216         shouldInsertText,
217         shouldDeleteRange,
218         shouldChangeSelectedRange,
219         shouldApplyStyle,
220         didBeginEditing,
221         didEndEditing,
222         didChange,
223         didChangeSelection
224     };
225     WKBundlePageSetEditorClient(m_page, &editorClient);
226 }
227
228 InjectedBundlePage::~InjectedBundlePage()
229 {
230 }
231
232 void InjectedBundlePage::stopLoading()
233 {
234     WKBundlePageStopLoading(m_page);
235 }
236
237 void InjectedBundlePage::reset()
238 {
239     WKBundlePageClearMainFrameName(m_page);
240
241     WKBundlePageSetPageZoomFactor(m_page, 1);
242     WKBundlePageSetTextZoomFactor(m_page, 1);
243
244     m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
245 }
246
247 // Loader Client Callbacks
248
249 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
250 {
251     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
252 }
253
254 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
255 {
256     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
257 }
258
259 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
260 {
261     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
262 }
263
264 void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
265 {
266     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
267 }
268
269 void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
270 {
271     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
272 }
273
274 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
275 {
276     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
277 }
278
279 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
280 {
281     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
282 }
283
284 void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
285 {
286     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
287 }
288
289 void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
290 {
291     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
292 }
293
294 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
295 {
296     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
297 }
298
299 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
300 {
301     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(frame, url, delay, date);
302 }
303
304 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
305 {
306     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
307 }
308
309 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
310 {
311     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
312 }
313
314 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
315 {
316     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
317 }
318
319 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
320 {
321     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
322 }
323
324 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKStringRef, const void* clientInfo)
325 {
326     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldLoadResourceForFrame(frame);
327 }
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     if (WKBundleScriptWorldNormalWorld() != world)
530         return;
531
532     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
533     JSObjectRef window = JSContextGetGlobalObject(context);
534
535     JSValueRef exception = 0;
536     InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception);
537     InjectedBundle::shared().gcController()->makeWindowObject(context, window, &exception);
538     InjectedBundle::shared().eventSendingController()->makeWindowObject(context, window, &exception);
539 }
540
541 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
542 {
543 }
544
545 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundleFrameRef frame, WKURLRef url, double delay, double date)
546 {
547 }
548
549 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
550 {
551 }
552
553 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
554 {
555     if (!InjectedBundle::shared().isTestRunning())
556         return;
557
558     unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
559     if (pendingFrameUnloadEvents)
560         InjectedBundle::shared().os() << frame << " - has " << pendingFrameUnloadEvents << " onunload handler(s)\n";
561 }
562
563 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
564 {
565 }
566
567 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef frame)
568 {
569 }
570
571 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef frame)
572 {
573 }
574
575 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundleFrameRef frame)
576 {
577     return true;
578 }
579
580 // UI Client Callbacks
581
582 void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
583 {
584     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
585 }
586
587 void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
588 {
589     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
590 }
591
592 void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
593 {
594     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
595 }
596
597 void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
598 {
599     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
600 }
601
602 void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
603 {
604     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
605 }
606
607 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
608 {
609     if (!InjectedBundle::shared().isTestRunning())
610         return;
611
612     // FIXME: Strip file: urls.
613     InjectedBundle::shared().os() << "CONSOLE MESSAGE: line " << lineNumber << ": " << message << "\n";
614 }
615
616 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
617 {
618     if (!InjectedBundle::shared().isTestRunning())
619         return;
620
621     if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks())
622         return;
623
624     InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << statusbarText << "\n";
625 }
626
627 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
628 {
629     if (!InjectedBundle::shared().isTestRunning())
630         return;
631
632     InjectedBundle::shared().os() << "ALERT: " << message << "\n";
633 }
634
635 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
636 {
637     if (!InjectedBundle::shared().isTestRunning())
638         return;
639
640     InjectedBundle::shared().os() << "CONFIRM: " << message << "\n";
641 }
642
643 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
644 {
645     InjectedBundle::shared().os() << "PROMPT: " << message << ", default text: " << defaultValue <<  "\n";
646 }
647
648 // Editor Client Callbacks
649
650 bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
651 {
652     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
653 }
654
655 bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
656 {
657     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
658 }
659
660 bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
661 {
662     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
663 }
664
665 bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
666 {
667     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
668 }
669
670 bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
671 {
672     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
673 }
674
675 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
676 {
677     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
678 }
679
680 bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
681 {
682     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
683 }
684
685 void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
686 {
687     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
688 }
689
690 void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
691 {
692     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
693 }
694
695 void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
696 {
697     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
698 }
699
700 void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
701 {
702     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
703 }
704
705 bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
706 {
707     if (!InjectedBundle::shared().isTestRunning())
708         return true;
709
710     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
711         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldBeginEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
712     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
713 }
714
715 bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
716 {
717     if (!InjectedBundle::shared().isTestRunning())
718         return true;
719
720     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
721         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldEndEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
722     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
723 }
724
725 bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
726 {
727     if (!InjectedBundle::shared().isTestRunning())
728         return true;
729
730     static const char* insertactionstring[] = {
731         "WebViewInsertActionTyped",
732         "WebViewInsertActionPasted",
733         "WebViewInsertActionDropped",
734     };
735
736     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
737         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";
738     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
739 }
740
741 bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
742 {
743     if (!InjectedBundle::shared().isTestRunning())
744         return true;
745
746     static const char *insertactionstring[] = {
747         "WebViewInsertActionTyped",
748         "WebViewInsertActionPasted",
749         "WebViewInsertActionDropped",
750     };
751
752     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
753         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertText:" << text << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
754     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
755 }
756
757 bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
758 {
759     if (!InjectedBundle::shared().isTestRunning())
760         return true;
761
762     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
763         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldDeleteDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
764     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
765 }
766
767 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
768 {
769     if (!InjectedBundle::shared().isTestRunning())
770         return true;
771
772     static const char *affinitystring[] = {
773         "NSSelectionAffinityUpstream",
774         "NSSelectionAffinityDownstream"
775     };
776     static const char *boolstring[] = {
777         "FALSE",
778         "TRUE"
779     };
780
781     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
782         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";
783     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
784 }
785
786 bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
787 {
788     if (!InjectedBundle::shared().isTestRunning())
789         return true;
790
791     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
792         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldApplyStyle:" << style << " toElementsInDOMRange:" << toStr(m_page, m_world.get(), range)  << "\n";
793     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
794 }
795
796 void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
797 {
798     if (!InjectedBundle::shared().isTestRunning())
799         return;
800
801     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
802         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidBeginEditing:" << notificationName << "\n";
803 }
804
805 void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
806 {
807     if (!InjectedBundle::shared().isTestRunning())
808         return;
809
810     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
811         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidEndEditing:" << notificationName << "\n";
812 }
813
814 void InjectedBundlePage::didChange(WKStringRef notificationName)
815 {
816     if (!InjectedBundle::shared().isTestRunning())
817         return;
818
819     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
820         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChange:" << notificationName << "\n";
821 }
822
823 void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
824 {
825     if (!InjectedBundle::shared().isTestRunning())
826         return;
827
828     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
829         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChangeSelection:" << notificationName << "\n";
830 }
831
832 static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
833 {
834     return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
835 }
836
837 static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem)
838 {
839     unsigned column = 0;
840     if (isCurrentItem) {
841         InjectedBundle::shared().os() << "curr->";
842         column = 6;
843     }
844     for (unsigned i = column; i < indent; i++)
845         InjectedBundle::shared().os() << ' ';
846
847     string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
848     if (hasPrefix(url, "file:")) {
849         string directoryName = "/LayoutTests/";
850         size_t start = url.find(directoryName);
851         if (start == string::npos)
852             start = 0;
853         else
854             start += directoryName.size();
855         InjectedBundle::shared().os() << "(file test):" << url.substr(start);
856     } else
857         InjectedBundle::shared().os() << url;
858
859     string target = toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
860     if (target.length())
861         InjectedBundle::shared().os() << " (in frame \"" << target << "\")";
862
863     // FIXME: Need WKBackForwardListItemIsTargetItem.
864     if (WKBundleBackForwardListItemIsTargetItem(item))
865         InjectedBundle::shared().os() << "  **nav target**";
866
867     InjectedBundle::shared().os() << '\n';
868
869     if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
870         // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
871         size_t size = WKArrayGetSize(kids.get());
872         Vector<WKBundleBackForwardListItemRef> sortedKids(size);
873         for (size_t i = 0; i < size; ++i)
874             sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
875         stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
876         for (size_t i = 0; i < size; ++i)
877             dumpBackForwardListItem(sortedKids[i], indent + 4, false);
878     }
879 }
880
881 void InjectedBundlePage::dumpBackForwardList()
882 {
883     InjectedBundle::shared().os() << "\n============== Back Forward List ==============\n";
884
885     WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
886
887     // Print out all items in the list after m_previousTestBackForwardListItem.
888     // Gather items from the end of the list, then print them out from oldest to newest.
889     Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
890     for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
891         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
892         // Something is wrong if the item from the last test is in the forward part of the list.
893         ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
894         itemsToPrint.append(item);
895     }
896
897     ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
898
899     itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
900
901     int currentItemIndex = itemsToPrint.size() - 1;
902
903     int backListCount = WKBundleBackForwardListGetBackListCount(list);
904     for (int i = -1; i >= -backListCount; --i) {
905         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
906         if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
907             break;
908         itemsToPrint.append(item);
909     }
910
911     for (int i = itemsToPrint.size() - 1; i >= 0; i--)
912         dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex);
913
914     InjectedBundle::shared().os() << "===============================================\n";
915 }
916
917 } // namespace WTR