/* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "ScriptController.h" #include "ScriptableDocumentParser.h" #include "Event.h" #include "EventNames.h" #include "Frame.h" #include "FrameLoaderClient.h" #include "GCController.h" #include "HTMLPlugInElement.h" #include "InspectorInstrumentation.h" #include "JSDocument.h" #include "JSMainThreadExecState.h" #include "NP_jsobject.h" #include "Page.h" #include "PageGroup.h" #include "ScriptSourceCode.h" #include "ScriptValue.h" #include "Settings.h" #include "StorageNamespace.h" #include "UserGestureIndicator.h" #include "WebCoreJSClientData.h" #include "npruntime_impl.h" #include "runtime_root.h" #include #include #include #include using namespace JSC; using namespace std; namespace WebCore { void ScriptController::initializeThreading() { JSC::initializeThreading(); WTF::initializeMainThread(); } ScriptController::ScriptController(Frame* frame) : m_frame(frame) , m_sourceURL(0) , m_inExecuteScript(false) , m_processingTimerCallback(false) , m_paused(false) , m_allowPopupsFromPlugin(false) #if ENABLE(NETSCAPE_PLUGIN_API) , m_windowScriptNPObject(0) #endif #if PLATFORM(MAC) , m_windowScriptObject(0) #endif { #if PLATFORM(MAC) && ENABLE(JAVA_BRIDGE) static bool initializedJavaJSBindings; if (!initializedJavaJSBindings) { initializedJavaJSBindings = true; initJavaJSBindings(); } #endif } ScriptController::~ScriptController() { disconnectPlatformScriptObjects(); if (m_cacheableBindingRootObject) { m_cacheableBindingRootObject->invalidate(); m_cacheableBindingRootObject = 0; } // It's likely that destroying m_windowShells will create a lot of garbage. if (!m_windowShells.isEmpty()) { while (!m_windowShells.isEmpty()) destroyWindowShell(m_windowShells.begin()->first.get()); gcController().garbageCollectSoon(); } } void ScriptController::destroyWindowShell(DOMWrapperWorld* world) { ASSERT(m_windowShells.contains(world)); m_windowShells.remove(world); world->didDestroyWindowShell(this); } JSDOMWindowShell* ScriptController::createWindowShell(DOMWrapperWorld* world) { ASSERT(!m_windowShells.contains(world)); Strong windowShell(*world->globalData(), new JSDOMWindowShell(m_frame->domWindow(), world)); Strong windowShell2(windowShell); m_windowShells.add(world, windowShell); world->didCreateWindowShell(this); return windowShell.get(); } ScriptValue ScriptController::evaluateInWorld(const ScriptSourceCode& sourceCode, DOMWrapperWorld* world) { const SourceCode& jsSourceCode = sourceCode.jsSourceCode(); String sourceURL = ustringToString(jsSourceCode.provider()->url()); // evaluate code. Returns the JS return value or 0 // if there was none, an error occurred or the type couldn't be converted. // inlineCode is true for // and false for . Check if it has the // expected value in all cases. // See smart window.open policy for where this is used. JSDOMWindowShell* shell = windowShell(world); ExecState* exec = shell->window()->globalExec(); const String* savedSourceURL = m_sourceURL; m_sourceURL = &sourceURL; JSLock lock(SilenceAssertionsOnly); RefPtr protect = m_frame; InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, sourceURL, sourceCode.startLine()); exec->globalData().timeoutChecker.start(); Completion comp = JSMainThreadExecState::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), jsSourceCode, shell); exec->globalData().timeoutChecker.stop(); InspectorInstrumentation::didEvaluateScript(cookie); // Evaluating the JavaScript could cause the frame to be deallocated // so we start the keep alive timer here. m_frame->keepAlive(); if (comp.complType() == Normal || comp.complType() == ReturnValue) { m_sourceURL = savedSourceURL; return ScriptValue(exec->globalData(), comp.value()); } if (comp.complType() == Throw || comp.complType() == Interrupted) reportException(exec, comp.value()); m_sourceURL = savedSourceURL; return ScriptValue(); } ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode) { return evaluateInWorld(sourceCode, mainThreadNormalWorld()); } PassRefPtr ScriptController::createWorld() { return DOMWrapperWorld::create(JSDOMWindow::commonJSGlobalData()); } void ScriptController::getAllWorlds(Vector& worlds) { static_cast(JSDOMWindow::commonJSGlobalData()->clientData)->getAllWorlds(worlds); } void ScriptController::clearWindowShell(bool goingIntoPageCache) { if (m_windowShells.isEmpty()) return; JSLock lock(SilenceAssertionsOnly); for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) { JSDOMWindowShell* windowShell = iter->second.get(); // Clear the debugger from the current window before setting the new window. attachDebugger(windowShell, 0); windowShell->window()->willRemoveFromWindowShell(); windowShell->setWindow(m_frame->domWindow()); // An m_cacheableBindingRootObject persists between page navigations // so needs to know about the new JSDOMWindow. if (m_cacheableBindingRootObject) m_cacheableBindingRootObject->updateGlobalObject(windowShell->window()); if (Page* page = m_frame->page()) { attachDebugger(windowShell, page->debugger()); windowShell->window()->setProfileGroup(page->group().identifier()); } } // It's likely that resetting our windows created a lot of garbage, unless // it went in a back/forward cache. if (!goingIntoPageCache) gcController().garbageCollectSoon(); } JSDOMWindowShell* ScriptController::initScript(DOMWrapperWorld* world) { ASSERT(!m_windowShells.contains(world)); JSLock lock(SilenceAssertionsOnly); JSDOMWindowShell* windowShell = createWindowShell(world); windowShell->window()->updateDocument(); if (Page* page = m_frame->page()) { attachDebugger(windowShell, page->debugger()); windowShell->window()->setProfileGroup(page->group().identifier()); } m_frame->loader()->dispatchDidClearWindowObjectInWorld(world); return windowShell; } int ScriptController::eventHandlerLineNumber() const { // JSC expects 1-based line numbers, so we must add one here to get it right. ScriptableDocumentParser* parser = m_frame->document()->scriptableDocumentParser(); if (parser) return parser->lineNumber() + 1; return 0; } bool ScriptController::processingUserGesture() { ExecState* exec = JSMainThreadExecState::currentState(); Frame* frame = exec ? toDynamicFrame(exec) : 0; // No script is running, so it is user-initiated unless the gesture stack // explicitly says it is not. if (!frame) return UserGestureIndicator::getUserGestureState() != DefinitelyNotProcessingUserGesture; // FIXME: We check the plugin popup flag and javascript anchor navigation // from the dynamic frame becuase they should only be initiated on the // dynamic frame in which execution began if they do happen. ScriptController* scriptController = frame->script(); ASSERT(scriptController); if (scriptController->allowPopupsFromPlugin() || scriptController->isJavaScriptAnchorNavigation()) return true; // If a DOM event is being processed, check that it was initiated by the user // and that it is in the whitelist of event types allowed to generate pop-ups. if (JSDOMWindowShell* shell = scriptController->existingWindowShell(currentWorld(exec))) if (Event* event = shell->window()->currentEvent()) return event->fromUserGesture(); return UserGestureIndicator::processingUserGesture(); } // FIXME: This seems like an insufficient check to verify a click on a javascript: anchor. bool ScriptController::isJavaScriptAnchorNavigation() const { // This is the