• 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