From cad810f21b803229eb11403f9209855525a25d57 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Fri, 6 May 2011 11:45:16 +0100 Subject: Merge WebKit at r75315: Initial merge by git. Change-Id: I570314b346ce101c935ed22a626b48c2af266b84 --- Source/WebCore/bindings/js/ScriptController.cpp | 520 ++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 Source/WebCore/bindings/js/ScriptController.cpp (limited to 'Source/WebCore/bindings/js/ScriptController.cpp') diff --git a/Source/WebCore/bindings/js/ScriptController.cpp b/Source/WebCore/bindings/js/ScriptController.cpp new file mode 100644 index 0000000..cf55080 --- /dev/null +++ b/Source/WebCore/bindings/js/ScriptController.cpp @@ -0,0 +1,520 @@ +/* + * 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 "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; + + 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 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()); + + // 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