blob: 506aa5955be77b08e9a5e8a5e3bbe095c322871a [file] [log] [blame]
// Copyright 2017 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 "components/offline_pages/core/prefetch/generate_page_bundle_task.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/ptr_util.h"
#include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h"
#include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h"
#include "components/offline_pages/core/prefetch/store/prefetch_store.h"
#include "sql/connection.h"
#include "sql/statement.h"
#include "sql/transaction.h"
namespace offline_pages {
namespace {
// Temporary storage for Urls metadata fetched from the storage.
struct FetchedUrl {
FetchedUrl() = default;
FetchedUrl(int64_t offline_id,
const std::string& requested_url,
int generate_bundle_attempts)
: offline_id(offline_id),
requested_url(requested_url),
generate_bundle_attempts(generate_bundle_attempts) {}
int64_t offline_id;
std::string requested_url;
int generate_bundle_attempts;
};
// This is maximum URLs that Offline Page Service can take in one request.
const int kMaxUrlsToSend = 100;
bool UpdateStateSync(sql::Connection* db, const FetchedUrl& url) {
static const char kSql[] =
"UPDATE prefetch_items SET state = ?, generate_bundle_attempts = ?"
" WHERE offline_id = ?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt(
0, static_cast<int>(PrefetchItemState::SENT_GENERATE_PAGE_BUNDLE));
statement.BindInt(1, url.generate_bundle_attempts + 1);
statement.BindInt64(2, url.offline_id);
return statement.Run();
}
std::unique_ptr<std::vector<FetchedUrl>> FetchUrlsSync(sql::Connection* db) {
static const char kSql[] =
"SELECT offline_id, requested_url, generate_bundle_attempts"
" FROM prefetch_items"
" WHERE state = ?"
" ORDER BY creation_time DESC";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt(0, static_cast<int>(PrefetchItemState::NEW_REQUEST));
auto urls = base::MakeUnique<std::vector<FetchedUrl>>();
while (statement.Step()) {
urls->push_back(
FetchedUrl(statement.ColumnInt64(0), // offline_id
statement.ColumnString(1), // requested_url
statement.ColumnInt(2))); // generate_bundle_attempts
}
return urls;
}
bool MarkExtraUrlsFinished(sql::Connection* db, const FetchedUrl& url) {
static const char kSql[] =
"UPDATE prefetch_items SET state = ?, error_code = ?"
" WHERE offline_id = ?";
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt(0, static_cast<int>(PrefetchItemState::FINISHED));
statement.BindInt(1,
static_cast<int>(PrefetchItemErrorCode::TOO_MANY_NEW_URLS));
statement.BindInt64(2, url.offline_id);
return statement.Run();
}
std::unique_ptr<std::vector<std::string>> SelectUrlsToPrefetchSync(
sql::Connection* db) {
if (!db)
return nullptr;
sql::Transaction transaction(db);
if (!transaction.Begin())
return nullptr;
auto urls = FetchUrlsSync(db);
if (!urls || urls->empty())
return nullptr;
// If we've got more than kMaxUrlsToSend URLs, mark the extra ones FINISHED
// and remove them from the list.
if (urls->size() > kMaxUrlsToSend) {
for (size_t index = kMaxUrlsToSend; index < urls->size(); ++index) {
if (!MarkExtraUrlsFinished(db, urls->at(index)))
return nullptr;
}
urls->resize(kMaxUrlsToSend);
}
auto url_specs = base::MakeUnique<std::vector<std::string>>();
for (const auto& url : *urls) {
if (!UpdateStateSync(db, url))
return nullptr;
url_specs->push_back(std::move(url.requested_url));
}
if (!transaction.Commit())
return nullptr;
return url_specs;
}
} // namespace
GeneratePageBundleTask::GeneratePageBundleTask(
PrefetchStore* prefetch_store,
PrefetchGCMHandler* gcm_handler,
PrefetchNetworkRequestFactory* request_factory,
const PrefetchRequestFinishedCallback& callback)
: prefetch_store_(prefetch_store),
gcm_handler_(gcm_handler),
request_factory_(request_factory),
callback_(callback),
weak_factory_(this) {}
GeneratePageBundleTask::~GeneratePageBundleTask() {}
void GeneratePageBundleTask::Run() {
prefetch_store_->Execute(
base::BindOnce(&SelectUrlsToPrefetchSync),
base::BindOnce(&GeneratePageBundleTask::StartGeneratePageBundle,
weak_factory_.GetWeakPtr()));
}
void GeneratePageBundleTask::StartGeneratePageBundle(
std::unique_ptr<std::vector<std::string>> urls) {
if (!urls || urls->empty()) {
TaskComplete();
return;
}
gcm_handler_->GetGCMToken(
base::Bind(&GeneratePageBundleTask::GotRegistrationId,
weak_factory_.GetWeakPtr(), base::Passed(std::move(urls))));
}
void GeneratePageBundleTask::GotRegistrationId(
std::unique_ptr<std::vector<std::string>> urls,
const std::string& id,
instance_id::InstanceID::Result result) {
// TODO(dimich): Add UMA reporting on instance_id::InstanceID::Result.
request_factory_->MakeGeneratePageBundleRequest(*urls, id, callback_);
TaskComplete();
}
} // namespace offline_pages