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 "GCController.h"
28 #include "HTMLPlugInElement.h"
29 #include "JSDocument.h"
30 #include "JSLazyEventListener.h"
31 #include "NP_jsobject.h"
33 #include "PageGroup.h"
34 #include "ScriptSourceCode.h"
35 #include "ScriptValue.h"
37 #include "npruntime_impl.h"
38 #include "runtime_root.h"
39 #include <debugger/Debugger.h>
40 #include <runtime/JSLock.h>
46 ScriptController::ScriptController(Frame* frame)
50 , m_processingTimerCallback(false)
52 #if ENABLE(NETSCAPE_PLUGIN_API)
53 , m_windowScriptNPObject(0)
56 , m_windowScriptObject(0)
59 #if PLATFORM(MAC) && ENABLE(MAC_JAVA_BRIDGE)
60 static bool initializedJavaJSBindings;
61 if (!initializedJavaJSBindings) {
62 initializedJavaJSBindings = true;
68 ScriptController::~ScriptController()
73 // It's likely that releasing the global object has created a lot of garbage.
74 gcController().garbageCollectSoon();
77 disconnectPlatformScriptObjects();
80 ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode)
82 // evaluate code. Returns the JS return value or 0
83 // if there was none, an error occured or the type couldn't be converted.
85 const SourceCode& jsSourceCode = sourceCode.jsSourceCode();
88 // inlineCode is true for <a href="javascript:doSomething()">
89 // and false for <script>doSomething()</script>. Check if it has the
90 // expected value in all cases.
91 // See smart window.open policy for where this is used.
92 ExecState* exec = m_windowShell->window()->globalExec();
93 const String* savedSourceURL = m_sourceURL;
94 String sourceURL = jsSourceCode.provider()->url();
95 m_sourceURL = &sourceURL;
99 // Evaluating the JavaScript could cause the frame to be deallocated
100 // so we start the keep alive timer here.
101 m_frame->keepAlive();
103 m_windowShell->window()->globalData()->timeoutChecker.start();
104 Completion comp = JSC::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), jsSourceCode, m_windowShell);
105 m_windowShell->window()->globalData()->timeoutChecker.stop();
107 if (comp.complType() == Normal || comp.complType() == ReturnValue) {
108 m_sourceURL = savedSourceURL;
112 if (comp.complType() == Throw || comp.complType() == Interrupted)
113 reportException(exec, comp.value());
115 m_sourceURL = savedSourceURL;
119 void ScriptController::clearWindowShell()
125 m_windowShell->window()->clear();
126 m_liveFormerWindows.add(m_windowShell->window());
127 m_windowShell->setWindow(m_frame->domWindow());
128 if (Page* page = m_frame->page()) {
129 attachDebugger(page->debugger());
130 m_windowShell->window()->setProfileGroup(page->group().identifier());
133 // There is likely to be a lot of garbage now.
134 gcController().garbageCollectSoon();
137 PassRefPtr<EventListener> ScriptController::createInlineEventListener(const String& functionName, const String& code, Node* node)
139 initScriptIfNeeded();
141 return JSLazyEventListener::create(JSLazyEventListener::HTMLLazyEventListener, functionName, code, m_windowShell->window(), node, m_handlerLineno);
146 PassRefPtr<EventListener> ScriptController::createSVGEventHandler(const String& functionName, const String& code, Node* node)
148 initScriptIfNeeded();
150 return JSLazyEventListener::create(JSLazyEventListener::SVGLazyEventListener, functionName, code, m_windowShell->window(), node, m_handlerLineno);
155 void ScriptController::initScript()
162 m_windowShell = new JSDOMWindowShell(m_frame->domWindow());
165 if (Page* page = m_frame->page()) {
166 attachDebugger(page->debugger());
167 m_windowShell->window()->setProfileGroup(page->group().identifier());
170 m_frame->loader()->dispatchWindowObjectAvailable();
173 bool ScriptController::processingUserGesture() const
175 return processingUserGestureEvent() || isJavaScriptAnchorNavigation();
178 bool ScriptController::processingUserGestureEvent() const
183 if (Event* event = m_windowShell->window()->currentEvent()) {
184 const AtomicString& type = event->type();
186 type == eventNames().clickEvent || type == eventNames().mousedownEvent ||
187 type == eventNames().mouseupEvent || type == eventNames().dblclickEvent ||
189 type == eventNames().keydownEvent || type == eventNames().keypressEvent ||
190 type == eventNames().keyupEvent ||
191 // other accepted events
192 type == eventNames().selectEvent || type == eventNames().changeEvent ||
193 type == eventNames().focusEvent || type == eventNames().blurEvent ||
194 type == eventNames().submitEvent)
201 // FIXME: This seems like an insufficient check to verify a click on a javascript: anchor.
202 bool ScriptController::isJavaScriptAnchorNavigation() const
204 // This is the <a href="javascript:window.open('...')> case -> we let it through
205 if (m_sourceURL && m_sourceURL->isNull() && !m_processingTimerCallback)
208 // This is the <script>window.open(...)</script> case or a timer callback -> block it
212 bool ScriptController::anyPageIsProcessingUserGesture() const
214 Page* page = m_frame->page();
218 const HashSet<Page*>& pages = page->group().pages();
219 HashSet<Page*>::const_iterator end = pages.end();
220 for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) {
221 for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
222 if (frame->script()->processingUserGesture())
230 bool ScriptController::isEnabled()
232 Settings* settings = m_frame->settings();
233 return (settings && settings->isJavaScriptEnabled());
236 void ScriptController::attachDebugger(JSC::Debugger* debugger)
242 debugger->attach(m_windowShell->window());
243 else if (JSC::Debugger* currentDebugger = m_windowShell->window()->debugger())
244 currentDebugger->detach(m_windowShell->window());
247 void ScriptController::updateDocument()
249 if (!m_frame->document())
254 m_windowShell->window()->updateDocument();
255 HashSet<JSDOMWindow*>::iterator end = m_liveFormerWindows.end();
256 for (HashSet<JSDOMWindow*>::iterator it = m_liveFormerWindows.begin(); it != end; ++it)
257 (*it)->updateDocument();
260 void ScriptController::updateSecurityOrigin()
262 // Our bindings do not do anything in this case.
265 Bindings::RootObject* ScriptController::bindingRootObject()
270 if (!m_bindingRootObject) {
272 m_bindingRootObject = Bindings::RootObject::create(0, globalObject());
274 return m_bindingRootObject.get();
277 PassRefPtr<Bindings::RootObject> ScriptController::createRootObject(void* nativeHandle)
279 RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
280 if (it != m_rootObjects.end())
283 RefPtr<Bindings::RootObject> rootObject = Bindings::RootObject::create(nativeHandle, globalObject());
285 m_rootObjects.set(nativeHandle, rootObject);
286 return rootObject.release();
289 #if ENABLE(NETSCAPE_PLUGIN_API)
291 NPObject* ScriptController::windowScriptNPObject()
293 if (!m_windowScriptNPObject) {
295 // JavaScript is enabled, so there is a JavaScript window object.
296 // Return an NPObject bound to the window object.
297 JSC::JSLock lock(false);
298 JSObject* win = windowShell()->window();
300 Bindings::RootObject* root = bindingRootObject();
301 m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root);
303 // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
304 // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
305 m_windowScriptNPObject = _NPN_CreateNoScriptObject();
309 return m_windowScriptNPObject;
312 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
314 JSObject* object = jsObjectForPluginElement(plugin);
316 return _NPN_CreateNoScriptObject();
318 // Wrap the JSObject in an NPObject
319 return _NPN_CreateScriptObject(0, object, bindingRootObject());
324 JSObject* ScriptController::jsObjectForPluginElement(HTMLPlugInElement* plugin)
326 // Can't create JSObjects when JavaScript is disabled
330 // Create a JSObject bound to this element
332 ExecState* exec = globalObject()->globalExec();
333 JSValuePtr jsElementValue = toJS(exec, plugin);
334 if (!jsElementValue || !jsElementValue.isObject())
337 return jsElementValue.getObject();
342 void ScriptController::updatePlatformScriptObjects()
346 void ScriptController::disconnectPlatformScriptObjects()
352 void ScriptController::cleanupScriptObjectsForPlugin(void* nativeHandle)
354 RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
356 if (it == m_rootObjects.end())
359 it->second->invalidate();
360 m_rootObjects.remove(it);
363 void ScriptController::clearScriptObjects()
367 RootObjectMap::const_iterator end = m_rootObjects.end();
368 for (RootObjectMap::const_iterator it = m_rootObjects.begin(); it != end; ++it)
369 it->second->invalidate();
371 m_rootObjects.clear();
373 if (m_bindingRootObject) {
374 m_bindingRootObject->invalidate();
375 m_bindingRootObject = 0;
378 #if ENABLE(NETSCAPE_PLUGIN_API)
379 if (m_windowScriptNPObject) {
380 // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
381 // script object properly.
382 // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
383 _NPN_DeallocateObject(m_windowScriptNPObject);
384 m_windowScriptNPObject = 0;
389 } // namespace WebCore