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