Commit c0b5eb0b authored by tommycli@chromium.org's avatar tommycli@chromium.org

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
parent 78d46959
// 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
// 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_
......@@ -386,18 +386,39 @@ void UIThreadExtensionFunction::SendResponse(bool success) {
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()
......
......@@ -44,6 +44,10 @@ class ExtensionMessageFilter;
class QuotaLimitHeuristic;
}
namespace IPC {
class Sender;
}
#ifdef NDEBUG
#define EXTENSION_FUNCTION_VALIDATE(test) \
do { \
......@@ -441,6 +445,9 @@ class UIThreadExtensionFunction : public ExtensionFunction {
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 @@ class UIThreadExtensionFunction : public ExtensionFunction {
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
......
......@@ -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 @@ void ExtensionMessageFilter::OverrideThreadForMessage(
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 @@ bool ExtensionMessageFilter::OnMessageReceived(const IPC::Message& message) {
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 @@ void ExtensionMessageFilter::OnExtensionSuspendAck(
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;
......
......@@ -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 @@ class ExtensionMessageFilter : public content::BrowserMessageFilter {
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);
......
......@@ -455,6 +455,14 @@ IPC_MESSAGE_ROUTED0(ExtensionMsg_AppWindowClosed)
IPC_MESSAGE_CONTROL1(ExtensionMsg_WatchPages,
std::vector<std::string> /* CSS selectors */)
// Send by the browser to indicate a Blob handle has been transferred to the
// renderer. This is sent after the actual extension response, and depends on
// the sequential nature of IPCs so that the blob has already been caught.
// This is a separate control message, so that the renderer process will send
// an acknowledgement even if the RenderView has closed or navigated away.
IPC_MESSAGE_CONTROL1(ExtensionMsg_TransferBlobs,
std::vector<std::string> /* blob_uuids */)
// Messages sent from the renderer to the browser.
// A renderer sends this message when an extension process starts an API
......@@ -642,3 +650,7 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_AddDOMActionToActivityLog,
// to change.
IPC_MESSAGE_ROUTED1(ExtensionHostMsg_OnWatchedPageChange,
std::vector<std::string> /* Matching CSS selectors */)
// Sent by the renderer when it has received a Blob handle from the browser.
IPC_MESSAGE_CONTROL1(ExtensionHostMsg_TransferBlobsAck,
std::vector<std::string> /* blob_uuids */)
......@@ -311,6 +311,8 @@
'browser/api_activity_monitor.h',
'browser/app_sorting.h',
'browser/blacklist_state.h',
'browser/blob_holder.cc',
'browser/blob_holder.h',
'browser/browser_context_keyed_api_factory.h',
'browser/browser_context_keyed_service_factories.cc',
'browser/browser_context_keyed_service_factories.h',
......
......@@ -19,6 +19,24 @@ void GetBlobUuid(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::NewFromUtf8(args.GetIsolate(), blob.uuid().utf8().data()));
}
// Take ownership of a Blob created on the browser process. Expects the Blob's
// UUID, type, and size as arguments. Returns the Blob we just took to
// Javascript. The Blob reference in the browser process is dropped through
// a separate flow to avoid leaking Blobs if the script context is destroyed.
void TakeBrowserProcessBlob(const v8::FunctionCallbackInfo<v8::Value>& args) {
DCHECK_EQ(3, args.Length());
DCHECK(args[0]->IsString());
DCHECK(args[1]->IsString());
DCHECK(args[2]->IsInt32());
std::string uuid(*v8::String::Utf8Value(args[0]->ToString()));
std::string type(*v8::String::Utf8Value(args[1]->ToString()));
blink::WebBlob blob =
blink::WebBlob::createFromUUID(blink::WebString::fromUTF8(uuid),
blink::WebString::fromUTF8(type),
args[2]->Int32Value());
args.GetReturnValue().Set(blob.toV8Value());
}
} // namespace
namespace extensions {
......@@ -26,6 +44,7 @@ namespace extensions {
BlobNativeHandler::BlobNativeHandler(ScriptContext* context)
: ObjectBackedNativeHandler(context) {
RouteFunction("GetBlobUuid", base::Bind(&GetBlobUuid));
RouteFunction("TakeBrowserProcessBlob", base::Bind(&TakeBrowserProcessBlob));
}
} // namespace extensions
......@@ -461,6 +461,7 @@ bool Dispatcher::OnControlMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(ExtensionMsg_SetSystemFont, OnSetSystemFont)
IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldSuspend, OnShouldSuspend)
IPC_MESSAGE_HANDLER(ExtensionMsg_Suspend, OnSuspend)
IPC_MESSAGE_HANDLER(ExtensionMsg_TransferBlobs, OnTransferBlobs)
IPC_MESSAGE_HANDLER(ExtensionMsg_Unloaded, OnUnloaded)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions,
......@@ -680,6 +681,10 @@ void Dispatcher::OnSuspend(const std::string& extension_id) {
RenderThread::Get()->Send(new ExtensionHostMsg_SuspendAck(extension_id));
}
void Dispatcher::OnTransferBlobs(const std::vector<std::string>& blob_uuids) {
RenderThread::Get()->Send(new ExtensionHostMsg_TransferBlobsAck(blob_uuids));
}
void Dispatcher::OnUnloaded(const std::string& id) {
extensions_.Remove(id);
active_extension_ids_.erase(id);
......
......@@ -173,6 +173,7 @@ class Dispatcher : public content::RenderProcessObserver {
const std::string& font_size);
void OnShouldSuspend(const std::string& extension_id, int sequence_id);
void OnSuspend(const std::string& extension_id);
void OnTransferBlobs(const std::vector<std::string>& blob_uuids);
void OnUnloaded(const std::string& id);
void OnUpdatePermissions(const ExtensionMsg_UpdatePermissions_Params& params);
void OnUpdateTabSpecificPermissions(int page_id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment