Move resumeAnimations to use Internals interface
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / LayoutTestController.cpp
1 /*
2  * Copyright (C) 2010, 2011, 2012 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "LayoutTestController.h"
28
29 #include "InjectedBundle.h"
30 #include "InjectedBundlePage.h"
31 #include "JSLayoutTestController.h"
32 #include "PlatformWebView.h"
33 #include "StringFunctions.h"
34 #include "TestController.h"
35 #include <WebCore/PageVisibilityState.h>
36 #include <WebKit2/WKBundleBackForwardList.h>
37 #include <WebKit2/WKBundleFrame.h>
38 #include <WebKit2/WKBundleFramePrivate.h>
39 #include <WebKit2/WKBundleInspector.h>
40 #include <WebKit2/WKBundleNodeHandlePrivate.h>
41 #include <WebKit2/WKBundlePagePrivate.h>
42 #include <WebKit2/WKBundlePrivate.h>
43 #include <WebKit2/WKBundleScriptWorld.h>
44 #include <WebKit2/WKRetainPtr.h>
45 #include <WebKit2/WebKit2.h>
46 #include <wtf/HashMap.h>
47 #include <wtf/text/StringBuilder.h>
48
49 namespace WTR {
50
51 // This is lower than DumpRenderTree's timeout, to make it easier to work through the failures
52 // Eventually it should be changed to match.
53 const double LayoutTestController::waitToDumpWatchdogTimerInterval = 6;
54
55 static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
56 {
57     if (!object)
58         return 0;
59     JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
60     JSValueRef exception;
61     return JSObjectGetProperty(context, object, propertyNameString.get(), &exception);
62 }
63
64 static JSObjectRef propertyObject(JSContextRef context, JSObjectRef object, const char* propertyName)
65 {
66     JSValueRef value = propertyValue(context, object, propertyName);
67     if (!value || !JSValueIsObject(context, value))
68         return 0;
69     return const_cast<JSObjectRef>(value);
70 }
71
72 static JSObjectRef getElementById(WKBundleFrameRef frame, JSStringRef elementId)
73 {
74     JSContextRef context = WKBundleFrameGetJavaScriptContext(frame);
75     JSObjectRef document = propertyObject(context, JSContextGetGlobalObject(context), "document");
76     if (!document)
77         return 0;
78     JSValueRef getElementById = propertyObject(context, document, "getElementById");
79     if (!getElementById || !JSValueIsObject(context, getElementById))
80         return 0;
81     JSValueRef elementIdValue = JSValueMakeString(context, elementId);
82     JSValueRef exception;
83     JSValueRef element = JSObjectCallAsFunction(context, const_cast<JSObjectRef>(getElementById), document, 1, &elementIdValue, &exception);
84     if (!element || !JSValueIsObject(context, element))
85         return 0;
86     return const_cast<JSObjectRef>(element);
87 }
88
89 PassRefPtr<LayoutTestController> LayoutTestController::create()
90 {
91     return adoptRef(new LayoutTestController);
92 }
93
94 LayoutTestController::LayoutTestController()
95     : m_whatToDump(RenderTree)
96     , m_shouldDumpAllFrameScrollPositions(false)
97     , m_shouldDumpBackForwardListsForAllWindows(false)
98     , m_shouldAllowEditing(true)
99     , m_shouldCloseExtraWindows(false)
100     , m_dumpEditingCallbacks(false)
101     , m_dumpStatusCallbacks(false)
102     , m_dumpTitleChanges(false)
103     , m_dumpPixels(true)
104     , m_dumpFullScreenCallbacks(false)
105     , m_dumpFrameLoadCallbacks(false)
106     , m_waitToDump(false)
107     , m_testRepaint(false)
108     , m_testRepaintSweepHorizontally(false)
109     , m_willSendRequestReturnsNull(false)
110     , m_policyDelegateEnabled(false)
111     , m_policyDelegatePermissive(false)
112     , m_globalFlag(false)
113     , m_customFullScreenBehavior(false)
114 {
115     platformInitialize();
116 }
117
118 LayoutTestController::~LayoutTestController()
119 {
120 }
121
122 JSClassRef LayoutTestController::wrapperClass()
123 {
124     return JSLayoutTestController::layoutTestControllerClass();
125 }
126
127 void LayoutTestController::display()
128 {
129     WKBundlePageRef page = InjectedBundle::shared().page()->page();
130     WKBundlePageForceRepaint(page);
131     WKBundlePageSetTracksRepaints(page, true);
132     WKBundlePageResetTrackedRepaints(page);
133 }
134
135 void LayoutTestController::dumpAsText(bool dumpPixels)
136 {
137     if (m_whatToDump < MainFrameText)
138         m_whatToDump = MainFrameText;
139     m_dumpPixels = dumpPixels;
140 }
141
142 // FIXME: Needs a full implementation see https://bugs.webkit.org/show_bug.cgi?id=42546
143 void LayoutTestController::setCustomPolicyDelegate(bool enabled, bool permissive)
144 {
145     m_policyDelegateEnabled = enabled;
146     m_policyDelegatePermissive = permissive;
147 }
148
149 void LayoutTestController::waitForPolicyDelegate()
150 {
151     setCustomPolicyDelegate(true);
152     waitUntilDone();
153 }
154
155 void LayoutTestController::waitUntilDone()
156 {
157     m_waitToDump = true;
158     if (InjectedBundle::shared().useWaitToDumpWatchdogTimer())
159         initializeWaitToDumpWatchdogTimerIfNeeded();
160 }
161
162 void LayoutTestController::waitToDumpWatchdogTimerFired()
163 {
164     invalidateWaitToDumpWatchdogTimer();
165     const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
166     InjectedBundle::shared().stringBuilder()->append(message);
167     InjectedBundle::shared().stringBuilder()->append("\n");
168     InjectedBundle::shared().done();
169 }
170
171 void LayoutTestController::notifyDone()
172 {
173     if (!InjectedBundle::shared().isTestRunning())
174         return;
175
176     if (m_waitToDump && !InjectedBundle::shared().topLoadingFrame())
177         InjectedBundle::shared().page()->dump();
178
179     m_waitToDump = false;
180 }
181
182 unsigned LayoutTestController::numberOfActiveAnimations() const
183 {
184     // FIXME: Is it OK this works only for the main frame?
185     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
186     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
187     return WKBundleFrameGetNumberOfActiveAnimations(mainFrame);
188 }
189
190 bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
191 {
192     // FIXME: Is it OK this works only for the main frame?
193     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
194     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
195     return WKBundleFramePauseAnimationOnElementWithId(mainFrame, toWK(animationName).get(), toWK(elementId).get(), time);
196 }
197
198 bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId)
199 {
200     // FIXME: Is it OK this works only for the main frame?
201     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
202     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
203     return WKBundleFramePauseTransitionOnElementWithId(mainFrame, toWK(propertyName).get(), toWK(elementId).get(), time);
204 }
205
206 void LayoutTestController::suspendAnimations()
207 {
208     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
209     WKBundleFrameSuspendAnimations(mainFrame);
210 }
211
212 JSRetainPtr<JSStringRef> LayoutTestController::layerTreeAsText() const
213 {
214     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
215     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyLayerTreeAsText(mainFrame));
216     return toJS(text);
217 }
218
219 void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
220 {
221     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
222     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
223
224     WKBundleAddUserScript(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
225         (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
226         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
227 }
228
229 void LayoutTestController::addUserStyleSheet(JSStringRef source, bool allFrames)
230 {
231     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
232     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
233
234     WKBundleAddUserStyleSheet(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
235         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
236 }
237
238 void LayoutTestController::keepWebHistory()
239 {
240     WKBundleSetShouldTrackVisitedLinks(InjectedBundle::shared().bundle(), true);
241 }
242
243 JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSValueRef element)
244 {
245     // FIXME: Is it OK this works only for the main frame?
246     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
247     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
248     if (!JSValueIsObject(context, element))
249         return JSValueMakeUndefined(context);
250     JSValueRef value = WKBundleFrameGetComputedStyleIncludingVisitedInfo(mainFrame, const_cast<JSObjectRef>(element));
251     if (!value)
252         return JSValueMakeUndefined(context);
253     return value;
254 }
255
256 JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef elementId)
257 {
258     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
259     JSObjectRef element = getElementById(mainFrame, elementId);
260     if (!element)
261         return 0;
262     WKRetainPtr<WKStringRef> value(AdoptWK, WKBundleFrameCopyCounterValue(mainFrame, const_cast<JSObjectRef>(element)));
263     return toJS(value);
264 }
265
266 JSRetainPtr<JSStringRef> LayoutTestController::markerTextForListItem(JSValueRef element)
267 {
268     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
269     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
270     if (!element || !JSValueIsObject(context, element))
271         return 0;
272     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyMarkerText(mainFrame, const_cast<JSObjectRef>(element)));
273     if (WKStringIsEmpty(text.get()))
274         return 0;
275     return toJS(text);
276 }
277
278 void LayoutTestController::execCommand(JSStringRef name, JSStringRef argument)
279 {
280     WKBundlePageExecuteEditingCommand(InjectedBundle::shared().page()->page(), toWK(name).get(), toWK(argument).get());
281 }
282
283 bool LayoutTestController::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
284 {
285     WKFindOptions options = 0;
286
287     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
288     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
289     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
290     JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
291     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
292     if (!JSValueIsNumber(context, lengthValue))
293         return false;
294
295     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
296     for (size_t i = 0; i < length; ++i) {
297         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
298         if (!JSValueIsString(context, value))
299             continue;
300
301         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
302
303         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
304             options |= kWKFindOptionsCaseInsensitive;
305         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
306             options |= kWKFindOptionsAtWordStarts;
307         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
308             options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
309         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
310             options |= kWKFindOptionsBackwards;
311         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
312             options |= kWKFindOptionsWrapAround;
313         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
314             // FIXME: No kWKFindOptionsStartInSelection.
315         }
316     }
317
318     return WKBundlePageFindString(InjectedBundle::shared().page()->page(), toWK(target).get(), options);
319 }
320
321 void LayoutTestController::clearAllDatabases()
322 {
323     WKBundleClearAllDatabases(InjectedBundle::shared().bundle());
324 }
325
326 void LayoutTestController::setDatabaseQuota(uint64_t quota)
327 {
328     return WKBundleSetDatabaseQuota(InjectedBundle::shared().bundle(), quota);
329 }
330
331 void LayoutTestController::clearAllApplicationCaches()
332 {
333     WKBundleClearApplicationCache(InjectedBundle::shared().bundle());
334 }
335
336 void LayoutTestController::setAppCacheMaximumSize(uint64_t size)
337 {
338     WKBundleSetAppCacheMaximumSize(InjectedBundle::shared().bundle(), size);
339 }
340
341 bool LayoutTestController::isCommandEnabled(JSStringRef name)
342 {
343     return WKBundlePageIsEditingCommandEnabled(InjectedBundle::shared().page()->page(), toWK(name).get());
344 }
345
346 void LayoutTestController::setCanOpenWindows(bool)
347 {
348     // It's not clear if or why any tests require opening windows be forbidden.
349     // For now, just ignore this setting, and if we find later it's needed we can add it.
350 }
351
352 void LayoutTestController::setXSSAuditorEnabled(bool enabled)
353 {
354     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
355     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), key.get(), enabled);
356 }
357
358 void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled)
359 {
360     WKBundleSetAllowUniversalAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
361 }
362
363 void LayoutTestController::setAllowFileAccessFromFileURLs(bool enabled)
364 {
365     WKBundleSetAllowFileAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
366 }
367
368 void LayoutTestController::setFrameFlatteningEnabled(bool enabled)
369 {
370     WKBundleSetFrameFlatteningEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
371 }
372
373 void LayoutTestController::setGeolocationPermission(bool enabled)
374 {
375     WKBundleSetGeolocationPermission(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
376 }
377
378 void LayoutTestController::setJavaScriptCanAccessClipboard(bool enabled)
379 {
380      WKBundleSetJavaScriptCanAccessClipboard(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
381 }
382
383 void LayoutTestController::setPrivateBrowsingEnabled(bool enabled)
384 {
385      WKBundleSetPrivateBrowsingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
386 }
387
388 void LayoutTestController::setPopupBlockingEnabled(bool enabled)
389 {
390      WKBundleSetPopupBlockingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
391 }
392
393 void LayoutTestController::setAuthorAndUserStylesEnabled(bool enabled)
394 {
395      WKBundleSetAuthorAndUserStylesEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
396 }
397
398 void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
399 {
400     WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
401 }
402
403 void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
404 {
405     WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
406 }
407
408 int LayoutTestController::numberOfPages(double pageWidthInPixels, double pageHeightInPixels)
409 {
410     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
411     return WKBundleNumberOfPages(InjectedBundle::shared().bundle(), mainFrame, pageWidthInPixels, pageHeightInPixels);
412 }
413
414 int LayoutTestController::pageNumberForElementById(JSStringRef id, double pageWidthInPixels, double pageHeightInPixels)
415 {
416     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
417     return WKBundlePageNumberForElementById(InjectedBundle::shared().bundle(), mainFrame, toWK(id).get(), pageWidthInPixels, pageHeightInPixels);
418 }
419
420 JSRetainPtr<JSStringRef> LayoutTestController::pageSizeAndMarginsInPixels(int pageIndex, int width, int height, int marginTop, int marginRight, int marginBottom, int marginLeft)
421 {
422     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
423     return toJS(WKBundlePageSizeAndMarginsInPixels(InjectedBundle::shared().bundle(), mainFrame, pageIndex, width, height, marginTop, marginRight, marginBottom, marginLeft));
424 }
425
426 bool LayoutTestController::isPageBoxVisible(int pageIndex)
427 {
428     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
429     return WKBundleIsPageBoxVisible(InjectedBundle::shared().bundle(), mainFrame, pageIndex);
430 }
431
432 void LayoutTestController::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
433 {
434     if (!element || !JSValueIsObject(context, element))
435         return;
436
437     WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
438     WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
439 }
440
441 unsigned LayoutTestController::windowCount()
442 {
443     return InjectedBundle::shared().pageCount();
444 }
445
446 void LayoutTestController::clearBackForwardList()
447 {
448     WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::shared().page()->page()));
449 }
450
451 // Object Creation
452
453 void LayoutTestController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
454 {
455     setProperty(context, windowObject, "layoutTestController", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
456 }
457
458 void LayoutTestController::showWebInspector()
459 {
460 #if ENABLE(INSPECTOR)
461     WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
462 #endif // ENABLE(INSPECTOR)
463 }
464
465 void LayoutTestController::closeWebInspector()
466 {
467 #if ENABLE(INSPECTOR)
468     WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
469 #endif // ENABLE(INSPECTOR)
470 }
471
472 void LayoutTestController::evaluateInWebInspector(long callID, JSStringRef script)
473 {
474 #if ENABLE(INSPECTOR)
475     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
476     WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()), callID, scriptWK.get());
477 #endif // ENABLE(INSPECTOR)
478 }
479
480 void LayoutTestController::setJavaScriptProfilingEnabled(bool enabled)
481 {
482 #if ENABLE(INSPECTOR)
483     WKBundleInspectorSetJavaScriptProfilingEnabled(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()), enabled);
484 #endif // ENABLE(INSPECTOR)
485 }
486
487 typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
488 static WorldMap& worldMap()
489 {
490     static WorldMap& map = *new WorldMap;
491     return map;
492 }
493
494 unsigned LayoutTestController::worldIDForWorld(WKBundleScriptWorldRef world)
495 {
496     WorldMap::const_iterator end = worldMap().end();
497     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
498         if (it->second == world)
499             return it->first;
500     }
501
502     return 0;
503 }
504
505 void LayoutTestController::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
506 {
507     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
508     // that is created once and cached forever.
509     WKRetainPtr<WKBundleScriptWorldRef> world;
510     if (!worldID)
511         world.adopt(WKBundleScriptWorldCreateWorld());
512     else {
513         WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, 0).iterator->second;
514         if (!worldSlot)
515             worldSlot.adopt(WKBundleScriptWorldCreateWorld());
516         world = worldSlot;
517     }
518
519     WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
520     if (!frame)
521         frame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
522
523     JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
524     JSEvaluateScript(jsContext, script, 0, 0, 0, 0); 
525 }
526
527 void LayoutTestController::setPOSIXLocale(JSStringRef locale)
528 {
529     char localeBuf[32];
530     JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
531     setlocale(LC_ALL, localeBuf);
532 }
533
534 void LayoutTestController::setTextDirection(JSStringRef direction)
535 {
536     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
537     return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
538 }
539     
540 void LayoutTestController::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
541 {
542     InjectedBundle::shared().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
543 }
544
545 void LayoutTestController::setPageVisibility(JSStringRef state)
546 {
547     WKStringRef visibilityStateKey = toWK(state).get();
548     WebCore::PageVisibilityState visibilityState = WebCore::PageVisibilityStateVisible;
549
550     if (WKStringIsEqualToUTF8CString(visibilityStateKey, "hidden"))
551         visibilityState = WebCore::PageVisibilityStateHidden;
552     else if (WKStringIsEqualToUTF8CString(visibilityStateKey, "prerender"))
553         visibilityState = WebCore::PageVisibilityStatePrerender;
554
555     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), visibilityState, /* isInitialState */ false);
556 }
557
558 void LayoutTestController::resetPageVisibility()
559 {
560     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), WebCore::PageVisibilityStateVisible, /* isInitialState */ true);
561 }
562
563 void LayoutTestController::dumpConfigurationForViewport(int deviceDPI, int deviceWidth, int deviceHeight, int availableWidth, int availableHeight)
564 {
565     InjectedBundle::shared().stringBuilder()->append(toWTFString(adoptWK(WKBundlePageViewportConfigurationAsText(InjectedBundle::shared().page()->page(), deviceDPI, deviceWidth, deviceHeight, availableWidth, availableHeight))));
566 }
567
568 typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
569 static CallbackMap& callbackMap()
570 {
571     static CallbackMap& map = *new CallbackMap;
572     return map;
573 }
574
575 enum {
576     AddChromeInputFieldCallbackID = 1,
577     RemoveChromeInputFieldCallbackID,
578     FocusWebViewCallbackID,
579     SetBackingScaleFactorCallbackID
580 };
581
582 static void cacheLayoutTestControllerCallback(unsigned index, JSValueRef callback)
583 {
584     if (!callback)
585         return;
586
587     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
588     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
589     JSValueProtect(context, callback);
590     callbackMap().add(index, callback);
591 }
592
593 static void callLayoutTestControllerCallback(unsigned index)
594 {
595     if (!callbackMap().contains(index))
596         return;
597     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
598     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
599     JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
600     JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), 0, 0, 0);
601     JSValueUnprotect(context, callback);
602 }
603
604 void LayoutTestController::addChromeInputField(JSValueRef callback)
605 {
606     cacheLayoutTestControllerCallback(AddChromeInputFieldCallbackID, callback);
607     InjectedBundle::shared().postAddChromeInputField();
608 }
609
610 void LayoutTestController::removeChromeInputField(JSValueRef callback)
611 {
612     cacheLayoutTestControllerCallback(RemoveChromeInputFieldCallbackID, callback);
613     InjectedBundle::shared().postRemoveChromeInputField();
614 }
615
616 void LayoutTestController::focusWebView(JSValueRef callback)
617 {
618     cacheLayoutTestControllerCallback(FocusWebViewCallbackID, callback);
619     InjectedBundle::shared().postFocusWebView();
620 }
621
622 void LayoutTestController::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
623 {
624     cacheLayoutTestControllerCallback(SetBackingScaleFactorCallbackID, callback);
625     InjectedBundle::shared().postSetBackingScaleFactor(backingScaleFactor);
626 }
627
628 void LayoutTestController::setWindowIsKey(bool isKey)
629 {
630     InjectedBundle::shared().postSetWindowIsKey(isKey);
631 }
632
633 void LayoutTestController::callAddChromeInputFieldCallback()
634 {
635     callLayoutTestControllerCallback(AddChromeInputFieldCallbackID);
636 }
637
638 void LayoutTestController::callRemoveChromeInputFieldCallback()
639 {
640     callLayoutTestControllerCallback(RemoveChromeInputFieldCallbackID);
641 }
642
643 void LayoutTestController::callFocusWebViewCallback()
644 {
645     callLayoutTestControllerCallback(FocusWebViewCallbackID);
646 }
647
648 void LayoutTestController::callSetBackingScaleFactorCallback()
649 {
650     callLayoutTestControllerCallback(SetBackingScaleFactorCallbackID);
651 }
652
653 void LayoutTestController::overridePreference(JSStringRef preference, bool value)
654 {
655     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), toWK(preference).get(), value);
656 }
657
658 } // namespace WTR