1/*
2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include "config.h"
22#include "WindowProxy.h"
23
24#include "CommonVM.h"
25#include "Frame.h"
26#include "GCController.h"
27#include "JSWindowProxy.h"
28#include "Page.h"
29#include "PageConsoleClient.h"
30#include "PageGroup.h"
31#include "RemoteFrame.h"
32#include "ScriptController.h"
33#include "runtime_root.h"
34#include <JavaScriptCore/JSLock.h>
35#include <JavaScriptCore/WeakGCMapInlines.h>
36#include <wtf/MemoryPressureHandler.h>
37
38namespace WebCore {
39
40using namespace JSC;
41
42static void collectGarbageAfterWindowProxyDestruction()
43{
44 // Make sure to GC Extra Soon(tm) during memory pressure conditions
45 // to soften high peaks of memory usage during navigation.
46 if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
47 // NOTE: We do the collection on next runloop to ensure that there's no pointer
48 // to the window object on the stack.
49 GCController::singleton().garbageCollectOnNextRunLoop();
50 } else
51 GCController::singleton().garbageCollectSoon();
52}
53
54WindowProxy::WindowProxy(AbstractFrame& frame)
55 : m_frame(&frame)
56{
57}
58
59WindowProxy::~WindowProxy()
60{
61 ASSERT(!m_frame);
62 ASSERT(m_jsWindowProxies.isEmpty());
63}
64
65void WindowProxy::detachFromFrame()
66{
67 ASSERT(m_frame);
68
69 m_frame = nullptr;
70
71 // It's likely that destroying windowProxies will create a lot of garbage.
72 if (!m_jsWindowProxies.isEmpty()) {
73 while (!m_jsWindowProxies.isEmpty()) {
74 auto it = m_jsWindowProxies.begin();
75 it->value->window()->setConsoleClient(nullptr);
76 destroyJSWindowProxy(*it->key);
77 }
78 collectGarbageAfterWindowProxyDestruction();
79 }
80}
81
82void WindowProxy::destroyJSWindowProxy(DOMWrapperWorld& world)
83{
84 ASSERT(m_jsWindowProxies.contains(&world));
85 m_jsWindowProxies.remove(&world);
86 world.didDestroyWindowProxy(this);
87}
88
89JSWindowProxy& WindowProxy::createJSWindowProxy(DOMWrapperWorld& world)
90{
91 ASSERT(m_frame);
92
93 ASSERT(!m_jsWindowProxies.contains(&world));
94 ASSERT(m_frame->window());
95
96 VM& vm = world.vm();
97
98 Strong<JSWindowProxy> jsWindowProxy(vm, &JSWindowProxy::create(vm, *m_frame->window(), world));
99 Strong<JSWindowProxy> jsWindowProxy2(jsWindowProxy);
100 m_jsWindowProxies.add(&world, jsWindowProxy);
101 world.didCreateWindowProxy(this);
102 return *jsWindowProxy.get();
103}
104
105Vector<JSC::Strong<JSWindowProxy>> WindowProxy::jsWindowProxiesAsVector() const
106{
107 return copyToVector(m_jsWindowProxies.values());
108}
109
110JSDOMGlobalObject* WindowProxy::globalObject(DOMWrapperWorld& world)
111{
112 if (auto* windowProxy = jsWindowProxy(world))
113 return windowProxy->window();
114 return nullptr;
115}
116
117JSWindowProxy& WindowProxy::createJSWindowProxyWithInitializedScript(DOMWrapperWorld& world)
118{
119 ASSERT(m_frame);
120
121 JSLockHolder lock(world.vm());
122 auto& windowProxy = createJSWindowProxy(world);
123 if (is<Frame>(*m_frame))
124 downcast<Frame>(*m_frame).script().initScriptForWindowProxy(windowProxy);
125 return windowProxy;
126}
127
128void WindowProxy::clearJSWindowProxiesNotMatchingDOMWindow(AbstractDOMWindow* newDOMWindow, bool goingIntoPageCache)
129{
130 if (m_jsWindowProxies.isEmpty())
131 return;
132
133 JSLockHolder lock(commonVM());
134
135 for (auto& windowProxy : jsWindowProxiesAsVector()) {
136 if (&windowProxy->wrapped() == newDOMWindow)
137 continue;
138
139 // Clear the debugger and console from the current window before setting the new window.
140 windowProxy->attachDebugger(nullptr);
141 windowProxy->window()->setConsoleClient(nullptr);
142 if (auto* jsDOMWindow = jsDynamicCast<JSDOMWindowBase*>(*windowProxy->vm(), windowProxy->window()))
143 jsDOMWindow->willRemoveFromWindowProxy();
144 }
145
146 // It's likely that resetting our windows created a lot of garbage, unless
147 // it went in a back/forward cache.
148 if (!goingIntoPageCache)
149 collectGarbageAfterWindowProxyDestruction();
150}
151
152void WindowProxy::setDOMWindow(AbstractDOMWindow* newDOMWindow)
153{
154 ASSERT(newDOMWindow);
155
156 if (m_jsWindowProxies.isEmpty())
157 return;
158
159 ASSERT(m_frame);
160
161 JSLockHolder lock(commonVM());
162
163 for (auto& windowProxy : jsWindowProxiesAsVector()) {
164 if (&windowProxy->wrapped() == newDOMWindow)
165 continue;
166
167 windowProxy->setWindow(*newDOMWindow);
168
169 ScriptController* scriptController = nullptr;
170 Page* page = nullptr;
171 if (is<Frame>(*m_frame)) {
172 auto& frame = downcast<Frame>(*m_frame);
173 scriptController = &frame.script();
174 page = frame.page();
175 }
176
177 // ScriptController's m_cacheableBindingRootObject persists between page navigations
178 // so needs to know about the new JSDOMWindow.
179 if (auto* cacheableBindingRootObject = scriptController ? scriptController->existingCacheableBindingRootObject() : nullptr)
180 cacheableBindingRootObject->updateGlobalObject(windowProxy->window());
181
182 windowProxy->attachDebugger(page ? page->debugger() : nullptr);
183 if (page)
184 windowProxy->window()->setProfileGroup(page->group().identifier());
185 windowProxy->window()->setConsoleClient(page ? &page->console() : nullptr);
186 }
187}
188
189void WindowProxy::attachDebugger(JSC::Debugger* debugger)
190{
191 for (auto& windowProxy : m_jsWindowProxies.values())
192 windowProxy->attachDebugger(debugger);
193}
194
195AbstractDOMWindow* WindowProxy::window() const
196{
197 return m_frame ? m_frame->window() : nullptr;
198}
199
200} // namespace WebCore
201