Make it possible to test effect of view exposed rect on tiled backing
[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 #if PLATFORM(GTK)
100     , m_waitToDumpWatchdogTimer(RunLoop::main(), this, &TestRunner::waitToDumpWatchdogTimerFired)
101 #endif
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::singleton().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::singleton().setCustomPolicyDelegate(enabled, permissive);
136 }
137
138 void TestRunner::waitForPolicyDelegate()
139 {
140     setCustomPolicyDelegate(true);
141     waitUntilDone();
142 }
143
144 void TestRunner::waitUntilDownloadFinished()
145 {
146     m_shouldFinishAfterDownload = true;
147     waitUntilDone();
148 }
149
150 void TestRunner::waitUntilDone()
151 {
152     m_waitToDump = true;
153     if (InjectedBundle::singleton().useWaitToDumpWatchdogTimer())
154         initializeWaitToDumpWatchdogTimerIfNeeded();
155 }
156
157 void TestRunner::waitToDumpWatchdogTimerFired()
158 {
159     invalidateWaitToDumpWatchdogTimer();
160     auto& injectedBundle = InjectedBundle::singleton();
161     injectedBundle.outputText("FAIL: Timed out waiting for notifyDone to be called\n\n");
162     injectedBundle.done();
163 }
164
165 void TestRunner::notifyDone()
166 {
167     auto& injectedBundle = InjectedBundle::singleton();
168     if (!injectedBundle.isTestRunning())
169         return;
170
171     if (m_waitToDump && !injectedBundle.topLoadingFrame())
172         injectedBundle.page()->dump();
173
174     // We don't call invalidateWaitToDumpWatchdogTimer() here, even if we continue to wait for a load to finish.
175     // The test is still subject to timeout checking - it is better to detect an async timeout inside WebKitTestRunner
176     // than to let webkitpy do that, because WebKitTestRunner will dump partial results.
177
178     m_waitToDump = false;
179 }
180
181 void TestRunner::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
182 {
183     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
184
185     WKBundlePageAddUserScript(InjectedBundle::singleton().page()->page(), sourceWK.get(),
186         (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
187         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
188 }
189
190 void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames)
191 {
192     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
193
194     WKBundlePageAddUserStyleSheet(InjectedBundle::singleton().page()->page(), sourceWK.get(),
195         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
196 }
197
198 void TestRunner::keepWebHistory()
199 {
200     InjectedBundle::singleton().postSetAddsVisitedLinks(true);
201 }
202
203 void TestRunner::execCommand(JSStringRef name, JSStringRef argument)
204 {
205     WKBundlePageExecuteEditingCommand(InjectedBundle::singleton().page()->page(), toWK(name).get(), toWK(argument).get());
206 }
207
208 bool TestRunner::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
209 {
210     WKFindOptions options = 0;
211
212     auto& injectedBundle = InjectedBundle::singleton();
213     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
214     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
215     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
216     JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
217     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
218     if (!JSValueIsNumber(context, lengthValue))
219         return false;
220
221     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
222     for (size_t i = 0; i < length; ++i) {
223         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
224         if (!JSValueIsString(context, value))
225             continue;
226
227         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
228
229         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
230             options |= kWKFindOptionsCaseInsensitive;
231         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
232             options |= kWKFindOptionsAtWordStarts;
233         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
234             options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
235         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
236             options |= kWKFindOptionsBackwards;
237         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
238             options |= kWKFindOptionsWrapAround;
239         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
240             // FIXME: No kWKFindOptionsStartInSelection.
241         }
242     }
243
244     return WKBundlePageFindString(injectedBundle.page()->page(), toWK(target).get(), options);
245 }
246
247 void TestRunner::clearAllDatabases()
248 {
249     WKBundleClearAllDatabases(InjectedBundle::singleton().bundle());
250 }
251
252 void TestRunner::setDatabaseQuota(uint64_t quota)
253 {
254     return WKBundleSetDatabaseQuota(InjectedBundle::singleton().bundle(), quota);
255 }
256
257 void TestRunner::clearAllApplicationCaches()
258 {
259     WKBundlePageClearApplicationCache(InjectedBundle::singleton().page()->page());
260 }
261
262 void TestRunner::clearApplicationCacheForOrigin(JSStringRef origin)
263 {
264     WKBundlePageClearApplicationCacheForOrigin(InjectedBundle::singleton().page()->page(), toWK(origin).get());
265 }
266
267 void TestRunner::setAppCacheMaximumSize(uint64_t size)
268 {
269     WKBundlePageSetAppCacheMaximumSize(InjectedBundle::singleton().page()->page(), size);
270 }
271
272 long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef origin)
273 {
274     return WKBundlePageGetAppCacheUsageForOrigin(InjectedBundle::singleton().page()->page(), toWK(origin).get());
275 }
276
277 void TestRunner::disallowIncreaseForApplicationCacheQuota()
278 {
279     m_disallowIncreaseForApplicationCacheQuota = true;
280 }
281
282 static inline JSValueRef stringArrayToJS(JSContextRef context, WKArrayRef strings)
283 {
284     const size_t count = WKArrayGetSize(strings);
285
286     JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
287     JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
288     for (size_t i = 0; i < count; ++i) {
289         WKStringRef stringRef = static_cast<WKStringRef>(WKArrayGetItemAtIndex(strings, i));
290         JSRetainPtr<JSStringRef> stringJS = toJS(stringRef);
291         JSObjectSetPropertyAtIndex(context, arrayObj, i, JSValueMakeString(context, stringJS.get()), 0);
292     }
293
294     return arrayResult;
295 }
296
297 JSValueRef TestRunner::originsWithApplicationCache()
298 {
299     WKBundlePageRef page = InjectedBundle::singleton().page()->page();
300
301     WKRetainPtr<WKArrayRef> origins(AdoptWK, WKBundlePageCopyOriginsWithApplicationCache(page));
302
303     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(page);
304     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
305
306     return stringArrayToJS(context, origins.get());
307 }
308
309 bool TestRunner::isCommandEnabled(JSStringRef name)
310 {
311     return WKBundlePageIsEditingCommandEnabled(InjectedBundle::singleton().page()->page(), toWK(name).get());
312 }
313
314 void TestRunner::setCanOpenWindows(bool)
315 {
316     // It's not clear if or why any tests require opening windows be forbidden.
317     // For now, just ignore this setting, and if we find later it's needed we can add it.
318 }
319
320 void TestRunner::setXSSAuditorEnabled(bool enabled)
321 {
322     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
323     auto& injectedBundle = InjectedBundle::singleton();
324     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
325 }
326
327 void TestRunner::setShadowDOMEnabled(bool enabled)
328 {
329     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitShadowDOMEnabled"));
330     auto& injectedBundle = InjectedBundle::singleton();
331     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
332 }
333
334 void TestRunner::setCustomElementsEnabled(bool enabled)
335 {
336     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitCustomElementsEnabled"));
337     auto& injectedBundle = InjectedBundle::singleton();
338     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
339 }
340
341 void TestRunner::setWebGL2Enabled(bool enabled)
342 {
343     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitWebGL2Enabled"));
344     auto& injectedBundle = InjectedBundle::singleton();
345     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
346 }
347
348 void TestRunner::setFetchAPIEnabled(bool enabled)
349 {
350     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitFetchAPIEnabled"));
351     auto& injectedBundle = InjectedBundle::singleton();
352     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
353 }
354
355 void TestRunner::setAllowUniversalAccessFromFileURLs(bool enabled)
356 {
357     auto& injectedBundle = InjectedBundle::singleton();
358     WKBundleSetAllowUniversalAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
359 }
360
361 void TestRunner::setAllowFileAccessFromFileURLs(bool enabled)
362 {
363     auto& injectedBundle = InjectedBundle::singleton();
364     WKBundleSetAllowFileAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
365 }
366
367 void TestRunner::setPluginsEnabled(bool enabled)
368 {
369     auto& injectedBundle = InjectedBundle::singleton();
370     WKBundleSetPluginsEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
371 }
372
373 void TestRunner::setJavaScriptCanAccessClipboard(bool enabled)
374 {
375     auto& injectedBundle = InjectedBundle::singleton();
376      WKBundleSetJavaScriptCanAccessClipboard(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
377 }
378
379 void TestRunner::setPrivateBrowsingEnabled(bool enabled)
380 {
381     auto& injectedBundle = InjectedBundle::singleton();
382      WKBundleSetPrivateBrowsingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
383 }
384
385 void TestRunner::setPopupBlockingEnabled(bool enabled)
386 {
387     auto& injectedBundle = InjectedBundle::singleton();
388      WKBundleSetPopupBlockingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
389 }
390
391 void TestRunner::setAuthorAndUserStylesEnabled(bool enabled)
392 {
393     auto& injectedBundle = InjectedBundle::singleton();
394      WKBundleSetAuthorAndUserStylesEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
395 }
396
397 void TestRunner::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
398 {
399     WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
400 }
401
402 void TestRunner::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
403 {
404     WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
405 }
406
407 bool TestRunner::isPageBoxVisible(int pageIndex)
408 {
409     auto& injectedBundle = InjectedBundle::singleton();
410     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
411     return WKBundleIsPageBoxVisible(injectedBundle.bundle(), mainFrame, pageIndex);
412 }
413
414 void TestRunner::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
415 {
416     if (!element || !JSValueIsObject(context, element))
417         return;
418
419     WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
420     WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
421 }
422
423 void TestRunner::setAudioResult(JSContextRef context, JSValueRef data)
424 {
425     auto& injectedBundle = InjectedBundle::singleton();
426     // FIXME (123058): Use a JSC API to get buffer contents once such is exposed.
427     WKRetainPtr<WKDataRef> audioData(AdoptWK, WKBundleCreateWKDataFromUInt8Array(injectedBundle.bundle(), context, data));
428     injectedBundle.setAudioResult(audioData.get());
429     m_whatToDump = Audio;
430     m_dumpPixels = false;
431 }
432
433 unsigned TestRunner::windowCount()
434 {
435     return InjectedBundle::singleton().pageCount();
436 }
437
438 void TestRunner::clearBackForwardList()
439 {
440     WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::singleton().page()->page()));
441 }
442
443 // Object Creation
444
445 void TestRunner::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
446 {
447     setProperty(context, windowObject, "testRunner", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
448 }
449
450 void TestRunner::showWebInspector()
451 {
452     WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
453 }
454
455 void TestRunner::closeWebInspector()
456 {
457     WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
458 }
459
460 void TestRunner::evaluateInWebInspector(JSStringRef script)
461 {
462     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
463     WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()), scriptWK.get());
464 }
465
466 typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
467 static WorldMap& worldMap()
468 {
469     static WorldMap& map = *new WorldMap;
470     return map;
471 }
472
473 unsigned TestRunner::worldIDForWorld(WKBundleScriptWorldRef world)
474 {
475     WorldMap::const_iterator end = worldMap().end();
476     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
477         if (it->value == world)
478             return it->key;
479     }
480
481     return 0;
482 }
483
484 void TestRunner::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
485 {
486     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
487     // that is created once and cached forever.
488     WKRetainPtr<WKBundleScriptWorldRef> world;
489     if (!worldID)
490         world.adopt(WKBundleScriptWorldCreateWorld());
491     else {
492         WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, nullptr).iterator->value;
493         if (!worldSlot)
494             worldSlot.adopt(WKBundleScriptWorldCreateWorld());
495         world = worldSlot;
496     }
497
498     WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
499     if (!frame)
500         frame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
501
502     JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
503     JSEvaluateScript(jsContext, script, 0, 0, 0, 0); 
504 }
505
506 void TestRunner::setPOSIXLocale(JSStringRef locale)
507 {
508     char localeBuf[32];
509     JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
510     setlocale(LC_ALL, localeBuf);
511 }
512
513 void TestRunner::setTextDirection(JSStringRef direction)
514 {
515     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
516     return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
517 }
518     
519 void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
520 {
521     InjectedBundle::singleton().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
522 }
523
524 void TestRunner::setDefersLoading(bool shouldDeferLoading)
525 {
526     WKBundlePageSetDefersLoading(InjectedBundle::singleton().page()->page(), shouldDeferLoading);
527 }
528
529 void TestRunner::setPageVisibility(JSStringRef state)
530 {
531     InjectedBundle::singleton().setHidden(JSStringIsEqualToUTF8CString(state, "hidden") || JSStringIsEqualToUTF8CString(state, "prerender"));
532 }
533
534 void TestRunner::resetPageVisibility()
535 {
536     InjectedBundle::singleton().setHidden(false);
537 }
538
539 typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
540 static CallbackMap& callbackMap()
541 {
542     static CallbackMap& map = *new CallbackMap;
543     return map;
544 }
545
546 enum {
547     AddChromeInputFieldCallbackID = 1,
548     RemoveChromeInputFieldCallbackID,
549     FocusWebViewCallbackID,
550     SetBackingScaleFactorCallbackID,
551     DidBeginSwipeCallbackID,
552     WillEndSwipeCallbackID,
553     DidEndSwipeCallbackID,
554     DidRemoveSwipeSnapshotCallbackID,
555     FirstUIScriptCallbackID = 100
556 };
557
558 static void cacheTestRunnerCallback(unsigned index, JSValueRef callback)
559 {
560     if (!callback)
561         return;
562
563     if (callbackMap().contains(index)) {
564         InjectedBundle::singleton().outputText(String::format("FAIL: Tried to install a second TestRunner callback for the same event (id %d)\n\n", index));
565         return;
566     }
567
568     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
569     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
570     JSValueProtect(context, callback);
571     callbackMap().add(index, callback);
572 }
573
574 static void callTestRunnerCallback(unsigned index, size_t argumentCount = 0, const JSValueRef arguments[] = nullptr)
575 {
576     if (!callbackMap().contains(index))
577         return;
578     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
579     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
580     JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
581     JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), argumentCount, arguments, 0);
582     JSValueUnprotect(context, callback);
583 }
584
585 void TestRunner::clearTestRunnerCallbacks()
586 {
587     for (auto& iter : callbackMap()) {
588         WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
589         JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
590         JSObjectRef callback = JSValueToObject(context, iter.value, 0);
591         JSValueUnprotect(context, callback);
592     }
593
594     callbackMap().clear();
595 }
596
597 void TestRunner::addChromeInputField(JSValueRef callback)
598 {
599     cacheTestRunnerCallback(AddChromeInputFieldCallbackID, callback);
600     InjectedBundle::singleton().postAddChromeInputField();
601 }
602
603 void TestRunner::removeChromeInputField(JSValueRef callback)
604 {
605     cacheTestRunnerCallback(RemoveChromeInputFieldCallbackID, callback);
606     InjectedBundle::singleton().postRemoveChromeInputField();
607 }
608
609 void TestRunner::focusWebView(JSValueRef callback)
610 {
611     cacheTestRunnerCallback(FocusWebViewCallbackID, callback);
612     InjectedBundle::singleton().postFocusWebView();
613 }
614
615 void TestRunner::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
616 {
617     cacheTestRunnerCallback(SetBackingScaleFactorCallbackID, callback);
618     InjectedBundle::singleton().postSetBackingScaleFactor(backingScaleFactor);
619 }
620
621 void TestRunner::setWindowIsKey(bool isKey)
622 {
623     InjectedBundle::singleton().postSetWindowIsKey(isKey);
624 }
625
626 void TestRunner::setViewSize(double width, double height)
627 {
628     InjectedBundle::singleton().postSetViewSize(width, height);
629 }
630
631 void TestRunner::callAddChromeInputFieldCallback()
632 {
633     callTestRunnerCallback(AddChromeInputFieldCallbackID);
634 }
635
636 void TestRunner::callRemoveChromeInputFieldCallback()
637 {
638     callTestRunnerCallback(RemoveChromeInputFieldCallbackID);
639 }
640
641 void TestRunner::callFocusWebViewCallback()
642 {
643     callTestRunnerCallback(FocusWebViewCallbackID);
644 }
645
646 void TestRunner::callSetBackingScaleFactorCallback()
647 {
648     callTestRunnerCallback(SetBackingScaleFactorCallbackID);
649 }
650
651 static inline bool toBool(JSStringRef value)
652 {
653     return JSStringIsEqualToUTF8CString(value, "true") || JSStringIsEqualToUTF8CString(value, "1");
654 }
655
656 void TestRunner::overridePreference(JSStringRef preference, JSStringRef value)
657 {
658     auto& injectedBundle = InjectedBundle::singleton();
659     // FIXME: handle non-boolean preferences.
660     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), toWK(preference).get(), toBool(value));
661 }
662
663 void TestRunner::setAlwaysAcceptCookies(bool accept)
664 {
665     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAlwaysAcceptCookies"));
666
667     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(accept));
668
669     WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), 0);
670 }
671
672 double TestRunner::preciseTime()
673 {
674     return currentTime();
675 }
676
677 void TestRunner::setUserStyleSheetEnabled(bool enabled)
678 {
679     m_userStyleSheetEnabled = enabled;
680
681     WKRetainPtr<WKStringRef> emptyUrl = adoptWK(WKStringCreateWithUTF8CString(""));
682     WKStringRef location = enabled ? m_userStyleSheetLocation.get() : emptyUrl.get();
683     auto& injectedBundle = InjectedBundle::singleton();
684     WKBundleSetUserStyleSheetLocation(injectedBundle.bundle(), injectedBundle.pageGroup(), location);
685 }
686
687 void TestRunner::setUserStyleSheetLocation(JSStringRef location)
688 {
689     m_userStyleSheetLocation = adoptWK(WKStringCreateWithJSString(location));
690
691     if (m_userStyleSheetEnabled)
692         setUserStyleSheetEnabled(true);
693 }
694
695 void TestRunner::setSpatialNavigationEnabled(bool enabled)
696 {
697     auto& injectedBundle = InjectedBundle::singleton();
698     WKBundleSetSpatialNavigationEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
699 }
700
701 void TestRunner::setTabKeyCyclesThroughElements(bool enabled)
702 {
703     auto& injectedBundle = InjectedBundle::singleton();
704     WKBundleSetTabKeyCyclesThroughElements(injectedBundle.bundle(), injectedBundle.page()->page(), enabled);
705 }
706
707 void TestRunner::setSerializeHTTPLoads()
708 {
709     // WK2 doesn't reorder loads.
710 }
711
712 void TestRunner::dispatchPendingLoadRequests()
713 {
714     // WK2 doesn't keep pending requests.
715 }
716
717 void TestRunner::setCacheModel(int model)
718 {
719     InjectedBundle::singleton().setCacheModel(model);
720 }
721
722 void TestRunner::setAsynchronousSpellCheckingEnabled(bool enabled)
723 {
724     auto& injectedBundle = InjectedBundle::singleton();
725     WKBundleSetAsynchronousSpellCheckingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
726 }
727
728 void TestRunner::grantWebNotificationPermission(JSStringRef origin)
729 {
730     WKRetainPtr<WKStringRef> originWK = toWK(origin);
731     auto& injectedBundle = InjectedBundle::singleton();
732     WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), true);
733 }
734
735 void TestRunner::denyWebNotificationPermission(JSStringRef origin)
736 {
737     WKRetainPtr<WKStringRef> originWK = toWK(origin);
738     auto& injectedBundle = InjectedBundle::singleton();
739     WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), false);
740 }
741
742 void TestRunner::removeAllWebNotificationPermissions()
743 {
744     auto& injectedBundle = InjectedBundle::singleton();
745     WKBundleRemoveAllWebNotificationPermissions(injectedBundle.bundle(), injectedBundle.page()->page());
746 }
747
748 void TestRunner::simulateWebNotificationClick(JSValueRef notification)
749 {
750     auto& injectedBundle = InjectedBundle::singleton();
751     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
752     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
753     uint64_t notificationID = WKBundleGetWebNotificationID(injectedBundle.bundle(), context, notification);
754     injectedBundle.postSimulateWebNotificationClick(notificationID);
755 }
756
757 void TestRunner::setGeolocationPermission(bool enabled)
758 {
759     // FIXME: this should be done by frame.
760     InjectedBundle::singleton().setGeolocationPermission(enabled);
761 }
762
763 bool TestRunner::isGeolocationProviderActive()
764 {
765     return InjectedBundle::singleton().isGeolocationProviderActive();
766 }
767
768 void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, JSValueRef jsAltitude, JSValueRef jsAltitudeAccuracy, JSValueRef jsHeading, JSValueRef jsSpeed)
769 {
770     auto& injectedBundle = InjectedBundle::singleton();
771     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
772     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
773
774     bool providesAltitude = false;
775     double altitude = 0.;
776     if (!JSValueIsUndefined(context, jsAltitude)) {
777         providesAltitude = true;
778         altitude = JSValueToNumber(context, jsAltitude, 0);
779     }
780
781     bool providesAltitudeAccuracy = false;
782     double altitudeAccuracy = 0.;
783     if (!JSValueIsUndefined(context, jsAltitudeAccuracy)) {
784         providesAltitudeAccuracy = true;
785         altitudeAccuracy = JSValueToNumber(context, jsAltitudeAccuracy, 0);
786     }
787
788     bool providesHeading = false;
789     double heading = 0.;
790     if (!JSValueIsUndefined(context, jsHeading)) {
791         providesHeading = true;
792         heading = JSValueToNumber(context, jsHeading, 0);
793     }
794
795     bool providesSpeed = false;
796     double speed = 0.;
797     if (!JSValueIsUndefined(context, jsSpeed)) {
798         providesSpeed = true;
799         speed = JSValueToNumber(context, jsSpeed, 0);
800     }
801
802     injectedBundle.setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
803 }
804
805 void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message)
806 {
807     WKRetainPtr<WKStringRef> messageWK = toWK(message);
808     InjectedBundle::singleton().setMockGeolocationPositionUnavailableError(messageWK.get());
809 }
810
811 void TestRunner::setUserMediaPermission(bool enabled)
812 {
813     // FIXME: this should be done by frame.
814     InjectedBundle::singleton().setUserMediaPermission(enabled);
815 }
816
817 void TestRunner::setUserMediaPermissionForOrigin(bool permission, JSStringRef origin, JSStringRef parentOrigin)
818 {
819     WKRetainPtr<WKStringRef> originWK = toWK(origin);
820     WKRetainPtr<WKStringRef> parentOriginWK = toWK(parentOrigin);
821     InjectedBundle::singleton().setUserMediaPermissionForOrigin(permission, originWK.get(), parentOriginWK.get());
822 }
823
824 bool TestRunner::callShouldCloseOnWebView()
825 {
826     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
827     return WKBundleFrameCallShouldCloseOnWebView(mainFrame);
828 }
829
830 void TestRunner::queueBackNavigation(unsigned howFarBackward)
831 {
832     InjectedBundle::singleton().queueBackNavigation(howFarBackward);
833 }
834
835 void TestRunner::queueForwardNavigation(unsigned howFarForward)
836 {
837     InjectedBundle::singleton().queueForwardNavigation(howFarForward);
838 }
839
840 void TestRunner::queueLoad(JSStringRef url, JSStringRef target, bool shouldOpenExternalURLs)
841 {
842     auto& injectedBundle = InjectedBundle::singleton();
843     WKRetainPtr<WKURLRef> baseURLWK(AdoptWK, WKBundleFrameCopyURL(WKBundlePageGetMainFrame(injectedBundle.page()->page())));
844     WKRetainPtr<WKURLRef> urlWK(AdoptWK, WKURLCreateWithBaseURL(baseURLWK.get(), toWTFString(toWK(url)).utf8().data()));
845     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(urlWK.get()));
846
847     injectedBundle.queueLoad(urlStringWK.get(), toWK(target).get(), shouldOpenExternalURLs);
848 }
849
850 void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL)
851 {
852     WKRetainPtr<WKStringRef> contentWK = toWK(content);
853     WKRetainPtr<WKStringRef> baseURLWK = baseURL ? toWK(baseURL) : WKRetainPtr<WKStringRef>();
854     WKRetainPtr<WKStringRef> unreachableURLWK = unreachableURL ? toWK(unreachableURL) : WKRetainPtr<WKStringRef>();
855
856     InjectedBundle::singleton().queueLoadHTMLString(contentWK.get(), baseURLWK.get(), unreachableURLWK.get());
857 }
858
859 void TestRunner::queueReload()
860 {
861     InjectedBundle::singleton().queueReload();
862 }
863
864 void TestRunner::queueLoadingScript(JSStringRef script)
865 {
866     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
867     InjectedBundle::singleton().queueLoadingScript(scriptWK.get());
868 }
869
870 void TestRunner::queueNonLoadingScript(JSStringRef script)
871 {
872     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
873     InjectedBundle::singleton().queueNonLoadingScript(scriptWK.get());
874 }
875
876 void TestRunner::setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges)
877 {
878     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetHandlesAuthenticationChallenge"));
879     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(handlesAuthenticationChallenges));
880     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
881 }
882
883 void TestRunner::setAuthenticationUsername(JSStringRef username)
884 {
885     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationUsername"));
886     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(username));
887     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
888 }
889
890 void TestRunner::setAuthenticationPassword(JSStringRef password)
891 {
892     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationPassword"));
893     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(password));
894     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
895 }
896
897 bool TestRunner::secureEventInputIsEnabled() const
898 {
899     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SecureEventInputIsEnabled"));
900     WKTypeRef returnData = 0;
901
902     WKBundlePagePostSynchronousMessageForTesting(InjectedBundle::singleton().page()->page(), messageName.get(), 0, &returnData);
903     return WKBooleanGetValue(static_cast<WKBooleanRef>(returnData));
904 }
905
906 void TestRunner::setBlockAllPlugins(bool shouldBlock)
907 {
908     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetBlockAllPlugins"));
909     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(shouldBlock));
910     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
911 }
912
913 JSValueRef TestRunner::failNextNewCodeBlock()
914 {
915     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
916     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
917     return JSC::failNextNewCodeBlock(context);
918 }
919
920 JSValueRef TestRunner::numberOfDFGCompiles(JSValueRef theFunction)
921 {
922     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
923     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
924     return JSC::numberOfDFGCompiles(context, theFunction);
925 }
926
927 JSValueRef TestRunner::neverInlineFunction(JSValueRef theFunction)
928 {
929     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
930     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
931     return JSC::setNeverInline(context, theFunction);
932 }
933
934 void TestRunner::setShouldDecideNavigationPolicyAfterDelay(bool value)
935 {
936     m_shouldDecideNavigationPolicyAfterDelay = value;
937     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetShouldDecideNavigationPolicyAfterDelay"));
938     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(value));
939     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
940 }
941
942 void TestRunner::setNavigationGesturesEnabled(bool value)
943 {
944     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetNavigationGesturesEnabled"));
945     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(value));
946     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
947 }
948
949 static unsigned nextUIScriptCallbackID()
950 {
951     static unsigned callbackID = FirstUIScriptCallbackID;
952     return callbackID++;
953 }
954
955 void TestRunner::runUIScript(JSStringRef script, JSValueRef callback)
956 {
957     unsigned callbackID = nextUIScriptCallbackID();
958     cacheTestRunnerCallback(callbackID, callback);
959
960     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("RunUIProcessScript"));
961
962     WKRetainPtr<WKMutableDictionaryRef> testDictionary(AdoptWK, WKMutableDictionaryCreate());
963
964     WKRetainPtr<WKStringRef> scriptKey(AdoptWK, WKStringCreateWithUTF8CString("Script"));
965     WKRetainPtr<WKStringRef> scriptValue(AdoptWK, WKStringCreateWithJSString(script));
966
967     WKRetainPtr<WKStringRef> callbackIDKey(AdoptWK, WKStringCreateWithUTF8CString("CallbackID"));
968     WKRetainPtr<WKUInt64Ref> callbackIDValue = adoptWK(WKUInt64Create(callbackID));
969
970     WKDictionarySetItem(testDictionary.get(), scriptKey.get(), scriptValue.get());
971     WKDictionarySetItem(testDictionary.get(), callbackIDKey.get(), callbackIDValue.get());
972
973     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), testDictionary.get());
974 }
975
976 void TestRunner::runUIScriptCallback(unsigned callbackID, JSStringRef result)
977 {
978     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
979     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
980
981     JSValueRef resultValue = JSValueMakeString(context, result);
982     callTestRunnerCallback(callbackID, 1, &resultValue);
983 }
984
985 void TestRunner::installDidBeginSwipeCallback(JSValueRef callback)
986 {
987     cacheTestRunnerCallback(DidBeginSwipeCallbackID, callback);
988 }
989
990 void TestRunner::installWillEndSwipeCallback(JSValueRef callback)
991 {
992     cacheTestRunnerCallback(WillEndSwipeCallbackID, callback);
993 }
994
995 void TestRunner::installDidEndSwipeCallback(JSValueRef callback)
996 {
997     cacheTestRunnerCallback(DidEndSwipeCallbackID, callback);
998 }
999
1000 void TestRunner::installDidRemoveSwipeSnapshotCallback(JSValueRef callback)
1001 {
1002     cacheTestRunnerCallback(DidRemoveSwipeSnapshotCallbackID, callback);
1003 }
1004
1005 void TestRunner::callDidBeginSwipeCallback()
1006 {
1007     callTestRunnerCallback(DidBeginSwipeCallbackID);
1008 }
1009
1010 void TestRunner::callWillEndSwipeCallback()
1011 {
1012     callTestRunnerCallback(WillEndSwipeCallbackID);
1013 }
1014
1015 void TestRunner::callDidEndSwipeCallback()
1016 {
1017     callTestRunnerCallback(DidEndSwipeCallbackID);
1018 }
1019
1020 void TestRunner::callDidRemoveSwipeSnapshotCallback()
1021 {
1022     callTestRunnerCallback(DidRemoveSwipeSnapshotCallbackID);
1023 }
1024
1025 } // namespace WTR