// Copyright 2012 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 "content/renderer/web_ui_extension.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "content/common/view_messages.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "content/public/renderer/v8_value_converter.h"
#include "content/renderer/web_ui_extension_data.h"
#include "gin/arguments.h"
#include "gin/function_template.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "url/gurl.h"
#include "v8/include/v8.h"
namespace content {
namespace {
bool ShouldRespondToRequest(
blink::WebFrame** frame_ptr,
RenderView** render_view_ptr) {
blink::WebFrame* frame = blink::WebLocalFrame::frameForCurrentContext();
if (!frame || !frame->view())
return false;
RenderView* render_view = RenderView::FromWebView(frame->view());
if (!render_view)
return false;
GURL frame_url = frame->document().url();
bool webui_enabled =
(render_view->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI) &&
(frame_url.SchemeIs(kChromeUIScheme) ||
frame_url.SchemeIs(url::kDataScheme));
if (!webui_enabled)
return false;
*frame_ptr = frame;
*render_view_ptr = render_view;
return true;
}
} // namespace
// Exposes two methods:
// - chrome.send: Used to send messages to the browser. Requires the message
// name as the first argument and can have an optional second argument that
// should be an array.
// - chrome.getVariableValue: Returns value for the input variable name if such
// a value was set by the browser. Else will return an empty string.
void WebUIExtension::Install(blink::WebFrame* frame) {
v8::Isolate* isolate = blink::mainThreadIsolate();
v8::HandleScope handle_scope(isolate);
v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
if (context.IsEmpty())
return;
v8::Context::Scope context_scope(context);
v8::Handle<v8::Object> global = context->Global();
v8::Handle<v8::Object> chrome =
global->Get(gin::StringToV8(isolate, "chrome"))->ToObject();
if (chrome.IsEmpty()) {
chrome = v8::Object::New(isolate);
global->Set(gin::StringToSymbol(isolate, "chrome"), chrome);
}
chrome->Set(gin::StringToSymbol(isolate, "send"),
gin::CreateFunctionTemplate(
isolate, base::Bind(&WebUIExtension::Send))->GetFunction());
chrome->Set(gin::StringToSymbol(isolate, "getVariableValue"),
gin::CreateFunctionTemplate(
isolate, base::Bind(&WebUIExtension::GetVariableValue))
->GetFunction());
}
// static
void WebUIExtension::Send(gin::Arguments* args) {
blink::WebFrame* frame;
RenderView* render_view;
if (!ShouldRespondToRequest(&frame, &render_view))
return;
std::string message;
if (!args->GetNext(&message)) {
args->ThrowError();
return;
}
// If they've provided an optional message parameter, convert that into a
// Value to send to the browser process.
scoped_ptr<base::ListValue> content;
if (args->PeekNext().IsEmpty() || args->PeekNext()->IsUndefined()) {
content.reset(new base::ListValue());
} else {
v8::Handle<v8::Object> obj;
if (!args->GetNext(&obj)) {
args->ThrowError();
return;
}
scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
base::Value* value =
converter->FromV8Value(obj, frame->mainWorldScriptContext());
base::ListValue* list = NULL;
value->GetAsList(&list);
DCHECK(list);
content.reset(list);
}
// Send the message up to the browser.
render_view->Send(new ViewHostMsg_WebUISend(render_view->GetRoutingID(),
frame->document().url(),
message,
*content));
}
// static
std::string WebUIExtension::GetVariableValue(const std::string& name) {
blink::WebFrame* frame;
RenderView* render_view;
if (!ShouldRespondToRequest(&frame, &render_view))
return std::string();
return WebUIExtensionData::Get(render_view)->GetValue(name);
}
} // namespace content