// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/child/web_database_observer_impl.h"
#include "base/metrics/histogram.h"
#include "base/strings/string16.h"
#include "content/common/database_messages.h"
#include "third_party/WebKit/public/platform/WebCString.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/sqlite/sqlite3.h"
using blink::WebString;
namespace content {
namespace {
const int kResultHistogramSize = 50;
const int kCallsiteHistogramSize = 10;
int DetermineHistogramResult(int websql_error, int sqlite_error) {
// If we have a sqlite error, log it after trimming the extended bits.
// There are 26 possible values, but we leave room for some new ones.
if (sqlite_error)
return std::min(sqlite_error & 0xff, 30);
// Otherwise, websql_error may be an SQLExceptionCode, SQLErrorCode
// or a DOMExceptionCode, or -1 for success.
if (websql_error == -1)
return 0; // no error
// SQLExceptionCode starts at 1000
if (websql_error >= 1000)
websql_error -= 1000;
return std::min(websql_error + 30, kResultHistogramSize - 1);
}
#define HISTOGRAM_WEBSQL_RESULT(name, is_sync_database, \
callsite, websql_error, sqlite_error) \
do { \
DCHECK(callsite < kCallsiteHistogramSize); \
int result = DetermineHistogramResult(websql_error, sqlite_error); \
if (is_sync_database) { \
UMA_HISTOGRAM_ENUMERATION("websql.Sync." name, \
result, kResultHistogramSize); \
if (result) { \
UMA_HISTOGRAM_ENUMERATION("websql.Sync." name ".ErrorSite", \
callsite, kCallsiteHistogramSize); \
} \
} else { \
UMA_HISTOGRAM_ENUMERATION("websql.Async." name, \
result, kResultHistogramSize); \
if (result) { \
UMA_HISTOGRAM_ENUMERATION("websql.Async." name ".ErrorSite", \
callsite, kCallsiteHistogramSize); \
} \
} \
} while (0)
} // namespace
WebDatabaseObserverImpl::WebDatabaseObserverImpl(
IPC::SyncMessageFilter* sender)
: sender_(sender),
open_connections_(new webkit_database::DatabaseConnectionsWrapper) {
DCHECK(sender);
}
WebDatabaseObserverImpl::~WebDatabaseObserverImpl() {
}
void WebDatabaseObserverImpl::databaseOpened(
const WebString& origin_identifier,
const WebString& database_name,
const WebString& database_display_name,
unsigned long estimated_size) {
open_connections_->AddOpenConnection(origin_identifier.utf8(),
database_name);
sender_->Send(new DatabaseHostMsg_Opened(
origin_identifier.utf8(), database_name,
database_display_name, estimated_size));
}
void WebDatabaseObserverImpl::databaseModified(
const WebString& origin_identifier,
const WebString& database_name) {
sender_->Send(new DatabaseHostMsg_Modified(
origin_identifier.utf8(), database_name));
}
void WebDatabaseObserverImpl::databaseClosed(
const WebString& origin_identifier,
const WebString& database_name) {
sender_->Send(new DatabaseHostMsg_Closed(
origin_identifier.utf8(), database_name));
open_connections_->RemoveOpenConnection(origin_identifier.utf8(),
database_name);
}
void WebDatabaseObserverImpl::reportOpenDatabaseResult(
const WebString& origin_identifier,
const WebString& database_name,
bool is_sync_database,
int callsite, int websql_error, int sqlite_error) {
HISTOGRAM_WEBSQL_RESULT("OpenResult", is_sync_database,
callsite, websql_error, sqlite_error);
HandleSqliteError(origin_identifier, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::reportChangeVersionResult(
const WebString& origin_identifier,
const WebString& database_name,
bool is_sync_database,
int callsite, int websql_error, int sqlite_error) {
HISTOGRAM_WEBSQL_RESULT("ChangeVersionResult", is_sync_database,
callsite, websql_error, sqlite_error);
HandleSqliteError(origin_identifier, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::reportStartTransactionResult(
const WebString& origin_identifier,
const WebString& database_name,
bool is_sync_database,
int callsite, int websql_error, int sqlite_error) {
HISTOGRAM_WEBSQL_RESULT("BeginResult", is_sync_database,
callsite, websql_error, sqlite_error);
HandleSqliteError(origin_identifier, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::reportCommitTransactionResult(
const WebString& origin_identifier,
const WebString& database_name,
bool is_sync_database,
int callsite, int websql_error, int sqlite_error) {
HISTOGRAM_WEBSQL_RESULT("CommitResult", is_sync_database,
callsite, websql_error, sqlite_error);
HandleSqliteError(origin_identifier, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::reportExecuteStatementResult(
const WebString& origin_identifier,
const WebString& database_name,
bool is_sync_database,
int callsite, int websql_error, int sqlite_error) {
HISTOGRAM_WEBSQL_RESULT("StatementResult", is_sync_database,
callsite, websql_error, sqlite_error);
HandleSqliteError(origin_identifier, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::reportVacuumDatabaseResult(
const WebString& origin_identifier,
const WebString& database_name,
bool is_sync_database,
int sqlite_error) {
int result = DetermineHistogramResult(-1, sqlite_error);
if (is_sync_database) {
UMA_HISTOGRAM_ENUMERATION("websql.Sync.VacuumResult",
result, kResultHistogramSize);
} else {
UMA_HISTOGRAM_ENUMERATION("websql.Async.VacuumResult",
result, kResultHistogramSize);
}
HandleSqliteError(origin_identifier, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::WaitForAllDatabasesToClose() {
open_connections_->WaitForAllDatabasesToClose();
}
void WebDatabaseObserverImpl::HandleSqliteError(
const WebString& origin_identifier,
const WebString& database_name,
int error) {
// We filter out errors which the backend doesn't act on to avoid
// a unnecessary ipc traffic, this method can get called at a fairly
// high frequency (per-sqlstatement).
if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) {
sender_->Send(new DatabaseHostMsg_HandleSqliteError(
origin_identifier.utf8(),
database_name,
error));
}
}
} // namespace content