diff -r 000000000000 -r 4f2f89ce4247 WebCore/bindings/js/ScriptController.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebCore/bindings/js/ScriptController.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,509 @@ +/* + * 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 "InspectorTimelineAgent.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 "XSSAuditor.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 + , m_XSSAuditor(new XSSAuditor(frame)) +{ +#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)); + JSDOMWindowShell* windowShell = new JSDOMWindowShell(m_frame->domWindow(), world); + m_windowShells.add(world, windowShell); + world->didCreateWindowShell(this); + return windowShell; +} + +ScriptValue ScriptController::evaluateInWorld(const ScriptSourceCode& sourceCode, DOMWrapperWorld* world, ShouldAllowXSS shouldAllowXSS) +{ + const SourceCode& jsSourceCode = sourceCode.jsSourceCode(); + String sourceURL = ustringToString(jsSourceCode.provider()->url()); + + if (shouldAllowXSS == DoNotAllowXSS && !m_XSSAuditor->canEvaluate(sourceCode.source())) { + // This script is not safe to be evaluated. + return JSValue(); + } + + // 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; + +#if ENABLE(INSPECTOR) + if (InspectorTimelineAgent* timelineAgent = m_frame->page() ? m_frame->page()->inspectorTimelineAgent() : 0) + timelineAgent->willEvaluateScript(sourceURL, sourceCode.startLine()); +#endif + + exec->globalData().timeoutChecker.start(); + Completion comp = JSMainThreadExecState::evaluate(exec, exec->dynamicGlobalObject()->globalScopeChain(), jsSourceCode, shell); + exec->globalData().timeoutChecker.stop(); + +#if ENABLE(INSPECTOR) + if (InspectorTimelineAgent* timelineAgent = m_frame->page() ? m_frame->page()->inspectorTimelineAgent() : 0) + timelineAgent->didEvaluateScript(); +#endif + + // 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 comp.value(); + } + + if (comp.complType() == Throw || comp.complType() == Interrupted) + reportException(exec, comp.value()); + + m_sourceURL = savedSourceURL; + return JSValue(); +} + +ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode, ShouldAllowXSS shouldAllowXSS) +{ + return evaluateInWorld(sourceCode, mainThreadNormalWorld(), shouldAllowXSS); +} + +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; + + // Clear the debugger from the current window before setting the new window. + attachDebugger(windowShell, 0); + + windowShell->window()->willRemoveFromWindowShell(); + windowShell->setWindow(m_frame->domWindow()); + + 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(DOMWrapperWorld* world) const +{ + if (m_allowPopupsFromPlugin || 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 = existingWindowShell(world)) + 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