summaryrefslogtreecommitdiffstats
path: root/chrome/common/net
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/common/net
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/net')
-rw-r--r--chrome/common/net/cache_uitest.cc201
-rw-r--r--chrome/common/net/cookie_monster_sqlite.cc378
-rw-r--r--chrome/common/net/cookie_monster_sqlite.h76
-rw-r--r--chrome/common/net/dns.h49
-rw-r--r--chrome/common/net/url_request_intercept_job.cc234
-rw-r--r--chrome/common/net/url_request_intercept_job.h88
-rw-r--r--chrome/common/net/url_util_unittest.cc77
7 files changed, 1103 insertions, 0 deletions
diff --git a/chrome/common/net/cache_uitest.cc b/chrome/common/net/cache_uitest.cc
new file mode 100644
index 0000000..36ad388
--- /dev/null
+++ b/chrome/common/net/cache_uitest.cc
@@ -0,0 +1,201 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include "base/string_util.h"
+#include "chrome/test/ui/ui_test.h"
+#include "chrome/test/automation/automation_proxy.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "net/url_request/url_request_unittest.h"
+
+// The CacheTest class extends the UITest class and provides functions to
+// get a new tab and to run the tests on a particular path
+//
+// Typical usage:
+//
+// 1. Provide this class as the TestCase for TEST_F macro
+// 2. Then run the cache test on a specific path using the function
+// RunCacheTest
+//
+// For example:
+//
+// TEST_F(CacheTest, NoCacheMaxAge) {
+// RunCacheTest(L"nocachetime/maxage", false, false);
+// }
+//
+// Note that delays used in running the test is initialized to defaults
+class CacheTest : public UITest {
+ protected:
+
+ // Runs the cache test for the specified path.
+ // Can specify the test to check if a new tab is loaded from the cache
+ // and also if a delayed reload is required. A true value passed to the
+ // third parameter causes a delayed reload of the path in a new tab.
+ // The amount of delay is set by a class constant.
+ void RunCacheTest(const std::wstring &url,
+ bool expect_new_tab_cached,
+ bool expect_delayed_reload);
+
+ private:
+ // Class constants
+ static const int kWaitForCacheUpdateMsec = 2000;
+ static const int kCacheWaitMultiplier = 4; // Used to increase delay
+
+ // Appends a new tab to the test chrome window and loads the specified
+ // URL. The new tab will try to get the URL from the cache before requesting
+ // the server for it.
+ void GetNewTab(AutomationProxy* automationProxy, const GURL& tab_url);
+};
+
+// Runs the cache test for the specified path.
+void CacheTest::RunCacheTest(const std::wstring &url,
+ bool expect_new_tab_cached,
+ bool expect_delayed_reload) {
+ TestServer server(L"chrome/test/data");
+ GURL test_page(server.TestServerPageW(url));
+
+ NavigateToURL(test_page);
+ std::wstring original_time = GetActiveTabTitle();
+
+ Sleep(kWaitForCacheUpdateMsec);
+
+ GetNewTab(automation(), test_page);
+ std::wstring revisit_time = GetActiveTabTitle();
+
+ if (expect_new_tab_cached) {
+ EXPECT_EQ(original_time, revisit_time);
+ }else {
+ EXPECT_NE(original_time, revisit_time);
+ }
+
+ Sleep(kWaitForCacheUpdateMsec);
+
+ // Force reload, overriding the caching behavior
+ NavigateToURL(test_page);
+ std::wstring reload_time = GetActiveTabTitle();
+
+ EXPECT_NE(revisit_time, reload_time);
+
+ if (expect_delayed_reload) {
+ Sleep(kWaitForCacheUpdateMsec * kCacheWaitMultiplier);
+
+ GetNewTab(automation(), test_page);
+ revisit_time = GetActiveTabTitle();
+
+ EXPECT_NE(reload_time, revisit_time);
+ }
+}
+
+// Appends a new tab to the test chrome window and loads the specified URL.
+void CacheTest::GetNewTab(AutomationProxy* automationProxy,
+ const GURL& tab_url) {
+ scoped_ptr<BrowserProxy> window_proxy(automationProxy->GetBrowserWindow(0));
+ ASSERT_TRUE(window_proxy.get());
+ ASSERT_TRUE(window_proxy->AppendTab(tab_url));
+}
+
+// Tests that a cached copy of the page is not used when max-age=0 headers
+// are specified.
+TEST_F(CacheTest, NoCacheMaxAge) {
+ RunCacheTest(L"nocachetime/maxage", false, false);
+}
+
+// Tests that a cached copy of the page is not used when no-cache header
+// is specified.
+TEST_F(CacheTest, NoCache) {
+ RunCacheTest(L"nocachetime", false, false);
+}
+
+// Tests that a cached copy of a page is used when its headers specify
+// that it should be cached for 60 seconds.
+TEST_F(CacheTest, Cache) {
+ RunCacheTest(L"cachetime", true, false);
+}
+
+// Tests that a cached copy of the page is used when expires header
+// specifies that the page has not yet expired.
+TEST_F(CacheTest, Expires) {
+ RunCacheTest(L"cache/expires", true, false);
+}
+
+// Tests that a cached copy of the page is used when proxy-revalidate header
+// is specified and the page has not yet expired.
+TEST_F(CacheTest, ProxyRevalidate) {
+ RunCacheTest(L"cache/proxy-revalidate", true, false);
+}
+
+// Tests that a cached copy of the page is used when private header
+// is specified and the page has not yet expired.
+TEST_F(CacheTest, Private) {
+ RunCacheTest(L"cache/private", true, true);
+}
+
+// Tests that a cached copy of the page is used when public header
+// is specified and the page has not yet expired.
+TEST_F(CacheTest, Public) {
+ RunCacheTest(L"cache/public", true, true);
+}
+
+// Tests that a cached copy of the page is not used when s-maxage header
+// is specified.
+TEST_F(CacheTest, SMaxAge) {
+ RunCacheTest(L"cache/s-maxage", false, false);
+}
+
+// Tests that a cached copy of the page is not used when must-revalidate header
+// is specified.
+TEST_F(CacheTest, MustRevalidate) {
+ RunCacheTest(L"cache/must-revalidate", false, false);
+}
+
+// Tests that a cached copy of the page is not used when must-revalidate header
+// is specified, even though the page has not yet expired.
+TEST_F(CacheTest, MustRevalidateMaxAge) {
+ RunCacheTest(L"cache/must-revalidate/max-age", false, false);
+}
+
+// Tests that a cached copy of the page is not used when no-store header
+// is specified.
+TEST_F(CacheTest, NoStore) {
+ RunCacheTest(L"cache/no-store", false, false);
+}
+
+// Tests that a cached copy of the page is not used when no-store header
+// is specified, even though the page has not yet expired.
+TEST_F(CacheTest, NoStoreMaxAge) {
+ RunCacheTest(L"cache/no-store/max-age", false, false);
+}
+
+// Tests that a cached copy of the page is not transformed when no-transform
+// header is specified.
+TEST_F(CacheTest, NoTransform) {
+ RunCacheTest(L"cache/no-transform", false, false);
+}
diff --git a/chrome/common/net/cookie_monster_sqlite.cc b/chrome/common/net/cookie_monster_sqlite.cc
new file mode 100644
index 0000000..259b8f2
--- /dev/null
+++ b/chrome/common/net/cookie_monster_sqlite.cc
@@ -0,0 +1,378 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/common/net/cookie_monster_sqlite.h"
+
+#include "base/logging.h"
+#include "base/ref_counted.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "chrome/common/sqlite_compiled_statement.h"
+#include "chrome/common/sqlite_utils.h"
+
+// This class is designed to be shared between any calling threads and the
+// database thread. It batches operations and commits them on a timer.
+class SQLitePersistentCookieStore::Backend
+ : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> {
+ public:
+ // The passed database pointer must be already-initialized. This object will
+ // take ownership.
+ explicit Backend(sqlite3* db, MessageLoop* loop)
+ : db_(db),
+ background_loop_(loop),
+ cache_(new SqliteStatementCache(db)),
+ num_pending_(0) {
+ DCHECK(db_) << "Database must exist.";
+ }
+
+ // You should call Close() before destructing this object.
+ ~Backend() {
+ DCHECK(!db_) << "Close should have already been called.";
+ DCHECK(num_pending_ == 0 && pending_.empty());
+ }
+
+ // Batch a cookie add
+ void AddCookie(const std::string& key,
+ const CookieMonster::CanonicalCookie& cc);
+ // Batch a cookie delete
+ void DeleteCookie(const CookieMonster::CanonicalCookie& cc);
+ // Commit and pending operations and close the database, must be called
+ // before the object is destructed.
+ void Close();
+
+ private:
+ class PendingOperation {
+ public:
+ typedef enum {
+ COOKIE_ADD,
+ COOKIE_DELETE,
+ } OperationType;
+
+ PendingOperation(OperationType op,
+ const std::string& key,
+ const CookieMonster::CanonicalCookie& cc)
+ : op_(op), key_(key), cc_(cc) { }
+
+ OperationType op() const { return op_; }
+ const std::string& key() const { return key_; }
+ const CookieMonster::CanonicalCookie& cc() const { return cc_; }
+
+ private:
+ OperationType op_;
+ std::string key_; // Only used for OP_ADD
+ CookieMonster::CanonicalCookie cc_;
+ };
+
+ private:
+ // Batch a cookie operation (add or delete)
+ void BatchOperation(PendingOperation::OperationType op,
+ const std::string& key,
+ const CookieMonster::CanonicalCookie& cc);
+ // Commit our pending operations to the database.
+ void Commit();
+ // Close() executed on the background thread.
+ void InternalBackgroundClose();
+
+ sqlite3* db_;
+ MessageLoop* background_loop_;
+ SqliteStatementCache* cache_;
+
+ typedef std::list<PendingOperation*> PendingOperationsList;
+ PendingOperationsList pending_;
+ PendingOperationsList::size_type num_pending_;
+ Lock pending_lock_; // Guard pending_ and num_pending_
+
+ DISALLOW_EVIL_CONSTRUCTORS(Backend);
+};
+
+void SQLitePersistentCookieStore::Backend::AddCookie(
+ const std::string& key,
+ const CookieMonster::CanonicalCookie& cc) {
+ BatchOperation(PendingOperation::COOKIE_ADD, key, cc);
+}
+
+void SQLitePersistentCookieStore::Backend::DeleteCookie(
+ const CookieMonster::CanonicalCookie& cc) {
+ BatchOperation(PendingOperation::COOKIE_DELETE, std::string(), cc);
+}
+
+void SQLitePersistentCookieStore::Backend::BatchOperation(
+ PendingOperation::OperationType op,
+ const std::string& key,
+ const CookieMonster::CanonicalCookie& cc) {
+ // Commit every 30 seconds.
+ static const int kCommitIntervalMs = 30 * 1000;
+ // Commit right away if we have more than 512 outstanding operations.
+ static const int kCommitAfterBatchSize = 512;
+ DCHECK(MessageLoop::current() != background_loop_);
+
+ // We do a full copy of the cookie here, and hopefully just here.
+ scoped_ptr<PendingOperation> po(new PendingOperation(op, key, cc));
+ CHECK(po.get());
+
+ PendingOperationsList::size_type num_pending;
+ {
+ AutoLock locked(pending_lock_);
+ pending_.push_back(po.release());
+ num_pending = ++num_pending_;
+ }
+
+ // TODO(abarth): What if the DB thread is being destroyed on the UI thread?
+ if (num_pending == 1) {
+ // We've gotten our first entry for this batch, fire off the timer.
+ background_loop_->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs);
+ } else if (num_pending == kCommitAfterBatchSize) {
+ // We've reached a big enough batch, fire off a commit now.
+ background_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &Backend::Commit));
+ }
+}
+
+void SQLitePersistentCookieStore::Backend::Commit() {
+ DCHECK(MessageLoop::current() == background_loop_);
+ PendingOperationsList ops;
+ {
+ AutoLock locked(pending_lock_);
+ pending_.swap(ops);
+ num_pending_ = 0;
+ }
+
+ // Maybe an old timer fired or we are already Close()'ed.
+ if (!db_ || ops.empty())
+ return;
+
+ SQLITE_UNIQUE_STATEMENT(add_smt, *cache_,
+ "INSERT INTO cookies VALUES (?,?,?,?,?,?,?,?)");
+ if (!add_smt.is_valid()) {
+ NOTREACHED();
+ return;
+ }
+ SQLITE_UNIQUE_STATEMENT(del_smt, *cache_,
+ "DELETE FROM cookies WHERE creation_utc=?");
+ if (!del_smt.is_valid()) {
+ NOTREACHED();
+ return;
+ }
+
+ SQLTransaction transaction(db_);
+ transaction.Begin();
+ for (PendingOperationsList::iterator it = ops.begin();
+ it != ops.end(); ++it) {
+ // Free the cookies as we commit them to the database.
+ scoped_ptr<PendingOperation> po(*it);
+ switch (po->op()) {
+ case PendingOperation::COOKIE_ADD:
+ add_smt->reset();
+ add_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue());
+ add_smt->bind_string(1, po->key());
+ add_smt->bind_string(2, po->cc().Name());
+ add_smt->bind_string(3, po->cc().Value());
+ add_smt->bind_string(4, po->cc().Path());
+ add_smt->bind_int64(5, po->cc().ExpiryDate().ToInternalValue());
+ add_smt->bind_int(6, po->cc().IsSecure());
+ add_smt->bind_int(7, po->cc().IsHttpOnly());
+ if (add_smt->step() != SQLITE_DONE) {
+ NOTREACHED() << "Could not add a cookie to the DB.";
+ }
+ break;
+ case PendingOperation::COOKIE_DELETE:
+ del_smt->reset();
+ del_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue());
+ if (del_smt->step() != SQLITE_DONE) {
+ NOTREACHED() << "Could not delete a cookie from the DB.";
+ }
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+ transaction.Commit();
+}
+
+// Fire off a close message to the background thread. We could still have a
+// pending commit timer that will be holding a reference on us, but if/when
+// this fires we will already have been cleaned up and it will be ignored.
+void SQLitePersistentCookieStore::Backend::Close() {
+ DCHECK(MessageLoop::current() != background_loop_);
+ // Must close the backend on the background thread.
+ // TODO(abarth): What if the DB thread is being destroyed on the UI thread?
+ background_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &Backend::InternalBackgroundClose));
+}
+
+void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() {
+ DCHECK(MessageLoop::current() == background_loop_);
+ // Commit any pending operations
+ Commit();
+ // We must destroy the cache before closing the database.
+ delete cache_;
+ cache_ = NULL;
+ sqlite3_close(db_);
+ db_ = NULL;
+}
+
+SQLitePersistentCookieStore::SQLitePersistentCookieStore(
+ const std::wstring& path, MessageLoop* background_loop)
+ : path_(path),
+ background_loop_(background_loop) {
+ DCHECK(background_loop) << "SQLitePersistentCookieStore needs a MessageLoop";
+}
+
+SQLitePersistentCookieStore::~SQLitePersistentCookieStore() {
+ if (backend_.get()) {
+ backend_->Close();
+ // Release our reference, it will probably still have a reference if the
+ // background thread has not run Close() yet.
+ backend_ = NULL;
+ }
+}
+
+// Version number of the database.
+static const int kCurrentVersionNumber = 2;
+
+namespace {
+
+// Initializes the cookies table, returning true on success.
+bool InitTable(sqlite3* db) {
+ if (!DoesSqliteTableExist(db, "cookies")) {
+ if (sqlite3_exec(db, "CREATE TABLE cookies ("
+ "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY,"
+ "host_key TEXT NOT NULL,"
+ "name TEXT NOT NULL,"
+ "value TEXT NOT NULL,"
+ "path TEXT NOT NULL,"
+ // We only store persistent, so we know it expires
+ "expires_utc INTEGER NOT NULL,"
+ "secure INTEGER NOT NULL,"
+ "httponly INTEGER NOT NULL)",
+ NULL, NULL, NULL) != SQLITE_OK)
+ return false;
+ }
+
+ // Try to create the index every time. Older versions did not have this index,
+ // so we want those people to get it. Ignore errors, since it may exist.
+ sqlite3_exec(db, "CREATE INDEX cookie_times ON cookies (creation_utc)",
+ NULL, NULL, NULL);
+
+ return true;
+}
+
+} // namespace
+
+bool SQLitePersistentCookieStore::Load(
+ std::vector<CookieMonster::KeyedCanonicalCookie>* cookies) {
+ DCHECK(!path_.empty());
+ sqlite3* db;
+ if (sqlite3_open(WideToUTF8(path_).c_str(), &db) != SQLITE_OK) {
+ NOTREACHED() << "Unable to open cookie DB.";
+ return false;
+ }
+
+ if (!EnsureDatabaseVersion(db) || !InitTable(db)) {
+ NOTREACHED() << "Unable to initialize cookie DB.";
+ sqlite3_close(db);
+ return false;
+ }
+
+ // Slurp all the cookies into the out-vector.
+ SQLStatement smt;
+ if (smt.prepare(db, "SELECT * FROM cookies") != SQLITE_OK) {
+ NOTREACHED() << "select statement prep failed";
+ sqlite3_close(db);
+ return false;
+ }
+
+ // Step the statement and do the preload operation. Preload() requires that a
+ // statement be open on the database, so we oblige. We don't bother keeping
+ // this and reset the statement so we can read all the rows.
+ //
+ // The Preload call makes the read faster, since we will be reading most of
+ // the database, it's better to read it in one chunk than have a lot of seeks
+ // bringing it in organically.
+ if (smt.step() != SQLITE_ROW)
+ sqlite3Preload(db);
+ smt.reset();
+
+ while (smt.step() == SQLITE_ROW) {
+ std::string key = smt.column_string(1);
+ scoped_ptr<CookieMonster::CanonicalCookie> cc(
+ new CookieMonster::CanonicalCookie(
+ smt.column_string(2), // name
+ smt.column_string(3), // value
+ smt.column_string(4), // path
+ smt.column_int(6) != 0, // secure
+ smt.column_int(7) != 0, // httponly
+ Time::FromInternalValue(smt.column_int64(0)), // creation_utc
+ true, // has_expires
+ Time::FromInternalValue(smt.column_int64(5)))); // expires_utc
+ // Memory allocation failed.
+ if (!cc.get())
+ break;
+
+ DLOG_IF(WARNING,
+ cc->CreationDate() > Time::Now()) << L"CreationDate too recent";
+ cookies->push_back(
+ CookieMonster::KeyedCanonicalCookie(smt.column_string(1),
+ cc.release()));
+ }
+
+ // Create the backend, this will take ownership of the db pointer.
+ backend_ = new Backend(db, background_loop_);
+
+ return true;
+}
+
+bool SQLitePersistentCookieStore::EnsureDatabaseVersion(sqlite3* db) {
+ // Version check.
+ if (!meta_table_.Init(std::string(), kCurrentVersionNumber, db))
+ return false;
+
+ int compat_version = meta_table_.GetCompatibleVersionNumber();
+ if (compat_version > kCurrentVersionNumber) {
+ DLOG(WARNING) << "Cookie DB version from the future: " << compat_version;
+ return false;
+ }
+
+ return true;
+}
+
+void SQLitePersistentCookieStore::AddCookie(
+ const std::string& key,
+ const CookieMonster::CanonicalCookie& cc) {
+ if (backend_.get())
+ backend_->AddCookie(key, cc);
+}
+
+void SQLitePersistentCookieStore::DeleteCookie(
+ const CookieMonster::CanonicalCookie& cc) {
+ if (backend_.get())
+ backend_->DeleteCookie(cc);
+}
diff --git a/chrome/common/net/cookie_monster_sqlite.h b/chrome/common/net/cookie_monster_sqlite.h
new file mode 100644
index 0000000..49de96a
--- /dev/null
+++ b/chrome/common/net/cookie_monster_sqlite.h
@@ -0,0 +1,76 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A sqlite implementation of a cookie monster persistent store.
+
+#ifndef CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__
+#define CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/message_loop.h"
+#include "chrome/browser/meta_table_helper.h"
+#include "net/base/cookie_monster.h"
+
+struct sqlite3;
+
+class SQLitePersistentCookieStore
+ : public CookieMonster::PersistentCookieStore {
+ public:
+ SQLitePersistentCookieStore(const std::wstring& path,
+ MessageLoop* background_loop);
+ ~SQLitePersistentCookieStore();
+
+ virtual bool Load(std::vector<CookieMonster::KeyedCanonicalCookie>*);
+
+ virtual void AddCookie(const std::string&,
+ const CookieMonster::CanonicalCookie&);
+ virtual void DeleteCookie(const CookieMonster::CanonicalCookie&);
+
+ private:
+ class Backend;
+
+ // Database upgrade statements.
+ bool EnsureDatabaseVersion(sqlite3* db);
+ bool UpdateSchemaToVersion2(sqlite3* db);
+
+ std::wstring path_;
+ scoped_refptr<Backend> backend_;
+
+ // Background MessageLoop on which to access the backend_;
+ MessageLoop* background_loop_;
+
+ MetaTableHelper meta_table_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SQLitePersistentCookieStore);
+};
+
+#endif // CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__
diff --git a/chrome/common/net/dns.h b/chrome/common/net/dns.h
new file mode 100644
index 0000000..9a178e3
--- /dev/null
+++ b/chrome/common/net/dns.h
@@ -0,0 +1,49 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file has shared types used across IPC between render_dns_master.cc
+// and dns_master.cc
+
+
+#ifndef CHROME_COMMON_DNS_H__
+#define CHROME_COMMON_DNS_H__
+
+#include <vector>
+
+namespace chrome_common_net{
+
+// IPC messages are passed from the renderer to the browser in the form of
+// Namelist instances.
+// Each element of this vector is a hostname that needs to be looked up.
+// The hostnames should never be empty strings.
+typedef std::vector<std::string> NameList;
+
+}
+
+#endif // CHROME_COMMON_DNS_H__ \ No newline at end of file
diff --git a/chrome/common/net/url_request_intercept_job.cc b/chrome/common/net/url_request_intercept_job.cc
new file mode 100644
index 0000000..4da58d0c
--- /dev/null
+++ b/chrome/common/net/url_request_intercept_job.cc
@@ -0,0 +1,234 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This job type handles Chrome plugin network interception. When a plugin
+// wants to intercept a request, a job of this type is created. The intercept
+// job communicates with the plugin to retrieve the response headers and data.
+
+#include "chrome/common/net/url_request_intercept_job.h"
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_plugin_lib.h"
+#include "net/base/net_errors.h"
+
+//
+// URLRequestInterceptJob
+//
+
+URLRequestInterceptJob::URLRequestInterceptJob(URLRequest* request,
+ ChromePluginLib* plugin,
+ ScopableCPRequest* cprequest)
+ : URLRequestJob(request),
+ cprequest_(cprequest),
+ plugin_(plugin),
+ read_buffer_(NULL),
+ got_headers_(false) {
+ cprequest_->data = this; // see FromCPRequest().
+
+ NotificationService::current()->AddObserver(
+ this, NOTIFY_CHROME_PLUGIN_UNLOADED,
+ Source<ChromePluginLib>(plugin_));
+}
+
+URLRequestInterceptJob::~URLRequestInterceptJob() {
+ if (plugin_) {
+ plugin_->functions().request_funcs->end_request(cprequest_.get(),
+ CPERR_SUCCESS);
+ DetachPlugin();
+ }
+}
+
+void URLRequestInterceptJob::DetachPlugin() {
+ NotificationService::current()->RemoveObserver(
+ this, NOTIFY_CHROME_PLUGIN_UNLOADED,
+ Source<ChromePluginLib>(plugin_));
+ plugin_ = NULL;
+}
+
+void URLRequestInterceptJob::Start() {
+ // Start reading asynchronously so that all error reporting and data
+ // callbacks happen as they would for network requests.
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &URLRequestInterceptJob::StartAsync));
+}
+
+void URLRequestInterceptJob::Kill() {
+ if (plugin_) {
+ plugin_->functions().request_funcs->end_request(cprequest_.get(),
+ CPERR_CANCELLED);
+ DetachPlugin();
+ }
+ URLRequestJob::Kill();
+}
+
+bool URLRequestInterceptJob::ReadRawData(char* dest, int dest_size,
+ int* bytes_read) {
+ DCHECK_NE(dest_size, 0);
+ DCHECK(bytes_read);
+
+ if (!plugin_)
+ return false;
+
+ int rv = plugin_->functions().request_funcs->read(cprequest_.get(),
+ dest, dest_size);
+ if (rv >= 0) {
+ *bytes_read = rv;
+ return true;
+ }
+
+ if (rv == CPERR_IO_PENDING) {
+ read_buffer_ = dest;
+ read_buffer_size_ = dest_size;
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+ } else {
+ // TODO(mpcomplete): better error code
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED));
+ }
+
+ return false;
+}
+
+bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) {
+ return request_->response_headers()->GetMimeType(mime_type);
+}
+
+bool URLRequestInterceptJob::GetCharset(std::string* charset) {
+ return request_->response_headers()->GetCharset(charset);
+}
+
+bool URLRequestInterceptJob::GetContentEncoding(std::string* encoding_type) {
+ // TODO(darin): what if there are multiple content encodings?
+ return request_->response_headers()->EnumerateHeader(NULL, "Content-Encoding",
+ encoding_type);
+}
+
+void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) {
+ if (!plugin_)
+ return;
+
+ std::string raw_headers;
+ int size = plugin_->functions().request_funcs->get_response_info(
+ cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0);
+ int rv = size < 0 ? size :
+ plugin_->functions().request_funcs->get_response_info(
+ cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS,
+ WriteInto(&raw_headers, size+1), size);
+ if (rv != CPERR_SUCCESS) {
+ // TODO(mpcomplete): what should we do here?
+ raw_headers = "HTTP/1.1 404 Not Found" + '\0';
+ }
+
+ info->headers = new net::HttpResponseHeaders(raw_headers);
+
+ if (request_->url().SchemeIsSecure()) {
+ // Make up a fake certificate for intercepted data since we don't have
+ // access to the real SSL info.
+ // TODO(mpcomplete): we should probably have the interception API transmit
+ // the SSL info, but the only consumer of this API (Gears) doesn't keep that
+ // around. We should change that.
+ const char* kCertIssuer = "Chrome Internal";
+ const int kLifetimeDays = 100;
+
+ DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL "
+ << request_->url();
+
+ info->ssl_info.cert =
+ new X509Certificate(request_->url().GetWithEmptyPath().spec(),
+ kCertIssuer,
+ Time::Now(),
+ Time::Now() + TimeDelta::FromDays(kLifetimeDays));
+ info->ssl_info.cert_status = 0;
+ info->ssl_info.security_bits = 0;
+ }
+}
+
+int URLRequestInterceptJob::GetResponseCode() {
+ if (!plugin_)
+ return -1;
+
+ int status = 200;
+ plugin_->functions().request_funcs->get_response_info(
+ cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status));
+
+ return status;
+}
+
+bool URLRequestInterceptJob::IsRedirectResponse(GURL* location,
+ int* http_status_code) {
+ if (!request_->response_headers())
+ return false;
+
+ std::string value;
+ if (!request_->response_headers()->IsRedirect(&value))
+ return false;
+
+ *location = request_->url().Resolve(value);
+ *http_status_code = request_->response_headers()->response_code();
+ return true;
+}
+
+void URLRequestInterceptJob::StartAsync() {
+ // We may have been orphaned...
+ if (!request_ || !plugin_)
+ return;
+
+ int rv = plugin_->functions().request_funcs->start_request(cprequest_.get());
+ if (rv != CPERR_IO_PENDING)
+ OnStartCompleted(rv);
+}
+
+void URLRequestInterceptJob::OnStartCompleted(int result) {
+ if (result != CPERR_SUCCESS) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
+ net::ERR_CONNECTION_FAILED));
+ return;
+ }
+
+ NotifyHeadersComplete();
+}
+
+void URLRequestInterceptJob::OnReadCompleted(int bytes_read) {
+ if (bytes_read < 0) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED));
+ return;
+ }
+
+ SetStatus(URLRequestStatus()); // clear the async flag
+ NotifyReadComplete(bytes_read);
+}
+
+void URLRequestInterceptJob::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NOTIFY_CHROME_PLUGIN_UNLOADED);
+ DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr());
+
+ DetachPlugin();
+}
diff --git a/chrome/common/net/url_request_intercept_job.h b/chrome/common/net/url_request_intercept_job.h
new file mode 100644
index 0000000..9eea7a6
--- /dev/null
+++ b/chrome/common/net/url_request_intercept_job.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__
+#define CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__
+
+#include "base/basictypes.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+#include "chrome/browser/chrome_plugin_host.h"
+#include "chrome/common/chrome_plugin_api.h"
+#include "chrome/common/chrome_plugin_util.h"
+#include "chrome/common/notification_service.h"
+
+class ChromePluginLib;
+
+// A request job that handles network requests intercepted by a Chrome plugin.
+class URLRequestInterceptJob
+ : public URLRequestJob, public NotificationObserver {
+ public:
+ static URLRequestInterceptJob* FromCPRequest(CPRequest* request) {
+ return ScopableCPRequest::GetData<URLRequestInterceptJob*>(request);
+ }
+
+ URLRequestInterceptJob(URLRequest* request, ChromePluginLib* plugin,
+ ScopableCPRequest* cprequest);
+ virtual ~URLRequestInterceptJob();
+
+ // Plugin callbacks.
+ void OnStartCompleted(int result);
+ void OnReadCompleted(int bytes_read);
+
+ // URLRequestJob
+ virtual void Start();
+ virtual void Kill();
+ virtual bool ReadRawData(char* buf, int buf_size, int* bytes_read);
+ virtual bool GetMimeType(std::string* mime_type);
+ virtual bool GetCharset(std::string* charset);
+ virtual void GetResponseInfo(net::HttpResponseInfo* info);
+ virtual int GetResponseCode();
+ virtual bool GetContentEncoding(std::string* encoding_type);
+ virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+ private:
+ void StartAsync();
+ void DetachPlugin();
+
+ scoped_ptr<ScopableCPRequest> cprequest_;
+ ChromePluginLib* plugin_;
+ bool got_headers_;
+ char* read_buffer_;
+ int read_buffer_size_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(URLRequestInterceptJob);
+};
+
+
+#endif // CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__
diff --git a/chrome/common/net/url_util_unittest.cc b/chrome/common/net/url_util_unittest.cc
new file mode 100644
index 0000000..28606f1
--- /dev/null
+++ b/chrome/common/net/url_util_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "googleurl/src/url_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(URLUtil, Scheme) {
+ enum SchemeType {
+ NO_SCHEME = 0,
+ SCHEME = 1 << 0,
+ STANDARD_SCHEME = 1 << 1,
+ };
+ const struct {
+ const wchar_t* url;
+ const char* try_scheme;
+ const bool scheme_matches;
+ const int flags;
+ } tests[] = {
+ {L" ", "hello", false, NO_SCHEME},
+ {L"foo", NULL, false, NO_SCHEME},
+ {L"google.com/foo:bar", NULL, false, NO_SCHEME},
+ {L"Garbage:foo.com", "garbage", true, SCHEME},
+ {L"Garbage:foo.com", "trash", false, SCHEME},
+ {L"gopher:", "gopher", true, SCHEME | STANDARD_SCHEME},
+ {L"About:blank", "about", true, SCHEME},
+ {L"http://foo.com:123", "foo", false, SCHEME | STANDARD_SCHEME},
+ {L"file://c/", "file", true, SCHEME | STANDARD_SCHEME},
+ };
+
+ for (size_t i = 0; i < arraysize(tests); i++) {
+ int url_len = static_cast<int>(wcslen(tests[i].url));
+
+ url_parse::Component parsed_scheme;
+ bool has_scheme = url_parse::ExtractScheme(tests[i].url, url_len,
+ &parsed_scheme);
+
+ EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME),
+ url_util::IsStandard(tests[i].url, url_len));
+ EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME),
+ url_util::IsStandardScheme(tests[i].url, parsed_scheme.len));
+
+ url_parse::Component found_scheme;
+ EXPECT_EQ(tests[i].scheme_matches,
+ url_util::FindAndCompareScheme(tests[i].url, url_len,
+ tests[i].try_scheme,
+ &found_scheme));
+ EXPECT_EQ(parsed_scheme.begin, found_scheme.begin);
+ EXPECT_EQ(parsed_scheme.len, found_scheme.len);
+ }
+}