Blobs: Catching browser-process created Blobs in extension code.

This is a spinoff of https://codereview.chromium.org/266373006/, Patchset 11.

BUG=304290

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274268 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/extensions/browser/blob_holder.cc b/extensions/browser/blob_holder.cc
new file mode 100644
index 0000000..5f73953
--- /dev/null
+++ b/extensions/browser/blob_holder.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/blob_holder.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "content/public/browser/blob_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+
+namespace extensions {
+
+namespace {
+
+// Address to this variable used as the user data key.
+const int kBlobHolderUserDataKey = 0;
+}
+
+// static
+BlobHolder* BlobHolder::FromRenderProcessHost(
+    content::RenderProcessHost* render_process_host) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(render_process_host);
+  BlobHolder* existing = static_cast<BlobHolder*>(
+      render_process_host->GetUserData(&kBlobHolderUserDataKey));
+
+  if (existing)
+    return existing;
+
+  BlobHolder* new_instance = new BlobHolder(render_process_host);
+  render_process_host->SetUserData(&kBlobHolderUserDataKey, new_instance);
+  return new_instance;
+}
+
+BlobHolder::~BlobHolder() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+void BlobHolder::HoldBlobReference(scoped_ptr<content::BlobHandle> blob) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!ContainsBlobHandle(blob.get()));
+
+  held_blobs_.insert(
+      make_pair(blob->GetUUID(), make_linked_ptr(blob.release())));
+}
+
+BlobHolder::BlobHolder(content::RenderProcessHost* render_process_host)
+    : render_process_host_(render_process_host) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
+bool BlobHolder::ContainsBlobHandle(content::BlobHandle* handle) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  for (BlobHandleMultimap::const_iterator it = held_blobs_.begin();
+       it != held_blobs_.end();
+       ++it) {
+    if (it->second.get() == handle)
+      return true;
+  }
+
+  return false;
+}
+
+void BlobHolder::DropBlobs(const std::vector<std::string>& blob_uuids) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  for (std::vector<std::string>::const_iterator uuid_it = blob_uuids.begin();
+       uuid_it != blob_uuids.end();
+       ++uuid_it) {
+    BlobHandleMultimap::iterator it = held_blobs_.find(*uuid_it);
+
+    if (it != held_blobs_.end()) {
+      held_blobs_.erase(it);
+    } else {
+      DLOG(ERROR) << "Tried to release a Blob we don't have ownership to."
+                  << "UUID: " << *uuid_it;
+      render_process_host_->ReceivedBadMessage();
+    }
+  }
+}
+
+}  // namespace extensions
diff --git a/extensions/browser/blob_holder.h b/extensions/browser/blob_holder.h
new file mode 100644
index 0000000..9a1d8f51
--- /dev/null
+++ b/extensions/browser/blob_holder.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_BLOB_HOLDER_H_
+#define EXTENSIONS_BROWSER_BLOB_HOLDER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/supports_user_data.h"
+
+namespace content {
+class BlobHandle;
+class RenderProcessHost;
+}
+
+namespace extensions {
+
+class ExtensionMessageFilter;
+
+// Used for holding onto Blobs created into the browser process until a
+// renderer takes over ownership. This class operates on the UI thread.
+class BlobHolder : public base::SupportsUserData::Data {
+ public:
+  // Will create the BlobHolder if it doesn't already exist.
+  static BlobHolder* FromRenderProcessHost(
+      content::RenderProcessHost* render_process_host);
+
+  virtual ~BlobHolder();
+
+  // Causes BlobHolder to take ownership of |blob|.
+  void HoldBlobReference(scoped_ptr<content::BlobHandle> blob);
+
+ private:
+  typedef std::multimap<std::string, linked_ptr<content::BlobHandle> >
+      BlobHandleMultimap;
+
+  explicit BlobHolder(content::RenderProcessHost* render_process_host);
+
+  // BlobHolder will drop a blob handle for each element in blob_uuids.
+  // If caller wishes to drop multiple references to the same blob,
+  // |blob_uuids| may contain duplicate UUIDs.
+  void DropBlobs(const std::vector<std::string>& blob_uuids);
+  friend class ExtensionMessageFilter;
+
+  bool ContainsBlobHandle(content::BlobHandle* handle) const;
+
+  // A reference to the owner of this class.
+  content::RenderProcessHost* render_process_host_;
+
+  BlobHandleMultimap held_blobs_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlobHolder);
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_BROWSER_BLOB_HOLDER_H_
diff --git a/extensions/browser/extension_function.cc b/extensions/browser/extension_function.cc
index 7d092053..15e92cc 100644
--- a/extensions/browser/extension_function.cc
+++ b/extensions/browser/extension_function.cc
@@ -386,18 +386,39 @@
     delegate_->OnSendResponse(this, success, bad_message_);
   else
     SendResponseImpl(success);
+
+  if (!transferred_blob_uuids_.empty()) {
+    DCHECK(!delegate_) << "Blob transfer not supported with test delegate.";
+    GetIPCSender()->Send(
+        new ExtensionMsg_TransferBlobs(transferred_blob_uuids_));
+  }
+}
+
+void UIThreadExtensionFunction::SetTransferredBlobUUIDs(
+    const std::vector<std::string>& blob_uuids) {
+  DCHECK(transferred_blob_uuids_.empty());  // Should only be called once.
+  transferred_blob_uuids_ = blob_uuids;
 }
 
 void UIThreadExtensionFunction::WriteToConsole(
     content::ConsoleMessageLevel level,
     const std::string& message) {
-  if (render_view_host_) {
-    render_view_host_->Send(new ExtensionMsg_AddMessageToConsole(
-        render_view_host_->GetRoutingID(), level, message));
-  } else {
-    render_frame_host_->Send(new ExtensionMsg_AddMessageToConsole(
-        render_frame_host_->GetRoutingID(), level, message));
-  }
+  GetIPCSender()->Send(
+      new ExtensionMsg_AddMessageToConsole(GetRoutingID(), level, message));
+}
+
+IPC::Sender* UIThreadExtensionFunction::GetIPCSender() {
+  if (render_view_host_)
+    return render_view_host_;
+  else
+    return render_frame_host_;
+}
+
+int UIThreadExtensionFunction::GetRoutingID() {
+  if (render_view_host_)
+    return render_view_host_->GetRoutingID();
+  else
+    return render_frame_host_->GetRoutingID();
 }
 
 IOThreadExtensionFunction::IOThreadExtensionFunction()
diff --git a/extensions/browser/extension_function.h b/extensions/browser/extension_function.h
index 0116193..aef934a 100644
--- a/extensions/browser/extension_function.h
+++ b/extensions/browser/extension_function.h
@@ -44,6 +44,10 @@
 class QuotaLimitHeuristic;
 }
 
+namespace IPC {
+class Sender;
+}
+
 #ifdef NDEBUG
 #define EXTENSION_FUNCTION_VALIDATE(test) \
   do {                                    \
@@ -441,6 +445,9 @@
 
   virtual void SendResponse(bool success) OVERRIDE;
 
+  // Sets the Blob UUIDs whose ownership is being transferred to the renderer.
+  void SetTransferredBlobUUIDs(const std::vector<std::string>& blob_uuids);
+
   // The dispatcher that will service this extension function call.
   base::WeakPtr<extensions::ExtensionFunctionDispatcher> dispatcher_;
 
@@ -460,9 +467,16 @@
 
   virtual void Destruct() const OVERRIDE;
 
+  // TODO(tommycli): Remove once RenderViewHost is gone.
+  IPC::Sender* GetIPCSender();
+  int GetRoutingID();
+
   scoped_ptr<RenderHostTracker> tracker_;
 
   DelegateForTests* delegate_;
+
+  // The blobs transferred to the renderer process.
+  std::vector<std::string> transferred_blob_uuids_;
 };
 
 // Extension functions that run on the IO thread. This type of function avoids
diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc
index 3cf93f0..e25480d 100644
--- a/extensions/browser/extension_message_filter.cc
+++ b/extensions/browser/extension_message_filter.cc
@@ -7,6 +7,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/resource_dispatcher_host.h"
+#include "extensions/browser/blob_holder.h"
 #include "extensions/browser/event_router.h"
 #include "extensions/browser/extension_function_dispatcher.h"
 #include "extensions/browser/extension_system.h"
@@ -44,6 +45,7 @@
     case ExtensionHostMsg_RemoveFilteredListener::ID:
     case ExtensionHostMsg_ShouldSuspendAck::ID:
     case ExtensionHostMsg_SuspendAck::ID:
+    case ExtensionHostMsg_TransferBlobsAck::ID:
       *thread = BrowserThread::UI;
       break;
     default:
@@ -70,6 +72,8 @@
                         OnExtensionShouldSuspendAck)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_SuspendAck,
                         OnExtensionSuspendAck)
+    IPC_MESSAGE_HANDLER(ExtensionHostMsg_TransferBlobsAck,
+                        OnExtensionTransferBlobsAck)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_GenerateUniqueID,
                         OnExtensionGenerateUniqueID)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_ResumeRequests,
@@ -167,6 +171,14 @@
     process_manager->OnSuspendAck(extension_id);
 }
 
+void ExtensionMessageFilter::OnExtensionTransferBlobsAck(
+    const std::vector<std::string>& blob_uuids) {
+  RenderProcessHost* process = RenderProcessHost::FromID(render_process_id_);
+  if (!process)
+    return;
+  BlobHolder::FromRenderProcessHost(process)->DropBlobs(blob_uuids);
+}
+
 void ExtensionMessageFilter::OnExtensionGenerateUniqueID(int* unique_id) {
   static int next_unique_id = 0;
   *unique_id = ++next_unique_id;
diff --git a/extensions/browser/extension_message_filter.h b/extensions/browser/extension_message_filter.h
index 26060c5..3305794 100644
--- a/extensions/browser/extension_message_filter.h
+++ b/extensions/browser/extension_message_filter.h
@@ -6,6 +6,7 @@
 #define EXTENSIONS_BROWSER_EXTENSION_MESSAGE_FILTER_H_
 
 #include <string>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
@@ -65,6 +66,7 @@
   void OnExtensionShouldSuspendAck(const std::string& extension_id,
                                    int sequence_id);
   void OnExtensionSuspendAck(const std::string& extension_id);
+  void OnExtensionTransferBlobsAck(const std::vector<std::string>& blob_uuids);
 
   // Message handlers on the IO thread.
   void OnExtensionGenerateUniqueID(int* unique_id);