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/sqlite/sqlite3.h"
13
14using blink::WebString;
15
16namespace content {
17
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 UMA_HISTOGRAM_WEBSQL_RESULT(name, is_sync_database, \
42                                    callsite, websql_error, sqlite_error) \
43  do { \
44    DCHECK(callsite < kCallsiteHistogramSize); \
45    int result = DetermineHistogramResult(websql_error, sqlite_error); \
46    if (is_sync_database) { \
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(IPC::SyncMessageFilter* sender)
66    : sender_(sender),
67      open_connections_(new storage::DatabaseConnectionsWrapper) {
68  DCHECK(sender);
69}
70
71WebDatabaseObserverImpl::~WebDatabaseObserverImpl() {
72}
73
74void WebDatabaseObserverImpl::databaseOpened(
75    const WebString& origin_identifier,
76    const WebString& database_name,
77    const WebString& database_display_name,
78    unsigned long estimated_size) {
79  open_connections_->AddOpenConnection(origin_identifier.utf8(),
80                                       database_name);
81  sender_->Send(new DatabaseHostMsg_Opened(
82      origin_identifier.utf8(), database_name,
83      database_display_name, estimated_size));
84}
85
86void WebDatabaseObserverImpl::databaseModified(
87    const WebString& origin_identifier,
88    const WebString& database_name) {
89  sender_->Send(new DatabaseHostMsg_Modified(
90      origin_identifier.utf8(), database_name));
91}
92
93void WebDatabaseObserverImpl::databaseClosed(
94    const WebString& origin_identifier,
95    const WebString& database_name) {
96  sender_->Send(new DatabaseHostMsg_Closed(
97      origin_identifier.utf8(), database_name));
98  open_connections_->RemoveOpenConnection(origin_identifier.utf8(),
99                                          database_name);
100}
101
102void WebDatabaseObserverImpl::reportOpenDatabaseResult(
103    const WebString& origin_identifier,
104    const WebString& database_name,
105    bool is_sync_database,
106    int callsite, int websql_error, int sqlite_error) {
107  UMA_HISTOGRAM_WEBSQL_RESULT("OpenResult", is_sync_database, callsite,
108                              websql_error, sqlite_error);
109  HandleSqliteError(origin_identifier, database_name, sqlite_error);
110}
111
112void WebDatabaseObserverImpl::reportChangeVersionResult(
113    const WebString& origin_identifier,
114    const WebString& database_name,
115    bool is_sync_database,
116    int callsite, int websql_error, int sqlite_error) {
117  UMA_HISTOGRAM_WEBSQL_RESULT("ChangeVersionResult", is_sync_database, callsite,
118                              websql_error, sqlite_error);
119  HandleSqliteError(origin_identifier, database_name, sqlite_error);
120}
121
122void WebDatabaseObserverImpl::reportStartTransactionResult(
123    const WebString& origin_identifier,
124    const WebString& database_name,
125    bool is_sync_database,
126    int callsite, int websql_error, int sqlite_error) {
127  UMA_HISTOGRAM_WEBSQL_RESULT("BeginResult", is_sync_database, callsite,
128                              websql_error, sqlite_error);
129  HandleSqliteError(origin_identifier, database_name, sqlite_error);
130}
131
132void WebDatabaseObserverImpl::reportCommitTransactionResult(
133    const WebString& origin_identifier,
134    const WebString& database_name,
135    bool is_sync_database,
136    int callsite, int websql_error, int sqlite_error) {
137  UMA_HISTOGRAM_WEBSQL_RESULT("CommitResult", is_sync_database, callsite,
138                              websql_error, sqlite_error);
139  HandleSqliteError(origin_identifier, database_name, sqlite_error);
140}
141
142void WebDatabaseObserverImpl::reportExecuteStatementResult(
143    const WebString& origin_identifier,
144    const WebString& database_name,
145    bool is_sync_database,
146    int callsite, int websql_error, int sqlite_error) {
147  UMA_HISTOGRAM_WEBSQL_RESULT("StatementResult", is_sync_database, callsite,
148                              websql_error, sqlite_error);
149  HandleSqliteError(origin_identifier, database_name, sqlite_error);
150}
151
152void WebDatabaseObserverImpl::reportVacuumDatabaseResult(
153    const WebString& origin_identifier,
154    const WebString& database_name,
155    bool is_sync_database,
156    int sqlite_error) {
157  int result = DetermineHistogramResult(-1, sqlite_error);
158  if (is_sync_database) {
159    UMA_HISTOGRAM_ENUMERATION("websql.Sync.VacuumResult",
160                              result, kResultHistogramSize);
161  } else {
162    UMA_HISTOGRAM_ENUMERATION("websql.Async.VacuumResult",
163                              result, kResultHistogramSize);
164  }
165  HandleSqliteError(origin_identifier, database_name, sqlite_error);
166}
167
168void WebDatabaseObserverImpl::WaitForAllDatabasesToClose() {
169  open_connections_->WaitForAllDatabasesToClose();
170}
171
172void WebDatabaseObserverImpl::HandleSqliteError(
173    const WebString& origin_identifier,
174    const WebString& database_name,
175    int error) {
176  // We filter out errors which the backend doesn't act on to avoid
177  // a unnecessary ipc traffic, this method can get called at a fairly
178  // high frequency (per-sqlstatement).
179  if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) {
180    sender_->Send(new DatabaseHostMsg_HandleSqliteError(
181        origin_identifier.utf8(),
182        database_name,
183        error));
184  }
185}
186
187}  // namespace content
188