tizen beta release
[profile/ivi/webkit-efl.git] / Source / WebCore / bindings / js / ScriptController.cpp
1 /*
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.
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22 #include "ScriptController.h"
23
24 #include "Event.h"
25 #include "EventNames.h"
26 #include "Frame.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"
35 #include "Page.h"
36 #include "PageGroup.h"
37 #include "ScriptSourceCode.h"
38 #include "ScriptValue.h"
39 #include "ScriptableDocumentParser.h"
40 #include "Settings.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>
52
53 using namespace JSC;
54 using namespace std;
55
56 namespace WebCore {
57
58 void ScriptController::initializeThreading()
59 {
60     JSC::initializeThreading();
61     WTF::initializeMainThread();
62 }
63
64 ScriptController::ScriptController(Frame* frame)
65     : m_frame(frame)
66     , m_sourceURL(0)
67     , m_inExecuteScript(false)
68     , m_paused(false)
69 #if ENABLE(NETSCAPE_PLUGIN_API)
70     , m_windowScriptNPObject(0)
71 #endif
72 #if PLATFORM(MAC)
73     , m_windowScriptObject(0)
74 #endif
75 {
76 #if PLATFORM(MAC) && ENABLE(JAVA_BRIDGE)
77     static bool initializedJavaJSBindings;
78     if (!initializedJavaJSBindings) {
79         initializedJavaJSBindings = true;
80         initJavaJSBindings();
81     }
82 #endif
83 }
84
85 ScriptController::~ScriptController()
86 {
87     disconnectPlatformScriptObjects();
88
89     if (m_cacheableBindingRootObject) {
90         m_cacheableBindingRootObject->invalidate();
91         m_cacheableBindingRootObject = 0;
92     }
93
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();
99     }
100 }
101
102 void ScriptController::destroyWindowShell(DOMWrapperWorld* world)
103 {
104     ASSERT(m_windowShells.contains(world));
105     m_windowShells.remove(world);
106     world->didDestroyWindowShell(this);
107 }
108
109 JSDOMWindowShell* ScriptController::createWindowShell(DOMWrapperWorld* world)
110 {
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();
118 }
119
120 ScriptValue ScriptController::evaluateInWorld(const ScriptSourceCode& sourceCode, DOMWrapperWorld* world)
121 {
122     const SourceCode& jsSourceCode = sourceCode.jsSourceCode();
123     String sourceURL = ustringToString(jsSourceCode.provider()->url());
124
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.
127
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;
136
137     JSLock lock(SilenceAssertionsOnly);
138
139     RefPtr<Frame> protect = m_frame;
140
141     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, sourceURL, sourceCode.startLine());
142
143     JSValue evaluationException;
144
145     exec->globalData().timeoutChecker.start();
146     JSValue returnValue = JSMainThreadExecState::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), jsSourceCode, shell, &evaluationException);
147     exec->globalData().timeoutChecker.stop();
148
149     InspectorInstrumentation::didEvaluateScript(cookie);
150
151     if (evaluationException) {
152         reportException(exec, evaluationException);
153         m_sourceURL = savedSourceURL;
154         return ScriptValue();
155     }
156
157     m_sourceURL = savedSourceURL;
158     return ScriptValue(exec->globalData(), returnValue);
159 }
160
161 ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode) 
162 {
163     return evaluateInWorld(sourceCode, mainThreadNormalWorld());
164 }
165
166 PassRefPtr<DOMWrapperWorld> ScriptController::createWorld()
167 {
168     return DOMWrapperWorld::create(JSDOMWindow::commonJSGlobalData());
169 }
170
171 void ScriptController::getAllWorlds(Vector<DOMWrapperWorld*>& worlds)
172 {
173     static_cast<WebCoreJSClientData*>(JSDOMWindow::commonJSGlobalData()->clientData)->getAllWorlds(worlds);
174 }
175
176 void ScriptController::clearWindowShell(bool goingIntoPageCache)
177 {
178     if (m_windowShells.isEmpty())
179         return;
180
181     JSLock lock(SilenceAssertionsOnly);
182
183     for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) {
184         JSDOMWindowShell* windowShell = iter->second.get();
185
186         // Clear the debugger from the current window before setting the new window.
187         attachDebugger(windowShell, 0);
188
189         windowShell->window()->willRemoveFromWindowShell();
190         windowShell->setWindow(m_frame->domWindow());
191
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());
196
197         if (Page* page = m_frame->page()) {
198             attachDebugger(windowShell, page->debugger());
199             windowShell->window()->setProfileGroup(page->group().identifier());
200         }
201     }
202
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();
207 }
208
209 JSDOMWindowShell* ScriptController::initScript(DOMWrapperWorld* world)
210 {
211     ASSERT(!m_windowShells.contains(world));
212
213     JSLock lock(SilenceAssertionsOnly);
214
215     JSDOMWindowShell* windowShell = createWindowShell(world);
216
217     windowShell->window()->updateDocument();
218
219     if (Page* page = m_frame->page()) {
220         attachDebugger(windowShell, page->debugger());
221         windowShell->window()->setProfileGroup(page->group().identifier());
222     }
223
224     m_frame->loader()->dispatchDidClearWindowObjectInWorld(world);
225
226     return windowShell;
227 }
228
229 TextPosition ScriptController::eventHandlerPosition() const
230 {
231     ScriptableDocumentParser* parser = m_frame->document()->scriptableDocumentParser();
232     if (parser)
233         return parser->textPosition();
234     return TextPosition::belowRangePosition();
235 }
236
237 void ScriptController::disableEval()
238 {
239     windowShell(mainThreadNormalWorld())->window()->setEvalEnabled(false);
240 }
241
242 bool ScriptController::processingUserGesture()
243 {
244     return UserGestureIndicator::processingUserGesture();
245 }
246
247 bool ScriptController::canAccessFromCurrentOrigin(Frame *frame)
248 {
249     ExecState* exec = JSMainThreadExecState::currentState();
250     if (exec)
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).
254     return true;
255 }
256
257 void ScriptController::attachDebugger(JSC::Debugger* debugger)
258 {
259     for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter)
260         attachDebugger(iter->second.get(), debugger);
261 }
262
263 void ScriptController::attachDebugger(JSDOMWindowShell* shell, JSC::Debugger* debugger)
264 {
265     if (!shell)
266         return;
267
268     JSDOMWindow* globalObject = shell->window();
269     if (debugger)
270         debugger->attach(globalObject);
271     else if (JSC::Debugger* currentDebugger = globalObject->debugger())
272         currentDebugger->detach(globalObject);
273 }
274
275 void ScriptController::updateDocument()
276 {
277     if (!m_frame->document())
278         return;
279
280     JSLock lock(SilenceAssertionsOnly);
281     for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter)
282         iter->second->window()->updateDocument();
283 }
284
285 void ScriptController::updateSecurityOrigin()
286 {
287     // Our bindings do not do anything in this case.
288 }
289
290 Bindings::RootObject* ScriptController::cacheableBindingRootObject()
291 {
292     if (!canExecuteScripts(NotAboutToExecuteScript))
293         return 0;
294
295     if (!m_cacheableBindingRootObject) {
296         JSLock lock(SilenceAssertionsOnly);
297         m_cacheableBindingRootObject = Bindings::RootObject::create(0, globalObject(pluginWorld()));
298     }
299     return m_cacheableBindingRootObject.get();
300 }
301
302 Bindings::RootObject* ScriptController::bindingRootObject()
303 {
304     if (!canExecuteScripts(NotAboutToExecuteScript))
305         return 0;
306
307     if (!m_bindingRootObject) {
308         JSLock lock(SilenceAssertionsOnly);
309         m_bindingRootObject = Bindings::RootObject::create(0, globalObject(pluginWorld()));
310     }
311     return m_bindingRootObject.get();
312 }
313
314 PassRefPtr<Bindings::RootObject> ScriptController::createRootObject(void* nativeHandle)
315 {
316     RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
317     if (it != m_rootObjects.end())
318         return it->second;
319
320     RefPtr<Bindings::RootObject> rootObject = Bindings::RootObject::create(nativeHandle, globalObject(pluginWorld()));
321
322     m_rootObjects.set(nativeHandle, rootObject);
323     return rootObject.release();
324 }
325
326 #if ENABLE(INSPECTOR)
327 void ScriptController::setCaptureCallStackForUncaughtExceptions(bool)
328 {
329 }
330 #endif
331
332 #if ENABLE(NETSCAPE_PLUGIN_API)
333
334 NPObject* ScriptController::windowScriptNPObject()
335 {
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();
342             ASSERT(win);
343             Bindings::RootObject* root = bindingRootObject();
344             m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root);
345         } else {
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();
349         }
350     }
351
352     return m_windowScriptNPObject;
353 }
354
355 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
356 {
357     JSObject* object = jsObjectForPluginElement(plugin);
358     if (!object)
359         return _NPN_CreateNoScriptObject();
360
361     // Wrap the JSObject in an NPObject
362     return _NPN_CreateScriptObject(0, object, bindingRootObject());
363 }
364
365 #endif
366
367 JSObject* ScriptController::jsObjectForPluginElement(HTMLPlugInElement* plugin)
368 {
369     // Can't create JSObjects when JavaScript is disabled
370     if (!canExecuteScripts(NotAboutToExecuteScript))
371         return 0;
372
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())
379         return 0;
380     
381     return jsElementValue.getObject();
382 }
383
384 #if !PLATFORM(MAC)
385
386 void ScriptController::updatePlatformScriptObjects()
387 {
388 }
389
390 void ScriptController::disconnectPlatformScriptObjects()
391 {
392 }
393
394 #endif
395
396 void ScriptController::cleanupScriptObjectsForPlugin(void* nativeHandle)
397 {
398     RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
399
400     if (it == m_rootObjects.end())
401         return;
402
403     it->second->invalidate();
404     m_rootObjects.remove(it);
405 }
406
407 void ScriptController::clearScriptObjects()
408 {
409     JSLock lock(SilenceAssertionsOnly);
410
411     RootObjectMap::const_iterator end = m_rootObjects.end();
412     for (RootObjectMap::const_iterator it = m_rootObjects.begin(); it != end; ++it)
413         it->second->invalidate();
414
415     m_rootObjects.clear();
416
417     if (m_bindingRootObject) {
418         m_bindingRootObject->invalidate();
419         m_bindingRootObject = 0;
420     }
421
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;
429     }
430 #endif
431 }
432
433 ScriptValue ScriptController::executeScriptInWorld(DOMWrapperWorld* world, const String& script, bool forceUserGesture)
434 {
435     UserGestureIndicator gestureIndicator(forceUserGesture ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture);
436     ScriptSourceCode sourceCode(script, m_frame->document()->url());
437
438     if (!canExecuteScripts(AboutToExecuteScript) || isPaused())
439         return ScriptValue();
440
441     bool wasInExecuteScript = m_inExecuteScript;
442     m_inExecuteScript = true;
443
444     ScriptValue result = evaluateInWorld(sourceCode, world);
445
446     if (!wasInExecuteScript) {
447         m_inExecuteScript = false;
448         Document::updateStyleForAllDocuments();
449     }
450
451     return result;
452 }
453
454 } // namespace WebCore