ShouldLoadResourceForFrame should use strings, not URLs.
[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     , m_isLoading(false)
172 {
173     WKBundlePageLoaderClient loaderClient = {
174         0,
175         this,
176         didStartProvisionalLoadForFrame,
177         didReceiveServerRedirectForProvisionalLoadForFrame,
178         didFailProvisionalLoadWithErrorForFrame,
179         didCommitLoadForFrame,
180         didFinishDocumentLoadForFrame,
181         didFinishLoadForFrame,
182         didFailLoadWithErrorForFrame,
183         didSameDocumentNavigationForFrame,
184         didReceiveTitleForFrame,
185         0,
186         0,
187         0,
188         didDisplayInsecureContentForFrame,
189         didRunInsecureContentForFrame,
190         didClearWindowForFrame,
191         didCancelClientRedirectForFrame,
192         willPerformClientRedirectForFrame,
193         didHandleOnloadEventsForFrame,
194         shouldLoadResourceForFrame
195     };
196     WKBundlePageSetLoaderClient(m_page, &loaderClient);
197
198     WKBundlePageUIClient uiClient = {
199         0,
200         this,
201         willAddMessageToConsole,
202         willSetStatusbarText,
203         willRunJavaScriptAlert,
204         willRunJavaScriptConfirm,
205         willRunJavaScriptPrompt,
206         0, /*mouseDidMoveOverElement*/
207         0, /*pageDidScroll*/
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     m_isLoading = false;
237 }
238
239 void InjectedBundlePage::reset()
240 {
241     WKBundlePageClearMainFrameName(m_page);
242
243     WKBundlePageSetPageZoomFactor(m_page, 1);
244     WKBundlePageSetTextZoomFactor(m_page, 1);
245
246     m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
247 }
248
249 // Loader Client Callbacks
250
251 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
252 {
253     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
254 }
255
256 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
257 {
258     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
259 }
260
261 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
262 {
263     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
264 }
265
266 void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
267 {
268     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
269 }
270
271 void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
272 {
273     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
274 }
275
276 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
277 {
278     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
279 }
280
281 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
282 {
283     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
284 }
285
286 void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
287 {
288     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
289 }
290
291 void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
292 {
293     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
294 }
295
296 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
297 {
298     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
299 }
300
301 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
302 {
303     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(frame, url, delay, date);
304 }
305
306 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
307 {
308     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
309 }
310
311 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
312 {
313     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
314 }
315
316 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
317 {
318     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
319 }
320
321 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
322 {
323     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
324 }
325
326 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKStringRef, const void* clientInfo)
327 {
328     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldLoadResourceForFrame(frame);
329 }
330
331
332 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
333 {
334     if (!InjectedBundle::shared().isTestRunning())
335         return;
336
337     if (frame == WKBundlePageGetMainFrame(m_page))
338         m_isLoading = true;
339 }
340
341 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
342 {
343 }
344
345 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
346 {
347 }
348
349 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
350 {
351 }
352
353 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
354
355 static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
356 {
357     double x = numericWindowPropertyValue(frame, "pageXOffset");
358     double y = numericWindowPropertyValue(frame, "pageYOffset");
359     if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) {
360         if (shouldIncludeFrameName) {
361             WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
362             InjectedBundle::shared().os() << "frame '" << name << "' ";
363         }
364         InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n";
365     }
366 }
367
368 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame)
369 {
370     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
371     size_t size = WKArrayGetSize(childFrames.get());
372     for (size_t i = 0; i < size; ++i) {
373         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
374         dumpFrameScrollPosition(subframe, ShouldIncludeFrameName);
375         dumpDescendantFrameScrollPositions(subframe);
376     }
377 }
378
379 void InjectedBundlePage::dumpAllFrameScrollPositions()
380 {
381     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
382     dumpFrameScrollPosition(frame);
383     dumpDescendantFrameScrollPositions(frame);
384 }
385
386 static JSRetainPtr<JSStringRef> toJS(const char* string)
387 {
388     return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
389 }
390
391 static bool hasDocumentElement(WKBundleFrameRef frame)
392 {
393     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
394     JSObjectRef globalObject = JSContextGetGlobalObject(context);
395
396     JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
397     if (!documentValue)
398         return false;
399
400     ASSERT(JSValueIsObject(context, documentValue));
401     JSObjectRef document = JSValueToObject(context, documentValue, 0);
402
403     JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
404     if (!documentElementValue)
405         return false;
406
407     return JSValueToBoolean(context, documentElementValue);
408 }
409
410 static void dumpFrameText(WKBundleFrameRef frame)
411 {
412     // If the frame doesn't have a document element, its inner text will be an empty string, so
413     // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
414     // anything in this case, so we shouldn't either.
415     if (!hasDocumentElement(frame))
416         return;
417
418     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
419     InjectedBundle::shared().os() << text << "\n";
420 }
421
422 static void dumpDescendantFramesText(WKBundleFrameRef frame)
423 {
424     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
425     size_t size = WKArrayGetSize(childFrames.get());
426     for (size_t i = 0; i < size; ++i) {
427         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
428         WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
429         InjectedBundle::shared().os() << "\n--------\nFrame: '" << subframeName << "'\n--------\n";
430         dumpFrameText(subframe);
431         dumpDescendantFramesText(subframe);
432     }
433 }
434
435 void InjectedBundlePage::dumpAllFramesText()
436 {
437     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
438     dumpFrameText(frame);
439     dumpDescendantFramesText(frame);
440 }
441
442 void InjectedBundlePage::dump()
443 {
444     ASSERT(InjectedBundle::shared().isTestRunning());
445
446     InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdogTimer();
447
448     switch (InjectedBundle::shared().layoutTestController()->whatToDump()) {
449     case LayoutTestController::RenderTree: {
450         WKRetainPtr<WKStringRef> text(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page));
451         InjectedBundle::shared().os() << text;
452         break;
453     }
454     case LayoutTestController::MainFrameText:
455         dumpFrameText(WKBundlePageGetMainFrame(m_page));
456         break;
457     case LayoutTestController::AllFramesText:
458         dumpAllFramesText();
459         break;
460     }
461
462     if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions())
463         dumpAllFrameScrollPositions();
464     else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition())
465         dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page));
466
467     if (InjectedBundle::shared().layoutTestController()->shouldDumpBackForwardListsForAllWindows())
468         InjectedBundle::shared().dumpBackForwardListsForAllPages();
469
470     InjectedBundle::shared().done();
471 }
472
473 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
474 {
475     if (!InjectedBundle::shared().isTestRunning())
476         return;
477
478     if (!WKBundleFrameIsMainFrame(frame))
479         return;
480
481     m_isLoading = false;
482
483     if (this != InjectedBundle::shared().page())
484         return;
485
486     if (InjectedBundle::shared().layoutTestController()->waitToDump())
487         return;
488
489     dump();
490 }
491
492 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
493 {
494     if (!InjectedBundle::shared().isTestRunning())
495         return;
496
497     if (!WKBundleFrameIsMainFrame(frame))
498         return;
499
500     m_isLoading = false;
501
502     if (this != InjectedBundle::shared().page())
503         return;
504
505     InjectedBundle::shared().done();
506 }
507
508 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
509 {
510     if (!InjectedBundle::shared().isTestRunning())
511         return;
512
513     if (!InjectedBundle::shared().layoutTestController()->shouldDumpTitleChanges())
514         return;
515
516     InjectedBundle::shared().os() << "TITLE CHANGED: " << title << "\n";
517 }
518
519 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
520 {
521     if (!InjectedBundle::shared().isTestRunning())
522         return;
523
524     if (WKBundleScriptWorldNormalWorld() != world)
525         return;
526
527     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
528     JSObjectRef window = JSContextGetGlobalObject(context);
529
530     JSValueRef exception = 0;
531     InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception);
532     InjectedBundle::shared().gcController()->makeWindowObject(context, window, &exception);
533     InjectedBundle::shared().eventSendingController()->makeWindowObject(context, window, &exception);
534 }
535
536 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
537 {
538 }
539
540 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundleFrameRef frame, WKURLRef url, double delay, double date)
541 {
542 }
543
544 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
545 {
546 }
547
548 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
549 {
550     if (!InjectedBundle::shared().isTestRunning())
551         return;
552
553     unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
554     if (pendingFrameUnloadEvents)
555         InjectedBundle::shared().os() << frame << " - has " << pendingFrameUnloadEvents << " onunload handler(s)\n";
556 }
557
558 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
559 {
560 }
561
562 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef frame)
563 {
564 }
565
566 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef frame)
567 {
568 }
569
570 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundleFrameRef frame)
571 {
572     return true;
573 }
574
575 // UI Client Callbacks
576
577 void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
578 {
579     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
580 }
581
582 void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
583 {
584     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
585 }
586
587 void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
588 {
589     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
590 }
591
592 void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
593 {
594     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
595 }
596
597 void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
598 {
599     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
600 }
601
602 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
603 {
604     if (!InjectedBundle::shared().isTestRunning())
605         return;
606
607     // FIXME: Strip file: urls.
608     InjectedBundle::shared().os() << "CONSOLE MESSAGE: line " << lineNumber << ": " << message << "\n";
609 }
610
611 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
612 {
613     if (!InjectedBundle::shared().isTestRunning())
614         return;
615
616     if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks())
617         return;
618
619     InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << statusbarText << "\n";
620 }
621
622 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
623 {
624     if (!InjectedBundle::shared().isTestRunning())
625         return;
626
627     InjectedBundle::shared().os() << "ALERT: " << message << "\n";
628 }
629
630 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
631 {
632     if (!InjectedBundle::shared().isTestRunning())
633         return;
634
635     InjectedBundle::shared().os() << "CONFIRM: " << message << "\n";
636 }
637
638 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
639 {
640     InjectedBundle::shared().os() << "PROMPT: " << message << ", default text: " << defaultValue <<  "\n";
641 }
642
643 // Editor Client Callbacks
644
645 bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
646 {
647     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
648 }
649
650 bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
651 {
652     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
653 }
654
655 bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
656 {
657     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
658 }
659
660 bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
661 {
662     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
663 }
664
665 bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
666 {
667     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
668 }
669
670 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
671 {
672     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
673 }
674
675 bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
676 {
677     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
678 }
679
680 void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
681 {
682     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
683 }
684
685 void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
686 {
687     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
688 }
689
690 void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
691 {
692     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
693 }
694
695 void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
696 {
697     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
698 }
699
700 bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
701 {
702     if (!InjectedBundle::shared().isTestRunning())
703         return true;
704
705     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
706         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldBeginEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
707     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
708 }
709
710 bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
711 {
712     if (!InjectedBundle::shared().isTestRunning())
713         return true;
714
715     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
716         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldEndEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
717     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
718 }
719
720 bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
721 {
722     if (!InjectedBundle::shared().isTestRunning())
723         return true;
724
725     static const char* insertactionstring[] = {
726         "WebViewInsertActionTyped",
727         "WebViewInsertActionPasted",
728         "WebViewInsertActionDropped",
729     };
730
731     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
732         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";
733     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
734 }
735
736 bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
737 {
738     if (!InjectedBundle::shared().isTestRunning())
739         return true;
740
741     static const char *insertactionstring[] = {
742         "WebViewInsertActionTyped",
743         "WebViewInsertActionPasted",
744         "WebViewInsertActionDropped",
745     };
746
747     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
748         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertText:" << text << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
749     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
750 }
751
752 bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
753 {
754     if (!InjectedBundle::shared().isTestRunning())
755         return true;
756
757     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
758         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldDeleteDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
759     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
760 }
761
762 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
763 {
764     if (!InjectedBundle::shared().isTestRunning())
765         return true;
766
767     static const char *affinitystring[] = {
768         "NSSelectionAffinityUpstream",
769         "NSSelectionAffinityDownstream"
770     };
771     static const char *boolstring[] = {
772         "FALSE",
773         "TRUE"
774     };
775
776     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
777         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";
778     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
779 }
780
781 bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
782 {
783     if (!InjectedBundle::shared().isTestRunning())
784         return true;
785
786     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
787         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldApplyStyle:" << style << " toElementsInDOMRange:" << toStr(m_page, m_world.get(), range)  << "\n";
788     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
789 }
790
791 void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
792 {
793     if (!InjectedBundle::shared().isTestRunning())
794         return;
795
796     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
797         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidBeginEditing:" << notificationName << "\n";
798 }
799
800 void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
801 {
802     if (!InjectedBundle::shared().isTestRunning())
803         return;
804
805     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
806         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidEndEditing:" << notificationName << "\n";
807 }
808
809 void InjectedBundlePage::didChange(WKStringRef notificationName)
810 {
811     if (!InjectedBundle::shared().isTestRunning())
812         return;
813
814     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
815         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChange:" << notificationName << "\n";
816 }
817
818 void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
819 {
820     if (!InjectedBundle::shared().isTestRunning())
821         return;
822
823     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
824         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChangeSelection:" << notificationName << "\n";
825 }
826
827 static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
828 {
829     return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
830 }
831
832 static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem)
833 {
834     unsigned column = 0;
835     if (isCurrentItem) {
836         InjectedBundle::shared().os() << "curr->";
837         column = 6;
838     }
839     for (unsigned i = column; i < indent; i++)
840         InjectedBundle::shared().os() << ' ';
841
842     string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
843     if (hasPrefix(url, "file:")) {
844         string directoryName = "/LayoutTests/";
845         size_t start = url.find(directoryName);
846         if (start == string::npos)
847             start = 0;
848         else
849             start += directoryName.size();
850         InjectedBundle::shared().os() << "(file test):" << url.substr(start);
851     } else
852         InjectedBundle::shared().os() << url;
853
854     string target = toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
855     if (target.length())
856         InjectedBundle::shared().os() << " (in frame \"" << target << "\")";
857
858     // FIXME: Need WKBackForwardListItemIsTargetItem.
859     if (WKBundleBackForwardListItemIsTargetItem(item))
860         InjectedBundle::shared().os() << "  **nav target**";
861
862     InjectedBundle::shared().os() << '\n';
863
864     if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
865         // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
866         size_t size = WKArrayGetSize(kids.get());
867         Vector<WKBundleBackForwardListItemRef> sortedKids(size);
868         for (size_t i = 0; i < size; ++i)
869             sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
870         stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
871         for (size_t i = 0; i < size; ++i)
872             dumpBackForwardListItem(sortedKids[i], indent + 4, false);
873     }
874 }
875
876 void InjectedBundlePage::dumpBackForwardList()
877 {
878     InjectedBundle::shared().os() << "\n============== Back Forward List ==============\n";
879
880     WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
881
882     // Print out all items in the list after m_previousTestBackForwardListItem.
883     // Gather items from the end of the list, then print them out from oldest to newest.
884     Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
885     for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
886         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
887         // Something is wrong if the item from the last test is in the forward part of the list.
888         ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
889         itemsToPrint.append(item);
890     }
891
892     ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
893
894     itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
895
896     int currentItemIndex = itemsToPrint.size() - 1;
897
898     int backListCount = WKBundleBackForwardListGetBackListCount(list);
899     for (int i = -1; i >= -backListCount; --i) {
900         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
901         if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
902             break;
903         itemsToPrint.append(item);
904     }
905
906     for (int i = itemsToPrint.size() - 1; i >= 0; i--)
907         dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex);
908
909     InjectedBundle::shared().os() << "===============================================\n";
910 }
911
912 } // namespace WTR