blob: 7359e3ce71722958473f772b5d1194fa14906412 [file] [log] [blame]
// 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/renderer/web_database_observer_impl.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string16.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/renderer/storage_util.h"
#include "storage/common/database/database_identifier.h"
#include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/sqlite/sqlite3.h"
using blink::WebSecurityOrigin;
using blink::WebString;
namespace content {
namespace {
const int kResultHistogramSize = 50;
const int kCallsiteHistogramSize = 10;
const int kWebSQLSuccess = -1;
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 == kWebSQLSuccess)
return 0; // no error
// SQLExceptionCode starts at 1000
if (websql_error >= 1000)
websql_error -= 1000;
return std::min(websql_error + 30, kResultHistogramSize - 1);
}
#define UMA_HISTOGRAM_WEBSQL_RESULT(name, callsite, websql_error, sqlite_error) \
do { \
DCHECK(callsite < kCallsiteHistogramSize); \
int result = DetermineHistogramResult(websql_error, sqlite_error); \
UMA_HISTOGRAM_ENUMERATION("websql.Async." name, \
result, kResultHistogramSize); \
if (result) { \
UMA_HISTOGRAM_ENUMERATION("websql.Async." name ".ErrorSite", \
callsite, kCallsiteHistogramSize); \
} \
} while (0)
// TODO(jsbell): Replace with use of url::Origin end-to-end.
// https://crbug.com/591482
std::string GetIdentifierFromOrigin(const WebSecurityOrigin& origin) {
return storage::GetIdentifierFromOrigin(WebSecurityOriginToGURL(origin));
}
} // namespace
WebDatabaseObserverImpl::WebDatabaseObserverImpl(
scoped_refptr<blink::mojom::ThreadSafeWebDatabaseHostPtr> web_database_host)
: web_database_host_(std::move(web_database_host)),
open_connections_(new storage::DatabaseConnectionsWrapper),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
DCHECK(main_thread_task_runner_);
}
WebDatabaseObserverImpl::~WebDatabaseObserverImpl() = default;
void WebDatabaseObserverImpl::DatabaseOpened(
const WebSecurityOrigin& origin,
const WebString& database_name,
const WebString& database_display_name,
unsigned long estimated_size) {
open_connections_->AddOpenConnection(GetIdentifierFromOrigin(origin),
database_name.Utf16());
GetWebDatabaseHost().Opened(origin, database_name.Utf16(),
database_display_name.Utf16(), estimated_size);
}
void WebDatabaseObserverImpl::DatabaseModified(const WebSecurityOrigin& origin,
const WebString& database_name) {
GetWebDatabaseHost().Modified(origin, database_name.Utf16());
}
void WebDatabaseObserverImpl::DatabaseClosed(const WebSecurityOrigin& origin,
const WebString& database_name) {
DCHECK(!main_thread_task_runner_->RunsTasksInCurrentSequence());
GetWebDatabaseHost().Closed(origin, database_name.Utf16());
open_connections_->RemoveOpenConnection(GetIdentifierFromOrigin(origin),
database_name.Utf16());
}
void WebDatabaseObserverImpl::ReportOpenDatabaseResult(
const WebSecurityOrigin& origin,
const WebString& database_name,
int callsite,
int websql_error,
int sqlite_error,
double call_time) {
UMA_HISTOGRAM_WEBSQL_RESULT("OpenResult", callsite,
websql_error, sqlite_error);
HandleSqliteError(origin, database_name, sqlite_error);
if (websql_error == kWebSQLSuccess && sqlite_error == SQLITE_OK) {
UMA_HISTOGRAM_TIMES("websql.Async.OpenTime.Success",
base::TimeDelta::FromSecondsD(call_time));
} else {
UMA_HISTOGRAM_TIMES("websql.Async.OpenTime.Error",
base::TimeDelta::FromSecondsD(call_time));
}
}
void WebDatabaseObserverImpl::ReportChangeVersionResult(
const WebSecurityOrigin& origin,
const WebString& database_name,
int callsite,
int websql_error,
int sqlite_error) {
UMA_HISTOGRAM_WEBSQL_RESULT("ChangeVersionResult", callsite,
websql_error, sqlite_error);
HandleSqliteError(origin, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::ReportStartTransactionResult(
const WebSecurityOrigin& origin,
const WebString& database_name,
int callsite,
int websql_error,
int sqlite_error) {
UMA_HISTOGRAM_WEBSQL_RESULT("BeginResult", callsite,
websql_error, sqlite_error);
HandleSqliteError(origin, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::ReportCommitTransactionResult(
const WebSecurityOrigin& origin,
const WebString& database_name,
int callsite,
int websql_error,
int sqlite_error) {
UMA_HISTOGRAM_WEBSQL_RESULT("CommitResult", callsite,
websql_error, sqlite_error);
HandleSqliteError(origin, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::ReportExecuteStatementResult(
const WebSecurityOrigin& origin,
const WebString& database_name,
int callsite,
int websql_error,
int sqlite_error) {
UMA_HISTOGRAM_WEBSQL_RESULT("StatementResult", callsite,
websql_error, sqlite_error);
HandleSqliteError(origin, database_name, sqlite_error);
}
void WebDatabaseObserverImpl::ReportVacuumDatabaseResult(
const WebSecurityOrigin& origin,
const WebString& database_name,
int sqlite_error) {
int result = DetermineHistogramResult(-1, sqlite_error);
UMA_HISTOGRAM_ENUMERATION("websql.Async.VacuumResult",
result, kResultHistogramSize);
HandleSqliteError(origin, database_name, sqlite_error);
}
bool WebDatabaseObserverImpl::WaitForAllDatabasesToClose(
base::TimeDelta timeout) {
DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
return open_connections_->WaitForAllDatabasesToClose(timeout);
}
void WebDatabaseObserverImpl::HandleSqliteError(const WebSecurityOrigin& origin,
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) {
GetWebDatabaseHost().HandleSqliteError(origin, database_name.Utf16(),
error);
}
}
blink::mojom::WebDatabaseHost& WebDatabaseObserverImpl::GetWebDatabaseHost() {
return **web_database_host_;
}
} // namespace content