blob: 9018d23d23ca1e947aed2993d184f9102db7189b [file] [log] [blame]
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
* Copyright (C) 2006-2018 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 "WindowProxy.h"
#include "CommonVM.h"
#include "Frame.h"
#include "GCController.h"
#include "JSWindowProxy.h"
#include "Page.h"
#include "PageConsoleClient.h"
#include "PageGroup.h"
#include "RemoteFrame.h"
#include "ScriptController.h"
#include "runtime_root.h"
#include <JavaScriptCore/JSLock.h>
#include <wtf/MemoryPressureHandler.h>
namespace WebCore {
static void collectGarbageAfterWindowProxyDestruction()
{
// 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();
}
WindowProxy::WindowProxy(AbstractFrame& frame)
: m_frame(frame)
{
}
WindowProxy::~WindowProxy()
{
// It's likely that destroying windowProxies will create a lot of garbage.
if (!m_jsWindowProxies.isEmpty()) {
while (!m_jsWindowProxies.isEmpty()) {
auto it = m_jsWindowProxies.begin();
it->value->window()->setConsoleClient(nullptr);
destroyJSWindowProxy(*it->key);
}
collectGarbageAfterWindowProxyDestruction();
}
}
void WindowProxy::destroyJSWindowProxy(DOMWrapperWorld& world)
{
ASSERT(m_jsWindowProxies.contains(&world));
m_jsWindowProxies.remove(&world);
world.didDestroyWindowProxy(this);
}
JSWindowProxy& WindowProxy::createJSWindowProxy(DOMWrapperWorld& world)
{
ASSERT(!m_jsWindowProxies.contains(&world));
ASSERT(m_frame.window());
VM& vm = world.vm();
Strong<JSWindowProxy> jsWindowProxy(vm, &JSWindowProxy::create(vm, *m_frame.window(), world));
Strong<JSWindowProxy> jsWindowProxy2(jsWindowProxy);
m_jsWindowProxies.add(&world, jsWindowProxy);
world.didCreateWindowProxy(this);
return *jsWindowProxy.get();
}
Vector<JSC::Strong<JSWindowProxy>> WindowProxy::jsWindowProxiesAsVector() const
{
return copyToVector(m_jsWindowProxies.values());
}
JSDOMGlobalObject* WindowProxy::globalObject(DOMWrapperWorld& world)
{
return jsWindowProxy(world).window();
}
JSWindowProxy& WindowProxy::createJSWindowProxyWithInitializedScript(DOMWrapperWorld& world)
{
JSLockHolder lock(world.vm());
auto& windowProxy = createJSWindowProxy(world);
if (is<Frame>(m_frame))
downcast<Frame>(m_frame).script().initScriptForWindowProxy(windowProxy);
return windowProxy;
}
void WindowProxy::clearJSWindowProxiesNotMatchingDOMWindow(AbstractDOMWindow* newDOMWindow, bool goingIntoPageCache)
{
if (m_jsWindowProxies.isEmpty())
return;
JSLockHolder lock(commonVM());
for (auto& windowProxy : jsWindowProxiesAsVector()) {
if (&windowProxy->wrapped() == newDOMWindow)
continue;
// Clear the debugger and console from the current window before setting the new window.
windowProxy->attachDebugger(nullptr);
windowProxy->window()->setConsoleClient(nullptr);
if (auto* jsDOMWindow = jsDynamicCast<JSDOMWindowBase*>(*windowProxy->vm(), windowProxy->window()))
jsDOMWindow->willRemoveFromWindowProxy();
}
// It's likely that resetting our windows created a lot of garbage, unless
// it went in a back/forward cache.
if (!goingIntoPageCache)
collectGarbageAfterWindowProxyDestruction();
}
void WindowProxy::setDOMWindow(AbstractDOMWindow* newDOMWindow)
{
ASSERT(newDOMWindow);
if (m_jsWindowProxies.isEmpty())
return;
JSLockHolder lock(commonVM());
for (auto& windowProxy : jsWindowProxiesAsVector()) {
if (&windowProxy->wrapped() == newDOMWindow)
continue;
windowProxy->setWindow(*newDOMWindow);
ScriptController* scriptController = nullptr;
Page* page = nullptr;
if (is<Frame>(m_frame)) {
auto& frame = downcast<Frame>(m_frame);
scriptController = &frame.script();
page = frame.page();
}
// ScriptController's m_cacheableBindingRootObject persists between page navigations
// so needs to know about the new JSDOMWindow.
if (auto* cacheableBindingRootObject = scriptController ? scriptController->existingCacheableBindingRootObject() : nullptr)
cacheableBindingRootObject->updateGlobalObject(windowProxy->window());
windowProxy->attachDebugger(page ? page->debugger() : nullptr);
if (page)
windowProxy->window()->setProfileGroup(page->group().identifier());
windowProxy->window()->setConsoleClient(page ? &page->console() : nullptr);
}
}
void WindowProxy::attachDebugger(JSC::Debugger* debugger)
{
for (auto& windowProxy : m_jsWindowProxies.values())
windowProxy->attachDebugger(debugger);
}
AbstractDOMWindow* WindowProxy::window() const
{
return m_frame.window();
}
void WindowProxy::ref()
{
m_frame.ref();
}
void WindowProxy::deref()
{
m_frame.deref();
}
} // namespace WebCore