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

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

Issue 2853023002: [Extensions Bindings] Add native declarativeContent verification (Closed)
Patch Set: lazyboy's 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/common/api/declarative/declarative_constants.h"
10 #include "extensions/renderer/api_type_reference_map.h"
11 #include "extensions/renderer/argument_spec.h"
12 #include "gin/arguments.h"
13 #include "gin/converter.h"
14 #include "third_party/WebKit/public/platform/WebString.h"
15 #include "third_party/WebKit/public/web/WebSelector.h"
16
17 namespace extensions {
18
19 namespace {
20
21 void CallbackHelper(const v8::FunctionCallbackInfo<v8::Value>& info) {
22 CHECK(info.Data()->IsExternal());
23 v8::Local<v8::External> external = info.Data().As<v8::External>();
24 auto* callback =
25 static_cast<DeclarativeContentHooksDelegate::HandlerCallback*>(
26 external->Value());
27 callback->Run(info);
28 }
29
30 // Copies the 'own' properties from src -> dst.
31 bool V8Assign(v8::Local<v8::Context> context,
32 v8::Local<v8::Object> src,
33 v8::Local<v8::Object> dst) {
34 v8::Local<v8::Array> own_property_names;
35 if (!src->GetOwnPropertyNames(context).ToLocal(&own_property_names))
36 return false;
37
38 uint32_t length = own_property_names->Length();
39 for (uint32_t i = 0; i < length; ++i) {
40 v8::Local<v8::Value> key;
41 if (!own_property_names->Get(context, i).ToLocal(&key))
42 return false;
43 DCHECK(key->IsString() || key->IsUint32());
44
45 v8::Local<v8::Value> prop_value;
46 if (!src->Get(context, key).ToLocal(&prop_value))
47 return false;
48
49 v8::Maybe<bool> success =
50 key->IsString()
51 ? dst->CreateDataProperty(context, key.As<v8::String>(), prop_value)
52 : dst->CreateDataProperty(context, key.As<v8::Uint32>()->Value(),
53 prop_value);
54 if (!success.IsJust() || !success.FromJust())
55 return false;
56 }
57
58 return true;
59 }
60
61 // Canonicalizes any css selectors specified in a page state matcher, returning
62 // true on success.
63 bool CanonicalizeCssSelectors(v8::Local<v8::Context> context,
64 v8::Local<v8::Object> object,
65 std::string* error) {
66 v8::Isolate* isolate = context->GetIsolate();
67 v8::Local<v8::String> key =
68 gin::StringToSymbol(isolate, declarative_content_constants::kCss);
69 v8::Maybe<bool> has_css = object->HasOwnProperty(context, key);
70 // Note: don't bother populating |error| if script threw an exception.
71 if (!has_css.IsJust())
72 return false;
73
74 if (!has_css.FromJust())
75 return true;
76
77 v8::Local<v8::Value> css;
78 if (!object->Get(context, key).ToLocal(&css))
79 return false;
80
81 if (css->IsUndefined())
82 return true;
83
84 if (!css->IsArray())
85 return false;
86
87 v8::Local<v8::Array> css_array = css.As<v8::Array>();
88 uint32_t length = css_array->Length();
89 for (uint32_t i = 0; i < length; ++i) {
90 v8::Local<v8::Value> val;
91 if (!css_array->Get(context, i).ToLocal(&val) || !val->IsString())
92 return false;
93 v8::String::Utf8Value selector(val.As<v8::String>());
94 // Note: See the TODO in css_natives_handler.cc.
95 std::string parsed =
96 blink::CanonicalizeSelector(
97 blink::WebString::FromUTF8(*selector, selector.length()),
98 blink::kWebSelectorTypeCompound)
99 .Utf8();
100 if (parsed.empty()) {
101 *error =
102 "Invalid CSS selector: " + std::string(*selector, selector.length());
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_object|. 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,
131 gin::StringToSymbol(isolate,
132 declarative_content_constants::kInstanceType),
133 gin::StringToSymbol(isolate, type_name));
134 if (!set_result.IsJust() || !set_result.FromJust()) {
135 return false;
136 }
137
138 if (!spec->ParseArgument(context, this_object, type_refs, nullptr, error)) {
139 return false;
140 }
141
142 if (type_name == declarative_content_constants::kPageStateMatcherType &&
143 !CanonicalizeCssSelectors(context, this_object, error)) {
144 return false;
145 }
146 return true;
147 }
148
149 } // namespace
150
151 DeclarativeContentHooksDelegate::DeclarativeContentHooksDelegate() {}
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 {declarative_content_constants::kPageStateMatcherType,
166 "PageStateMatcher"},
167 {declarative_content_constants::kShowPageAction, "ShowPageAction"},
168 {declarative_content_constants::kSetIcon, "SetIcon"},
169 {declarative_content_constants::kRequestContentScript,
170 "RequestContentScript"},
171 };
172 callbacks_.reserve(arraysize(kTypes));
173 for (const auto& type : kTypes) {
174 const ArgumentSpec* spec = type_refs.GetSpec(type.full_name);
175 DCHECK(spec);
176 // This object should outlive any calls to the function, so this
177 // base::Unretained and the callback itself are safe. Similarly, the same
178 // bindings system owns all these objects, so the spec and type refs should
179 // also be safe.
180 callbacks_.push_back(base::MakeUnique<HandlerCallback>(
181 base::Bind(&DeclarativeContentHooksDelegate::HandleCall,
182 base::Unretained(this), spec, &type_refs, type.full_name)));
183 object_template->Set(
184 gin::StringToSymbol(isolate, type.exposed_name),
185 v8::FunctionTemplate::New(
186 isolate, &CallbackHelper,
187 v8::External::New(isolate, callbacks_.back().get())));
188 }
189 }
190
191 void DeclarativeContentHooksDelegate::HandleCall(
192 const ArgumentSpec* spec,
193 const APITypeReferenceMap* type_refs,
194 const std::string& type_name,
195 const v8::FunctionCallbackInfo<v8::Value>& info) {
196 gin::Arguments arguments(info);
197 v8::Isolate* isolate = arguments.isolate();
198 v8::HandleScope handle_scope(isolate);
199 v8::Local<v8::Context> context = isolate->GetCurrentContext();
200
201 // TODO(devlin): It would be pretty nice to be able to throw an error if
202 // Arguments::IsConstructCall() is false. That would ensure that the caller
203 // used `new declarativeContent.Foo()`, which is a) the documented approach
204 // and b) allows us (more) confidence that the |this| object we receive is
205 // an unmodified instance. But we don't know how many extensions enforcing
206 // that may break, and it's also incompatible with SetIcon().
207
208 v8::Local<v8::Object> this_object = info.This();
209 if (this_object.IsEmpty()) {
210 // Crazy script (e.g. declarativeContent.Foo.apply(null, args);).
211 NOTREACHED();
212 return;
213 }
214
215 // TODO(devlin): Find a way to use APISignature here? It's a little awkward
216 // because of undocumented expected properties like instanceType and not
217 // requiring an argument at all. We may need a better way of expressing these
218 // in the JSON schema.
219 if (arguments.Length() > 1) {
220 arguments.ThrowTypeError("Invalid invocation.");
221 return;
222 }
223
224 v8::Local<v8::Object> properties;
225 if (arguments.Length() == 1 && !arguments.GetNext(&properties)) {
226 arguments.ThrowTypeError("Invalid invocation.");
227 return;
228 }
229
230 std::string error;
231 bool success = false;
232 {
233 v8::TryCatch try_catch(isolate);
234 success = Validate(spec, *type_refs, context, this_object, properties,
235 type_name, &error);
236 if (try_catch.HasCaught()) {
237 try_catch.ReThrow();
238 return;
239 }
240 }
241
242 if (!success)
243 arguments.ThrowTypeError("Invalid invocation: " + error);
244 }
245
246 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/declarative_content_hooks_delegate.h ('k') | extensions/renderer/native_extension_bindings_system.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698