This source file includes following definitions.
- CheckVisitOrdering
- expected_loop_
- MergeDataAndStartSyncing
- StopSyncing
- GetAllSyncData
- ProcessSyncChanges
- OnUrlsModified
- OnUrlVisited
- OnUrlsDeleted
- ShouldIgnoreUrl
- ShouldSyncVisit
- CreateOrUpdateSyncNode
- AddTypedUrlToChangeList
- WriteToTypedUrlSpecifics
- FixupURLAndGetVisits
#include "chrome/browser/history/typed_url_syncable_service.h"
#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/history/history_backend.h"
#include "net/base/net_util.h"
#include "sync/protocol/sync.pb.h"
#include "sync/protocol/typed_url_specifics.pb.h"
namespace {
static const int kMaxTypedUrlVisits = 100;
static const int kMaxVisitsToFetch = 1000;
static const int kTypedUrlVisitThrottleThreshold = 10;
static const int kTypedUrlVisitThrottleMultiple = 10;
}
namespace history {
const char kTypedUrlTag[] = "google_chrome_typed_urls";
static bool CheckVisitOrdering(const VisitVector& visits) {
int64 previous_visit_time = 0;
for (VisitVector::const_iterator visit = visits.begin();
visit != visits.end(); ++visit) {
if (visit != visits.begin()) {
if (previous_visit_time == visit->visit_time.ToInternalValue())
DVLOG(1) << "Duplicate visit time encountered";
else if (previous_visit_time > visit->visit_time.ToInternalValue())
return false;
}
previous_visit_time = visit->visit_time.ToInternalValue();
}
return true;
}
TypedUrlSyncableService::TypedUrlSyncableService(
HistoryBackend* history_backend)
: history_backend_(history_backend),
processing_syncer_changes_(false),
expected_loop_(base::MessageLoop::current()) {
DCHECK(history_backend_);
DCHECK(expected_loop_ == base::MessageLoop::current());
}
TypedUrlSyncableService::~TypedUrlSyncableService() {
DCHECK(expected_loop_ == base::MessageLoop::current());
}
syncer::SyncMergeResult TypedUrlSyncableService::MergeDataAndStartSyncing(
syncer::ModelType type,
const syncer::SyncDataList& initial_sync_data,
scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
scoped_ptr<syncer::SyncErrorFactory> error_handler) {
DCHECK(expected_loop_ == base::MessageLoop::current());
DCHECK(!sync_processor_.get());
DCHECK(sync_processor.get());
DCHECK(error_handler.get());
DCHECK_EQ(type, syncer::TYPED_URLS);
syncer::SyncMergeResult merge_result(type);
sync_processor_ = sync_processor.Pass();
sync_error_handler_ = error_handler.Pass();
return merge_result;
}
void TypedUrlSyncableService::StopSyncing(syncer::ModelType type) {
DCHECK(expected_loop_ == base::MessageLoop::current());
DCHECK_EQ(type, syncer::TYPED_URLS);
sync_processor_.reset();
sync_error_handler_.reset();
}
syncer::SyncDataList TypedUrlSyncableService::GetAllSyncData(
syncer::ModelType type) const {
DCHECK(expected_loop_ == base::MessageLoop::current());
syncer::SyncDataList list;
return list;
}
syncer::SyncError TypedUrlSyncableService::ProcessSyncChanges(
const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) {
DCHECK(expected_loop_ == base::MessageLoop::current());
return syncer::SyncError(FROM_HERE,
syncer::SyncError::DATATYPE_ERROR,
"Typed url syncable service is not implemented.",
syncer::TYPED_URLS);
}
void TypedUrlSyncableService::OnUrlsModified(URLRows* changed_urls) {
DCHECK(expected_loop_ == base::MessageLoop::current());
DCHECK(changed_urls);
if (processing_syncer_changes_)
return;
if (!sync_processor_.get())
return;
syncer::SyncChangeList changes;
for (URLRows::iterator url = changed_urls->begin();
url != changed_urls->end(); ++url) {
if (url->typed_count() > 0) {
CreateOrUpdateSyncNode(*url, &changes);
}
}
if (changes.size() > 0)
sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
}
void TypedUrlSyncableService::OnUrlVisited(content::PageTransition transition,
URLRow* row) {
DCHECK(expected_loop_ == base::MessageLoop::current());
DCHECK(row);
if (processing_syncer_changes_)
return;
if (!sync_processor_.get())
return;
if (!ShouldSyncVisit(transition, row))
return;
syncer::SyncChangeList changes;
CreateOrUpdateSyncNode(*row, &changes);
if (changes.size() > 0)
sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
}
void TypedUrlSyncableService::OnUrlsDeleted(bool all_history,
bool archived,
URLRows* rows) {
DCHECK(expected_loop_ == base::MessageLoop::current());
if (processing_syncer_changes_)
return;
if (!sync_processor_.get())
return;
if (archived)
return;
syncer::SyncChangeList changes;
if (all_history) {
for (std::set<GURL>::const_iterator url = synced_typed_urls_.begin();
url != synced_typed_urls_.end(); ++url) {
VisitVector visits;
URLRow row(*url);
AddTypedUrlToChangeList(syncer::SyncChange::ACTION_DELETE,
row, visits, url->spec(), &changes);
}
synced_typed_urls_.clear();
} else {
DCHECK(rows);
for (URLRows::const_iterator row = rows->begin();
row != rows->end(); ++row) {
if (synced_typed_urls_.find(row->url()) != synced_typed_urls_.end()) {
VisitVector visits;
AddTypedUrlToChangeList(syncer::SyncChange::ACTION_DELETE,
*row, visits, row->url().spec(), &changes);
synced_typed_urls_.erase(row->url());
}
}
}
if (changes.size() > 0)
sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
}
bool TypedUrlSyncableService::ShouldIgnoreUrl(const GURL& url) {
if (url.spec().empty())
return true;
if (url.SchemeIsFile())
return true;
if (net::IsLocalhost(url.host()))
return true;
return false;
}
bool TypedUrlSyncableService::ShouldSyncVisit(
content::PageTransition page_transition,
URLRow* row) {
if (!row)
return false;
int typed_count = row->typed_count();
content::PageTransition transition = static_cast<content::PageTransition>(
page_transition & content::PAGE_TRANSITION_CORE_MASK);
return (transition == content::PAGE_TRANSITION_TYPED &&
typed_count > 0 &&
(typed_count < kTypedUrlVisitThrottleThreshold ||
(typed_count % kTypedUrlVisitThrottleMultiple) == 0));
}
bool TypedUrlSyncableService::CreateOrUpdateSyncNode(
URLRow url,
syncer::SyncChangeList* changes) {
DCHECK_GT(url.typed_count(), 0);
if (ShouldIgnoreUrl(url.url()))
return true;
VisitVector visit_vector;
if (!FixupURLAndGetVisits(&url, &visit_vector)) {
DLOG(ERROR) << "Could not load visits for url: " << url.url();
return false;
}
DCHECK(!visit_vector.empty());
std::string title = url.url().spec();
syncer::SyncChange::SyncChangeType change_type;
change_type =
(synced_typed_urls_.find(url.url()) != synced_typed_urls_.end()) ?
syncer::SyncChange::ACTION_UPDATE :
syncer::SyncChange::ACTION_ADD;
synced_typed_urls_.insert(url.url());
AddTypedUrlToChangeList(change_type, url, visit_vector, title, changes);
return true;
}
void TypedUrlSyncableService::AddTypedUrlToChangeList(
syncer::SyncChange::SyncChangeType change_type,
const URLRow& row,
const VisitVector& visits,
std::string title,
syncer::SyncChangeList* change_list) {
sync_pb::EntitySpecifics entity_specifics;
sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
if (change_type == syncer::SyncChange::ACTION_DELETE) {
typed_url->set_url(row.url().spec());
} else {
WriteToTypedUrlSpecifics(row, visits, typed_url);
}
change_list->push_back(
syncer::SyncChange(FROM_HERE, change_type,
syncer::SyncData::CreateLocalData(
kTypedUrlTag, title, entity_specifics)));
}
void TypedUrlSyncableService::WriteToTypedUrlSpecifics(
const URLRow& url,
const VisitVector& visits,
sync_pb::TypedUrlSpecifics* typed_url) {
DCHECK(!url.last_visit().is_null());
DCHECK(!visits.empty());
DCHECK_EQ(url.last_visit().ToInternalValue(),
visits.back().visit_time.ToInternalValue());
typed_url->set_url(url.url().spec());
typed_url->set_title(base::UTF16ToUTF8(url.title()));
typed_url->set_hidden(url.hidden());
DCHECK(CheckVisitOrdering(visits));
bool only_typed = false;
int skip_count = 0;
if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) {
int typed_count = 0;
int total = 0;
for (VisitVector::const_iterator visit = visits.begin();
visit != visits.end(); ++visit) {
content::PageTransition transition = content::PageTransitionFromInt(
visit->transition & content::PAGE_TRANSITION_CORE_MASK);
if (transition == content::PAGE_TRANSITION_RELOAD)
continue;
++total;
if (transition == content::PAGE_TRANSITION_TYPED)
++typed_count;
}
DCHECK(typed_count > 0);
if (typed_count > kMaxTypedUrlVisits) {
only_typed = true;
skip_count = typed_count - kMaxTypedUrlVisits;
} else if (total > kMaxTypedUrlVisits) {
skip_count = total - kMaxTypedUrlVisits;
}
}
for (VisitVector::const_iterator visit = visits.begin();
visit != visits.end(); ++visit) {
content::PageTransition transition = content::PageTransitionFromInt(
visit->transition & content::PAGE_TRANSITION_CORE_MASK);
if (transition == content::PAGE_TRANSITION_RELOAD)
continue;
if (only_typed && transition != content::PAGE_TRANSITION_TYPED)
continue;
if (skip_count > 0) {
if (only_typed || transition != content::PAGE_TRANSITION_TYPED) {
--skip_count;
continue;
}
}
typed_url->add_visits(visit->visit_time.ToInternalValue());
typed_url->add_visit_transitions(visit->transition);
}
DCHECK_EQ(skip_count, 0);
if (typed_url->visits_size() == 0) {
typed_url->add_visits(url.last_visit().ToInternalValue());
typed_url->add_visit_transitions(content::PAGE_TRANSITION_RELOAD);
}
CHECK_GT(typed_url->visits_size(), 0);
CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits);
CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size());
}
bool TypedUrlSyncableService::FixupURLAndGetVisits(
URLRow* url,
VisitVector* visits) {
++num_db_accesses_;
CHECK(history_backend_);
if (!history_backend_->GetMostRecentVisitsForURL(
url->id(), kMaxVisitsToFetch, visits)) {
++num_db_errors_;
return false;
}
if (visits->empty()) {
DVLOG(1) << "Found empty visits for URL: " << url->url();
VisitRow visit(
url->id(), url->last_visit(), 0, content::PAGE_TRANSITION_TYPED, 0);
visits->push_back(visit);
}
std::reverse(visits->begin(), visits->end());
url->set_last_visit(visits->back().visit_time);
DCHECK(CheckVisitOrdering(*visits));
return true;
}
}