blob: 775395fbdc0c550a5f346a9c7ce590a947e907c6 [file] [log] [blame]
/*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/dom/storage_area.h"
#include "base/bind.h"
#include "base/stringprintf.h"
#include "cobalt/dom/local_storage_database.h"
#include "cobalt/dom/storage.h"
#include "googleurl/src/gurl.h"
namespace cobalt {
namespace dom {
namespace {
const char kLocalStorageSuffix[] = ".localstorage";
const char kSessionStorageSuffix[] = ".sessionstorage";
std::string OriginToDatabaseIdentifier(const GURL& url) {
// For compatibility with existing saved data, this tries to behave the same
// as WebCore's SecurityOrigin::databaseIdentifier().
// e.g. https://www.youtube.com/tv should be converted to
// https_www.youtube.com_0
// NOTE: WebCore passes the encoded host through FileSystem's
// encodeForFilename(). We assume our host will be well-formed.
std::string encoded_host = url.HostNoBrackets();
int port = url.IntPort();
if (port == -1) {
// A default/unspecified port in googleurl is -1, but Steel used KURL
// from WebCore where a default port is 0. Convert to 0 for compatibility.
port = 0;
}
return base::StringPrintf("%s_%s_%d", url.scheme().c_str(),
encoded_host.c_str(), port);
}
} // namespace
StorageArea::StorageArea(Storage* storage_node, LocalStorageDatabase* db)
: read_event_(true, false),
storage_node_(storage_node),
size_bytes_(0),
db_interface_(db),
initialized_(false) {
}
StorageArea::~StorageArea() {}
int StorageArea::length() {
Init();
return static_cast<int>(storage_map_->size());
}
base::optional<std::string> StorageArea::Key(int index) {
Init();
if (index < 0 || index >= length()) {
return base::nullopt;
}
// Advance N elements to get to index.
// TODO: If this is called often, we should cache the iterator.
StorageMap::const_iterator it = storage_map_->begin();
int count = 0;
while (count < index) {
++it;
++count;
}
return it->first;
}
base::optional<std::string> StorageArea::GetItem(const std::string& key) {
Init();
StorageMap::const_iterator it = storage_map_->find(key);
if (it != storage_map_->end()) {
return it->second;
} else {
return base::nullopt;
}
}
void StorageArea::SetItem(const std::string& key, const std::string& value) {
Init();
base::optional<std::string> old_value = GetItem(key);
// TODO: Implement quota handling.
size_bytes_ +=
static_cast<int>(value.length() - old_value.value_or("").length());
(*storage_map_)[key] = value;
storage_node_->DispatchEvent(key, old_value, value);
if (db_interface_) {
db_interface_->Write(identifier_, key, value);
}
}
void StorageArea::RemoveItem(const std::string& key) {
Init();
base::optional<std::string> old_value;
StorageMap::iterator it = storage_map_->find(key);
if (it != storage_map_->end()) {
size_bytes_ -= static_cast<int>(it->second.length());
old_value = it->second;
storage_map_->erase(it);
}
storage_node_->DispatchEvent(key, old_value, base::nullopt);
if (db_interface_) {
db_interface_->Delete(identifier_, key);
}
}
void StorageArea::Clear() {
Init();
storage_map_->clear();
size_bytes_ = 0;
storage_node_->DispatchEvent(base::nullopt, base::nullopt, base::nullopt);
if (db_interface_) {
db_interface_->Clear(identifier_);
}
}
bool StorageArea::key_exists(const std::string& key_name) {
Init();
return storage_map_->find(key_name) != storage_map_->end();
}
void StorageArea::Init() {
if (initialized_) {
return;
}
identifier_ = OriginToDatabaseIdentifier(storage_node_->origin());
if (db_interface_) {
// LocalStorage path. We read our StorageMap from the database.
// Do a one-time read from the database for what's currently in
// LocalStorage.
identifier_ += kLocalStorageSuffix;
db_interface_->ReadAll(
identifier_,
base::Bind(&StorageArea::OnInitComplete, base::Unretained(this)));
read_event_.Wait();
} else {
// SessionStorage. Create a new, empty StorageMap.
identifier_ += kSessionStorageSuffix;
storage_map_.reset(new StorageMap);
}
initialized_ = true;
}
void StorageArea::OnInitComplete(scoped_ptr<StorageMap> data) {
storage_map_.reset(data.release());
read_event_.Signal();
}
} // namespace dom
} // namespace cobalt