Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / database_message_filter.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/database_message_filter.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/threading/thread.h"
13 #include "content/common/database_messages.h"
14 #include "content/public/browser/user_metrics.h"
15 #include "content/public/common/result_codes.h"
16 #include "storage/browser/database/database_util.h"
17 #include "storage/browser/database/vfs_backend.h"
18 #include "storage/browser/quota/quota_manager.h"
19 #include "storage/browser/quota/quota_manager_proxy.h"
20 #include "storage/common/database/database_identifier.h"
21 #include "third_party/sqlite/sqlite3.h"
22
23 #if defined(OS_POSIX)
24 #include "base/file_descriptor_posix.h"
25 #endif
26
27 using storage::QuotaManager;
28 using storage::QuotaStatusCode;
29 using storage::DatabaseTracker;
30 using storage::DatabaseUtil;
31 using storage::VfsBackend;
32
33 namespace content {
34 namespace {
35
36 const int kNumDeleteRetries = 2;
37 const int kDelayDeleteRetryMs = 100;
38
39 }  // namespace
40
41 DatabaseMessageFilter::DatabaseMessageFilter(
42     storage::DatabaseTracker* db_tracker)
43     : BrowserMessageFilter(DatabaseMsgStart),
44       db_tracker_(db_tracker),
45       observer_added_(false) {
46   DCHECK(db_tracker_.get());
47 }
48
49 void DatabaseMessageFilter::OnChannelClosing() {
50   if (observer_added_) {
51     observer_added_ = false;
52     BrowserThread::PostTask(
53         BrowserThread::FILE, FROM_HERE,
54         base::Bind(&DatabaseMessageFilter::RemoveObserver, this));
55   }
56 }
57
58 void DatabaseMessageFilter::AddObserver() {
59   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
60   db_tracker_->AddObserver(this);
61 }
62
63 void DatabaseMessageFilter::RemoveObserver() {
64   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
65   db_tracker_->RemoveObserver(this);
66
67   // If the renderer process died without closing all databases,
68   // then we need to manually close those connections
69   db_tracker_->CloseDatabases(database_connections_);
70   database_connections_.RemoveAllConnections();
71 }
72
73 void DatabaseMessageFilter::OverrideThreadForMessage(
74     const IPC::Message& message,
75     BrowserThread::ID* thread) {
76   if (message.type() == DatabaseHostMsg_GetSpaceAvailable::ID)
77     *thread = BrowserThread::IO;
78   else if (IPC_MESSAGE_CLASS(message) == DatabaseMsgStart)
79     *thread = BrowserThread::FILE;
80
81   if (message.type() == DatabaseHostMsg_Opened::ID && !observer_added_) {
82     observer_added_ = true;
83     BrowserThread::PostTask(
84         BrowserThread::FILE, FROM_HERE,
85         base::Bind(&DatabaseMessageFilter::AddObserver, this));
86   }
87 }
88
89 bool DatabaseMessageFilter::OnMessageReceived(const IPC::Message& message) {
90   bool handled = true;
91   IPC_BEGIN_MESSAGE_MAP(DatabaseMessageFilter, message)
92     IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile,
93                                     OnDatabaseOpenFile)
94     IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile,
95                                     OnDatabaseDeleteFile)
96     IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileAttributes,
97                                     OnDatabaseGetFileAttributes)
98     IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileSize,
99                                     OnDatabaseGetFileSize)
100     IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetSpaceAvailable,
101                                     OnDatabaseGetSpaceAvailable)
102     IPC_MESSAGE_HANDLER(DatabaseHostMsg_Opened, OnDatabaseOpened)
103     IPC_MESSAGE_HANDLER(DatabaseHostMsg_Modified, OnDatabaseModified)
104     IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed)
105     IPC_MESSAGE_HANDLER(DatabaseHostMsg_HandleSqliteError, OnHandleSqliteError)
106     IPC_MESSAGE_UNHANDLED(handled = false)
107   IPC_END_MESSAGE_MAP()
108   return handled;
109 }
110
111 DatabaseMessageFilter::~DatabaseMessageFilter() {
112 }
113
114 void DatabaseMessageFilter::OnDatabaseOpenFile(
115     const base::string16& vfs_file_name,
116     int desired_flags,
117     IPC::Message* reply_msg) {
118   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
119   base::File file;
120   const base::File* tracked_file = NULL;
121   std::string origin_identifier;
122   base::string16 database_name;
123
124   // When in incognito mode, we want to make sure that all DB files are
125   // removed when the incognito browser context goes away, so we add the
126   // SQLITE_OPEN_DELETEONCLOSE flag when opening all files, and keep
127   // open handles to them in the database tracker to make sure they're
128   // around for as long as needed.
129   if (vfs_file_name.empty()) {
130     file = VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
131                                                desired_flags);
132   } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier,
133                                             &database_name, NULL) &&
134              !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier,
135                                                           database_name)) {
136     base::FilePath db_file = DatabaseUtil::GetFullFilePathForVfsFile(
137         db_tracker_.get(), vfs_file_name);
138     if (!db_file.empty()) {
139       if (db_tracker_->IsIncognitoProfile()) {
140         tracked_file = db_tracker_->GetIncognitoFile(vfs_file_name);
141         if (!tracked_file) {
142           file =
143               VfsBackend::OpenFile(db_file,
144                                    desired_flags | SQLITE_OPEN_DELETEONCLOSE);
145           if (!(desired_flags & SQLITE_OPEN_DELETEONCLOSE)) {
146             tracked_file = db_tracker_->SaveIncognitoFile(vfs_file_name,
147                                                           file.Pass());
148           }
149         }
150       } else {
151         file = VfsBackend::OpenFile(db_file, desired_flags);
152       }
153     }
154   }
155
156   // Then we duplicate the file handle to make it useable in the renderer
157   // process. The original handle is closed, unless we saved it in the
158   // database tracker.
159   IPC::PlatformFileForTransit target_handle =
160       IPC::InvalidPlatformFileForTransit();
161   if (file.IsValid()) {
162     target_handle = IPC::TakeFileHandleForProcess(file.Pass(), PeerHandle());
163   } else if (tracked_file) {
164     DCHECK(tracked_file->IsValid());
165     target_handle =
166         IPC::GetFileHandleForProcess(tracked_file->GetPlatformFile(),
167                                      PeerHandle(), false);
168   }
169
170   DatabaseHostMsg_OpenFile::WriteReplyParams(reply_msg, target_handle);
171   Send(reply_msg);
172 }
173
174 void DatabaseMessageFilter::OnDatabaseDeleteFile(
175     const base::string16& vfs_file_name,
176     const bool& sync_dir,
177     IPC::Message* reply_msg) {
178   DatabaseDeleteFile(vfs_file_name, sync_dir, reply_msg, kNumDeleteRetries);
179 }
180
181 void DatabaseMessageFilter::DatabaseDeleteFile(
182     const base::string16& vfs_file_name,
183     bool sync_dir,
184     IPC::Message* reply_msg,
185     int reschedule_count) {
186   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
187
188   // Return an error if the file name is invalid or if the file could not
189   // be deleted after kNumDeleteRetries attempts.
190   int error_code = SQLITE_IOERR_DELETE;
191   base::FilePath db_file =
192       DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
193   if (!db_file.empty()) {
194     // In order to delete a journal file in incognito mode, we only need to
195     // close the open handle to it that's stored in the database tracker.
196     if (db_tracker_->IsIncognitoProfile()) {
197       const base::string16 wal_suffix(base::ASCIIToUTF16("-wal"));
198       base::string16 sqlite_suffix;
199
200       // WAL files can be deleted without having previously been opened.
201       if (!db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name) &&
202           DatabaseUtil::CrackVfsFileName(vfs_file_name,
203                                          NULL, NULL, &sqlite_suffix) &&
204           sqlite_suffix == wal_suffix) {
205         error_code = SQLITE_OK;
206       } else {
207         db_tracker_->CloseIncognitoFileHandle(vfs_file_name);
208         error_code = SQLITE_OK;
209       }
210     } else {
211       error_code = VfsBackend::DeleteFile(db_file, sync_dir);
212     }
213
214     if ((error_code == SQLITE_IOERR_DELETE) && reschedule_count) {
215       // If the file could not be deleted, try again.
216       BrowserThread::PostDelayedTask(
217           BrowserThread::FILE, FROM_HERE,
218           base::Bind(&DatabaseMessageFilter::DatabaseDeleteFile, this,
219                      vfs_file_name, sync_dir, reply_msg, reschedule_count - 1),
220           base::TimeDelta::FromMilliseconds(kDelayDeleteRetryMs));
221       return;
222     }
223   }
224
225   DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code);
226   Send(reply_msg);
227 }
228
229 void DatabaseMessageFilter::OnDatabaseGetFileAttributes(
230     const base::string16& vfs_file_name,
231     IPC::Message* reply_msg) {
232   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
233   int32 attributes = -1;
234   base::FilePath db_file =
235       DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
236   if (!db_file.empty())
237     attributes = VfsBackend::GetFileAttributes(db_file);
238
239   DatabaseHostMsg_GetFileAttributes::WriteReplyParams(
240       reply_msg, attributes);
241   Send(reply_msg);
242 }
243
244 void DatabaseMessageFilter::OnDatabaseGetFileSize(
245     const base::string16& vfs_file_name, IPC::Message* reply_msg) {
246   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
247   int64 size = 0;
248   base::FilePath db_file =
249       DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_.get(), vfs_file_name);
250   if (!db_file.empty())
251     size = VfsBackend::GetFileSize(db_file);
252
253   DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size);
254   Send(reply_msg);
255 }
256
257 void DatabaseMessageFilter::OnDatabaseGetSpaceAvailable(
258     const std::string& origin_identifier, IPC::Message* reply_msg) {
259   DCHECK_CURRENTLY_ON(BrowserThread::IO);
260   DCHECK(db_tracker_->quota_manager_proxy());
261
262   QuotaManager* quota_manager =
263       db_tracker_->quota_manager_proxy()->quota_manager();
264   if (!quota_manager) {
265     NOTREACHED();  // The system is shutting down, messages are unexpected.
266     DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(
267         reply_msg, static_cast<int64>(0));
268     Send(reply_msg);
269     return;
270   }
271
272   quota_manager->GetUsageAndQuota(
273       storage::GetOriginFromIdentifier(origin_identifier),
274       storage::kStorageTypeTemporary,
275       base::Bind(
276           &DatabaseMessageFilter::OnDatabaseGetUsageAndQuota, this, reply_msg));
277 }
278
279 void DatabaseMessageFilter::OnDatabaseGetUsageAndQuota(
280     IPC::Message* reply_msg,
281     storage::QuotaStatusCode status,
282     int64 usage,
283     int64 quota) {
284   int64 available = 0;
285   if ((status == storage::kQuotaStatusOk) && (usage < quota))
286     available = quota - usage;
287   DatabaseHostMsg_GetSpaceAvailable::WriteReplyParams(reply_msg, available);
288   Send(reply_msg);
289 }
290
291 void DatabaseMessageFilter::OnDatabaseOpened(
292     const std::string& origin_identifier,
293     const base::string16& database_name,
294     const base::string16& description,
295     int64 estimated_size) {
296   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
297
298   if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) {
299     RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
300     BadMessageReceived();
301     return;
302   }
303
304   int64 database_size = 0;
305   db_tracker_->DatabaseOpened(origin_identifier, database_name, description,
306                               estimated_size, &database_size);
307   database_connections_.AddConnection(origin_identifier, database_name);
308   Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
309                                   database_size));
310 }
311
312 void DatabaseMessageFilter::OnDatabaseModified(
313     const std::string& origin_identifier,
314     const base::string16& database_name) {
315   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
316   if (!database_connections_.IsDatabaseOpened(
317           origin_identifier, database_name)) {
318     RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
319     BadMessageReceived();
320     return;
321   }
322
323   db_tracker_->DatabaseModified(origin_identifier, database_name);
324 }
325
326 void DatabaseMessageFilter::OnDatabaseClosed(
327     const std::string& origin_identifier,
328     const base::string16& database_name) {
329   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
330   if (!database_connections_.IsDatabaseOpened(
331           origin_identifier, database_name)) {
332     RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
333     BadMessageReceived();
334     return;
335   }
336
337   database_connections_.RemoveConnection(origin_identifier, database_name);
338   db_tracker_->DatabaseClosed(origin_identifier, database_name);
339 }
340
341 void DatabaseMessageFilter::OnHandleSqliteError(
342     const std::string& origin_identifier,
343     const base::string16& database_name,
344     int error) {
345   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
346   if (!DatabaseUtil::IsValidOriginIdentifier(origin_identifier)) {
347     RecordAction(base::UserMetricsAction("BadMessageTerminate_DBMF"));
348     BadMessageReceived();
349     return;
350   }
351
352   db_tracker_->HandleSqliteError(origin_identifier, database_name, error);
353 }
354
355 void DatabaseMessageFilter::OnDatabaseSizeChanged(
356     const std::string& origin_identifier,
357     const base::string16& database_name,
358     int64 database_size) {
359   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
360   if (database_connections_.IsOriginUsed(origin_identifier)) {
361     Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
362                                     database_size));
363   }
364 }
365
366 void DatabaseMessageFilter::OnDatabaseScheduledForDeletion(
367     const std::string& origin_identifier,
368     const base::string16& database_name) {
369   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
370   Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name));
371 }
372
373 }  // namespace content