diff options
Diffstat (limited to 'Source/WebCore/bindings/js/ScriptController.cpp')
-rw-r--r-- | Source/WebCore/bindings/js/ScriptController.cpp | 444 |
1 files changed, 309 insertions, 135 deletions
diff --git a/Source/WebCore/bindings/js/ScriptController.cpp b/Source/WebCore/bindings/js/ScriptController.cpp index c0135ae8d..68eb91bed 100644 --- a/Source/WebCore/bindings/js/ScriptController.cpp +++ b/Source/WebCore/bindings/js/ScriptController.cpp @@ -1,7 +1,7 @@ /* * 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. + * Copyright (C) 2006-2016 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 @@ -22,36 +22,48 @@ #include "ScriptController.h" #include "BridgeJSC.h" +#include "CachedScriptFetcher.h" +#include "CommonVM.h" #include "ContentSecurityPolicy.h" #include "DocumentLoader.h" #include "Event.h" -#include "EventNames.h" #include "Frame.h" #include "FrameLoaderClient.h" #include "GCController.h" #include "HTMLPlugInElement.h" #include "InspectorInstrumentation.h" +#include "JSDOMBindingSecurity.h" +#include "JSDOMExceptionHandling.h" #include "JSDOMWindow.h" #include "JSDocument.h" #include "JSMainThreadExecState.h" +#include "LoadableModuleScript.h" +#include "MainFrame.h" +#include "MemoryPressureHandler.h" +#include "ModuleFetchFailureKind.h" #include "NP_jsobject.h" #include "Page.h" +#include "PageConsoleClient.h" #include "PageGroup.h" -#include "PluginView.h" -#include "ScriptCallStack.h" +#include "PluginViewBase.h" #include "ScriptSourceCode.h" #include "ScriptableDocumentParser.h" #include "Settings.h" -#include "StorageNamespace.h" #include "UserGestureIndicator.h" #include "WebCoreJSClientData.h" #include "npruntime_impl.h" #include "runtime_root.h" -#include <bindings/ScriptValue.h> #include <debugger/Debugger.h> #include <heap/StrongInlines.h> +#include <inspector/ScriptCallStack.h> #include <runtime/InitializeThreading.h> +#include <runtime/JSFunction.h> +#include <runtime/JSInternalPromise.h> #include <runtime/JSLock.h> +#include <runtime/JSModuleRecord.h> +#include <runtime/JSNativeStdFunction.h> +#include <runtime/JSScriptFetcher.h> +#include <wtf/SetForScope.h> #include <wtf/Threading.h> #include <wtf/text/TextPosition.h> @@ -59,6 +71,18 @@ using namespace JSC; namespace WebCore { +static void collectGarbageAfterWindowShellDestruction() +{ + // Make sure to GC Extra Soon(tm) during memory pressure conditions + // to soften high peaks of memory usage during navigation. + if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) { + // NOTE: We do the collection on next runloop to ensure that there's no pointer + // to the window object on the stack. + GCController::singleton().garbageCollectOnNextRunLoop(); + } else + GCController::singleton().garbageCollectSoon(); +} + void ScriptController::initializeThreading() { #if !PLATFORM(IOS) @@ -74,7 +98,7 @@ ScriptController::ScriptController(Frame& frame) #if ENABLE(NETSCAPE_PLUGIN_API) , m_windowScriptNPObject(0) #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) , m_windowScriptObject(0) #endif { @@ -85,16 +109,19 @@ ScriptController::~ScriptController() disconnectPlatformScriptObjects(); if (m_cacheableBindingRootObject) { - JSLockHolder lock(JSDOMWindowBase::commonVM()); + JSLockHolder lock(commonVM()); m_cacheableBindingRootObject->invalidate(); - m_cacheableBindingRootObject = 0; + m_cacheableBindingRootObject = nullptr; } // 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()->key); - gcController().garbageCollectSoon(); + while (!m_windowShells.isEmpty()) { + ShellMap::iterator iter = m_windowShells.begin(); + iter->value->window()->setConsoleClient(nullptr); + destroyWindowShell(*iter->key); + } + collectGarbageAfterWindowShellDestruction(); } } @@ -105,22 +132,24 @@ void ScriptController::destroyWindowShell(DOMWrapperWorld& world) world.didDestroyWindowShell(this); } -JSDOMWindowShell* ScriptController::createWindowShell(DOMWrapperWorld& world) +JSDOMWindowShell& ScriptController::createWindowShell(DOMWrapperWorld& world) { ASSERT(!m_windowShells.contains(&world)); - VM& vm = *world.vm(); + VM& vm = world.vm(); Structure* structure = JSDOMWindowShell::createStructure(vm, jsNull()); Strong<JSDOMWindowShell> windowShell(vm, JSDOMWindowShell::create(vm, m_frame.document()->domWindow(), structure, world)); Strong<JSDOMWindowShell> windowShell2(windowShell); m_windowShells.add(&world, windowShell); world.didCreateWindowShell(this); - return windowShell.get(); + return *windowShell.get(); } -Deprecated::ScriptValue ScriptController::evaluateInWorld(const ScriptSourceCode& sourceCode, DOMWrapperWorld& world) +JSValue ScriptController::evaluateInWorld(const ScriptSourceCode& sourceCode, DOMWrapperWorld& world, ExceptionDetails* exceptionDetails) { + JSLockHolder lock(world.vm()); + const SourceCode& jsSourceCode = sourceCode.jsSourceCode(); String sourceURL = jsSourceCode.provider()->url(); @@ -136,77 +165,178 @@ Deprecated::ScriptValue ScriptController::evaluateInWorld(const ScriptSourceCode const String* savedSourceURL = m_sourceURL; m_sourceURL = &sourceURL; - JSLockHolder lock(exec); - - Ref<Frame> protect(m_frame); - - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(&m_frame, sourceURL, sourceCode.startLine()); + Ref<Frame> protector(m_frame); - JSValue evaluationException; + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, sourceURL, sourceCode.startLine()); - JSValue returnValue = JSMainThreadExecState::evaluate(exec, jsSourceCode, shell, &evaluationException); + NakedPtr<JSC::Exception> evaluationException; + JSValue returnValue = JSMainThreadExecState::profiledEvaluate(exec, JSC::ProfilingReason::Other, jsSourceCode, shell, evaluationException); - InspectorInstrumentation::didEvaluateScript(cookie); + InspectorInstrumentation::didEvaluateScript(cookie, m_frame); if (evaluationException) { - reportException(exec, evaluationException, sourceCode.cachedScript()); + reportException(exec, evaluationException, sourceCode.cachedScript(), exceptionDetails); m_sourceURL = savedSourceURL; - return Deprecated::ScriptValue(); + return { }; } m_sourceURL = savedSourceURL; - return Deprecated::ScriptValue(exec->vm(), returnValue); + return returnValue; +} + +JSValue ScriptController::evaluate(const ScriptSourceCode& sourceCode, ExceptionDetails* exceptionDetails) +{ + return evaluateInWorld(sourceCode, mainThreadNormalWorld(), exceptionDetails); +} + +void ScriptController::loadModuleScriptInWorld(LoadableModuleScript& moduleScript, const String& moduleName, DOMWrapperWorld& world) +{ + JSLockHolder lock(world.vm()); + + auto& shell = *windowShell(world); + auto& state = *shell.window()->globalExec(); + + auto& promise = JSMainThreadExecState::loadModule(state, moduleName, JSC::JSScriptFetcher::create(state.vm(), { &moduleScript })); + setupModuleScriptHandlers(moduleScript, promise, world); +} + +void ScriptController::loadModuleScript(LoadableModuleScript& moduleScript, const String& moduleName) +{ + loadModuleScriptInWorld(moduleScript, moduleName, mainThreadNormalWorld()); +} + +void ScriptController::loadModuleScriptInWorld(LoadableModuleScript& moduleScript, const ScriptSourceCode& sourceCode, DOMWrapperWorld& world) +{ + JSLockHolder lock(world.vm()); + + auto& shell = *windowShell(world); + auto& state = *shell.window()->globalExec(); + + auto& promise = JSMainThreadExecState::loadModule(state, sourceCode.jsSourceCode(), JSC::JSScriptFetcher::create(state.vm(), { &moduleScript })); + setupModuleScriptHandlers(moduleScript, promise, world); +} + +void ScriptController::loadModuleScript(LoadableModuleScript& moduleScript, const ScriptSourceCode& sourceCode) +{ + loadModuleScriptInWorld(moduleScript, sourceCode, mainThreadNormalWorld()); +} + +JSC::JSValue ScriptController::linkAndEvaluateModuleScriptInWorld(LoadableModuleScript& moduleScript, DOMWrapperWorld& world) +{ + JSLockHolder lock(world.vm()); + + auto& shell = *windowShell(world); + auto& state = *shell.window()->globalExec(); + + // FIXME: Preventing Frame from being destroyed is essentially unnecessary. + // https://bugs.webkit.org/show_bug.cgi?id=164763 + Ref<Frame> protector(m_frame); + + NakedPtr<JSC::Exception> evaluationException; + auto returnValue = JSMainThreadExecState::linkAndEvaluateModule(state, Identifier::fromUid(&state.vm(), moduleScript.moduleKey()), jsUndefined(), evaluationException); + if (evaluationException) { + // FIXME: Give a chance to dump the stack trace if the "crossorigin" attribute allows. + // https://bugs.webkit.org/show_bug.cgi?id=164539 + reportException(&state, evaluationException, nullptr); + return jsUndefined(); + } + return returnValue; +} + +JSC::JSValue ScriptController::linkAndEvaluateModuleScript(LoadableModuleScript& moduleScript) +{ + return linkAndEvaluateModuleScriptInWorld(moduleScript, mainThreadNormalWorld()); +} + +JSC::JSValue ScriptController::evaluateModule(const URL& sourceURL, JSModuleRecord& moduleRecord, DOMWrapperWorld& world) +{ + JSLockHolder lock(world.vm()); + + const auto& jsSourceCode = moduleRecord.sourceCode(); + + auto& shell = *windowShell(world); + auto& state = *shell.window()->globalExec(); + SetForScope<const String*> sourceURLScope(m_sourceURL, &sourceURL.string()); + + Ref<Frame> protector(m_frame); + + auto cookie = InspectorInstrumentation::willEvaluateScript(m_frame, sourceURL, jsSourceCode.firstLine().oneBasedInt()); + + auto returnValue = moduleRecord.evaluate(&state); + InspectorInstrumentation::didEvaluateScript(cookie, m_frame); + + return returnValue; +} + +JSC::JSValue ScriptController::evaluateModule(const URL& sourceURL, JSModuleRecord& moduleRecord) +{ + return evaluateModule(sourceURL, moduleRecord, mainThreadNormalWorld()); } -Deprecated::ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode) +Ref<DOMWrapperWorld> ScriptController::createWorld() { - return evaluateInWorld(sourceCode, mainThreadNormalWorld()); + return DOMWrapperWorld::create(commonVM()); } -PassRefPtr<DOMWrapperWorld> ScriptController::createWorld() +Vector<JSC::Strong<JSDOMWindowShell>> ScriptController::windowShells() { - return DOMWrapperWorld::create(JSDOMWindow::commonVM()); + Vector<JSC::Strong<JSDOMWindowShell>> windowShells; + copyValuesToVector(m_windowShells, windowShells); + return windowShells; } void ScriptController::getAllWorlds(Vector<Ref<DOMWrapperWorld>>& worlds) { - static_cast<WebCoreJSClientData*>(JSDOMWindow::commonVM()->clientData)->getAllWorlds(worlds); + static_cast<JSVMClientData*>(commonVM().clientData)->getAllWorlds(worlds); } -void ScriptController::clearWindowShell(DOMWindow* newDOMWindow, bool goingIntoPageCache) +void ScriptController::clearWindowShellsNotMatchingDOMWindow(DOMWindow* newDOMWindow, bool goingIntoPageCache) { if (m_windowShells.isEmpty()) return; - JSLockHolder lock(JSDOMWindowBase::commonVM()); + JSLockHolder lock(commonVM()); - for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) { - JSDOMWindowShell* windowShell = iter->value.get(); - - if (&windowShell->window()->impl() == newDOMWindow) + for (auto& windowShell : windowShells()) { + if (&windowShell->window()->wrapped() == newDOMWindow) continue; - // Clear the debugger from the current window before setting the new window. - attachDebugger(windowShell, 0); - + // Clear the debugger and console from the current window before setting the new window. + attachDebugger(windowShell.get(), nullptr); + windowShell->window()->setConsoleClient(nullptr); windowShell->window()->willRemoveFromWindowShell(); - windowShell->setWindow(newDOMWindow); + } + // It's likely that resetting our windows created a lot of garbage, unless + // it went in a back/forward cache. + if (!goingIntoPageCache) + collectGarbageAfterWindowShellDestruction(); +} + +void ScriptController::setDOMWindowForWindowShell(DOMWindow* newDOMWindow) +{ + if (m_windowShells.isEmpty()) + return; + + JSLockHolder lock(commonVM()); + + for (auto& windowShell : windowShells()) { + if (&windowShell->window()->wrapped() == newDOMWindow) + continue; + + windowShell->setWindow(newDOMWindow); + // 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()); + attachDebugger(windowShell.get(), page->debugger()); windowShell->window()->setProfileGroup(page->group().identifier()); + windowShell->window()->setConsoleClient(&page->console()); } } - - // 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) @@ -215,29 +345,97 @@ JSDOMWindowShell* ScriptController::initScript(DOMWrapperWorld& world) JSLockHolder lock(world.vm()); - JSDOMWindowShell* windowShell = createWindowShell(world); + JSDOMWindowShell& windowShell = createWindowShell(world); - windowShell->window()->updateDocument(); + windowShell.window()->updateDocument(); - if (m_frame.document()) - windowShell->window()->setEvalEnabled(m_frame.document()->contentSecurityPolicy()->allowEval(0, ContentSecurityPolicy::SuppressReport), m_frame.document()->contentSecurityPolicy()->evalDisabledErrorMessage()); + if (Document* document = m_frame.document()) + document->contentSecurityPolicy()->didCreateWindowShell(windowShell); if (Page* page = m_frame.page()) { - attachDebugger(windowShell, page->debugger()); - windowShell->window()->setProfileGroup(page->group().identifier()); + attachDebugger(&windowShell, page->debugger()); + windowShell.window()->setProfileGroup(page->group().identifier()); + windowShell.window()->setConsoleClient(&page->console()); } m_frame.loader().dispatchDidClearWindowObjectInWorld(world); - return windowShell; + return &windowShell; +} + +static Identifier jsValueToModuleKey(ExecState* exec, JSValue value) +{ + if (value.isSymbol()) + return Identifier::fromUid(jsCast<Symbol*>(value)->privateName()); + ASSERT(value.isString()); + return asString(value)->toIdentifier(exec); +} + +void ScriptController::setupModuleScriptHandlers(LoadableModuleScript& moduleScriptRef, JSInternalPromise& promise, DOMWrapperWorld& world) +{ + auto& shell = *windowShell(world); + auto& state = *shell.window()->globalExec(); + + // It is not guaranteed that either fulfillHandler or rejectHandler is eventually called. + // For example, if the page load is canceled, the DeferredPromise used in the module loader pipeline will stop executing JS code. + // Thus the promise returned from this function could remain unresolved. + + RefPtr<LoadableModuleScript> moduleScript(&moduleScriptRef); + + auto& fulfillHandler = *JSNativeStdFunction::create(state.vm(), shell.window(), 1, String(), [moduleScript](ExecState* exec) { + Identifier moduleKey = jsValueToModuleKey(exec, exec->argument(0)); + moduleScript->notifyLoadCompleted(*moduleKey.impl()); + return JSValue::encode(jsUndefined()); + }); + + auto& rejectHandler = *JSNativeStdFunction::create(state.vm(), shell.window(), 1, String(), [moduleScript](ExecState* exec) { + VM& vm = exec->vm(); + JSValue errorValue = exec->argument(0); + if (errorValue.isObject()) { + auto* object = JSC::asObject(errorValue); + if (JSValue failureKindValue = object->getDirect(vm, static_cast<JSVMClientData&>(*vm.clientData).builtinNames().failureKindPrivateName())) { + // This is host propagated error in the module loader pipeline. + switch (static_cast<ModuleFetchFailureKind>(failureKindValue.asInt32())) { + case ModuleFetchFailureKind::WasErrored: + moduleScript->notifyLoadFailed(LoadableScript::Error { + LoadableScript::ErrorType::CachedScript, + std::nullopt + }); + break; + case ModuleFetchFailureKind::WasCanceled: + moduleScript->notifyLoadWasCanceled(); + break; + } + return JSValue::encode(jsUndefined()); + } + } + + auto scope = DECLARE_CATCH_SCOPE(vm); + moduleScript->notifyLoadFailed(LoadableScript::Error { + LoadableScript::ErrorType::CachedScript, + LoadableScript::ConsoleMessage { + MessageSource::JS, + MessageLevel::Error, + retrieveErrorMessage(*exec, vm, errorValue, scope), + } + }); + return JSValue::encode(jsUndefined()); + }); + + promise.then(&state, &fulfillHandler, &rejectHandler); } TextPosition ScriptController::eventHandlerPosition() const { + // FIXME: If we are not currently parsing, we should use our current location + // in JavaScript, to cover cases like "element.setAttribute('click', ...)". + + // FIXME: This location maps to the end of the HTML tag, and not to the + // exact column number belonging to the event handler attribute. ScriptableDocumentParser* parser = m_frame.document()->scriptableDocumentParser(); if (parser) return parser->textPosition(); - return TextPosition::minimumPosition(); + return TextPosition(); } void ScriptController::enableEval() @@ -261,20 +459,27 @@ bool ScriptController::processingUserGesture() return UserGestureIndicator::processingUserGesture(); } -bool ScriptController::canAccessFromCurrentOrigin(Frame *frame) +bool ScriptController::processingUserGestureForMedia() { - ExecState* exec = JSMainThreadExecState::currentState(); - if (exec) - return shouldAllowAccessToFrame(exec, frame); - // If the current state is 0 we're in a call path where the DOM security - // check doesn't apply (eg. parser). - return true; + return UserGestureIndicator::processingUserGestureForMedia(); +} + +bool ScriptController::canAccessFromCurrentOrigin(Frame* frame) +{ + ExecState* state = JSMainThreadExecState::currentState(); + + // If the current state is null we're in a call path where the DOM security check doesn't apply (eg. parser). + if (!state) + return true; + + return BindingSecurity::shouldAllowAccessToFrame(state, frame); } void ScriptController::attachDebugger(JSC::Debugger* debugger) { - for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) - attachDebugger(iter->value.get(), debugger); + Vector<JSC::Strong<JSDOMWindowShell>> windowShells = this->windowShells(); + for (size_t i = 0; i < windowShells.size(); ++i) + attachDebugger(windowShells[i].get(), debugger); } void ScriptController::attachDebugger(JSDOMWindowShell* shell, JSC::Debugger* debugger) @@ -283,6 +488,7 @@ void ScriptController::attachDebugger(JSDOMWindowShell* shell, JSC::Debugger* de return; JSDOMWindow* globalObject = shell->window(); + JSLockHolder lock(globalObject->vm()); if (debugger) debugger->attach(globalObject); else if (JSC::Debugger* currentDebugger = globalObject->debugger()) @@ -291,9 +497,11 @@ void ScriptController::attachDebugger(JSDOMWindowShell* shell, JSC::Debugger* de void ScriptController::updateDocument() { - for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) { - JSLockHolder lock(iter->key->vm()); - iter->value->window()->updateDocument(); + Vector<JSC::Strong<JSDOMWindowShell>> windowShells = this->windowShells(); + for (size_t i = 0; i < windowShells.size(); ++i) { + JSDOMWindowShell* windowShell = windowShells[i].get(); + JSLockHolder lock(windowShell->world().vm()); + windowShell->window()->updateDocument(); } } @@ -303,7 +511,7 @@ Bindings::RootObject* ScriptController::cacheableBindingRootObject() return 0; if (!m_cacheableBindingRootObject) { - JSLockHolder lock(JSDOMWindowBase::commonVM()); + JSLockHolder lock(commonVM()); m_cacheableBindingRootObject = Bindings::RootObject::create(0, globalObject(pluginWorld())); } return m_cacheableBindingRootObject.get(); @@ -315,13 +523,13 @@ Bindings::RootObject* ScriptController::bindingRootObject() return 0; if (!m_bindingRootObject) { - JSLockHolder lock(JSDOMWindowBase::commonVM()); + JSLockHolder lock(commonVM()); m_bindingRootObject = Bindings::RootObject::create(0, globalObject(pluginWorld())); } return m_bindingRootObject.get(); } -PassRefPtr<Bindings::RootObject> ScriptController::createRootObject(void* nativeHandle) +RefPtr<Bindings::RootObject> ScriptController::createRootObject(void* nativeHandle) { RootObjectMap::iterator it = m_rootObjects.find(nativeHandle); if (it != m_rootObjects.end()) @@ -330,36 +538,28 @@ PassRefPtr<Bindings::RootObject> ScriptController::createRootObject(void* native RefPtr<Bindings::RootObject> rootObject = Bindings::RootObject::create(nativeHandle, globalObject(pluginWorld())); m_rootObjects.set(nativeHandle, rootObject); - return rootObject.release(); -} - -#if ENABLE(INSPECTOR) -void ScriptController::setCaptureCallStackForUncaughtExceptions(bool) -{ + return rootObject; } void ScriptController::collectIsolatedContexts(Vector<std::pair<JSC::ExecState*, SecurityOrigin*>>& result) { for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) { JSC::ExecState* exec = iter->value->window()->globalExec(); - SecurityOrigin* origin = iter->value->window()->impl().document()->securityOrigin(); + SecurityOrigin* origin = &iter->value->window()->wrapped().document()->securityOrigin(); result.append(std::pair<JSC::ExecState*, SecurityOrigin*>(exec, origin)); } } -#endif - #if ENABLE(NETSCAPE_PLUGIN_API) - NPObject* ScriptController::windowScriptNPObject() { if (!m_windowScriptNPObject) { + JSLockHolder lock(commonVM()); if (canExecuteScripts(NotAboutToExecuteScript)) { // JavaScript is enabled, so there is a JavaScript window object. // Return an NPObject bound to the window object. JSDOMWindow* win = windowShell(pluginWorld())->window(); ASSERT(win); - JSC::JSLockHolder lock(win->globalExec()); Bindings::RootObject* root = bindingRootObject(); m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root); } else { @@ -371,26 +571,15 @@ NPObject* ScriptController::windowScriptNPObject() return m_windowScriptNPObject; } - -NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin) -{ - JSObject* object = jsObjectForPluginElement(plugin); - if (!object) - return _NPN_CreateNoScriptObject(); - - // Wrap the JSObject in an NPObject - return _NPN_CreateScriptObject(0, object, bindingRootObject()); -} - #endif -#if !PLATFORM(MAC) -PassRefPtr<JSC::Bindings::Instance> ScriptController::createScriptInstanceForWidget(Widget* widget) +#if !PLATFORM(COCOA) +RefPtr<JSC::Bindings::Instance> ScriptController::createScriptInstanceForWidget(Widget* widget) { - if (!widget->isPluginView()) - return 0; + if (!is<PluginViewBase>(*widget)) + return nullptr; - return toPluginView(widget)->bindingInstance(); + return downcast<PluginViewBase>(*widget).bindingInstance(); } #endif @@ -400,9 +589,10 @@ JSObject* ScriptController::jsObjectForPluginElement(HTMLPlugInElement* plugin) if (!canExecuteScripts(NotAboutToExecuteScript)) return 0; + JSLockHolder lock(commonVM()); + // Create a JSObject bound to this element JSDOMWindow* globalObj = globalObject(pluginWorld()); - JSLockHolder lock(globalObj->globalExec()); // FIXME: is normal okay? - used for NP plugins? JSValue jsElementValue = toJS(globalObj->globalExec(), globalObj, plugin); if (!jsElementValue || !jsElementValue.isObject()) @@ -411,7 +601,7 @@ JSObject* ScriptController::jsObjectForPluginElement(HTMLPlugInElement* plugin) return jsElementValue.getObject(); } -#if !PLATFORM(MAC) +#if !PLATFORM(COCOA) void ScriptController::updatePlatformScriptObjects() { @@ -436,7 +626,7 @@ void ScriptController::cleanupScriptObjectsForPlugin(void* nativeHandle) void ScriptController::clearScriptObjects() { - JSLockHolder lock(JSDOMWindowBase::commonVM()); + JSLockHolder lock(commonVM()); RootObjectMap::const_iterator end = m_rootObjects.end(); for (RootObjectMap::const_iterator it = m_rootObjects.begin(); it != end; ++it) @@ -446,7 +636,7 @@ void ScriptController::clearScriptObjects() if (m_bindingRootObject) { m_bindingRootObject->invalidate(); - m_bindingRootObject = 0; + m_bindingRootObject = nullptr; } #if ENABLE(NETSCAPE_PLUGIN_API) @@ -455,67 +645,53 @@ void ScriptController::clearScriptObjects() // script object properly. // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point. _NPN_DeallocateObject(m_windowScriptNPObject); - m_windowScriptNPObject = 0; + m_windowScriptNPObject = nullptr; } #endif } -Deprecated::ScriptValue ScriptController::executeScriptInWorld(DOMWrapperWorld& world, const String& script, bool forceUserGesture) +JSValue ScriptController::executeScriptInWorld(DOMWrapperWorld& world, const String& script, bool forceUserGesture) { - UserGestureIndicator gestureIndicator(forceUserGesture ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture); - ScriptSourceCode sourceCode(script, m_frame.document()->url()); + UserGestureIndicator gestureIndicator(forceUserGesture ? std::optional<ProcessingUserGestureState>(ProcessingUserGesture) : std::nullopt); + ScriptSourceCode sourceCode(script, m_frame.document()->url(), TextPosition(), JSC::SourceProviderSourceType::Program, CachedScriptFetcher::create(m_frame.document()->charset())); if (!canExecuteScripts(AboutToExecuteScript) || isPaused()) - return Deprecated::ScriptValue(); + return { }; return evaluateInWorld(sourceCode, world); } -bool ScriptController::shouldBypassMainWorldContentSecurityPolicy() -{ - CallFrame* callFrame = JSDOMWindow::commonVM()->topCallFrame; - if (callFrame == CallFrame::noCaller()) - return false; - DOMWrapperWorld& domWrapperWorld = currentWorld(callFrame); - if (domWrapperWorld.isNormal()) - return false; - return true; -} - bool ScriptController::canExecuteScripts(ReasonForCallingCanExecuteScripts reason) { if (m_frame.document() && m_frame.document()->isSandboxed(SandboxScripts)) { // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. if (reason == AboutToExecuteScript) - m_frame.document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked script execution in '" + m_frame.document()->url().stringCenterEllipsizedToLength() + "' because the document's frame is sandboxed and the 'allow-scripts' permission is not set."); + m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked script execution in '" + m_frame.document()->url().stringCenterEllipsizedToLength() + "' because the document's frame is sandboxed and the 'allow-scripts' permission is not set."); return false; } - if (m_frame.document() && m_frame.document()->isViewSource()) { - ASSERT(m_frame.document()->securityOrigin()->isUnique()); - return true; - } - if (!m_frame.page()) return false; return m_frame.loader().client().allowScript(m_frame.settings().isScriptEnabled()); } -Deprecated::ScriptValue ScriptController::executeScript(const String& script, bool forceUserGesture) +JSValue ScriptController::executeScript(const String& script, bool forceUserGesture, ExceptionDetails* exceptionDetails) { - UserGestureIndicator gestureIndicator(forceUserGesture ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture); - return executeScript(ScriptSourceCode(script, m_frame.document()->url())); + UserGestureIndicator gestureIndicator(forceUserGesture ? std::optional<ProcessingUserGestureState>(ProcessingUserGesture) : std::nullopt); + return executeScript(ScriptSourceCode(script, m_frame.document()->url(), TextPosition(), JSC::SourceProviderSourceType::Program, CachedScriptFetcher::create(m_frame.document()->charset())), exceptionDetails); } -Deprecated::ScriptValue ScriptController::executeScript(const ScriptSourceCode& sourceCode) +JSValue ScriptController::executeScript(const ScriptSourceCode& sourceCode, ExceptionDetails* exceptionDetails) { if (!canExecuteScripts(AboutToExecuteScript) || isPaused()) - return Deprecated::ScriptValue(); + return { }; // FIXME: Would jsNull be better? - Ref<Frame> protect(m_frame); // Script execution can destroy the frame, and thus the ScriptController. + // FIXME: Preventing Frame from being destroyed is essentially unnecessary. + // https://bugs.webkit.org/show_bug.cgi?id=164763 + Ref<Frame> protector(m_frame); // Script execution can destroy the frame, and thus the ScriptController. - return evaluate(sourceCode); + return evaluate(sourceCode, exceptionDetails); } bool ScriptController::executeIfJavaScriptURL(const URL& url, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL) @@ -534,7 +710,7 @@ bool ScriptController::executeIfJavaScriptURL(const URL& url, ShouldReplaceDocum const int javascriptSchemeLength = sizeof("javascript:") - 1; String decodedURL = decodeURLEscapeSequences(url.string()); - Deprecated::ScriptValue result = executeScript(decodedURL.substring(javascriptSchemeLength)); + auto result = executeScript(decodedURL.substring(javascriptSchemeLength)); // If executing script caused this frame to be removed from the page, we // don't want to try to replace its document! @@ -542,9 +718,7 @@ bool ScriptController::executeIfJavaScriptURL(const URL& url, ShouldReplaceDocum return true; String scriptResult; - JSDOMWindowShell* shell = windowShell(mainThreadNormalWorld()); - JSC::ExecState* exec = shell->window()->globalExec(); - if (!result.getString(exec, scriptResult)) + if (!result || !result.getString(windowShell(mainThreadNormalWorld())->window()->globalExec(), scriptResult)) return true; // FIXME: We should always replace the document, but doing so |