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) { ...@@ -386,18 +386,39 @@ void UIThreadExtensionFunction::SendResponse(bool success) {
delegate_->OnSendResponse(this, success, bad_message_); delegate_->OnSendResponse(this, success, bad_message_);
else else
SendResponseImpl(success); 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( void UIThreadExtensionFunction::WriteToConsole(
content::ConsoleMessageLevel level, content::ConsoleMessageLevel level,
const std::string& message) { const std::string& message) {
if (render_view_host_) { GetIPCSender()->Send(
render_view_host_->Send(new ExtensionMsg_AddMessageToConsole( new ExtensionMsg_AddMessageToConsole(GetRoutingID(), level, message));
render_view_host_->GetRoutingID(), level, message)); }
} else {
render_frame_host_->Send(new ExtensionMsg_AddMessageToConsole( IPC::Sender* UIThreadExtensionFunction::GetIPCSender() {
render_frame_host_->GetRoutingID(), level, message)); 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() IOThreadExtensionFunction::IOThreadExtensionFunction()
......
...@@ -44,6 +44,10 @@ class ExtensionMessageFilter; ...@@ -44,6 +44,10 @@ class ExtensionMessageFilter;
class QuotaLimitHeuristic; class QuotaLimitHeuristic;
} }
namespace IPC {
class Sender;
}
#ifdef NDEBUG #ifdef NDEBUG
#define EXTENSION_FUNCTION_VALIDATE(test) \ #define EXTENSION_FUNCTION_VALIDATE(test) \
do { \ do { \
...@@ -441,6 +445,9 @@ class UIThreadExtensionFunction : public ExtensionFunction { ...@@ -441,6 +445,9 @@ class UIThreadExtensionFunction : public ExtensionFunction {
virtual void SendResponse(bool success) OVERRIDE; 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. // The dispatcher that will service this extension function call.
base::WeakPtr<extensions::ExtensionFunctionDispatcher> dispatcher_; base::WeakPtr<extensions::ExtensionFunctionDispatcher> dispatcher_;
...@@ -460,9 +467,16 @@ class UIThreadExtensionFunction : public ExtensionFunction { ...@@ -460,9 +467,16 @@ class UIThreadExtensionFunction : public ExtensionFunction {
virtual void Destruct() const OVERRIDE; virtual void Destruct() const OVERRIDE;
// TODO(tommycli): Remove once RenderViewHost is gone.
IPC::Sender* GetIPCSender();
int GetRoutingID();
scoped_ptr<RenderHostTracker> tracker_; scoped_ptr<RenderHostTracker> tracker_;
DelegateForTests* delegate_; 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 // Extension functions that run on the IO thread. This type of function avoids
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
#include "content/public/browser/resource_dispatcher_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/event_router.h"
#include "extensions/browser/extension_function_dispatcher.h" #include "extensions/browser/extension_function_dispatcher.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
...@@ -44,6 +45,7 @@ void ExtensionMessageFilter::OverrideThreadForMessage( ...@@ -44,6 +45,7 @@ void ExtensionMessageFilter::OverrideThreadForMessage(
case ExtensionHostMsg_RemoveFilteredListener::ID: case ExtensionHostMsg_RemoveFilteredListener::ID:
case ExtensionHostMsg_ShouldSuspendAck::ID: case ExtensionHostMsg_ShouldSuspendAck::ID:
case ExtensionHostMsg_SuspendAck::ID: case ExtensionHostMsg_SuspendAck::ID:
case ExtensionHostMsg_TransferBlobsAck::ID:
*thread = BrowserThread::UI; *thread = BrowserThread::UI;
break; break;
default: default:
...@@ -70,6 +72,8 @@ bool ExtensionMessageFilter::OnMessageReceived(const IPC::Message& message) { ...@@ -70,6 +72,8 @@ bool ExtensionMessageFilter::OnMessageReceived(const IPC::Message& message) {
OnExtensionShouldSuspendAck) OnExtensionShouldSuspendAck)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_SuspendAck, IPC_MESSAGE_HANDLER(ExtensionHostMsg_SuspendAck,
OnExtensionSuspendAck) OnExtensionSuspendAck)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_TransferBlobsAck,
OnExtensionTransferBlobsAck)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_GenerateUniqueID, IPC_MESSAGE_HANDLER(ExtensionHostMsg_GenerateUniqueID,
OnExtensionGenerateUniqueID) OnExtensionGenerateUniqueID)
IPC_MESSAGE_HANDLER(ExtensionHostMsg_ResumeRequests, IPC_MESSAGE_HANDLER(ExtensionHostMsg_ResumeRequests,
...@@ -167,6 +171,14 @@ void ExtensionMessageFilter::OnExtensionSuspendAck( ...@@ -167,6 +171,14 @@ void ExtensionMessageFilter::OnExtensionSuspendAck(
process_manager->OnSuspendAck(extension_id); 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) { void ExtensionMessageFilter::OnExtensionGenerateUniqueID(int* unique_id) {
static int next_unique_id = 0; static int next_unique_id = 0;
*unique_id = ++next_unique_id; *unique_id = ++next_unique_id;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define EXTENSIONS_BROWSER_EXTENSION_MESSAGE_FILTER_H_ #define EXTENSIONS_BROWSER_EXTENSION_MESSAGE_FILTER_H_
#include <string> #include <string>
#include <vector>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
...@@ -65,6 +66,7 @@ class ExtensionMessageFilter : public content::BrowserMessageFilter { ...@@ -65,6 +66,7 @@ class ExtensionMessageFilter : public content::BrowserMessageFilter {
void OnExtensionShouldSuspendAck(const std::string& extension_id, void OnExtensionShouldSuspendAck(const std::string& extension_id,
int sequence_id); int sequence_id);
void OnExtensionSuspendAck(const std::string& extension_id); void OnExtensionSuspendAck(const std::string& extension_id);
void OnExtensionTransferBlobsAck(const std::vector<std::string>& blob_uuids);
// Message handlers on the IO thread. // Message handlers on the IO thread.
void OnExtensionGenerateUniqueID(int* unique_id); void OnExtensionGenerateUniqueID(int* unique_id);
......
...@@ -455,6 +455,14 @@ IPC_MESSAGE_ROUTED0(ExtensionMsg_AppWindowClosed) ...@@ -455,6 +455,14 @@ IPC_MESSAGE_ROUTED0(ExtensionMsg_AppWindowClosed)
IPC_MESSAGE_CONTROL1(ExtensionMsg_WatchPages, IPC_MESSAGE_CONTROL1(ExtensionMsg_WatchPages,
std::vector<std::string> /* CSS selectors */) 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. // Messages sent from the renderer to the browser.
// A renderer sends this message when an extension process starts an API // A renderer sends this message when an extension process starts an API
...@@ -642,3 +650,7 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_AddDOMActionToActivityLog, ...@@ -642,3 +650,7 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_AddDOMActionToActivityLog,
// to change. // to change.
IPC_MESSAGE_ROUTED1(ExtensionHostMsg_OnWatchedPageChange, IPC_MESSAGE_ROUTED1(ExtensionHostMsg_OnWatchedPageChange,
std::vector<std::string> /* Matching CSS selectors */) 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 @@ ...@@ -311,6 +311,8 @@
'browser/api_activity_monitor.h', 'browser/api_activity_monitor.h',
'browser/app_sorting.h', 'browser/app_sorting.h',
'browser/blacklist_state.h', 'browser/blacklist_state.h',
'browser/blob_holder.cc',
'browser/blob_holder.h',
'browser/browser_context_keyed_api_factory.h', 'browser/browser_context_keyed_api_factory.h',
'browser/browser_context_keyed_service_factories.cc', 'browser/browser_context_keyed_service_factories.cc',
'browser/browser_context_keyed_service_factories.h', 'browser/browser_context_keyed_service_factories.h',
......
...@@ -19,6 +19,24 @@ void GetBlobUuid(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -19,6 +19,24 @@ void GetBlobUuid(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::NewFromUtf8(args.GetIsolate(), blob.uuid().utf8().data())); 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
namespace extensions { namespace extensions {
...@@ -26,6 +44,7 @@ namespace extensions { ...@@ -26,6 +44,7 @@ namespace extensions {
BlobNativeHandler::BlobNativeHandler(ScriptContext* context) BlobNativeHandler::BlobNativeHandler(ScriptContext* context)
: ObjectBackedNativeHandler(context) { : ObjectBackedNativeHandler(context) {
RouteFunction("GetBlobUuid", base::Bind(&GetBlobUuid)); RouteFunction("GetBlobUuid", base::Bind(&GetBlobUuid));
RouteFunction("TakeBrowserProcessBlob", base::Bind(&TakeBrowserProcessBlob));
} }
} // namespace extensions } // namespace extensions
...@@ -461,6 +461,7 @@ bool Dispatcher::OnControlMessageReceived(const IPC::Message& message) { ...@@ -461,6 +461,7 @@ bool Dispatcher::OnControlMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(ExtensionMsg_SetSystemFont, OnSetSystemFont) IPC_MESSAGE_HANDLER(ExtensionMsg_SetSystemFont, OnSetSystemFont)
IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldSuspend, OnShouldSuspend) IPC_MESSAGE_HANDLER(ExtensionMsg_ShouldSuspend, OnShouldSuspend)
IPC_MESSAGE_HANDLER(ExtensionMsg_Suspend, OnSuspend) IPC_MESSAGE_HANDLER(ExtensionMsg_Suspend, OnSuspend)
IPC_MESSAGE_HANDLER(ExtensionMsg_TransferBlobs, OnTransferBlobs)
IPC_MESSAGE_HANDLER(ExtensionMsg_Unloaded, OnUnloaded) IPC_MESSAGE_HANDLER(ExtensionMsg_Unloaded, OnUnloaded)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions) IPC_MESSAGE_HANDLER(ExtensionMsg_UpdatePermissions, OnUpdatePermissions)
IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions, IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateTabSpecificPermissions,
...@@ -680,6 +681,10 @@ void Dispatcher::OnSuspend(const std::string& extension_id) { ...@@ -680,6 +681,10 @@ void Dispatcher::OnSuspend(const std::string& extension_id) {
RenderThread::Get()->Send(new ExtensionHostMsg_SuspendAck(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) { void Dispatcher::OnUnloaded(const std::string& id) {
extensions_.Remove(id); extensions_.Remove(id);
active_extension_ids_.erase(id); active_extension_ids_.erase(id);
......
...@@ -173,6 +173,7 @@ class Dispatcher : public content::RenderProcessObserver { ...@@ -173,6 +173,7 @@ class Dispatcher : public content::RenderProcessObserver {
const std::string& font_size); const std::string& font_size);
void OnShouldSuspend(const std::string& extension_id, int sequence_id); void OnShouldSuspend(const std::string& extension_id, int sequence_id);
void OnSuspend(const std::string& extension_id); void OnSuspend(const std::string& extension_id);
void OnTransferBlobs(const std::vector<std::string>& blob_uuids);
void OnUnloaded(const std::string& id); void OnUnloaded(const std::string& id);
void OnUpdatePermissions(const ExtensionMsg_UpdatePermissions_Params& params); void OnUpdatePermissions(const ExtensionMsg_UpdatePermissions_Params& params);
void OnUpdateTabSpecificPermissions(int page_id, 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