Add WKTR support for "should open external URLs".
[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     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
176
177     auto& injectedBundle = InjectedBundle::singleton();
178     WKBundleAddUserScript(injectedBundle.bundle(), injectedBundle.pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
179         (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
180         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
181 }
182
183 void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames)
184 {
185     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
186     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
187
188     auto& injectedBundle = InjectedBundle::singleton();
189     WKBundleAddUserStyleSheet(injectedBundle.bundle(), injectedBundle.pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
190         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
191 }
192
193 void TestRunner::keepWebHistory()
194 {
195     InjectedBundle::singleton().postSetAddsVisitedLinks(true);
196 }
197
198 void TestRunner::execCommand(JSStringRef name, JSStringRef argument)
199 {
200     WKBundlePageExecuteEditingCommand(InjectedBundle::singleton().page()->page(), toWK(name).get(), toWK(argument).get());
201 }
202
203 bool TestRunner::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
204 {
205     WKFindOptions options = 0;
206
207     auto& injectedBundle = InjectedBundle::singleton();
208     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
209     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
210     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
211     JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
212     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
213     if (!JSValueIsNumber(context, lengthValue))
214         return false;
215
216     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
217     for (size_t i = 0; i < length; ++i) {
218         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
219         if (!JSValueIsString(context, value))
220             continue;
221
222         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
223
224         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
225             options |= kWKFindOptionsCaseInsensitive;
226         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
227             options |= kWKFindOptionsAtWordStarts;
228         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
229             options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
230         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
231             options |= kWKFindOptionsBackwards;
232         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
233             options |= kWKFindOptionsWrapAround;
234         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
235             // FIXME: No kWKFindOptionsStartInSelection.
236         }
237     }
238
239     return WKBundlePageFindString(injectedBundle.page()->page(), toWK(target).get(), options);
240 }
241
242 void TestRunner::clearAllDatabases()
243 {
244     WKBundleClearAllDatabases(InjectedBundle::singleton().bundle());
245 }
246
247 void TestRunner::setDatabaseQuota(uint64_t quota)
248 {
249     return WKBundleSetDatabaseQuota(InjectedBundle::singleton().bundle(), quota);
250 }
251
252 void TestRunner::clearAllApplicationCaches()
253 {
254     WKBundleClearApplicationCache(InjectedBundle::singleton().bundle());
255 }
256
257 void TestRunner::clearApplicationCacheForOrigin(JSStringRef origin)
258 {
259     WKBundleClearApplicationCacheForOrigin(InjectedBundle::singleton().bundle(), toWK(origin).get());
260 }
261
262 void TestRunner::setAppCacheMaximumSize(uint64_t size)
263 {
264     WKBundleSetAppCacheMaximumSize(InjectedBundle::singleton().bundle(), size);
265 }
266
267 long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef origin)
268 {
269     return WKBundleGetAppCacheUsageForOrigin(InjectedBundle::singleton().bundle(), toWK(origin).get());
270 }
271
272 void TestRunner::disallowIncreaseForApplicationCacheQuota()
273 {
274     m_disallowIncreaseForApplicationCacheQuota = true;
275 }
276
277 static inline JSValueRef stringArrayToJS(JSContextRef context, WKArrayRef strings)
278 {
279     const size_t count = WKArrayGetSize(strings);
280
281     JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
282     JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
283     for (size_t i = 0; i < count; ++i) {
284         WKStringRef stringRef = static_cast<WKStringRef>(WKArrayGetItemAtIndex(strings, i));
285         JSRetainPtr<JSStringRef> stringJS = toJS(stringRef);
286         JSObjectSetPropertyAtIndex(context, arrayObj, i, JSValueMakeString(context, stringJS.get()), 0);
287     }
288
289     return arrayResult;
290 }
291
292 JSValueRef TestRunner::originsWithApplicationCache()
293 {
294     auto& injectedBundle = InjectedBundle::singleton();
295     WKRetainPtr<WKArrayRef> origins(AdoptWK, WKBundleCopyOriginsWithApplicationCache(injectedBundle.bundle()));
296
297     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
298     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
299
300     return stringArrayToJS(context, origins.get());
301 }
302
303 bool TestRunner::isCommandEnabled(JSStringRef name)
304 {
305     return WKBundlePageIsEditingCommandEnabled(InjectedBundle::singleton().page()->page(), toWK(name).get());
306 }
307
308 void TestRunner::setCanOpenWindows(bool)
309 {
310     // It's not clear if or why any tests require opening windows be forbidden.
311     // For now, just ignore this setting, and if we find later it's needed we can add it.
312 }
313
314 void TestRunner::setXSSAuditorEnabled(bool enabled)
315 {
316     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
317     auto& injectedBundle = InjectedBundle::singleton();
318     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), key.get(), enabled);
319 }
320
321 void TestRunner::setAllowUniversalAccessFromFileURLs(bool enabled)
322 {
323     auto& injectedBundle = InjectedBundle::singleton();
324     WKBundleSetAllowUniversalAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
325 }
326
327 void TestRunner::setAllowFileAccessFromFileURLs(bool enabled)
328 {
329     auto& injectedBundle = InjectedBundle::singleton();
330     WKBundleSetAllowFileAccessFromFileURLs(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
331 }
332
333 void TestRunner::setPluginsEnabled(bool enabled)
334 {
335     auto& injectedBundle = InjectedBundle::singleton();
336     WKBundleSetPluginsEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
337 }
338
339 void TestRunner::setJavaScriptCanAccessClipboard(bool enabled)
340 {
341     auto& injectedBundle = InjectedBundle::singleton();
342      WKBundleSetJavaScriptCanAccessClipboard(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
343 }
344
345 void TestRunner::setPrivateBrowsingEnabled(bool enabled)
346 {
347     auto& injectedBundle = InjectedBundle::singleton();
348      WKBundleSetPrivateBrowsingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
349 }
350
351 void TestRunner::setPopupBlockingEnabled(bool enabled)
352 {
353     auto& injectedBundle = InjectedBundle::singleton();
354      WKBundleSetPopupBlockingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
355 }
356
357 void TestRunner::setAuthorAndUserStylesEnabled(bool enabled)
358 {
359     auto& injectedBundle = InjectedBundle::singleton();
360      WKBundleSetAuthorAndUserStylesEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
361 }
362
363 void TestRunner::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
364 {
365     WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
366 }
367
368 void TestRunner::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
369 {
370     WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
371 }
372
373 bool TestRunner::isPageBoxVisible(int pageIndex)
374 {
375     auto& injectedBundle = InjectedBundle::singleton();
376     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
377     return WKBundleIsPageBoxVisible(injectedBundle.bundle(), mainFrame, pageIndex);
378 }
379
380 void TestRunner::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
381 {
382     if (!element || !JSValueIsObject(context, element))
383         return;
384
385     WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
386     WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
387 }
388
389 void TestRunner::setAudioResult(JSContextRef context, JSValueRef data)
390 {
391     auto& injectedBundle = InjectedBundle::singleton();
392     // FIXME (123058): Use a JSC API to get buffer contents once such is exposed.
393     WKRetainPtr<WKDataRef> audioData(AdoptWK, WKBundleCreateWKDataFromUInt8Array(injectedBundle.bundle(), context, data));
394     injectedBundle.setAudioResult(audioData.get());
395     m_whatToDump = Audio;
396     m_dumpPixels = false;
397 }
398
399 unsigned TestRunner::windowCount()
400 {
401     return InjectedBundle::singleton().pageCount();
402 }
403
404 void TestRunner::clearBackForwardList()
405 {
406     WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::singleton().page()->page()));
407 }
408
409 // Object Creation
410
411 void TestRunner::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
412 {
413     setProperty(context, windowObject, "testRunner", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
414 }
415
416 void TestRunner::showWebInspector()
417 {
418     WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
419 }
420
421 void TestRunner::closeWebInspector()
422 {
423     WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()));
424 }
425
426 void TestRunner::evaluateInWebInspector(JSStringRef script)
427 {
428     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
429     WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::singleton().page()->page()), scriptWK.get());
430 }
431
432 typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
433 static WorldMap& worldMap()
434 {
435     static WorldMap& map = *new WorldMap;
436     return map;
437 }
438
439 unsigned TestRunner::worldIDForWorld(WKBundleScriptWorldRef world)
440 {
441     WorldMap::const_iterator end = worldMap().end();
442     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
443         if (it->value == world)
444             return it->key;
445     }
446
447     return 0;
448 }
449
450 void TestRunner::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
451 {
452     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
453     // that is created once and cached forever.
454     WKRetainPtr<WKBundleScriptWorldRef> world;
455     if (!worldID)
456         world.adopt(WKBundleScriptWorldCreateWorld());
457     else {
458         WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, nullptr).iterator->value;
459         if (!worldSlot)
460             worldSlot.adopt(WKBundleScriptWorldCreateWorld());
461         world = worldSlot;
462     }
463
464     WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
465     if (!frame)
466         frame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
467
468     JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
469     JSEvaluateScript(jsContext, script, 0, 0, 0, 0); 
470 }
471
472 void TestRunner::setPOSIXLocale(JSStringRef locale)
473 {
474     char localeBuf[32];
475     JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
476     setlocale(LC_ALL, localeBuf);
477 }
478
479 void TestRunner::setTextDirection(JSStringRef direction)
480 {
481     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
482     return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
483 }
484     
485 void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
486 {
487     InjectedBundle::singleton().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
488 }
489
490 void TestRunner::setDefersLoading(bool shouldDeferLoading)
491 {
492     WKBundlePageSetDefersLoading(InjectedBundle::singleton().page()->page(), shouldDeferLoading);
493 }
494
495 void TestRunner::setPageVisibility(JSStringRef state)
496 {
497     InjectedBundle::singleton().setHidden(JSStringIsEqualToUTF8CString(state, "hidden") || JSStringIsEqualToUTF8CString(state, "prerender"));
498 }
499
500 void TestRunner::resetPageVisibility()
501 {
502     InjectedBundle::singleton().setHidden(false);
503 }
504
505 typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
506 static CallbackMap& callbackMap()
507 {
508     static CallbackMap& map = *new CallbackMap;
509     return map;
510 }
511
512 enum {
513     AddChromeInputFieldCallbackID = 1,
514     RemoveChromeInputFieldCallbackID,
515     FocusWebViewCallbackID,
516     SetBackingScaleFactorCallbackID
517 };
518
519 static void cacheTestRunnerCallback(unsigned index, JSValueRef callback)
520 {
521     if (!callback)
522         return;
523
524     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
525     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
526     JSValueProtect(context, callback);
527     callbackMap().add(index, callback);
528 }
529
530 static void callTestRunnerCallback(unsigned index)
531 {
532     if (!callbackMap().contains(index))
533         return;
534     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
535     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
536     JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
537     JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), 0, 0, 0);
538     JSValueUnprotect(context, callback);
539 }
540
541 void TestRunner::addChromeInputField(JSValueRef callback)
542 {
543     cacheTestRunnerCallback(AddChromeInputFieldCallbackID, callback);
544     InjectedBundle::singleton().postAddChromeInputField();
545 }
546
547 void TestRunner::removeChromeInputField(JSValueRef callback)
548 {
549     cacheTestRunnerCallback(RemoveChromeInputFieldCallbackID, callback);
550     InjectedBundle::singleton().postRemoveChromeInputField();
551 }
552
553 void TestRunner::focusWebView(JSValueRef callback)
554 {
555     cacheTestRunnerCallback(FocusWebViewCallbackID, callback);
556     InjectedBundle::singleton().postFocusWebView();
557 }
558
559 void TestRunner::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
560 {
561     cacheTestRunnerCallback(SetBackingScaleFactorCallbackID, callback);
562     InjectedBundle::singleton().postSetBackingScaleFactor(backingScaleFactor);
563 }
564
565 void TestRunner::setWindowIsKey(bool isKey)
566 {
567     InjectedBundle::singleton().postSetWindowIsKey(isKey);
568 }
569
570 void TestRunner::callAddChromeInputFieldCallback()
571 {
572     callTestRunnerCallback(AddChromeInputFieldCallbackID);
573 }
574
575 void TestRunner::callRemoveChromeInputFieldCallback()
576 {
577     callTestRunnerCallback(RemoveChromeInputFieldCallbackID);
578 }
579
580 void TestRunner::callFocusWebViewCallback()
581 {
582     callTestRunnerCallback(FocusWebViewCallbackID);
583 }
584
585 void TestRunner::callSetBackingScaleFactorCallback()
586 {
587     callTestRunnerCallback(SetBackingScaleFactorCallbackID);
588 }
589
590 static inline bool toBool(JSStringRef value)
591 {
592     return JSStringIsEqualToUTF8CString(value, "true") || JSStringIsEqualToUTF8CString(value, "1");
593 }
594
595 void TestRunner::overridePreference(JSStringRef preference, JSStringRef value)
596 {
597     auto& injectedBundle = InjectedBundle::singleton();
598     // FIXME: handle non-boolean preferences.
599     WKBundleOverrideBoolPreferenceForTestRunner(injectedBundle.bundle(), injectedBundle.pageGroup(), toWK(preference).get(), toBool(value));
600 }
601
602 void TestRunner::setAlwaysAcceptCookies(bool accept)
603 {
604     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAlwaysAcceptCookies"));
605
606     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(accept));
607
608     WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), 0);
609 }
610
611 double TestRunner::preciseTime()
612 {
613     return currentTime();
614 }
615
616 void TestRunner::setUserStyleSheetEnabled(bool enabled)
617 {
618     m_userStyleSheetEnabled = enabled;
619
620     WKRetainPtr<WKStringRef> emptyUrl = adoptWK(WKStringCreateWithUTF8CString(""));
621     WKStringRef location = enabled ? m_userStyleSheetLocation.get() : emptyUrl.get();
622     auto& injectedBundle = InjectedBundle::singleton();
623     WKBundleSetUserStyleSheetLocation(injectedBundle.bundle(), injectedBundle.pageGroup(), location);
624 }
625
626 void TestRunner::setUserStyleSheetLocation(JSStringRef location)
627 {
628     m_userStyleSheetLocation = adoptWK(WKStringCreateWithJSString(location));
629
630     if (m_userStyleSheetEnabled)
631         setUserStyleSheetEnabled(true);
632 }
633
634 void TestRunner::setSpatialNavigationEnabled(bool enabled)
635 {
636     auto& injectedBundle = InjectedBundle::singleton();
637     WKBundleSetSpatialNavigationEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
638 }
639
640 void TestRunner::setTabKeyCyclesThroughElements(bool enabled)
641 {
642     auto& injectedBundle = InjectedBundle::singleton();
643     WKBundleSetTabKeyCyclesThroughElements(injectedBundle.bundle(), injectedBundle.page()->page(), enabled);
644 }
645
646 void TestRunner::setSerializeHTTPLoads()
647 {
648     // WK2 doesn't reorder loads.
649 }
650
651 void TestRunner::dispatchPendingLoadRequests()
652 {
653     // WK2 doesn't keep pending requests.
654 }
655
656 void TestRunner::setCacheModel(int model)
657 {
658     InjectedBundle::singleton().setCacheModel(model);
659 }
660
661 void TestRunner::setAsynchronousSpellCheckingEnabled(bool enabled)
662 {
663     auto& injectedBundle = InjectedBundle::singleton();
664     WKBundleSetAsynchronousSpellCheckingEnabled(injectedBundle.bundle(), injectedBundle.pageGroup(), enabled);
665 }
666
667 void TestRunner::grantWebNotificationPermission(JSStringRef origin)
668 {
669     WKRetainPtr<WKStringRef> originWK = toWK(origin);
670     auto& injectedBundle = InjectedBundle::singleton();
671     WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), true);
672 }
673
674 void TestRunner::denyWebNotificationPermission(JSStringRef origin)
675 {
676     WKRetainPtr<WKStringRef> originWK = toWK(origin);
677     auto& injectedBundle = InjectedBundle::singleton();
678     WKBundleSetWebNotificationPermission(injectedBundle.bundle(), injectedBundle.page()->page(), originWK.get(), false);
679 }
680
681 void TestRunner::removeAllWebNotificationPermissions()
682 {
683     auto& injectedBundle = InjectedBundle::singleton();
684     WKBundleRemoveAllWebNotificationPermissions(injectedBundle.bundle(), injectedBundle.page()->page());
685 }
686
687 void TestRunner::simulateWebNotificationClick(JSValueRef notification)
688 {
689     auto& injectedBundle = InjectedBundle::singleton();
690     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
691     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
692     uint64_t notificationID = WKBundleGetWebNotificationID(injectedBundle.bundle(), context, notification);
693     injectedBundle.postSimulateWebNotificationClick(notificationID);
694 }
695
696 void TestRunner::setGeolocationPermission(bool enabled)
697 {
698     // FIXME: this should be done by frame.
699     InjectedBundle::singleton().setGeolocationPermission(enabled);
700 }
701
702 void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, JSValueRef jsAltitude, JSValueRef jsAltitudeAccuracy, JSValueRef jsHeading, JSValueRef jsSpeed)
703 {
704     auto& injectedBundle = InjectedBundle::singleton();
705     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(injectedBundle.page()->page());
706     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
707
708     bool providesAltitude = false;
709     double altitude = 0.;
710     if (!JSValueIsUndefined(context, jsAltitude)) {
711         providesAltitude = true;
712         altitude = JSValueToNumber(context, jsAltitude, 0);
713     }
714
715     bool providesAltitudeAccuracy = false;
716     double altitudeAccuracy = 0.;
717     if (!JSValueIsUndefined(context, jsAltitudeAccuracy)) {
718         providesAltitudeAccuracy = true;
719         altitudeAccuracy = JSValueToNumber(context, jsAltitudeAccuracy, 0);
720     }
721
722     bool providesHeading = false;
723     double heading = 0.;
724     if (!JSValueIsUndefined(context, jsHeading)) {
725         providesHeading = true;
726         heading = JSValueToNumber(context, jsHeading, 0);
727     }
728
729     bool providesSpeed = false;
730     double speed = 0.;
731     if (!JSValueIsUndefined(context, jsSpeed)) {
732         providesSpeed = true;
733         speed = JSValueToNumber(context, jsSpeed, 0);
734     }
735
736     injectedBundle.setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
737 }
738
739 void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message)
740 {
741     WKRetainPtr<WKStringRef> messageWK = toWK(message);
742     InjectedBundle::singleton().setMockGeolocationPositionUnavailableError(messageWK.get());
743 }
744
745 void TestRunner::setUserMediaPermission(bool enabled)
746 {
747     // FIXME: this should be done by frame.
748     InjectedBundle::singleton().setUserMediaPermission(enabled);
749 }
750
751 bool TestRunner::callShouldCloseOnWebView()
752 {
753     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
754     return WKBundleFrameCallShouldCloseOnWebView(mainFrame);
755 }
756
757 void TestRunner::queueBackNavigation(unsigned howFarBackward)
758 {
759     InjectedBundle::singleton().queueBackNavigation(howFarBackward);
760 }
761
762 void TestRunner::queueForwardNavigation(unsigned howFarForward)
763 {
764     InjectedBundle::singleton().queueForwardNavigation(howFarForward);
765 }
766
767 void TestRunner::queueLoad(JSStringRef url, JSStringRef target, bool shouldOpenExternalURLs)
768 {
769     auto& injectedBundle = InjectedBundle::singleton();
770     WKRetainPtr<WKURLRef> baseURLWK(AdoptWK, WKBundleFrameCopyURL(WKBundlePageGetMainFrame(injectedBundle.page()->page())));
771     WKRetainPtr<WKURLRef> urlWK(AdoptWK, WKURLCreateWithBaseURL(baseURLWK.get(), toWTFString(toWK(url)).utf8().data()));
772     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(urlWK.get()));
773
774     injectedBundle.queueLoad(urlStringWK.get(), toWK(target).get(), shouldOpenExternalURLs);
775 }
776
777 void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL)
778 {
779     WKRetainPtr<WKStringRef> contentWK = toWK(content);
780     WKRetainPtr<WKStringRef> baseURLWK = baseURL ? toWK(baseURL) : WKRetainPtr<WKStringRef>();
781     WKRetainPtr<WKStringRef> unreachableURLWK = unreachableURL ? toWK(unreachableURL) : WKRetainPtr<WKStringRef>();
782
783     InjectedBundle::singleton().queueLoadHTMLString(contentWK.get(), baseURLWK.get(), unreachableURLWK.get());
784 }
785
786 void TestRunner::queueReload()
787 {
788     InjectedBundle::singleton().queueReload();
789 }
790
791 void TestRunner::queueLoadingScript(JSStringRef script)
792 {
793     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
794     InjectedBundle::singleton().queueLoadingScript(scriptWK.get());
795 }
796
797 void TestRunner::queueNonLoadingScript(JSStringRef script)
798 {
799     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
800     InjectedBundle::singleton().queueNonLoadingScript(scriptWK.get());
801 }
802
803 void TestRunner::setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges)
804 {
805     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetHandlesAuthenticationChallenge"));
806     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(handlesAuthenticationChallenges));
807     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
808 }
809
810 void TestRunner::setAuthenticationUsername(JSStringRef username)
811 {
812     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationUsername"));
813     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(username));
814     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
815 }
816
817 void TestRunner::setAuthenticationPassword(JSStringRef password)
818 {
819     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetAuthenticationPassword"));
820     WKRetainPtr<WKStringRef> messageBody(AdoptWK, WKStringCreateWithJSString(password));
821     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
822 }
823
824 bool TestRunner::secureEventInputIsEnabled() const
825 {
826     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SecureEventInputIsEnabled"));
827     WKTypeRef returnData = 0;
828
829     WKBundlePagePostSynchronousMessage(InjectedBundle::singleton().page()->page(), messageName.get(), 0, &returnData);
830     return WKBooleanGetValue(static_cast<WKBooleanRef>(returnData));
831 }
832
833 void TestRunner::setBlockAllPlugins(bool shouldBlock)
834 {
835     WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("SetBlockAllPlugins"));
836     WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(shouldBlock));
837     WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), messageBody.get());
838 }
839
840 JSValueRef TestRunner::numberOfDFGCompiles(JSValueRef theFunction)
841 {
842     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
843     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
844     return JSC::numberOfDFGCompiles(context, theFunction);
845 }
846
847 JSValueRef TestRunner::neverInlineFunction(JSValueRef theFunction)
848 {
849     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
850     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
851     return JSC::setNeverInline(context, theFunction);
852 }
853
854 } // namespace WTR