[Blobs] Refactor extensions BlobReader to no longer use blob URLs.

Instead just read the blob through the mojom Blob interface directly.
For now still relying on blob UUIDs, but ultimately code calling into
this should already have a BlobPtr rather than having to ever look it
up from a UUID.

Bug: 701851
Change-Id: If267c750368cc4a43d52d239b8462cc65d7214bb
Reviewed-on: https://chromium-review.googlesource.com/1101899
Reviewed-by: Ken Rockot <rockot@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Christian Dullweber <dullweber@chromium.org>
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568063}
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.cc b/content/browser/blob_storage/chrome_blob_storage_context.cc
index 17347db..c09c2876 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.cc
+++ b/content/browser/blob_storage/chrome_blob_storage_context.cc
@@ -197,6 +197,26 @@
       std::move(blob_url_loader_factory_ptr));
 }
 
+// static
+blink::mojom::BlobPtr ChromeBlobStorageContext::GetBlobPtr(
+    BrowserContext* browser_context,
+    const std::string& uuid) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  blink::mojom::BlobPtr blob_ptr;
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::BindOnce(
+          [](scoped_refptr<ChromeBlobStorageContext> context,
+             blink::mojom::BlobRequest request, const std::string& uuid) {
+            auto handle = context->context()->GetBlobDataFromUUID(uuid);
+            if (handle)
+              storage::BlobImpl::Create(std::move(handle), std::move(request));
+          },
+          base::WrapRefCounted(GetFor(browser_context)), MakeRequest(&blob_ptr),
+          uuid));
+  return blob_ptr;
+}
+
 ChromeBlobStorageContext::~ChromeBlobStorageContext() {}
 
 void ChromeBlobStorageContext::DeleteOnCorrectThread() const {
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.h b/content/browser/blob_storage/chrome_blob_storage_context.h
index 812a6a5..e22df5f 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.h
+++ b/content/browser/blob_storage/chrome_blob_storage_context.h
@@ -70,6 +70,10 @@
   URLLoaderFactoryForToken(BrowserContext* browser_context,
                            blink::mojom::BlobURLTokenPtr token);
 
+  // Must be called on the UI thread.
+  static blink::mojom::BlobPtr GetBlobPtr(BrowserContext* browser_context,
+                                          const std::string& uuid);
+
  protected:
   virtual ~ChromeBlobStorageContext();
 
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index 569e77c..ff199b3 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -349,6 +349,14 @@
 }
 
 // static
+blink::mojom::BlobPtr BrowserContext::GetBlobPtr(
+    BrowserContext* browser_context,
+    const std::string& uuid) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return ChromeBlobStorageContext::GetBlobPtr(browser_context, uuid);
+}
+
+// static
 void BrowserContext::DeliverPushMessage(
     BrowserContext* browser_context,
     const GURL& origin,
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h
index b5cad2b..faf278e 100644
--- a/content/public/browser/browser_context.h
+++ b/content/public/browser/browser_context.h
@@ -20,6 +20,7 @@
 #include "net/url_request/url_request_interceptor.h"
 #include "net/url_request/url_request_job_factory.h"
 #include "services/service_manager/embedder/embedded_service_info.h"
+#include "third_party/blink/public/mojom/blob/blob.mojom.h"
 
 #if !defined(OS_ANDROID)
 #include "content/public/browser/zoom_level_delegate.h"
@@ -147,6 +148,14 @@
   static BlobContextGetter GetBlobStorageContext(
       BrowserContext* browser_context);
 
+  // Returns a mojom::BlobPtr for a specific blob. If no blob exists with the
+  // given UUID, the BlobPtr pipe will close.
+  // This method should be called on the UI thread.
+  // TODO(mek): Blob UUIDs should be entirely internal to the blob system, so
+  // eliminate this method in favor of just passing around the BlobPtr directly.
+  static blink::mojom::BlobPtr GetBlobPtr(BrowserContext* browser_context,
+                                          const std::string& uuid);
+
   // Delivers a push message with |data| to the Service Worker identified by
   // |origin| and |service_worker_registration_id|.
   static void DeliverPushMessage(
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS
index 98b35d2..22492d9 100644
--- a/extensions/browser/DEPS
+++ b/extensions/browser/DEPS
@@ -32,6 +32,7 @@
   "+skia/ext/image_operations.h",
   "+third_party/leveldatabase",
   "+third_party/re2",
+  "+third_party/blink/public/mojom",
   "+third_party/blink/public/platform",
   "+third_party/blink/public/web",
   "+third_party/zlib/google",
diff --git a/extensions/browser/blob_reader.cc b/extensions/browser/blob_reader.cc
index d4a4e2da..6df12eb 100644
--- a/extensions/browser/blob_reader.cc
+++ b/extensions/browser/blob_reader.cc
@@ -7,57 +7,21 @@
 #include <limits>
 #include <utility>
 
-#include "base/format_macros.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/storage_partition.h"
-#include "net/http/http_request_headers.h"
-#include "net/http/http_response_headers.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_context_getter.h"
 
 BlobReader::BlobReader(content::BrowserContext* browser_context,
                        const std::string& blob_uuid,
                        BlobReadCallback callback)
-    : callback_(callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  GURL blob_url = GURL(std::string("blob:uuid/") + blob_uuid);
-  DCHECK(blob_url.is_valid());
+    : BlobReader(
+          content::BrowserContext::GetBlobPtr(browser_context, blob_uuid),
+          std::move(callback)) {}
 
-  net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("blob_reader", R"(
-        semantics {
-          sender: "BlobReader"
-          description:
-            "Blobs are used for a variety of use cases, and are basically "
-            "immutable blocks of data. See https://chromium.googlesource.com/"
-            "chromium/src/+/master/storage/browser/blob/README.md for an "
-            "explanation of blobs and their implementation in Chrome. These "
-            "can be created by scripts in a website, web platform features, or "
-            "internally in the browser."
-          trigger:
-            "Request for reading the contents of a blob."
-          data:
-            "A reference to a Blob, File, or CacheStorage entry created from "
-            "script, a web platform feature, or browser internals."
-          destination: LOCAL
-        }
-        policy {
-          cookies_allowed: NO
-          setting: "This feature cannot be disabled by settings."
-          policy_exception_justification:
-            "Not implemented. This is a local data fetch request and has no "
-            "network activity."
-        })");
-  fetcher_ = net::URLFetcher::Create(blob_url, net::URLFetcher::GET, this,
-                                     traffic_annotation);
-  fetcher_->SetRequestContext(
-      content::BrowserContext::GetDefaultStoragePartition(browser_context)
-          ->GetURLRequestContext());
+BlobReader::BlobReader(blink::mojom::BlobPtr blob, BlobReadCallback callback)
+    : callback_(std::move(callback)), blob_(std::move(blob)), binding_(this) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  blob_.set_connection_error_handler(
+      base::BindOnce(&BlobReader::Failed, base::Unretained(this)));
 }
 
 BlobReader::~BlobReader() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); }
@@ -68,28 +32,56 @@
   CHECK_GT(length, 0);
   CHECK_LE(offset, std::numeric_limits<int64_t>::max() - length);
 
-  net::HttpRequestHeaders headers;
-  headers.SetHeader(
-      net::HttpRequestHeaders::kRange,
-      base::StringPrintf("bytes=%" PRId64 "-%" PRId64, offset,
-                         offset + length - 1));
-  fetcher_->SetExtraRequestHeaders(headers.ToString());
+  read_range_ = Range{offset, length};
 }
 
 void BlobReader::Start() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  fetcher_->Start();
+  mojo::ScopedDataPipeProducerHandle producer_handle;
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  MojoResult result =
+      CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
+  if (result != MOJO_RESULT_OK) {
+    Failed();
+    return;
+  }
+  blink::mojom::BlobReaderClientPtr client_ptr;
+  binding_.Bind(MakeRequest(&client_ptr));
+  if (read_range_) {
+    blob_->ReadRange(read_range_->offset, read_range_->length,
+                     std::move(producer_handle), std::move(client_ptr));
+  } else {
+    blob_->ReadAll(std::move(producer_handle), std::move(client_ptr));
+  }
+  data_pipe_drainer_ =
+      std::make_unique<mojo::DataPipeDrainer>(this, std::move(consumer_handle));
 }
 
-// Overridden from net::URLFetcherDelegate.
-void BlobReader::OnURLFetchComplete(const net::URLFetcher* source) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  std::unique_ptr<std::string> response(new std::string);
-  int64_t first = 0, last = 0, length = 0;
-  source->GetResponseAsString(response.get());
-  if (source->GetResponseHeaders())
-    source->GetResponseHeaders()->GetContentRangeFor206(&first, &last, &length);
-  callback_.Run(std::move(response), length);
+void BlobReader::OnCalculatedSize(uint64_t total_size,
+                                  uint64_t expected_content_size) {
+  blob_length_ = total_size;
+  if (data_complete_)
+    Succeeded();
+}
 
+void BlobReader::OnDataAvailable(const void* data, size_t num_bytes) {
+  if (!blob_data_)
+    blob_data_ = std::make_unique<std::string>();
+  blob_data_->append(static_cast<const char*>(data), num_bytes);
+}
+
+void BlobReader::OnDataComplete() {
+  data_complete_ = true;
+  if (blob_length_)
+    Succeeded();
+}
+
+void BlobReader::Failed() {
+  std::move(callback_).Run(std::make_unique<std::string>(), 0);
+  delete this;
+}
+
+void BlobReader::Succeeded() {
+  std::move(callback_).Run(std::move(blob_data_), *blob_length_);
   delete this;
 }
diff --git a/extensions/browser/blob_reader.h b/extensions/browser/blob_reader.h
index 23083ec..126d6c5 100644
--- a/extensions/browser/blob_reader.h
+++ b/extensions/browser/blob_reader.h
@@ -12,32 +12,31 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "net/base/io_buffer.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/data_pipe_drainer.h"
+#include "third_party/blink/public/mojom/blob/blob.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
 class BrowserContext;
 }
 
-namespace net {
-class URLFetcher;
-}
-
-// This class may only be used from the UI thread.
-class BlobReader : public net::URLFetcherDelegate {
+// This class may only be used from the UI thread. It self-deletes when finished
+// reading.
+class BlobReader : public blink::mojom::BlobReaderClient,
+                   public mojo::DataPipeDrainer::Client {
  public:
   // |blob_data| contains the portion of the Blob requested. |blob_total_size|
   // is the total size of the Blob, and may be larger than |blob_data->size()|.
   // |blob_total_size| is -1 if it cannot be determined.
-  typedef base::Callback<void(std::unique_ptr<std::string> blob_data,
-                              int64_t blob_total_size)>
+  typedef base::OnceCallback<void(std::unique_ptr<std::string> blob_data,
+                                  int64_t blob_total_size)>
       BlobReadCallback;
 
   BlobReader(content::BrowserContext* browser_context,
              const std::string& blob_uuid,
              BlobReadCallback callback);
+  BlobReader(blink::mojom::BlobPtr blob, BlobReadCallback callback);
   ~BlobReader() override;
 
   void SetByteRange(int64_t offset, int64_t length);
@@ -45,11 +44,32 @@
   void Start();
 
  private:
-  // Overridden from net::URLFetcherDelegate.
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
+  // blink::mojom::BlobReaderClient:
+  void OnCalculatedSize(uint64_t total_size,
+                        uint64_t expected_content_size) override;
+  void OnComplete(int32_t status, uint64_t data_length) override {}
+
+  // mojo::DataPipeDrainer:
+  void OnDataAvailable(const void* data, size_t num_bytes) override;
+  void OnDataComplete() override;
+
+  void Failed();
+  void Succeeded();
 
   BlobReadCallback callback_;
-  std::unique_ptr<net::URLFetcher> fetcher_;
+  blink::mojom::BlobPtr blob_;
+  struct Range {
+    uint64_t offset;
+    uint64_t length;
+  };
+  base::Optional<Range> read_range_;
+
+  mojo::Binding<blink::mojom::BlobReaderClient> binding_;
+  std::unique_ptr<mojo::DataPipeDrainer> data_pipe_drainer_;
+
+  base::Optional<uint64_t> blob_length_;
+  std::unique_ptr<std::string> blob_data_;
+  bool data_complete_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(BlobReader);
 };
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index ddf6c29..dc18810 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -28,7 +28,7 @@
  <item id="blink_extension_resource_loader" hash_code="84165821" type="0" content_hash_code="3695143" os_list="linux,windows" file_path="content/renderer/loader/web_url_loader_impl.cc"/>
  <item id="blink_resource_loader" hash_code="101845102" type="0" content_hash_code="75331172" os_list="linux,windows" file_path="content/renderer/loader/web_url_loader_impl.cc"/>
  <item id="blob_read" hash_code="112303907" type="0" content_hash_code="135449692" os_list="linux,windows" file_path="storage/browser/blob/blob_url_request_job_factory.cc"/>
- <item id="blob_reader" hash_code="5154306" type="0" content_hash_code="39702178" os_list="linux,windows" file_path="extensions/browser/blob_reader.cc"/>
+ <item id="blob_reader" hash_code="5154306" type="0" deprecated="2018-06-14" content_hash_code="39702178" file_path="extensions/browser/blob_reader.cc"/>
  <item id="bluetooth_socket" hash_code="94099818" type="0" content_hash_code="30932349" os_list="linux,windows" file_path="device/bluetooth/bluetooth_socket_net.cc"/>
  <item id="brandcode_config" hash_code="109679553" type="0" content_hash_code="128843792" os_list="linux,windows" file_path="chrome/browser/profile_resetter/brandcode_config_fetcher.cc"/>
  <item id="captive_portal_service" hash_code="88754904" type="0" content_hash_code="70737580" os_list="linux,windows" file_path="chrome/browser/captive_portal/captive_portal_service.cc"/>