Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(697)

Side by Side Diff: extensions/renderer/declarative_content_hooks_delegate.cc

Issue 2853023002: [Extensions Bindings] Add native declarativeContent verification (Closed)
Patch Set: +test Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "extensions/renderer/declarative_content_hooks_delegate.h"
6
7 #include "base/bind.h"
8 #include "base/memory/ptr_util.h"
9 #include "extensions/renderer/api_type_reference_map.h"
10 #include "extensions/renderer/argument_spec.h"
11 #include "gin/arguments.h"
12 #include "gin/converter.h"
13 #include "third_party/WebKit/public/platform/WebString.h"
14 #include "third_party/WebKit/public/web/WebSelector.h"
15
16 namespace extensions {
17
18 namespace {
19
20 void CallbackHelper(const v8::FunctionCallbackInfo<v8::Value>& info) {
21 CHECK(info.Data()->IsExternal());
22 v8::Local<v8::External> external = info.Data().As<v8::External>();
23 auto* callback =
24 static_cast<DeclarativeContentHooksDelegate::HandlerCallback*>(
25 external->Value());
26 callback->Run(info);
27 }
28
29 // Copies the properties from src -> dst, similar to Object.assign().
jbroman 2017/05/01 20:16:40 Don't know how much you care, but at a glance I th
Devlin 2017/05/01 20:46:48 I'm not too worried about those. The goal wasn't
30 bool V8Assign(v8::Local<v8::Context> context,
31 v8::Local<v8::Object> src,
32 v8::Local<v8::Object> dst) {
33 v8::Local<v8::Array> own_property_names;
34 if (!src->GetOwnPropertyNames(context).ToLocal(&own_property_names))
35 return false;
36
37 uint32_t length = own_property_names->Length();
38 for (uint32_t i = 0; i < length; ++i) {
39 v8::Local<v8::Value> key;
40 if (!own_property_names->Get(context, i).ToLocal(&key))
41 return false;
42 DCHECK(key->IsString() || key->IsNumber());
jbroman 2017/05/01 20:16:40 Note that this is true (without a property filter,
Devlin 2017/05/01 20:46:48 I think the DCHECK is useful only because below we
43
44 v8::Local<v8::Value> prop_value;
45 if (!src->Get(context, key).ToLocal(&prop_value))
46 return false;
47
48 bool success = false;
jbroman 2017/05/01 20:16:40 super-nit: might as well factor out the result che
Devlin 2017/05/01 20:46:48 Sure. I thought it would be too verbose, but if y
49 if (key->IsString()) {
50 v8::Maybe<bool> set_result =
51 dst->CreateDataProperty(context, key.As<v8::String>(), prop_value);
52 success = set_result.IsJust() && set_result.FromJust();
53 } else {
54 v8::Maybe<bool> set_result = dst->CreateDataProperty(
55 context, key.As<v8::Uint32>()->Value(), prop_value);
56 success = set_result.IsJust() && set_result.FromJust();
57 }
58 if (!success)
59 return false;
60 }
61
62 return true;
63 }
64
65 // Canonicalizes any css selectors specified in a page state matcher, returning
66 // true on success.
67 bool CanonicalizeCssSelectors(v8::Local<v8::Context> context,
68 v8::Local<v8::Object> object,
69 std::string* error) {
70 v8::Isolate* isolate = context->GetIsolate();
71 v8::Local<v8::String> key = gin::StringToSymbol(isolate, "css");
72 v8::Maybe<bool> has_css = object->HasOwnProperty(context, key);
73 // Note: don't bother populating |error| if script threw an exception.
74 if (!has_css.IsJust())
75 return false;
76
77 if (!has_css.FromJust())
78 return true;
79
80 v8::Local<v8::Value> css;
81 if (!object->Get(context, key).ToLocal(&css))
82 return false;
83
84 if (css->IsUndefined())
85 return true;
86
87 if (!css->IsArray())
88 return false;
89
90 v8::Local<v8::Array> css_array = css.As<v8::Array>();
91 uint32_t length = css_array->Length();
92 for (uint32_t i = 0; i < length; ++i) {
93 v8::Local<v8::Value> val;
94 if (!css_array->Get(context, i).ToLocal(&val) || !val->IsString())
95 return false;
96 const char* selector = *v8::String::Utf8Value(val.As<v8::String>());
jbroman 2017/05/01 20:16:40 This is unsafe. The buffer returned by Utf8Value::
Devlin 2017/05/01 20:46:48 Oh, I had no idea. Fixed. Thanks for catching it
97 std::string parsed =
98 blink::CanonicalizeSelector(blink::WebString::FromUTF8(selector),
99 blink::kWebSelectorTypeCompound)
100 .Utf8();
101 if (parsed.empty()) {
102 *error = "Invalid css: " + std::string(selector);
103 return false;
104 }
105 v8::Maybe<bool> set_result =
106 css_array->Set(context, i, gin::StringToSymbol(isolate, parsed));
107 if (!set_result.IsJust() || !set_result.FromJust())
108 return false;
109 }
110
111 return true;
112 }
113
114 // Validates the source object against the expected spec, and copies over values
115 // to |this|. Returns true on success.
116 bool Validate(const ArgumentSpec* spec,
117 const APITypeReferenceMap& type_refs,
118 v8::Local<v8::Context> context,
119 v8::Local<v8::Object> this_object,
120 v8::Local<v8::Object> source_object,
121 const std::string& type_name,
122 std::string* error) {
123 if (!source_object.IsEmpty() &&
124 !V8Assign(context, source_object, this_object)) {
125 return false;
126 }
127
128 v8::Isolate* isolate = context->GetIsolate();
129 v8::Maybe<bool> set_result = this_object->CreateDataProperty(
130 context, gin::StringToSymbol(isolate, "instanceType"),
131 gin::StringToSymbol(isolate, type_name));
132 if (!set_result.IsJust() || !set_result.FromJust()) {
133 return false;
134 }
135
136 if (!spec->ParseArgument(context, this_object, type_refs, nullptr, error)) {
137 return false;
138 }
139
140 if (type_name == "declarativeContent.PageStateMatcher" &&
141 !CanonicalizeCssSelectors(context, this_object, error)) {
142 return false;
143 }
144 return true;
145 }
146
147 } // namespace
148
149 DeclarativeContentHooksDelegate::DeclarativeContentHooksDelegate()
150 : weak_factory_(this) {}
151
152 DeclarativeContentHooksDelegate::~DeclarativeContentHooksDelegate() {}
153
154 void DeclarativeContentHooksDelegate::InitializeTemplate(
155 v8::Isolate* isolate,
156 v8::Local<v8::ObjectTemplate> object_template,
157 const APITypeReferenceMap& type_refs) {
158 // Add constructors for the API types.
159 // TODO(devlin): We'll need to extract out common logic here and share it with
160 // declarativeWebRequest.
161 struct {
162 const char* full_name;
163 const char* exposed_name;
164 } kTypes[] = {
165 {"declarativeContent.PageStateMatcher", "PageStateMatcher"},
166 {"declarativeContent.ShowPageAction", "ShowPageAction"},
167 {"declarativeContent.SetIcon", "SetIcon"},
168 {"declarativeContent.RequestContentScript", "RequestContentScript"},
169 };
170 for (const auto type : kTypes) {
171 const ArgumentSpec* spec = type_refs.GetSpec(type.full_name);
172 DCHECK(spec);
173 callbacks_.push_back(base::MakeUnique<HandlerCallback>(base::Bind(
174 &DeclarativeContentHooksDelegate::HandleCall,
jbroman 2017/05/01 20:16:40 This seems slightly paradoxical. If the Declarativ
Devlin 2017/05/01 20:46:48 Fair enough - habit. Removed.
175 weak_factory_.GetWeakPtr(), spec, &type_refs, type.full_name)));
176 object_template->Set(
177 gin::StringToSymbol(isolate, type.exposed_name),
178 v8::FunctionTemplate::New(
179 isolate, &CallbackHelper,
180 v8::External::New(isolate, callbacks_.back().get())));
181 }
182 }
183
184 void DeclarativeContentHooksDelegate::HandleCall(
185 const ArgumentSpec* spec,
186 const APITypeReferenceMap* type_refs,
187 const std::string& type_name,
188 const v8::FunctionCallbackInfo<v8::Value>& info) {
189 gin::Arguments arguments(info);
190 v8::Isolate* isolate = arguments.isolate();
191 v8::HandleScope handle_scope(isolate);
192 v8::Local<v8::Context> context = isolate->GetCurrentContext();
193
194 // TODO(devlin): It would be pretty nice to be able to throw an error if
195 // Arguments::IsConstructCall() is false. That would ensure that the caller
196 // used `new declarativeContent.Foo()`, which is a) the documented approach
197 // and b) allows us (more) confidence that the |this| object we receive is
198 // an unmodified instance. But we don't know how many extensions enforcing
199 // that may break, and it's also incompatible with SetIcon().
200
201 v8::Local<v8::Object> this_object = info.This();
202 if (this_object.IsEmpty()) {
203 // Crazy script (e.g. declarativeContent.Foo.apply(null, args);).
204 NOTREACHED();
205 return;
206 }
207
208 // TODO(devlin): Find a way to use APISignature here? It's a little awkward
209 // because of undocumented expected properties like instanceType and not
210 // requiring an argument at all. We may need a better way of expressing these
211 // in the JSON schema.
212 std::vector<v8::Local<v8::Value>> argument_list = arguments.GetAll();
213 if (argument_list.size() > 1) {
214 arguments.ThrowTypeError("Invalid invocation.");
Devlin 2017/05/01 19:29:45 Note: will rebase and use the new API invocation e
215 return;
216 }
217
218 v8::Local<v8::Object> properties;
219 if (!argument_list.empty()) {
220 if (!argument_list[0]->IsObject()) {
221 arguments.ThrowTypeError("Invalid invocation.");
222 return;
223 }
224 properties = argument_list[0].As<v8::Object>();
225 }
226
227 std::string error;
228 bool success = false;
229 {
230 v8::TryCatch try_catch(isolate);
231 success = Validate(spec, *type_refs, context, this_object, properties,
232 type_name, &error);
233 if (try_catch.HasCaught()) {
234 try_catch.ReThrow();
235 return;
236 }
237 }
238
239 if (!success)
240 arguments.ThrowTypeError("Invalid invocation: " + error);
241 }
242
243 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698