WebKitTestRunner needs support for setHandlesAuthenticationChallenges
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / TestRunner.cpp
1 /*
2  * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "TestRunner.h"
28
29 #include "InjectedBundle.h"
30 #include "InjectedBundlePage.h"
31 #include "JSTestRunner.h"
32 #include "PlatformWebView.h"
33 #include "StringFunctions.h"
34 #include "TestController.h"
35 #include <WebCore/PageVisibilityState.h>
36 #include <WebKit2/WKBundle.h>
37 #include <WebKit2/WKBundleBackForwardList.h>
38 #include <WebKit2/WKBundleFrame.h>
39 #include <WebKit2/WKBundleFramePrivate.h>
40 #include <WebKit2/WKBundleInspector.h>
41 #include <WebKit2/WKBundleNodeHandlePrivate.h>
42 #include <WebKit2/WKBundlePagePrivate.h>
43 #include <WebKit2/WKBundlePrivate.h>
44 #include <WebKit2/WKBundleScriptWorld.h>
45 #include <WebKit2/WKData.h>
46 #include <WebKit2/WKRetainPtr.h>
47 #include <WebKit2/WKSerializedScriptValue.h>
48 #include <WebKit2/WebKit2_C.h>
49 #include <wtf/CurrentTime.h>
50 #include <wtf/HashMap.h>
51 #include <wtf/OwnArrayPtr.h>
52 #include <wtf/PassOwnArrayPtr.h>
53 #include <wtf/text/CString.h>
54 #include <wtf/text/StringBuilder.h>
55
56 #if ENABLE(WEB_INTENTS)
57 #include <WebKit2/WKBundleIntent.h>
58 #include <WebKit2/WKBundleIntentRequest.h>
59 #endif
60
61 namespace WTR {
62
63 const double TestRunner::waitToDumpWatchdogTimerInterval = 30;
64
65 PassRefPtr<TestRunner> TestRunner::create()
66 {
67     return adoptRef(new TestRunner);
68 }
69
70 TestRunner::TestRunner()
71     : m_whatToDump(RenderTree)
72     , m_shouldDumpAllFrameScrollPositions(false)
73     , m_shouldDumpBackForwardListsForAllWindows(false)
74     , m_shouldAllowEditing(true)
75     , m_shouldCloseExtraWindows(false)
76     , m_dumpEditingCallbacks(false)
77     , m_dumpStatusCallbacks(false)
78     , m_dumpTitleChanges(false)
79     , m_dumpPixels(true)
80     , m_dumpSelectionRect(false)
81     , m_dumpFullScreenCallbacks(false)
82     , m_dumpFrameLoadCallbacks(false)
83     , m_dumpProgressFinishedCallback(false)
84     , m_dumpResourceLoadCallbacks(false)
85     , m_dumpResourceResponseMIMETypes(false)
86     , m_dumpWillCacheResponse(false)
87     , m_dumpApplicationCacheDelegateCallbacks(false)
88     , m_dumpDatabaseCallbacks(false)
89     , m_disallowIncreaseForApplicationCacheQuota(false)
90     , m_waitToDump(false)
91     , m_testRepaint(false)
92     , m_testRepaintSweepHorizontally(false)
93     , m_willSendRequestReturnsNull(false)
94     , m_willSendRequestReturnsNullOnRedirect(false)
95     , m_shouldStopProvisionalFrameLoads(false)
96     , m_policyDelegateEnabled(false)
97     , m_policyDelegatePermissive(false)
98     , m_globalFlag(false)
99     , m_customFullScreenBehavior(false)
100     , m_userStyleSheetEnabled(false)
101     , m_userStyleSheetLocation(adoptWK(WKStringCreateWithUTF8CString("")))
102 {
103     platformInitialize();
104 }
105
106 TestRunner::~TestRunner()
107 {
108 }
109
110 JSClassRef TestRunner::wrapperClass()
111 {
112     return JSTestRunner::testRunnerClass();
113 }
114
115 void TestRunner::display()
116 {
117     WKBundlePageRef page = InjectedBundle::shared().page()->page();
118     WKBundlePageForceRepaint(page);
119     WKBundlePageSetTracksRepaints(page, true);
120     WKBundlePageResetTrackedRepaints(page);
121 }
122
123 void TestRunner::dumpAsText(bool dumpPixels)
124 {
125     if (m_whatToDump < MainFrameText)
126         m_whatToDump = MainFrameText;
127     m_dumpPixels = dumpPixels;
128 }
129
130 void TestRunner::setCustomPolicyDelegate(bool enabled, bool permissive)
131 {
132     m_policyDelegateEnabled = enabled;
133     m_policyDelegatePermissive = permissive;
134
135     InjectedBundle::shared().setCustomPolicyDelegate(enabled, permissive);
136 }
137
138 void TestRunner::waitForPolicyDelegate()
139 {
140     setCustomPolicyDelegate(true);
141     waitUntilDone();
142 }
143
144 void TestRunner::waitUntilDone()
145 {
146     m_waitToDump = true;
147     if (InjectedBundle::shared().useWaitToDumpWatchdogTimer())
148         initializeWaitToDumpWatchdogTimerIfNeeded();
149 }
150
151 void TestRunner::waitToDumpWatchdogTimerFired()
152 {
153     invalidateWaitToDumpWatchdogTimer();
154     InjectedBundle::shared().outputText("FAIL: Timed out waiting for notifyDone to be called\n\n");
155     InjectedBundle::shared().done();
156 }
157
158 void TestRunner::notifyDone()
159 {
160     if (!InjectedBundle::shared().isTestRunning())
161         return;
162
163     if (m_waitToDump && !InjectedBundle::shared().topLoadingFrame())
164         InjectedBundle::shared().page()->dump();
165
166     m_waitToDump = false;
167 }
168
169 unsigned TestRunner::numberOfActiveAnimations() const
170 {
171     // FIXME: Is it OK this works only for the main frame?
172     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
173     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
174     return WKBundleFrameGetNumberOfActiveAnimations(mainFrame);
175 }
176
177 bool TestRunner::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
178 {
179     // FIXME: Is it OK this works only for the main frame?
180     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
181     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
182     return WKBundleFramePauseAnimationOnElementWithId(mainFrame, toWK(animationName).get(), toWK(elementId).get(), time);
183 }
184
185 bool TestRunner::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId)
186 {
187     // FIXME: Is it OK this works only for the main frame?
188     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
189     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
190     return WKBundleFramePauseTransitionOnElementWithId(mainFrame, toWK(propertyName).get(), toWK(elementId).get(), time);
191 }
192
193 void TestRunner::suspendAnimations()
194 {
195     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
196     WKBundleFrameSuspendAnimations(mainFrame);
197 }
198
199 void TestRunner::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
200 {
201     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
202     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
203
204     WKBundleAddUserScript(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
205         (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
206         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
207 }
208
209 void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames)
210 {
211     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
212     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
213
214     WKBundleAddUserStyleSheet(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
215         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
216 }
217
218 void TestRunner::keepWebHistory()
219 {
220     WKBundleSetShouldTrackVisitedLinks(InjectedBundle::shared().bundle(), true);
221 }
222
223 JSValueRef TestRunner::computedStyleIncludingVisitedInfo(JSValueRef element)
224 {
225     // FIXME: Is it OK this works only for the main frame?
226     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
227     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
228     if (!JSValueIsObject(context, element))
229         return JSValueMakeUndefined(context);
230     JSValueRef value = WKBundleFrameGetComputedStyleIncludingVisitedInfo(mainFrame, const_cast<JSObjectRef>(element));
231     if (!value)
232         return JSValueMakeUndefined(context);
233     return value;
234 }
235
236 JSRetainPtr<JSStringRef> TestRunner::markerTextForListItem(JSValueRef element)
237 {
238     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
239     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
240     if (!element || !JSValueIsObject(context, element))
241         return 0;
242     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyMarkerText(mainFrame, const_cast<JSObjectRef>(element)));
243     if (WKStringIsEmpty(text.get()))
244         return 0;
245     return toJS(text);
246 }
247
248 void TestRunner::execCommand(JSStringRef name, JSStringRef argument)
249 {
250     WKBundlePageExecuteEditingCommand(InjectedBundle::shared().page()->page(), toWK(name).get(), toWK(argument).get());
251 }
252
253 bool TestRunner::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
254 {
255     WKFindOptions options = 0;
256
257     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
258     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
259     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
260     JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
261     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
262     if (!JSValueIsNumber(context, lengthValue))
263         return false;
264
265     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
266     for (size_t i = 0; i < length; ++i) {
267         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
268         if (!JSValueIsString(context, value))
269             continue;
270
271         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
272
273         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
274             options |= kWKFindOptionsCaseInsensitive;
275         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
276             options |= kWKFindOptionsAtWordStarts;
277         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
278             options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
279         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
280             options |= kWKFindOptionsBackwards;
281         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
282             options |= kWKFindOptionsWrapAround;
283         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
284             // FIXME: No kWKFindOptionsStartInSelection.
285         }
286     }
287
288     return WKBundlePageFindString(InjectedBundle::shared().page()->page(), toWK(target).get(), options);
289 }
290
291 void TestRunner::clearAllDatabases()
292 {
293     WKBundleClearAllDatabases(InjectedBundle::shared().bundle());
294 }
295
296 void TestRunner::setDatabaseQuota(uint64_t quota)
297 {
298     return WKBundleSetDatabaseQuota(InjectedBundle::shared().bundle(), quota);
299 }
300
301 void TestRunner::clearAllApplicationCaches()
302 {
303     WKBundleClearApplicationCache(InjectedBundle::shared().bundle());
304 }
305
306 void TestRunner::clearApplicationCacheForOrigin(JSStringRef origin)
307 {
308     WKBundleClearApplicationCacheForOrigin(InjectedBundle::shared().bundle(), toWK(origin).get());
309 }
310
311 void TestRunner::setAppCacheMaximumSize(uint64_t size)
312 {
313     WKBundleSetAppCacheMaximumSize(InjectedBundle::shared().bundle(), size);
314 }
315
316 long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef origin)
317 {
318     return WKBundleGetAppCacheUsageForOrigin(InjectedBundle::shared().bundle(), toWK(origin).get());
319 }
320
321 void TestRunner::setApplicationCacheOriginQuota(unsigned long long bytes)
322 {
323     WKRetainPtr<WKStringRef> origin(AdoptWK, WKStringCreateWithUTF8CString("http://127.0.0.1:8000"));
324     WKBundleSetApplicationCacheOriginQuota(InjectedBundle::shared().bundle(), origin.get(), bytes);
325 }
326
327 void TestRunner::disallowIncreaseForApplicationCacheQuota()
328 {
329     m_disallowIncreaseForApplicationCacheQuota = true;
330 }
331
332 static inline JSValueRef stringArrayToJS(JSContextRef context, WKArrayRef strings)
333 {
334     const size_t count = WKArrayGetSize(strings);
335
336     OwnArrayPtr<JSValueRef> jsStringsArray = adoptArrayPtr(new JSValueRef[count]);
337     for (size_t i = 0; i < count; ++i) {
338         WKStringRef stringRef = static_cast<WKStringRef>(WKArrayGetItemAtIndex(strings, i));
339         JSRetainPtr<JSStringRef> stringJS = toJS(stringRef);
340         jsStringsArray[i] = JSValueMakeString(context, stringJS.get());
341     }
342
343     return JSObjectMakeArray(context, count, jsStringsArray.get(), 0);
344 }
345
346 JSValueRef TestRunner::originsWithApplicationCache()
347 {
348     WKRetainPtr<WKArrayRef> origins(AdoptWK, WKBundleCopyOriginsWithApplicationCache(InjectedBundle::shared().bundle()));
349
350     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
351     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
352
353     return stringArrayToJS(context, origins.get());
354 }
355
356 bool TestRunner::isCommandEnabled(JSStringRef name)
357 {
358     return WKBundlePageIsEditingCommandEnabled(InjectedBundle::shared().page()->page(), toWK(name).get());
359 }
360
361 void TestRunner::setCanOpenWindows(bool)
362 {
363     // It's not clear if or why any tests require opening windows be forbidden.
364     // For now, just ignore this setting, and if we find later it's needed we can add it.
365 }
366
367 void TestRunner::setXSSAuditorEnabled(bool enabled)
368 {
369     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
370     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), key.get(), enabled);
371 }
372
373 void TestRunner::setAllowUniversalAccessFromFileURLs(bool enabled)
374 {
375     WKBundleSetAllowUniversalAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
376 }
377
378 void TestRunner::setAllowFileAccessFromFileURLs(bool enabled)
379 {
380     WKBundleSetAllowFileAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
381 }
382
383 void TestRunner::setFrameFlatteningEnabled(bool enabled)
384 {
385     WKBundleSetFrameFlatteningEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
386 }
387
388 void TestRunner::setPluginsEnabled(bool enabled)
389 {
390     WKBundleSetPluginsEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
391 }
392
393 void TestRunner::setJavaScriptCanAccessClipboard(bool enabled)
394 {
395      WKBundleSetJavaScriptCanAccessClipboard(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
396 }
397
398 void TestRunner::setPrivateBrowsingEnabled(bool enabled)
399 {
400      WKBundleSetPrivateBrowsingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
401 }
402
403 void TestRunner::setPopupBlockingEnabled(bool enabled)
404 {
405      WKBundleSetPopupBlockingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
406 }
407
408 void TestRunner::setAuthorAndUserStylesEnabled(bool enabled)
409 {
410      WKBundleSetAuthorAndUserStylesEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
411 }
412
413 void TestRunner::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
414 {
415     WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
416 }
417
418 void TestRunner::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
419 {
420     WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
421 }
422
423 bool TestRunner::isPageBoxVisible(int pageIndex)
424 {
425     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
426     return WKBundleIsPageBoxVisible(InjectedBundle::shared().bundle(), mainFrame, pageIndex);
427 }
428
429 void TestRunner::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
430 {
431     if (!element || !JSValueIsObject(context, element))
432         return;
433
434     WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
435     WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
436 }
437
438 void TestRunner::setAudioData(JSContextRef context, JSValueRef data)
439 {
440     WKRetainPtr<WKDataRef> audioData(AdoptWK, WKBundleCreateWKDataFromUInt8Array(InjectedBundle::shared().bundle(), context, data));
441     InjectedBundle::shared().setAudioResult(audioData.get());
442     m_whatToDump = Audio;
443 }
444
445 unsigned TestRunner::windowCount()
446 {
447     return InjectedBundle::shared().pageCount();
448 }
449
450 void TestRunner::clearBackForwardList()
451 {
452     WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::shared().page()->page()));
453 }
454
455 // Object Creation
456
457 void TestRunner::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
458 {
459     setProperty(context, windowObject, "testRunner", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
460 }
461
462 void TestRunner::showWebInspector()
463 {
464 #if ENABLE(INSPECTOR)
465     WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
466 #endif // ENABLE(INSPECTOR)
467 }
468
469 void TestRunner::closeWebInspector()
470 {
471 #if ENABLE(INSPECTOR)
472     WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
473 #endif // ENABLE(INSPECTOR)
474 }
475
476 void TestRunner::evaluateInWebInspector(long callID, JSStringRef script)
477 {
478 #if ENABLE(INSPECTOR)
479     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
480     WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()), callID, scriptWK.get());
481 #endif // ENABLE(INSPECTOR)
482 }
483
484 typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
485 static WorldMap& worldMap()
486 {
487     static WorldMap& map = *new WorldMap;
488     return map;
489 }
490
491 unsigned TestRunner::worldIDForWorld(WKBundleScriptWorldRef world)
492 {
493     WorldMap::const_iterator end = worldMap().end();
494     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
495         if (it->value == world)
496             return it->key;
497     }
498
499     return 0;
500 }
501
502 void TestRunner::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
503 {
504     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
505     // that is created once and cached forever.
506     WKRetainPtr<WKBundleScriptWorldRef> world;
507     if (!worldID)
508         world.adopt(WKBundleScriptWorldCreateWorld());
509     else {
510         WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, 0).iterator->value;
511         if (!worldSlot)
512             worldSlot.adopt(WKBundleScriptWorldCreateWorld());
513         world = worldSlot;
514     }
515
516     WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
517     if (!frame)
518         frame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
519
520     JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
521     JSEvaluateScript(jsContext, script, 0, 0, 0, 0); 
522 }
523
524 void TestRunner::setPOSIXLocale(JSStringRef locale)
525 {
526     char localeBuf[32];
527     JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
528     setlocale(LC_ALL, localeBuf);
529 }
530
531 void TestRunner::setTextDirection(JSStringRef direction)
532 {
533     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
534     return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
535 }
536     
537 void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
538 {
539     InjectedBundle::shared().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
540 }
541
542 void TestRunner::setDefersLoading(bool shouldDeferLoading)
543 {
544     WKBundlePageSetDefersLoading(InjectedBundle::shared().page()->page(), shouldDeferLoading);
545 }
546
547 void TestRunner::setPageVisibility(JSStringRef state)
548 {
549     WebCore::PageVisibilityState visibilityState = WebCore::PageVisibilityStateVisible;
550
551     if (JSStringIsEqualToUTF8CString(state, "hidden"))
552         visibilityState = WebCore::PageVisibilityStateHidden;
553     else if (JSStringIsEqualToUTF8CString(state, "prerender"))
554         visibilityState = WebCore::PageVisibilityStatePrerender;
555     else if (JSStringIsEqualToUTF8CString(state, "preview"))
556         visibilityState = WebCore::PageVisibilityStatePreview;
557
558     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), visibilityState, /* isInitialState */ false);
559 }
560
561 void TestRunner::resetPageVisibility()
562 {
563     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), WebCore::PageVisibilityStateVisible, /* isInitialState */ true);
564 }
565
566 typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
567 static CallbackMap& callbackMap()
568 {
569     static CallbackMap& map = *new CallbackMap;
570     return map;
571 }
572
573 enum {
574     AddChromeInputFieldCallbackID = 1,
575     RemoveChromeInputFieldCallbackID,
576     FocusWebViewCallbackID,
577     SetBackingScaleFactorCallbackID
578 };
579
580 static void cacheTestRunnerCallback(unsigned index, JSValueRef callback)
581 {
582     if (!callback)
583         return;
584
585     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
586     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
587     JSValueProtect(context, callback);
588     callbackMap().add(index, callback);
589 }
590
591 static void callTestRunnerCallback(unsigned index)
592 {
593     if (!callbackMap().contains(index))
594         return;
595     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
596     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
597     JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
598     JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), 0, 0, 0);
599     JSValueUnprotect(context, callback);
600 }
601
602 unsigned TestRunner::workerThreadCount()
603 {
604     return WKBundleGetWorkerThreadCount(InjectedBundle::shared().bundle());
605 }
606
607 void TestRunner::addChromeInputField(JSValueRef callback)
608 {
609     cacheTestRunnerCallback(AddChromeInputFieldCallbackID, callback);
610     InjectedBundle::shared().postAddChromeInputField();
611 }
612
613 void TestRunner::removeChromeInputField(JSValueRef callback)
614 {
615     cacheTestRunnerCallback(RemoveChromeInputFieldCallbackID, callback);
616     InjectedBundle::shared().postRemoveChromeInputField();
617 }
618
619 void TestRunner::focusWebView(JSValueRef callback)
620 {
621     cacheTestRunnerCallback(FocusWebViewCallbackID, callback);
622     InjectedBundle::shared().postFocusWebView();
623 }
624
625 void TestRunner::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
626 {
627     cacheTestRunnerCallback(SetBackingScaleFactorCallbackID, callback);
628     InjectedBundle::shared().postSetBackingScaleFactor(backingScaleFactor);
629 }
630
631 void TestRunner::setWindowIsKey(bool isKey)
632 {
633     InjectedBundle::shared().postSetWindowIsKey(isKey);
634 }
635
636 void TestRunner::callAddChromeInputFieldCallback()
637 {
638     callTestRunnerCallback(AddChromeInputFieldCallbackID);
639 }
640
641 void TestRunner::callRemoveChromeInputFieldCallback()
642 {
643     callTestRunnerCallback(RemoveChromeInputFieldCallbackID);
644 }
645
646 void TestRunner::callFocusWebViewCallback()
647 {
648     callTestRunnerCallback(FocusWebViewCallbackID);
649 }
650
651 void TestRunner::callSetBackingScaleFactorCallback()
652 {
653     callTestRunnerCallback(SetBackingScaleFactorCallbackID);
654 }
655
656 static inline bool toBool(JSStringRef value)
657 {
658     return JSStringIsEqualToUTF8CString(value, "true") || JSStringIsEqualToUTF8CString(value, "1");
659 }
660
661 void TestRunner::overridePreference(JSStringRef preference, JSStringRef value)
662 {
663     // FIXME: handle non-boolean preferences.
664     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), toWK(preference).get(), toBool(value));
665 }
666
667 void TestRunner::sendWebIntentResponse(JSStringRef reply)
668 {
669 #if ENABLE(WEB_INTENTS)
670     WKRetainPtr<WKBundleIntentRequestRef> currentRequest = InjectedBundle::shared().page()->currentIntentRequest();
671     if (!currentRequest)
672         return;
673
674     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
675     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
676
677     if (reply) {
678         WKRetainPtr<WKSerializedScriptValueRef> serializedData(AdoptWK, WKSerializedScriptValueCreate(context, JSValueMakeString(context, reply), 0));
679         WKBundleIntentRequestPostResult(currentRequest.get(), serializedData.get());
680     } else {
681         JSRetainPtr<JSStringRef> errorReply(JSStringCreateWithUTF8CString("ERROR"));
682         WKRetainPtr<WKSerializedScriptValueRef> serializedData(AdoptWK, WKSerializedScriptValueCreate(context, JSValueMakeString(context, errorReply.get()), 0));
683         WKBundleIntentRequestPostFailure(currentRequest.get(), serializedData.get());
684     }
685 #endif
686 }
687
688 void TestRunner::deliverWebIntent(JSStringRef action, JSStringRef type, JSStringRef data)
689 {
690 #if ENABLE(WEB_INTENTS)
691     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
692     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
693
694     WKRetainPtr<WKStringRef> actionWK = toWK(action);
695     WKRetainPtr<WKStringRef> typeWK = toWK(type);
696     WKRetainPtr<WKSerializedScriptValueRef> dataWK(AdoptWK, WKSerializedScriptValueCreate(context, JSValueMakeString(context, data), 0));
697
698     WKRetainPtr<WKMutableDictionaryRef> intentInitDict(AdoptWK, WKMutableDictionaryCreate());
699     WKRetainPtr<WKStringRef> actionKey(AdoptWK, WKStringCreateWithUTF8CString("action"));
700     WKDictionaryAddItem(intentInitDict.get(), actionKey.get(), actionWK.get());
701
702     WKRetainPtr<WKStringRef> typeKey(AdoptWK, WKStringCreateWithUTF8CString("type"));
703     WKDictionaryAddItem(intentInitDict.get(), typeKey.get(), typeWK.get());
704
705     WKRetainPtr<WKStringRef> dataKey(AdoptWK, WKStringCreateWithUTF8CString("data"));
706     WKDictionaryAddItem(intentInitDict.get(), dataKey.get(), dataWK.get());
707
708     WKRetainPtr<WKBundleIntentRef> wkIntent(AdoptWK, WKBundleIntentCreate(intentInitDict.get()));
709     WKBundlePageDeliverIntentToFrame(InjectedBundle::shared().page()->page(), mainFrame, wkIntent.get());
710 #endif
711 }
712
713 void TestRunner::setAlwaysAcceptCookies(bool accept)
714 {
715     WKBundleSetAlwaysAcceptCookies(InjectedBundle::shared().bundle(), accept);
716 }
717
718 double TestRunner::preciseTime()
719 {
720     return currentTime();
721 }
722
723 void TestRunner::setUserStyleSheetEnabled(bool enabled)
724 {
725     m_userStyleSheetEnabled = enabled;
726
727     WKRetainPtr<WKStringRef> emptyUrl = adoptWK(WKStringCreateWithUTF8CString(""));
728     WKStringRef location = enabled ? m_userStyleSheetLocation.get() : emptyUrl.get();
729     WKBundleSetUserStyleSheetLocation(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), location);
730 }
731
732 void TestRunner::setUserStyleSheetLocation(JSStringRef location)
733 {
734     m_userStyleSheetLocation = adoptWK(WKStringCreateWithJSString(location));
735
736     if (m_userStyleSheetEnabled)
737         setUserStyleSheetEnabled(true);
738 }
739
740 void TestRunner::setMinimumTimerInterval(double seconds)
741 {
742     WKBundleSetMinimumTimerInterval(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), seconds);
743 }
744
745 void TestRunner::setSpatialNavigationEnabled(bool enabled)
746 {
747     WKBundleSetSpatialNavigationEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
748 }
749
750 void TestRunner::setTabKeyCyclesThroughElements(bool enabled)
751 {
752     WKBundleSetTabKeyCyclesThroughElements(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), enabled);
753 }
754
755 void TestRunner::setSerializeHTTPLoads()
756 {
757     WKBundleSetSerialLoadingEnabled(InjectedBundle::shared().bundle(), true);
758 }
759
760 void TestRunner::dispatchPendingLoadRequests()
761 {
762     WKBundleDispatchPendingLoadRequests(InjectedBundle::shared().bundle());
763 }
764
765 void TestRunner::setCacheModel(int model)
766 {
767     WKBundleSetCacheModel(InjectedBundle::shared().bundle(), model);
768 }
769
770 void TestRunner::grantWebNotificationPermission(JSStringRef origin)
771 {
772     WKRetainPtr<WKStringRef> originWK = toWK(origin);
773     WKBundleSetWebNotificationPermission(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), originWK.get(), true);
774 }
775
776 void TestRunner::denyWebNotificationPermission(JSStringRef origin)
777 {
778     WKRetainPtr<WKStringRef> originWK = toWK(origin);
779     WKBundleSetWebNotificationPermission(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), originWK.get(), false);
780 }
781
782 void TestRunner::removeAllWebNotificationPermissions()
783 {
784     WKBundleRemoveAllWebNotificationPermissions(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page());
785 }
786
787 void TestRunner::simulateWebNotificationClick(JSValueRef notification)
788 {
789     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
790     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
791     uint64_t notificationID = WKBundleGetWebNotificationID(InjectedBundle::shared().bundle(), context, notification);
792     InjectedBundle::shared().postSimulateWebNotificationClick(notificationID);
793 }
794
795 void TestRunner::setGeolocationPermission(bool enabled)
796 {
797     // FIXME: this should be done by frame.
798     InjectedBundle::shared().setGeolocationPermission(enabled);
799 }
800
801 void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, JSValueRef jsAltitude, JSValueRef jsAltitudeAccuracy, JSValueRef jsHeading, JSValueRef jsSpeed)
802 {
803     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
804     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
805
806     bool providesAltitude = false;
807     double altitude = 0.;
808     if (!JSValueIsUndefined(context, jsAltitude)) {
809         providesAltitude = true;
810         altitude = JSValueToNumber(context, jsAltitude, 0);
811     }
812
813     bool providesAltitudeAccuracy = false;
814     double altitudeAccuracy = 0.;
815     if (!JSValueIsUndefined(context, jsAltitudeAccuracy)) {
816         providesAltitudeAccuracy = true;
817         altitudeAccuracy = JSValueToNumber(context, jsAltitudeAccuracy, 0);
818     }
819
820     bool providesHeading = false;
821     double heading = 0.;
822     if (!JSValueIsUndefined(context, jsHeading)) {
823         providesHeading = true;
824         heading = JSValueToNumber(context, jsHeading, 0);
825     }
826
827     bool providesSpeed = false;
828     double speed = 0.;
829     if (!JSValueIsUndefined(context, jsSpeed)) {
830         providesSpeed = true;
831         speed = JSValueToNumber(context, jsSpeed, 0);
832     }
833
834     InjectedBundle::shared().setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
835 }
836
837 void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message)
838 {
839     WKRetainPtr<WKStringRef> messageWK = toWK(message);
840     InjectedBundle::shared().setMockGeolocationPositionUnavailableError(messageWK.get());
841 }
842
843 bool TestRunner::callShouldCloseOnWebView()
844 {
845     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
846     return WKBundleFrameCallShouldCloseOnWebView(mainFrame);
847 }
848
849 void TestRunner::queueBackNavigation(unsigned howFarBackward)
850 {
851     InjectedBundle::shared().queueBackNavigation(howFarBackward);
852 }
853
854 void TestRunner::queueForwardNavigation(unsigned howFarForward)
855 {
856     InjectedBundle::shared().queueForwardNavigation(howFarForward);
857 }
858
859 void TestRunner::queueLoad(JSStringRef url, JSStringRef target)
860 {
861     WKRetainPtr<WKURLRef> baseURLWK(AdoptWK, WKBundleFrameCopyURL(WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page())));
862     WKRetainPtr<WKURLRef> urlWK(AdoptWK, WKURLCreateWithBaseURL(baseURLWK.get(), toWTFString(toWK(url)).utf8().data()));
863     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(urlWK.get()));
864
865     InjectedBundle::shared().queueLoad(urlStringWK.get(), toWK(target).get());
866 }
867
868 void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL)
869 {
870     WKRetainPtr<WKStringRef> contentWK = toWK(content);
871     WKRetainPtr<WKStringRef> baseURLWK = baseURL ? toWK(baseURL) : WKRetainPtr<WKStringRef>();
872     WKRetainPtr<WKStringRef> unreachableURLWK = unreachableURL ? toWK(unreachableURL) : WKRetainPtr<WKStringRef>();
873
874     InjectedBundle::shared().queueLoadHTMLString(contentWK.get(), baseURLWK.get(), unreachableURLWK.get());
875 }
876
877 void TestRunner::queueReload()
878 {
879     InjectedBundle::shared().queueReload();
880 }
881
882 void TestRunner::queueLoadingScript(JSStringRef script)
883 {
884     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
885     InjectedBundle::shared().queueLoadingScript(scriptWK.get());
886 }
887
888 void TestRunner::queueNonLoadingScript(JSStringRef script)
889 {
890     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
891     InjectedBundle::shared().queueNonLoadingScript(scriptWK.get());
892 }
893
894 void TestRunner::setViewModeMediaFeature(JSStringRef mode)
895 {
896     WKRetainPtr<WKStringRef> modeWK = toWK(mode);
897     WKBundlePageSetViewMode(InjectedBundle::shared().page()->page(), modeWK.get());
898 }
899
900 void TestRunner::setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges)
901 {
902     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetHandlesAuthenticationChallenge"));
903     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(handlesAuthenticationChallenges));
904     WKBundlePostMessage(InjectedBundle::shared().bundle(), messageName.get(), messageBody.get());
905 }
906
907 void TestRunner::setAuthenticationUsername(JSStringRef username)
908 {
909     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationUsername"));
910     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(username));
911     WKBundlePostMessage(InjectedBundle::shared().bundle(), messageName.get(), messageBody.get());
912 }
913
914 void TestRunner::setAuthenticationPassword(JSStringRef password)
915 {
916     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationPassword"));
917     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(password));
918     WKBundlePostMessage(InjectedBundle::shared().bundle(), messageName.get(), messageBody.get());
919 }
920
921 } // namespace WTR