1// Copyright 2013 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/child/web_database_observer_impl.h"
6
7#include "base/metrics/histogram.h"
8#include "base/strings/string16.h"
9#include "content/common/database_messages.h"
10#include "third_party/WebKit/public/platform/WebCString.h"
11#include "third_party/WebKit/public/platform/WebString.h"
12#include "third_party/WebKit/public/web/WebDatabase.h"
13#include "third_party/sqlite/sqlite3.h"
14
15using WebKit::WebDatabase;
16
17namespace content {
18namespace {
19
20const int kResultHistogramSize = 50;
21const int kCallsiteHistogramSize = 10;
22
23int DetermineHistogramResult(int websql_error, int sqlite_error) {
24  // If we have a sqlite error, log it after trimming the extended bits.
25  // There are 26 possible values, but we leave room for some new ones.
26  if (sqlite_error)
27    return std::min(sqlite_error & 0xff, 30);
28
29  // Otherwise, websql_error may be an SQLExceptionCode, SQLErrorCode
30  // or a DOMExceptionCode, or -1 for success.
31  if (websql_error == -1)
32    return 0;  // no error
33
34  // SQLExceptionCode starts at 1000
35  if (websql_error >= 1000)
36    websql_error -= 1000;
37
38  return std::min(websql_error + 30, kResultHistogramSize - 1);
39}
40
41#define HISTOGRAM_WEBSQL_RESULT(name, database, callsite, \
42                                websql_error, sqlite_error) \
43  do { \
44    DCHECK(callsite < kCallsiteHistogramSize); \
45    int result = DetermineHistogramResult(websql_error, sqlite_error); \
46    if (database.isSyncDatabase()) { \
47      UMA_HISTOGRAM_ENUMERATION("websql.Sync." name, \
48                                result, kResultHistogramSize); \
49      if (result) { \
50        UMA_HISTOGRAM_ENUMERATION("websql.Sync." name ".ErrorSite", \
51                                  callsite, kCallsiteHistogramSize); \
52      } \
53    } else { \
54      UMA_HISTOGRAM_ENUMERATION("websql.Async." name, \
55                                result, kResultHistogramSize); \
56      if (result) { \
57        UMA_HISTOGRAM_ENUMERATION("websql.Async." name ".ErrorSite", \
58                                  callsite, kCallsiteHistogramSize); \
59      } \
60    } \
61  } while (0)
62
63}  // namespace
64
65WebDatabaseObserverImpl::WebDatabaseObserverImpl(
66    IPC::SyncMessageFilter* sender)
67    : sender_(sender),
68      open_connections_(new webkit_database::DatabaseConnectionsWrapper) {
69  DCHECK(sender);
70}
71
72WebDatabaseObserverImpl::~WebDatabaseObserverImpl() {
73}
74
75void WebDatabaseObserverImpl::databaseOpened(
76    const WebDatabase& database) {
77  std::string origin_identifier =
78      database.securityOrigin().databaseIdentifier().utf8();
79  string16 database_name = database.name();
80  open_connections_->AddOpenConnection(origin_identifier, database_name);
81  sender_->Send(new DatabaseHostMsg_Opened(
82      origin_identifier, database_name,
83      database.displayName(), database.estimatedSize()));
84}
85
86void WebDatabaseObserverImpl::databaseModified(
87    const WebDatabase& database) {
88  sender_->Send(new DatabaseHostMsg_Modified(
89      database.securityOrigin().databaseIdentifier().utf8(), database.name()));
90}
91
92void WebDatabaseObserverImpl::databaseClosed(
93    const WebDatabase& database) {
94  std::string origin_identifier =
95      database.securityOrigin().databaseIdentifier().utf8();
96  string16 database_name = database.name();
97  sender_->Send(new DatabaseHostMsg_Closed(
98      origin_identifier, database_name));
99  open_connections_->RemoveOpenConnection(origin_identifier, database_name);
100}
101
102void WebDatabaseObserverImpl::reportOpenDatabaseResult(
103    const WebDatabase& database, int callsite, int websql_error,
104    int sqlite_error) {
105  HISTOGRAM_WEBSQL_RESULT("OpenResult", database, callsite,
106                          websql_error, sqlite_error);
107  HandleSqliteError(database, sqlite_error);
108}
109
110void WebDatabaseObserverImpl::reportChangeVersionResult(
111    const WebDatabase& database, int callsite, int websql_error,
112    int sqlite_error) {
113  HISTOGRAM_WEBSQL_RESULT("ChangeVersionResult", database, callsite,
114                          websql_error, sqlite_error);
115  HandleSqliteError(database, sqlite_error);
116}
117
118void WebDatabaseObserverImpl::reportStartTransactionResult(
119    const WebDatabase& database, int callsite, int websql_error,
120    int sqlite_error) {
121  HISTOGRAM_WEBSQL_RESULT("BeginResult", database, callsite,
122                          websql_error, sqlite_error);
123  HandleSqliteError(database, sqlite_error);
124}
125
126void WebDatabaseObserverImpl::reportCommitTransactionResult(
127    const WebDatabase& database, int callsite, int websql_error,
128    int sqlite_error) {
129  HISTOGRAM_WEBSQL_RESULT("CommitResult", database, callsite,
130                          websql_error, sqlite_error);
131  HandleSqliteError(database, sqlite_error);
132}
133
134void WebDatabaseObserverImpl::reportExecuteStatementResult(
135    const WebDatabase& database, int callsite, int websql_error,
136    int sqlite_error) {
137  HISTOGRAM_WEBSQL_RESULT("StatementResult", database, callsite,
138                          websql_error, sqlite_error);
139  HandleSqliteError(database, sqlite_error);
140}
141
142void WebDatabaseObserverImpl::reportVacuumDatabaseResult(
143    const WebDatabase& database, int sqlite_error) {
144  int result = DetermineHistogramResult(-1, sqlite_error);
145  if (database.isSyncDatabase()) {
146    UMA_HISTOGRAM_ENUMERATION("websql.Sync.VacuumResult",
147                              result, kResultHistogramSize);
148  } else {
149    UMA_HISTOGRAM_ENUMERATION("websql.Async.VacuumResult",
150                              result, kResultHistogramSize);
151  }
152  HandleSqliteError(database, sqlite_error);
153}
154
155void WebDatabaseObserverImpl::WaitForAllDatabasesToClose() {
156  open_connections_->WaitForAllDatabasesToClose();
157}
158
159void WebDatabaseObserverImpl::HandleSqliteError(
160    const WebDatabase& database, int error) {
161  // We filter out errors which the backend doesn't act on to avoid
162  // a unnecessary ipc traffic, this method can get called at a fairly
163  // high frequency (per-sqlstatement).
164  if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) {
165    sender_->Send(new DatabaseHostMsg_HandleSqliteError(
166        database.securityOrigin().databaseIdentifier().utf8(),
167        database.name(),
168        error));
169  }
170}
171
172}  // namespace content
173