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 COMPUTER, 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 COMPUTER, 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 "modules/webdatabase/DatabaseContext.h" 30 31 #include "core/dom/Document.h" 32 #include "core/dom/ScriptExecutionContext.h" 33 #include "core/page/Chrome.h" 34 #include "core/page/ChromeClient.h" 35 #include "core/page/Page.h" 36 #include "core/page/Settings.h" 37 #include "modules/webdatabase/Database.h" 38 #include "modules/webdatabase/DatabaseBackendContext.h" 39 #include "modules/webdatabase/DatabaseManager.h" 40 #include "modules/webdatabase/DatabaseTask.h" 41 #include "modules/webdatabase/DatabaseThread.h" 42 #include "weborigin/SchemeRegistry.h" 43 #include "weborigin/SecurityOrigin.h" 44 45 namespace WebCore { 46 47 // How the DatabaseContext Life-Cycle works? 48 // ======================================== 49 // ... in other words, who's keeping the DatabaseContext alive and how long does 50 // it need to stay alive? 51 // 52 // The DatabaseContext is referenced from RefPtrs in: 53 // 1. ScriptExecutionContext 54 // 2. Database 55 // 56 // At Birth: 57 // ======== 58 // We create a DatabaseContext only when there is a need i.e. the script tries to 59 // open a Database via DatabaseManager::openDatabase(). 60 // 61 // The DatabaseContext constructor will call setDatabaseContext() on the 62 // the ScriptExecutionContext. This sets the RefPtr in the ScriptExecutionContext 63 // for keeping the DatabaseContext alive. Since the DatabaseContext is only 64 // created from the script thread, it is safe for the constructor to call 65 // ScriptExecutionContext::setDatabaseContext(). 66 // 67 // Once a DatabaseContext is associated with a ScriptExecutionContext, it will 68 // live until after the ScriptExecutionContext destructs. This is true even if 69 // we don't succeed in opening any Databases for that context. When we do 70 // succeed in opening Databases for this ScriptExecutionContext, the Database 71 // will re-use the same DatabaseContext. 72 // 73 // At Shutdown: 74 // =========== 75 // During shutdown, the DatabaseContext needs to: 76 // 1. "outlive" the ScriptExecutionContext. 77 // - This is needed because the DatabaseContext needs to remove itself from the 78 // ScriptExecutionContext's ActiveDOMObject list and ContextLifecycleObserver 79 // list. This removal needs to be executed on the script's thread. Hence, we 80 // rely on the ScriptExecutionContext's shutdown process to call 81 // stop() and contextDestroyed() to give us a chance to clean these up from 82 // the script thread. 83 // 84 // 2. "outlive" the Databases. 85 // - This is because they may make use of the DatabaseContext to execute a close 86 // task and shutdown in an orderly manner. When the Databases are destructed, 87 // they will deref the DatabaseContext from the DatabaseThread. 88 // 89 // During shutdown, the ScriptExecutionContext is shutting down on the script thread 90 // while the Databases are shutting down on the DatabaseThread. Hence, there can be 91 // a race condition as to whether the ScriptExecutionContext or the Databases 92 // destruct first. 93 // 94 // The RefPtrs in the Databases and ScriptExecutionContext will ensure that the 95 // DatabaseContext will outlive both regardless of which of the 2 destructs first. 96 97 98 DatabaseContext::DatabaseContext(ScriptExecutionContext* context) 99 : ActiveDOMObject(context) 100 , m_hasOpenDatabases(false) 101 , m_isRegistered(true) // will register on construction below. 102 , m_hasRequestedTermination(false) 103 { 104 // ActiveDOMObject expects this to be called to set internal flags. 105 suspendIfNeeded(); 106 107 context->setDatabaseContext(this); 108 109 // For debug accounting only. We must do this before we register the 110 // instance. The assertions assume this. 111 DatabaseManager::manager().didConstructDatabaseContext(); 112 113 DatabaseManager::manager().registerDatabaseContext(this); 114 } 115 116 DatabaseContext::~DatabaseContext() 117 { 118 stopDatabases(); 119 ASSERT(!m_databaseThread || m_databaseThread->terminationRequested()); 120 121 // For debug accounting only. We must call this last. The assertions assume 122 // this. 123 DatabaseManager::manager().didDestructDatabaseContext(); 124 } 125 126 // This is called if the associated ScriptExecutionContext is destructing while 127 // we're still associated with it. That's our cue to disassociate and shutdown. 128 // To do this, we stop the database and let everything shutdown naturally 129 // because the database closing process may still make use of this context. 130 // It is not safe to just delete the context here. 131 void DatabaseContext::contextDestroyed() 132 { 133 stopDatabases(); 134 ActiveDOMObject::contextDestroyed(); 135 } 136 137 // stop() is from stopActiveDOMObjects() which indicates that the owner Frame 138 // or WorkerThread is shutting down. Initiate the orderly shutdown by stopping 139 // the associated databases. 140 void DatabaseContext::stop() 141 { 142 stopDatabases(); 143 } 144 145 PassRefPtr<DatabaseBackendContext> DatabaseContext::backend() 146 { 147 DatabaseBackendContext* backend = static_cast<DatabaseBackendContext*>(this); 148 return backend; 149 } 150 151 DatabaseThread* DatabaseContext::databaseThread() 152 { 153 if (!m_databaseThread && !m_hasOpenDatabases) { 154 // It's OK to ask for the m_databaseThread after we've requested 155 // termination because we're still using it to execute the closing 156 // of the database. However, it is NOT OK to create a new thread 157 // after we've requested termination. 158 ASSERT(!m_hasRequestedTermination); 159 160 // Create the database thread on first request - but not if at least one database was already opened, 161 // because in that case we already had a database thread and terminated it and should not create another. 162 m_databaseThread = DatabaseThread::create(); 163 if (!m_databaseThread->start()) 164 m_databaseThread = 0; 165 } 166 167 return m_databaseThread.get(); 168 } 169 170 bool DatabaseContext::stopDatabases(DatabaseTaskSynchronizer* cleanupSync) 171 { 172 if (m_isRegistered) { 173 DatabaseManager::manager().unregisterDatabaseContext(this); 174 m_isRegistered = false; 175 } 176 177 // Though we initiate termination of the DatabaseThread here in 178 // stopDatabases(), we can't clear the m_databaseThread ref till we get to 179 // the destructor. This is because the Databases that are managed by 180 // DatabaseThread still rely on this ref between the context and the thread 181 // to execute the task for closing the database. By the time we get to the 182 // destructor, we're guaranteed that the databases are destructed (which is 183 // why our ref count is 0 then and we're destructing). Then, the 184 // m_databaseThread RefPtr destructor will deref and delete the 185 // DatabaseThread. 186 187 if (m_databaseThread && !m_hasRequestedTermination) { 188 m_databaseThread->requestTermination(cleanupSync); 189 m_hasRequestedTermination = true; 190 return true; 191 } 192 return false; 193 } 194 195 bool DatabaseContext::allowDatabaseAccess() const 196 { 197 if (scriptExecutionContext()->isDocument()) { 198 Document* document = toDocument(scriptExecutionContext()); 199 return document->page(); 200 } 201 ASSERT(scriptExecutionContext()->isWorkerGlobalScope()); 202 // allowDatabaseAccess is not yet implemented for workers. 203 return true; 204 } 205 206 } // namespace WebCore 207