1// Copyright (c) 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 "chrome/renderer/external_extension.h"
6
7#include "chrome/common/render_messages.h"
8#include "chrome/common/search_provider.h"
9#include "content/public/renderer/render_view.h"
10#include "third_party/WebKit/public/web/WebDocument.h"
11#include "third_party/WebKit/public/web/WebLocalFrame.h"
12#include "third_party/WebKit/public/web/WebView.h"
13#include "v8/include/v8.h"
14
15using blink::WebLocalFrame;
16using blink::WebView;
17using content::RenderView;
18
19namespace extensions_v8 {
20
21namespace {
22
23const char* const kSearchProviderApi =
24    "var external;"
25    "if (!external)"
26    "  external = {};"
27    "external.AddSearchProvider = function(name) {"
28    "  native function NativeAddSearchProvider();"
29    "  NativeAddSearchProvider(name);"
30    "};"
31    "external.IsSearchProviderInstalled = function(name) {"
32    "  native function NativeIsSearchProviderInstalled();"
33    "  return NativeIsSearchProviderInstalled(name);"
34    "};";
35
36const char kExternalExtensionName[] = "v8/External";
37
38}  // namespace
39
40class ExternalExtensionWrapper : public v8::Extension {
41 public:
42  ExternalExtensionWrapper();
43
44  // Allows v8's javascript code to call the native functions defined
45  // in this class for window.external.
46  virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
47      v8::Isolate* isolate,
48      v8::Handle<v8::String> name) OVERRIDE;
49
50  // Helper function to find the RenderView. May return NULL.
51  static RenderView* GetRenderView();
52
53  // Implementation of window.external.AddSearchProvider.
54  static void AddSearchProvider(
55      const v8::FunctionCallbackInfo<v8::Value>& args);
56
57  // Implementation of window.external.IsSearchProviderInstalled.
58  static void IsSearchProviderInstalled(
59      const v8::FunctionCallbackInfo<v8::Value>& args);
60
61 private:
62  DISALLOW_COPY_AND_ASSIGN(ExternalExtensionWrapper);
63};
64
65ExternalExtensionWrapper::ExternalExtensionWrapper()
66    : v8::Extension(kExternalExtensionName, kSearchProviderApi) {
67}
68
69v8::Handle<v8::FunctionTemplate>
70ExternalExtensionWrapper::GetNativeFunctionTemplate(
71    v8::Isolate* isolate,
72    v8::Handle<v8::String> name) {
73  if (name->Equals(v8::String::NewFromUtf8(isolate, "NativeAddSearchProvider")))
74    return v8::FunctionTemplate::New(isolate, AddSearchProvider);
75
76  if (name->Equals(v8::String::NewFromUtf8(
77          isolate, "NativeIsSearchProviderInstalled"))) {
78    return v8::FunctionTemplate::New(isolate, IsSearchProviderInstalled);
79  }
80
81  return v8::Handle<v8::FunctionTemplate>();
82}
83
84// static
85RenderView* ExternalExtensionWrapper::GetRenderView() {
86  WebLocalFrame* webframe = WebLocalFrame::frameForCurrentContext();
87  DCHECK(webframe) << "There should be an active frame since we just got "
88      "a native function called.";
89  if (!webframe)
90    return NULL;
91
92  WebView* webview = webframe->view();
93  if (!webview)
94    return NULL;  // can happen during closing
95
96  return RenderView::FromWebView(webview);
97}
98
99// static
100void ExternalExtensionWrapper::AddSearchProvider(
101    const v8::FunctionCallbackInfo<v8::Value>& args) {
102  if (!args.Length() || !args[0]->IsString())
103    return;
104
105  std::string osdd_string(*v8::String::Utf8Value(args[0]));
106  if (osdd_string.empty())
107    return;
108
109  RenderView* render_view = GetRenderView();
110  if (!render_view)
111    return;
112
113  WebLocalFrame* webframe = WebLocalFrame::frameForCurrentContext();
114  if (!webframe)
115    return;
116
117  GURL osdd_url(osdd_string);
118  if (!osdd_url.is_empty() && osdd_url.is_valid()) {
119    render_view->Send(new ChromeViewHostMsg_PageHasOSDD(
120        render_view->GetRoutingID(), webframe->document().url(), osdd_url,
121        search_provider::EXPLICIT_PROVIDER));
122  }
123}
124
125// static
126void ExternalExtensionWrapper::IsSearchProviderInstalled(
127    const v8::FunctionCallbackInfo<v8::Value>& args) {
128  if (!args.Length() || !args[0]->IsString())
129    return;
130
131  v8::String::Utf8Value utf8name(args[0]);
132  if (!utf8name.length())
133    return;
134
135  std::string name(*utf8name);
136  RenderView* render_view = GetRenderView();
137  if (!render_view)
138    return;
139
140  WebLocalFrame* webframe = WebLocalFrame::frameForCurrentContext();
141  if (!webframe)
142    return;
143
144  search_provider::InstallState install = search_provider::DENIED;
145  GURL inquiry_url = GURL(name);
146  if (!inquiry_url.is_empty()) {
147      render_view->Send(new ChromeViewHostMsg_GetSearchProviderInstallState(
148          render_view->GetRoutingID(),
149          webframe->document().url(),
150          inquiry_url,
151          &install));
152  }
153
154  if (install == search_provider::DENIED) {
155    // FIXME: throw access denied exception.
156    v8::Isolate* isolate = args.GetIsolate();
157    isolate->ThrowException(v8::Exception::Error(v8::String::Empty(isolate)));
158    return;
159  }
160  args.GetReturnValue().Set(static_cast<int32_t>(install));
161}
162
163v8::Extension* ExternalExtension::Get() {
164  return new ExternalExtensionWrapper();
165}
166
167}  // namespace extensions_v8
168