Offline By Default

Update the shell to try to run applications from the cache
before trying to download those from the web.

If an application is thus run from the cache, the shell
will asynchronously connect the server to update the
application for the next run, if needed.

BUG=https://github.com/domokit/mojo/issues/363
R=ppi@chromium.org

Review URL: https://codereview.chromium.org/1276073004 .
diff --git a/.gitignore b/.gitignore
index 9d30a0f..0a28957 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,6 +59,7 @@
 /third_party/go/
 /third_party/icu/
 /third_party/jsr-305/src/
+/third_party/leveldatabase/
 /third_party/libc++/trunk/
 /third_party/libc++abi/trunk/
 /third_party/libjpeg_turbo/
@@ -75,6 +76,7 @@
 /third_party/robolectric/src/
 /third_party/skia/
 /third_party/smhasher/src/
+/third_party/snappy/src/
 /third_party/yasm/binaries/
 /third_party/yasm/source/patched-yasm/
 /tools/grit/
diff --git a/DEPS b/DEPS
index 728468a..2f09622 100644
--- a/DEPS
+++ b/DEPS
@@ -160,6 +160,12 @@
 
   'src/third_party/lss':
     Var('chromium_git') + '/external/linux-syscall-support/lss.git' + '@' + Var('lss_revision'),
+
+  'src/third_party/leveldatabase/src':
+    Var('chromium_git') + '/external/leveldb.git' + '@' + '40c17c0b84ac0b791fb434096fd5c05f3819ad55',
+
+  'src/third_party/snappy/src':
+    Var('chromium_git') + '/external/snappy.git' + '@' + '762bb32f0c9d2f31ba4958c7c0933d22e80c20bf',
 }
 
 deps_os = {
diff --git a/build/secondary/third_party/leveldatabase/BUILD.gn b/build/secondary/third_party/leveldatabase/BUILD.gn
new file mode 100644
index 0000000..757f4dd
--- /dev/null
+++ b/build/secondary/third_party/leveldatabase/BUILD.gn
@@ -0,0 +1,325 @@
+# Copyright 2014 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.
+
+defines = [ "LEVELDB_PLATFORM_POSIX=1" ]
+
+if (is_android) {
+  defines += [ "OS_ANDROID=1" ]
+}
+
+# Snappy is a compression library we use.
+# TODO(brettw) It's not clear why this needs to be parameterized.
+
+import("//testing/test.gni")
+
+use_snappy = true
+
+config("leveldatabase_config") {
+  include_dirs = [
+    ".",
+    "src",
+    "src/include",
+  ]
+}
+
+static_library("leveldatabase") {
+  sources = [
+    "src/db/builder.cc",
+    "src/db/builder.h",
+    "src/db/db_impl.cc",
+    "src/db/db_impl.h",
+    "src/db/db_iter.cc",
+    "src/db/db_iter.h",
+    "src/db/dbformat.cc",
+    "src/db/dbformat.h",
+    "src/db/filename.cc",
+    "src/db/filename.h",
+    "src/db/log_format.h",
+    "src/db/log_reader.cc",
+    "src/db/log_reader.h",
+    "src/db/log_writer.cc",
+    "src/db/log_writer.h",
+    "src/db/memtable.cc",
+    "src/db/memtable.h",
+    "src/db/repair.cc",
+    "src/db/skiplist.h",
+    "src/db/snapshot.h",
+    "src/db/table_cache.cc",
+    "src/db/table_cache.h",
+    "src/db/version_edit.cc",
+    "src/db/version_edit.h",
+    "src/db/version_set.cc",
+    "src/db/version_set.h",
+    "src/db/write_batch.cc",
+    "src/db/write_batch_internal.h",
+    "src/helpers/memenv/memenv.cc",
+    "src/helpers/memenv/memenv.h",
+    "src/include/leveldb/cache.h",
+    "src/include/leveldb/comparator.h",
+    "src/include/leveldb/db.h",
+    "src/include/leveldb/env.h",
+    "src/include/leveldb/filter_policy.h",
+    "src/include/leveldb/iterator.h",
+    "src/include/leveldb/options.h",
+    "src/include/leveldb/slice.h",
+    "src/include/leveldb/status.h",
+    "src/include/leveldb/table.h",
+    "src/include/leveldb/table_builder.h",
+    "src/include/leveldb/write_batch.h",
+    "src/port/port.h",
+    "src/port/port_example.h",
+    "src/port/port_posix.cc",
+    "src/port/port_posix.h",
+    "src/table/block.cc",
+    "src/table/block.h",
+    "src/table/block_builder.cc",
+    "src/table/block_builder.h",
+    "src/table/filter_block.cc",
+    "src/table/filter_block.h",
+    "src/table/format.cc",
+    "src/table/format.h",
+    "src/table/iterator.cc",
+    "src/table/iterator_wrapper.h",
+    "src/table/merger.cc",
+    "src/table/merger.h",
+    "src/table/table.cc",
+    "src/table/table_builder.cc",
+    "src/table/two_level_iterator.cc",
+    "src/table/two_level_iterator.h",
+    "src/util/arena.cc",
+    "src/util/arena.h",
+    "src/util/bloom.cc",
+    "src/util/cache.cc",
+    "src/util/coding.cc",
+    "src/util/coding.h",
+    "src/util/comparator.cc",
+    "src/util/crc32c.cc",
+    "src/util/crc32c.h",
+    "src/util/env.cc",
+    "src/util/env_posix.cc",
+    "src/util/filter_policy.cc",
+    "src/util/hash.cc",
+    "src/util/hash.h",
+    "src/util/logging.cc",
+    "src/util/logging.h",
+    "src/util/mutexlock.h",
+    "src/util/options.cc",
+    "src/util/random.h",
+    "src/util/status.cc",
+  ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+
+  public_configs = [ ":leveldatabase_config" ]
+
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+  ]
+
+  if (use_snappy) {
+    defines += [ "USE_SNAPPY=1" ]
+    deps += [ "//third_party/snappy" ]
+  }
+}
+
+if (!is_android) {
+  static_library("leveldb_testutil") {
+    sources = [
+      "src/util/histogram.cc",
+      "src/util/histogram.h",
+      "src/util/testharness.cc",
+      "src/util/testharness.h",
+      "src/util/testutil.cc",
+      "src/util/testutil.h",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+
+    public_deps = [
+      ":leveldatabase",
+    ]
+    deps = [
+      "//base",
+    ]
+  }
+
+  test("leveldb_arena_test") {
+    sources = [
+      "src/util/arena_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_bloom_test") {
+    sources = [
+      "src/util/bloom_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_cache_test") {
+    sources = [
+      "src/util/cache_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_corruption_test") {
+    sources = [
+      "src/db/corruption_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_crc32c_test") {
+    sources = [
+      "src/util/crc32c_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_db_bench") {
+    sources = [
+      "src/db/db_bench.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_db_test") {
+    sources = [
+      "src/db/db_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_dbformat_test") {
+    sources = [
+      "src/db/dbformat_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_env_test") {
+    sources = [
+      "src/util/env_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_filename_test") {
+    sources = [
+      "src/db/filename_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_filter_block_test") {
+    sources = [
+      "src/table/filter_block_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_log_test") {
+    sources = [
+      "src/db/log_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_skiplist_test") {
+    sources = [
+      "src/db/skiplist_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_table_test") {
+    sources = [
+      "src/table/table_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_version_edit_test") {
+    sources = [
+      "src/db/version_edit_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+
+  test("leveldb_write_batch_test") {
+    sources = [
+      "src/db/write_batch_test.cc",
+    ]
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    deps = [
+      ":leveldb_testutil",
+    ]
+  }
+}
diff --git a/mojo/devtools/common/mojo_test b/mojo/devtools/common/mojo_test
index 45cdb0d..8f06f42 100755
--- a/mojo/devtools/common/mojo_test
+++ b/mojo/devtools/common/mojo_test
@@ -49,6 +49,8 @@
 
 _logger = logging.getLogger()
 
+_CACHE_SERVICE_URL = 'mojo:url_response_disk_cache'
+
 
 def main():
   parser = argparse.ArgumentParser(
@@ -66,6 +68,9 @@
       # We need root to have the stdout of the shell available on the host.
       config.require_root = True
     shell, common_shell_args = shell_arguments.get_shell(config, shell_args)
+    # Tests must be reproducible. Start with an empty cache.
+    common_shell_args.append(
+        "--args-for=%s %s" % (_CACHE_SERVICE_URL, "--clear"))
   except shell_config.ShellConfigurationException as e:
     print e
     return 1
diff --git a/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom b/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom
index d6868b4..a6c7a22 100644
--- a/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom
+++ b/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom
@@ -17,20 +17,37 @@
 //            with http servers that do not support ETags.
 interface URLResponseDiskCache {
 
-   // Given a URLResponse, returns a pair of paths. |file_path| is a file
-   // containing the body of the response. |cache_dir_path| is a directory that
-   // the applicaton can use to store content. This service guarantee that
-   // |cache_dir_path| will be emptied when |file_path| content changes. For
-   // example, a content handler that is backed by a VM that compiles files
-   // could have the VM use this directory to cache the compiled files.
-   GetFile(mojo.URLResponse response) =>
+   // Given an URL, returns a tuple. If the |url| is not in the cache, all the
+   // response parameters are null. Otherwise |response| is the cached response
+   // stripped of the body, |file_path| is a file containing the body of the
+   // response and  |cache_dir_path| is a directory that the applicaton can use
+   // to store content. This service guarantee that |cache_dir_path| will be
+   // emptied when |file_path| content changes. For example, a content handler
+   // that is backed by a VM that compiles files could have the VM use this
+   // directory to cache the compiled files.
+   Get(string url) => (mojo.URLResponse? response,
+                       array<uint8>? file_path,
+                       array<uint8>? cache_dir_path);
+
+   // Update the cache with the given response.
+   Update(mojo.URLResponse response);
+
+   // Given a URLResponse, updates the cache and returns a pair of paths.
+   // |file_path| is a file containing the body of the response.
+   // |cache_dir_path| is a directory that the applicaton can use to store
+   // content. This service guarantee that |cache_dir_path| will be emptied
+   // when |file_path| content changes. For example, a content handler that is
+   // backed by a VM that compiles files could have the VM use this directory
+   // to cache the compiled files.
+   UpdateAndGet(mojo.URLResponse response) =>
        (array<uint8>? file_path, array<uint8>? cache_dir_path);
 
-   // Given a URLResponse that is expected to have a zipped body, returns a
-   // pair of paths. |extracted_dir_path| is a directory containing the unzipped
-   // body of the response. |cache_dir_path| is a directory that the applicaton
-   // can use to store content. This service guarantee that |cache_dir_path|
-   // will be emptied when |extracted_dir_path| content changes.
-   GetExtractedContent(mojo.URLResponse response) =>
+   // Given a URLResponse that is expected to have a zipped body, updates the
+   // cache and returns a pair of paths. |extracted_dir_path| is a directory
+   // containing the unzipped body of the response. |cache_dir_path| is a
+   // directory that the applicaton can use to store content. This service
+   // guarantee that |cache_dir_path| will be emptied when |extracted_dir_path|
+   // content changes.
+   UpdateAndGetExtracted(mojo.URLResponse response) =>
        (array<uint8>? extracted_dir_path, array<uint8>? cache_dir_path);
 };
diff --git a/mojo/tools/data/unittests b/mojo/tools/data/unittests
index 3809325..4d56b02 100644
--- a/mojo/tools/data/unittests
+++ b/mojo/tools/data/unittests
@@ -45,6 +45,9 @@
     "test": "mojo_surfaces_lib_unittests",
   },
   {
+    "test": "url_response_disk_cache_unittests",
+  },
+  {
     "test": "view_manager_service_unittests",
   },
   {
diff --git a/services/android/java_handler.cc b/services/android/java_handler.cc
index 257a0b9..63a7a52 100644
--- a/services/android/java_handler.cc
+++ b/services/android/java_handler.cc
@@ -102,7 +102,7 @@
                                  base::FilePath* cache_dir,
                                  mojo::URLResponsePtr response,
                                  const base::Closure& callback) {
-  url_response_disk_cache_->GetFile(
+  url_response_disk_cache_->UpdateAndGet(
       response.Pass(),
       [archive_path, cache_dir, callback](mojo::Array<uint8_t> extracted_path,
                                           mojo::Array<uint8_t> cache_path) {
diff --git a/services/dart/content_handler_main.cc b/services/dart/content_handler_main.cc
index f0adeea..215bb6c 100644
--- a/services/dart/content_handler_main.cc
+++ b/services/dart/content_handler_main.cc
@@ -70,7 +70,7 @@
   void ExtractApplication(base::FilePath* application_dir,
                           mojo::URLResponsePtr response,
                           const base::Closure& callback) {
-    url_response_disk_cache_->GetExtractedContent(
+    url_response_disk_cache_->UpdateAndGetExtracted(
         response.Pass(),
         [application_dir, callback](mojo::Array<uint8_t> application_dir_path,
                                     mojo::Array<uint8_t> cache_path) {
diff --git a/services/dart/dart_apptests/lib/src/connect_to_loader_apptests.dart b/services/dart/dart_apptests/lib/src/connect_to_loader_apptests.dart
index 6b77420..e5a9008 100644
--- a/services/dart/dart_apptests/lib/src/connect_to_loader_apptests.dart
+++ b/services/dart/dart_apptests/lib/src/connect_to_loader_apptests.dart
@@ -16,11 +16,11 @@
 connectToLoaderApptests(Application application, String url) {
   test('Connection', () async {
     var diskCacheProxy = new UrlResponseDiskCacheProxy.unbound();
-    application.connectToService("mojo:url_response_disk_cache",
-        diskCacheProxy);
+    application.connectToService(
+        "mojo:url_response_disk_cache", diskCacheProxy);
     var response = new UrlResponse();
     response.url = 'http://www.example.com';
-    await diskCacheProxy.ptr.getFile(response);
+    await diskCacheProxy.ptr.updateAndGet(response);
     await diskCacheProxy.close();
   });
 }
diff --git a/services/url_response_disk_cache/BUILD.gn b/services/url_response_disk_cache/BUILD.gn
index 2cb04bb..bb0bb87 100644
--- a/services/url_response_disk_cache/BUILD.gn
+++ b/services/url_response_disk_cache/BUILD.gn
@@ -6,6 +6,16 @@
 import("//mojo/public/mojo_application.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//mojo/tools/embed/rules.gni")
+import("//testing/test.gni")
+
+group("tests") {
+  testonly = true
+
+  deps = [
+    ":apptests",
+    ":unittests",
+  ]
+}
 
 source_set("url_response_disk_cache") {
   sources = [
@@ -17,25 +27,45 @@
 
   deps = [
     ":bindings",
+    ":url_response_disk_cache_db",
     "//base",
+    "//crypto",
     "//mojo/application",
     "//mojo/data_pipe_utils",
     "//mojo/environment:chromium",
     "//mojo/public/cpp/application",
     "//mojo/public/cpp/system",
+    "//mojo/public/interfaces/network",
     "//mojo/services/url_response_disk_cache/public/interfaces",
     "//third_party/zlib:zip",
     "//url:url",
   ]
 }
 
+source_set("url_response_disk_cache_db") {
+  sources = [
+    "url_response_disk_cache_db.cc",
+    "url_response_disk_cache_db.h",
+  ]
+
+  deps = [
+    ":bindings",
+    "//base",
+    "//third_party/leveldatabase",
+  ]
+}
+
 mojom("bindings") {
   sources = [
     "url_response_disk_cache_entry.mojom",
   ]
+
+  deps = [
+    "//mojo/public/interfaces/network",
+  ]
 }
 
-mojo_native_application("tests") {
+mojo_native_application("apptests") {
   output_name = "url_response_disk_cache_apptests"
 
   testonly = true
@@ -58,6 +88,26 @@
   data_deps = [ ":url_response_disk_cache" ]
 }
 
+test("unittests") {
+  output_name = "url_response_disk_cache_unittests"
+
+  sources = [
+    "url_response_disk_cache_db_unittests.cc",
+  ]
+
+  deps = [
+    ":url_response_disk_cache_db",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//base/test:test_support",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//mojo/public/platform/native:system",
+    "//testing/gtest",
+  ]
+}
+
 action("test_data") {
   script = "//mojo/public/tools/gn/zip.py"
   inputs = [
diff --git a/services/url_response_disk_cache/url_response_disk_cache_app.cc b/services/url_response_disk_cache/url_response_disk_cache_app.cc
index 5d93695..67632ae 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_app.cc
+++ b/services/url_response_disk_cache/url_response_disk_cache_app.cc
@@ -9,18 +9,17 @@
 
 namespace mojo {
 
-URLResponseDiskCacheApp::URLResponseDiskCacheApp(base::TaskRunner* task_runner)
-    : task_runner_(task_runner) {
-}
+URLResponseDiskCacheApp::URLResponseDiskCacheApp(
+    scoped_refptr<base::TaskRunner> task_runner)
+    : task_runner_(task_runner) {}
 
 URLResponseDiskCacheApp::~URLResponseDiskCacheApp() {
 }
 
 void URLResponseDiskCacheApp::Initialize(ApplicationImpl* app) {
   base::CommandLine command_line(app->args());
-  if (command_line.HasSwitch("clear")) {
-    URLResponseDiskCacheImpl::ClearCache(task_runner_);
-  }
+  bool force_clean = command_line.HasSwitch("clear");
+  db_ = URLResponseDiskCacheImpl::CreateDB(task_runner_, force_clean);
 }
 
 bool URLResponseDiskCacheApp::ConfigureIncomingConnection(
@@ -33,7 +32,7 @@
     ApplicationConnection* connection,
     InterfaceRequest<URLResponseDiskCache> request) {
   new URLResponseDiskCacheImpl(
-      task_runner_, connection->GetRemoteApplicationURL(), request.Pass());
+      task_runner_, db_, connection->GetRemoteApplicationURL(), request.Pass());
 }
 
 }  // namespace mojo
diff --git a/services/url_response_disk_cache/url_response_disk_cache_app.h b/services/url_response_disk_cache/url_response_disk_cache_app.h
index afab220..47c0dd4 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_app.h
+++ b/services/url_response_disk_cache/url_response_disk_cache_app.h
@@ -6,6 +6,7 @@
 #define SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_APP_H_
 
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/task_runner.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "mojo/public/cpp/application/application_connection.h"
@@ -13,13 +14,14 @@
 #include "mojo/public/cpp/application/application_impl.h"
 #include "mojo/public/cpp/application/interface_factory.h"
 #include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
+#include "services/url_response_disk_cache/url_response_disk_cache_db.h"
 
 namespace mojo {
 
 class URLResponseDiskCacheApp : public ApplicationDelegate,
                                 public InterfaceFactory<URLResponseDiskCache> {
  public:
-  URLResponseDiskCacheApp(base::TaskRunner* task_runner);
+  explicit URLResponseDiskCacheApp(scoped_refptr<base::TaskRunner> task_runner);
   ~URLResponseDiskCacheApp() override;
 
  private:
@@ -31,7 +33,8 @@
   void Create(ApplicationConnection* connection,
               InterfaceRequest<URLResponseDiskCache> request) override;
 
-  base::TaskRunner* task_runner_;
+  scoped_refptr<base::TaskRunner> task_runner_;
+  scoped_refptr<URLResponseDiskCacheDB> db_;
 
   DISALLOW_COPY_AND_ASSIGN(URLResponseDiskCacheApp);
 };
diff --git a/services/url_response_disk_cache/url_response_disk_cache_apptest.cc b/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
index 61bf5a2..1ef3bcd 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
+++ b/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
@@ -51,7 +51,58 @@
 
 }  // namespace
 
-TEST_F(URLResponseDiskCacheAppTest, GetFile) {
+TEST_F(URLResponseDiskCacheAppTest, BaseCache) {
+  const std::string url = "http://www.example.com/1";
+
+  url_response_disk_cache_->Get(
+      url, [](URLResponsePtr url_response, Array<uint8_t> received_file_path,
+              Array<uint8_t> received_cache_dir_path) {
+        EXPECT_FALSE(url_response);
+      });
+  url_response_disk_cache_.WaitForIncomingResponse();
+
+  URLResponsePtr url_response = mojo::URLResponse::New();
+  url_response->url = "http://www.example.com/1";
+  url_response->headers.push_back(RandomEtagHeader());
+  DataPipe pipe;
+  std::string content = base::RandBytesAsString(32);
+  uint32_t num_bytes = content.size();
+  ASSERT_EQ(MOJO_RESULT_OK,
+            WriteDataRaw(pipe.producer_handle.get(), content.c_str(),
+                         &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+  ASSERT_EQ(content.size(), num_bytes);
+  pipe.producer_handle.reset();
+  url_response->body = pipe.consumer_handle.Pass();
+
+  url_response_disk_cache_->Update(url_response.Pass());
+
+  base::FilePath file;
+  base::FilePath cache_dir;
+
+  // Wait up to 1 second for the cache to be updated.
+  base::Time start = base::Time::Now();
+  while (file.empty() &&
+         ((base::Time::Now() - start) < base::TimeDelta::FromSeconds(1))) {
+    url_response_disk_cache_->Get(
+        url, [&url_response, &file, &cache_dir](
+                 URLResponsePtr response, Array<uint8_t> received_file_path,
+                 Array<uint8_t> received_cache_dir_path) {
+          url_response = response.Pass();
+          file = toPath(received_file_path.Pass());
+          cache_dir = toPath(received_cache_dir_path.Pass());
+        });
+    url_response_disk_cache_.WaitForIncomingResponse();
+  }
+
+  EXPECT_TRUE(url_response);
+  EXPECT_FALSE(file.empty());
+  EXPECT_EQ(url, url_response->url);
+  std::string received_content;
+  ASSERT_TRUE(base::ReadFileToString(file, &received_content));
+  EXPECT_EQ(content, received_content);
+}
+
+TEST_F(URLResponseDiskCacheAppTest, UpdateAndGet) {
   URLResponsePtr url_response = mojo::URLResponse::New();
   url_response->url = "http://www.example.com/1";
   url_response->headers.push_back(RandomEtagHeader());
@@ -66,7 +117,7 @@
   url_response->body = pipe.consumer_handle.Pass();
   base::FilePath file;
   base::FilePath cache_dir;
-  url_response_disk_cache_->GetFile(
+  url_response_disk_cache_->UpdateAndGet(
       url_response.Pass(),
       [&file, &cache_dir](Array<uint8_t> received_file_path,
                           Array<uint8_t> received_cache_dir_path) {
@@ -80,7 +131,7 @@
   EXPECT_EQ(content, received_content);
 }
 
-TEST_F(URLResponseDiskCacheAppTest, GetExtractedContent) {
+TEST_F(URLResponseDiskCacheAppTest, UpdateAndGetExtracted) {
   URLResponsePtr url_response = mojo::URLResponse::New();
   url_response->url = "http://www.example.com/2";
   url_response->headers.push_back(RandomEtagHeader());
@@ -95,7 +146,7 @@
   url_response->body = pipe.consumer_handle.Pass();
   base::FilePath extracted_dir;
   base::FilePath cache_dir;
-  url_response_disk_cache_->GetExtractedContent(
+  url_response_disk_cache_->UpdateAndGetExtracted(
       url_response.Pass(),
       [&extracted_dir, &cache_dir](Array<uint8_t> received_extracted_dir,
                                    Array<uint8_t> received_cache_dir_path) {
@@ -134,7 +185,7 @@
   url_response->body = pipe1.consumer_handle.Pass();
   base::FilePath file;
   base::FilePath cache_dir;
-  url_response_disk_cache_->GetFile(
+  url_response_disk_cache_->UpdateAndGet(
       url_response.Pass(),
       [&file, &cache_dir](Array<uint8_t> received_file_path,
                           Array<uint8_t> received_cache_dir_path) {
@@ -171,7 +222,7 @@
   ASSERT_EQ(new_content.size(), num_bytes);
   pipe2.producer_handle.reset();
   url_response->body = pipe2.consumer_handle.Pass();
-  url_response_disk_cache_->GetFile(
+  url_response_disk_cache_->UpdateAndGet(
       url_response.Pass(),
       [&file, &cache_dir](Array<uint8_t> received_file_path,
                           Array<uint8_t> received_cache_dir_path) {
@@ -202,7 +253,7 @@
   ASSERT_EQ(new_content.size(), num_bytes);
   pipe3.producer_handle.reset();
   url_response->body = pipe3.consumer_handle.Pass();
-  url_response_disk_cache_->GetFile(
+  url_response_disk_cache_->UpdateAndGet(
       url_response.Pass(),
       [&file, &cache_dir](Array<uint8_t> received_file_path,
                           Array<uint8_t> received_cache_dir_path) {
@@ -231,7 +282,7 @@
   ASSERT_EQ(new_content.size(), num_bytes);
   pipe4.producer_handle.reset();
   url_response->body = pipe4.consumer_handle.Pass();
-  url_response_disk_cache_->GetFile(
+  url_response_disk_cache_->UpdateAndGet(
       url_response.Pass(),
       [&file, &cache_dir](Array<uint8_t> received_file_path,
                           Array<uint8_t> received_cache_dir_path) {
@@ -261,7 +312,7 @@
   ASSERT_EQ(new_content.size(), num_bytes);
   pipe5.producer_handle.reset();
   url_response->body = pipe5.consumer_handle.Pass();
-  url_response_disk_cache_->GetFile(
+  url_response_disk_cache_->UpdateAndGet(
       url_response.Pass(),
       [&file, &cache_dir](Array<uint8_t> received_file_path,
                           Array<uint8_t> received_cache_dir_path) {
diff --git a/services/url_response_disk_cache/url_response_disk_cache_db.cc b/services/url_response_disk_cache/url_response_disk_cache_db.cc
new file mode 100644
index 0000000..4e65c01
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_db.cc
@@ -0,0 +1,222 @@
+// Copyright 2015 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 "services/url_response_disk_cache/url_response_disk_cache_db.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "leveldb/comparator.h"
+#include "leveldb/db.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "services/url_response_disk_cache/url_response_disk_cache_entry.mojom.h"
+
+namespace mojo {
+namespace {
+
+// List of keys for metadata. All metadata keys must start with a '\1'
+// character.
+const char kVersionKey[] = "\1version";
+
+// TODO(darin): These Serialize / Deserialize methods should not live here.
+// They use private details of the bindings system. Instead, we should provide
+// these as helper functions under mojo/public/cpp/bindings/.
+
+template <typename T>
+void Serialize(T input, std::string* output) {
+  typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
+  size_t size = GetSerializedSize_(input);
+
+  output->clear();
+  output->resize(size);
+
+  mojo::internal::FixedBuffer buf;
+  buf.Initialize(&output->at(0), size);
+
+  DataType data_type;
+  Serialize_(input.Pass(), &buf, &data_type);
+  std::vector<Handle> handles;
+  data_type->EncodePointersAndHandles(&handles);
+}
+
+template <typename T>
+bool Deserialize(void* data, size_t size, T* output) {
+  typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
+  mojo::internal::BoundsChecker bounds_checker(data, size, 0);
+  if (!std::remove_pointer<DataType>::type::Validate(data, &bounds_checker)) {
+    return false;
+  }
+  DataType data_type = reinterpret_cast<DataType>(data);
+  std::vector<Handle> handles;
+  data_type->DecodePointersAndHandles(&handles);
+  Deserialize_(data_type, output);
+  return true;
+}
+
+template <typename T>
+bool Deserialize(std::string s, T* output) {
+  return Deserialize(&s.at(0), s.size(), output);
+}
+
+template <typename T>
+bool Deserialize(const leveldb::Slice& s, T* output) {
+  return Deserialize(s.ToString(), output);
+}
+
+// Returns whether the key is for a metadata entry. Metadata entries are
+// declared at the start of this file.
+bool IsMetaDataKey(const leveldb::Slice& s) {
+  return s.size() != 0 && s[0] == '\1';
+}
+
+class KeyComparator : public leveldb::Comparator {
+ public:
+  int Compare(const leveldb::Slice& s1,
+              const leveldb::Slice& s2) const override {
+    if (IsMetaDataKey(s1) != IsMetaDataKey(s2)) {
+      if (IsMetaDataKey(s1))
+        return -1;
+      return 1;
+    }
+
+    if (IsMetaDataKey(s1))
+      return leveldb::BytewiseComparator()->Compare(s1, s2);
+
+    mojo::CacheKeyPtr k1, k2;
+    bool result = Deserialize(s1, &k1) && Deserialize(s2, &k2);
+    DCHECK(result);
+    if (k1->request_origin.get() < k2->request_origin.get())
+      return -1;
+    if (k1->request_origin.get() > k2->request_origin.get())
+      return +1;
+    if (k1->url.get() < k2->url.get())
+      return -1;
+    if (k1->url.get() > k2->url.get())
+      return +1;
+    if (k1->timestamp < k2->timestamp)
+      return 1;
+    if (k1->timestamp > k2->timestamp)
+      return -1;
+    return 0;
+  }
+
+  const char* Name() const override { return "KeyComparator"; }
+  void FindShortestSeparator(std::string*,
+                             const leveldb::Slice&) const override {}
+  void FindShortSuccessor(std::string*) const override {}
+};
+
+}  // namespace
+
+URLResponseDiskCacheDB::Iterator::Iterator(linked_ptr<leveldb::DB> db)
+    : db_(db) {
+  it_.reset(db_->NewIterator(leveldb::ReadOptions()));
+  it_->SeekToFirst();
+}
+
+URLResponseDiskCacheDB::Iterator::~Iterator() {}
+
+bool URLResponseDiskCacheDB::Iterator::HasNext() {
+  while (it_->Valid() && IsMetaDataKey(it_->key())) {
+    it_->Next();
+  }
+  return it_->Valid();
+}
+
+void URLResponseDiskCacheDB::Iterator::GetNext(CacheKeyPtr* key,
+                                               CacheEntryPtr* entry) {
+  // Calling HasNext() to ensure metadata are skipped.
+  bool has_next = HasNext();
+  DCHECK(has_next);
+  if (key)
+    Deserialize(it_->key(), key);
+  if (entry)
+    Deserialize(it_->value(), entry);
+  it_->Next();
+}
+
+URLResponseDiskCacheDB::URLResponseDiskCacheDB(const base::FilePath& db_path)
+    : comparator_(new KeyComparator) {
+  leveldb::DB* db;
+  leveldb::Options options;
+  options.create_if_missing = true;
+  options.comparator = comparator_.get();
+  leveldb::Status status = leveldb::DB::Open(options, db_path.value(), &db);
+  DCHECK(status.ok()) << status.ToString();
+  db_.reset(db);
+}
+
+uint64_t URLResponseDiskCacheDB::GetVersion() {
+  std::string value;
+  leveldb::Status status =
+      db_->Get(leveldb::ReadOptions(), kVersionKey, &value);
+  if (status.IsNotFound())
+    return 0u;
+  DCHECK(status.ok());
+  uint64_t version;
+  memcpy(&version, value.data(), sizeof(version));
+  return version;
+}
+
+void URLResponseDiskCacheDB::SetVersion(uint64_t version) {
+  leveldb::Status status = db_->Put(
+      leveldb::WriteOptions(), kVersionKey,
+      leveldb::Slice(reinterpret_cast<char*>(&version), sizeof(version)));
+  DCHECK(status.ok());
+}
+
+void URLResponseDiskCacheDB::PutNew(const std::string& request_origin,
+                                    const std::string& url,
+                                    CacheEntryPtr entry) {
+  CacheKeyPtr key = CacheKey::New();
+  key->request_origin = request_origin;
+  key->url = url;
+  key->timestamp = base::Time::Now().ToInternalValue();
+  std::string key_string;
+  Serialize(key.Pass(), &key_string);
+  std::string entry_string;
+  Serialize(entry.Pass(), &entry_string);
+  leveldb::Status s =
+      db_->Put(leveldb::WriteOptions(), key_string, entry_string);
+  DCHECK(s.ok());
+}
+
+CacheEntryPtr URLResponseDiskCacheDB::GetNewest(
+    const std::string& request_origin,
+    const std::string& url) {
+  CacheKeyPtr key = CacheKey::New();
+  key->request_origin = request_origin;
+  key->url = url;
+  key->timestamp = std::numeric_limits<int64>::max();
+  std::string key_string;
+  Serialize(key.Pass(), &key_string);
+  scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
+  it->Seek(key_string);
+  CacheEntryPtr result;
+  if (it->Valid()) {
+    Deserialize(it->key(), &key);
+    if (key->request_origin == request_origin && key->url == url) {
+      Deserialize(it->value(), &result);
+    }
+  }
+  return result.Pass();
+}
+
+void URLResponseDiskCacheDB::Delete(CacheKeyPtr key) {
+  std::string key_string;
+  Serialize(key.Pass(), &key_string);
+  leveldb::Status s = db_->Delete(leveldb::WriteOptions(), key_string);
+  DCHECK(s.ok());
+}
+
+scoped_ptr<URLResponseDiskCacheDB::Iterator>
+URLResponseDiskCacheDB::GetIterator() {
+  return make_scoped_ptr(new Iterator(db_));
+}
+
+URLResponseDiskCacheDB::~URLResponseDiskCacheDB() {}
+
+}  // namespace mojo
diff --git a/services/url_response_disk_cache/url_response_disk_cache_db.h b/services/url_response_disk_cache/url_response_disk_cache_db.h
new file mode 100644
index 0000000..c8aee1b
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_db.h
@@ -0,0 +1,78 @@
+// Copyright 2015 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.
+
+#ifndef SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_DB_H_
+#define SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_DB_H_
+
+#include "base/files/file_path.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "services/url_response_disk_cache/url_response_disk_cache_entry.mojom.h"
+
+namespace leveldb {
+class Comparator;
+class DB;
+class Iterator;
+};
+
+namespace mojo {
+
+// Specialized database for the cache content.
+class URLResponseDiskCacheDB
+    : public base::RefCountedThreadSafe<URLResponseDiskCacheDB> {
+ public:
+  class Iterator {
+   public:
+    explicit Iterator(linked_ptr<leveldb::DB> db);
+    ~Iterator();
+
+    bool HasNext();
+    void GetNext(CacheKeyPtr* key, CacheEntryPtr* entry);
+
+   private:
+    linked_ptr<leveldb::DB> db_;
+    scoped_ptr<leveldb::Iterator> it_;
+  };
+
+  // Constructs the database. |db_path| is the path to the leveldb database. If
+  // the path exists, the database will be opened, otherwise it will be created.
+  explicit URLResponseDiskCacheDB(const base::FilePath& db_path);
+
+  // Set and get the version of the database.
+  uint64_t GetVersion();
+  void SetVersion(uint64_t version);
+
+  // Put an entry for the given |request_origin| and |url|. Older entry for the
+  // same |request_origin| and |url| will not be removed, but will be shadowed
+  // by the new one.
+  void PutNew(const std::string& request_origin,
+              const std::string& url,
+              CacheEntryPtr entry);
+
+  // Returns the newest entry for the given |request_origin| and |url|, or null
+  // if none exist.
+  CacheEntryPtr GetNewest(const std::string& request_origin,
+                          const std::string& url);
+
+  // Delete the entry for the given |key|.
+  void Delete(CacheKeyPtr key);
+
+  // Returns an iterator over all the entries in the database. For a given
+  // |request_origin| and |url|, entries will be sorted from newest to oldest.
+  // An iterator will not be invalidated nor any of its values will be modified
+  // by further changes to the database.
+  scoped_ptr<Iterator> GetIterator();
+
+ private:
+  friend class base::RefCountedThreadSafe<URLResponseDiskCacheDB>;
+  ~URLResponseDiskCacheDB();
+
+  scoped_ptr<leveldb::Comparator> comparator_;
+  linked_ptr<leveldb::DB> db_;
+};
+
+}  // namespace
+
+#endif  // SERVICES_URL_RESPONSE_DISK_CACHE_URL_RESPONSE_DISK_CACHE_DB_H_
diff --git a/services/url_response_disk_cache/url_response_disk_cache_db_unittests.cc b/services/url_response_disk_cache/url_response_disk_cache_db_unittests.cc
new file mode 100644
index 0000000..c69f707
--- /dev/null
+++ b/services/url_response_disk_cache/url_response_disk_cache_db_unittests.cc
@@ -0,0 +1,144 @@
+// Copyright 2015 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 "base/files/scoped_temp_dir.h"
+#include "base/threading/platform_thread.h"
+#include "services/url_response_disk_cache/url_response_disk_cache_db.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+
+namespace {
+
+class URLResponseDiskCacheDBTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
+    Open();
+  }
+
+  void Open() {
+    db_ = nullptr;
+    base::FilePath db_path = tmp_dir_.path().Append("db");
+    db_ = new URLResponseDiskCacheDB(db_path);
+  }
+
+  CacheEntryPtr NewEntry() {
+    CacheEntryPtr entry = CacheEntry::New();
+    entry->response = URLResponse::New();
+    entry->entry_directory = "/cache";
+    entry->response_body_path = "/cache/content";
+    return entry.Pass();
+  }
+
+  base::ScopedTempDir tmp_dir_;
+  scoped_refptr<URLResponseDiskCacheDB> db_;
+};
+
+TEST_F(URLResponseDiskCacheDBTest, Create) {}
+
+TEST_F(URLResponseDiskCacheDBTest, Version) {
+  EXPECT_EQ(0lu, db_->GetVersion());
+  db_->SetVersion(15);
+  EXPECT_EQ(15lu, db_->GetVersion());
+}
+
+TEST_F(URLResponseDiskCacheDBTest, Persist) {
+  db_->SetVersion(15);
+  EXPECT_EQ(15lu, db_->GetVersion());
+  Open();
+  EXPECT_EQ(15lu, db_->GetVersion());
+}
+
+TEST_F(URLResponseDiskCacheDBTest, Entry) {
+  std::string origin = "origin";
+  std::string url = "url";
+  db_->PutNew(origin, url, NewEntry());
+  CacheEntryPtr entry = db_->GetNewest(origin, url);
+  EXPECT_TRUE(entry);
+  Open();
+  entry = db_->GetNewest(origin, url);
+  EXPECT_TRUE(entry);
+}
+
+TEST_F(URLResponseDiskCacheDBTest, Newest) {
+  std::string origin = "origin";
+  std::string url = "url";
+  std::string new_entry_directory = "/newcache/";
+  db_->PutNew(origin, url, NewEntry());
+  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5));
+  CacheEntryPtr entry = NewEntry();
+  entry->entry_directory = new_entry_directory;
+  db_->PutNew(origin, url, entry.Pass());
+  entry = db_->GetNewest(origin, url);
+  EXPECT_TRUE(entry);
+  EXPECT_EQ(new_entry_directory, entry->entry_directory);
+}
+
+TEST_F(URLResponseDiskCacheDBTest, Iterator) {
+  std::string origin = "origin";
+  std::string url1 = "a";
+  std::string url2 = "b";
+  std::string url3 = "c";
+  db_->PutNew(origin, url2, NewEntry());
+  CacheEntryPtr entry = db_->GetNewest(origin, url2);
+  EXPECT_TRUE(entry);
+  db_->PutNew(origin, url1, NewEntry());
+  db_->PutNew(origin, url3, NewEntry());
+  entry = CacheEntry::New();
+  scoped_ptr<URLResponseDiskCacheDB::Iterator> iterator = db_->GetIterator();
+  EXPECT_TRUE(iterator->HasNext());
+  CacheKeyPtr key;
+  iterator->GetNext(&key, &entry);
+  EXPECT_TRUE(iterator->HasNext());
+  EXPECT_TRUE(key);
+  EXPECT_EQ(url1, key->url);
+  EXPECT_TRUE(entry);
+  iterator->GetNext(&key, &entry);
+  EXPECT_TRUE(iterator->HasNext());
+  EXPECT_TRUE(key);
+  EXPECT_EQ(url2, key->url);
+  EXPECT_TRUE(entry);
+  iterator->GetNext(&key, &entry);
+  EXPECT_FALSE(iterator->HasNext());
+  EXPECT_TRUE(key);
+  EXPECT_EQ(url3, key->url);
+  EXPECT_TRUE(entry);
+}
+
+TEST_F(URLResponseDiskCacheDBTest, Delete) {
+  std::string origin = "origin";
+  std::string url = "url";
+  db_->PutNew(origin, url, NewEntry());
+  CacheEntryPtr entry = db_->GetNewest(origin, url);
+  EXPECT_TRUE(entry);
+  entry = CacheEntry::New();
+  scoped_ptr<URLResponseDiskCacheDB::Iterator> iterator = db_->GetIterator();
+  EXPECT_TRUE(iterator->HasNext());
+  CacheKeyPtr key;
+  iterator->GetNext(&key, &entry);
+  EXPECT_FALSE(iterator->HasNext());
+  EXPECT_TRUE(key);
+  EXPECT_TRUE(entry);
+  db_->Delete(key.Pass());
+  entry = db_->GetNewest(origin, url);
+  EXPECT_FALSE(entry);
+}
+
+TEST_F(URLResponseDiskCacheDBTest, IteratorFrozen) {
+  std::string origin = "origin";
+  std::string url = "url";
+  db_->PutNew(origin, url, NewEntry());
+  scoped_ptr<URLResponseDiskCacheDB::Iterator> iterator = db_->GetIterator();
+  std::string url2 = "url2";
+  db_->PutNew(origin, url2, NewEntry());
+
+  EXPECT_TRUE(iterator->HasNext());
+  iterator->GetNext(nullptr, nullptr);
+  EXPECT_FALSE(iterator->HasNext());
+}
+
+}  // namespace
+
+}  // namespace mojo
diff --git a/services/url_response_disk_cache/url_response_disk_cache_entry.mojom b/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
index 0d12a6e..f7701aa 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
+++ b/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
@@ -2,18 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-[DartPackage="mojo_services"]
+// These mojom structs are declared only to get generated serialization code
+// (ie. they are not used for ipc).
 module mojo;
 
-struct CacheHeaders {
-  string name;
-  string value;
+import "mojo/public/interfaces/network/url_response.mojom";
+
+struct CacheKey {
+  string request_origin;
+  string url;
+  int64 timestamp;
 };
 
 // One entry in the service cache.
 struct CacheEntry {
-  uint32 version = 0;
-  string url;
-  string content_path;
-  array<CacheHeaders>? headers;
+  URLResponse response;
+  string entry_directory;
+  string response_body_path;
 };
diff --git a/services/url_response_disk_cache/url_response_disk_cache_impl.cc b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
index 865c713..91f781c 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_impl.cc
+++ b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
@@ -11,16 +11,20 @@
 #include <type_traits>
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
+#include "crypto/random.h"
 #include "mojo/data_pipe_utils/data_pipe_utils.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/interfaces/network/http_header.mojom.h"
 #include "services/url_response_disk_cache/url_response_disk_cache_entry.mojom.h"
 #include "third_party/zlib/google/zip_reader.h"
 #include "url/gurl.h"
@@ -31,69 +35,44 @@
 
 // The current version of the cache. This should only be incremented. When this
 // is incremented, all current cache entries will be invalidated.
-const uint32_t kCurrentVersion = 2;
+const uint32_t kCurrentVersion = 0;
+
+// The delay to wait before starting deleting data. This is delayed to not
+// interfere with the shell startup.
+const uint32_t kTrashDelayInSeconds = 60;
 
 const char kEtagHeader[] = "etag";
 
-const char kEntryName[] = "entry";
-const char kEntryTmpName[] = "entry.tmp";
-
-// TODO(darin): These Serialize / Deserialize methods should not live here.
-// They use private details of the bindings system. Instead, we should provide
-// these as helper functions under mojo/public/cpp/bindings/.
-
-template <typename T>
-void Serialize(T input, std::string* output) {
-  typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
-  size_t size = GetSerializedSize_(input);
-
-  output->clear();
-  output->resize(size);
-
-  mojo::internal::FixedBuffer buf;
-  buf.Initialize(&output->at(0), size);
-
-  DataType data_type;
-  Serialize_(input.Pass(), &buf, &data_type);
-  std::vector<Handle> handles;
-  data_type->EncodePointersAndHandles(&handles);
+// Create a new identifier for a cache entry. This will be used as an unique
+// directory name.
+std::string GetNewIdentifier() {
+  char bytes[32];
+  crypto::RandBytes(bytes, arraysize(bytes));
+  return base::HexEncode(bytes, arraysize(bytes));
 }
 
-template <typename T>
-bool Deserialize(std::string input, T* output) {
-  typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
-  mojo::internal::BoundsChecker bounds_checker(&input[0], input.size(), 0);
-  if (!std::remove_pointer<DataType>::type::Validate(&input[0],
-                                                     &bounds_checker)) {
-    return false;
+void DoNothing(const base::FilePath& fp1, const base::FilePath& fp2) {}
+
+void MovePathIntoDir(const base::FilePath& source,
+                     const base::FilePath& destination) {
+  if (!PathExists(source))
+    return;
+
+  base::FilePath tmp_dir;
+  base::CreateTemporaryDirInDir(destination, "", &tmp_dir);
+  base::File::Error error;
+  if (!base::ReplaceFile(source, tmp_dir, &error)) {
+    LOG(ERROR) << "Failed to clear dir content: " << error;
   }
-  DataType data_type = reinterpret_cast<DataType>(&input[0]);
-  std::vector<Handle> handles;
-  data_type->DecodePointersAndHandles(&handles);
-  Deserialize_(data_type, output);
-  return true;
 }
 
-void SaveEntry(CacheEntryPtr entry, base::ScopedFD dir) {
-  TRACE_EVENT0("url_response_disk_cache", "SaveEntry");
-
-  std::string serialized_entry;
-  Serialize(entry.Pass(), &serialized_entry);
-  DCHECK_LT(serialized_entry.size(),
-            static_cast<size_t>(std::numeric_limits<int>::max()));
-
-  int file_fd =
-      HANDLE_EINTR(openat(dir.get(), kEntryTmpName, O_WRONLY | O_CREAT, 0600));
-  if (file_fd < 0)
-    return;
-  base::File file(file_fd);
-  if (file.WriteAtCurrentPos(serialized_entry.data(),
-                             serialized_entry.size()) !=
-      static_cast<int>(serialized_entry.size()))
-    return;
-  // The file must be closed before the file is moved.
-  file.Close();
-  renameat(dir.get(), kEntryTmpName, dir.get(), kEntryName);
+void ClearTrashDir(scoped_refptr<base::TaskRunner> task_runner,
+                   const base::FilePath& trash_dir) {
+  // Delete the trash directory.
+  task_runner->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(base::IgnoreResult(&base::DeleteFile), trash_dir, true),
+      base::TimeDelta::FromSeconds(kTrashDelayInSeconds));
 }
 
 Array<uint8_t> PathToArray(const base::FilePath& path) {
@@ -123,83 +102,92 @@
   return result;
 }
 
-// Encode a string in ascii. This uses _ as an escape character. It also escapes
-// ':' because it is an usual path separator, and '#' because dart refuses it in
-// URLs.
-std::string EncodeString(const std::string& string) {
-  std::string result = "";
-  for (size_t i = 0; i < string.size(); ++i) {
-    unsigned char c = string[i];
-    if (c >= 32 && c < 128 && c != '_' && c != ':' && c != '#') {
-      result += c;
-    } else {
-      result += base::StringPrintf("_%02x", c);
-    }
-  }
-  return result;
-}
-
 // This service use a directory under HOME to store all of its data,
 base::FilePath GetBaseDirectory() {
   return base::FilePath(getenv("HOME")).Append(".mojo_url_response_disk_cache");
 }
 
+// Returns the directory containing live data for the cache.
 base::FilePath GetCacheDirectory() {
   return GetBaseDirectory().Append("cache");
 }
 
+// Returns a temporary that will be deleted after startup. This is used to have
+// a consistent directory for outdated files in case the trash process doesn't
+// finish.
 base::FilePath GetTrashDirectory() {
   return GetBaseDirectory().Append("trash");
 }
 
-// Returns the directory used store cached data for the given |url|, under
-// |base_directory|.
-base::FilePath GetDirName(base::FilePath base_directory,
-                          const std::string& url) {
-  // TODO(qsr): If the speed of directory traversal is problematic, this might
-  // need to change to use less directories.
-  return base_directory.Append(EncodeString(CanonicalizeURL(url)));
+// Returns a staging directory to save file before an entry can be saved in the
+// database. This directory is deleted when the service is started. This is used
+// to prevent leaking files if there is an interruption while downloading a
+// response.
+base::FilePath GetStagingDirectory() {
+  return GetBaseDirectory().Append("staging");
 }
 
-// Returns the directory that the consumer can use to cache its own data.
-base::FilePath GetConsumerCacheDirectory(const base::FilePath& main_cache) {
-  return main_cache.Append("consumer_cache");
+// Returns path of the directory that the consumer can use to cache its own
+// data.
+base::FilePath GetConsumerCacheDirectory(
+    const base::FilePath& entry_directory) {
+  return entry_directory.Append("consumer_cache");
 }
 
-// Returns the path of the sentinel used to keep track of a zipped response has
+// Returns the path of the sentinel used to mark that a zipped response has
 // already been extracted.
-base::FilePath GetExtractedSentinel(const base::FilePath& main_cache) {
-  return main_cache.Append("extracted_sentinel");
+base::FilePath GetExtractionSentinel(const base::FilePath& entry_directory) {
+  return entry_directory.Append("extraction_sentinel");
+}
+
+// Returns the path of the directory where a zipped content is extracted.
+base::FilePath GetExtractionDirectory(const base::FilePath& entry_directory) {
+  return entry_directory.Append("extracted");
 }
 
 // Runs the given callback. If |success| is false, call back with an error.
-// Otherwise, store |entry| in |entry_path|, then call back with the given
-// paths.
+// Otherwise, store a new entry in the databse, then call back with the content
+// path and the consumer cache path.
 void RunCallbackWithSuccess(
-    const URLResponseDiskCacheImpl::FilePathPairCallback& callback,
-    const base::FilePath& content_path,
-    const base::FilePath& cache_dir,
-    const base::FilePath& entry_path,
-    CacheEntryPtr entry,
-    base::TaskRunner* task_runner,
+    const URLResponseDiskCacheImpl::ResponseFileAndCacheDirCallback& callback,
+    const std::string& identifier,
+    const std::string& request_origin,
+    const std::string& url,
+    URLResponsePtr response,
+    scoped_refptr<URLResponseDiskCacheDB> db,
+    scoped_refptr<base::TaskRunner> task_runner,
     bool success) {
-  TRACE_EVENT1("url_response_disk_cache", "RunCallbackWithSuccess",
-               "content_path", content_path.value());
+  TRACE_EVENT2("url_response_disk_cache", "RunCallbackWithSuccess", "url", url,
+               "identifier", identifier);
   if (!success) {
     callback.Run(base::FilePath(), base::FilePath());
     return;
   }
 
-  // Save the entry in a background thread. The entry directory is opened and
-  // passed because if this service wants to replace the content later on, it
-  // will start by moving the current directory.
-  base::ScopedFD dir(HANDLE_EINTR(
-      open(entry_path.DirName().value().c_str(), O_RDONLY | O_DIRECTORY)));
-  task_runner->PostTask(FROM_HERE,
-                        base::Bind(&SaveEntry, base::Passed(entry.Pass()),
-                                   base::Passed(dir.Pass())));
+  base::FilePath staged_response_body_path =
+      GetStagingDirectory().Append(identifier);
+  base::FilePath entry_directory = GetCacheDirectory().Append(identifier);
+  base::FilePath response_body_path = entry_directory.Append(identifier);
+  base::FilePath consumer_cache_directory =
+      GetConsumerCacheDirectory(entry_directory);
 
-  callback.Run(content_path, cache_dir);
+  CacheEntryPtr entry = CacheEntry::New();
+  entry->response = response.Pass();
+  entry->entry_directory = entry_directory.value();
+  entry->response_body_path = response_body_path.value();
+
+  db->PutNew(request_origin, url, entry.Pass());
+
+  if (!base::CreateDirectoryAndGetError(entry_directory, nullptr) ||
+      !base::CreateDirectoryAndGetError(consumer_cache_directory, nullptr) ||
+      !base::ReplaceFile(staged_response_body_path, response_body_path,
+                         nullptr)) {
+    MovePathIntoDir(entry_directory, GetStagingDirectory());
+    callback.Run(base::FilePath(), base::FilePath());
+    return;
+  }
+
+  callback.Run(response_body_path, consumer_cache_directory);
 }
 
 // Run the given mojo callback with the given paths.
@@ -212,48 +200,39 @@
 
 // Returns the list of values for the given |header_name| in the given list of
 // headers.
-template <typename HeaderType>
 std::vector<std::string> GetHeaderValues(const std::string& header_name,
-                                         const Array<HeaderType>& headers) {
+                                         const Array<HttpHeaderPtr>& headers) {
   std::vector<std::string> result;
   for (size_t i = 0u; i < headers.size(); ++i) {
-    std::string name = headers[i]->name;
+    const std::string& name = headers[i]->name.get();
     if (base::LowerCaseEqualsASCII(name, header_name.c_str()))
       result.push_back(headers[i]->value);
   }
   return result;
 }
 
-// Returns whether the given directory |dir| constains a valid entry file for
-// the given |response|. If this is the case and |output| is not |nullptr|, then
-// the deserialized entry is returned in |*output|.
-bool IsCacheEntryValid(const base::FilePath& dir,
-                       URLResponse* response,
-                       CacheEntryPtr* output) {
-  // Find the entry file, and deserialize it.
-  base::FilePath entry_path = dir.Append(kEntryName);
-  if (!base::PathExists(entry_path))
-    return false;
-  std::string serialized_entry;
-  if (!ReadFileToString(entry_path, &serialized_entry))
-    return false;
-  CacheEntryPtr entry;
-  if (!Deserialize(serialized_entry, &entry))
-    return false;
+// Returns whether the given |entry| is valid.
+bool IsCacheEntryValid(const CacheEntryPtr& entry) {
+  return entry && PathExists(base::FilePath(entry->response_body_path));
+}
 
-  // Obsolete entries are invalidated.
-  if (entry->version != kCurrentVersion)
+// Returns whether the given directory |entry| is valid and its content can be
+// used for the given |response|.
+bool IsCacheEntryFresh(const URLResponsePtr& response,
+                       const CacheEntryPtr& entry) {
+  DCHECK(response);
+  if (!IsCacheEntryValid(entry))
     return false;
 
   // If |entry| or |response| has not headers, it is not possible to check if
   // the entry is valid, so returns |false|.
-  if (entry->headers.is_null() || response->headers.is_null())
+  if (entry->response->headers.is_null() || response->headers.is_null())
     return false;
 
   // Only handle etag for the moment.
   std::string etag_header_name = kEtagHeader;
   std::vector<std::string> entry_etags =
-      GetHeaderValues(etag_header_name, entry->headers);
+      GetHeaderValues(etag_header_name, entry->response->headers);
   if (entry_etags.size() == 0)
     return false;
   std::vector<std::string> response_etags =
@@ -262,165 +241,224 @@
     return false;
 
   // Looking for the first etag header.
-  bool result = entry_etags[0] == response_etags[0];
+  return entry_etags[0] == response_etags[0];
+}
 
-  // Returns |entry| if requested.
-  if (output)
-    *output = entry.Pass();
-
-  return result;
+void PruneCache(scoped_refptr<URLResponseDiskCacheDB> db,
+                const scoped_ptr<URLResponseDiskCacheDB::Iterator>& iterator) {
+  CacheKeyPtr last_key;
+  CacheKeyPtr key;
+  CacheEntryPtr entry;
+  while (iterator->HasNext()) {
+    iterator->GetNext(&key, &entry);
+    if (last_key && last_key->request_origin == key->request_origin &&
+        last_key->url == key->url) {
+      base::FilePath entry_directory = base::FilePath(entry->entry_directory);
+      if (base::DeleteFile(entry_directory, true))
+        db->Delete(key.Clone());
+    }
+    last_key = key.Pass();
+  }
 }
 
 }  // namespace
 
 // static
-void URLResponseDiskCacheImpl::ClearCache(base::TaskRunner* task_runner) {
-  // Create a unique subdirectory in trash.
+scoped_refptr<URLResponseDiskCacheDB> URLResponseDiskCacheImpl::CreateDB(
+    scoped_refptr<base::TaskRunner> task_runner,
+    bool force_clean) {
+  // Create the trash directory if needed.
   base::FilePath trash_dir = GetTrashDirectory();
   base::CreateDirectory(trash_dir);
-  base::FilePath dest_dir;
-  base::CreateTemporaryDirInDir(trash_dir, "", &dest_dir);
 
-  // Move the current cache directory, if present, into trash.
-  base::FilePath cache_dir = GetCacheDirectory();
-  if (PathExists(cache_dir)) {
-    base::File::Error error;
-    if (!base::ReplaceFile(cache_dir, dest_dir, &error)) {
-      LOG(ERROR) << "Failed to clear cache content: " << error;
+  // Clean the trash directory when exiting this method.
+  base::ScopedClosureRunner trash_cleanup(
+      base::Bind(&ClearTrashDir, task_runner, trash_dir));
+
+  // Move the staging directory to trash.
+  MovePathIntoDir(GetStagingDirectory(), trash_dir);
+  // And recreate it.
+  base::CreateDirectory(GetStagingDirectory());
+
+  base::FilePath db_path = GetBaseDirectory().Append("db");
+
+  if (!force_clean && PathExists(db_path)) {
+    scoped_refptr<URLResponseDiskCacheDB> db =
+        new URLResponseDiskCacheDB(db_path);
+    if (db->GetVersion() == kCurrentVersion) {
+      task_runner->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(&PruneCache, db, base::Passed(db->GetIterator())),
+          base::TimeDelta::FromSeconds(kTrashDelayInSeconds));
+      return db;
     }
   }
 
-  // Delete the trash directory.
-  task_runner->PostTask(
-      FROM_HERE,
-      base::Bind(base::IgnoreResult(&base::DeleteFile), trash_dir, true));
+  // Move the database to trash.
+  MovePathIntoDir(db_path, trash_dir);
+  // Move the current cache content to trash.
+  MovePathIntoDir(GetCacheDirectory(), trash_dir);
+
+  scoped_refptr<URLResponseDiskCacheDB> result =
+      new URLResponseDiskCacheDB(db_path);
+  result->SetVersion(kCurrentVersion);
+  return result;
 }
 
 URLResponseDiskCacheImpl::URLResponseDiskCacheImpl(
-    base::TaskRunner* task_runner,
+    scoped_refptr<base::TaskRunner> task_runner,
+    scoped_refptr<URLResponseDiskCacheDB> db,
     const std::string& remote_application_url,
     InterfaceRequest<URLResponseDiskCache> request)
-    : task_runner_(task_runner), binding_(this, request.Pass()) {
-  base_directory_ = GetCacheDirectory();
-  // The cached files are shared only for application of the same origin.
-  if (remote_application_url != "") {
-    base_directory_ = base_directory_.Append(
-        EncodeString(GURL(remote_application_url).GetOrigin().spec()));
-  }
+    : task_runner_(task_runner), db_(db), binding_(this, request.Pass()) {
+  request_origin_ = GURL(remote_application_url).GetOrigin().spec();
 }
 
 URLResponseDiskCacheImpl::~URLResponseDiskCacheImpl() {
 }
 
-void URLResponseDiskCacheImpl::GetFile(URLResponsePtr response,
-                                       const GetFileCallback& callback) {
-  return GetFileInternal(response.Pass(),
-                         base::Bind(&RunMojoCallback, callback));
+void URLResponseDiskCacheImpl::Get(const String& url,
+                                   const GetCallback& callback) {
+  CacheEntryPtr entry = db_->GetNewest(request_origin_, CanonicalizeURL(url));
+  if (!IsCacheEntryValid(entry)) {
+    callback.Run(URLResponsePtr(), Array<uint8_t>(), Array<uint8_t>());
+    return;
+  }
+  callback.Run(entry->response.Pass(),
+               PathToArray(base::FilePath(entry->response_body_path)),
+               PathToArray(GetConsumerCacheDirectory(
+                   base::FilePath(entry->entry_directory))));
 }
 
-void URLResponseDiskCacheImpl::GetExtractedContent(
+void URLResponseDiskCacheImpl::Update(URLResponsePtr response) {
+  UpdateAndGetInternal(response.Pass(), base::Bind(&DoNothing));
+}
+
+void URLResponseDiskCacheImpl::UpdateAndGet(
     URLResponsePtr response,
-    const GetExtractedContentCallback& callback) {
-  base::FilePath dir = GetDirName(base_directory_, response->url);
-  base::FilePath extracted_dir = dir.Append("extracted");
-  if (IsCacheEntryValid(dir, response.get(), nullptr) &&
-      PathExists(GetExtractedSentinel(dir))) {
-    callback.Run(PathToArray(extracted_dir),
-                 PathToArray(GetConsumerCacheDirectory(dir)));
+    const UpdateAndGetCallback& callback) {
+  UpdateAndGetInternal(response.Pass(), base::Bind(&RunMojoCallback, callback));
+}
+
+void URLResponseDiskCacheImpl::UpdateAndGetExtracted(
+    URLResponsePtr response,
+    const UpdateAndGetExtractedCallback& callback) {
+  if (response->error ||
+      (response->status_code >= 400 && response->status_code < 600)) {
+    callback.Run(Array<uint8_t>(), Array<uint8_t>());
     return;
   }
 
-  GetFileInternal(
-      response.Pass(),
-      base::Bind(&URLResponseDiskCacheImpl::GetExtractedContentInternal,
-                 base::Unretained(this), base::Bind(&RunMojoCallback, callback),
-                 dir, extracted_dir));
-}
-
-void URLResponseDiskCacheImpl::GetFileInternal(
-    URLResponsePtr response,
-    const FilePathPairCallback& callback) {
-  base::FilePath dir = GetDirName(base_directory_, response->url);
+  std::string url = CanonicalizeURL(response->url);
 
   // Check if the response is cached and valid. If that's the case, returns the
   // cached value.
-  CacheEntryPtr entry;
-  if (IsCacheEntryValid(dir, response.get(), &entry)) {
-    callback.Run(base::FilePath(entry->content_path),
-                 GetConsumerCacheDirectory(dir));
+  CacheEntryPtr entry = db_->GetNewest(request_origin_, url);
+
+  if (!IsCacheEntryFresh(response, entry)) {
+    UpdateAndGetInternal(
+        response.Pass(),
+        base::Bind(&URLResponseDiskCacheImpl::UpdateAndGetExtractedInternal,
+                   base::Unretained(this),
+                   base::Bind(&RunMojoCallback, callback)));
     return;
   }
 
-  // As the response was either not cached or the cached value is not valid, if
-  // the cache directory for the response exists, it needs to be cleaned.
-  if (base::PathExists(dir)) {
-    base::FilePath to_delete;
-    CHECK(CreateTemporaryDirInDir(base_directory_, "to_delete", &to_delete));
-    CHECK(Move(dir, to_delete));
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(base::IgnoreResult(&base::DeleteFile), to_delete, true));
-  }
-
-  // If the response has not a valid body, and it is not possible to create
-  // either the cache directory or the consumer cache directory, returns an
-  // error.
-  if (!response->body.is_valid() ||
-      !base::CreateDirectoryAndGetError(dir, nullptr) ||
-      !base::CreateDirectoryAndGetError(GetConsumerCacheDirectory(dir),
-                                        nullptr)) {
-    callback.Run(base::FilePath(), base::FilePath());
+  base::FilePath entry_directory = base::FilePath(entry->entry_directory);
+  base::FilePath extraction_directory = GetExtractionDirectory(entry_directory);
+  if (!PathExists(GetExtractionSentinel(entry_directory))) {
+    UpdateAndGetExtractedInternal(base::Bind(&RunMojoCallback, callback),
+                                  base::FilePath(entry->response_body_path),
+                                  GetConsumerCacheDirectory(entry_directory));
     return;
   }
 
-  // Fill the entry values for the request.
-  base::FilePath entry_path = dir.Append(kEntryName);
-  base::FilePath content;
-  CHECK(CreateTemporaryFileInDir(dir, &content));
-  entry = CacheEntry::New();
-  entry->version = kCurrentVersion;
-  entry->url = response->url;
-  entry->content_path = content.value();
-  for (size_t i = 0u; i < response->headers.size(); ++i) {
-    auto cache_header = CacheHeaders::New();
-    cache_header->name = response->headers[i]->name;
-    cache_header->value = response->headers[i]->value;
-    entry->headers.push_back(cache_header.Pass());
-  }
-  // Asynchronously copy the response body to the cached file. The entry is send
-  // to the callback so that it is saved on disk only if the copy of the body
-  // succeded.
-  common::CopyToFile(
-      response->body.Pass(), content, task_runner_,
-      base::Bind(&RunCallbackWithSuccess, callback, content,
-                 GetConsumerCacheDirectory(dir), entry_path,
-                 base::Passed(entry.Pass()), base::Unretained(task_runner_)));
+  callback.Run(PathToArray(extraction_directory),
+               PathToArray(GetConsumerCacheDirectory(entry_directory)));
 }
 
-void URLResponseDiskCacheImpl::GetExtractedContentInternal(
-    const FilePathPairCallback& callback,
-    const base::FilePath& base_dir,
-    const base::FilePath& extracted_dir,
-    const base::FilePath& content,
-    const base::FilePath& cache_dir) {
-  TRACE_EVENT1("url_response_disk_cache", "GetExtractedContentInternal",
-               "extracted_dir", extracted_dir.value());
-  // If it is not possible to get the cached file, returns an error.
-  if (content.empty()) {
+void URLResponseDiskCacheImpl::UpdateAndGetInternal(
+    URLResponsePtr response,
+    const ResponseFileAndCacheDirCallback& callback) {
+  if (response->error ||
+      (response->status_code >= 400 && response->status_code < 600)) {
     callback.Run(base::FilePath(), base::FilePath());
     return;
   }
 
+  std::string url = CanonicalizeURL(response->url);
+
+  // Check if the response is cached and valid. If that's the case, returns
+  // the cached value.
+  CacheEntryPtr entry = db_->GetNewest(request_origin_, url);
+  if (IsCacheEntryFresh(response, entry)) {
+    callback.Run(
+        base::FilePath(entry->response_body_path),
+        GetConsumerCacheDirectory(base::FilePath(entry->entry_directory)));
+    return;
+  }
+
+  if (!response->body.is_valid()) {
+    callback.Run(base::FilePath(), base::FilePath());
+    return;
+  }
+
+  std::string identifier = GetNewIdentifier();
+  // The content is copied to the staging directory so that files are not leaked
+  // if the shell terminates before an entry is saved to the database.
+  base::FilePath staged_response_body_path =
+      GetStagingDirectory().Append(identifier);
+
+  ScopedDataPipeConsumerHandle body = response->body.Pass();
+
+  // Asynchronously copy the response body to the staging directory. The
+  // callback will move it to the cache directory and save an entry in the
+  // database only if the copy of the body succeded.
+  common::CopyToFile(
+      body.Pass(), staged_response_body_path, task_runner_.get(),
+      base::Bind(&RunCallbackWithSuccess, callback, identifier, request_origin_,
+                 url, base::Passed(response.Pass()), db_, task_runner_));
+}
+
+void URLResponseDiskCacheImpl::UpdateAndGetExtractedInternal(
+    const ResponseFileAndCacheDirCallback& callback,
+    const base::FilePath& response_body_path,
+    const base::FilePath& consumer_cache_directory) {
+  TRACE_EVENT1("url_response_disk_cache", "UpdateAndGetExtractedInternal",
+               "response_body_path", response_body_path.value());
+  // If it is not possible to get the cached file, returns an error.
+  if (response_body_path.empty()) {
+    callback.Run(base::FilePath(), base::FilePath());
+    return;
+  }
+
+  base::FilePath entry_directory = consumer_cache_directory.DirName();
+  base::FilePath extraction_directory = GetExtractionDirectory(entry_directory);
+  base::FilePath extraction_sentinel = GetExtractionSentinel(entry_directory);
+
+  if (PathExists(extraction_sentinel)) {
+    callback.Run(extraction_directory, consumer_cache_directory);
+    return;
+  }
+
+  if (PathExists(extraction_directory)) {
+    if (!base::DeleteFile(extraction_directory, true)) {
+      callback.Run(base::FilePath(), base::FilePath());
+      return;
+    }
+  }
+
   // Unzip the content to the extracted directory. In case of any error, returns
   // an error.
   zip::ZipReader reader;
-  if (!reader.Open(content)) {
+  if (!reader.Open(response_body_path)) {
     callback.Run(base::FilePath(), base::FilePath());
     return;
   }
   while (reader.HasMore()) {
     bool success = reader.OpenCurrentEntryInZip();
-    success = success && reader.ExtractCurrentEntryIntoDirectory(extracted_dir);
+    success = success &&
+              reader.ExtractCurrentEntryIntoDirectory(extraction_directory);
     success = success && reader.AdvanceToNextEntry();
     if (!success) {
       callback.Run(base::FilePath(), base::FilePath());
@@ -429,8 +467,8 @@
   }
   // We can ignore write error, as it will just force to clear the cache on the
   // next request.
-  WriteFile(GetExtractedSentinel(base_dir), nullptr, 0);
-  callback.Run(extracted_dir, cache_dir);
+  WriteFile(GetExtractionSentinel(entry_directory), nullptr, 0);
+  callback.Run(extraction_directory, consumer_cache_directory);
 }
 
 }  // namespace mojo
diff --git a/services/url_response_disk_cache/url_response_disk_cache_impl.h b/services/url_response_disk_cache/url_response_disk_cache_impl.h
index fc70766..95695e7 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_impl.h
+++ b/services/url_response_disk_cache/url_response_disk_cache_impl.h
@@ -7,55 +7,63 @@
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/task_runner.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom.h"
+#include "services/url_response_disk_cache/url_response_disk_cache_db.h"
 
 namespace mojo {
 
 class URLResponseDiskCacheImpl : public URLResponseDiskCache {
  public:
-  using FilePathPairCallback =
+  using ResponseFileAndCacheDirCallback =
       base::Callback<void(const base::FilePath&, const base::FilePath&)>;
 
-  // Cleares the cached content. The actual deletion will be performed using the
-  // given task runner, but cache appears as cleared immediately after the
-  // function returns.
-  static void ClearCache(base::TaskRunner* task_runner);
+  // Creates the disk cache database. If |force_clean| is true, or the database
+  // is outdated, it will clear the cached content. The actual deletion will be
+  // performed using the given task runner, but cache appears as cleared
+  // immediately after the function returns.
+  static scoped_refptr<URLResponseDiskCacheDB> CreateDB(
+      scoped_refptr<base::TaskRunner> task_runner,
+      bool force_clean);
 
-  URLResponseDiskCacheImpl(base::TaskRunner* task_runner,
+  URLResponseDiskCacheImpl(scoped_refptr<base::TaskRunner> task_runner,
+                           scoped_refptr<URLResponseDiskCacheDB> db,
                            const std::string& remote_application_url,
                            InterfaceRequest<URLResponseDiskCache> request);
   ~URLResponseDiskCacheImpl() override;
 
  private:
   // URLResponseDiskCache
-  void GetFile(mojo::URLResponsePtr response,
-               const GetFileCallback& callback) override;
-  void GetExtractedContent(
-      mojo::URLResponsePtr response,
-      const GetExtractedContentCallback& callback) override;
+  void Get(const String& url, const GetCallback& callback) override;
+  void Update(URLResponsePtr response) override;
+  void UpdateAndGet(URLResponsePtr response,
+                    const UpdateAndGetCallback& callback) override;
+  void UpdateAndGetExtracted(
+      URLResponsePtr response,
+      const UpdateAndGetExtractedCallback& callback) override;
 
-  // As |GetFile|, but uses FilePath instead of mojo arrays.
-  void GetFileInternal(mojo::URLResponsePtr response,
-                       const FilePathPairCallback& callback);
+  // Internal implementation of |UpdateAndGet| using a pair of base::FilePath to
+  // represent the paths the the response body and to the per-response client
+  // cache directory.
+  void UpdateAndGetInternal(URLResponsePtr response,
+                            const ResponseFileAndCacheDirCallback& callback);
 
-  // Internal implementation of |GetExtractedContent|. The parameters are:
+  // Internal implementation of |UpdateAndGetExtracted|. The parameters are:
   // |callback|: The callback to return values to the caller. It uses FilePath
   //             instead of mojo arrays.
-  // |base_dir|: The base directory for caching data associated to the response.
-  // |extracted_dir|: The directory where the file content must be extracted. It
-  //                  will be  returned to the consumer.
-  // |content|: The content of the body of the response.
-  // |cache_dir|: The directory the user can user to cache its own content.
-  void GetExtractedContentInternal(const FilePathPairCallback& callback,
-                                   const base::FilePath& base_dir,
-                                   const base::FilePath& extracted_dir,
-                                   const base::FilePath& content,
-                                   const base::FilePath& cache_dir);
+  // |response_body_path|: The path to the content of the body of the response.
+  // |consumer_cache_directory|: The directory the user can user to cache its
+  //                             own content.
+  void UpdateAndGetExtractedInternal(
+      const ResponseFileAndCacheDirCallback& callback,
+      const base::FilePath& response_body_path,
+      const base::FilePath& consumer_cache_directory);
 
-  base::TaskRunner* task_runner_;
-  base::FilePath base_directory_;
+  scoped_refptr<base::TaskRunner> task_runner_;
+  std::string request_origin_;
+  scoped_refptr<URLResponseDiskCacheDB> db_;
   StrongBinding<URLResponseDiskCache> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(URLResponseDiskCacheImpl);
diff --git a/shell/android/android_handler.cc b/shell/android/android_handler.cc
index 9523446..2a30ed5 100644
--- a/shell/android/android_handler.cc
+++ b/shell/android/android_handler.cc
@@ -139,7 +139,7 @@
                                         base::FilePath* cache_dir,
                                         mojo::URLResponsePtr response,
                                         const base::Closure& callback) {
-  url_response_disk_cache_->GetExtractedContent(
+  url_response_disk_cache_->UpdateAndGetExtracted(
       response.Pass(),
       [extracted_dir, cache_dir, callback](mojo::Array<uint8_t> extracted_path,
                                            mojo::Array<uint8_t> cache_path) {
diff --git a/shell/application_manager/application_manager.cc b/shell/application_manager/application_manager.cc
index 2346d3a..374b6c9 100644
--- a/shell/application_manager/application_manager.cc
+++ b/shell/application_manager/application_manager.cc
@@ -185,20 +185,22 @@
   if (!url_response_disk_cache_) {
     ConnectToService(GURL("mojo:url_response_disk_cache"),
                      &url_response_disk_cache_);
+    ConnectToService(GURL("mojo:network_service"), &network_service_);
+    ConnectToService(GURL("mojo:network_service"),
+                     &authenticating_network_service_);
   }
 
-  if (!network_service_) {
-    ConnectToService(GURL("mojo:network_service"), &network_service_);
-  }
+  mojo::NetworkService* network_service = authenticating_network_service_.get();
 
   // NOTE: Attempting to initialize the apps used authentication for while
   // connecting to those apps would result in a recursive loop, so it has to be
   // explicitly avoided here. What this means in practice is that these apps
   // cannot themselves require authentication to obtain.
-  if (!initialized_authentication_interceptor_ &&
-      !EndsWith(resolved_url.path(), "/authentication.mojo", true) &&
-      !EndsWith(resolved_url.path(),
-                "/authenticating_url_loader_interceptor.mojo", true)) {
+  if (EndsWith(resolved_url.path(), "/authentication.mojo", true) ||
+      EndsWith(resolved_url.path(),
+               "/authenticating_url_loader_interceptor.mojo", true)) {
+    network_service = network_service_.get();
+  } else if (!initialized_authentication_interceptor_) {
     authentication::AuthenticationServicePtr authentication_service;
     ConnectToService(GURL("mojo:authentication"), &authentication_service);
     mojo::AuthenticatingURLLoaderInterceptorMetaFactoryPtr
@@ -208,13 +210,13 @@
     mojo::URLLoaderInterceptorFactoryPtr interceptor_factory;
     interceptor_meta_factory->CreateURLLoaderInterceptorFactory(
         GetProxy(&interceptor_factory), authentication_service.Pass());
-    network_service_->RegisterURLLoaderInterceptor(interceptor_factory.Pass());
+    authenticating_network_service_->RegisterURLLoaderInterceptor(
+        interceptor_factory.Pass());
     initialized_authentication_interceptor_ = true;
   }
 
   new NetworkFetcher(options_.disable_cache, resolved_url,
-                     url_response_disk_cache_.get(), network_service_.get(),
-                     callback);
+                     url_response_disk_cache_.get(), network_service, callback);
 }
 
 bool ApplicationManager::ConnectToRunningApplication(
diff --git a/shell/application_manager/application_manager.h b/shell/application_manager/application_manager.h
index b1b647f..7a14311 100644
--- a/shell/application_manager/application_manager.h
+++ b/shell/application_manager/application_manager.h
@@ -240,6 +240,7 @@
   base::SequencedWorkerPool* blocking_pool_;
   mojo::URLResponseDiskCachePtr url_response_disk_cache_;
   mojo::NetworkServicePtr network_service_;
+  mojo::NetworkServicePtr authenticating_network_service_;
   MimeTypeToURLMap mime_type_to_url_;
   ScopedVector<NativeRunner> native_runners_;
   bool initialized_authentication_interceptor_;
diff --git a/shell/application_manager/fetcher.cc b/shell/application_manager/fetcher.cc
index f76876b..539cb64 100644
--- a/shell/application_manager/fetcher.cc
+++ b/shell/application_manager/fetcher.cc
@@ -4,12 +4,16 @@
 
 #include "shell/application_manager/fetcher.h"
 
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "url/gurl.h"
 
 namespace shell {
 
-const char Fetcher::kMojoMagic[] = "#!mojo ";
-const size_t Fetcher::kMaxShebangLength = 2048;
+namespace {
+const char kMojoMagic[] = "#!mojo ";
+const size_t kMaxShebangLength = 2048;
+}
 
 Fetcher::Fetcher(const FetchCallback& loader_callback)
     : loader_callback_(loader_callback) {
@@ -33,4 +37,24 @@
   return false;
 }
 
+// static
+bool Fetcher::HasMojoMagic(const base::FilePath& path) {
+  DCHECK(!path.empty());
+  std::string magic;
+  ReadFileToString(path, &magic, strlen(kMojoMagic));
+  return magic == kMojoMagic;
+}
+
+// static
+bool Fetcher::PeekFirstLine(const base::FilePath& path, std::string* line) {
+  DCHECK(!path.empty());
+  std::string start_of_file;
+  ReadFileToString(path, &start_of_file, kMaxShebangLength);
+  size_t return_position = start_of_file.find('\n');
+  if (return_position == std::string::npos)
+    return false;
+  *line = start_of_file.substr(0, return_position + 1);
+  return true;
+}
+
 }  // namespace shell
diff --git a/shell/application_manager/fetcher.h b/shell/application_manager/fetcher.h
index 0789387..e1039f3 100644
--- a/shell/application_manager/fetcher.h
+++ b/shell/application_manager/fetcher.h
@@ -63,8 +63,8 @@
                           GURL* mojo_content_handler_url);
 
  protected:
-  static const char kMojoMagic[];
-  static const size_t kMaxShebangLength;
+  static bool HasMojoMagic(const base::FilePath& path);
+  static bool PeekFirstLine(const base::FilePath& path, std::string* line);
 
   FetchCallback loader_callback_;
 };
diff --git a/shell/application_manager/local_fetcher.cc b/shell/application_manager/local_fetcher.cc
index ce75385..de52d4a 100644
--- a/shell/application_manager/local_fetcher.cc
+++ b/shell/application_manager/local_fetcher.cc
@@ -91,19 +91,11 @@
 }
 
 bool LocalFetcher::HasMojoMagic() {
-  std::string magic;
-  ReadFileToString(path_, &magic, strlen(kMojoMagic));
-  return magic == kMojoMagic;
+  return Fetcher::HasMojoMagic(path_);
 }
 
 bool LocalFetcher::PeekFirstLine(std::string* line) {
-  std::string start_of_file;
-  ReadFileToString(path_, &start_of_file, kMaxShebangLength);
-  size_t return_position = start_of_file.find('\n');
-  if (return_position == std::string::npos)
-    return false;
-  *line = start_of_file.substr(0, return_position + 1);
-  return true;
+  return Fetcher::PeekFirstLine(path_, line);
 }
 
 }  // namespace shell
diff --git a/shell/application_manager/network_fetcher.cc b/shell/application_manager/network_fetcher.cc
index 31caa19..039cbea 100644
--- a/shell/application_manager/network_fetcher.cc
+++ b/shell/application_manager/network_fetcher.cc
@@ -32,8 +32,96 @@
 #else
 #error "Unsupported."
 #endif
+
+// The delay to wait before trying to update an application after having served
+// it from the cache.
+const uint32_t kUpdateApplicationDelayInSeconds = 60;
+
+base::FilePath ToFilePath(const mojo::Array<uint8_t>& array) {
+  return base::FilePath(
+      std::string(reinterpret_cast<const char*>(&array.front()), array.size()));
+}
+
+void IgnoreResult(bool result) {}
+
+mojo::URLRequestPtr GetRequest(const GURL& url, bool disable_cache) {
+  mojo::URLRequestPtr request(mojo::URLRequest::New());
+  request->url = mojo::String::From(url);
+  request->auto_follow_redirects = false;
+  if (disable_cache)
+    request->cache_mode = mojo::URLRequest::CACHE_MODE_BYPASS_CACHE;
+  auto architecture_header = mojo::HttpHeader::New();
+  architecture_header->name = "X-Architecture";
+  architecture_header->value = kArchitecture;
+  mojo::Array<mojo::HttpHeaderPtr> headers;
+  headers.push_back(architecture_header.Pass());
+  request->headers = headers.Pass();
+
+  return request.Pass();
+}
+
+// This class is self owned and will delete itself after having tried to update
+// the application cache.
+class ApplicationUpdater : public base::MessageLoop::DestructionObserver {
+ public:
+  ApplicationUpdater(const GURL& url,
+                     const base::TimeDelta& update_delay,
+                     mojo::URLResponseDiskCache* url_response_disk_cache,
+                     mojo::NetworkService* network_service);
+  ~ApplicationUpdater() override;
+
+ private:
+  // DestructionObserver
+  void WillDestroyCurrentMessageLoop() override;
+
+  void UpdateApplication();
+  void OnLoadComplete(mojo::URLResponsePtr response);
+
+  GURL url_;
+  base::TimeDelta update_delay_;
+  mojo::URLResponseDiskCache* url_response_disk_cache_;
+  mojo::NetworkService* network_service_;
+  mojo::URLLoaderPtr url_loader_;
 };
 
+ApplicationUpdater::ApplicationUpdater(
+    const GURL& url,
+    const base::TimeDelta& update_delay,
+    mojo::URLResponseDiskCache* url_response_disk_cache,
+    mojo::NetworkService* network_service)
+    : url_(url),
+      update_delay_(update_delay),
+      url_response_disk_cache_(url_response_disk_cache),
+      network_service_(network_service) {
+  base::MessageLoop::current()->AddDestructionObserver(this);
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE, base::Bind(&ApplicationUpdater::UpdateApplication,
+                            base::Unretained(this)),
+      update_delay_);
+}
+
+ApplicationUpdater::~ApplicationUpdater() {
+  base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+void ApplicationUpdater::WillDestroyCurrentMessageLoop() {
+  delete this;
+}
+
+void ApplicationUpdater::UpdateApplication() {
+  network_service_->CreateURLLoader(GetProxy(&url_loader_));
+  url_loader_->Start(
+      GetRequest(url_, false),
+      base::Bind(&ApplicationUpdater::OnLoadComplete, base::Unretained(this)));
+}
+
+void ApplicationUpdater::OnLoadComplete(mojo::URLResponsePtr response) {
+  url_response_disk_cache_->Update(response.Pass());
+  delete this;
+}
+
+}  // namespace
+
 NetworkFetcher::NetworkFetcher(
     bool disable_cache,
     const GURL& url,
@@ -46,7 +134,11 @@
       url_response_disk_cache_(url_response_disk_cache),
       network_service_(network_service),
       weak_ptr_factory_(this) {
-  StartNetworkRequest(FROM_NETWORK);
+  if (CanLoadDirectlyFromCache()) {
+    LoadFromCache(true);
+  } else {
+    StartNetworkRequest();
+  }
 }
 
 NetworkFetcher::~NetworkFetcher() {
@@ -69,15 +161,128 @@
 mojo::URLResponsePtr NetworkFetcher::AsURLResponse(
     base::TaskRunner* task_runner,
     uint32_t skip) {
-  if (skip != 0) {
-    MojoResult result = ReadDataRaw(
-        response_->body.get(), nullptr, &skip,
-        MOJO_READ_DATA_FLAG_ALL_OR_NONE | MOJO_READ_DATA_FLAG_DISCARD);
-    DCHECK_EQ(result, MOJO_RESULT_OK);
-  }
+  DCHECK(response_);
+  DCHECK(!path_.empty());
+  mojo::DataPipe data_pipe;
+  response_->body = data_pipe.consumer_handle.Pass();
+  mojo::common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
+                             task_runner, base::Bind(&IgnoreResult));
   return response_.Pass();
 }
 
+void NetworkFetcher::AsPath(
+    base::TaskRunner* task_runner,
+    base::Callback<void(const base::FilePath&, bool)> callback) {
+  // This should only called once, when we have a response.
+  DCHECK(response_.get());
+
+  base::MessageLoop::current()->PostTask(
+      FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
+  response_.reset();
+  return;
+}
+
+std::string NetworkFetcher::MimeType() {
+  return response_->mime_type;
+}
+
+bool NetworkFetcher::HasMojoMagic() {
+  return Fetcher::HasMojoMagic(path_);
+}
+
+bool NetworkFetcher::PeekFirstLine(std::string* line) {
+  return Fetcher::PeekFirstLine(path_, line);
+}
+
+bool NetworkFetcher::CanLoadDirectlyFromCache() {
+  if (disable_cache_)
+    return false;
+
+  const std::string& host = url_.host();
+  return !(host == "localhost" || host == "127.0.0.1" || host == "[::1]");
+}
+
+void NetworkFetcher::LoadFromCache(bool schedule_update) {
+  url_response_disk_cache_->Get(
+      mojo::String::From(url_),
+      base::Bind(&NetworkFetcher::OnCachedResponseReceived,
+                 base::Unretained(this), schedule_update));
+}
+
+void NetworkFetcher::OnCachedResponseReceived(
+    bool schedule_update,
+    mojo::URLResponsePtr response,
+    mojo::Array<uint8_t> path_as_array,
+    mojo::Array<uint8_t> cache_dir) {
+  if (!response) {
+    // Not in cache, loading from net.
+    StartNetworkRequest();
+    return;
+  }
+  if (schedule_update) {
+    // The response has been found in the cache. Plan updating the application.
+    new ApplicationUpdater(
+        url_, base::TimeDelta::FromSeconds(kUpdateApplicationDelayInSeconds),
+        url_response_disk_cache_, network_service_);
+  }
+  response_ = response.Pass();
+  path_ = ToFilePath(path_as_array);
+  RecordCacheToURLMapping(path_, url_);
+  loader_callback_.Run(make_scoped_ptr(this));
+}
+
+void NetworkFetcher::StartNetworkRequest() {
+  TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this,
+                           "url", url_.spec());
+  network_service_->CreateURLLoader(GetProxy(&url_loader_));
+  url_loader_->Start(GetRequest(url_, disable_cache_),
+                     base::Bind(&NetworkFetcher::OnLoadComplete,
+                                weak_ptr_factory_.GetWeakPtr()));
+}
+
+void NetworkFetcher::OnLoadComplete(mojo::URLResponsePtr response) {
+  TRACE_EVENT_ASYNC_END0("mojo_shell", "NetworkFetcher::NetworkRequest", this);
+  if (response->error) {
+    LOG(ERROR) << "Error (" << response->error->code << ": "
+               << response->error->description << ") while fetching "
+               << response->url;
+    loader_callback_.Run(nullptr);
+    delete this;
+    return;
+  }
+
+  if (response->status_code >= 400 && response->status_code < 600) {
+    LOG(ERROR) << "Error (" << response->status_code << ": "
+               << response->status_line << "): "
+               << "while fetching " << response->url;
+    loader_callback_.Run(nullptr);
+    delete this;
+    return;
+  }
+
+  if (!response->redirect_url.is_null()) {
+    response_ = response.Pass();
+    loader_callback_.Run(make_scoped_ptr(this));
+    return;
+  }
+
+  url_response_disk_cache_->UpdateAndGet(
+      response.Pass(), base::Bind(&NetworkFetcher::OnFileSavedToCache,
+                                  weak_ptr_factory_.GetWeakPtr()));
+}
+
+void NetworkFetcher::OnFileSavedToCache(mojo::Array<uint8_t> path_as_array,
+                                        mojo::Array<uint8_t> cache_dir) {
+  if (!path_as_array) {
+    LOG(WARNING) << "Error when retrieving content from cache for: "
+                 << url_.spec();
+    loader_callback_.Run(nullptr);
+    delete this;
+    return;
+  }
+  LoadFromCache(false);
+}
+
 void NetworkFetcher::RecordCacheToURLMapping(const base::FilePath& path,
                                              const GURL& url) {
   // This is used to extract symbols on android.
@@ -94,101 +299,11 @@
   std::string map_entry =
       base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
   // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
-  if (!PathExists(map_path))
+  if (!PathExists(map_path)) {
     base::WriteFile(map_path, map_entry.data(), map_entry.length());
-  else
+  } else {
     base::AppendToFile(map_path, map_entry.data(), map_entry.length());
-}
-
-void NetworkFetcher::OnFileRetrievedFromCache(
-    base::Callback<void(const base::FilePath&, bool)> callback,
-    mojo::Array<uint8_t> path_as_array,
-    mojo::Array<uint8_t> cache_dir) {
-  bool success = !path_as_array.is_null();
-  if (success) {
-    path_ = base::FilePath(std::string(
-        reinterpret_cast<char*>(&path_as_array.front()), path_as_array.size()));
-    RecordCacheToURLMapping(path_, url_);
   }
-
-  base::MessageLoop::current()->PostTask(FROM_HERE,
-                                         base::Bind(callback, path_, success));
-}
-
-void NetworkFetcher::AsPath(
-    base::TaskRunner* task_runner,
-    base::Callback<void(const base::FilePath&, bool)> callback) {
-  // This should only called once, when we have a response.
-  DCHECK(response_.get());
-
-  url_response_disk_cache_->GetFile(
-      response_.Pass(), base::Bind(&NetworkFetcher::OnFileRetrievedFromCache,
-                                   weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-std::string NetworkFetcher::MimeType() {
-  return response_->mime_type;
-}
-
-bool NetworkFetcher::HasMojoMagic() {
-  std::string magic;
-  return BlockingPeekNBytes(response_->body.get(), &magic, strlen(kMojoMagic),
-                            kPeekTimeout) &&
-         magic == kMojoMagic;
-}
-
-bool NetworkFetcher::PeekFirstLine(std::string* line) {
-  return BlockingPeekLine(response_->body.get(), line, kMaxShebangLength,
-                          kPeekTimeout);
-}
-
-void NetworkFetcher::StartNetworkRequest(RequestType request_type) {
-  TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this,
-                           "url", url_.spec());
-  mojo::URLRequestPtr request(mojo::URLRequest::New());
-  request->url = mojo::String::From(url_);
-  request->auto_follow_redirects = false;
-  if (disable_cache_)
-    request->cache_mode = mojo::URLRequest::CACHE_MODE_BYPASS_CACHE;
-  auto header = mojo::HttpHeader::New();
-  header->name = "X-Architecture";
-  header->value = kArchitecture;
-  mojo::Array<mojo::HttpHeaderPtr> headers;
-  headers.push_back(header.Pass());
-  request->headers = headers.Pass();
-
-  network_service_->CreateURLLoader(GetProxy(&url_loader_));
-  url_loader_->Start(request.Pass(),
-                     base::Bind(&NetworkFetcher::OnLoadComplete,
-                                weak_ptr_factory_.GetWeakPtr(), request_type));
-}
-
-void NetworkFetcher::OnLoadComplete(RequestType request_type,
-                                    mojo::URLResponsePtr response) {
-  TRACE_EVENT_ASYNC_END0("mojo_shell", "NetworkFetcher::NetworkRequest", this);
-  scoped_ptr<Fetcher> owner(this);
-  if (response->error) {
-    LOG(ERROR) << "Error (" << response->error->code << ": "
-               << response->error->description << ") while fetching "
-               << response->url;
-    if (request_type == FROM_NETWORK) {
-      StartNetworkRequest(FROM_CACHE);
-    } else {
-      loader_callback_.Run(nullptr);
-    }
-    return;
-  }
-
-  if (response->status_code >= 400 && response->status_code < 600) {
-    LOG(ERROR) << "Error (" << response->status_code << ": "
-               << response->status_line << "): "
-               << "while fetching " << response->url;
-    loader_callback_.Run(nullptr);
-    return;
-  }
-
-  response_ = response.Pass();
-  loader_callback_.Run(owner.Pass());
 }
 
 }  // namespace shell
diff --git a/shell/application_manager/network_fetcher.h b/shell/application_manager/network_fetcher.h
index 887aa7c..9f1d455 100644
--- a/shell/application_manager/network_fetcher.h
+++ b/shell/application_manager/network_fetcher.h
@@ -28,31 +28,12 @@
   ~NetworkFetcher() override;
 
  private:
-  // TODO(hansmuller): Revisit this when a real peek operation is available.
-  static const MojoDeadline kPeekTimeout = MOJO_DEADLINE_INDEFINITE;
-
-  // The network fetcher will first try to request an application from the
-  // network. If that request fails, it will then try to request the application
-  // from the cache.
-  enum RequestType {
-    FROM_NETWORK,
-    FROM_CACHE,
-  };
-
   const GURL& GetURL() const override;
   GURL GetRedirectURL() const override;
 
   mojo::URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
                                      uint32_t skip) override;
 
-  static void RecordCacheToURLMapping(const base::FilePath& path,
-                                      const GURL& url);
-
-  void OnFileRetrievedFromCache(
-      base::Callback<void(const base::FilePath&, bool)> callback,
-      mojo::Array<uint8_t> path_as_array,
-      mojo::Array<uint8_t> cache_dir);
-
   void AsPath(
       base::TaskRunner* task_runner,
       base::Callback<void(const base::FilePath&, bool)> callback) override;
@@ -63,9 +44,26 @@
 
   bool PeekFirstLine(std::string* line) override;
 
-  void StartNetworkRequest(RequestType request_type);
+  // Returns whether the content can be loaded directly from cache. Local hosts
+  // are not loaded from cache to allow effective development.
+  bool CanLoadDirectlyFromCache();
 
-  void OnLoadComplete(RequestType request_type, mojo::URLResponsePtr response);
+  void LoadFromCache(bool schedule_update);
+
+  void OnCachedResponseReceived(bool schedule_update,
+                                mojo::URLResponsePtr response,
+                                mojo::Array<uint8_t> path_as_array,
+                                mojo::Array<uint8_t> cache_dir);
+
+  void StartNetworkRequest();
+
+  void OnLoadComplete(mojo::URLResponsePtr response);
+
+  void OnFileSavedToCache(mojo::Array<uint8_t> path_as_array,
+                          mojo::Array<uint8_t> cache_dir);
+
+  static void RecordCacheToURLMapping(const base::FilePath& path,
+                                      const GURL& url);
 
   const bool disable_cache_;
   const GURL url_;
diff --git a/shell/url_response_disk_cache_loader.cc b/shell/url_response_disk_cache_loader.cc
index 8906bb4..ef0d259 100644
--- a/shell/url_response_disk_cache_loader.cc
+++ b/shell/url_response_disk_cache_loader.cc
@@ -7,9 +7,8 @@
 namespace shell {
 
 URLResponseDiskCacheLoader::URLResponseDiskCacheLoader(
-    base::TaskRunner* task_runner)
-    : url_response_disk_cache_(task_runner) {
-}
+    scoped_refptr<base::TaskRunner> task_runner)
+    : url_response_disk_cache_(task_runner) {}
 
 URLResponseDiskCacheLoader::~URLResponseDiskCacheLoader() {
 }
diff --git a/shell/url_response_disk_cache_loader.h b/shell/url_response_disk_cache_loader.h
index c5ee366..8f52610 100644
--- a/shell/url_response_disk_cache_loader.h
+++ b/shell/url_response_disk_cache_loader.h
@@ -16,7 +16,7 @@
 
 class URLResponseDiskCacheLoader : public ApplicationLoader {
  public:
-  URLResponseDiskCacheLoader(base::TaskRunner* task_runner);
+  URLResponseDiskCacheLoader(scoped_refptr<base::TaskRunner> task_runner);
   ~URLResponseDiskCacheLoader() override;
 
  private:
diff --git a/third_party/snappy/BUILD.gn b/third_party/snappy/BUILD.gn
new file mode 100644
index 0000000..0f440a5
--- /dev/null
+++ b/third_party/snappy/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright (c) 2013 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.
+
+config("snappy_config") {
+  include_dirs = [ "src" ]
+
+  # These OS-specific generated headers were made by running the configure
+  # script offline.
+  if (is_win) {
+    include_dirs += [ "win32" ]
+  } else if (is_mac) {
+    include_dirs += [ "mac" ]
+  } else {
+    include_dirs += [ "linux" ]
+  }
+}
+
+static_library("snappy") {
+  sources = [
+    "src/snappy-internal.h",
+    "src/snappy-sinksource.cc",
+    "src/snappy-sinksource.h",
+    "src/snappy-stubs-internal.cc",
+    "src/snappy-stubs-internal.h",
+    "src/snappy.cc",
+    "src/snappy.h",
+  ]
+
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+  public_configs = [ ":snappy_config" ]
+
+  if (is_clang) {
+    # snappy-stubs-internal.h unapologetically has: using namespace std
+    # https://code.google.com/p/snappy/issues/detail?id=70
+    configs -= [ "//build/config/clang:extra_warnings" ]
+  }
+
+  if (is_win) {
+    # https://code.google.com/p/snappy/issues/detail?id=75
+    cflags = [ "/wd4267" ]  # Conversion from size_t to 'type'.
+  }
+}
diff --git a/third_party/snappy/OWNERS b/third_party/snappy/OWNERS
new file mode 100644
index 0000000..55e9be9
--- /dev/null
+++ b/third_party/snappy/OWNERS
@@ -0,0 +1,4 @@
+dgrogan@chromium.org
+jsbell@chromium.org
+kinuko@chromium.org
+michaeln@chromium.org
diff --git a/third_party/snappy/README.chromium b/third_party/snappy/README.chromium
new file mode 100644
index 0000000..f7e3742
--- /dev/null
+++ b/third_party/snappy/README.chromium
@@ -0,0 +1,17 @@
+Name: Snappy: A fast compressor/decompressor
+Short Name: snappy
+URL: http://code.google.com/p/snappy/
+Version: r80
+License: New BSD
+License File: src/COPYING
+Security Critical: yes
+
+Description:
+Compression library used by LevelDB.
+
+Local Additions:
+* gyp file for building in chromium
+  * Suppress clang header-hygiene warning - https://code.google.com/p/snappy/issues/detail?id=70
+  * Suppress MSVC signed/unsigned warning - https://code.google.com/p/snappy/issues/detail?id=71
+  * Suppress MSVC x64 size_t warnings - https://code.google.com/p/snappy/issues/detail?id=75
+* {mac,linux,win32}/snappy-stubs-public.h autogenerated public headers
diff --git a/third_party/snappy/linux/config.h b/third_party/snappy/linux/config.h
new file mode 100644
index 0000000..12b9327
--- /dev/null
+++ b/third_party/snappy/linux/config.h
@@ -0,0 +1,135 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define to 1 if the compiler supports __builtin_ctz and friends. */
+#define HAVE_BUILTIN_CTZ 1
+
+/* Define to 1 if the compiler supports __builtin_expect. */
+#define HAVE_BUILTIN_EXPECT 1
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#define HAVE_BYTESWAP_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Use the gflags package for command-line parsing. */
+/* #undef HAVE_GFLAGS */
+
+/* Defined when Google Test is available. */
+/* #undef HAVE_GTEST */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `fastlz' library (-lfastlz). */
+/* #undef HAVE_LIBFASTLZ */
+
+/* Define to 1 if you have the `lzf' library (-llzf). */
+/* #undef HAVE_LIBLZF */
+
+/* Define to 1 if you have the `lzo2' library (-llzo2). */
+/* #undef HAVE_LIBLZO2 */
+
+/* Define to 1 if you have the `quicklz' library (-lquicklz). */
+/* #undef HAVE_LIBQUICKLZ */
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/byteswap.h> header file. */
+/* #undef HAVE_SYS_BYTESWAP_H */
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+/* #undef HAVE_SYS_ENDIAN_H */
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "snappy"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "snappy"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "snappy 1.1.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "snappy"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.1.0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "1.1.0"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+#if defined __BIG_ENDIAN__
+#define WORDS_BIGENDIAN 1
+#endif
+#else
+#ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+#endif
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef ssize_t */
diff --git a/third_party/snappy/linux/snappy-stubs-public.h b/third_party/snappy/linux/snappy-stubs-public.h
new file mode 100644
index 0000000..5da65aa
--- /dev/null
+++ b/third_party/snappy/linux/snappy-stubs-public.h
@@ -0,0 +1,98 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: sesse@google.com (Steinar H. Gunderson)
+//
+// 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.
+//
+// Various type stubs for the open-source version of Snappy.
+//
+// This file cannot include config.h, as it is included from snappy.h,
+// which is a public header. Instead, snappy-stubs-public.h is generated by
+// from snappy-stubs-public.h.in at configure time.
+
+#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
+#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
+
+#if 1
+#include <stdint.h>
+#endif
+
+#if 1
+#include <stddef.h>
+#endif
+
+#if 1
+#include <sys/uio.h>
+#endif
+
+#define SNAPPY_MAJOR 1
+#define SNAPPY_MINOR 1
+#define SNAPPY_PATCHLEVEL 0
+#define SNAPPY_VERSION \
+  ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
+
+#include <string>
+
+namespace snappy {
+
+#if 1
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef uint16_t uint16;
+typedef int32_t int32;
+typedef uint32_t uint32;
+typedef int64_t int64;
+typedef uint64_t uint64;
+#else
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+typedef long long int64;
+typedef unsigned long long uint64;
+#endif
+
+typedef std::string string;
+
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+#if 0
+// Windows does not have an iovec type, yet the concept is universally useful.
+// It is simple to define it ourselves, so we put it inside our own namespace.
+struct iovec {
+	void* iov_base;
+	size_t iov_len;
+};
+#endif
+
+}  // namespace snappy
+
+#endif  // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
diff --git a/third_party/snappy/mac/config.h b/third_party/snappy/mac/config.h
new file mode 100644
index 0000000..c518e33
--- /dev/null
+++ b/third_party/snappy/mac/config.h
@@ -0,0 +1,76 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+/* Define to 1 if the compiler supports __builtin_ctz and friends. */
+#define HAVE_BUILTIN_CTZ 1
+/* Define to 1 if the compiler supports __builtin_expect. */
+#define HAVE_BUILTIN_EXPECT 1
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+/* Use the gflags package for command-line parsing. */
+/* #undef HAVE_GFLAGS */
+/* Defined when Google Test is available. */
+/* #undef HAVE_GTEST */
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+/* Define to 1 if you have the `fastlz' library (-lfastlz). */
+/* #undef HAVE_LIBFASTLZ */
+/* Define to 1 if you have the `lzf' library (-llzf). */
+/* #undef HAVE_LIBLZF */
+/* Define to 1 if you have the `lzo2' library (-llzo2). */
+/* #undef HAVE_LIBLZO2 */
+/* Define to 1 if you have the `quicklz' library (-lquicklz). */
+/* #undef HAVE_LIBQUICKLZ */
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ *    */
+#define LT_OBJDIR ".libs/"
+/* Name of package */
+#define PACKAGE "snappy"
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "snappy"
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "snappy 1.0.5"
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "snappy"
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.0.5"
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+/* Version number of package */
+#define VERSION "1.0.5"
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ *    significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+#if defined __BIG_ENDIAN__
+#define WORDS_BIGENDIAN 1
+#endif
+#else
+#ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+#endif
+#endif
diff --git a/third_party/snappy/mac/snappy-stubs-public.h b/third_party/snappy/mac/snappy-stubs-public.h
new file mode 100644
index 0000000..5da65aa
--- /dev/null
+++ b/third_party/snappy/mac/snappy-stubs-public.h
@@ -0,0 +1,98 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: sesse@google.com (Steinar H. Gunderson)
+//
+// 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.
+//
+// Various type stubs for the open-source version of Snappy.
+//
+// This file cannot include config.h, as it is included from snappy.h,
+// which is a public header. Instead, snappy-stubs-public.h is generated by
+// from snappy-stubs-public.h.in at configure time.
+
+#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
+#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
+
+#if 1
+#include <stdint.h>
+#endif
+
+#if 1
+#include <stddef.h>
+#endif
+
+#if 1
+#include <sys/uio.h>
+#endif
+
+#define SNAPPY_MAJOR 1
+#define SNAPPY_MINOR 1
+#define SNAPPY_PATCHLEVEL 0
+#define SNAPPY_VERSION \
+  ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
+
+#include <string>
+
+namespace snappy {
+
+#if 1
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef uint16_t uint16;
+typedef int32_t int32;
+typedef uint32_t uint32;
+typedef int64_t int64;
+typedef uint64_t uint64;
+#else
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+typedef long long int64;
+typedef unsigned long long uint64;
+#endif
+
+typedef std::string string;
+
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+#if 0
+// Windows does not have an iovec type, yet the concept is universally useful.
+// It is simple to define it ourselves, so we put it inside our own namespace.
+struct iovec {
+	void* iov_base;
+	size_t iov_len;
+};
+#endif
+
+}  // namespace snappy
+
+#endif  // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
diff --git a/third_party/snappy/snappy.gyp b/third_party/snappy/snappy.gyp
new file mode 100644
index 0000000..ac0186f
--- /dev/null
+++ b/third_party/snappy/snappy.gyp
@@ -0,0 +1,99 @@
+# Copyright (c) 2012 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.
+
+{
+'variables': {
+    'conditions': [
+      # Define an "os_include" variable that points at the OS-specific generated
+      # headers.  These were generated by running the configure script offline.
+      ['os_posix == 1 and OS != "mac"', {
+        'os_include': 'linux'
+      }],
+      ['OS=="mac"', {'os_include': 'mac'}],
+      ['OS=="win"', {'os_include': 'win32'}],
+    ],
+    'use_system_libxml%': 0,
+  },
+  'targets': [
+    {
+      'target_name': 'snappy',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(os_include)',
+        'src',
+        '../..',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<(os_include)',
+          'src',
+        ],
+      },
+      'variables': {
+        'clang_warning_flags_unset': [
+          # snappy-stubs-internal.h unapologetically has: using namespace std
+          # https://code.google.com/p/snappy/issues/detail?id=70
+          '-Wheader-hygiene',
+        ],
+      },
+      'sources': [
+        'src/snappy-internal.h',
+        'src/snappy-sinksource.cc',
+        'src/snappy-sinksource.h',
+        'src/snappy-stubs-internal.cc',
+        'src/snappy-stubs-internal.h',
+        'src/snappy.cc',
+        'src/snappy.h',
+      ],
+      'conditions': [
+        ['OS=="linux" or OS=="mac"', {
+          'defines': [
+            # TODO(tfarina): Only Mac and Linux has the generated config.h for
+            # now. Generate the config.h for Windows too and enable this there
+            # as well.
+            'HAVE_CONFIG_H=1',
+          ],
+        }],
+        ['OS=="win"', {
+          # Signed/unsigned comparison
+          'msvs_disabled_warnings': [
+            # https://code.google.com/p/snappy/issues/detail?id=71
+            4018,
+            # https://code.google.com/p/snappy/issues/detail?id=75
+            4267,
+          ],
+        }],
+      ],
+    },
+    {
+      'target_name': 'snappy_unittest',
+      'type': 'executable',
+      'sources': [
+        'src/snappy-test.cc',
+        'src/snappy-test.h',
+        'src/snappy_unittest.cc',
+      ],
+      'dependencies': [
+        'snappy',
+        '../../base/base.gyp:base',
+        '../../testing/gtest.gyp:gtest',
+        '../../third_party/zlib/zlib.gyp:zlib',
+      ],
+      'variables': {
+        'clang_warning_flags': [ '-Wno-return-type' ],
+        'clang_warning_flags_unset': [ '-Wheader-hygiene' ],
+      },
+      'conditions': [
+        ['OS=="linux" or OS=="mac"', {
+          'defines': [
+            # TODO(tfarina): Only Mac and Linux has the generated config.h for
+            # now. Generate the config.h for Windows too and enable this there
+            # as well.
+            'HAVE_CONFIG_H=1',
+          ],
+        }],
+      ],
+    },
+  ],
+}
diff --git a/third_party/snappy/snappy.target.mk b/third_party/snappy/snappy.target.mk
new file mode 100644
index 0000000..269af54
--- /dev/null
+++ b/third_party/snappy/snappy.target.mk
@@ -0,0 +1,210 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := snappy
+DEFS_Debug := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_DEFAULT_RENDER_THEME=1' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_NSS=1' \
+	'-DUSE_X11=1' \
+	'-DENABLE_ONE_CLICK_SIGNIN' \
+	'-DGTK_DISABLE_SINGLE_INCLUDES=1' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_WEBRTC=1' \
+	'-DENABLE_CONFIGURATION_POLICY' \
+	'-DENABLE_INPUT_SPEECH' \
+	'-DENABLE_NOTIFICATIONS' \
+	'-DENABLE_GPU=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_TASK_MANAGER=1' \
+	'-DENABLE_EXTENSIONS=1' \
+	'-DENABLE_PLUGIN_INSTALLATION=1' \
+	'-DENABLE_PLUGINS=1' \
+	'-DENABLE_SESSION_SERVICE=1' \
+	'-DENABLE_THEMES=1' \
+	'-DENABLE_BACKGROUND=1' \
+	'-DENABLE_AUTOMATION=1' \
+	'-DENABLE_GOOGLE_NOW=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DENABLE_PRINTING=1' \
+	'-DENABLE_CAPTIVE_PORTAL_DETECTION=1' \
+	'-DENABLE_MANAGED_USERS=1' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+# Flags passed to all source files.
+CFLAGS_Debug := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-pthread \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-Wno-format \
+	-Wno-unused-result \
+	-O0 \
+	-g
+
+# Flags passed to only C files.
+CFLAGS_C_Debug :=
+
+# Flags passed to only C++ files.
+CFLAGS_CC_Debug := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated
+
+INCS_Debug := \
+	-Ithird_party/snappy/linux \
+	-Ithird_party/snappy/src \
+	-I.
+
+DEFS_Release := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_DEFAULT_RENDER_THEME=1' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_NSS=1' \
+	'-DUSE_X11=1' \
+	'-DENABLE_ONE_CLICK_SIGNIN' \
+	'-DGTK_DISABLE_SINGLE_INCLUDES=1' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_WEBRTC=1' \
+	'-DENABLE_CONFIGURATION_POLICY' \
+	'-DENABLE_INPUT_SPEECH' \
+	'-DENABLE_NOTIFICATIONS' \
+	'-DENABLE_GPU=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_TASK_MANAGER=1' \
+	'-DENABLE_EXTENSIONS=1' \
+	'-DENABLE_PLUGIN_INSTALLATION=1' \
+	'-DENABLE_PLUGINS=1' \
+	'-DENABLE_SESSION_SERVICE=1' \
+	'-DENABLE_THEMES=1' \
+	'-DENABLE_BACKGROUND=1' \
+	'-DENABLE_AUTOMATION=1' \
+	'-DENABLE_GOOGLE_NOW=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DENABLE_PRINTING=1' \
+	'-DENABLE_CAPTIVE_PORTAL_DETECTION=1' \
+	'-DENABLE_MANAGED_USERS=1' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to all source files.
+CFLAGS_Release := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-pthread \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-Wno-format \
+	-Wno-unused-result \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C files.
+CFLAGS_C_Release :=
+
+# Flags passed to only C++ files.
+CFLAGS_CC_Release := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated
+
+INCS_Release := \
+	-Ithird_party/snappy/linux \
+	-Ithird_party/snappy/src \
+	-I.
+
+OBJS := \
+	$(obj).target/$(TARGET)/third_party/snappy/src/snappy-sinksource.o \
+	$(obj).target/$(TARGET)/third_party/snappy/src/snappy-stubs-internal.o \
+	$(obj).target/$(TARGET)/third_party/snappy/src/snappy.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-pthread \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-Wl,--threads \
+	-Wl,--thread-count=4 \
+	-B$(builddir)/../../third_party/gold \
+	-Wl,--icf=none
+
+LDFLAGS_Release := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-pthread \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-Wl,--threads \
+	-Wl,--thread-count=4 \
+	-B$(builddir)/../../third_party/gold \
+	-Wl,--icf=none \
+	-Wl,-O1 \
+	-Wl,--as-needed \
+	-Wl,--gc-sections
+
+LIBS := \
+	
+
+$(obj).target/third_party/snappy/libsnappy.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/third_party/snappy/libsnappy.a: LIBS := $(LIBS)
+$(obj).target/third_party/snappy/libsnappy.a: TOOLSET := $(TOOLSET)
+$(obj).target/third_party/snappy/libsnappy.a: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,alink_thin)
+
+all_deps += $(obj).target/third_party/snappy/libsnappy.a
+# Add target alias
+.PHONY: snappy
+snappy: $(obj).target/third_party/snappy/libsnappy.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: snappy
+
diff --git a/third_party/snappy/snappy_unittest.target.mk b/third_party/snappy/snappy_unittest.target.mk
new file mode 100644
index 0000000..680b220
--- /dev/null
+++ b/third_party/snappy/snappy_unittest.target.mk
@@ -0,0 +1,269 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := snappy_unittest
+DEFS_Debug := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_DEFAULT_RENDER_THEME=1' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_NSS=1' \
+	'-DUSE_X11=1' \
+	'-DENABLE_ONE_CLICK_SIGNIN' \
+	'-DGTK_DISABLE_SINGLE_INCLUDES=1' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_WEBRTC=1' \
+	'-DENABLE_CONFIGURATION_POLICY' \
+	'-DENABLE_INPUT_SPEECH' \
+	'-DENABLE_NOTIFICATIONS' \
+	'-DENABLE_GPU=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_TASK_MANAGER=1' \
+	'-DENABLE_EXTENSIONS=1' \
+	'-DENABLE_PLUGIN_INSTALLATION=1' \
+	'-DENABLE_PLUGINS=1' \
+	'-DENABLE_SESSION_SERVICE=1' \
+	'-DENABLE_THEMES=1' \
+	'-DENABLE_BACKGROUND=1' \
+	'-DENABLE_AUTOMATION=1' \
+	'-DENABLE_GOOGLE_NOW=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DENABLE_PRINTING=1' \
+	'-DENABLE_CAPTIVE_PORTAL_DETECTION=1' \
+	'-DENABLE_MANAGED_USERS=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+	'-D_DEBUG'
+
+# Flags passed to all source files.
+CFLAGS_Debug := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-pthread \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-pthread \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/x86_64-linux-gnu/glib-2.0/include \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/gdk-pixbuf-2.0 \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/libpng12 \
+	-pthread \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/x86_64-linux-gnu/glib-2.0/include \
+	-Wno-format \
+	-Wno-unused-result \
+	-O0 \
+	-g
+
+# Flags passed to only C files.
+CFLAGS_C_Debug :=
+
+# Flags passed to only C++ files.
+CFLAGS_CC_Debug := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated
+
+INCS_Debug := \
+	-Ithird_party/snappy/linux \
+	-Ithird_party/snappy/src \
+	-I. \
+	-Itesting/gtest/include \
+	-Ithird_party/zlib
+
+DEFS_Release := \
+	'-D_FILE_OFFSET_BITS=64' \
+	'-DUSE_LINUX_BREAKPAD' \
+	'-DCHROMIUM_BUILD' \
+	'-DUSE_DEFAULT_RENDER_THEME=1' \
+	'-DUSE_LIBJPEG_TURBO=1' \
+	'-DUSE_NSS=1' \
+	'-DUSE_X11=1' \
+	'-DENABLE_ONE_CLICK_SIGNIN' \
+	'-DGTK_DISABLE_SINGLE_INCLUDES=1' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_WEBRTC=1' \
+	'-DENABLE_CONFIGURATION_POLICY' \
+	'-DENABLE_INPUT_SPEECH' \
+	'-DENABLE_NOTIFICATIONS' \
+	'-DENABLE_GPU=1' \
+	'-DENABLE_EGLIMAGE=1' \
+	'-DENABLE_TASK_MANAGER=1' \
+	'-DENABLE_EXTENSIONS=1' \
+	'-DENABLE_PLUGIN_INSTALLATION=1' \
+	'-DENABLE_PLUGINS=1' \
+	'-DENABLE_SESSION_SERVICE=1' \
+	'-DENABLE_THEMES=1' \
+	'-DENABLE_BACKGROUND=1' \
+	'-DENABLE_AUTOMATION=1' \
+	'-DENABLE_GOOGLE_NOW=1' \
+	'-DENABLE_LANGUAGE_DETECTION=1' \
+	'-DENABLE_PRINTING=1' \
+	'-DENABLE_CAPTIVE_PORTAL_DETECTION=1' \
+	'-DENABLE_MANAGED_USERS=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to all source files.
+CFLAGS_Release := \
+	-fstack-protector \
+	--param=ssp-buffer-size=4 \
+	-pthread \
+	-fno-exceptions \
+	-fno-strict-aliasing \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-fvisibility=hidden \
+	-pipe \
+	-fPIC \
+	-pthread \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/x86_64-linux-gnu/glib-2.0/include \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/gdk-pixbuf-2.0 \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/libpng12 \
+	-pthread \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/x86_64-linux-gnu/glib-2.0/include \
+	-Wno-format \
+	-Wno-unused-result \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C files.
+CFLAGS_C_Release :=
+
+# Flags passed to only C++ files.
+CFLAGS_CC_Release := \
+	-fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden \
+	-Wno-deprecated
+
+INCS_Release := \
+	-Ithird_party/snappy/linux \
+	-Ithird_party/snappy/src \
+	-I. \
+	-Itesting/gtest/include \
+	-Ithird_party/zlib
+
+OBJS := \
+	$(obj).target/$(TARGET)/third_party/snappy/src/snappy-test.o \
+	$(obj).target/$(TARGET)/third_party/snappy/src/snappy_unittest.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/third_party/snappy/libsnappy.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/zlib/libchrome_zlib.a $(obj).target/base/libbase_static.a $(obj).target/base/allocator/liballocator_extension_thunks.a $(obj).target/testing/gtest_prod.stamp $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/build/linux/glib.stamp $(obj).target/base/libxdg_mime.a $(obj).target/build/linux/gtk.stamp $(obj).target/build/linux/x11.stamp $(obj).target/third_party/libevent/libevent.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))  $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-pthread \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-Wl,--threads \
+	-Wl,--thread-count=4 \
+	-B$(builddir)/../../third_party/gold \
+	-Wl,--icf=none
+
+LDFLAGS_Release := \
+	-Wl,-z,now \
+	-Wl,-z,relro \
+	-pthread \
+	-Wl,-z,noexecstack \
+	-fPIC \
+	-Wl,--threads \
+	-Wl,--thread-count=4 \
+	-B$(builddir)/../../third_party/gold \
+	-Wl,--icf=none \
+	-Wl,-O1 \
+	-Wl,--as-needed \
+	-Wl,--gc-sections
+
+LIBS := \
+	 \
+	-lrt \
+	-ldl \
+	-lgmodule-2.0 \
+	-lgobject-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lXtst \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lpangocairo-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lX11 \
+	-lXi
+
+$(builddir)/snappy_unittest: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/snappy_unittest: LIBS := $(LIBS)
+$(builddir)/snappy_unittest: LD_INPUTS := $(OBJS) $(obj).target/third_party/snappy/libsnappy.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/zlib/libchrome_zlib.a $(obj).target/base/libbase_static.a $(obj).target/base/allocator/liballocator_extension_thunks.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/base/libxdg_mime.a $(obj).target/third_party/libevent/libevent.a
+$(builddir)/snappy_unittest: TOOLSET := $(TOOLSET)
+$(builddir)/snappy_unittest: $(OBJS) $(obj).target/third_party/snappy/libsnappy.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/zlib/libchrome_zlib.a $(obj).target/base/libbase_static.a $(obj).target/base/allocator/liballocator_extension_thunks.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/base/libxdg_mime.a $(obj).target/third_party/libevent/libevent.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/snappy_unittest
+# Add target alias
+.PHONY: snappy_unittest
+snappy_unittest: $(builddir)/snappy_unittest
+
diff --git a/third_party/snappy/win32/snappy-stubs-public.h b/third_party/snappy/win32/snappy-stubs-public.h
new file mode 100644
index 0000000..2afe73a
--- /dev/null
+++ b/third_party/snappy/win32/snappy-stubs-public.h
@@ -0,0 +1,104 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: sesse@google.com (Steinar H. Gunderson)
+//
+// 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.
+//
+// Various type stubs for the open-source version of Snappy.
+//
+// This file cannot include config.h, as it is included from snappy.h,
+// which is a public header. Instead, snappy-stubs-public.h is generated by
+// from snappy-stubs-public.h.in at configure time.
+
+#ifndef UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
+#define UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
+
+#if 0
+#include <stdint.h>
+#endif
+
+#if 1
+#include <stddef.h>
+#endif
+
+#if 0
+#include <sys/uio.h>
+#endif
+
+#define SNAPPY_MAJOR 1
+#define SNAPPY_MINOR 1
+#define SNAPPY_PATCHLEVEL 0
+#define SNAPPY_VERSION \
+  ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
+
+#include <string>
+
+namespace snappy {
+
+#if 0
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef uint16_t uint16;
+typedef int32_t int32;
+typedef uint32_t uint32;
+typedef int64_t int64;
+typedef uint64_t uint64;
+#else
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+typedef long long int64;
+typedef unsigned long long uint64;
+#endif
+
+typedef std::string string;
+
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);               \
+  void operator=(const TypeName&)
+
+#if 1
+// Windows does not have an iovec type, yet the concept is universally useful.
+// It is simple to define it ourselves, so we put it inside our own namespace.
+struct iovec {
+  void* iov_base;
+  size_t iov_len;
+};
+#endif
+
+// MSVC does not have ssize_t by default; autoconf suggests defining as `int'.
+// Would be in config.h, but Chromium does not use automake/autoconf.
+#include <Windows.h>
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+
+}  // namespace snappy
+
+#endif  // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_