• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GCController.h"
28 #include "HTMLPlugInElement.h"
29 #include "JSDocument.h"
30 #include "NP_jsobject.h"
31 #include "Page.h"
32 #include "PageGroup.h"
33 #include "ScriptSourceCode.h"
34 #include "ScriptValue.h"
35 #include "Settings.h"
36 #include "XSSAuditor.h"
37 #include "npruntime_impl.h"
38 #include "runtime_root.h"
39 #include <debugger/Debugger.h>
40 #include <runtime/JSLock.h>
41 
42 using namespace JSC;
43 
44 namespace WebCore {
45 
ScriptController(Frame * frame)46 ScriptController::ScriptController(Frame* frame)
47     : m_frame(frame)
48     , m_handlerLineNumber(0)
49     , m_sourceURL(0)
50     , m_processingTimerCallback(false)
51     , m_paused(false)
52     , m_allowPopupsFromPlugin(false)
53 #if ENABLE(NETSCAPE_PLUGIN_API)
54     , m_windowScriptNPObject(0)
55 #endif
56 #if PLATFORM(MAC)
57     , m_windowScriptObject(0)
58 #endif
59     , m_XSSAuditor(new XSSAuditor(frame))
60 {
61 #if PLATFORM(MAC) && ENABLE(MAC_JAVA_BRIDGE)
62     static bool initializedJavaJSBindings;
63     if (!initializedJavaJSBindings) {
64         initializedJavaJSBindings = true;
65         initJavaJSBindings();
66     }
67 #endif
68 }
69 
~ScriptController()70 ScriptController::~ScriptController()
71 {
72     if (m_windowShell) {
73         m_windowShell = 0;
74 
75         // It's likely that releasing the global object has created a lot of garbage.
76         gcController().garbageCollectSoon();
77     }
78 
79     disconnectPlatformScriptObjects();
80 }
81 
evaluate(const ScriptSourceCode & sourceCode)82 ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode)
83 {
84     const SourceCode& jsSourceCode = sourceCode.jsSourceCode();
85     String sourceURL = jsSourceCode.provider()->url();
86 
87     if (sourceURL.isNull() && !m_XSSAuditor->canEvaluateJavaScriptURL(sourceCode.source())) {
88         // This JavaScript URL is not safe to be evaluated.
89         return JSValue();
90     }
91 
92     if (!sourceURL.isNull() && !m_XSSAuditor->canEvaluate(sourceCode.source())) {
93         // This script is not safe to be evaluated.
94         return JSValue();
95     }
96 
97     // evaluate code. Returns the JS return value or 0
98     // if there was none, an error occured or the type couldn't be converted.
99 
100     initScriptIfNeeded();
101     // inlineCode is true for <a href="javascript:doSomething()">
102     // and false for <script>doSomething()</script>. Check if it has the
103     // expected value in all cases.
104     // See smart window.open policy for where this is used.
105     ExecState* exec = m_windowShell->window()->globalExec();
106     const String* savedSourceURL = m_sourceURL;
107     m_sourceURL = &sourceURL;
108 
109     JSLock lock(SilenceAssertionsOnly);
110 
111     RefPtr<Frame> protect = m_frame;
112 
113     m_windowShell->window()->globalData()->timeoutChecker.start();
114     Completion comp = JSC::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), jsSourceCode, m_windowShell);
115     m_windowShell->window()->globalData()->timeoutChecker.stop();
116 
117     // Evaluating the JavaScript could cause the frame to be deallocated
118     // so we start the keep alive timer here.
119     m_frame->keepAlive();
120 
121     if (comp.complType() == Normal || comp.complType() == ReturnValue) {
122         m_sourceURL = savedSourceURL;
123         return comp.value();
124     }
125 
126     if (comp.complType() == Throw || comp.complType() == Interrupted)
127         reportException(exec, comp.value());
128 
129     m_sourceURL = savedSourceURL;
130     return JSValue();
131 }
132 
clearWindowShell()133 void ScriptController::clearWindowShell()
134 {
135     if (!m_windowShell)
136         return;
137 
138     JSLock lock(SilenceAssertionsOnly);
139 
140     // Clear the debugger from the current window before setting the new window.
141     attachDebugger(0);
142 
143     m_windowShell->window()->willRemoveFromWindowShell();
144     m_windowShell->setWindow(m_frame->domWindow());
145 
146     if (Page* page = m_frame->page()) {
147         attachDebugger(page->debugger());
148         m_windowShell->window()->setProfileGroup(page->group().identifier());
149     }
150 
151     // There is likely to be a lot of garbage now.
152     gcController().garbageCollectSoon();
153 }
154 
initScript()155 void ScriptController::initScript()
156 {
157     if (m_windowShell)
158         return;
159 
160     JSLock lock(SilenceAssertionsOnly);
161 
162     m_windowShell = new JSDOMWindowShell(m_frame->domWindow());
163     m_windowShell->window()->updateDocument();
164 
165     if (Page* page = m_frame->page()) {
166         attachDebugger(page->debugger());
167         m_windowShell->window()->setProfileGroup(page->group().identifier());
168     }
169 
170     m_frame->loader()->dispatchWindowObjectAvailable();
171 }
172 
processingUserGesture() const173 bool ScriptController::processingUserGesture() const
174 {
175     return m_allowPopupsFromPlugin || processingUserGestureEvent() || isJavaScriptAnchorNavigation();
176 }
177 
processingUserGestureEvent() const178 bool ScriptController::processingUserGestureEvent() const
179 {
180     if (!m_windowShell)
181         return false;
182 
183     if (Event* event = m_windowShell->window()->currentEvent()) {
184         const AtomicString& type = event->type();
185         if ( // mouse events
186             type == eventNames().clickEvent || type == eventNames().mousedownEvent ||
187             type == eventNames().mouseupEvent || type == eventNames().dblclickEvent ||
188             // keyboard events
189             type == eventNames().keydownEvent || type == eventNames().keypressEvent ||
190             type == eventNames().keyupEvent ||
191 #if ENABLE(TOUCH_EVENTS) // Android
192             // touch events
193             type == eventNames().touchstartEvent || type == eventNames().touchmoveEvent ||
194             type == eventNames().touchendEvent || type == eventNames().touchcancelEvent ||
195 #endif
196             // other accepted events
197             type == eventNames().selectEvent || type == eventNames().changeEvent ||
198             type == eventNames().focusEvent || type == eventNames().blurEvent ||
199             type == eventNames().submitEvent)
200             return true;
201     }
202 
203     return false;
204 }
205 
206 // FIXME: This seems like an insufficient check to verify a click on a javascript: anchor.
isJavaScriptAnchorNavigation() const207 bool ScriptController::isJavaScriptAnchorNavigation() const
208 {
209     // This is the <a href="javascript:window.open('...')> case -> we let it through
210     if (m_sourceURL && m_sourceURL->isNull() && !m_processingTimerCallback)
211         return true;
212 
213     // This is the <script>window.open(...)</script> case or a timer callback -> block it
214     return false;
215 }
216 
anyPageIsProcessingUserGesture() const217 bool ScriptController::anyPageIsProcessingUserGesture() const
218 {
219     Page* page = m_frame->page();
220     if (!page)
221         return false;
222 
223     const HashSet<Page*>& pages = page->group().pages();
224     HashSet<Page*>::const_iterator end = pages.end();
225     for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) {
226         for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
227             if (frame->script()->processingUserGesture())
228                 return true;
229         }
230     }
231 
232     return false;
233 }
234 
isEnabled()235 bool ScriptController::isEnabled()
236 {
237     Settings* settings = m_frame->settings();
238     return (settings && settings->isJavaScriptEnabled());
239 }
240 
attachDebugger(JSC::Debugger * debugger)241 void ScriptController::attachDebugger(JSC::Debugger* debugger)
242 {
243     if (!m_windowShell)
244         return;
245 
246     if (debugger)
247         debugger->attach(m_windowShell->window());
248     else if (JSC::Debugger* currentDebugger = m_windowShell->window()->debugger())
249         currentDebugger->detach(m_windowShell->window());
250 }
251 
updateDocument()252 void ScriptController::updateDocument()
253 {
254     if (!m_frame->document())
255         return;
256 
257     JSLock lock(SilenceAssertionsOnly);
258     if (m_windowShell)
259         m_windowShell->window()->updateDocument();
260 }
261 
updateSecurityOrigin()262 void ScriptController::updateSecurityOrigin()
263 {
264     // Our bindings do not do anything in this case.
265 }
266 
bindingRootObject()267 Bindings::RootObject* ScriptController::bindingRootObject()
268 {
269     if (!isEnabled())
270         return 0;
271 
272     if (!m_bindingRootObject) {
273         JSLock lock(SilenceAssertionsOnly);
274         m_bindingRootObject = Bindings::RootObject::create(0, globalObject());
275     }
276     return m_bindingRootObject.get();
277 }
278 
createRootObject(void * nativeHandle)279 PassRefPtr<Bindings::RootObject> ScriptController::createRootObject(void* nativeHandle)
280 {
281     RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
282     if (it != m_rootObjects.end())
283         return it->second;
284 
285     RefPtr<Bindings::RootObject> rootObject = Bindings::RootObject::create(nativeHandle, globalObject());
286 
287     m_rootObjects.set(nativeHandle, rootObject);
288     return rootObject.release();
289 }
290 
291 #if ENABLE(NETSCAPE_PLUGIN_API)
292 
windowScriptNPObject()293 NPObject* ScriptController::windowScriptNPObject()
294 {
295     if (!m_windowScriptNPObject) {
296         if (isEnabled()) {
297             // JavaScript is enabled, so there is a JavaScript window object.
298             // Return an NPObject bound to the window object.
299             JSC::JSLock lock(SilenceAssertionsOnly);
300             JSObject* win = windowShell()->window();
301             ASSERT(win);
302             Bindings::RootObject* root = bindingRootObject();
303             m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root);
304         } else {
305             // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
306             // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
307             m_windowScriptNPObject = _NPN_CreateNoScriptObject();
308         }
309     }
310 
311     return m_windowScriptNPObject;
312 }
313 
createScriptObjectForPluginElement(HTMLPlugInElement * plugin)314 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
315 {
316     JSObject* object = jsObjectForPluginElement(plugin);
317     if (!object)
318         return _NPN_CreateNoScriptObject();
319 
320     // Wrap the JSObject in an NPObject
321     return _NPN_CreateScriptObject(0, object, bindingRootObject());
322 }
323 
324 #endif
325 
jsObjectForPluginElement(HTMLPlugInElement * plugin)326 JSObject* ScriptController::jsObjectForPluginElement(HTMLPlugInElement* plugin)
327 {
328     // Can't create JSObjects when JavaScript is disabled
329     if (!isEnabled())
330         return 0;
331 
332     // Create a JSObject bound to this element
333     JSLock lock(SilenceAssertionsOnly);
334     ExecState* exec = globalObject()->globalExec();
335     JSValue jsElementValue = toJS(exec, globalObject(), plugin);
336     if (!jsElementValue || !jsElementValue.isObject())
337         return 0;
338 
339     return jsElementValue.getObject();
340 }
341 
342 #if !PLATFORM(MAC)
343 
updatePlatformScriptObjects()344 void ScriptController::updatePlatformScriptObjects()
345 {
346 }
347 
disconnectPlatformScriptObjects()348 void ScriptController::disconnectPlatformScriptObjects()
349 {
350 }
351 
352 #endif
353 
cleanupScriptObjectsForPlugin(void * nativeHandle)354 void ScriptController::cleanupScriptObjectsForPlugin(void* nativeHandle)
355 {
356     RootObjectMap::iterator it = m_rootObjects.find(nativeHandle);
357 
358     if (it == m_rootObjects.end())
359         return;
360 
361     it->second->invalidate();
362     m_rootObjects.remove(it);
363 }
364 
clearScriptObjects()365 void ScriptController::clearScriptObjects()
366 {
367     JSLock lock(SilenceAssertionsOnly);
368 
369     RootObjectMap::const_iterator end = m_rootObjects.end();
370     for (RootObjectMap::const_iterator it = m_rootObjects.begin(); it != end; ++it)
371         it->second->invalidate();
372 
373     m_rootObjects.clear();
374 
375     if (m_bindingRootObject) {
376         m_bindingRootObject->invalidate();
377         m_bindingRootObject = 0;
378     }
379 
380 #if ENABLE(NETSCAPE_PLUGIN_API)
381     if (m_windowScriptNPObject) {
382         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
383         // script object properly.
384         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
385         _NPN_DeallocateObject(m_windowScriptNPObject);
386         m_windowScriptNPObject = 0;
387     }
388 #endif
389 }
390 
391 } // namespace WebCore
392