diff options
Diffstat (limited to 'Source/WebCore/bindings/js/JSDOMWindowBase.cpp')
-rw-r--r-- | Source/WebCore/bindings/js/JSDOMWindowBase.cpp | 335 |
1 files changed, 222 insertions, 113 deletions
diff --git a/Source/WebCore/bindings/js/JSDOMWindowBase.cpp b/Source/WebCore/bindings/js/JSDOMWindowBase.cpp index c6739780b..45df5a8e0 100644 --- a/Source/WebCore/bindings/js/JSDOMWindowBase.cpp +++ b/Source/WebCore/bindings/js/JSDOMWindowBase.cpp @@ -1,8 +1,9 @@ /* * Copyright (C) 2000 Harri Porten (porten@kde.org) * Copyright (C) 2006 Jon Shier (jshier@iastate.edu) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reseved. + * Copyright (C) 2003-2009, 2014, 2016 Apple Inc. All rights reseved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (c) 2015 Canon 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 @@ -23,45 +24,60 @@ #include "config.h" #include "JSDOMWindowBase.h" +#include "ActiveDOMCallbackMicrotask.h" #include "Chrome.h" -#include "Console.h" +#include "CommonVM.h" #include "DOMWindow.h" #include "Frame.h" #include "InspectorController.h" +#include "JSDOMBindingSecurity.h" #include "JSDOMGlobalObjectTask.h" #include "JSDOMWindowCustom.h" +#include "JSMainThreadExecState.h" #include "JSNode.h" +#include "Language.h" #include "Logging.h" #include "Page.h" +#include "RuntimeApplicationChecks.h" #include "ScriptController.h" +#include "ScriptModuleLoader.h" #include "SecurityOrigin.h" #include "Settings.h" #include "WebCoreJSClientData.h" +#include <bytecode/CodeBlock.h> +#include <heap/StrongInlines.h> +#include <runtime/JSInternalPromiseDeferred.h> #include <runtime/Microtask.h> #include <wtf/MainThread.h> #if PLATFORM(IOS) #include "ChromeClient.h" -#include "WebSafeGCActivityCallbackIOS.h" -#include "WebSafeIncrementalSweeperIOS.h" #endif using namespace JSC; namespace WebCore { -static bool shouldAllowAccessFrom(const JSGlobalObject* thisObject, ExecState* exec) -{ - return BindingSecurity::shouldAllowAccessToDOMWindow(exec, asJSDOMWindow(thisObject)->impl()); -} - -const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) }; - -const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &shouldAllowAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout }; - -JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, PassRefPtr<DOMWindow> window, JSDOMWindowShell* shell) - : JSDOMGlobalObject(vm, structure, &shell->world(), &s_globalObjectMethodTable) - , m_impl(window) +const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) }; + +const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { + &supportsRichSourceInfo, + &shouldInterruptScript, + &javaScriptRuntimeFlags, + &queueTaskToEventLoop, + &shouldInterruptScriptBeforeTimeout, + &moduleLoaderImportModule, + &moduleLoaderResolve, + &moduleLoaderFetch, + nullptr, + &moduleLoaderEvaluate, + &defaultLanguage +}; + +JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, RefPtr<DOMWindow>&& window, JSDOMWindowShell* shell) + : JSDOMGlobalObject(vm, structure, shell->world(), &s_globalObjectMethodTable) + , m_windowCloseWatchpoints((window && window->frame()) ? IsWatched : IsInvalidated) + , m_wrapped(WTFMove(window)) , m_shell(shell) { } @@ -69,14 +85,24 @@ JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, PassRefPtr<DOMWin void JSDOMWindowBase::finishCreation(VM& vm, JSDOMWindowShell* shell) { Base::finishCreation(vm, shell); - ASSERT(inherits(info())); + ASSERT(inherits(vm, info())); GlobalPropertyInfo staticGlobals[] = { GlobalPropertyInfo(vm.propertyNames->document, jsNull(), DontDelete | ReadOnly), - GlobalPropertyInfo(vm.propertyNames->window, m_shell, DontDelete | ReadOnly) + GlobalPropertyInfo(vm.propertyNames->window, m_shell, DontDelete | ReadOnly), }; - + addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals)); + + if (m_wrapped && m_wrapped->frame() && m_wrapped->frame()->settings().needsSiteSpecificQuirks()) + setNeedsSiteSpecificQuirks(true); +} + +void JSDOMWindowBase::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSDOMWindowBase* thisObject = jsCast<JSDOMWindowBase*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); } void JSDOMWindowBase::destroy(JSCell* cell) @@ -86,48 +112,31 @@ void JSDOMWindowBase::destroy(JSCell* cell) void JSDOMWindowBase::updateDocument() { - ASSERT(m_impl->document()); + // Since "document" property is defined as { configurable: false, writable: false, enumerable: true }, + // users cannot change its attributes further. + // Reaching here, the attributes of "document" property should be never changed. + ASSERT(m_wrapped->document()); ExecState* exec = globalExec(); - symbolTablePutWithAttributes(this, exec->vm(), exec->vm().propertyNames->document, toJS(exec, this, m_impl->document()), DontDelete | ReadOnly); + bool shouldThrowReadOnlyError = false; + bool ignoreReadOnlyErrors = true; + bool putResult = false; + symbolTablePutTouchWatchpointSet(this, exec, exec->vm().propertyNames->document, toJS(exec, this, m_wrapped->document()), shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult); } ScriptExecutionContext* JSDOMWindowBase::scriptExecutionContext() const { - return m_impl->document(); + return m_wrapped->document(); } void JSDOMWindowBase::printErrorMessage(const String& message) const { - printErrorMessageForFrame(impl().frame(), message); -} - -bool JSDOMWindowBase::supportsProfiling(const JSGlobalObject* object) -{ -#if !ENABLE(INSPECTOR) - UNUSED_PARAM(object); - return false; -#else - const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); - Frame* frame = thisObject->impl().frame(); - if (!frame) - return false; - - Page* page = frame->page(); - if (!page) - return false; - - return page->inspectorController().profilerEnabled(); -#endif // ENABLE(INSPECTOR) + printErrorMessageForFrame(wrapped().frame(), message); } bool JSDOMWindowBase::supportsRichSourceInfo(const JSGlobalObject* object) { -#if !ENABLE(INSPECTOR) - UNUSED_PARAM(object); - return false; -#else const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); - Frame* frame = thisObject->impl().frame(); + Frame* frame = thisObject->wrapped().frame(); if (!frame) return false; @@ -137,9 +146,7 @@ bool JSDOMWindowBase::supportsRichSourceInfo(const JSGlobalObject* object) bool enabled = page->inspectorController().enabled(); ASSERT(enabled || !thisObject->debugger()); - ASSERT(enabled || !supportsProfiling(thisObject)); return enabled; -#endif } static inline bool shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(Page* page) @@ -157,16 +164,16 @@ static inline bool shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPag bool JSDOMWindowBase::shouldInterruptScript(const JSGlobalObject* object) { const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); - ASSERT(thisObject->impl().frame()); - Page* page = thisObject->impl().frame()->page(); - return shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page) || page->chrome().shouldInterruptJavaScript(); + ASSERT(thisObject->wrapped().frame()); + Page* page = thisObject->wrapped().frame()->page(); + return shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page); } bool JSDOMWindowBase::shouldInterruptScriptBeforeTimeout(const JSGlobalObject* object) { const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); - ASSERT(thisObject->impl().frame()); - Page* page = thisObject->impl().frame()->page(); + ASSERT(thisObject->wrapped().frame()); + Page* page = thisObject->wrapped().frame()->page(); if (shouldInterruptScriptToPreventInfiniteRecursionWhenClosingPage(page)) return true; @@ -179,85 +186,79 @@ bool JSDOMWindowBase::shouldInterruptScriptBeforeTimeout(const JSGlobalObject* o return JSGlobalObject::shouldInterruptScriptBeforeTimeout(object); } -bool JSDOMWindowBase::javaScriptExperimentsEnabled(const JSGlobalObject* object) +RuntimeFlags JSDOMWindowBase::javaScriptRuntimeFlags(const JSGlobalObject* object) { const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); - Frame* frame = thisObject->impl().frame(); + Frame* frame = thisObject->wrapped().frame(); if (!frame) - return false; - return frame->settings().javaScriptExperimentsEnabled(); + return RuntimeFlags(); + return frame->settings().javaScriptRuntimeFlags(); } -void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, PassRefPtr<Microtask> task) -{ - const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); - thisObject->scriptExecutionContext()->postTask(JSGlobalObjectTask::create((JSDOMWindowBase*)thisObject, task)); -} +class JSDOMWindowMicrotaskCallback : public RefCounted<JSDOMWindowMicrotaskCallback> { +public: + static Ref<JSDOMWindowMicrotaskCallback> create(JSDOMWindowBase* globalObject, Ref<JSC::Microtask>&& task) + { + return adoptRef(*new JSDOMWindowMicrotaskCallback(globalObject, WTFMove(task))); + } -void JSDOMWindowBase::willRemoveFromWindowShell() -{ - setCurrentEvent(0); -} + void call() + { + Ref<JSDOMWindowMicrotaskCallback> protectedThis(*this); + VM& vm = m_globalObject->vm(); + JSLockHolder lock(vm); + auto scope = DECLARE_THROW_SCOPE(vm); -JSDOMWindowShell* JSDOMWindowBase::shell() const -{ - return m_shell; -} + ExecState* exec = m_globalObject->globalExec(); -VM* JSDOMWindowBase::commonVM() -{ - ASSERT(isMainThread()); + JSMainThreadExecState::runTask(exec, m_task); -#if !PLATFORM(IOS) - static VM* vm = 0; -#else - VM*& vm = commonVMInternal(); -#endif - if (!vm) { - ScriptController::initializeThreading(); - vm = VM::createLeaked(LargeHeap).leakRef(); -#if PLATFORM(IOS) - PassOwnPtr<WebSafeGCActivityCallback> activityCallback = WebSafeGCActivityCallback::create(&vm->heap); - vm->heap.setActivityCallback(activityCallback); - PassOwnPtr<WebSafeIncrementalSweeper> incrementalSweeper = WebSafeIncrementalSweeper::create(&vm->heap); - vm->heap.setIncrementalSweeper(incrementalSweeper); - vm->makeUsableFromMultipleThreads(); - vm->heap.machineThreads().addCurrentThread(); -#else - vm->exclusiveThread = currentThread(); -#endif // !PLATFORM(IOS) - initNormalWorldClientData(vm); + ASSERT_UNUSED(scope, !scope.exception()); } - return vm; +private: + JSDOMWindowMicrotaskCallback(JSDOMWindowBase* globalObject, Ref<JSC::Microtask>&& task) + : m_globalObject(globalObject->vm(), globalObject) + , m_task(WTFMove(task)) + { + } + + Strong<JSDOMWindowBase> m_globalObject; + Ref<JSC::Microtask> m_task; +}; + +void JSDOMWindowBase::queueTaskToEventLoop(const JSGlobalObject* object, Ref<JSC::Microtask>&& task) +{ + const JSDOMWindowBase* thisObject = static_cast<const JSDOMWindowBase*>(object); + + RefPtr<JSDOMWindowMicrotaskCallback> callback = JSDOMWindowMicrotaskCallback::create((JSDOMWindowBase*)thisObject, WTFMove(task)); + auto microtask = std::make_unique<ActiveDOMCallbackMicrotask>(MicrotaskQueue::mainThreadQueue(), *thisObject->scriptExecutionContext(), [callback]() mutable { + callback->call(); + }); + + MicrotaskQueue::mainThreadQueue().append(WTFMove(microtask)); } -#if PLATFORM(IOS) -bool JSDOMWindowBase::commonVMExists() +void JSDOMWindowBase::willRemoveFromWindowShell() { - return commonVMInternal(); + setCurrentEvent(0); } -VM*& JSDOMWindowBase::commonVMInternal() +JSDOMWindowShell* JSDOMWindowBase::shell() const { - ASSERT(isMainThread()); - static VM* commonVM; - return commonVM; + return m_shell; } -#endif // JSDOMGlobalObject* is ignored, accessing a window in any context will // use that DOMWindow's prototype chain. -JSValue toJS(ExecState* exec, JSDOMGlobalObject*, DOMWindow* domWindow) +JSValue toJS(ExecState* exec, JSDOMGlobalObject*, DOMWindow& domWindow) { return toJS(exec, domWindow); } -JSValue toJS(ExecState* exec, DOMWindow* domWindow) +JSValue toJS(ExecState* exec, DOMWindow& domWindow) { - if (!domWindow) - return jsNull(); - Frame* frame = domWindow->frame(); + Frame* frame = domWindow.frame(); if (!frame) return jsNull(); return frame->script().windowShell(currentWorld(exec)); @@ -270,16 +271,124 @@ JSDOMWindow* toJSDOMWindow(Frame* frame, DOMWrapperWorld& world) return frame->script().windowShell(world)->window(); } -JSDOMWindow* toJSDOMWindow(JSValue value) +JSDOMWindow* toJSDOMWindow(JSC::VM& vm, JSValue value) { if (!value.isObject()) return 0; - const ClassInfo* classInfo = asObject(value)->classInfo(); - if (classInfo == JSDOMWindow::info()) - return jsCast<JSDOMWindow*>(asObject(value)); - if (classInfo == JSDOMWindowShell::info()) - return jsCast<JSDOMWindowShell*>(asObject(value))->window(); + + while (!value.isNull()) { + JSObject* object = asObject(value); + const ClassInfo* classInfo = object->classInfo(vm); + if (classInfo == JSDOMWindow::info()) + return jsCast<JSDOMWindow*>(object); + if (classInfo == JSDOMWindowShell::info()) + return jsCast<JSDOMWindowShell*>(object)->window(); + value = object->getPrototypeDirect(); + } return 0; } +DOMWindow& callerDOMWindow(ExecState* exec) +{ + class GetCallerGlobalObjectFunctor { + public: + GetCallerGlobalObjectFunctor() = default; + + StackVisitor::Status operator()(StackVisitor& visitor) const + { + if (!m_hasSkippedFirstFrame) { + m_hasSkippedFirstFrame = true; + return StackVisitor::Continue; + } + + if (auto* codeBlock = visitor->codeBlock()) + m_globalObject = codeBlock->globalObject(); + else { + ASSERT(visitor->callee()); + // FIXME: Callee is not an object if the caller is Web Assembly. + // Figure out what to do here. We can probably get the global object + // from the top-most Wasm Instance. https://bugs.webkit.org/show_bug.cgi?id=165721 + if (visitor->callee()->isObject()) + m_globalObject = jsCast<JSObject*>(visitor->callee())->globalObject(); + } + return StackVisitor::Done; + } + + JSGlobalObject* globalObject() const { return m_globalObject; } + + private: + mutable bool m_hasSkippedFirstFrame { false }; + mutable JSGlobalObject* m_globalObject { nullptr }; + }; + + GetCallerGlobalObjectFunctor iter; + exec->iterate(iter); + return iter.globalObject() ? asJSDOMWindow(iter.globalObject())->wrapped() : firstDOMWindow(exec); +} + +DOMWindow& activeDOMWindow(ExecState* exec) +{ + return asJSDOMWindow(exec->lexicalGlobalObject())->wrapped(); +} + +DOMWindow& firstDOMWindow(ExecState* exec) +{ + return asJSDOMWindow(exec->vmEntryGlobalObject())->wrapped(); +} + +void JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(DOMWindow* window) +{ + JSC::VM& vm = commonVM(); + JSVMClientData* clientData = static_cast<JSVMClientData*>(vm.clientData); + Vector<Ref<DOMWrapperWorld>> wrapperWorlds; + clientData->getAllWorlds(wrapperWorlds); + for (unsigned i = 0; i < wrapperWorlds.size(); ++i) { + DOMObjectWrapperMap& wrappers = wrapperWorlds[i]->m_wrappers; + auto result = wrappers.find(window); + if (result == wrappers.end()) + continue; + JSC::JSObject* wrapper = result->value.get(); + if (!wrapper) + continue; + JSDOMWindowBase* jsWindow = JSC::jsCast<JSDOMWindowBase*>(wrapper); + jsWindow->m_windowCloseWatchpoints.fireAll(vm, "Frame cleared"); + } +} + + +JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderResolve(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSModuleLoader* moduleLoader, JSC::JSValue moduleName, JSC::JSValue importerModuleKey, JSC::JSValue scriptFetcher) +{ + JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject); + if (RefPtr<Document> document = thisObject->wrapped().document()) + return document->moduleLoader()->resolve(globalObject, exec, moduleLoader, moduleName, importerModuleKey, scriptFetcher); + JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject); + return deferred->reject(exec, jsUndefined()); +} + +JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderFetch(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSModuleLoader* moduleLoader, JSC::JSValue moduleKey, JSC::JSValue scriptFetcher) +{ + JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject); + if (RefPtr<Document> document = thisObject->wrapped().document()) + return document->moduleLoader()->fetch(globalObject, exec, moduleLoader, moduleKey, scriptFetcher); + JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject); + return deferred->reject(exec, jsUndefined()); +} + +JSC::JSValue JSDOMWindowBase::moduleLoaderEvaluate(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSModuleLoader* moduleLoader, JSC::JSValue moduleKey, JSC::JSValue moduleRecord, JSC::JSValue scriptFetcher) +{ + JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject); + if (RefPtr<Document> document = thisObject->wrapped().document()) + return document->moduleLoader()->evaluate(globalObject, exec, moduleLoader, moduleKey, moduleRecord, scriptFetcher); + return JSC::jsUndefined(); +} + +JSC::JSInternalPromise* JSDOMWindowBase::moduleLoaderImportModule(JSC::JSGlobalObject* globalObject, JSC::ExecState* exec, JSC::JSModuleLoader* moduleLoader, JSC::JSString* moduleName, const JSC::SourceOrigin& sourceOrigin) +{ + JSDOMWindowBase* thisObject = JSC::jsCast<JSDOMWindowBase*>(globalObject); + if (RefPtr<Document> document = thisObject->wrapped().document()) + return document->moduleLoader()->importModule(globalObject, exec, moduleLoader, moduleName, sourceOrigin); + JSC::JSInternalPromiseDeferred* deferred = JSC::JSInternalPromiseDeferred::create(exec, globalObject); + return deferred->reject(exec, jsUndefined()); +} + } // namespace WebCore |