WebKitTestRunner should use WKWebView on OS X and iOS
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / TestRunner.cpp
1 /*
2  * Copyright (C) 2010, 2011, 2012, 2013 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 <JavaScriptCore/JSCTestRunnerUtils.h>
36 #include <WebKit/WKBundle.h>
37 #include <WebKit/WKBundleBackForwardList.h>
38 #include <WebKit/WKBundleFrame.h>
39 #include <WebKit/WKBundleFramePrivate.h>
40 #include <WebKit/WKBundleInspector.h>
41 #include <WebKit/WKBundleNodeHandlePrivate.h>
42 #include <WebKit/WKBundlePage.h>
43 #include <WebKit/WKBundlePagePrivate.h>
44 #include <WebKit/WKBundlePrivate.h>
45 #include <WebKit/WKBundleScriptWorld.h>
46 #include <WebKit/WKData.h>
47 #include <WebKit/WKRetainPtr.h>
48 #include <WebKit/WKSerializedScriptValue.h>
49 #include <WebKit/WebKit2_C.h>
50 #include <wtf/CurrentTime.h>
51 #include <wtf/HashMap.h>
52 #include <wtf/StdLibExtras.h>
53 #include <wtf/text/CString.h>
54 #include <wtf/text/StringBuilder.h>
55
56 namespace WTR {
57
58 PassRefPtr<TestRunner> TestRunner::create()
59 {
60     return adoptRef(new TestRunner);
61 }
62
63 TestRunner::TestRunner()
64     : m_whatToDump(RenderTree)
65     , m_shouldDumpAllFrameScrollPositions(false)
66     , m_shouldDumpBackForwardListsForAllWindows(false)
67     , m_shouldAllowEditing(true)
68     , m_shouldCloseExtraWindows(false)
69     , m_dumpEditingCallbacks(false)
70     , m_dumpStatusCallbacks(false)
71     , m_dumpTitleChanges(false)
72     , m_dumpPixels(true)
73     , m_dumpSelectionRect(false)
74     , m_dumpFullScreenCallbacks(false)
75     , m_dumpFrameLoadCallbacks(false)
76     , m_dumpProgressFinishedCallback(false)
77     , m_dumpResourceLoadCallbacks(false)
78     , m_dumpResourceResponseMIMETypes(false)
79     , m_dumpWillCacheResponse(false)
80     , m_dumpApplicationCacheDelegateCallbacks(false)
81     , m_dumpDatabaseCallbacks(false)
82     , m_disallowIncreaseForApplicationCacheQuota(false)
83     , m_waitToDump(false)
84     , m_testRepaint(false)
85     , m_testRepaintSweepHorizontally(false)
86     , m_isPrinting(false)
87     , m_willSendRequestReturnsNull(false)
88     , m_willSendRequestReturnsNullOnRedirect(false)
89     , m_shouldStopProvisionalFrameLoads(false)
90     , m_policyDelegateEnabled(false)
91     , m_policyDelegatePermissive(false)
92     , m_globalFlag(false)
93     , m_customFullScreenBehavior(false)
94     , m_timeout(30000)
95     , m_databaseDefaultQuota(-1)
96     , m_databaseMaxQuota(-1)
97     , m_userStyleSheetEnabled(false)
98     , m_userStyleSheetLocation(adoptWK(WKStringCreateWithUTF8CString("")))
99 {
100     platformInitialize();
101 }
102
103 TestRunner::~TestRunner()
104 {
105 }
106
107 JSClassRef TestRunner::wrapperClass()
108 {
109     return JSTestRunner::testRunnerClass();
110 }
111
112 void TestRunner::display()
113 {
114     WKBundlePageRef page = InjectedBundle::singleton().page()->page();
115     WKBundlePageForceRepaint(page);
116     WKBundlePageSetTracksRepaints(page, true);
117     WKBundlePageResetTrackedRepaints(page);
118 }
119
120 void TestRunner::dumpAsText(bool dumpPixels)
121 {
122     if (m_whatToDump < MainFrameText)
123         m_whatToDump = MainFrameText;
124     m_dumpPixels = dumpPixels;
125 }
126
127 void TestRunner::setCustomPolicyDelegate(bool enabled, bool permissive)
128 {
129     m_policyDelegateEnabled = enabled;
130     m_policyDelegatePermissive = permissive;
131
132     InjectedBundle::singleton().setCustomPolicyDelegate(enabled, permissive);
133 }
134
135 void TestRunner::waitForPolicyDelegate()
136 {
137     setCustomPolicyDelegate(true);
138     waitUntilDone();
139 }
140
141 void TestRunner::waitUntilDone()
142 {
143     m_waitToDump = true;
144     if (InjectedBundle::singleton().useWaitToDumpWatchdogTimer())
145         initializeWaitToDumpWatchdogTimerIfNeeded();
146 }
147
148 void TestRunner::waitToDumpWatchdogTimerFired()
149 {
150     invalidateWaitToDumpWatchdogTimer();
151     auto& injectedBundle = InjectedBundle::singleton();
152     injectedBundle.outputText("FAIL: Timed out waiting for notifyDone to be called\n\n");
153     injectedBundle.done();
154 }
155
156 void TestRunner::notifyDone()
157 {
158     auto& injectedBundle = InjectedBundle::singleton();
159     if (!injectedBundle.isTestRunning())
160         return;
161
162     if (m_waitToDump && !injectedBundle.topLoadingFrame())
163         injectedBundle.page()->dump();
164
165     // We don't call invalidateWaitToDumpWatchdogTimer() here, even if we continue to wait for a load to finish.
166     // The test is still subject to timeout checking - it is better to detect an async timeout inside WebKitTestRunner
167     // than to let webkitpy do that, because WebKitTestRunner will dump partial results.
168
169     m_waitToDump = false;
170 }
171
172 void TestRunner::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
173 {
174     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
175
176     WKBundlePageAddUserScript(InjectedBundle::singleton().page()->page(), sourceWK.get(),
177         (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
178         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
179 }
180
181 void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames)
182 {
183     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
184
185     WKBundlePageAddUserStyleSheet(InjectedBundle::singleton().page()->page(), sourceWK.get(),
186         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
187 }
188
189 void TestRunner::keepWebHistory()
190 {
191     InjectedBundle::singleton().postSetAddsVisitedLinks(true);
192 }
193
194 void TestRunner::execCommand(JSStringRef name, JSStringRef argument)
195 {
196     WKBundlePageExecuteEditingCommand(InjectedBundle::singleton().page()->page(), toWK(name).get(), toWK(argument).get());
197 }
198
199 bool TestRunner::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
200 {
201     WKFindOptions options = 0;
202
203     auto& injectedBundle = InjectedBundle::singleton();
204     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
205     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
206     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
207     JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
208     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
209     if (!JSValueIsNumber(context, lengthValue))
210         return false;
211
212     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
213     for (size_t i = 0; i < length; ++i) {
214         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
215         if (!JSValueIsString(context, value))
216             continue;
217
218         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
219
220         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
221             options |= kWKFindOptionsCaseInsensitive;
222         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
223             options |= kWKFindOptionsAtWordStarts;
224         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
225             options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
226         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
227             options |= kWKFindOptionsBackwards;
228         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
229             options |= kWKFindOptionsWrapAround;
230         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
231             // FIXME: No kWKFindOptionsStartInSelection.
232         }
233     }
234
235     return WKBundlePageFindString(injectedBundle.page()->page(), toWK(target).get(), options);
236 }
237
238 void TestRunner::clearAllDatabases()
239 {
240     WKBundleClearAllDatabases(InjectedBundle::singleton().bundle());
241 }
242
243 void TestRunner::setDatabaseQuota(uint64_t quota)
244 {
245     return WKBundleSetDatabaseQuota(InjectedBundle::singleton().bundle(), quota);
246 }
247
248 void TestRunner::clearAllApplicationCaches()
249 {
250     WKBundleClearApplicationCache(InjectedBundle::singleton().bundle());
251 }
252
253 void TestRunner::clearApplicationCacheForOrigin(JSStringRef origin)
254 {
255     WKBundleClearApplicationCacheForOrigin(InjectedBundle::singleton().bundle(), toWK(origin).get());
256 }
257
258 void TestRunner::setAppCacheMaximumSize(uint64_t size)
259 {
260     WKBundleSetAppCacheMaximumSize(InjectedBundle::singleton().bundle(), size);
261 }
262
263 long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef origin)
264 {
265     return WKBundleGetAppCacheUsageForOrigin(InjectedBundle::singleton().bundle(), toWK(origin).get());
266 }
267
268 void TestRunner::disallowIncreaseForApplicationCacheQuota()
269 {
270     m_disallowIncreaseForApplicationCacheQuota = true;
271 }
272
273 static inline JSValueRef stringArrayToJS(JSContextRef context, WKArrayRef strings)
274 {
275     const size_t count = WKArrayGetSize(strings);
276
277     JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
278     JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
279     for (size_t i = 0; i < count; ++i) {
280         WKStringRef stringRef = static_cast<WKStringRef>(WKArrayGetItemAtIndex(strings, i));
281         JSRetainPtr<JSStringRef> stringJS = toJS(stringRef);
282         JSObjectSetPropertyAtIndex(context, arrayObj, i, JSValueMakeString(context, stringJS.get()), 0);
283     }
284
285     return arrayResult;
286 }
287
288 JSValueRef TestRunner::originsWithApplicationCache()
289 {
290     auto& injectedBundle = InjectedBundle::singleton();
291     WKRetainPtr<WKArrayRef> origins(AdoptWK, WKBundleCopyOriginsWithApplicationCache(injectedBundle.bundle()));
292
293     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
294     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
295
296     return stringArrayToJS(context, origins.get());
297 }
298
299 bool TestRunner::isCommandEnabled(JSStringRef name)
300 {
301     return WKBundlePageIsEditingCommandEnabled(InjectedBundle::singleton().page()->page(), toWK(name).get());
302 }
303
304 void TestRunner::setCanOpenWindows(bool)
305 {
306     // It's not clear if or why any tests require opening windows be forbidden.
307     // For now, just ignore this setting, and if we find later it's needed we can add it.
308 }
309
310 void TestRunner::setXSSAuditorEnabled(bool enabled)
311 {
312     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
313     auto& injectedBundle = InjectedBundle::singleton();
314     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
315 }
316
317 void TestRunner::setAllowUniversalAccessFromFileURLs(bool enabled)
318 {
319     auto& injectedBundle = InjectedBundle::singleton();
320     WKBundleSetAllowUniversalAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
321 }
322
323 void TestRunner::setAllowFileAccessFromFileURLs(bool enabled)
324 {
325     auto& injectedBundle = InjectedBundle::singleton();
326     WKBundleSetAllowFileAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
327 }
328
329 void TestRunner::setPluginsEnabled(bool enabled)
330 {
331     auto& injectedBundle = InjectedBundle::singleton();
332     WKBundleSetPluginsEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
333 }
334
335 void TestRunner::setJavaScriptCanAccessClipboard(bool enabled)
336 {
337     auto& injectedBundle = InjectedBundle::singleton();
338      WKBundleSetJavaScriptCanAccessClipboard(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
339 }
340
341 void TestRunner::setPrivateBrowsingEnabled(bool enabled)
342 {
343     auto& injectedBundle = InjectedBundle::singleton();
344      WKBundleSetPrivateBrowsingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
345 }
346
347 void TestRunner::setPopupBlockingEnabled(bool enabled)
348 {
349     auto& injectedBundle = InjectedBundle::singleton();
350      WKBundleSetPopupBlockingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
351 }
352
353 void TestRunner::setAuthorAndUserStylesEnabled(bool enabled)
354 {
355     auto& injectedBundle = InjectedBundle::singleton();
356      WKBundleSetAuthorAndUserStylesEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
357 }
358
359 void TestRunner::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
360 {
361     WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
362 }
363
364 void TestRunner::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
365 {
366     WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
367 }
368
369 bool TestRunner::isPageBoxVisible(int pageIndex)
370 {
371     auto& injectedBundle = InjectedBundle::singleton();
372     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
373     return WKBundleIsPageBoxVisible(injectedBundle.bundle(), mainFrame, pageIndex);
374 }
375
376 void TestRunner::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
377 {
378     if (!element || !JSValueIsObject(context, element))
379         return;
380
381     WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
382     WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
383 }
384
385 void TestRunner::setAudioResult(JSContextRef context, JSValueRef data)
386 {
387     auto& injectedBundle = InjectedBundle::singleton();
388     // FIXME (123058): Use a JSC API to get buffer contents once such is exposed.
389     WKRetainPtr<WKDataRef> audioData(AdoptWK, WKBundleCreateWKDataFromUInt8Array(injectedBundle.bundle(), context, data));
390     injectedBundle.setAudioResult(audioData.get());
391     m_whatToDump = Audio;
392     m_dumpPixels = false;
393 }
394
395 unsigned TestRunner::windowCount()
396 {
397     return InjectedBundle::singleton().pageCount();
398 }
399
400 void TestRunner::clearBackForwardList()
401 {
402     WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::singleton().page()->page()));
403 }
404
405 // Object Creation
406
407 void TestRunner::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
408 {
409     setProperty(context, windowObject, "testRunner", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
410 }
411
412 void TestRunner::showWebInspector()
413 {
414     WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
415 }
416
417 void TestRunner::closeWebInspector()
418 {
419     WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
420 }
421
422 void TestRunner::evaluateInWebInspector(JSStringRef script)
423 {
424     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
425     WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()), scriptWK.get());
426 }
427
428 typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
429 static WorldMap& worldMap()
430 {
431     static WorldMap& map = *new WorldMap;
432     return map;
433 }
434
435 unsigned TestRunner::worldIDForWorld(WKBundleScriptWorldRef world)
436 {
437     WorldMap::const_iterator end = worldMap().end();
438     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
439         if (it->value == world)
440             return it->key;
441     }
442
443     return 0;
444 }
445
446 void TestRunner::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
447 {
448     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
449     // that is created once and cached forever.
450     WKRetainPtr<WKBundleScriptWorldRef> world;
451     if (!worldID)
452         world.adopt(WKBundleScriptWorldCreateWorld());
453     else {
454         WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, nullptr).iterator->value;
455         if (!worldSlot)
456             worldSlot.adopt(WKBundleScriptWorldCreateWorld());
457         world = worldSlot;
458     }
459
460     WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
461     if (!frame)
462         frame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
463
464     JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
465     JSEvaluateScript(jsContext, script, 0, 0, 0, 0); 
466 }
467
468 void TestRunner::setPOSIXLocale(JSStringRef locale)
469 {
470     char localeBuf[32];
471     JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
472     setlocale(LC_ALL, localeBuf);
473 }
474
475 void TestRunner::setTextDirection(JSStringRef direction)
476 {
477     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
478     return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
479 }
480     
481 void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
482 {
483     InjectedBundle::singleton().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
484 }
485
486 void TestRunner::setDefersLoading(bool shouldDeferLoading)
487 {
488     WKBundlePageSetDefersLoading(InjectedBundle::singleton().page()->page(), shouldDeferLoading);
489 }
490
491 void TestRunner::setPageVisibility(JSStringRef state)
492 {
493     InjectedBundle::singleton().setHidden(JSStringIsEqualToUTF8CString(state, "hidden") || JSStringIsEqualToUTF8CString(state, "prerender"));
494 }
495
496 void TestRunner::resetPageVisibility()
497 {
498     InjectedBundle::singleton().setHidden(false);
499 }
500
501 typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
502 static CallbackMap& callbackMap()
503 {
504     static CallbackMap& map = *new CallbackMap;
505     return map;
506 }
507
508 enum {
509     AddChromeInputFieldCallbackID = 1,
510     RemoveChromeInputFieldCallbackID,
511     FocusWebViewCallbackID,
512     SetBackingScaleFactorCallbackID
513 };
514
515 static void cacheTestRunnerCallback(unsigned index, JSValueRef callback)
516 {
517     if (!callback)
518         return;
519
520     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
521     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
522     JSValueProtect(context, callback);
523     callbackMap().add(index, callback);
524 }
525
526 static void callTestRunnerCallback(unsigned index)
527 {
528     if (!callbackMap().contains(index))
529         return;
530     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
531     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
532     JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
533     JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), 0, 0, 0);
534     JSValueUnprotect(context, callback);
535 }
536
537 void TestRunner::addChromeInputField(JSValueRef callback)
538 {
539     cacheTestRunnerCallback(AddChromeInputFieldCallbackID, callback);
540     InjectedBundle::singleton().postAddChromeInputField();
541 }
542
543 void TestRunner::removeChromeInputField(JSValueRef callback)
544 {
545     cacheTestRunnerCallback(RemoveChromeInputFieldCallbackID, callback);
546     InjectedBundle::singleton().postRemoveChromeInputField();
547 }
548
549 void TestRunner::focusWebView(JSValueRef callback)
550 {
551     cacheTestRunnerCallback(FocusWebViewCallbackID, callback);
552     InjectedBundle::singleton().postFocusWebView();
553 }
554
555 void TestRunner::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
556 {
557     cacheTestRunnerCallback(SetBackingScaleFactorCallbackID, callback);
558     InjectedBundle::singleton().postSetBackingScaleFactor(backingScaleFactor);
559 }
560
561 void TestRunner::setWindowIsKey(bool isKey)
562 {
563     InjectedBundle::singleton().postSetWindowIsKey(isKey);
564 }
565
566 void TestRunner::callAddChromeInputFieldCallback()
567 {
568     callTestRunnerCallback(AddChromeInputFieldCallbackID);
569 }
570
571 void TestRunner::callRemoveChromeInputFieldCallback()
572 {
573     callTestRunnerCallback(RemoveChromeInputFieldCallbackID);
574 }
575
576 void TestRunner::callFocusWebViewCallback()
577 {
578     callTestRunnerCallback(FocusWebViewCallbackID);
579 }
580
581 void TestRunner::callSetBackingScaleFactorCallback()
582 {
583     callTestRunnerCallback(SetBackingScaleFactorCallbackID);
584 }
585
586 static inline bool toBool(JSStringRef value)
587 {
588     return JSStringIsEqualToUTF8CString(value, "true") || JSStringIsEqualToUTF8CString(value, "1");
589 }
590
591 void TestRunner::overridePreference(JSStringRef preference, JSStringRef value)
592 {
593     auto& injectedBundle = InjectedBundle::singleton();
594     // FIXME: handle non-boolean preferences.
595     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), toWK(preference).get(), toBool(value));
596 }
597
598 void TestRunner::setAlwaysAcceptCookies(bool accept)
599 {
600     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAlwaysAcceptCookies"));
601
602     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(accept));
603
604     WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), 0);
605 }
606
607 double TestRunner::preciseTime()
608 {
609     return currentTime();
610 }
611
612 void TestRunner::setUserStyleSheetEnabled(bool enabled)
613 {
614     m_userStyleSheetEnabled = enabled;
615
616     WKRetainPtr<WKStringRef> emptyUrl = adoptWK(WKStringCreateWithUTF8CString(""));
617     WKStringRef location = enabled ? m_userStyleSheetLocation.get() : emptyUrl.get();
618     auto& injectedBundle = InjectedBundle::singleton();
619     WKBundleSetUserStyleSheetLocation(injectedBundle.bundle(), injectedBundle.pageGroup(), location);
620 }
621
622 void TestRunner::setUserStyleSheetLocation(JSStringRef location)
623 {
624     m_userStyleSheetLocation = adoptWK(WKStringCreateWithJSString(location));
625
626     if (m_userStyleSheetEnabled)
627         setUserStyleSheetEnabled(true);
628 }
629
630 void TestRunner::setSpatialNavigationEnabled(bool enabled)
631 {
632     auto& injectedBundle = InjectedBundle::singleton();
633     WKBundleSetSpatialNavigationEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
634 }
635
636 void TestRunner::setTabKeyCyclesThroughElements(bool enabled)
637 {
638     auto& injectedBundle = InjectedBundle::singleton();
639     WKBundleSetTabKeyCyclesThroughElements(injectedBundle.bundle(), injectedBundle.page()->page(), enabled);
640 }
641
642 void TestRunner::setSerializeHTTPLoads()
643 {
644     // WK2 doesn't reorder loads.
645 }
646
647 void TestRunner::dispatchPendingLoadRequests()
648 {
649     // WK2 doesn't keep pending requests.
650 }
651
652 void TestRunner::setCacheModel(int model)
653 {
654     InjectedBundle::singleton().setCacheModel(model);
655 }
656
657 void TestRunner::setAsynchronousSpellCheckingEnabled(bool enabled)
658 {
659     auto& injectedBundle = InjectedBundle::singleton();
660     WKBundleSetAsynchronousSpellCheckingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
661 }
662
663 void TestRunner::grantWebNotificationPermission(JSStringRef origin)
664 {
665     WKRetainPtr<WKStringRef> originWK = toWK(origin);
666     auto& injectedBundle = InjectedBundle::singleton();
667     WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), true);
668 }
669
670 void TestRunner::denyWebNotificationPermission(JSStringRef origin)
671 {
672     WKRetainPtr<WKStringRef> originWK = toWK(origin);
673     auto& injectedBundle = InjectedBundle::singleton();
674     WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), false);
675 }
676
677 void TestRunner::removeAllWebNotificationPermissions()
678 {
679     auto& injectedBundle = InjectedBundle::singleton();
680     WKBundleRemoveAllWebNotificationPermissions(injectedBundle.bundle(), injectedBundle.page()->page());
681 }
682
683 void TestRunner::simulateWebNotificationClick(JSValueRef notification)
684 {
685     auto& injectedBundle = InjectedBundle::singleton();
686     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
687     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
688     uint64_t notificationID = WKBundleGetWebNotificationID(injectedBundle.bundle(), context, notification);
689     injectedBundle.postSimulateWebNotificationClick(notificationID);
690 }
691
692 void TestRunner::setGeolocationPermission(bool enabled)
693 {
694     // FIXME: this should be done by frame.
695     InjectedBundle::singleton().setGeolocationPermission(enabled);
696 }
697
698 bool TestRunner::isGeolocationProviderActive()
699 {
700     return InjectedBundle::singleton().isGeolocationProviderActive();
701 }
702
703 void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, JSValueRef jsAltitude, JSValueRef jsAltitudeAccuracy, JSValueRef jsHeading, JSValueRef jsSpeed)
704 {
705     auto& injectedBundle = InjectedBundle::singleton();
706     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
707     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
708
709     bool providesAltitude = false;
710     double altitude = 0.;
711     if (!JSValueIsUndefined(context, jsAltitude)) {
712         providesAltitude = true;
713         altitude = JSValueToNumber(context, jsAltitude, 0);
714     }
715
716     bool providesAltitudeAccuracy = false;
717     double altitudeAccuracy = 0.;
718     if (!JSValueIsUndefined(context, jsAltitudeAccuracy)) {
719         providesAltitudeAccuracy = true;
720         altitudeAccuracy = JSValueToNumber(context, jsAltitudeAccuracy, 0);
721     }
722
723     bool providesHeading = false;
724     double heading = 0.;
725     if (!JSValueIsUndefined(context, jsHeading)) {
726         providesHeading = true;
727         heading = JSValueToNumber(context, jsHeading, 0);
728     }
729
730     bool providesSpeed = false;
731     double speed = 0.;
732     if (!JSValueIsUndefined(context, jsSpeed)) {
733         providesSpeed = true;
734         speed = JSValueToNumber(context, jsSpeed, 0);
735     }
736
737     injectedBundle.setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
738 }
739
740 void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message)
741 {
742     WKRetainPtr<WKStringRef> messageWK = toWK(message);
743     InjectedBundle::singleton().setMockGeolocationPositionUnavailableError(messageWK.get());
744 }
745
746 void TestRunner::setUserMediaPermission(bool enabled)
747 {
748     // FIXME: this should be done by frame.
749     InjectedBundle::singleton().setUserMediaPermission(enabled);
750 }
751
752 bool TestRunner::callShouldCloseOnWebView()
753 {
754     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
755     return WKBundleFrameCallShouldCloseOnWebView(mainFrame);
756 }
757
758 void TestRunner::queueBackNavigation(unsigned howFarBackward)
759 {
760     InjectedBundle::singleton().queueBackNavigation(howFarBackward);
761 }
762
763 void TestRunner::queueForwardNavigation(unsigned howFarForward)
764 {
765     InjectedBundle::singleton().queueForwardNavigation(howFarForward);
766 }
767
768 void TestRunner::queueLoad(JSStringRef url, JSStringRef target, bool shouldOpenExternalURLs)
769 {
770     auto& injectedBundle = InjectedBundle::singleton();
771     WKRetainPtr<WKURLRef> baseURLWK(AdoptWK, WKBundleFrameCopyURL(WKBundlePageGetMainFrame(injectedBundle.page()->page())));
772     WKRetainPtr<WKURLRef> urlWK(AdoptWK, WKURLCreateWithBaseURL(baseURLWK.get(), toWTFString(toWK(url)).utf8().data()));
773     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(urlWK.get()));
774
775     injectedBundle.queueLoad(urlStringWK.get(), toWK(target).get(), shouldOpenExternalURLs);
776 }
777
778 void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL)
779 {
780     WKRetainPtr<WKStringRef> contentWK = toWK(content);
781     WKRetainPtr<WKStringRef> baseURLWK = baseURL ? toWK(baseURL) : WKRetainPtr<WKStringRef>();
782     WKRetainPtr<WKStringRef> unreachableURLWK = unreachableURL ? toWK(unreachableURL) : WKRetainPtr<WKStringRef>();
783
784     InjectedBundle::singleton().queueLoadHTMLString(contentWK.get(), baseURLWK.get(), unreachableURLWK.get());
785 }
786
787 void TestRunner::queueReload()
788 {
789     InjectedBundle::singleton().queueReload();
790 }
791
792 void TestRunner::queueLoadingScript(JSStringRef script)
793 {
794     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
795     InjectedBundle::singleton().queueLoadingScript(scriptWK.get());
796 }
797
798 void TestRunner::queueNonLoadingScript(JSStringRef script)
799 {
800     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
801     InjectedBundle::singleton().queueNonLoadingScript(scriptWK.get());
802 }
803
804 void TestRunner::setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges)
805 {
806     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetHandlesAuthenticationChallenge"));
807     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(handlesAuthenticationChallenges));
808     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
809 }
810
811 void TestRunner::setAuthenticationUsername(JSStringRef username)
812 {
813     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationUsername"));
814     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(username));
815     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
816 }
817
818 void TestRunner::setAuthenticationPassword(JSStringRef password)
819 {
820     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationPassword"));
821     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(password));
822     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
823 }
824
825 bool TestRunner::secureEventInputIsEnabled() const
826 {
827     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SecureEventInputIsEnabled"));
828     WKTypeRef returnData = 0;
829
830     WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), 0, &returnData);
831     return WKBooleanGetValue(static_cast<WKBooleanRef>(returnData));
832 }
833
834 void TestRunner::setBlockAllPlugins(bool shouldBlock)
835 {
836     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetBlockAllPlugins"));
837     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(shouldBlock));
838     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
839 }
840
841 JSValueRef TestRunner::numberOfDFGCompiles(JSValueRef theFunction)
842 {
843     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
844     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
845     return JSC::numberOfDFGCompiles(context, theFunction);
846 }
847
848 JSValueRef TestRunner::neverInlineFunction(JSValueRef theFunction)
849 {
850     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
851     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
852     return JSC::setNeverInline(context, theFunction);
853 }
854
855 } // namespace WTR