2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "ScriptController.h"
25 #include "EventNames.h"
27 #include "FrameLoaderClient.h"
28 #include "GCController.h"
29 #include "HTMLPlugInElement.h"
30 #include "InspectorInstrumentation.h"
31 #include "JSDOMWindow.h"
32 #include "JSDocument.h"
33 #include "JSMainThreadExecState.h"
34 #include "NP_jsobject.h"
36 #include "PageGroup.h"
37 #include "ScriptSourceCode.h"
38 #include "ScriptValue.h"
39 #include "ScriptableDocumentParser.h"
41 #include "StorageNamespace.h"
42 #include "UserGestureIndicator.h"
43 #include "WebCoreJSClientData.h"
44 #include "npruntime_impl.h"
45 #include "runtime_root.h"
46 #include <debugger/Debugger.h>
47 #include <heap/StrongInlines.h>
48 #include <runtime/InitializeThreading.h>
49 #include <runtime/JSLock.h>
50 #include <wtf/text/TextPosition.h>
51 #include <wtf/Threading.h>
58 void ScriptController::initializeThreading()
60 JSC::initializeThreading();
61 WTF::initializeMainThread();
64 ScriptController::ScriptController(Frame* frame)
67 , m_inExecuteScript(false)
69 #if ENABLE(NETSCAPE_PLUGIN_API)
70 , m_windowScriptNPObject(0)
73 , m_windowScriptObject(0)
76 #if PLATFORM(MAC) && ENABLE(JAVA_BRIDGE)
77 static bool initializedJavaJSBindings;
78 if (!initializedJavaJSBindings) {
79 initializedJavaJSBindings = true;
85 ScriptController::~ScriptController()
87 disconnectPlatformScriptObjects();
89 if (m_cacheableBindingRootObject) {
90 m_cacheableBindingRootObject->invalidate();
91 m_cacheableBindingRootObject = 0;
94 // It's likely that destroying m_windowShells will create a lot of garbage.
95 if (!m_windowShells.isEmpty()) {
96 while (!m_windowShells.isEmpty())
97 destroyWindowShell(m_windowShells.begin()->first.get());
98 gcController().garbageCollectSoon();
102 void ScriptController::destroyWindowShell(DOMWrapperWorld* world)
104 ASSERT(m_windowShells.contains(world));
105 m_windowShells.remove(world);
106 world->didDestroyWindowShell(this);
109 JSDOMWindowShell* ScriptController::createWindowShell(DOMWrapperWorld* world)
111 ASSERT(!m_windowShells.contains(world));
112 Structure* structure = JSDOMWindowShell::createStructure(*world->globalData(), jsNull());
113 Strong<JSDOMWindowShell> windowShell(*world->globalData(), JSDOMWindowShell::create(m_frame->domWindow(), structure, world));
114 Strong<JSDOMWindowShell> windowShell2(windowShell);
115 m_windowShells.add(world, windowShell);
116 world->didCreateWindowShell(this);
117 return windowShell.get();
120 ScriptValue ScriptController::evaluateInWorld(const ScriptSourceCode& sourceCode, DOMWrapperWorld* world)
122 const SourceCode& jsSourceCode = sourceCode.jsSourceCode();
123 String sourceURL = ustringToString(jsSourceCode.provider()->url());
125 // evaluate code. Returns the JS return value or 0
126 // if there was none, an error occurred or the type couldn't be converted.
128 // inlineCode is true for <a href="javascript:doSomething()">
129 // and false for <script>doSomething()</script>. Check if it has the
130 // expected value in all cases.
131 // See smart window.open policy for where this is used.
132 JSDOMWindowShell* shell = windowShell(world);
133 ExecState* exec = shell->window()->globalExec();
134 const String* savedSourceURL = m_sourceURL;
135 m_sourceURL = &sourceURL;
137 JSLock lock(SilenceAssertionsOnly);
139 RefPtr<Frame> protect = m_frame;
141 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, sourceURL, sourceCode.startLine());
143 JSValue evaluationException;
145 exec->globalData().timeoutChecker.start();
146 JSValue returnValue = JSMainThreadExecState::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), jsSourceCode, shell, &evaluationException);
147 exec->globalData().timeoutChecker.stop();
149 InspectorInstrumentation::didEvaluateScript(cookie);
151 if (evaluationException) {
152 reportException(exec, evaluationException);
153 m_sourceURL = savedSourceURL;
154 return ScriptValue();
157 m_sourceURL = savedSourceURL;
158 return ScriptValue(exec->globalData(), returnValue);
161 ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode)
163 return evaluateInWorld(sourceCode, mainThreadNormalWorld());
166 PassRefPtr<DOMWrapperWorld> ScriptController::createWorld()
168 return DOMWrapperWorld::create(JSDOMWindow::commonJSGlobalData());
171 void ScriptController::getAllWorlds(Vector<DOMWrapperWorld*>& worlds)
173 static_cast<WebCoreJSClientData*>(JSDOMWindow::commonJSGlobalData()->clientData)->getAllWorlds(worlds);
176 void ScriptController::clearWindowShell(bool goingIntoPageCache)
178 if (m_windowShells.isEmpty())
181 JSLock lock(SilenceAssertionsOnly);
183 for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) {
184 JSDOMWindowShell* windowShell = iter->second.get();
186 // Clear the debugger from the current window before setting the new window.
187 attachDebugger(windowShell, 0);
189 windowShell->window()->willRemoveFromWindowShell();
190 windowShell->setWindow(m_frame->domWindow());
192 // An m_cacheableBindingRootObject persists between page navigations
193 // so needs to know about the new JSDOMWindow.
194 if (m_cacheableBindingRootObject)
195 m_cacheableBindingRootObject->updateGlobalObject(windowShell->window());
197 if (Page* page = m_frame->page()) {
198 attachDebugger(windowShell, page->debugger());
199 windowShell->window()->setProfileGroup(page->group().identifier());
203 // It's likely that resetting our windows created a lot of garbage, unless
204 // it went in a back/forward cache.
205 if (!goingIntoPageCache)
206 gcController().garbageCollectSoon();
209 JSDOMWindowShell* ScriptController::initScript(DOMWrapperWorld* world)
211 ASSERT(!m_windowShells.contains(world));
213 JSLock lock(SilenceAssertionsOnly);
215 JSDOMWindowShell* windowShell = createWindowShell(world);
217 windowShell->window()->updateDocument();
219 if (Page* page = m_frame->page()) {
220 attachDebugger(windowShell, page->debugger());
221 windowShell->window()->setProfileGroup(page->group().identifier());
224 m_frame->loader()->dispatchDidClearWindowObjectInWorld(world);
229 TextPosition ScriptController::eventHandlerPosition() const
231 ScriptableDocumentParser* parser = m_frame->document()->scriptableDocumentParser();
233 return parser->textPosition();
234 return TextPosition::belowRangePosition();
237 void ScriptController::disableEval()
239 windowShell(mainThreadNormalWorld())->window()->setEvalEnabled(false);
242 bool ScriptController::processingUserGesture()
244 return UserGestureIndicator::processingUserGesture();
247 bool ScriptController::canAccessFromCurrentOrigin(Frame *frame)
249 ExecState* exec = JSMainThreadExecState::currentState();
251 return allowsAccessFromFrame(exec, frame);
252 // If the current state is 0 we're in a call path where the DOM security
253 // check doesn't apply (eg. parser).
257 void ScriptController::attachDebugger(JSC::Debugger* debugger)
259 for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter)
260 attachDebugger(iter->second.get(), debugger);
263 void ScriptController::attachDebugger(JSDOMWindowShell* shell, JSC::Debugger* debugger)
268 JSDOMWindow* globalObject = shell->window();
270 debugger->attach(globalObject);
271 else if (JSC::Debugger* currentDebugger = globalObject->debugger())
272 currentDebugger->detach(globalObject);
275 void ScriptController::updateDocument()
277 if (!m_frame->document())
280 JSLock lock(SilenceAssertionsOnly);
281 for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter)
282 iter->second->window()->updateDocument();
285 void ScriptController::updateSecurityOrigin()
287 // Our bindings do not do anything in this case.
290 Bindings::RootObject* ScriptController::cacheableBindingRootObject()
292 if (!canExecuteScripts(NotAboutToExecuteScript))
295 if (!m_cacheableBindingRootObject) {
296 JSLock lock(SilenceAssertionsOnly);
297 m_cacheableBindingRootObject = Bindings::RootObject::create(0, globalObject(pluginWorld()));
299 return m_cacheableBindingRootObject.get();
302 Bindings::RootObject* ScriptController::bindingRootObject()
304 if (!canExecuteScripts(NotAboutToExecuteScript))
307 if (!m_bindingRootObject) {
308 JSLock lock(SilenceAssertionsOnly);
309 m_bindingRootObject = Bindings::RootObject::create(0, globalObject(pluginWorld()));
311 return m_bindingRootObject.get();
314 PassRefPtr<Bindings::RootObject> ScriptController::createRootObject(void* nativeHandle)
316 RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
317 if (it != m_rootObjects.end())
320 RefPtr<Bindings::RootObject> rootObject = Bindings::RootObject::create(nativeHandle, globalObject(pluginWorld()));
322 m_rootObjects.set(nativeHandle, rootObject);
323 return rootObject.release();
326 #if ENABLE(INSPECTOR)
327 void ScriptController::setCaptureCallStackForUncaughtExceptions(bool)
332 #if ENABLE(NETSCAPE_PLUGIN_API)
334 NPObject* ScriptController::windowScriptNPObject()
336 if (!m_windowScriptNPObject) {
337 if (canExecuteScripts(NotAboutToExecuteScript)) {
338 // JavaScript is enabled, so there is a JavaScript window object.
339 // Return an NPObject bound to the window object.
340 JSC::JSLock lock(SilenceAssertionsOnly);
341 JSObject* win = windowShell(pluginWorld())->window();
343 Bindings::RootObject* root = bindingRootObject();
344 m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root);
346 // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
347 // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
348 m_windowScriptNPObject = _NPN_CreateNoScriptObject();
352 return m_windowScriptNPObject;
355 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
357 JSObject* object = jsObjectForPluginElement(plugin);
359 return _NPN_CreateNoScriptObject();
361 // Wrap the JSObject in an NPObject
362 return _NPN_CreateScriptObject(0, object, bindingRootObject());
367 JSObject* ScriptController::jsObjectForPluginElement(HTMLPlugInElement* plugin)
369 // Can't create JSObjects when JavaScript is disabled
370 if (!canExecuteScripts(NotAboutToExecuteScript))
373 // Create a JSObject bound to this element
374 JSLock lock(SilenceAssertionsOnly);
375 JSDOMWindow* globalObj = globalObject(pluginWorld());
376 // FIXME: is normal okay? - used for NP plugins?
377 JSValue jsElementValue = toJS(globalObj->globalExec(), globalObj, plugin);
378 if (!jsElementValue || !jsElementValue.isObject())
381 return jsElementValue.getObject();
386 void ScriptController::updatePlatformScriptObjects()
390 void ScriptController::disconnectPlatformScriptObjects()
396 void ScriptController::cleanupScriptObjectsForPlugin(void* nativeHandle)
398 RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
400 if (it == m_rootObjects.end())
403 it->second->invalidate();
404 m_rootObjects.remove(it);
407 void ScriptController::clearScriptObjects()
409 JSLock lock(SilenceAssertionsOnly);
411 RootObjectMap::const_iterator end = m_rootObjects.end();
412 for (RootObjectMap::const_iterator it = m_rootObjects.begin(); it != end; ++it)
413 it->second->invalidate();
415 m_rootObjects.clear();
417 if (m_bindingRootObject) {
418 m_bindingRootObject->invalidate();
419 m_bindingRootObject = 0;
422 #if ENABLE(NETSCAPE_PLUGIN_API)
423 if (m_windowScriptNPObject) {
424 // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
425 // script object properly.
426 // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
427 _NPN_DeallocateObject(m_windowScriptNPObject);
428 m_windowScriptNPObject = 0;
433 ScriptValue ScriptController::executeScriptInWorld(DOMWrapperWorld* world, const String& script, bool forceUserGesture)
435 UserGestureIndicator gestureIndicator(forceUserGesture ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture);
436 ScriptSourceCode sourceCode(script, m_frame->document()->url());
438 if (!canExecuteScripts(AboutToExecuteScript) || isPaused())
439 return ScriptValue();
441 bool wasInExecuteScript = m_inExecuteScript;
442 m_inExecuteScript = true;
444 ScriptValue result = evaluateInWorld(sourceCode, world);
446 if (!wasInExecuteScript) {
447 m_inExecuteScript = false;
448 Document::updateStyleForAllDocuments();
454 } // namespace WebCore