This source file includes following definitions.
- HasPortData
- GetPortData
- ClearPortData
- ClearPortDataAndNotifyDispatcher
- PostMessage
- CloseChannel
- PortAddRef
- PortRelease
- Bind
- NearDeathCallback
- isolate_
- RunCallback
- BindToGC
- Get
- DispatchOnConnect
- DeliverMessage
- DispatchOnDisconnect
#include "chrome/renderer/extensions/messaging_bindings.h"
#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/message_loop/message_loop.h"
#include "base/values.h"
#include "chrome/common/extensions/manifest_handlers/externally_connectable.h"
#include "chrome/common/extensions/message_bundle.h"
#include "chrome/common/url_constants.h"
#include "chrome/renderer/extensions/chrome_v8_context.h"
#include "chrome/renderer/extensions/chrome_v8_context_set.h"
#include "chrome/renderer/extensions/chrome_v8_extension.h"
#include "chrome/renderer/extensions/dispatcher.h"
#include "chrome/renderer/extensions/event_bindings.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "content/public/renderer/v8_value_converter.h"
#include "extensions/common/api/messaging/message.h"
#include "extensions/common/extension_messages.h"
#include "extensions/renderer/scoped_persistent.h"
#include "grit/renderer_resources.h"
#include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
#include "third_party/WebKit/public/web/WebScopedWindowFocusAllowedIndicator.h"
#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "v8/include/v8.h"
using content::RenderThread;
using content::V8ValueConverter;
namespace extensions {
namespace {
struct ExtensionData {
struct PortData {
int ref_count;
PortData() : ref_count(0) {}
};
std::map<int, PortData> ports;
};
base::LazyInstance<ExtensionData> g_extension_data =
LAZY_INSTANCE_INITIALIZER;
bool HasPortData(int port_id) {
return g_extension_data.Get().ports.find(port_id) !=
g_extension_data.Get().ports.end();
}
ExtensionData::PortData& GetPortData(int port_id) {
return g_extension_data.Get().ports[port_id];
}
void ClearPortData(int port_id) {
g_extension_data.Get().ports.erase(port_id);
}
const char kPortClosedError[] = "Attempting to use a disconnected port object";
const char kReceivingEndDoesntExistError[] =
"Could not establish connection. Receiving end does not exist.";
class ExtensionImpl : public ChromeV8Extension {
public:
ExtensionImpl(Dispatcher* dispatcher, ChromeV8Context* context)
: ChromeV8Extension(dispatcher, context) {
RouteFunction("CloseChannel",
base::Bind(&ExtensionImpl::CloseChannel, base::Unretained(this)));
RouteFunction("PortAddRef",
base::Bind(&ExtensionImpl::PortAddRef, base::Unretained(this)));
RouteFunction("PortRelease",
base::Bind(&ExtensionImpl::PortRelease, base::Unretained(this)));
RouteFunction("PostMessage",
base::Bind(&ExtensionImpl::PostMessage, base::Unretained(this)));
RouteFunction("BindToGC",
base::Bind(&ExtensionImpl::BindToGC, base::Unretained(this)));
}
virtual ~ExtensionImpl() {}
void ClearPortDataAndNotifyDispatcher(int port_id) {
ClearPortData(port_id);
dispatcher()->ClearPortData(port_id);
}
void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
content::RenderView* renderview = GetRenderView();
if (!renderview)
return;
CHECK(args.Length() == 2 &&
args[0]->IsInt32() &&
args[1]->IsString());
int port_id = args[0]->Int32Value();
if (!HasPortData(port_id)) {
args.GetIsolate()->ThrowException(v8::Exception::Error(
v8::String::NewFromUtf8(args.GetIsolate(), kPortClosedError)));
return;
}
renderview->Send(new ExtensionHostMsg_PostMessage(
renderview->GetRoutingID(), port_id,
Message(*v8::String::Utf8Value(args[1]),
blink::WebUserGestureIndicator::isProcessingUserGesture())));
}
void CloseChannel(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_EQ(2, args.Length());
CHECK(args[0]->IsInt32());
CHECK(args[1]->IsBoolean());
int port_id = args[0]->Int32Value();
if (!HasPortData(port_id))
return;
bool notify_browser = args[1]->BooleanValue();
if (notify_browser) {
content::RenderThread::Get()->Send(
new ExtensionHostMsg_CloseChannel(port_id, std::string()));
}
ClearPortDataAndNotifyDispatcher(port_id);
}
void PortAddRef(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_EQ(1, args.Length());
CHECK(args[0]->IsInt32());
int port_id = args[0]->Int32Value();
++GetPortData(port_id).ref_count;
}
void PortRelease(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK_EQ(1, args.Length());
CHECK(args[0]->IsInt32());
int port_id = args[0]->Int32Value();
if (HasPortData(port_id) && --GetPortData(port_id).ref_count == 0) {
content::RenderThread::Get()->Send(
new ExtensionHostMsg_CloseChannel(port_id, std::string()));
ClearPortDataAndNotifyDispatcher(port_id);
}
}
class GCCallback {
public:
static void Bind(v8::Handle<v8::Object> object,
v8::Handle<v8::Function> callback,
v8::Isolate* isolate) {
GCCallback* cb = new GCCallback(object, callback, isolate);
cb->object_.SetWeak(cb, NearDeathCallback);
}
private:
static void NearDeathCallback(
const v8::WeakCallbackData<v8::Object, GCCallback>& data) {
data.GetParameter()->object_.reset();
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&GCCallback::RunCallback,
base::Owned(data.GetParameter())));
}
GCCallback(v8::Handle<v8::Object> object,
v8::Handle<v8::Function> callback,
v8::Isolate* isolate)
: object_(object), callback_(callback), isolate_(isolate) {}
void RunCallback() {
v8::HandleScope handle_scope(isolate_);
v8::Handle<v8::Function> callback = callback_.NewHandle(isolate_);
v8::Handle<v8::Context> context = callback->CreationContext();
if (context.IsEmpty())
return;
v8::Context::Scope context_scope(context);
blink::WebScopedMicrotaskSuppression suppression;
callback->Call(context->Global(), 0, NULL);
}
ScopedPersistent<v8::Object> object_;
ScopedPersistent<v8::Function> callback_;
v8::Isolate* isolate_;
DISALLOW_COPY_AND_ASSIGN(GCCallback);
};
void BindToGC(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(args.Length() == 2 && args[0]->IsObject() && args[1]->IsFunction());
GCCallback::Bind(args[0].As<v8::Object>(),
args[1].As<v8::Function>(),
args.GetIsolate());
}
};
}
ChromeV8Extension* MessagingBindings::Get(
Dispatcher* dispatcher,
ChromeV8Context* context) {
return new ExtensionImpl(dispatcher, context);
}
void MessagingBindings::DispatchOnConnect(
const ChromeV8ContextSet::ContextSet& contexts,
int target_port_id,
const std::string& channel_name,
const base::DictionaryValue& source_tab,
const std::string& source_extension_id,
const std::string& target_extension_id,
const GURL& source_url,
const std::string& tls_channel_id,
content::RenderView* restrict_to_render_view) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate);
scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
bool port_created = false;
std::string source_url_spec = source_url.spec();
for (ChromeV8ContextSet::ContextSet::const_iterator it = contexts.begin();
it != contexts.end(); ++it) {
if (restrict_to_render_view &&
restrict_to_render_view != (*it)->GetRenderView()) {
continue;
}
if ((*it)->v8_context().IsEmpty())
continue;
v8::Handle<v8::Value> tab = v8::Null(isolate);
v8::Handle<v8::Value> tls_channel_id_value = v8::Undefined(isolate);
const Extension* extension = (*it)->extension();
if (extension) {
if (!source_tab.empty() && !extension->is_platform_app())
tab = converter->ToV8Value(&source_tab, (*it)->v8_context());
ExternallyConnectableInfo* externally_connectable =
ExternallyConnectableInfo::Get(extension);
if (externally_connectable &&
externally_connectable->accepts_tls_channel_id) {
tls_channel_id_value =
v8::String::NewFromUtf8(isolate,
tls_channel_id.c_str(),
v8::String::kNormalString,
tls_channel_id.size());
}
}
v8::Handle<v8::Value> arguments[] = {
v8::Integer::New(isolate, target_port_id),
v8::String::NewFromUtf8(isolate,
channel_name.c_str(),
v8::String::kNormalString,
channel_name.size()),
tab,
v8::String::NewFromUtf8(isolate,
source_extension_id.c_str(),
v8::String::kNormalString,
source_extension_id.size()),
v8::String::NewFromUtf8(isolate,
target_extension_id.c_str(),
v8::String::kNormalString,
target_extension_id.size()),
v8::String::NewFromUtf8(isolate,
source_url_spec.c_str(),
v8::String::kNormalString,
source_url_spec.size()),
tls_channel_id_value,
};
v8::Handle<v8::Value> retval = (*it)->module_system()->CallModuleMethod(
"messaging",
"dispatchOnConnect",
arraysize(arguments), arguments);
if (retval.IsEmpty()) {
LOG(ERROR) << "Empty return value from dispatchOnConnect.";
continue;
}
CHECK(retval->IsBoolean());
port_created |= retval->BooleanValue();
}
if (!port_created) {
content::RenderThread::Get()->Send(
new ExtensionHostMsg_CloseChannel(
target_port_id, kReceivingEndDoesntExistError));
}
}
void MessagingBindings::DeliverMessage(
const ChromeV8ContextSet::ContextSet& contexts,
int target_port_id,
const Message& message,
content::RenderView* restrict_to_render_view) {
scoped_ptr<blink::WebScopedUserGesture> web_user_gesture;
scoped_ptr<blink::WebScopedWindowFocusAllowedIndicator> allow_window_focus;
if (message.user_gesture) {
web_user_gesture.reset(new blink::WebScopedUserGesture);
allow_window_focus.reset(new blink::WebScopedWindowFocusAllowedIndicator);
}
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate);
for (ChromeV8ContextSet::ContextSet::const_iterator it = contexts.begin();
it != contexts.end(); ++it) {
if (restrict_to_render_view &&
restrict_to_render_view != (*it)->GetRenderView()) {
continue;
}
if ((*it)->v8_context().IsEmpty())
continue;
v8::Handle<v8::Value> port_id_handle =
v8::Integer::New(isolate, target_port_id);
v8::Handle<v8::Value> has_port = (*it)->module_system()->CallModuleMethod(
"messaging",
"hasPort",
1, &port_id_handle);
CHECK(!has_port.IsEmpty());
if (!has_port->BooleanValue())
continue;
std::vector<v8::Handle<v8::Value> > arguments;
arguments.push_back(v8::String::NewFromUtf8(isolate,
message.data.c_str(),
v8::String::kNormalString,
message.data.size()));
arguments.push_back(port_id_handle);
(*it)->module_system()->CallModuleMethod("messaging",
"dispatchOnMessage",
&arguments);
}
}
void MessagingBindings::DispatchOnDisconnect(
const ChromeV8ContextSet::ContextSet& contexts,
int port_id,
const std::string& error_message,
content::RenderView* restrict_to_render_view) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handle_scope(isolate);
for (ChromeV8ContextSet::ContextSet::const_iterator it = contexts.begin();
it != contexts.end(); ++it) {
if (restrict_to_render_view &&
restrict_to_render_view != (*it)->GetRenderView()) {
continue;
}
if ((*it)->v8_context().IsEmpty())
continue;
std::vector<v8::Handle<v8::Value> > arguments;
arguments.push_back(v8::Integer::New(isolate, port_id));
if (!error_message.empty()) {
arguments.push_back(
v8::String::NewFromUtf8(isolate, error_message.c_str()));
} else {
arguments.push_back(v8::Null(isolate));
}
(*it)->module_system()->CallModuleMethod("messaging",
"dispatchOnDisconnect",
&arguments);
}
}
}