1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/web_ui_extension.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/values.h"
9#include "content/common/view_messages.h"
10#include "content/public/common/bindings_policy.h"
11#include "content/public/common/url_constants.h"
12#include "content/public/renderer/render_thread.h"
13#include "content/public/renderer/render_view.h"
14#include "content/public/renderer/v8_value_converter.h"
15#include "content/renderer/web_ui_extension_data.h"
16#include "gin/arguments.h"
17#include "gin/function_template.h"
18#include "third_party/WebKit/public/web/WebDocument.h"
19#include "third_party/WebKit/public/web/WebKit.h"
20#include "third_party/WebKit/public/web/WebLocalFrame.h"
21#include "third_party/WebKit/public/web/WebView.h"
22#include "url/gurl.h"
23#include "v8/include/v8.h"
24
25namespace content {
26
27namespace {
28
29bool ShouldRespondToRequest(
30    blink::WebFrame** frame_ptr,
31    RenderView** render_view_ptr) {
32  blink::WebFrame* frame = blink::WebLocalFrame::frameForCurrentContext();
33  if (!frame || !frame->view())
34    return false;
35
36  RenderView* render_view = RenderView::FromWebView(frame->view());
37  if (!render_view)
38    return false;
39
40  GURL frame_url = frame->document().url();
41
42  bool webui_enabled =
43      (render_view->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI) &&
44      (frame_url.SchemeIs(kChromeUIScheme) ||
45       frame_url.SchemeIs(url::kDataScheme));
46
47  if (!webui_enabled)
48    return false;
49
50  *frame_ptr = frame;
51  *render_view_ptr = render_view;
52  return true;
53}
54
55}  // namespace
56
57// Exposes two methods:
58//  - chrome.send: Used to send messages to the browser. Requires the message
59//      name as the first argument and can have an optional second argument that
60//      should be an array.
61//  - chrome.getVariableValue: Returns value for the input variable name if such
62//      a value was set by the browser. Else will return an empty string.
63void WebUIExtension::Install(blink::WebFrame* frame) {
64  v8::Isolate* isolate = blink::mainThreadIsolate();
65  v8::HandleScope handle_scope(isolate);
66  v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
67  if (context.IsEmpty())
68    return;
69
70  v8::Context::Scope context_scope(context);
71
72  v8::Handle<v8::Object> global = context->Global();
73  v8::Handle<v8::Object> chrome =
74      global->Get(gin::StringToV8(isolate, "chrome"))->ToObject();
75  if (chrome.IsEmpty()) {
76    chrome = v8::Object::New(isolate);
77    global->Set(gin::StringToSymbol(isolate, "chrome"), chrome);
78  }
79  chrome->Set(gin::StringToSymbol(isolate, "send"),
80              gin::CreateFunctionTemplate(
81                  isolate, base::Bind(&WebUIExtension::Send))->GetFunction());
82  chrome->Set(gin::StringToSymbol(isolate, "getVariableValue"),
83              gin::CreateFunctionTemplate(
84                  isolate, base::Bind(&WebUIExtension::GetVariableValue))
85                  ->GetFunction());
86}
87
88// static
89void WebUIExtension::Send(gin::Arguments* args) {
90  blink::WebFrame* frame;
91  RenderView* render_view;
92  if (!ShouldRespondToRequest(&frame, &render_view))
93    return;
94
95  std::string message;
96  if (!args->GetNext(&message)) {
97    args->ThrowError();
98    return;
99  }
100
101  // If they've provided an optional message parameter, convert that into a
102  // Value to send to the browser process.
103  scoped_ptr<base::ListValue> content;
104  if (args->PeekNext().IsEmpty() || args->PeekNext()->IsUndefined()) {
105    content.reset(new base::ListValue());
106  } else {
107    v8::Handle<v8::Object> obj;
108    if (!args->GetNext(&obj)) {
109      args->ThrowError();
110      return;
111    }
112
113    scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
114
115    base::Value* value =
116        converter->FromV8Value(obj, frame->mainWorldScriptContext());
117    base::ListValue* list = NULL;
118    value->GetAsList(&list);
119    DCHECK(list);
120    content.reset(list);
121  }
122
123  // Send the message up to the browser.
124  render_view->Send(new ViewHostMsg_WebUISend(render_view->GetRoutingID(),
125                                              frame->document().url(),
126                                              message,
127                                              *content));
128}
129
130// static
131std::string WebUIExtension::GetVariableValue(const std::string& name) {
132  blink::WebFrame* frame;
133  RenderView* render_view;
134  if (!ShouldRespondToRequest(&frame, &render_view))
135    return std::string();
136
137  return WebUIExtensionData::Get(render_view)->GetValue(name);
138}
139
140}  // namespace content
141