// 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 "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebView.h" #include "url/gurl.h" #include "v8/include/v8.h" namespace content { static const char* const kWebUIExtensionName = "v8/WebUI"; // Javascript that gets executed when the extension loads into all frames. // 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. static const char* const kWebUIExtensionJS = "var chrome;" "if (!chrome)" " chrome = {};" "chrome.send = function(name, data) {" " native function Send();" " Send(name, data);" "};" "chrome.getVariableValue = function(name) {" " native function GetVariableValue();" " return GetVariableValue(name);" "};"; class WebUIExtensionWrapper : public v8::Extension { public: WebUIExtensionWrapper(); virtual ~WebUIExtensionWrapper(); virtual v8::Handle GetNativeFunctionTemplate( v8::Isolate* isolate, v8::Handle name) OVERRIDE; static void Send(const v8::FunctionCallbackInfo& args); static void GetVariableValue(const v8::FunctionCallbackInfo& args); private: static bool ShouldRespondToRequest(blink::WebFrame** frame_ptr, RenderView** render_view_ptr); DISALLOW_COPY_AND_ASSIGN(WebUIExtensionWrapper); }; WebUIExtensionWrapper::WebUIExtensionWrapper() : v8::Extension(kWebUIExtensionName, kWebUIExtensionJS) {} WebUIExtensionWrapper::~WebUIExtensionWrapper() {} v8::Handle WebUIExtensionWrapper::GetNativeFunctionTemplate(v8::Isolate* isolate, v8::Handle name) { if (name->Equals(v8::String::NewFromUtf8(isolate, "Send"))) return v8::FunctionTemplate::New(isolate, Send); if (name->Equals(v8::String::NewFromUtf8(isolate, "GetVariableValue"))) return v8::FunctionTemplate::New(isolate, GetVariableValue); return v8::Handle(); } // static bool WebUIExtensionWrapper::ShouldRespondToRequest( blink::WebFrame** frame_ptr, RenderView** render_view_ptr) { blink::WebFrame* frame = blink::WebFrame::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(chrome::kChromeUIScheme) || frame_url.SchemeIs(chrome::kDataScheme)); if (!webui_enabled) return false; *frame_ptr = frame; *render_view_ptr = render_view; return true; } // static void WebUIExtensionWrapper::Send( const v8::FunctionCallbackInfo& args) { blink::WebFrame* frame; RenderView* render_view; if (!ShouldRespondToRequest(&frame, &render_view)) return; // We expect at least two parameters - a string message identifier, and // an object parameter. The object param can be undefined. if (args.Length() != 2 || !args[0]->IsString()) return; const std::string message = *v8::String::Utf8Value(args[0]->ToString()); // If they've provided an optional message parameter, convert that into a // Value to send to the browser process. scoped_ptr content; if (args[1]->IsUndefined()) { content.reset(new ListValue()); } else { if (!args[1]->IsObject()) return; scoped_ptr converter(V8ValueConverter::create()); base::Value* value = converter->FromV8Value( args[1], 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 void WebUIExtensionWrapper::GetVariableValue( const v8::FunctionCallbackInfo& args) { blink::WebFrame* frame; RenderView* render_view; if (!ShouldRespondToRequest(&frame, &render_view)) return; if (!args.Length() || !args[0]->IsString()) return; std::string key = *v8::String::Utf8Value(args[0]->ToString()); std::string value = WebUIExtensionData::Get(render_view)->GetValue(key); args.GetReturnValue().Set(v8::String::NewFromUtf8(args.GetIsolate(), value.c_str(), v8::String::kNormalString, value.length())); } // static v8::Extension* WebUIExtension::Get() { return new WebUIExtensionWrapper(); } } // namespace content