Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / extensions / renderer / safe_builtins.cc
blob96fb155ffc62880d547d6c931e9b46ac9d48323b
1 // Copyright 2014 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.
5 #include "extensions/renderer/safe_builtins.h"
7 #include "base/logging.h"
8 #include "base/stl_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "extensions/renderer/script_context.h"
11 #include "extensions/renderer/v8_helpers.h"
13 namespace extensions {
15 using namespace v8_helpers;
17 namespace {
19 const char kClassName[] = "extensions::SafeBuiltins";
21 // Documentation for makeCallback in the JavaScript, out here to reduce the
22 // (very small) amount of effort that the v8 parser needs to do:
24 // Returns a new object with every function on |obj| configured to call()\n"
25 // itself with the given arguments.\n"
26 // E.g. given\n"
27 // var result = makeCallable(Function.prototype)\n"
28 // |result| will be a object including 'bind' such that\n"
29 // result.bind(foo, 1, 2, 3);\n"
30 // is equivalent to Function.prototype.bind.call(foo, 1, 2, 3), and so on.\n"
31 // This is a convenient way to save functions that user scripts may clobber.\n"
32 const char kScript[] =
33 "(function() {\n"
34 "'use strict';\n"
35 "native function Apply();\n"
36 "native function Save();\n"
37 "\n"
38 "// Used in the callback implementation, could potentially be clobbered.\n"
39 "function makeCallable(obj, target, isStatic, propertyNames) {\n"
40 " propertyNames.forEach(function(propertyName) {\n"
41 " var property = obj[propertyName];\n"
42 " target[propertyName] = function() {\n"
43 " var recv = obj;\n"
44 " var firstArgIndex = 0;\n"
45 " if (!isStatic) {\n"
46 " if (arguments.length == 0)\n"
47 " throw 'There must be at least one argument, the receiver';\n"
48 " recv = arguments[0];\n"
49 " firstArgIndex = 1;\n"
50 " }\n"
51 " return Apply(\n"
52 " property, recv, arguments, firstArgIndex, arguments.length);\n"
53 " };\n"
54 " });\n"
55 "}\n"
56 "\n"
57 "function saveBuiltin(builtin, protoPropertyNames, staticPropertyNames) {\n"
58 " var safe = function() {\n"
59 " throw 'Safe objects cannot be called nor constructed. ' +\n"
60 " 'Use $Foo.self() or new $Foo.self() instead.';\n"
61 " };\n"
62 " safe.self = builtin;\n"
63 " makeCallable(builtin.prototype, safe, false, protoPropertyNames);\n"
64 " if (staticPropertyNames)\n"
65 " makeCallable(builtin, safe, true, staticPropertyNames);\n"
66 " Save(builtin.name, safe);\n"
67 "}\n"
68 "\n"
69 "// Save only what is needed by the extension modules.\n"
70 "saveBuiltin(Object,\n"
71 " ['hasOwnProperty'],\n"
72 " ['create', 'defineProperty', 'freeze',\n"
73 " 'getOwnPropertyDescriptor', 'getPrototypeOf', 'keys']);\n"
74 "saveBuiltin(Function,\n"
75 " ['apply', 'bind', 'call']);\n"
76 "saveBuiltin(Array,\n"
77 " ['concat', 'forEach', 'indexOf', 'join', 'push', 'slice',\n"
78 " 'splice', 'map', 'filter']);\n"
79 "saveBuiltin(String,\n"
80 " ['indexOf', 'slice', 'split', 'substr', 'toUpperCase',\n"
81 " 'replace']);\n"
82 "saveBuiltin(RegExp,\n"
83 " ['test']);\n"
84 "saveBuiltin(Error,\n"
85 " [],\n"
86 " ['captureStackTrace']);\n"
87 "\n"
88 "// JSON is trickier because extensions can override toJSON in\n"
89 "// incompatible ways, and we need to prevent that.\n"
90 "var builtinTypes = [\n"
91 " Object, Function, Array, String, Boolean, Number, Date, RegExp\n"
92 "];\n"
93 "var builtinToJSONs = builtinTypes.map(function(t) {\n"
94 " return t.toJSON;\n"
95 "});\n"
96 "var builtinArray = Array;\n"
97 "var builtinJSONStringify = JSON.stringify;\n"
98 "Save('JSON', {\n"
99 " parse: JSON.parse,\n"
100 " stringify: function(obj) {\n"
101 " var savedToJSONs = new builtinArray(builtinTypes.length);\n"
102 " try {\n"
103 " for (var i = 0; i < builtinTypes.length; ++i) {\n"
104 " try {\n"
105 " if (builtinTypes[i].prototype.toJSON !==\n"
106 " builtinToJSONs[i]) {\n"
107 " savedToJSONs[i] = builtinTypes[i].prototype.toJSON;\n"
108 " builtinTypes[i].prototype.toJSON = builtinToJSONs[i];\n"
109 " }\n"
110 " } catch (e) {}\n"
111 " }\n"
112 " } catch (e) {}\n"
113 " try {\n"
114 " return builtinJSONStringify(obj);\n"
115 " } finally {\n"
116 " for (var i = 0; i < builtinTypes.length; ++i) {\n"
117 " try {\n"
118 " if (i in savedToJSONs)\n"
119 " builtinTypes[i].prototype.toJSON = savedToJSONs[i];\n"
120 " } catch (e) {}\n"
121 " }\n"
122 " }\n"
123 " }\n"
124 "});\n"
125 "\n"
126 "}());\n";
128 v8::Local<v8::String> MakeKey(const char* name, v8::Isolate* isolate) {
129 return ToV8StringUnsafe(isolate,
130 base::StringPrintf("%s::%s", kClassName, name));
133 void SaveImpl(const char* name,
134 v8::Local<v8::Value> value,
135 v8::Local<v8::Context> context) {
136 CHECK(!value.IsEmpty() && value->IsObject()) << name;
137 context->Global()->SetHiddenValue(MakeKey(name, context->GetIsolate()),
138 value);
141 v8::Local<v8::Object> Load(const char* name, v8::Local<v8::Context> context) {
142 v8::Local<v8::Value> value =
143 context->Global()->GetHiddenValue(MakeKey(name, context->GetIsolate()));
144 CHECK(!value.IsEmpty() && value->IsObject()) << name;
145 return v8::Local<v8::Object>::Cast(value);
148 class ExtensionImpl : public v8::Extension {
149 public:
150 ExtensionImpl() : v8::Extension(kClassName, kScript) {}
152 private:
153 v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
154 v8::Isolate* isolate,
155 v8::Local<v8::String> name) override {
156 v8::Local<v8::Context> context = isolate->GetCurrentContext();
157 if (IsTrue(name->Equals(context, ToV8StringUnsafe(isolate, "Apply"))))
158 return v8::FunctionTemplate::New(isolate, Apply);
159 if (IsTrue(name->Equals(context, ToV8StringUnsafe(isolate, "Save"))))
160 return v8::FunctionTemplate::New(isolate, Save);
161 NOTREACHED() << *v8::String::Utf8Value(name);
162 return v8::Local<v8::FunctionTemplate>();
165 static void Apply(const v8::FunctionCallbackInfo<v8::Value>& info) {
166 CHECK(info.Length() == 5 && info[0]->IsFunction() && // function
167 // info[1] could be an object or a string
168 info[2]->IsObject() && // args
169 info[3]->IsInt32() && // first_arg_index
170 info[4]->IsInt32()); // args_length
171 v8::Local<v8::Function> function = info[0].As<v8::Function>();
172 v8::Local<v8::Object> recv;
173 if (info[1]->IsObject()) {
174 recv = v8::Local<v8::Object>::Cast(info[1]);
175 } else if (info[1]->IsString()) {
176 recv = v8::StringObject::New(v8::Local<v8::String>::Cast(info[1]))
177 .As<v8::Object>();
178 } else {
179 info.GetIsolate()->ThrowException(
180 v8::Exception::TypeError(ToV8StringUnsafe(
181 info.GetIsolate(),
182 "The first argument is the receiver and must be an object")));
183 return;
185 v8::Local<v8::Object> args = v8::Local<v8::Object>::Cast(info[2]);
186 int first_arg_index = info[3].As<v8::Int32>()->Value();
187 int args_length = info[4].As<v8::Int32>()->Value();
189 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
190 int argc = args_length - first_arg_index;
191 scoped_ptr<v8::Local<v8::Value> []> argv(new v8::Local<v8::Value>[argc]);
192 for (int i = 0; i < argc; ++i) {
193 CHECK(IsTrue(args->Has(context, i + first_arg_index)));
194 // Getting a property value could throw an exception.
195 if (!GetProperty(context, args, i + first_arg_index, &argv[i]))
196 return;
199 v8::Local<v8::Value> return_value;
200 if (function->Call(context, recv, argc, argv.get()).ToLocal(&return_value))
201 info.GetReturnValue().Set(return_value);
204 static void Save(const v8::FunctionCallbackInfo<v8::Value>& info) {
205 CHECK(info.Length() == 2 && info[0]->IsString() && info[1]->IsObject());
206 SaveImpl(*v8::String::Utf8Value(info[0]),
207 info[1],
208 info.GetIsolate()->GetCallingContext());
212 } // namespace
214 // static
215 v8::Extension* SafeBuiltins::CreateV8Extension() { return new ExtensionImpl(); }
217 SafeBuiltins::SafeBuiltins(ScriptContext* context) : context_(context) {}
219 SafeBuiltins::~SafeBuiltins() {}
221 v8::Local<v8::Object> SafeBuiltins::GetArray() const {
222 return Load("Array", context_->v8_context());
225 v8::Local<v8::Object> SafeBuiltins::GetFunction() const {
226 return Load("Function", context_->v8_context());
229 v8::Local<v8::Object> SafeBuiltins::GetJSON() const {
230 return Load("JSON", context_->v8_context());
233 v8::Local<v8::Object> SafeBuiltins::GetObjekt() const {
234 return Load("Object", context_->v8_context());
237 v8::Local<v8::Object> SafeBuiltins::GetRegExp() const {
238 return Load("RegExp", context_->v8_context());
241 v8::Local<v8::Object> SafeBuiltins::GetString() const {
242 return Load("String", context_->v8_context());
245 v8::Local<v8::Object> SafeBuiltins::GetError() const {
246 return Load("Error", context_->v8_context());
249 } // namespace extensions