1/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2011 Google, Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#include "config.h"
29#include "DatabaseContext.h"
30
31#include "Chrome.h"
32#include "ChromeClient.h"
33#include "Database.h"
34#include "DatabaseManager.h"
35#include "DatabaseTask.h"
36#include "DatabaseThread.h"
37#include "Document.h"
38#include "Page.h"
39#include "SchemeRegistry.h"
40#include "ScriptExecutionContext.h"
41#include "SecurityOrigin.h"
42#include "SecurityOriginData.h"
43
44namespace WebCore {
45
46// How the DatabaseContext Life-Cycle works?
47// ========================================
48// ... in other words, who's keeping the DatabaseContext alive and how long does
49// it need to stay alive?
50//
51// The DatabaseContext is referenced from RefPtrs in:
52// 1. ScriptExecutionContext
53// 2. Database
54//
55// At Birth:
56// ========
57// We create a DatabaseContext only when there is a need i.e. the script tries to
58// open a Database via DatabaseManager::openDatabase().
59//
60// The DatabaseContext constructor will call setDatabaseContext() on the
61// the ScriptExecutionContext. This sets the RefPtr in the ScriptExecutionContext
62// for keeping the DatabaseContext alive. Since the DatabaseContext is only
63// created from the script thread, it is safe for the constructor to call
64// ScriptExecutionContext::setDatabaseContext().
65//
66// Once a DatabaseContext is associated with a ScriptExecutionContext, it will
67// live until after the ScriptExecutionContext destructs. This is true even if
68// we don't succeed in opening any Databases for that context. When we do
69// succeed in opening Databases for this ScriptExecutionContext, the Database
70// will re-use the same DatabaseContext.
71//
72// At Shutdown:
73// ===========
74// During shutdown, the DatabaseContext needs to:
75// 1. "outlive" the ScriptExecutionContext.
76// - This is needed because the DatabaseContext needs to remove itself from the
77// ScriptExecutionContext's ActiveDOMObject list and ContextDestructionObserver
78// list. This removal needs to be executed on the script's thread. Hence, we
79// rely on the ScriptExecutionContext's shutdown process to call
80// stop() and contextDestroyed() to give us a chance to clean these up from
81// the script thread.
82//
83// 2. "outlive" the Databases.
84// - This is because they may make use of the DatabaseContext to execute a close
85// task and shutdown in an orderly manner. When the Databases are destructed,
86// they will deref the DatabaseContext from the DatabaseThread.
87//
88// During shutdown, the ScriptExecutionContext is shutting down on the script thread
89// while the Databases are shutting down on the DatabaseThread. Hence, there can be
90// a race condition as to whether the ScriptExecutionContext or the Databases
91// destruct first.
92//
93// The RefPtrs in the Databases and ScriptExecutionContext will ensure that the
94// DatabaseContext will outlive both regardless of which of the 2 destructs first.
95
96
97DatabaseContext::DatabaseContext(ScriptExecutionContext& context)
98 : ActiveDOMObject(&context)
99{
100 // ActiveDOMObject expects this to be called to set internal flags.
101 suspendIfNeeded();
102
103 ASSERT(!context.databaseContext());
104 context.setDatabaseContext(this);
105}
106
107DatabaseContext::~DatabaseContext()
108{
109 stopDatabases();
110 ASSERT(!m_databaseThread || m_databaseThread->terminationRequested());
111 ASSERT(!scriptExecutionContext() || !scriptExecutionContext()->databaseContext());
112}
113
114// This is called if the associated ScriptExecutionContext is destroyed while
115// we're still associated with it. That's our cue to disassociate and shutdown.
116// To do this, we stop the database and let everything shut down naturally
117// because the database closing process might still make use of this context.
118// It is not safe to just delete the context here.
119void DatabaseContext::contextDestroyed()
120{
121 ActiveDOMObject::contextDestroyed();
122 stopDatabases();
123}
124
125// stop() is from stopActiveDOMObjects() which indicates that the owner Frame
126// or WorkerThread is shutting down. Initiate the orderly shutdown by stopping
127// the associated databases.
128void DatabaseContext::stop()
129{
130 stopDatabases();
131}
132
133bool DatabaseContext::canSuspendForDocumentSuspension() const
134{
135 if (!hasOpenDatabases() || !m_databaseThread)
136 return true;
137
138 return !m_databaseThread->hasPendingDatabaseActivity();
139}
140
141DatabaseThread* DatabaseContext::databaseThread()
142{
143 if (!m_databaseThread && !m_hasOpenDatabases) {
144 // It's OK to ask for the m_databaseThread after we've requested
145 // termination because we're still using it to execute the closing
146 // of the database. However, it is NOT OK to create a new thread
147 // after we've requested termination.
148 ASSERT(!m_hasRequestedTermination);
149
150 // Create the database thread on first request - but not if at least one database was already opened,
151 // because in that case we already had a database thread and terminated it and should not create another.
152 m_databaseThread = DatabaseThread::create();
153 m_databaseThread->start();
154 }
155
156 return m_databaseThread.get();
157}
158
159bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* synchronizer)
160{
161 // FIXME: What guarantees this is never called after the script execution context is null?
162 ASSERT(scriptExecutionContext());
163
164 // Though we initiate termination of the DatabaseThread here in
165 // stopDatabases(), we can't clear the m_databaseThread ref till we get to
166 // the destructor. This is because the Databases that are managed by
167 // DatabaseThread still rely on this ref between the context and the thread
168 // to execute the task for closing the database. By the time we get to the
169 // destructor, we're guaranteed that the databases are destructed (which is
170 // why our ref count is 0 then and we're destructing). Then, the
171 // m_databaseThread RefPtr destructor will deref and delete the
172 // DatabaseThread.
173 bool result = m_databaseThread && !m_hasRequestedTermination;
174 if (result) {
175 m_databaseThread->requestTermination(synchronizer);
176 m_hasRequestedTermination = true;
177 }
178
179 auto& context = *scriptExecutionContext();
180 if (context.databaseContext()) {
181 ASSERT(context.databaseContext() == this);
182 context.setDatabaseContext(nullptr);
183 }
184
185 return result;
186}
187
188bool DatabaseContext::allowDatabaseAccess() const
189{
190 if (is<Document>(*m_scriptExecutionContext)) {
191 Document& document = downcast<Document>(*m_scriptExecutionContext);
192 if (!document.page() || (document.page()->usesEphemeralSession() && !SchemeRegistry::allowsDatabaseAccessInPrivateBrowsing(document.securityOrigin().protocol())))
193 return false;
194 return true;
195 }
196 ASSERT(m_scriptExecutionContext->isWorkerGlobalScope());
197 // allowDatabaseAccess is not yet implemented for workers.
198 return true;
199}
200
201void DatabaseContext::databaseExceededQuota(const String& name, DatabaseDetails details)
202{
203 if (is<Document>(*m_scriptExecutionContext)) {
204 Document& document = downcast<Document>(*m_scriptExecutionContext);
205 if (Page* page = document.page())
206 page->chrome().client().exceededDatabaseQuota(*document.frame(), name, details);
207 return;
208 }
209 ASSERT(m_scriptExecutionContext->isWorkerGlobalScope());
210}
211
212const SecurityOriginData& DatabaseContext::securityOrigin() const
213{
214 return m_scriptExecutionContext->securityOrigin()->data();
215}
216
217bool DatabaseContext::isContextThread() const
218{
219 return m_scriptExecutionContext->isContextThread();
220}
221
222} // namespace WebCore
223