Update to Chromium revision 106500.
git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@329 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
parent
7a07aaf5f1
commit
04c948fd51
|
@ -17,5 +17,5 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
'chromium_url': 'http://src.chromium.org/svn/trunk/src',
|
'chromium_url': 'http://src.chromium.org/svn/trunk/src',
|
||||||
'chromium_revision': '105051',
|
'chromium_revision': '106500',
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,8 +267,8 @@ class BrowserBackendProxy
|
||||||
status_result_ = appcache::UNCACHED;
|
status_result_ = appcache::UNCACHED;
|
||||||
event_.Reset();
|
event_.Reset();
|
||||||
system_->io_message_loop()->PostTask(
|
system_->io_message_loop()->PostTask(
|
||||||
FROM_HERE,
|
FROM_HERE, base::IgnoreReturn<appcache::Status>(
|
||||||
NewRunnableMethod(this, &BrowserBackendProxy::GetStatus, host_id));
|
base::Bind(&BrowserBackendProxy::GetStatus, this, host_id)));
|
||||||
event_.Wait();
|
event_.Wait();
|
||||||
} else if (system_->is_io_thread()) {
|
} else if (system_->is_io_thread()) {
|
||||||
system_->backend_impl_->GetStatusWithCallback(
|
system_->backend_impl_->GetStatusWithCallback(
|
||||||
|
@ -284,8 +284,8 @@ class BrowserBackendProxy
|
||||||
bool_result_ = false;
|
bool_result_ = false;
|
||||||
event_.Reset();
|
event_.Reset();
|
||||||
system_->io_message_loop()->PostTask(
|
system_->io_message_loop()->PostTask(
|
||||||
FROM_HERE,
|
FROM_HERE, base::IgnoreReturn<bool>(
|
||||||
NewRunnableMethod(this, &BrowserBackendProxy::StartUpdate, host_id));
|
base::Bind(&BrowserBackendProxy::StartUpdate, this, host_id)));
|
||||||
event_.Wait();
|
event_.Wait();
|
||||||
} else if (system_->is_io_thread()) {
|
} else if (system_->is_io_thread()) {
|
||||||
system_->backend_impl_->StartUpdateWithCallback(
|
system_->backend_impl_->StartUpdateWithCallback(
|
||||||
|
@ -301,8 +301,8 @@ class BrowserBackendProxy
|
||||||
bool_result_ = false;
|
bool_result_ = false;
|
||||||
event_.Reset();
|
event_.Reset();
|
||||||
system_->io_message_loop()->PostTask(
|
system_->io_message_loop()->PostTask(
|
||||||
FROM_HERE,
|
FROM_HERE, base::IgnoreReturn<bool>(
|
||||||
NewRunnableMethod(this, &BrowserBackendProxy::SwapCache, host_id));
|
base::Bind(&BrowserBackendProxy::SwapCache, this, host_id)));
|
||||||
event_.Wait();
|
event_.Wait();
|
||||||
} else if (system_->is_io_thread()) {
|
} else if (system_->is_io_thread()) {
|
||||||
system_->backend_impl_->SwapCacheWithCallback(
|
system_->backend_impl_->SwapCacheWithCallback(
|
||||||
|
|
|
@ -236,7 +236,7 @@ void BrowserDragDelegate::PrepareDragForFileContents(
|
||||||
if (file_name.value().empty()) {
|
if (file_name.value().empty()) {
|
||||||
// Retrieve the name from the URL.
|
// Retrieve the name from the URL.
|
||||||
file_name = FilePath(
|
file_name = FilePath(
|
||||||
net::GetSuggestedFilename(drop_data.url, "", "", "", "", string16()));
|
net::GetSuggestedFilename(drop_data.url, "", "", "", "", ""));
|
||||||
if (file_name.value().size() + drop_data.file_extension.size() + 1 >
|
if (file_name.value().size() + drop_data.file_extension.size() + 1 >
|
||||||
MAX_PATH) {
|
MAX_PATH) {
|
||||||
file_name = FilePath(file_name.value().substr(
|
file_name = FilePath(file_name.value().substr(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
// Copyright (c) 2011 The Chromium Embedded Framework Authors.
|
||||||
|
// Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -6,6 +7,9 @@
|
||||||
#include "cef_thread.h"
|
#include "cef_thread.h"
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "base/basictypes.h"
|
#include "base/basictypes.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
|
@ -16,9 +20,11 @@
|
||||||
#include "base/memory/scoped_ptr.h"
|
#include "base/memory/scoped_ptr.h"
|
||||||
#include "base/metrics/histogram.h"
|
#include "base/metrics/histogram.h"
|
||||||
#include "base/string_util.h"
|
#include "base/string_util.h"
|
||||||
|
#include "base/synchronization/lock.h"
|
||||||
#include "base/threading/thread.h"
|
#include "base/threading/thread.h"
|
||||||
#include "base/threading/thread_restrictions.h"
|
#include "base/threading/thread_restrictions.h"
|
||||||
#include "googleurl/src/gurl.h"
|
#include "googleurl/src/gurl.h"
|
||||||
|
#include "net/base/registry_controlled_domain.h"
|
||||||
#include "sql/meta_table.h"
|
#include "sql/meta_table.h"
|
||||||
#include "sql/statement.h"
|
#include "sql/statement.h"
|
||||||
#include "sql/transaction.h"
|
#include "sql/transaction.h"
|
||||||
|
@ -26,10 +32,24 @@
|
||||||
using base::Time;
|
using base::Time;
|
||||||
|
|
||||||
// This class is designed to be shared between any calling threads and the
|
// This class is designed to be shared between any calling threads and the
|
||||||
// database thread. It batches operations and commits them on a timer.
|
// database thread. It batches operations and commits them on a timer.
|
||||||
// This class expects to be Load()'ed once on any thread. Loading occurs
|
//
|
||||||
// asynchronously on the DB thread and the caller will be notified on the IO
|
// BrowserPersistentCookieStore::Load is called to load all cookies. It
|
||||||
// thread. Subsequent to loading, mutations may be queued by any thread using
|
// delegates to Backend::Load, which posts a Backend::LoadAndNotifyOnDBThread
|
||||||
|
// task to the DB thread. This task calls Backend::ChainLoadCookies(), which
|
||||||
|
// repeatedly posts itself to the DB thread to load each eTLD+1's cookies in
|
||||||
|
// separate tasks. When this is complete, Backend::NotifyOnIOThread is posted
|
||||||
|
// to the IO thread, which notifies the caller of BrowserPersistentCookieStore::
|
||||||
|
// Load that the load is complete.
|
||||||
|
//
|
||||||
|
// If a priority load request is invoked via BrowserPersistentCookieStore::
|
||||||
|
// LoadCookiesForKey, it is delegated to Backend::LoadCookiesForKey, which posts
|
||||||
|
// Backend::LoadKeyAndNotifyOnDBThread to the DB thread. That routine loads just
|
||||||
|
// that single domain key (eTLD+1)'s cookies, and posts a Backend::
|
||||||
|
// NotifyOnIOThread to the IO thread to notify the caller of
|
||||||
|
// BrowserPersistentCookieStore::LoadCookiesForKey that that load is complete.
|
||||||
|
//
|
||||||
|
// Subsequent to loading, mutations may be queued by any thread using
|
||||||
// AddCookie, UpdateCookieAccessTime, and DeleteCookie. These are flushed to
|
// AddCookie, UpdateCookieAccessTime, and DeleteCookie. These are flushed to
|
||||||
// disk on the DB thread every 30 seconds, 512 operations, or call to Flush(),
|
// disk on the DB thread every 30 seconds, 512 operations, or call to Flush(),
|
||||||
// whichever occurs first.
|
// whichever occurs first.
|
||||||
|
@ -40,11 +60,16 @@ class BrowserPersistentCookieStore::Backend
|
||||||
: path_(path),
|
: path_(path),
|
||||||
db_(NULL),
|
db_(NULL),
|
||||||
num_pending_(0),
|
num_pending_(0),
|
||||||
clear_local_state_on_exit_(false) {
|
clear_local_state_on_exit_(false),
|
||||||
|
initialized_(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates or load the SQLite database.
|
// Creates or loads the SQLite database.
|
||||||
bool Load(const LoadedCallback& loaded_callback);
|
void Load(const LoadedCallback& loaded_callback);
|
||||||
|
|
||||||
|
// Loads cookies for the domain key (eTLD+1).
|
||||||
|
void LoadCookiesForKey(const std::string& domain,
|
||||||
|
const LoadedCallback& loaded_callback);
|
||||||
|
|
||||||
// Batch a cookie addition.
|
// Batch a cookie addition.
|
||||||
void AddCookie(const net::CookieMonster::CanonicalCookie& cc);
|
void AddCookie(const net::CookieMonster::CanonicalCookie& cc);
|
||||||
|
@ -97,17 +122,30 @@ class BrowserPersistentCookieStore::Backend
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Creates or load the SQLite database on DB thread.
|
// Creates or loads the SQLite database on DB thread.
|
||||||
void LoadAndNotifyOnDBThread(const LoadedCallback& loaded_callback);
|
void LoadAndNotifyOnDBThread(const LoadedCallback& loaded_callback);
|
||||||
// Notify the CookieMonster when loading complete.
|
|
||||||
|
// Loads cookies for the domain key (eTLD+1) on DB thread.
|
||||||
|
void LoadKeyAndNotifyOnDBThread(const std::string& domains,
|
||||||
|
const LoadedCallback& loaded_callback);
|
||||||
|
|
||||||
|
// Notifies the CookieMonster when loading completes for a specific domain key
|
||||||
|
// or for all domain keys. Triggers the callback and passes it all cookies
|
||||||
|
// that have been loaded from DB since last IO notification.
|
||||||
void NotifyOnIOThread(
|
void NotifyOnIOThread(
|
||||||
const LoadedCallback& loaded_callback,
|
const LoadedCallback& loaded_callback,
|
||||||
bool load_success,
|
bool load_success);
|
||||||
const std::vector<net::CookieMonster::CanonicalCookie*>& cookies);
|
|
||||||
// Initialize the data base.
|
// Initialize the data base.
|
||||||
bool InitializeDatabase();
|
bool InitializeDatabase();
|
||||||
// Load cookies to the data base, and read cookies.
|
|
||||||
bool LoadInternal(std::vector<net::CookieMonster::CanonicalCookie*>* cookies);
|
// Loads cookies for the next domain key from the DB, then either reschedules
|
||||||
|
// itself or schedules the provided callback to run on the IO thread (if all
|
||||||
|
// domains are loaded).
|
||||||
|
void ChainLoadCookies(const LoadedCallback& loaded_callback);
|
||||||
|
|
||||||
|
// Load all cookies for a set of domains/hosts
|
||||||
|
bool LoadCookiesForDomains(const std::set<std::string>& key);
|
||||||
|
|
||||||
// Batch a cookie operation (add or delete)
|
// Batch a cookie operation (add or delete)
|
||||||
void BatchOperation(PendingOperation::OperationType op,
|
void BatchOperation(PendingOperation::OperationType op,
|
||||||
|
@ -126,9 +164,21 @@ class BrowserPersistentCookieStore::Backend
|
||||||
PendingOperationsList::size_type num_pending_;
|
PendingOperationsList::size_type num_pending_;
|
||||||
// True if the persistent store should be deleted upon destruction.
|
// True if the persistent store should be deleted upon destruction.
|
||||||
bool clear_local_state_on_exit_;
|
bool clear_local_state_on_exit_;
|
||||||
// Guard |pending_|, |num_pending_| and |clear_local_state_on_exit_|.
|
// Guard |cookies_|, |pending_|, |num_pending_| and
|
||||||
|
// |clear_local_state_on_exit_|.
|
||||||
base::Lock lock_;
|
base::Lock lock_;
|
||||||
|
|
||||||
|
// Temporary buffer for cookies loaded from DB. Accumulates cookies to reduce
|
||||||
|
// the number of messages sent to the IO thread. Sent back in response to
|
||||||
|
// individual load requests for domain keys or when all loading completes.
|
||||||
|
std::vector<net::CookieMonster::CanonicalCookie*> cookies_;
|
||||||
|
|
||||||
|
// Map of domain keys(eTLD+1) to domains/hosts that are to be loaded from DB.
|
||||||
|
std::map<std::string, std::set<std::string> > keys_to_load_;
|
||||||
|
|
||||||
|
// Indicates if DB has been initialized.
|
||||||
|
bool initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Backend);
|
DISALLOW_COPY_AND_ASSIGN(Backend);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,43 +216,91 @@ bool InitTable(sql::Connection* db) {
|
||||||
// so we want those people to get it. Ignore errors, since it may exist.
|
// so we want those people to get it. Ignore errors, since it may exist.
|
||||||
db->Execute("CREATE INDEX IF NOT EXISTS cookie_times ON cookies"
|
db->Execute("CREATE INDEX IF NOT EXISTS cookie_times ON cookies"
|
||||||
" (creation_utc)");
|
" (creation_utc)");
|
||||||
|
|
||||||
|
db->Execute("CREATE INDEX IF NOT EXISTS domain ON cookies(host_key)");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool BrowserPersistentCookieStore::Backend::Load(
|
void BrowserPersistentCookieStore::Backend::Load(
|
||||||
const LoadedCallback& loaded_callback) {
|
const LoadedCallback& loaded_callback) {
|
||||||
// This function should be called only once per instance.
|
// This function should be called only once per instance.
|
||||||
DCHECK(!db_.get());
|
DCHECK(!db_.get());
|
||||||
CefThread::PostTask(
|
CefThread::PostTask(
|
||||||
CefThread::FILE, FROM_HERE,
|
CefThread::FILE, FROM_HERE,
|
||||||
base::Bind(&Backend::LoadAndNotifyOnDBThread, base::Unretained(this),
|
base::Bind(&Backend::LoadAndNotifyOnDBThread, this, loaded_callback));
|
||||||
loaded_callback));
|
}
|
||||||
return true;
|
|
||||||
|
void BrowserPersistentCookieStore::Backend::LoadCookiesForKey(
|
||||||
|
const std::string& key,
|
||||||
|
const LoadedCallback& loaded_callback) {
|
||||||
|
CefThread::PostTask(
|
||||||
|
CefThread::FILE, FROM_HERE,
|
||||||
|
base::Bind(&Backend::LoadKeyAndNotifyOnDBThread, this,
|
||||||
|
key,
|
||||||
|
loaded_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserPersistentCookieStore::Backend::LoadAndNotifyOnDBThread(
|
void BrowserPersistentCookieStore::Backend::LoadAndNotifyOnDBThread(
|
||||||
const LoadedCallback& loaded_callback) {
|
const LoadedCallback& loaded_callback) {
|
||||||
DCHECK(CefThread::CurrentlyOn(CefThread::FILE));
|
DCHECK(CefThread::CurrentlyOn(CefThread::FILE));
|
||||||
std::vector<net::CookieMonster::CanonicalCookie*> cookies;
|
|
||||||
|
|
||||||
bool load_success = LoadInternal(&cookies);
|
if (!InitializeDatabase()) {
|
||||||
|
CefThread::PostTask(
|
||||||
|
CefThread::IO, FROM_HERE,
|
||||||
|
base::Bind(&BrowserPersistentCookieStore::Backend::NotifyOnIOThread,
|
||||||
|
this, loaded_callback, false));
|
||||||
|
} else {
|
||||||
|
ChainLoadCookies(loaded_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CefThread::PostTask(CefThread::IO, FROM_HERE, base::Bind(
|
void BrowserPersistentCookieStore::Backend::LoadKeyAndNotifyOnDBThread(
|
||||||
&BrowserPersistentCookieStore::Backend::NotifyOnIOThread,
|
const std::string& key,
|
||||||
base::Unretained(this), loaded_callback, load_success, cookies));
|
const LoadedCallback& loaded_callback) {
|
||||||
|
DCHECK(CefThread::CurrentlyOn(CefThread::FILE));
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
if (InitializeDatabase()) {
|
||||||
|
std::map<std::string, std::set<std::string> >::iterator
|
||||||
|
it = keys_to_load_.find(key);
|
||||||
|
if (it != keys_to_load_.end()) {
|
||||||
|
success = LoadCookiesForDomains(it->second);
|
||||||
|
keys_to_load_.erase(it);
|
||||||
|
} else {
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CefThread::PostTask(
|
||||||
|
CefThread::IO, FROM_HERE,
|
||||||
|
base::Bind(&BrowserPersistentCookieStore::Backend::NotifyOnIOThread,
|
||||||
|
this, loaded_callback, success));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserPersistentCookieStore::Backend::NotifyOnIOThread(
|
void BrowserPersistentCookieStore::Backend::NotifyOnIOThread(
|
||||||
const LoadedCallback& loaded_callback,
|
const LoadedCallback& loaded_callback,
|
||||||
bool load_success,
|
bool load_success) {
|
||||||
const std::vector<net::CookieMonster::CanonicalCookie*>& cookies) {
|
|
||||||
DCHECK(CefThread::CurrentlyOn(CefThread::IO));
|
DCHECK(CefThread::CurrentlyOn(CefThread::IO));
|
||||||
|
|
||||||
|
std::vector<net::CookieMonster::CanonicalCookie*> cookies;
|
||||||
|
{
|
||||||
|
base::AutoLock locked(lock_);
|
||||||
|
cookies.swap(cookies_);
|
||||||
|
}
|
||||||
|
|
||||||
loaded_callback.Run(cookies);
|
loaded_callback.Run(cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserPersistentCookieStore::Backend::InitializeDatabase() {
|
bool BrowserPersistentCookieStore::Backend::InitializeDatabase() {
|
||||||
|
DCHECK(CefThread::CurrentlyOn(CefThread::FILE));
|
||||||
|
|
||||||
|
if (initialized_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const FilePath dir = path_.DirName();
|
const FilePath dir = path_.DirName();
|
||||||
if (!file_util::PathExists(dir) && !file_util::CreateDirectory(dir)) {
|
if (!file_util::PathExists(dir) && !file_util::CreateDirectory(dir)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -224,47 +322,108 @@ bool BrowserPersistentCookieStore::Backend::InitializeDatabase() {
|
||||||
}
|
}
|
||||||
|
|
||||||
db_->Preload();
|
db_->Preload();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BrowserPersistentCookieStore::Backend::LoadInternal(
|
// Retrieve all the domains
|
||||||
std::vector<net::CookieMonster::CanonicalCookie*>* cookies) {
|
|
||||||
if (!InitializeDatabase()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slurp all the cookies into the out-vector.
|
|
||||||
sql::Statement smt(db_->GetUniqueStatement(
|
sql::Statement smt(db_->GetUniqueStatement(
|
||||||
"SELECT creation_utc, host_key, name, value, path, expires_utc, secure, "
|
"SELECT DISTINCT host_key FROM cookies"));
|
||||||
"httponly, last_access_utc FROM cookies"));
|
|
||||||
if (!smt) {
|
if (!smt) {
|
||||||
NOTREACHED() << "select statement prep failed";
|
NOTREACHED() << "select statement prep failed";
|
||||||
db_.reset();
|
db_.reset();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build a map of domain keys (always eTLD+1) to domains.
|
||||||
while (smt.Step()) {
|
while (smt.Step()) {
|
||||||
scoped_ptr<net::CookieMonster::CanonicalCookie> cc(
|
std::string domain = smt.ColumnString(0);
|
||||||
new net::CookieMonster::CanonicalCookie(
|
std::string key =
|
||||||
// The "source" URL is not used with persisted cookies.
|
net::RegistryControlledDomainService::GetDomainAndRegistry(domain);
|
||||||
GURL(), // Source
|
|
||||||
smt.ColumnString(2), // name
|
std::map<std::string, std::set<std::string> >::iterator it =
|
||||||
smt.ColumnString(3), // value
|
keys_to_load_.find(key);
|
||||||
smt.ColumnString(1), // domain
|
if (it == keys_to_load_.end())
|
||||||
smt.ColumnString(4), // path
|
it = keys_to_load_.insert(std::make_pair
|
||||||
std::string(), // TODO(abarth): Persist mac_key
|
(key, std::set<std::string>())).first;
|
||||||
std::string(), // TODO(abarth): Persist mac_algorithm
|
it->second.insert(domain);
|
||||||
Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc
|
|
||||||
Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc
|
|
||||||
Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc
|
|
||||||
smt.ColumnInt(6) != 0, // secure
|
|
||||||
smt.ColumnInt(7) != 0, // httponly
|
|
||||||
true)); // has_expires
|
|
||||||
DLOG_IF(WARNING,
|
|
||||||
cc->CreationDate() > Time::Now()) << L"CreationDate too recent";
|
|
||||||
cookies->push_back(cc.release());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserPersistentCookieStore::Backend::ChainLoadCookies(
|
||||||
|
const LoadedCallback& loaded_callback) {
|
||||||
|
DCHECK(CefThread::CurrentlyOn(CefThread::FILE));
|
||||||
|
|
||||||
|
bool load_success = true;
|
||||||
|
|
||||||
|
if (keys_to_load_.size() > 0) {
|
||||||
|
// Load cookies for the first domain key.
|
||||||
|
std::map<std::string, std::set<std::string> >::iterator
|
||||||
|
it = keys_to_load_.begin();
|
||||||
|
load_success = LoadCookiesForDomains(it->second);
|
||||||
|
keys_to_load_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If load is successful and there are more domain keys to be loaded,
|
||||||
|
// then post a DB task to continue chain-load;
|
||||||
|
// Otherwise notify on IO thread.
|
||||||
|
if (load_success && keys_to_load_.size() > 0) {
|
||||||
|
CefThread::PostTask(
|
||||||
|
CefThread::FILE, FROM_HERE,
|
||||||
|
base::Bind(&Backend::ChainLoadCookies, this, loaded_callback));
|
||||||
|
} else {
|
||||||
|
CefThread::PostTask(
|
||||||
|
CefThread::IO, FROM_HERE,
|
||||||
|
base::Bind(&BrowserPersistentCookieStore::Backend::NotifyOnIOThread,
|
||||||
|
this, loaded_callback, load_success));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowserPersistentCookieStore::Backend::LoadCookiesForDomains(
|
||||||
|
const std::set<std::string>& domains) {
|
||||||
|
DCHECK(CefThread::CurrentlyOn(CefThread::FILE));
|
||||||
|
|
||||||
|
sql::Statement smt(db_->GetCachedStatement(SQL_FROM_HERE,
|
||||||
|
"SELECT creation_utc, host_key, name, value, path, expires_utc, secure, "
|
||||||
|
"httponly, last_access_utc FROM cookies WHERE host_key = ?"));
|
||||||
|
if (!smt) {
|
||||||
|
NOTREACHED() << "select statement prep failed";
|
||||||
|
db_.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<net::CookieMonster::CanonicalCookie*> cookies;
|
||||||
|
std::set<std::string>::const_iterator it = domains.begin();
|
||||||
|
for (; it != domains.end(); ++it) {
|
||||||
|
smt.BindString(0, *it);
|
||||||
|
while (smt.Step()) {
|
||||||
|
scoped_ptr<net::CookieMonster::CanonicalCookie> cc(
|
||||||
|
new net::CookieMonster::CanonicalCookie(
|
||||||
|
// The "source" URL is not used with persisted cookies.
|
||||||
|
GURL(), // Source
|
||||||
|
smt.ColumnString(2), // name
|
||||||
|
smt.ColumnString(3), // value
|
||||||
|
smt.ColumnString(1), // domain
|
||||||
|
smt.ColumnString(4), // path
|
||||||
|
std::string(), // TODO(abarth): Persist mac_key
|
||||||
|
std::string(), // TODO(abarth): Persist mac_algorithm
|
||||||
|
Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc
|
||||||
|
Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc
|
||||||
|
Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc
|
||||||
|
smt.ColumnInt(6) != 0, // secure
|
||||||
|
smt.ColumnInt(7) != 0, // httponly
|
||||||
|
true)); // has_expires
|
||||||
|
DLOG_IF(WARNING,
|
||||||
|
cc->CreationDate() > Time::Now()) << L"CreationDate too recent";
|
||||||
|
cookies.push_back(cc.release());
|
||||||
|
}
|
||||||
|
smt.Reset();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
base::AutoLock locked(lock_);
|
||||||
|
cookies_.insert(cookies_.end(), cookies.begin(), cookies.end());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,13 +538,13 @@ void BrowserPersistentCookieStore::Backend::BatchOperation(
|
||||||
if (num_pending == 1) {
|
if (num_pending == 1) {
|
||||||
// We've gotten our first entry for this batch, fire off the timer.
|
// We've gotten our first entry for this batch, fire off the timer.
|
||||||
CefThread::PostDelayedTask(
|
CefThread::PostDelayedTask(
|
||||||
CefThread::FILE, FROM_HERE,
|
CefThread::FILE, FROM_HERE,
|
||||||
NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs);
|
NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs);
|
||||||
} else if (num_pending == kCommitAfterBatchSize) {
|
} else if (num_pending == kCommitAfterBatchSize) {
|
||||||
// We've reached a big enough batch, fire off a commit now.
|
// We've reached a big enough batch, fire off a commit now.
|
||||||
CefThread::PostTask(
|
CefThread::PostTask(
|
||||||
CefThread::FILE, FROM_HERE,
|
CefThread::FILE, FROM_HERE,
|
||||||
NewRunnableMethod(this, &Backend::Commit));
|
NewRunnableMethod(this, &Backend::Commit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,8 +690,14 @@ BrowserPersistentCookieStore::~BrowserPersistentCookieStore() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowserPersistentCookieStore::Load(const LoadedCallback& loaded_callback) {
|
void BrowserPersistentCookieStore::Load(const LoadedCallback& loaded_callback) {
|
||||||
return backend_->Load(loaded_callback);
|
backend_->Load(loaded_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserPersistentCookieStore::LoadCookiesForKey(
|
||||||
|
const std::string& key,
|
||||||
|
const LoadedCallback& loaded_callback) {
|
||||||
|
backend_->LoadCookiesForKey(key, loaded_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserPersistentCookieStore::AddCookie(
|
void BrowserPersistentCookieStore::AddCookie(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||||
// Portions copyright (c) 2010 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -17,10 +16,12 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/compiler_specific.h"
|
||||||
#include "base/memory/ref_counted.h"
|
#include "base/memory/ref_counted.h"
|
||||||
#include "net/base/cookie_monster.h"
|
#include "net/base/cookie_monster.h"
|
||||||
|
|
||||||
class FilePath;
|
class FilePath;
|
||||||
|
class Task;
|
||||||
|
|
||||||
// Implements the PersistentCookieStore interface in terms of a SQLite database.
|
// Implements the PersistentCookieStore interface in terms of a SQLite database.
|
||||||
// For documentation about the actual member functions consult the documentation
|
// For documentation about the actual member functions consult the documentation
|
||||||
|
@ -31,7 +32,10 @@ class BrowserPersistentCookieStore
|
||||||
explicit BrowserPersistentCookieStore(const FilePath& path);
|
explicit BrowserPersistentCookieStore(const FilePath& path);
|
||||||
virtual ~BrowserPersistentCookieStore();
|
virtual ~BrowserPersistentCookieStore();
|
||||||
|
|
||||||
virtual bool Load(const LoadedCallback& loaded_callback) OVERRIDE;
|
virtual void Load(const LoadedCallback& loaded_callback) OVERRIDE;
|
||||||
|
|
||||||
|
virtual void LoadCookiesForKey(const std::string& key,
|
||||||
|
const LoadedCallback& callback) OVERRIDE;
|
||||||
|
|
||||||
virtual void AddCookie(
|
virtual void AddCookie(
|
||||||
const net::CookieMonster::CanonicalCookie& cc) OVERRIDE;
|
const net::CookieMonster::CanonicalCookie& cc) OVERRIDE;
|
||||||
|
|
|
@ -243,7 +243,7 @@ class RequestProxy : public net::URLRequest::Delegate,
|
||||||
webkit_glue::ShouldDownload(content_disposition, info.mime_type)) {
|
webkit_glue::ShouldDownload(content_disposition, info.mime_type)) {
|
||||||
string16 filename = net::GetSuggestedFilename(url,
|
string16 filename = net::GetSuggestedFilename(url,
|
||||||
content_disposition, info.charset, "", info.mime_type,
|
content_disposition, info.charset, "", info.mime_type,
|
||||||
ASCIIToUTF16("download"));
|
"download");
|
||||||
CefRefPtr<CefDownloadHandler> dl_handler;
|
CefRefPtr<CefDownloadHandler> dl_handler;
|
||||||
if (handler->GetDownloadHandler(browser_, info.mime_type,
|
if (handler->GetDownloadHandler(browser_, info.mime_type,
|
||||||
filename, info.content_length,
|
filename, info.content_length,
|
||||||
|
|
|
@ -4,36 +4,23 @@
|
||||||
|
|
||||||
#include "browser_webblobregistry_impl.h"
|
#include "browser_webblobregistry_impl.h"
|
||||||
|
|
||||||
|
#include "base/bind.h"
|
||||||
#include "base/message_loop.h"
|
#include "base/message_loop.h"
|
||||||
#include "base/task.h"
|
|
||||||
#include "googleurl/src/gurl.h"
|
#include "googleurl/src/gurl.h"
|
||||||
#include "third_party/WebKit/Source/WebKit/chromium/public/WebBlobData.h"
|
#include "third_party/WebKit/Source/WebKit/chromium/public/WebBlobData.h"
|
||||||
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h"
|
|
||||||
#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
|
|
||||||
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
|
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
|
||||||
#include "webkit/blob/blob_data.h"
|
#include "webkit/blob/blob_data.h"
|
||||||
#include "webkit/blob/blob_storage_controller.h"
|
#include "webkit/blob/blob_storage_controller.h"
|
||||||
|
|
||||||
using WebKit::WebBlobData;
|
using WebKit::WebBlobData;
|
||||||
using WebKit::WebString;
|
|
||||||
using WebKit::WebURL;
|
using WebKit::WebURL;
|
||||||
|
using webkit_blob::BlobData;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
MessageLoop* g_io_thread;
|
MessageLoop* g_io_thread;
|
||||||
webkit_blob::BlobStorageController* g_blob_storage_controller;
|
webkit_blob::BlobStorageController* g_blob_storage_controller;
|
||||||
|
|
||||||
// WebURL contains a WebCString object that is ref-counted,
|
|
||||||
// but not thread-safe ref-counted.
|
|
||||||
// "Normal" copying of WebURL results in a copy that is not thread-safe.
|
|
||||||
// This method creates a deep copy of WebURL.
|
|
||||||
WebURL GetWebURLThreadsafeCopy(const WebURL& source) {
|
|
||||||
const WebKit::WebCString spec(source.spec().data(), source.spec().length());
|
|
||||||
const url_parse::Parsed& parsed(source.parsed());
|
|
||||||
const bool is_valid = source.isValid();
|
|
||||||
return WebURL(spec, parsed, is_valid);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
|
@ -55,70 +42,43 @@ BrowserWebBlobRegistryImpl::BrowserWebBlobRegistryImpl() {
|
||||||
void BrowserWebBlobRegistryImpl::registerBlobURL(
|
void BrowserWebBlobRegistryImpl::registerBlobURL(
|
||||||
const WebURL& url, WebBlobData& data) {
|
const WebURL& url, WebBlobData& data) {
|
||||||
DCHECK(g_io_thread);
|
DCHECK(g_io_thread);
|
||||||
CancelableTask* task;
|
GURL thread_safe_url = url; // WebURL uses refcounted strings.
|
||||||
{
|
g_io_thread->PostTask(FROM_HERE, base::Bind(
|
||||||
scoped_refptr<webkit_blob::BlobData> blob_data(
|
&BrowserWebBlobRegistryImpl::AddFinishedBlob, this,
|
||||||
new webkit_blob::BlobData(data));
|
thread_safe_url, make_scoped_refptr(new BlobData(data))));
|
||||||
WebURL url_copy = GetWebURLThreadsafeCopy(url);
|
|
||||||
task =
|
|
||||||
NewRunnableMethod(
|
|
||||||
this, &BrowserWebBlobRegistryImpl::DoRegisterBlobUrl, url_copy,
|
|
||||||
blob_data);
|
|
||||||
// After this block exits, url_copy is disposed, and
|
|
||||||
// the underlying WebCString will have a refcount=1 and will
|
|
||||||
// only be accessible from the task object.
|
|
||||||
}
|
|
||||||
g_io_thread->PostTask(FROM_HERE, task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWebBlobRegistryImpl::registerBlobURL(
|
void BrowserWebBlobRegistryImpl::registerBlobURL(
|
||||||
const WebURL& url, const WebURL& src_url) {
|
const WebURL& url, const WebURL& src_url) {
|
||||||
DCHECK(g_io_thread);
|
DCHECK(g_io_thread);
|
||||||
CancelableTask* task;
|
GURL thread_safe_url = url;
|
||||||
{
|
GURL thread_safe_src_url = src_url;
|
||||||
WebURL url_copy = GetWebURLThreadsafeCopy(url);
|
g_io_thread->PostTask(FROM_HERE, base::Bind(
|
||||||
WebURL src_url_copy = GetWebURLThreadsafeCopy(src_url);
|
&BrowserWebBlobRegistryImpl::CloneBlob, this,
|
||||||
task =
|
thread_safe_url, thread_safe_src_url));
|
||||||
NewRunnableMethod(this,
|
|
||||||
&BrowserWebBlobRegistryImpl::DoRegisterBlobUrlFrom,
|
|
||||||
url_copy,
|
|
||||||
src_url_copy);
|
|
||||||
// After this block exits, url_copy and src_url_copy are disposed, and
|
|
||||||
// the underlying WebCStrings will have a refcount=1 and will
|
|
||||||
// only be accessible from the task object.
|
|
||||||
}
|
|
||||||
g_io_thread->PostTask(FROM_HERE, task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWebBlobRegistryImpl::unregisterBlobURL(const WebURL& url) {
|
void BrowserWebBlobRegistryImpl::unregisterBlobURL(const WebURL& url) {
|
||||||
DCHECK(g_io_thread);
|
DCHECK(g_io_thread);
|
||||||
CancelableTask* task;
|
GURL thread_safe_url = url;
|
||||||
{
|
g_io_thread->PostTask(FROM_HERE, base::Bind(
|
||||||
WebURL url_copy = GetWebURLThreadsafeCopy(url);
|
&BrowserWebBlobRegistryImpl::RemoveBlob, this,
|
||||||
task =
|
thread_safe_url));
|
||||||
NewRunnableMethod(this,
|
|
||||||
&BrowserWebBlobRegistryImpl::DoUnregisterBlobUrl,
|
|
||||||
url_copy);
|
|
||||||
// After this block exits, url_copy is disposed, and
|
|
||||||
// the underlying WebCString will have a refcount=1 and will
|
|
||||||
// only be accessible from the task object.
|
|
||||||
}
|
|
||||||
g_io_thread->PostTask(FROM_HERE, task);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWebBlobRegistryImpl::DoRegisterBlobUrl(
|
void BrowserWebBlobRegistryImpl::AddFinishedBlob(
|
||||||
const GURL& url, webkit_blob::BlobData* blob_data) {
|
const GURL& url, BlobData* blob_data) {
|
||||||
DCHECK(g_blob_storage_controller);
|
DCHECK(g_blob_storage_controller);
|
||||||
g_blob_storage_controller->RegisterBlobUrl(url, blob_data);
|
g_blob_storage_controller->AddFinishedBlob(url, blob_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWebBlobRegistryImpl::DoRegisterBlobUrlFrom(
|
void BrowserWebBlobRegistryImpl::CloneBlob(
|
||||||
const GURL& url, const GURL& src_url) {
|
const GURL& url, const GURL& src_url) {
|
||||||
DCHECK(g_blob_storage_controller);
|
DCHECK(g_blob_storage_controller);
|
||||||
g_blob_storage_controller->RegisterBlobUrlFrom(url, src_url);
|
g_blob_storage_controller->CloneBlob(url, src_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWebBlobRegistryImpl::DoUnregisterBlobUrl(const GURL& url) {
|
void BrowserWebBlobRegistryImpl::RemoveBlob(const GURL& url) {
|
||||||
DCHECK(g_blob_storage_controller);
|
DCHECK(g_blob_storage_controller);
|
||||||
g_blob_storage_controller->UnregisterBlobUrl(url);
|
g_blob_storage_controller->RemoveBlob(url);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -32,15 +32,14 @@ class BrowserWebBlobRegistryImpl
|
||||||
const WebKit::WebURL& src_url);
|
const WebKit::WebURL& src_url);
|
||||||
virtual void unregisterBlobURL(const WebKit::WebURL& url);
|
virtual void unregisterBlobURL(const WebKit::WebURL& url);
|
||||||
|
|
||||||
// Run on I/O thread.
|
private:
|
||||||
void DoRegisterBlobUrl(const GURL& url, webkit_blob::BlobData* blob_data);
|
|
||||||
void DoRegisterBlobUrlFrom(const GURL& url, const GURL& src_url);
|
|
||||||
void DoUnregisterBlobUrl(const GURL& url);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class base::RefCountedThreadSafe<BrowserWebBlobRegistryImpl>;
|
friend class base::RefCountedThreadSafe<BrowserWebBlobRegistryImpl>;
|
||||||
|
|
||||||
private:
|
// Run on I/O thread.
|
||||||
|
void AddFinishedBlob(const GURL& url, webkit_blob::BlobData* blob_data);
|
||||||
|
void CloneBlob(const GURL& url, const GURL& src_url);
|
||||||
|
void RemoveBlob(const GURL& url);
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(BrowserWebBlobRegistryImpl);
|
DISALLOW_COPY_AND_ASSIGN(BrowserWebBlobRegistryImpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -225,7 +225,7 @@ WebKit::WebGraphicsContext3D* BrowserWebKitInit::createGraphicsContext3D() {
|
||||||
return new webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl();
|
return new webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl();
|
||||||
} else {
|
} else {
|
||||||
return new webkit::gpu::WebGraphicsContext3DInProcessImpl(
|
return new webkit::gpu::WebGraphicsContext3DInProcessImpl(
|
||||||
gfx::kNullPluginWindow);
|
gfx::kNullPluginWindow, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -683,6 +683,7 @@ WebMediaPlayer* BrowserWebViewDelegate::createMediaPlayer(
|
||||||
|
|
||||||
scoped_ptr<webkit_glue::WebMediaPlayerImpl> result(
|
scoped_ptr<webkit_glue::WebMediaPlayerImpl> result(
|
||||||
new webkit_glue::WebMediaPlayerImpl(client,
|
new webkit_glue::WebMediaPlayerImpl(client,
|
||||||
|
NULL,
|
||||||
collection.release(),
|
collection.release(),
|
||||||
message_loop_factory.release(),
|
message_loop_factory.release(),
|
||||||
NULL,
|
NULL,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "base/bind.h"
|
||||||
#include "base/file_path.h"
|
#include "base/file_path.h"
|
||||||
#include "base/file_util.h"
|
#include "base/file_util.h"
|
||||||
#include "base/string_util.h"
|
#include "base/string_util.h"
|
||||||
|
@ -59,9 +60,9 @@ int64 DOMStorageContext::CloneSessionStorage(int64 original_id) {
|
||||||
DCHECK(!CefThread::CurrentlyOn(CefThread::UI));
|
DCHECK(!CefThread::CurrentlyOn(CefThread::UI));
|
||||||
int64 clone_id = AllocateSessionStorageNamespaceId();
|
int64 clone_id = AllocateSessionStorageNamespaceId();
|
||||||
CefThread::PostTask(
|
CefThread::PostTask(
|
||||||
CefThread::UI, FROM_HERE, NewRunnableFunction(
|
CefThread::UI, FROM_HERE,
|
||||||
&DOMStorageContext::CompleteCloningSessionStorage,
|
base::Bind(&DOMStorageContext::CompleteCloningSessionStorage, this,
|
||||||
this, original_id, clone_id));
|
original_id, clone_id));
|
||||||
return clone_id;
|
return clone_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ void GenerateFileName(const GURL& url,
|
||||||
referrer_charset,
|
referrer_charset,
|
||||||
suggested_name,
|
suggested_name,
|
||||||
mime_type,
|
mime_type,
|
||||||
ASCIIToUTF16("download"));
|
"download");
|
||||||
|
|
||||||
// TODO(evan): this code is totally wrong -- we should just generate
|
// TODO(evan): this code is totally wrong -- we should just generate
|
||||||
// Unicode filenames and do all this encoding switching at the end.
|
// Unicode filenames and do all this encoding switching at the end.
|
||||||
|
|
|
@ -65,7 +65,7 @@ FilePath GetFileNameFromDragData(const WebDropData& drop_data) {
|
||||||
if (file_name.empty()) {
|
if (file_name.empty()) {
|
||||||
// Retrieve the name from the URL.
|
// Retrieve the name from the URL.
|
||||||
string16 suggested_filename =
|
string16 suggested_filename =
|
||||||
net::GetSuggestedFilename(drop_data.url, "", "", "", "", string16());
|
net::GetSuggestedFilename(drop_data.url, "", "", "", "", "");
|
||||||
file_name = FilePathFromFilename(suggested_filename);
|
file_name = FilePathFromFilename(suggested_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
Index: message_loop.cc
|
Index: message_loop.cc
|
||||||
===================================================================
|
===================================================================
|
||||||
--- message_loop.cc (revision 105051)
|
--- message_loop.cc (revision 106500)
|
||||||
+++ message_loop.cc (working copy)
|
+++ message_loop.cc (working copy)
|
||||||
@@ -394,9 +394,13 @@
|
@@ -400,9 +400,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageLoop::AssertIdle() const {
|
void MessageLoop::AssertIdle() const {
|
||||||
|
@ -19,9 +19,9 @@ Index: message_loop.cc
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
Index: message_loop.h
|
Index: message_loop.h
|
||||||
===================================================================
|
===================================================================
|
||||||
--- message_loop.h (revision 105051)
|
--- message_loop.h (revision 106500)
|
||||||
+++ message_loop.h (working copy)
|
+++ message_loop.h (working copy)
|
||||||
@@ -363,6 +363,9 @@
|
@@ -367,6 +367,9 @@
|
||||||
// Asserts that the MessageLoop is "idle".
|
// Asserts that the MessageLoop is "idle".
|
||||||
void AssertIdle() const;
|
void AssertIdle() const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue