ServiceWorkerWriteToCacheJob: refactor

This CL is a re-land of a previously reviewed and approved CL that was reverted: https://codereview.chromium.org/1356153002/

ServiceWorkerWriteToCacheJob is a subclass of URLRequestJob that is responsible for filling and updating a write-through cache for ServiceWorker data. Specifically, ServiceWorkerWriteToCacheJob creates an underlying URLRequestJob which it uses to do the actual network request for the script, then returns the script body to the creator of the ServiceWorkerWriteToCacheJob while also storing it in the ServiceWorker cache if it has been updated.

In order to avoid spurious cache rewrites, ServiceWorkerWriteToCacheJob defers writing an entry for as long as possible: if there is an existing entry, it compares incoming network data to the cache entry's data, and only starts writing once there is a mismatch. Since the mismatch may occur after much data has already been handled, once this happens ServiceWorkerWriteToCacheJob must transparently copy all the existing data up to that point from the cache, then resume streaming network data into the cache and back to its consumer. This is the "compare-and-copy" process. If there is no existing entry, network data is streamed directly to the cache; this is the "passthrough" process.

This CL removes the code implementing the compare-and-copy and passthrough processes from ServiceWorkerWriteToCacheJob and moves it into a separate class, ServiceWorkerCacheWriter. ServiceWorkerCacheWriter exposes an asynchronous API for overwriting a single cache entry that is now used by ServiceWorkerWriteToCacheJob.

BUG=474859
TBR=jochen,falken

Review URL: https://codereview.chromium.org/1375293004

Cr-Commit-Position: refs/heads/master@{#352593}
diff --git a/content/browser/appcache/appcache_response.h b/content/browser/appcache/appcache_response.h
index 8d6c790..a4c87db 100644
--- a/content/browser/appcache/appcache_response.h
+++ b/content/browser/appcache/appcache_response.h
@@ -209,8 +209,9 @@
   // negative error code or the number of bytes written. The 'callback' is a
   // required parameter. The contents of 'info_buf' are not modified.
   // Should only be called where there is no Write operation in progress.
-  void WriteInfo(HttpResponseInfoIOBuffer* info_buf,
-                 const net::CompletionCallback& callback);
+  // (virtual for testing)
+  virtual void WriteInfo(HttpResponseInfoIOBuffer* info_buf,
+                         const net::CompletionCallback& callback);
 
   // Writes data to storage. Always returns the result of the write
   // asynchronously through the 'callback'. Returns the number of bytes written
@@ -220,8 +221,10 @@
   // the number of bytes written. The 'callback' is a required parameter.
   // The contents of 'buf' are not modified.
   // Should only be called where there is no Write operation in progress.
-  void WriteData(net::IOBuffer* buf, int buf_len,
-                 const net::CompletionCallback& callback);
+  // (virtual for testing)
+  virtual void WriteData(net::IOBuffer* buf,
+                         int buf_len,
+                         const net::CompletionCallback& callback);
 
   // Returns true if there is a write pending.
   bool IsWritePending() { return IsIOPending(); }
diff --git a/content/browser/service_worker/service_worker_cache_writer.cc b/content/browser/service_worker/service_worker_cache_writer.cc
new file mode 100644
index 0000000..e934300
--- /dev/null
+++ b/content/browser/service_worker/service_worker_cache_writer.cc
@@ -0,0 +1,498 @@
+// 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 "content/browser/service_worker/service_worker_cache_writer.h"
+
+#include <algorithm>
+#include <string>
+
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+
+namespace {
+
+const size_t kCopyBufferSize = 16 * 1024;
+
+// Shim class used to turn always-async functions into async-or-result
+// functions. See the comments below near ReadInfoHelper.
+class AsyncOnlyCompletionCallbackAdaptor
+    : public base::RefCounted<AsyncOnlyCompletionCallbackAdaptor> {
+ public:
+  explicit AsyncOnlyCompletionCallbackAdaptor(
+      const net::CompletionCallback& callback)
+      : async_(false), result_(net::ERR_IO_PENDING), callback_(callback) {}
+
+  void set_async(bool async) { async_ = async; }
+  bool async() { return async_; }
+  int result() { return result_; }
+
+  void WrappedCallback(int result) {
+    result_ = result;
+    if (async_)
+      callback_.Run(result);
+  }
+
+ private:
+  friend class base::RefCounted<AsyncOnlyCompletionCallbackAdaptor>;
+  virtual ~AsyncOnlyCompletionCallbackAdaptor() {}
+
+  bool async_;
+  int result_;
+  net::CompletionCallback callback_;
+};
+
+}  // namespace
+
+namespace content {
+
+int ServiceWorkerCacheWriter::DoLoop(int status) {
+  do {
+    switch (state_) {
+      case STATE_START:
+        status = DoStart(status);
+        break;
+      case STATE_READ_HEADERS_FOR_COMPARE:
+        status = DoReadHeadersForCompare(status);
+        break;
+      case STATE_READ_HEADERS_FOR_COMPARE_DONE:
+        status = DoReadHeadersForCompareDone(status);
+        break;
+      case STATE_READ_DATA_FOR_COMPARE:
+        status = DoReadDataForCompare(status);
+        break;
+      case STATE_READ_DATA_FOR_COMPARE_DONE:
+        status = DoReadDataForCompareDone(status);
+        break;
+      case STATE_READ_HEADERS_FOR_COPY:
+        status = DoReadHeadersForCopy(status);
+        break;
+      case STATE_READ_HEADERS_FOR_COPY_DONE:
+        status = DoReadHeadersForCopyDone(status);
+        break;
+      case STATE_READ_DATA_FOR_COPY:
+        status = DoReadDataForCopy(status);
+        break;
+      case STATE_READ_DATA_FOR_COPY_DONE:
+        status = DoReadDataForCopyDone(status);
+        break;
+      case STATE_WRITE_HEADERS_FOR_PASSTHROUGH:
+        status = DoWriteHeadersForPassthrough(status);
+        break;
+      case STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE:
+        status = DoWriteHeadersForPassthroughDone(status);
+        break;
+      case STATE_WRITE_DATA_FOR_PASSTHROUGH:
+        status = DoWriteDataForPassthrough(status);
+        break;
+      case STATE_WRITE_DATA_FOR_PASSTHROUGH_DONE:
+        status = DoWriteDataForPassthroughDone(status);
+        break;
+      case STATE_WRITE_HEADERS_FOR_COPY:
+        status = DoWriteHeadersForCopy(status);
+        break;
+      case STATE_WRITE_HEADERS_FOR_COPY_DONE:
+        status = DoWriteHeadersForCopyDone(status);
+        break;
+      case STATE_WRITE_DATA_FOR_COPY:
+        status = DoWriteDataForCopy(status);
+        break;
+      case STATE_WRITE_DATA_FOR_COPY_DONE:
+        status = DoWriteDataForCopyDone(status);
+        break;
+      case STATE_DONE:
+        status = DoDone(status);
+        break;
+      default:
+        NOTREACHED() << "Unknown state in DoLoop";
+        state_ = STATE_DONE;
+        break;
+    }
+  } while (status >= net::OK && state_ != STATE_DONE);
+  io_pending_ = (status == net::ERR_IO_PENDING);
+  return status;
+}
+
+ServiceWorkerCacheWriter::ServiceWorkerCacheWriter(
+    const ResponseReaderCreator& reader_creator,
+    const ResponseWriterCreator& writer_creator)
+    : state_(STATE_START),
+      io_pending_(false),
+      comparing_(false),
+      did_replace_(false),
+      reader_creator_(reader_creator),
+      writer_creator_(writer_creator),
+      weak_factory_(this) {}
+
+ServiceWorkerCacheWriter::~ServiceWorkerCacheWriter() {}
+
+net::Error ServiceWorkerCacheWriter::MaybeWriteHeaders(
+    HttpResponseInfoIOBuffer* headers,
+    const OnWriteCompleteCallback& callback) {
+  DCHECK(!io_pending_);
+
+  headers_to_write_ = headers;
+  pending_callback_ = callback;
+  DCHECK_EQ(state_, STATE_START);
+  int result = DoLoop(net::OK);
+
+  // Synchronous errors and successes always go to STATE_DONE.
+  if (result != net::ERR_IO_PENDING)
+    DCHECK_EQ(state_, STATE_DONE);
+
+  // ERR_IO_PENDING has to have one of the STATE_*_DONE states as the next state
+  // (not STATE_DONE itself).
+  if (result == net::ERR_IO_PENDING) {
+    DCHECK(state_ == STATE_READ_HEADERS_FOR_COMPARE_DONE ||
+           state_ == STATE_WRITE_HEADERS_FOR_COPY_DONE ||
+           state_ == STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE)
+        << "Unexpected state: " << state_;
+    io_pending_ = true;
+  }
+
+  return result >= 0 ? net::OK : static_cast<net::Error>(result);
+}
+
+net::Error ServiceWorkerCacheWriter::MaybeWriteData(
+    net::IOBuffer* buf,
+    size_t buf_size,
+    const OnWriteCompleteCallback& callback) {
+  DCHECK(!io_pending_);
+
+  data_to_write_ = buf;
+  len_to_write_ = buf_size;
+  pending_callback_ = callback;
+
+  if (comparing_)
+    state_ = STATE_READ_DATA_FOR_COMPARE;
+  else
+    state_ = STATE_WRITE_DATA_FOR_PASSTHROUGH;
+
+  int result = DoLoop(net::OK);
+
+  // Synchronous completions are always STATE_DONE.
+  if (result != net::ERR_IO_PENDING)
+    DCHECK_EQ(state_, STATE_DONE);
+
+  // Asynchronous completion means the state machine must be waiting in one of
+  // the Done states for an IO operation to complete:
+  if (result == net::ERR_IO_PENDING) {
+    // Note that STATE_READ_HEADERS_FOR_COMPARE_DONE is excluded because the
+    // headers are compared in MaybeWriteHeaders, not here, and
+    // STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE is excluded because that write
+    // is done by MaybeWriteHeaders.
+    DCHECK(state_ == STATE_READ_DATA_FOR_COMPARE_DONE ||
+           state_ == STATE_READ_HEADERS_FOR_COPY_DONE ||
+           state_ == STATE_READ_DATA_FOR_COPY_DONE ||
+           state_ == STATE_WRITE_HEADERS_FOR_COPY_DONE ||
+           state_ == STATE_WRITE_DATA_FOR_COPY_DONE ||
+           state_ == STATE_WRITE_DATA_FOR_PASSTHROUGH_DONE)
+        << "Unexpected state: " << state_;
+  }
+
+  return result >= 0 ? net::OK : static_cast<net::Error>(result);
+}
+
+int ServiceWorkerCacheWriter::DoStart(int result) {
+  bytes_written_ = 0;
+  compare_reader_ = reader_creator_.Run();
+  if (compare_reader_.get()) {
+    state_ = STATE_READ_HEADERS_FOR_COMPARE;
+    comparing_ = true;
+  } else {
+    // No existing reader, just write the headers back directly.
+    state_ = STATE_WRITE_HEADERS_FOR_PASSTHROUGH;
+    comparing_ = false;
+  }
+  return net::OK;
+}
+
+int ServiceWorkerCacheWriter::DoReadHeadersForCompare(int result) {
+  DCHECK(headers_to_write_);
+
+  headers_to_read_ = new HttpResponseInfoIOBuffer;
+  state_ = STATE_READ_HEADERS_FOR_COMPARE_DONE;
+  return ReadInfoHelper(compare_reader_, headers_to_read_.get());
+}
+
+int ServiceWorkerCacheWriter::DoReadHeadersForCompareDone(int result) {
+  if (result < 0) {
+    state_ = STATE_DONE;
+    return result;
+  }
+  cached_length_ = headers_to_read_->response_data_size;
+  bytes_compared_ = 0;
+  state_ = STATE_DONE;
+  return net::OK;
+}
+
+int ServiceWorkerCacheWriter::DoReadDataForCompare(int result) {
+  DCHECK(data_to_write_);
+
+  data_to_read_ = new net::IOBuffer(len_to_write_);
+  len_to_read_ = len_to_write_;
+  state_ = STATE_READ_DATA_FOR_COMPARE_DONE;
+  compare_offset_ = 0;
+  // If this was an EOF, don't issue a read.
+  if (len_to_write_ > 0)
+    result = ReadDataHelper(compare_reader_, data_to_read_.get(), len_to_read_);
+  return result;
+}
+
+int ServiceWorkerCacheWriter::DoReadDataForCompareDone(int result) {
+  DCHECK(data_to_read_);
+  DCHECK(data_to_write_);
+  DCHECK_EQ(len_to_read_, len_to_write_);
+  DCHECK_LE(result + compare_offset_, static_cast<size_t>(len_to_write_));
+
+  if (result < 0) {
+    state_ = STATE_DONE;
+    return result;
+  }
+
+  // Premature EOF while reading the service worker script cache data to
+  // compare. Fail the comparison.
+  if (result == 0 && len_to_write_ != 0) {
+    comparing_ = false;
+    state_ = STATE_READ_HEADERS_FOR_COPY;
+    return net::OK;
+  }
+
+  // Compare the data from the ServiceWorker script cache to the data from the
+  // network.
+  if (memcmp(data_to_read_->data(), data_to_write_->data() + compare_offset_,
+             result)) {
+    // Data mismatched. This method already validated that all the bytes through
+    // |bytes_compared_| were identical, so copy the first |bytes_compared_|
+    // over, then start writing network data back after the changed point.
+    comparing_ = false;
+    state_ = STATE_READ_HEADERS_FOR_COPY;
+    return net::OK;
+  }
+
+  compare_offset_ += result;
+
+  // This is a little bit tricky. It is possible that not enough data was read
+  // to finish comparing the entire block of data from the network (which is
+  // kept in len_to_write_), so this method may need to issue another read and
+  // return to this state.
+  //
+  // Compare isn't complete yet. Issue another read for the remaining data. Note
+  // that this reuses the same IOBuffer.
+  if (compare_offset_ < static_cast<size_t>(len_to_read_)) {
+    state_ = STATE_READ_DATA_FOR_COMPARE_DONE;
+    return ReadDataHelper(compare_reader_, data_to_read_.get(),
+                          len_to_read_ - compare_offset_);
+  }
+
+  // Cached entry is longer than the network entry but the prefix matches. Copy
+  // just the prefix.
+  if (len_to_read_ == 0 && bytes_compared_ + compare_offset_ < cached_length_) {
+    comparing_ = false;
+    state_ = STATE_READ_HEADERS_FOR_COPY;
+    return net::OK;
+  }
+
+  // bytes_compared_ only gets incremented when a full block is compared, to
+  // avoid having to use only parts of the buffered network data.
+  bytes_compared_ += result;
+  state_ = STATE_DONE;
+  return net::OK;
+}
+
+int ServiceWorkerCacheWriter::DoReadHeadersForCopy(int result) {
+  bytes_copied_ = 0;
+  copy_reader_ = reader_creator_.Run();
+  headers_to_read_ = new HttpResponseInfoIOBuffer;
+  data_to_copy_ = new net::IOBuffer(kCopyBufferSize);
+  state_ = STATE_READ_HEADERS_FOR_COPY_DONE;
+  return ReadInfoHelper(copy_reader_, headers_to_read_.get());
+}
+
+int ServiceWorkerCacheWriter::DoReadHeadersForCopyDone(int result) {
+  if (result < 0) {
+    state_ = STATE_DONE;
+    return result;
+  }
+  state_ = STATE_WRITE_HEADERS_FOR_COPY;
+  return net::OK;
+}
+
+// Write the just-read headers back to the cache.
+// Note that this method must create |writer_|, since the only paths to this
+// state never create a writer.
+// Also note that this *discards* the read headers and replaces them with the
+// net headers.
+int ServiceWorkerCacheWriter::DoWriteHeadersForCopy(int result) {
+  DCHECK(!writer_);
+  writer_ = writer_creator_.Run();
+  state_ = STATE_WRITE_HEADERS_FOR_COPY_DONE;
+  return WriteInfoHelper(writer_, headers_to_write_.get());
+}
+
+int ServiceWorkerCacheWriter::DoWriteHeadersForCopyDone(int result) {
+  if (result < 0) {
+    state_ = STATE_DONE;
+    return result;
+  }
+  state_ = STATE_READ_DATA_FOR_COPY;
+  return net::OK;
+}
+
+int ServiceWorkerCacheWriter::DoReadDataForCopy(int result) {
+  size_t to_read = std::min(kCopyBufferSize, bytes_compared_ - bytes_copied_);
+  // At this point, all compared bytes have been read. Currently
+  // |data_to_write_| and |len_to_write_| hold the chunk of network input that
+  // caused the comparison failure, so those need to be written back and this
+  // object needs to go into passthrough mode.
+  if (to_read == 0) {
+    state_ = STATE_WRITE_DATA_FOR_PASSTHROUGH;
+    return net::OK;
+  }
+  state_ = STATE_READ_DATA_FOR_COPY_DONE;
+  return ReadDataHelper(copy_reader_, data_to_copy_.get(), to_read);
+}
+
+int ServiceWorkerCacheWriter::DoReadDataForCopyDone(int result) {
+  if (result < 0) {
+    state_ = STATE_DONE;
+    return result;
+  }
+  state_ = STATE_WRITE_DATA_FOR_COPY;
+  return result;
+}
+
+int ServiceWorkerCacheWriter::DoWriteDataForCopy(int result) {
+  state_ = STATE_WRITE_DATA_FOR_COPY_DONE;
+  DCHECK_GT(result, 0);
+  return WriteDataHelper(writer_, data_to_copy_.get(), result);
+}
+
+int ServiceWorkerCacheWriter::DoWriteDataForCopyDone(int result) {
+  if (result < 0) {
+    state_ = STATE_DONE;
+    return result;
+  }
+  bytes_written_ += result;
+  bytes_copied_ += result;
+  state_ = STATE_READ_DATA_FOR_COPY;
+  return result;
+}
+
+int ServiceWorkerCacheWriter::DoWriteHeadersForPassthrough(int result) {
+  writer_ = writer_creator_.Run();
+  state_ = STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE;
+  return WriteInfoHelper(writer_, headers_to_write_.get());
+}
+
+int ServiceWorkerCacheWriter::DoWriteHeadersForPassthroughDone(int result) {
+  state_ = STATE_DONE;
+  return net::OK;
+}
+
+int ServiceWorkerCacheWriter::DoWriteDataForPassthrough(int result) {
+  state_ = STATE_WRITE_DATA_FOR_PASSTHROUGH_DONE;
+  if (len_to_write_ > 0)
+    result = WriteDataHelper(writer_, data_to_write_.get(), len_to_write_);
+  return result;
+}
+
+int ServiceWorkerCacheWriter::DoWriteDataForPassthroughDone(int result) {
+  if (result < 0) {
+    state_ = STATE_DONE;
+    return result;
+  }
+  bytes_written_ += result;
+  state_ = STATE_DONE;
+  return net::OK;
+}
+
+int ServiceWorkerCacheWriter::DoDone(int result) {
+  state_ = STATE_DONE;
+  return net::OK;
+}
+
+// These helpers adapt the AppCache "always use the callback" pattern to the
+// //net "only use the callback for async" pattern using
+// AsyncCompletionCallbackAdaptor.
+//
+// Specifically, these methods return result codes directly for synchronous
+// completions, and only run their callback (which is AsyncDoLoop) for
+// asynchronous completions.
+
+int ServiceWorkerCacheWriter::ReadInfoHelper(
+    const scoped_ptr<ServiceWorkerResponseReader>& reader,
+    HttpResponseInfoIOBuffer* buf) {
+  net::CompletionCallback run_callback = base::Bind(
+      &ServiceWorkerCacheWriter::AsyncDoLoop, weak_factory_.GetWeakPtr());
+  scoped_refptr<AsyncOnlyCompletionCallbackAdaptor> adaptor(
+      new AsyncOnlyCompletionCallbackAdaptor(run_callback));
+  reader->ReadInfo(
+      buf, base::Bind(&AsyncOnlyCompletionCallbackAdaptor::WrappedCallback,
+                      adaptor));
+  adaptor->set_async(true);
+  return adaptor->result();
+}
+
+int ServiceWorkerCacheWriter::ReadDataHelper(
+    const scoped_ptr<ServiceWorkerResponseReader>& reader,
+    net::IOBuffer* buf,
+    int buf_len) {
+  net::CompletionCallback run_callback = base::Bind(
+      &ServiceWorkerCacheWriter::AsyncDoLoop, weak_factory_.GetWeakPtr());
+  scoped_refptr<AsyncOnlyCompletionCallbackAdaptor> adaptor(
+      new AsyncOnlyCompletionCallbackAdaptor(run_callback));
+  reader->ReadData(
+      buf, buf_len,
+      base::Bind(&AsyncOnlyCompletionCallbackAdaptor::WrappedCallback,
+                 adaptor));
+  adaptor->set_async(true);
+  return adaptor->result();
+}
+
+int ServiceWorkerCacheWriter::WriteInfoHelper(
+    const scoped_ptr<ServiceWorkerResponseWriter>& writer,
+    HttpResponseInfoIOBuffer* buf) {
+  did_replace_ = true;
+  net::CompletionCallback run_callback = base::Bind(
+      &ServiceWorkerCacheWriter::AsyncDoLoop, weak_factory_.GetWeakPtr());
+  scoped_refptr<AsyncOnlyCompletionCallbackAdaptor> adaptor(
+      new AsyncOnlyCompletionCallbackAdaptor(run_callback));
+  writer->WriteInfo(
+      buf, base::Bind(&AsyncOnlyCompletionCallbackAdaptor::WrappedCallback,
+                      adaptor));
+  adaptor->set_async(true);
+  return adaptor->result();
+}
+
+int ServiceWorkerCacheWriter::WriteDataHelper(
+    const scoped_ptr<ServiceWorkerResponseWriter>& writer,
+    net::IOBuffer* buf,
+    int buf_len) {
+  net::CompletionCallback run_callback = base::Bind(
+      &ServiceWorkerCacheWriter::AsyncDoLoop, weak_factory_.GetWeakPtr());
+  scoped_refptr<AsyncOnlyCompletionCallbackAdaptor> adaptor(
+      new AsyncOnlyCompletionCallbackAdaptor(run_callback));
+  writer->WriteData(
+      buf, buf_len,
+      base::Bind(&AsyncOnlyCompletionCallbackAdaptor::WrappedCallback,
+                 adaptor));
+  adaptor->set_async(true);
+  return adaptor->result();
+}
+
+void ServiceWorkerCacheWriter::AsyncDoLoop(int result) {
+  result = DoLoop(result);
+  // If the result is ERR_IO_PENDING, the pending callback will be run by a
+  // later invocation of AsyncDoLoop.
+  if (result != net::ERR_IO_PENDING) {
+    OnWriteCompleteCallback callback = pending_callback_;
+    pending_callback_.Reset();
+    net::Error error = result >= 0 ? net::OK : static_cast<net::Error>(result);
+    io_pending_ = false;
+    callback.Run(error);
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/service_worker/service_worker_cache_writer.h b/content/browser/service_worker/service_worker_cache_writer.h
new file mode 100644
index 0000000..dde71a6
--- /dev/null
+++ b/content/browser/service_worker/service_worker_cache_writer.h
@@ -0,0 +1,230 @@
+// 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 CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_WRITER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_WRITER_H_
+
+#include <map>
+#include <set>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace content {
+
+struct HttpResponseInfoIOBuffer;
+class ServiceWorkerCacheWriterCore;
+class ServiceWorkerResponseReader;
+class ServiceWorkerResponseWriter;
+class ServiceWorkerStorage;
+
+// This class is responsible for possibly updating the ServiceWorker script
+// cache for an installed ServiceWorker main script. If there is no existing
+// cache entry, this class always writes supplied data back to the cache; if
+// there is an existing cache entry, this class only writes supplied data back
+// if there is a cache mismatch.
+//
+// Note that writes done by this class cannot be "short" - ie, if they succeed,
+// they always write all the supplied data back. Therefore completions are
+// signalled with net::Error without a count of bytes written.
+//
+// This class's behavior is modelled as a state machine; see the DoLoop function
+// for comments about this.
+class CONTENT_EXPORT ServiceWorkerCacheWriter {
+ public:
+  using OnWriteCompleteCallback = base::Callback<void(net::Error)>;
+
+  // The types for the factory functions passed into the constructor. These are
+  // responsible for creating readers from the existing cache entry and writers
+  // to the new cache entry when called. These are passed in as factories
+  // instead of passing readers and writers in directly to avoid creating
+  // writers to entries that won't be updated, and because this class may need
+  // multiple readers internally.
+  using ResponseReaderCreator =
+      base::Callback<scoped_ptr<ServiceWorkerResponseReader>(void)>;
+  using ResponseWriterCreator =
+      base::Callback<scoped_ptr<ServiceWorkerResponseWriter>(void)>;
+
+  // The existing reader may be null, in which case this instance will
+  // unconditionally write back data supplied to |MaybeWriteHeaders| and
+  // |MaybeWriteData|.
+  ServiceWorkerCacheWriter(const ResponseReaderCreator& reader_creator,
+                           const ResponseWriterCreator& writer_creator);
+
+  ~ServiceWorkerCacheWriter();
+
+  // Writes the supplied |headers| back to the cache. Returns ERR_IO_PENDING if
+  // the write will complete asynchronously, in which case |callback| will be
+  // called when it completes. Otherwise, returns a code other than
+  // ERR_IO_PENDING and does not invoke |callback|. Note that this method will
+  // not necessarily write data back to the cache if the incoming data is
+  // equivalent to the existing cached data. See the source of this function for
+  // details about how this function drives the state machine.
+  net::Error MaybeWriteHeaders(HttpResponseInfoIOBuffer* headers,
+                               const OnWriteCompleteCallback& callback);
+
+  // Writes the supplied body data |data| back to the cache. Returns
+  // ERR_IO_PENDING if the write will complete asynchronously, in which case
+  // |callback| will be called when it completes. Otherwise, returns a code
+  // other than ERR_IO_PENDING and does not invoke |callback|. Note that this
+  // method will not necessarily write data back to the cache if the incoming
+  // data is equivalent to the existing cached data. See the source of this
+  // function for details about how this function drives the state machine.
+  net::Error MaybeWriteData(net::IOBuffer* buf,
+                            size_t buf_size,
+                            const OnWriteCompleteCallback& callback);
+
+  // Returns a count of bytes written back to the cache.
+  size_t bytes_written() const { return bytes_written_; }
+  bool did_replace() const { return did_replace_; }
+
+ private:
+  // States for the state machine.
+  //
+  // The state machine flows roughly like this: if there is no existing cache
+  // entry, incoming headers and data are written directly back to the cache
+  // ("passthrough mode", the PASSTHROUGH states). If there is an existing cache
+  // entry, incoming headers and data are compared to the existing cache entry
+  // ("compare mode", the COMPARE states); if at any point the incoming
+  // headers/data are not equal to the cached headers/data, this class copies
+  // the cached data up to the point where the incoming data and the cached data
+  // diverged ("copy mode", the COPY states), then switches to "passthrough
+  // mode" to write the remainder of the incoming data. The overall effect is to
+  // avoid rewriting the cache entry if the incoming data is identical to the
+  // cached data.
+  //
+  // Note that after a call to MaybeWriteHeaders or MaybeWriteData completes,
+  // the machine is always in STATE_DONE, indicating that the call is finished;
+  // those methods are responsible for setting a new initial state.
+  enum State {
+    STATE_START,
+    // Control flows linearly through these four states, then loops from
+    // READ_DATA_FOR_COMPARE_DONE to READ_DATA_FOR_COMPARE, or exits to
+    // READ_HEADERS_FOR_COPY.
+    STATE_READ_HEADERS_FOR_COMPARE,
+    STATE_READ_HEADERS_FOR_COMPARE_DONE,
+    STATE_READ_DATA_FOR_COMPARE,
+    STATE_READ_DATA_FOR_COMPARE_DONE,
+
+    // Control flows linearly through these states, with each pass from
+    // READ_DATA_FOR_COPY to WRITE_DATA_FOR_COPY_DONE copying one block of data
+    // at a time. Control loops from WRITE_DATA_FOR_COPY_DONE back to
+    // READ_DATA_FOR_COPY if there is more data to copy, or exits to
+    // WRITE_DATA_FOR_PASSTHROUGH.
+    STATE_READ_HEADERS_FOR_COPY,
+    STATE_READ_HEADERS_FOR_COPY_DONE,
+    STATE_WRITE_HEADERS_FOR_COPY,
+    STATE_WRITE_HEADERS_FOR_COPY_DONE,
+    STATE_READ_DATA_FOR_COPY,
+    STATE_READ_DATA_FOR_COPY_DONE,
+    STATE_WRITE_DATA_FOR_COPY,
+    STATE_WRITE_DATA_FOR_COPY_DONE,
+
+    // Control flows linearly through these states, with a loop between
+    // WRITE_DATA_FOR_PASSTHROUGH and WRITE_DATA_FOR_PASSTHROUGH_DONE.
+    STATE_WRITE_HEADERS_FOR_PASSTHROUGH,
+    STATE_WRITE_HEADERS_FOR_PASSTHROUGH_DONE,
+    STATE_WRITE_DATA_FOR_PASSTHROUGH,
+    STATE_WRITE_DATA_FOR_PASSTHROUGH_DONE,
+
+    // This state means "done with the current call; ready for another one."
+    STATE_DONE,
+  };
+
+  // Drives this class's state machine. This function steps the state machine
+  // until one of:
+  //   a) One of the state functions returns an error
+  //   b) The state machine reaches STATE_DONE
+  // A successful value (net::OK or greater) indicates that the requested
+  // operation completed synchronously. A return value of ERR_IO_PENDING
+  // indicates that some step had to submit asynchronous IO for later
+  // completion, and the state machine will resume running (via AsyncDoLoop)
+  // when that asynchronous IO completes. Any other return value indicates that
+  // the requested operation failed synchronously.
+  int DoLoop(int result);
+
+  // State handlers. See function comments in the corresponding source file for
+  // details on these.
+  int DoStart(int result);
+  int DoReadHeadersForCompare(int result);
+  int DoReadHeadersForCompareDone(int result);
+  int DoReadDataForCompare(int result);
+  int DoReadDataForCompareDone(int result);
+  int DoReadHeadersForCopy(int result);
+  int DoReadHeadersForCopyDone(int result);
+  int DoWriteHeadersForCopy(int result);
+  int DoWriteHeadersForCopyDone(int result);
+  int DoReadDataForCopy(int result);
+  int DoReadDataForCopyDone(int result);
+  int DoWriteDataForCopy(int result);
+  int DoWriteDataForCopyDone(int result);
+  int DoWriteHeadersForPassthrough(int result);
+  int DoWriteHeadersForPassthroughDone(int result);
+  int DoWriteDataForPassthrough(int result);
+  int DoWriteDataForPassthroughDone(int result);
+  int DoDone(int result);
+
+  // Wrappers for asynchronous calls. These are responsible for scheduling a
+  // callback to drive the state machine if needed. These either:
+  //   a) Return ERR_IO_PENDING, and schedule a callback to run the state
+  //      machine's Run() later, or
+  //   b) Return some other value and do not schedule a callback.
+  int ReadInfoHelper(const scoped_ptr<ServiceWorkerResponseReader>& reader,
+                     HttpResponseInfoIOBuffer* buf);
+  int ReadDataHelper(const scoped_ptr<ServiceWorkerResponseReader>& reader,
+                     net::IOBuffer* buf,
+                     int buf_len);
+  int WriteInfoHelper(const scoped_ptr<ServiceWorkerResponseWriter>& writer,
+                      HttpResponseInfoIOBuffer* buf);
+  int WriteDataHelper(const scoped_ptr<ServiceWorkerResponseWriter>& writer,
+                      net::IOBuffer* buf,
+                      int buf_len);
+
+  // Callback used by the above helpers for their IO operations. This is only
+  // run when those IO operations complete asynchronously, in which case it
+  // invokes the synchronous DoLoop function and runs the client callback (the
+  // one passed into MaybeWriteData/MaybeWriteHeaders) if that invocation
+  // of DoLoop completes synchronously.
+  void AsyncDoLoop(int result);
+
+  State state_;
+  // Note that this variable is only used for assertions; it reflects "state !=
+  // DONE && not in synchronous DoLoop".
+  bool io_pending_;
+  bool comparing_;
+
+  scoped_refptr<HttpResponseInfoIOBuffer> headers_to_read_;
+  scoped_refptr<HttpResponseInfoIOBuffer> headers_to_write_;
+  scoped_refptr<net::IOBuffer> data_to_read_;
+  int len_to_read_;
+  scoped_refptr<net::IOBuffer> data_to_copy_;
+  scoped_refptr<net::IOBuffer> data_to_write_;
+  int len_to_write_;
+  OnWriteCompleteCallback pending_callback_;
+
+  size_t cached_length_;
+
+  size_t bytes_compared_;
+  size_t bytes_copied_;
+  size_t bytes_written_;
+
+  bool did_replace_;
+
+  size_t compare_offset_;
+
+  ResponseReaderCreator reader_creator_;
+  ResponseWriterCreator writer_creator_;
+  scoped_ptr<ServiceWorkerResponseReader> compare_reader_;
+  scoped_ptr<ServiceWorkerResponseReader> copy_reader_;
+  scoped_ptr<ServiceWorkerResponseWriter> writer_;
+  base::WeakPtrFactory<ServiceWorkerCacheWriter> weak_factory_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_WRITER_H_
diff --git a/content/browser/service_worker/service_worker_cache_writer_unittest.cc b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
new file mode 100644
index 0000000..c58b57b5
--- /dev/null
+++ b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
@@ -0,0 +1,697 @@
+// 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 "content/browser/service_worker/service_worker_cache_writer.h"
+
+#include <list>
+#include <queue>
+#include <string>
+
+#include "base/stl_util.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+// A test implementation of ServiceWorkerResponseReader.
+//
+// This class exposes the ability to expect reads (see ExpectRead*() below).
+// Each call to ReadInfo() or ReadData() consumes another expected read, in the
+// order those reads were expected, so:
+//    reader->ExpectReadInfoOk(5, false);
+//    reader->ExpectReadDataOk("abcdef", false);
+//    reader->ExpectReadDataOk("ghijkl", false);
+// Expects these calls, in this order:
+//    reader->ReadInfo(...);  // reader writes 5 into
+//                            // |info_buf->response_data_size|
+//    reader->ReadData(...);  // reader writes "abcdef" into |buf|
+//    reader->ReadData(...);  // reader writes "ghijkl" into |buf|
+// If an unexpected call happens, this class DCHECKs.
+// If an expected read is marked "async", it will not complete immediately, but
+// must be completed by the test using CompletePendingRead().
+// These is a convenience method AllExpectedReadsDone() which returns whether
+// there are any expected reads that have not yet happened.
+class MockServiceWorkerResponseReader : public ServiceWorkerResponseReader {
+ public:
+  MockServiceWorkerResponseReader() : ServiceWorkerResponseReader(0, nullptr) {}
+  ~MockServiceWorkerResponseReader() override {}
+
+  // ServiceWorkerResponseReader overrides
+  void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
+                const net::CompletionCallback& callback) override;
+  void ReadData(net::IOBuffer* buf,
+                int buf_len,
+                const net::CompletionCallback& callback) override;
+
+  // Test helpers. ExpectReadInfo() and ExpectReadData() give precise control
+  // over both the data to be written and the result to return.
+  // ExpectReadInfoOk() and ExpectReadDataOk() are convenience functions for
+  // expecting successful reads, which always have their length as their result.
+
+  // Expect a call to ReadInfo() on this reader. For these functions, |len| will
+  // be used as |response_data_size|, not as the length of this particular read.
+  void ExpectReadInfo(size_t len, bool async, int result);
+  void ExpectReadInfoOk(size_t len, bool async);
+
+  // Expect a call to ReadData() on this reader. For these functions, |len| is
+  // the length of the data to be written back; in ExpectReadDataOk(), |len| is
+  // implicitly the length of |data|.
+  void ExpectReadData(const char* data, size_t len, bool async, int result);
+  void ExpectReadDataOk(const std::string& data, bool async);
+
+  // Complete a pending async read. It is an error to call this function without
+  // a pending async read (ie, a previous call to ReadInfo() or ReadData()
+  // having not run its callback yet).
+  void CompletePendingRead();
+
+  // Returns whether all expected reads have occurred.
+  bool AllExpectedReadsDone() { return expected_reads_.size() == 0; }
+
+ private:
+  struct ExpectedRead {
+    ExpectedRead(size_t len, bool async, int result)
+        : data(nullptr), len(len), info(true), async(async), result(result) {}
+    ExpectedRead(const char* data, size_t len, bool async, int result)
+        : data(data), len(len), info(false), async(async), result(result) {}
+    const char* data;
+    size_t len;
+    bool info;
+    bool async;
+    int result;
+  };
+
+  std::queue<ExpectedRead> expected_reads_;
+  scoped_refptr<net::IOBuffer> pending_buffer_;
+  size_t pending_buffer_len_;
+  scoped_refptr<HttpResponseInfoIOBuffer> pending_info_;
+  net::CompletionCallback pending_callback_;
+};
+
+void MockServiceWorkerResponseReader::ReadInfo(
+    HttpResponseInfoIOBuffer* info_buf,
+    const net::CompletionCallback& callback) {
+  DCHECK(!expected_reads_.empty());
+  ExpectedRead expected = expected_reads_.front();
+  EXPECT_TRUE(expected.info);
+  if (expected.async) {
+    pending_info_ = info_buf;
+    pending_callback_ = callback;
+  } else {
+    expected_reads_.pop();
+    info_buf->response_data_size = expected.len;
+    callback.Run(expected.result);
+  }
+}
+
+void MockServiceWorkerResponseReader::ReadData(
+    net::IOBuffer* buf,
+    int buf_len,
+    const net::CompletionCallback& callback) {
+  DCHECK(!expected_reads_.empty());
+  ExpectedRead expected = expected_reads_.front();
+  EXPECT_FALSE(expected.info);
+  if (expected.async) {
+    pending_callback_ = callback;
+    pending_buffer_ = buf;
+    pending_buffer_len_ = static_cast<size_t>(buf_len);
+  } else {
+    expected_reads_.pop();
+    if (expected.len > 0) {
+      size_t to_read = std::min(static_cast<size_t>(buf_len), expected.len);
+      memcpy(buf->data(), expected.data, to_read);
+    }
+    callback.Run(expected.result);
+  }
+}
+
+void MockServiceWorkerResponseReader::ExpectReadInfo(size_t len,
+                                                     bool async,
+                                                     int result) {
+  expected_reads_.push(ExpectedRead(len, async, result));
+}
+
+void MockServiceWorkerResponseReader::ExpectReadInfoOk(size_t len, bool async) {
+  expected_reads_.push(ExpectedRead(len, async, len));
+}
+
+void MockServiceWorkerResponseReader::ExpectReadData(const char* data,
+                                                     size_t len,
+                                                     bool async,
+                                                     int result) {
+  expected_reads_.push(ExpectedRead(data, len, async, result));
+}
+
+void MockServiceWorkerResponseReader::ExpectReadDataOk(const std::string& data,
+                                                       bool async) {
+  expected_reads_.push(
+      ExpectedRead(data.data(), data.size(), async, data.size()));
+}
+
+void MockServiceWorkerResponseReader::CompletePendingRead() {
+  DCHECK(!expected_reads_.empty());
+  ExpectedRead expected = expected_reads_.front();
+  expected_reads_.pop();
+  EXPECT_TRUE(expected.async);
+  if (expected.info) {
+    pending_info_->response_data_size = expected.len;
+  } else {
+    size_t to_read = std::min(pending_buffer_len_, expected.len);
+    if (to_read > 0)
+      memcpy(pending_buffer_->data(), expected.data, to_read);
+  }
+  pending_info_ = nullptr;
+  pending_buffer_ = nullptr;
+  net::CompletionCallback callback = pending_callback_;
+  pending_callback_.Reset();
+  callback.Run(expected.result);
+}
+
+// A test implementation of ServiceWorkerResponseWriter.
+//
+// This class exposes the ability to expect writes (see ExpectWrite*Ok() below).
+// Each write to this class via WriteInfo() or WriteData() consumes another
+// expected write, in the order they were added, so:
+//   writer->ExpectWriteInfoOk(5, false);
+//   writer->ExpectWriteDataOk(6, false);
+//   writer->ExpectWriteDataOk(6, false);
+// Expects these calls, in this order:
+//   writer->WriteInfo(...);  // checks that |buf->response_data_size| == 5
+//   writer->WriteData(...);  // checks that 6 bytes are being written
+//   writer->WriteData(...);  // checks that another 6 bytes are being written
+// If this class receives an unexpected call to WriteInfo() or WriteData(), it
+// DCHECKs.
+// Expected writes marked async do not complete synchronously, but rather return
+// without running their callback and need to be completed with
+// CompletePendingWrite().
+// A convenience method AllExpectedWritesDone() is exposed so tests can ensure
+// that all expected writes have been consumed by matching calls to WriteInfo()
+// or WriteData().
+class MockServiceWorkerResponseWriter : public ServiceWorkerResponseWriter {
+ public:
+  MockServiceWorkerResponseWriter()
+      : ServiceWorkerResponseWriter(0, nullptr),
+        info_written_(0),
+        data_written_(0) {}
+  ~MockServiceWorkerResponseWriter() override {}
+
+  // ServiceWorkerResponseWriter overrides
+  void WriteInfo(HttpResponseInfoIOBuffer* info_buf,
+                 const net::CompletionCallback& callback) override;
+  void WriteData(net::IOBuffer* buf,
+                 int buf_len,
+                 const net::CompletionCallback& callback) override;
+
+  // Enqueue expected writes.
+  void ExpectWriteInfoOk(size_t len, bool async);
+  void ExpectWriteDataOk(size_t len, bool async);
+
+  // Complete a pending asynchronous write. This method DCHECKs unless there is
+  // a pending write (a write for which WriteInfo() or WriteData() has been
+  // called but the callback has not yet been run).
+  void CompletePendingWrite();
+
+  // Returns whether all expected reads have been consumed.
+  bool AllExpectedWritesDone() { return expected_writes_.size() == 0; }
+
+ private:
+  struct ExpectedWrite {
+    ExpectedWrite(bool is_info, size_t length, bool async, int result)
+        : is_info(is_info), length(length), async(async), result(result) {}
+    bool is_info;
+    size_t length;
+    bool async;
+    int result;
+  };
+
+  std::queue<ExpectedWrite> expected_writes_;
+
+  size_t info_written_;
+  size_t data_written_;
+
+  net::CompletionCallback pending_callback_;
+};
+
+void MockServiceWorkerResponseWriter::WriteInfo(
+    HttpResponseInfoIOBuffer* info_buf,
+    const net::CompletionCallback& callback) {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  EXPECT_TRUE(write.is_info);
+  EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size));
+  info_written_ += info_buf->response_data_size;
+  if (!write.async) {
+    expected_writes_.pop();
+    callback.Run(write.result);
+  } else {
+    pending_callback_ = callback;
+  }
+}
+
+void MockServiceWorkerResponseWriter::WriteData(
+    net::IOBuffer* buf,
+    int buf_len,
+    const net::CompletionCallback& callback) {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  EXPECT_FALSE(write.is_info);
+  EXPECT_EQ(write.length, static_cast<size_t>(buf_len));
+  data_written_ += buf_len;
+  if (!write.async) {
+    expected_writes_.pop();
+    callback.Run(write.result);
+  } else {
+    pending_callback_ = callback;
+  }
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteInfoOk(size_t length,
+                                                        bool async) {
+  ExpectedWrite expected(true, length, async, length);
+  expected_writes_.push(expected);
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteDataOk(size_t length,
+                                                        bool async) {
+  ExpectedWrite expected(false, length, async, length);
+  expected_writes_.push(expected);
+}
+
+void MockServiceWorkerResponseWriter::CompletePendingWrite() {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  DCHECK(write.async);
+  expected_writes_.pop();
+  pending_callback_.Run(write.result);
+}
+
+class ServiceWorkerCacheWriterTest : public ::testing::Test {
+ public:
+  ServiceWorkerCacheWriterTest()
+      : readers_deleter_(&readers_), writers_deleter_(&writers_) {}
+
+  void SetUp() override {
+    ::testing::Test::SetUp();
+    cache_writer_.reset(new ServiceWorkerCacheWriter(
+        base::Bind(&ServiceWorkerCacheWriterTest::CreateReader,
+                   base::Unretained(this)),
+        base::Bind(&ServiceWorkerCacheWriterTest::CreateWriter,
+                   base::Unretained(this))));
+    write_complete_ = false;
+  }
+
+  MockServiceWorkerResponseReader* ExpectReader() {
+    scoped_ptr<MockServiceWorkerResponseReader> reader(
+        new MockServiceWorkerResponseReader);
+    MockServiceWorkerResponseReader* borrowed_reader = reader.get();
+    readers_.push_back(reader.release());  // give ownership to |readers_|
+    return borrowed_reader;
+  }
+
+  MockServiceWorkerResponseWriter* ExpectWriter() {
+    scoped_ptr<MockServiceWorkerResponseWriter> writer(
+        new MockServiceWorkerResponseWriter);
+    MockServiceWorkerResponseWriter* borrowed_writer = writer.get();
+    writers_.push_back(writer.release());  // give ownership to |writers_|
+    return borrowed_writer;
+  }
+
+ protected:
+  // TODO(ellyjones): when unique_ptr<> is allowed, make these instead:
+  //   std::list<unique_ptr<...>>
+  // Right now, these cannot use scoped_ptr.
+  // Their elements are deleted by the STLElementDeleters below when this object
+  // goes out of scope.
+  std::list<MockServiceWorkerResponseReader*> readers_;
+  std::list<MockServiceWorkerResponseWriter*> writers_;
+  STLElementDeleter<std::list<MockServiceWorkerResponseReader*>>
+      readers_deleter_;
+  STLElementDeleter<std::list<MockServiceWorkerResponseWriter*>>
+      writers_deleter_;
+  scoped_ptr<ServiceWorkerCacheWriter> cache_writer_;
+  bool write_complete_;
+  net::Error last_error_;
+
+  scoped_ptr<ServiceWorkerResponseReader> CreateReader() {
+    if (readers_.empty())
+      return make_scoped_ptr<ServiceWorkerResponseReader>(nullptr);
+    scoped_ptr<ServiceWorkerResponseReader> reader(readers_.front());
+    readers_.pop_front();
+    return reader.Pass();
+  }
+  scoped_ptr<ServiceWorkerResponseWriter> CreateWriter() {
+    if (writers_.empty())
+      return make_scoped_ptr<ServiceWorkerResponseWriter>(nullptr);
+    scoped_ptr<ServiceWorkerResponseWriter> writer(writers_.front());
+    writers_.pop_front();
+    return writer.Pass();
+  }
+
+  ServiceWorkerCacheWriter::OnWriteCompleteCallback CreateWriteCallback() {
+    return base::Bind(&ServiceWorkerCacheWriterTest::OnWriteComplete,
+                      base::Unretained(this));
+  }
+
+  void OnWriteComplete(net::Error error) {
+    write_complete_ = true;
+    last_error_ = error;
+  }
+
+  net::Error WriteHeaders(size_t len) {
+    scoped_refptr<HttpResponseInfoIOBuffer> buf(new HttpResponseInfoIOBuffer);
+    buf->response_data_size = len;
+    return cache_writer_->MaybeWriteHeaders(buf.get(), CreateWriteCallback());
+  }
+
+  net::Error WriteData(const std::string& data) {
+    scoped_refptr<net::IOBuffer> buf = new net::StringIOBuffer(data);
+    return cache_writer_->MaybeWriteData(buf.get(), data.size(),
+                                         CreateWriteCallback());
+  }
+};
+
+// Passthrough tests:
+// In these tests, the ServiceWorkerCacheWriter under test has no existing
+// reader, since no calls to ExpectReader() have been made; this means that
+// there is no existing cached response and the incoming data is written back to
+// the cache directly.
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughHeadersSync) {
+  const size_t kHeaderSize = 16;
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfoOk(kHeaderSize, false);
+
+  net::Error error = WriteHeaders(kHeaderSize);
+  EXPECT_EQ(net::OK, error);
+  EXPECT_FALSE(write_complete_);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughHeadersAsync) {
+  size_t kHeaderSize = 16;
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfoOk(kHeaderSize, true);
+
+  net::Error error = WriteHeaders(kHeaderSize);
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  EXPECT_FALSE(write_complete_);
+  writer->CompletePendingWrite();
+  EXPECT_TRUE(write_complete_);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughDataSync) {
+  const std::string data1 = "abcdef";
+  const std::string data2 = "ghijklmno";
+  size_t response_size = data1.size() + data2.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfoOk(response_size, false);
+  writer->ExpectWriteDataOk(data1.size(), false);
+  writer->ExpectWriteDataOk(data2.size(), false);
+
+  net::Error error = WriteHeaders(response_size);
+  EXPECT_EQ(net::OK, error);
+
+  error = WriteData(data1);
+  EXPECT_EQ(net::OK, error);
+
+  error = WriteData(data2);
+  EXPECT_EQ(net::OK, error);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, PassthroughDataAsync) {
+  const std::string data1 = "abcdef";
+  const std::string data2 = "ghijklmno";
+  size_t response_size = data1.size() + data2.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  writer->ExpectWriteInfoOk(response_size, false);
+  writer->ExpectWriteDataOk(data1.size(), true);
+  writer->ExpectWriteDataOk(data2.size(), true);
+
+  net::Error error = WriteHeaders(response_size);
+  EXPECT_EQ(net::OK, error);
+
+  error = WriteData(data1);
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  writer->CompletePendingWrite();
+  EXPECT_TRUE(write_complete_);
+
+  write_complete_ = false;
+  error = WriteData(data2);
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  writer->CompletePendingWrite();
+  EXPECT_TRUE(write_complete_);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
+// Comparison tests:
+// For the Compare* tests below, the ServiceWorkerCacheWriter under test has a
+// reader for an existing cached response, so it will compare the response being
+// written to it against the existing cached response.
+
+TEST_F(ServiceWorkerCacheWriterTest, CompareHeadersSync) {
+  size_t response_size = 3;
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* reader = ExpectReader();
+
+  reader->ExpectReadInfoOk(response_size, false);
+
+  net::Error error = WriteHeaders(response_size);
+  EXPECT_EQ(net::OK, error);
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_TRUE(reader->AllExpectedReadsDone());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, CompareDataOkSync) {
+  const std::string data1 = "abcdef";
+  size_t response_size = data1.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* reader = ExpectReader();
+
+  reader->ExpectReadInfoOk(response_size, false);
+  reader->ExpectReadDataOk(data1, false);
+
+  net::Error error = WriteHeaders(response_size);
+  EXPECT_EQ(net::OK, error);
+
+  error = WriteData(data1);
+  EXPECT_EQ(net::OK, error);
+
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_TRUE(reader->AllExpectedReadsDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, CompareShortCacheReads) {
+  const size_t kHeaderSize = 16;
+  const std::string& data1 = "abcdef";
+  const std::string& cache_data2 = "ghi";
+  const std::string& cache_data3 = "j";
+  const std::string& cache_data4 = "kl";
+  const std::string& net_data2 = "ghijkl";
+  const std::string& data5 = "mnopqrst";
+
+  MockServiceWorkerResponseReader* reader = ExpectReader();
+  reader->ExpectReadInfo(kHeaderSize, false, kHeaderSize);
+  reader->ExpectReadDataOk(data1, false);
+  reader->ExpectReadDataOk(cache_data2, false);
+  reader->ExpectReadDataOk(cache_data3, false);
+  reader->ExpectReadDataOk(cache_data4, false);
+  reader->ExpectReadDataOk(data5, false);
+
+  net::Error error = WriteHeaders(kHeaderSize);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(data1);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(net_data2);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(data5);
+  EXPECT_EQ(net::OK, error);
+  EXPECT_TRUE(reader->AllExpectedReadsDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, CompareDataOkAsync) {
+  const std::string data1 = "abcdef";
+  size_t response_size = data1.size();
+
+  MockServiceWorkerResponseReader* reader = ExpectReader();
+
+  reader->ExpectReadInfoOk(response_size, true);
+  reader->ExpectReadDataOk(data1, true);
+
+  net::Error error = WriteHeaders(response_size);
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  reader->CompletePendingRead();
+
+  error = WriteData(data1);
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  reader->CompletePendingRead();
+
+  EXPECT_TRUE(reader->AllExpectedReadsDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+TEST_F(ServiceWorkerCacheWriterTest, CompareDataManyOkAsync) {
+  const std::string expected_data[] = {
+      "abcdef", "ghijkl", "mnopqr", "stuvwxyz",
+  };
+  size_t response_size = 0;
+  for (size_t i = 0; i < arraysize(expected_data); ++i)
+    response_size += expected_data[i].size();
+
+  MockServiceWorkerResponseReader* reader = ExpectReader();
+
+  reader->ExpectReadInfoOk(response_size, true);
+  for (size_t i = 0; i < arraysize(expected_data); ++i) {
+    reader->ExpectReadDataOk(expected_data[i], true);
+  }
+
+  net::Error error = WriteHeaders(response_size);
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  reader->CompletePendingRead();
+
+  for (size_t i = 0; i < arraysize(expected_data); ++i) {
+    error = WriteData(expected_data[i]);
+    EXPECT_EQ(net::ERR_IO_PENDING, error);
+    reader->CompletePendingRead();
+    EXPECT_EQ(net::OK, last_error_);
+  }
+
+  EXPECT_TRUE(reader->AllExpectedReadsDone());
+  EXPECT_EQ(0U, cache_writer_->bytes_written());
+}
+
+// This test writes headers and three data blocks data1, data2, data3; data2
+// differs in the cached version. The writer should be asked to rewrite the
+// headers and body with the new value, and the copy reader should be asked to
+// read the header and data1.
+TEST_F(ServiceWorkerCacheWriterTest, CompareFailedCopySync) {
+  std::string data1 = "abcdef";
+  std::string cache_data2 = "ghijkl";
+  std::string net_data2 = "mnopqr";
+  std::string data3 = "stuvwxyz";
+  size_t cache_response_size = data1.size() + cache_data2.size() + data3.size();
+  size_t net_response_size = data1.size() + net_data2.size() + data3.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* compare_reader = ExpectReader();
+  MockServiceWorkerResponseReader* copy_reader = ExpectReader();
+
+  compare_reader->ExpectReadInfoOk(cache_response_size, false);
+  compare_reader->ExpectReadDataOk(data1, false);
+  compare_reader->ExpectReadDataOk(cache_data2, false);
+
+  copy_reader->ExpectReadInfoOk(cache_response_size, false);
+  copy_reader->ExpectReadDataOk(data1, false);
+
+  writer->ExpectWriteInfoOk(net_response_size, false);
+  writer->ExpectWriteDataOk(data1.size(), false);
+  writer->ExpectWriteDataOk(net_data2.size(), false);
+  writer->ExpectWriteDataOk(data3.size(), false);
+
+  net::Error error = WriteHeaders(net_response_size);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(data1);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(net_data2);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(data3);
+  EXPECT_EQ(net::OK, error);
+
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_TRUE(compare_reader->AllExpectedReadsDone());
+  EXPECT_TRUE(copy_reader->AllExpectedReadsDone());
+}
+
+// Tests behavior when the cached data is shorter than the network data.
+TEST_F(ServiceWorkerCacheWriterTest, CompareFailedCopyShort) {
+  std::string data1 = "abcdef";
+  std::string cache_data2 = "mnop";
+  std::string net_data2 = "mnopqr";
+  std::string data3 = "stuvwxyz";
+  size_t cache_response_size = data1.size() + cache_data2.size() + data3.size();
+  size_t net_response_size = data1.size() + net_data2.size() + data3.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* compare_reader = ExpectReader();
+  MockServiceWorkerResponseReader* copy_reader = ExpectReader();
+
+  compare_reader->ExpectReadInfoOk(cache_response_size, false);
+  compare_reader->ExpectReadDataOk(data1, false);
+  compare_reader->ExpectReadDataOk(cache_data2, false);
+  compare_reader->ExpectReadDataOk("", false);  // EOF read
+
+  copy_reader->ExpectReadInfoOk(cache_response_size, false);
+  copy_reader->ExpectReadDataOk(data1, false);
+
+  writer->ExpectWriteInfoOk(net_response_size, false);
+  writer->ExpectWriteDataOk(data1.size(), false);
+  writer->ExpectWriteDataOk(net_data2.size(), false);
+  writer->ExpectWriteDataOk(data3.size(), false);
+
+  net::Error error = WriteHeaders(net_response_size);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(data1);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(net_data2);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(data3);
+  EXPECT_EQ(net::OK, error);
+
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_TRUE(compare_reader->AllExpectedReadsDone());
+  EXPECT_TRUE(copy_reader->AllExpectedReadsDone());
+}
+
+// Tests behavior when the cached data is longer than the network data.
+TEST_F(ServiceWorkerCacheWriterTest, CompareFailedCopyLong) {
+  std::string data1 = "abcdef";
+  std::string cache_data2 = "mnop";
+  std::string net_data2 = "mnop";
+  std::string cache_data3 = "qr";
+  size_t cached_size = data1.size() + cache_data2.size() + cache_data3.size();
+  size_t net_size = data1.size() + net_data2.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* compare_reader = ExpectReader();
+  MockServiceWorkerResponseReader* copy_reader = ExpectReader();
+
+  compare_reader->ExpectReadInfoOk(cached_size, false);
+  compare_reader->ExpectReadDataOk(data1, false);
+  compare_reader->ExpectReadDataOk(cache_data2, false);
+
+  // The comparison should fail at the end of |cache_data2|, when the cache
+  // writer realizes the two responses are different sizes, and then the network
+  // data should be written back starting with |net_data2|.
+  copy_reader->ExpectReadInfoOk(cached_size, false);
+  copy_reader->ExpectReadDataOk(data1, false);
+  copy_reader->ExpectReadDataOk(net_data2, false);
+
+  writer->ExpectWriteInfoOk(net_size, false);
+  writer->ExpectWriteDataOk(data1.size(), false);
+  writer->ExpectWriteDataOk(net_data2.size(), false);
+
+  net::Error error = WriteHeaders(net_size);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(data1);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData(net_data2);
+  EXPECT_EQ(net::OK, error);
+  error = WriteData("");
+  EXPECT_EQ(net::OK, error);
+
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+  EXPECT_TRUE(compare_reader->AllExpectedReadsDone());
+  EXPECT_TRUE(copy_reader->AllExpectedReadsDone());
+}
+
+}  // namespace
+}  // namespace content
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc
index 1f98d99e..2f2160e 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -4,10 +4,11 @@
 
 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
 
-#include <algorithm>
-
+#include "base/bind.h"
+#include "base/callback.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
+#include "content/browser/service_worker/service_worker_cache_writer.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_disk_cache.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
@@ -18,7 +19,6 @@
 #include "net/http/http_network_session.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_status.h"
@@ -40,260 +40,8 @@
     "The script resource is behind a redirect, which is disallowed.";
 const char kServiceWorkerAllowed[] = "Service-Worker-Allowed";
 
-const int kBufferSize = 16 * 1024;
-
 }  // namespace
 
-// Reads an existing resource and copies it via
-// ServiceWorkerWriteToCacheJob::WriteData.
-class ServiceWorkerWriteToCacheJob::Copier : public base::RefCounted<Copier> {
- public:
-  Copier(base::WeakPtr<ServiceWorkerWriteToCacheJob> owner,
-         scoped_ptr<ServiceWorkerResponseReader> reader,
-         int bytes_to_copy,
-         const base::Callback<void(ServiceWorkerStatusCode)>& callback)
-      : owner_(owner),
-        reader_(reader.release()),
-        bytes_to_copy_(bytes_to_copy),
-        callback_(callback) {
-    DCHECK_LT(0, bytes_to_copy_);
-  }
-
-  void Start() {
-    io_buffer_ = new net::IOBuffer(kBufferSize);
-    ReadSomeData();
-  }
-
- private:
-  friend class base::RefCounted<Copier>;
-  ~Copier() {}
-
-  void ReadSomeData() {
-    reader_->ReadData(io_buffer_.get(), kBufferSize,
-                      base::Bind(&Copier::OnReadDataComplete, this));
-  }
-
-  void OnReadDataComplete(int result) {
-    if (!owner_)
-      return;
-    if (result <= 0) {
-      // We hit EOF or error but weren't done copying.
-      Complete(SERVICE_WORKER_ERROR_FAILED);
-      return;
-    }
-
-    int bytes_to_write = std::min(bytes_to_copy_, result);
-    owner_->WriteData(io_buffer_.get(), bytes_to_write,
-                      base::Bind(&Copier::OnWriteDataComplete, this));
-  }
-
-  void OnWriteDataComplete(int result) {
-    if (result < 0) {
-      Complete(SERVICE_WORKER_ERROR_FAILED);
-      return;
-    }
-
-    DCHECK_LE(result, bytes_to_copy_);
-    bytes_to_copy_ -= result;
-    if (bytes_to_copy_ == 0) {
-      Complete(SERVICE_WORKER_OK);
-      return;
-    }
-
-    ReadSomeData();
-  }
-
-  void Complete(ServiceWorkerStatusCode status) {
-    if (!owner_)
-      return;
-    callback_.Run(status);
-  }
-
-  base::WeakPtr<ServiceWorkerWriteToCacheJob> owner_;
-  scoped_ptr<ServiceWorkerResponseReader> reader_;
-  int bytes_to_copy_ = 0;
-  base::Callback<void(ServiceWorkerStatusCode)> callback_;
-  scoped_refptr<net::IOBuffer> io_buffer_;
-};
-
-// Abstract consumer for ServiceWorkerWriteToCacheJob that processes data from
-// the network.
-class ServiceWorkerWriteToCacheJob::NetDataConsumer {
- public:
-  virtual ~NetDataConsumer() {}
-
-  // Called by |owner_|'s OnResponseStarted.
-  virtual void OnResponseStarted() = 0;
-
-  // HandleData should call |owner_|->NotifyReadComplete() when done.
-  virtual void HandleData(net::IOBuffer* buf, int size) = 0;
-};
-
-// Dumb consumer that just writes everything it sees to disk.
-class ServiceWorkerWriteToCacheJob::PassThroughConsumer
-    : public ServiceWorkerWriteToCacheJob::NetDataConsumer {
- public:
-  explicit PassThroughConsumer(ServiceWorkerWriteToCacheJob* owner)
-      : owner_(owner), weak_factory_(this) {}
-  ~PassThroughConsumer() override {}
-
-  void OnResponseStarted() override {
-    owner_->WriteHeaders(
-        base::Bind(&PassThroughConsumer::OnWriteHeadersComplete,
-                   weak_factory_.GetWeakPtr()));
-    owner_->SetPendingIO();
-  }
-
-  void HandleData(net::IOBuffer* buf, int size) override {
-    if (size == 0) {
-      owner_->OnPassThroughComplete();
-      return;
-    }
-
-    owner_->WriteData(buf, size,
-                      base::Bind(&PassThroughConsumer::OnWriteDataComplete,
-                                 weak_factory_.GetWeakPtr()));
-    owner_->SetPendingIO();
-  }
-
- private:
-  void OnWriteHeadersComplete() {
-    owner_->ClearPendingIO();
-    owner_->CommitHeadersAndNotifyHeadersComplete();
-  }
-
-  void OnWriteDataComplete(int result) {
-    owner_->ClearPendingIO();
-    owner_->NotifyReadComplete(result);
-  }
-
-  ServiceWorkerWriteToCacheJob* owner_;
-  base::WeakPtrFactory<PassThroughConsumer> weak_factory_;
-};
-
-// Compares an existing resource with data progressively fed to it.
-// Calls back to |owner|->OnCompareComplete once done.
-class ServiceWorkerWriteToCacheJob::Comparer
-    : public ServiceWorkerWriteToCacheJob::NetDataConsumer {
- public:
-  Comparer(ServiceWorkerWriteToCacheJob* owner,
-           scoped_ptr<ServiceWorkerResponseReader> reader)
-      : owner_(owner), reader_(reader.release()), weak_factory_(this) {}
-  ~Comparer() override {}
-
-  void OnResponseStarted() override {
-    owner_->CommitHeadersAndNotifyHeadersComplete();
-  }
-
-  void HandleData(net::IOBuffer* buf, int size) override {
-    net_data_ = buf;
-    net_data_offset_ = 0;
-    net_data_size_ = size;
-
-    if (size == 0 && info_) {
-      Complete(bytes_matched_ == info_->response_data_size);
-      return;
-    }
-
-    if (!info_) {
-      read_buffer_ = new net::IOBuffer(kBufferSize);
-      info_ = new HttpResponseInfoIOBuffer;
-      reader_->ReadInfo(info_.get(), base::Bind(&Comparer::OnReadInfoComplete,
-                                                weak_factory_.GetWeakPtr()));
-      owner_->SetPendingIO();
-      return;
-    }
-
-    ReadSomeData();
-    owner_->SetPendingIO();
-  }
-
- private:
-  int bytes_remaining() {
-    DCHECK(net_data_);
-    DCHECK_LE(0, net_data_offset_);
-    DCHECK_LE(net_data_offset_, net_data_size_);
-    return net_data_size_ - net_data_offset_;
-  }
-
-  void OnReadInfoComplete(int result) {
-    if (result < 0) {
-      Complete(false);
-      return;
-    }
-
-    if (bytes_remaining() == 0) {
-      Complete(bytes_matched_ == info_->response_data_size);
-      return;
-    }
-
-    ReadSomeData();
-  }
-
-  void ReadSomeData() {
-    DCHECK_LT(0, bytes_remaining());
-    int bytes_to_read = std::min(bytes_remaining(), kBufferSize);
-    reader_->ReadData(
-        read_buffer_.get(), bytes_to_read,
-        base::Bind(&Comparer::OnReadDataComplete, weak_factory_.GetWeakPtr()));
-  }
-
-  void OnReadDataComplete(int result) {
-    if (result <= 0) {
-      // We hit error or EOF but had more to compare.
-      Complete(false);
-      return;
-    }
-
-    DCHECK_LE(result, bytes_remaining());
-    if (memcmp(net_data_->data() + net_data_offset_, read_buffer_->data(),
-               result) != 0) {
-      Complete(false);
-      return;
-    }
-
-    net_data_offset_ += result;
-    if (bytes_remaining() == 0) {
-      NotifyReadComplete();
-      return;
-    }
-
-    ReadSomeData();
-  }
-
-  // Completes one HandleData() call.
-  void NotifyReadComplete() {
-    int size = net_data_size_;
-    net_data_ = nullptr;
-    net_data_offset_ = 0;
-    net_data_size_ = 0;
-
-    bytes_matched_ += size;
-    owner_->ClearPendingIO();
-    owner_->NotifyReadComplete(size);
-  }
-
-  // Completes the entire Comparer.
-  void Complete(bool is_equal) {
-    owner_->OnCompareComplete(bytes_matched_, is_equal);
-  }
-
-  ServiceWorkerWriteToCacheJob* owner_;
-  scoped_ptr<ServiceWorkerResponseReader> reader_;
-  scoped_refptr<net::IOBuffer> read_buffer_;
-  scoped_refptr<HttpResponseInfoIOBuffer> info_;
-
-  // Cumulative number of bytes successfully compared.
-  int bytes_matched_ = 0;
-
-  // State used for one HandleData() call.
-  scoped_refptr<net::IOBuffer> net_data_;
-  int net_data_offset_ = 0;
-  int net_data_size_ = 0;
-
-  base::WeakPtrFactory<Comparer> weak_factory_;
-};
-
 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
     net::URLRequest* request,
     net::NetworkDelegate* network_delegate,
@@ -331,15 +79,14 @@
         net::URLRequestStatus::FAILED, net::ERR_FAILED));
     return;
   }
-  if (incumbent_response_id_ != kInvalidServiceWorkerResourceId &&
-      !version_->skip_script_comparison()) {
-    scoped_ptr<ServiceWorkerResponseReader> incumbent_reader =
-        context_->storage()->CreateResponseReader(incumbent_response_id_);
-    consumer_.reset(new Comparer(this, incumbent_reader.Pass()));
-  } else {
-    consumer_.reset(new PassThroughConsumer(this));
-  }
 
+  // These uses of Unretained are safe because this object is the sole owner of
+  // |cache_writer_|, which in turn is the sole user of these callbacks.
+  cache_writer_.reset(new ServiceWorkerCacheWriter(
+      base::Bind(&ServiceWorkerWriteToCacheJob::CreateCacheResponseReader,
+                 base::Unretained(this)),
+      base::Bind(&ServiceWorkerWriteToCacheJob::CreateCacheResponseWriter,
+                 base::Unretained(this))));
   version_->script_cache_map()->NotifyStartedCaching(
       url_, response_id_);
   did_notify_started_ = true;
@@ -352,12 +99,9 @@
   weak_factory_.InvalidateWeakPtrs();
   has_been_killed_ = true;
   net_request_.reset();
-  if (did_notify_started_ && !did_notify_finished_) {
-    version_->script_cache_map()->NotifyFinishedCaching(
-        url_, -1,
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED),
-        kKilledError);
-    did_notify_finished_ = true;
+  if (did_notify_started_) {
+    NotifyFinishedCaching(net::URLRequestStatus::FromError(net::ERR_ABORTED),
+                          kKilledError);
   }
   writer_.reset();
   context_.reset();
@@ -413,17 +157,21 @@
     return false;
 
   if (!status.is_success()) {
-    AsyncNotifyDoneHelper(status, kFetchScriptError);
+    NotifyDoneHelper(status, kFetchScriptError);
     return false;
   }
 
-  DCHECK_EQ(0, *bytes_read);
-  consumer_->HandleData(buf, 0);
-  if (did_notify_finished_)
-    return GetStatus().is_success();
-  if (GetStatus().is_io_pending())
+  HandleNetData(*bytes_read);
+
+  if (!status.is_success()) {
+    NotifyDoneHelper(status, "");
     return false;
-  return status.is_success();
+  }
+
+  // Since URLRequestStatus::is_success() means "SUCCESS or IO_PENDING", but the
+  // contract of this function is "return true for synchronous successes only",
+  // it is important to test against SUCCESS explicitly here.
+  return GetStatus().status() == net::URLRequestStatus::SUCCESS;
 }
 
 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
@@ -462,104 +210,14 @@
     int* bytes_read) {
   DCHECK_GT(buf_size, 0);
   DCHECK(bytes_read);
-  *bytes_read = 0;
   io_buffer_ = buf;
   io_buffer_bytes_ = 0;
-  int net_bytes_read = 0;
-  if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
-    if (net_request_->status().is_io_pending())
-      return net_request_->status();
-    DCHECK(!net_request_->status().is_success());
-    return net_request_->status();
-  }
+  if (!net_request_->Read(buf, buf_size, bytes_read))
+    DCHECK_NE(net::URLRequestStatus::SUCCESS, net_request_->status().status());
 
-  if (net_bytes_read != 0) {
-    HandleNetData(net_bytes_read);
-    DCHECK(GetStatus().is_io_pending());
-    return GetStatus();
-  }
-
-  DCHECK(net_request_->status().is_success());
   return net_request_->status();
 }
 
-void ServiceWorkerWriteToCacheJob::WriteHeaders(const base::Closure& callback) {
-  if (!context_) {
-    AsyncNotifyDoneHelper(
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED),
-        kFetchScriptError);
-    return;
-  }
-  TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
-                               "ServiceWorkerWriteToCacheJob::ExecutingJob",
-                               this, "WriteHeaders");
-  writer_ = context_->storage()->CreateResponseWriter(response_id_);
-  scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
-      new HttpResponseInfoIOBuffer(
-          new net::HttpResponseInfo(net_request_->response_info()));
-  writer_->WriteInfo(
-      info_buffer.get(),
-      base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
-                 weak_factory_.GetWeakPtr(), callback));
-}
-
-void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(
-    const base::Closure& callback,
-    int result) {
-  if (result < 0) {
-    ServiceWorkerMetrics::CountWriteResponseResult(
-        ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
-    AsyncNotifyDoneHelper(
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, result),
-        kFetchScriptError);
-    return;
-  }
-  TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
-                               "ServiceWorkerWriteToCacheJob::ExecutingJob",
-                               this, "WriteHeadersCompleted");
-  callback.Run();
-}
-
-void ServiceWorkerWriteToCacheJob::WriteData(
-    net::IOBuffer* buf,
-    int bytes_to_write,
-    const base::Callback<void(int result)>& callback) {
-  DCHECK_LT(0, bytes_to_write);
-  TRACE_EVENT_ASYNC_STEP_INTO1(
-      "ServiceWorker", "ServiceWorkerWriteToCacheJob::ExecutingJob", this,
-      "WriteData", "Amount to write", bytes_to_write);
-
-  writer_->WriteData(
-      buf, bytes_to_write,
-      base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
-                 weak_factory_.GetWeakPtr(), callback));
-}
-
-void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(
-    const base::Callback<void(int result)>& callback,
-    int result) {
-  DCHECK_NE(0, result);
-  if (!context_) {
-    AsyncNotifyDoneHelper(
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED),
-        kFetchScriptError);
-    return;
-  }
-  if (result < 0) {
-    ServiceWorkerMetrics::CountWriteResponseResult(
-        ServiceWorkerMetrics::WRITE_DATA_ERROR);
-    AsyncNotifyDoneHelper(
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, result),
-        kFetchScriptError);
-    return;
-  }
-  ServiceWorkerMetrics::CountWriteResponseResult(
-      ServiceWorkerMetrics::WRITE_OK);
-  callback.Run(result);
-  TRACE_EVENT_ASYNC_END0("ServiceWorker",
-                         "ServiceWorkerWriteToCacheJob::ExecutingJob", this);
-}
-
 void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
     net::URLRequest* request,
     const net::RedirectInfo& redirect_info,
@@ -568,9 +226,9 @@
   TRACE_EVENT0("ServiceWorker",
                "ServiceWorkerWriteToCacheJob::OnReceivedRedirect");
   // Script resources can't redirect.
-  AsyncNotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                                              net::ERR_UNSAFE_REDIRECT),
-                        kRedirectError);
+  NotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                         net::ERR_UNSAFE_REDIRECT),
+                   kRedirectError);
 }
 
 void ServiceWorkerWriteToCacheJob::OnAuthRequired(
@@ -580,7 +238,7 @@
   TRACE_EVENT0("ServiceWorker",
                "ServiceWorkerWriteToCacheJob::OnAuthRequired");
   // TODO(michaeln): Pass this thru to our jobs client.
-  AsyncNotifyDoneHelper(
+  NotifyDoneHelper(
       net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED),
       kClientAuthenticationError);
 }
@@ -593,7 +251,7 @@
                "ServiceWorkerWriteToCacheJob::OnCertificateRequested");
   // TODO(michaeln): Pass this thru to our jobs client.
   // see NotifyCertificateRequested.
-  AsyncNotifyDoneHelper(
+  NotifyDoneHelper(
       net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED),
       kClientAuthenticationError);
 }
@@ -607,9 +265,9 @@
                "ServiceWorkerWriteToCacheJob::OnSSLCertificateError");
   // TODO(michaeln): Pass this thru to our jobs client,
   // see NotifySSLCertificateError.
-  AsyncNotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                                              net::ERR_INSECURE_RESPONSE),
-                        kSSLError);
+  NotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                         net::ERR_INSECURE_RESPONSE),
+                   kSSLError);
 }
 
 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
@@ -625,15 +283,15 @@
     net::URLRequest* request) {
   DCHECK_EQ(net_request_, request);
   if (!request->status().is_success()) {
-    AsyncNotifyDoneHelper(request->status(), kFetchScriptError);
+    NotifyDoneHelper(request->status(), kFetchScriptError);
     return;
   }
   if (request->GetResponseCode() / 100 != 2) {
     std::string error_message =
         base::StringPrintf(kBadHTTPResponseError, request->GetResponseCode());
-    AsyncNotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                                                net::ERR_INVALID_RESPONSE),
-                          error_message);
+    NotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                           net::ERR_INVALID_RESPONSE),
+                     error_message);
     // TODO(michaeln): Instead of error'ing immediately, send the net
     // response to our consumer, just don't cache it?
     return;
@@ -644,9 +302,9 @@
     const net::HttpNetworkSession::Params* session_params =
         request->context()->GetNetworkSessionParams();
     if (!session_params || !session_params->ignore_certificate_errors) {
-      AsyncNotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                                                  net::ERR_INSECURE_RESPONSE),
-                            kSSLError);
+      NotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                             net::ERR_INSECURE_RESPONSE),
+                       kSSLError);
       return;
     }
   }
@@ -661,9 +319,9 @@
           mime_type.empty()
               ? kNoMIMEError
               : base::StringPrintf(kBadMIMEError, mime_type.c_str());
-      AsyncNotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                                                  net::ERR_INSECURE_RESPONSE),
-                            error_message);
+      NotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                             net::ERR_INSECURE_RESPONSE),
+                       error_message);
       return;
     }
 
@@ -676,17 +334,46 @@
   if (net_request_->response_info().network_accessed)
     version_->embedded_worker()->OnNetworkAccessedForScriptLoad();
 
-  consumer_->OnResponseStarted();
+  http_info_.reset(new net::HttpResponseInfo(net_request_->response_info()));
+  scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
+      new HttpResponseInfoIOBuffer(
+          new net::HttpResponseInfo(net_request_->response_info()));
+  net::Error error = cache_writer_->MaybeWriteHeaders(
+      info_buffer.get(),
+      base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
+                 weak_factory_.GetWeakPtr()));
+  SetStatus(net::URLRequestStatus::FromError(error));
+  if (error != net::ERR_IO_PENDING)
+    NotifyHeadersComplete();
 }
 
-void ServiceWorkerWriteToCacheJob::CommitHeadersAndNotifyHeadersComplete() {
-  http_info_.reset(new net::HttpResponseInfo(net_request_->response_info()));
+void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(net::Error error) {
+  SetStatus(net::URLRequestStatus::FromError(error));
   NotifyHeadersComplete();
 }
 
 void ServiceWorkerWriteToCacheJob::HandleNetData(int bytes_read) {
   io_buffer_bytes_ = bytes_read;
-  consumer_->HandleData(io_buffer_.get(), bytes_read);
+  net::Error error = cache_writer_->MaybeWriteData(
+      io_buffer_.get(), bytes_read,
+      base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
+                 weak_factory_.GetWeakPtr()));
+  SetStatus(net::URLRequestStatus::FromError(error));
+
+  // In case of ERR_IO_PENDING, this logic is done in OnWriteDataComplete.
+  if (error != net::ERR_IO_PENDING && bytes_read == 0) {
+    NotifyFinishedCaching(net::URLRequestStatus::FromError(error),
+                          std::string());
+  }
+}
+
+void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(net::Error error) {
+  SetStatus(net::URLRequestStatus::FromError(error));
+  DCHECK_NE(net::ERR_IO_PENDING, error);
+  if (io_buffer_bytes_ == 0) {
+    NotifyDoneHelper(net::URLRequestStatus::FromError(error), std::string());
+  }
+  NotifyReadComplete(error == net::OK ? io_buffer_bytes_ : error);
 }
 
 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
@@ -695,18 +382,23 @@
   DCHECK_EQ(net_request_, request);
   if (bytes_read < 0) {
     DCHECK(!request->status().is_success());
-    AsyncNotifyDoneHelper(request->status(), kFetchScriptError);
+    NotifyDoneHelper(request->status(), kFetchScriptError);
     return;
   }
-  if (bytes_read > 0) {
-    HandleNetData(bytes_read);
-    DCHECK(GetStatus().is_io_pending());
-    return;
+  HandleNetData(bytes_read);
+  // HandleNetData can cause status of this job to change. If the status changes
+  // to IO_PENDING, that means HandleNetData has pending IO, and
+  // NotifyReadComplete will be called later by the appropriate callback.
+  if (!GetStatus().is_io_pending()) {
+    int result = GetStatus().status() == net::URLRequestStatus::SUCCESS
+                     ? bytes_read
+                     : GetStatus().error();
+    // If bytes_read is 0, HandleNetData synchronously completed and this job is
+    // at EOF.
+    if (bytes_read == 0)
+      NotifyDoneHelper(GetStatus(), std::string());
+    NotifyReadComplete(result);
   }
-
-  // No more data to process, the job is complete.
-  DCHECK(request->status().is_success());
-  HandleNetData(0);
 }
 
 bool ServiceWorkerWriteToCacheJob::CheckPathRestriction(
@@ -720,107 +412,74 @@
   if (!ServiceWorkerUtils::IsPathRestrictionSatisfied(
           version_->scope(), url_,
           has_header ? &service_worker_allowed : nullptr, &error_message)) {
-    AsyncNotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
-                                                net::ERR_INSECURE_RESPONSE),
-                          error_message);
+    NotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
+                                           net::ERR_INSECURE_RESPONSE),
+                     error_message);
     return false;
   }
   return true;
 }
 
-void ServiceWorkerWriteToCacheJob::SetPendingIO() {
-  SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
-}
-
-void ServiceWorkerWriteToCacheJob::ClearPendingIO() {
-  SetStatus(net::URLRequestStatus());
-}
-
-void ServiceWorkerWriteToCacheJob::OnPassThroughComplete() {
-  NotifyFinishedCaching(net::URLRequestStatus(), std::string());
-  if (GetStatus().is_io_pending()) {
-    ClearPendingIO();
-    NotifyReadComplete(0);
-  }
-}
-
-void ServiceWorkerWriteToCacheJob::OnCompareComplete(int bytes_matched,
-                                                     bool is_equal) {
-  if (is_equal) {
-    // This version is identical to the incumbent, so discard it and fail this
-    // job.
-    version_->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS);
-    AsyncNotifyDoneHelper(
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED),
-        kFetchScriptError);
-    return;
-  }
-
-  // We must switch to the pass through consumer. Write what is known
-  // (headers + bytes matched) to disk.
-  WriteHeaders(base::Bind(&ServiceWorkerWriteToCacheJob::CopyIncumbent,
-                          weak_factory_.GetWeakPtr(), bytes_matched));
-  SetPendingIO();
-}
-
-void ServiceWorkerWriteToCacheJob::CopyIncumbent(int bytes_to_copy) {
-  if (bytes_to_copy == 0) {
-    OnCopyComplete(SERVICE_WORKER_OK);
-    return;
-  }
-  scoped_ptr<ServiceWorkerResponseReader> incumbent_reader =
-      context_->storage()->CreateResponseReader(incumbent_response_id_);
-  scoped_refptr<Copier> copier = new Copier(
-      weak_factory_.GetWeakPtr(), incumbent_reader.Pass(), bytes_to_copy,
-      base::Bind(&ServiceWorkerWriteToCacheJob::OnCopyComplete,
-                 weak_factory_.GetWeakPtr()));
-  copier->Start();  // It deletes itself when done.
-  DCHECK(GetStatus().is_io_pending());
-}
-
-void ServiceWorkerWriteToCacheJob::OnCopyComplete(
-    ServiceWorkerStatusCode status) {
-  if (status != SERVICE_WORKER_OK) {
-    AsyncNotifyDoneHelper(
-        net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED),
-        kFetchScriptError);
-    return;
-  }
-
-  // Continue processing the net data that triggered the comparison fail and
-  // copy.
-  if (io_buffer_bytes_ > 0) {
-    consumer_.reset(new PassThroughConsumer(this));
-    consumer_->HandleData(io_buffer_.get(), io_buffer_bytes_);
-    return;
-  }
-
-  // The copy was triggered by EOF from the network, which
-  // means the job is now done.
-  NotifyFinishedCaching(net::URLRequestStatus(), std::string());
-  ClearPendingIO();
-  NotifyReadComplete(0);
-}
-
-void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
+void ServiceWorkerWriteToCacheJob::NotifyDoneHelper(
     const net::URLRequestStatus& status,
     const std::string& status_message) {
   DCHECK(!status.is_io_pending());
+
+  // Note that NotifyFinishedCaching has logic in it to detect the special case
+  // mentioned below as well.
   NotifyFinishedCaching(status, status_message);
-  SetStatus(status);
-  NotifyDone(status);
+
+  net::URLRequestStatus reported_status = status;
+  std::string reported_status_message = status_message;
+
+  // A strange special case: requests that successfully fetch the entire
+  // ServiceWorker and write it back, but which did not replace the incumbent
+  // script because the new script was identical, are considered to have failed.
+  if (status.is_success() && !cache_writer_->did_replace()) {
+    reported_status = net::URLRequestStatus::FromError(net::ERR_FAILED);
+    reported_status_message = "";
+  }
+
+  SetStatus(reported_status);
+  NotifyDone(reported_status);
 }
 
 void ServiceWorkerWriteToCacheJob::NotifyFinishedCaching(
     net::URLRequestStatus status,
     const std::string& status_message) {
-  DCHECK(!did_notify_finished_);
+  if (did_notify_finished_)
+    return;
+
+  // If all the calls to MaybeWriteHeaders/MaybeWriteData succeeded, but the
+  // incumbent entry wasn't actually replaced because the new entry was
+  // equivalent, the new version didn't actually install because it already
+  // exists.
+  if (status.status() == net::URLRequestStatus::SUCCESS &&
+      !cache_writer_->did_replace()) {
+    status =
+        net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED);
+    version_->SetStartWorkerStatusCode(SERVICE_WORKER_ERROR_EXISTS);
+  }
+
   int size = -1;
   if (status.is_success())
-    size = writer_ ? writer_->amount_written() : 0;
+    size = cache_writer_->bytes_written();
+
   version_->script_cache_map()->NotifyFinishedCaching(url_, size, status,
                                                       status_message);
   did_notify_finished_ = true;
 }
 
+scoped_ptr<ServiceWorkerResponseReader>
+ServiceWorkerWriteToCacheJob::CreateCacheResponseReader() {
+  if (incumbent_response_id_ != kInvalidServiceWorkerResponseId)
+    return context_->storage()->CreateResponseReader(incumbent_response_id_);
+  return nullptr;
+}
+
+scoped_ptr<ServiceWorkerResponseWriter>
+ServiceWorkerWriteToCacheJob::CreateCacheResponseWriter() {
+  return context_->storage()->CreateResponseWriter(response_id_);
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.h b/content/browser/service_worker/service_worker_write_to_cache_job.h
index 9fcd4b86..deee1070 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job.h
+++ b/content/browser/service_worker/service_worker_write_to_cache_job.h
@@ -15,13 +15,14 @@
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/resource_type.h"
+#include "net/base/net_errors.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_job.h"
 
 namespace content {
 
+class ServiceWorkerCacheWriter;
 class ServiceWorkerContextCore;
-class ServiceWorkerResponseWriter;
 class ServiceWorkerVersions;
 
 // A URLRequestJob derivative used to cache the main script
@@ -58,10 +59,6 @@
                            UpdateAfter24Hours);
   FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest,
                            UpdateForceBypassCache);
-  class NetDataConsumer;
-  class PassThroughConsumer;
-  class Comparer;
-  class Copier;
 
   ~ServiceWorkerWriteToCacheJob() override;
 
@@ -86,14 +83,12 @@
                                     int buf_size,
                                     int* bytes_read);
 
-  void CommitHeadersAndNotifyHeadersComplete();
-  void WriteHeaders(const base::Closure& callback);
-  void OnWriteHeadersComplete(const base::Closure& callback, int result);
-  void WriteData(net::IOBuffer* buf,
-                 int amount_to_write,
-                 const base::Callback<void(int result)>& callback);
-  void OnWriteDataComplete(const base::Callback<void(int result)>& callback,
-                           int result);
+  // Callbacks for writing headers and data via |cache_writer_|. Note that since
+  // the MaybeWriteHeaders and MaybeWriteData methods on |cache_writer_| are
+  // guaranteed not to do short writes, these functions only receive a
+  // net::Error indicating success or failure, not a count of bytes written.
+  void OnWriteHeadersComplete(net::Error error);
+  void OnWriteDataComplete(net::Error error);
 
   // net::URLRequest::Delegate overrides that observe the net request.
   void OnReceivedRedirect(net::URLRequest* request,
@@ -113,20 +108,23 @@
 
   bool CheckPathRestriction(net::URLRequest* request);
 
-  void SetPendingIO();
-  void ClearPendingIO();
-  void OnPassThroughComplete();
-  void OnCompareComplete(int bytes_matched, bool is_equal);
-  void CopyIncumbent(int bytes_to_copy);
-  void OnCopyComplete(ServiceWorkerStatusCode status);
+  // Writes network data back to the script cache if needed, and notifies the
+  // script cache of fetch completion at EOF. This function might need to do
+  // asynchronous IO; if so, it signals this through setting the URLRequestJob's
+  // status to IO_PENDING. After this function returns, if the URLRequestJob
+  // isn't IO_PENDING, all of the data in |io_buffer_| has been written back to
+  // the script cache if necessary.
   void HandleNetData(int bytes_read);
 
-  void AsyncNotifyDoneHelper(const net::URLRequestStatus& status,
-                             const std::string& status_message);
+  void NotifyDoneHelper(const net::URLRequestStatus& status,
+                        const std::string& status_message);
 
   void NotifyFinishedCaching(net::URLRequestStatus status,
                              const std::string& status_message);
 
+  scoped_ptr<ServiceWorkerResponseReader> CreateCacheResponseReader();
+  scoped_ptr<ServiceWorkerResponseWriter> CreateCacheResponseWriter();
+
   ResourceType resource_type_;  // Differentiate main script and imports
   scoped_refptr<net::IOBuffer> io_buffer_;
   int io_buffer_bytes_;
@@ -138,7 +136,7 @@
   scoped_ptr<net::HttpResponseInfo> http_info_;
   scoped_ptr<ServiceWorkerResponseWriter> writer_;
   scoped_refptr<ServiceWorkerVersion> version_;
-  scoped_ptr<NetDataConsumer> consumer_;
+  scoped_ptr<ServiceWorkerCacheWriter> cache_writer_;
   bool has_been_killed_;
   bool did_notify_started_;
   bool did_notify_finished_;
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index ceaf350..3ae9d56 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -1375,6 +1375,8 @@
       'browser/service_worker/embedded_worker_instance.h',
       'browser/service_worker/embedded_worker_registry.cc',
       'browser/service_worker/embedded_worker_registry.h',
+      'browser/service_worker/service_worker_cache_writer.cc',
+      'browser/service_worker/service_worker_cache_writer.h',
       'browser/service_worker/service_worker_context_core.cc',
       'browser/service_worker/service_worker_context_core.h',
       'browser/service_worker/service_worker_context_observer.h',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index a892f45..896b483f 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -558,6 +558,7 @@
       'browser/service_worker/embedded_worker_instance_unittest.cc',
       'browser/service_worker/embedded_worker_test_helper.cc',
       'browser/service_worker/embedded_worker_test_helper.h',
+      'browser/service_worker/service_worker_cache_writer_unittest.cc',
       'browser/service_worker/service_worker_context_request_handler_unittest.cc',
       'browser/service_worker/service_worker_context_unittest.cc',
       'browser/service_worker/service_worker_controllee_request_handler_unittest.cc',