1 // Copyright 2015 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 // A simple C++ Pepper plugin for exercising deprecated PPAPI interfaces in
6 // Blink layout tests.
7 //
8 // Most layout tests should prefer to use the normal Blink test plugin, with the
9 // MIME type application/x-blink-test-plugin. For layout tests that absolutely
10 // need to test deprecated synchronous scripting interfaces, this plugin can be
11 // instantiated using the application/x-blink-deprecated-test-plugin MIME type.
12 //
13 // The plugin exposes the following interface:
14 //
15 // Attributes:
16 // testwindowopen: if set, the plugin will synchronously attempt to open a
17 // window from DidCreateInstance, and log a message if successful.
18 //
19 // keydownscript: if set, the plugin will execute the value of the attribute as
20 // a script on a key down.
21 //
22 // mousedownscript: if set, the plugin will execute the value of the attribute
23 // as a script on a mouse button down.
24 //
25 //
26 // Functions:
27 // * plugin.normalize(): synchronously calls window.pluginCallback.
28 //
29 // * plugin.remember(value): keeps a reference on |value| in the plugin.
30 //
31 // * plugin.testCloneObject(): creates and returns another instance of the
32 // plugin object.
33 //
34 // * plugin.testCreateTestObject(): creates and returns a new TestObject
35 // instance (see below).
36 //
37 // * plugin.testExecuteScript(script): synchronously evaluates |script| and
38 // returns the result.
39 //
40 // * plugin.testGetProperty(property): returns the property named |property|
41 // from the window object.
42 //
43 // * plugin.testPassTestObject(function, object): synchronously calls the
44 // function named |function| on the window object, passing it |object| as a
45 // parameter, and returns its result.
46 //
47 // * plugin.testScriptObjectInvoke(function, value): synchronously calls the
48 // function named |function| on the window object, passing it |value| as a
49 // parameter, and returns its result.
50 //
51 //
52 // Properties:
53 // * plugin.testObject (read-only): a TestObject instance (see below).
54 //
55 // * plugin.testObjectCount (read-only): the number of TestObject instance
56 // created.
57 //
58 // * plugin.testGetUndefined (read-only): returns undefined.
59 //
60 //
61 // TestObject exposes the following interface:
62 // Properties:
63 // * object.testObject (read-only: another TestObject instance.
64
65 #include <stdint.h>
66
67 #include <map>
68 #include <sstream>
69 #include <unordered_map>
70 #include <utility>
71
72 #include "base/bind.h"
73 #include "base/callback.h"
74 #include "base/callback_helpers.h"
75 #include "base/strings/stringprintf.h"
76 #include "ppapi/cpp/dev/scriptable_object_deprecated.h"
77 #include "ppapi/cpp/input_event.h"
78 #include "ppapi/cpp/module.h"
79 #include "ppapi/cpp/private/instance_private.h"
80 #include "ppapi/cpp/private/var_private.h"
81 #include "ppapi/cpp/var.h"
82
83 namespace {
84
85 class ScriptableBase : public pp::deprecated::ScriptableObject {
86 public:
ScriptableBase(pp::InstancePrivate * instance)87 explicit ScriptableBase(pp::InstancePrivate* instance)
88 : instance_(instance) {}
~ScriptableBase()89 ~ScriptableBase() override {}
90
91 // pp::deprecated::ScriptableObject overrides:
HasMethod(const pp::Var & name,pp::Var * exception)92 bool HasMethod(const pp::Var& name, pp::Var* exception) override {
93 return FindMethod(name) != methods_.end();
94 }
95
HasProperty(const pp::Var & name,pp::Var * exception)96 bool HasProperty(const pp::Var& name, pp::Var* exception) override {
97 return FindProperty(name) != properties_.end();
98 }
99
Call(const pp::Var & method_name,const std::vector<pp::Var> & args,pp::Var * exception)100 pp::Var Call(const pp::Var& method_name,
101 const std::vector<pp::Var>& args,
102 pp::Var* exception) override {
103 auto method = FindMethod(method_name);
104 if (method != methods_.end()) {
105 return method->second.Run(args, exception);
106 }
107
108 return ScriptableObject::Call(method_name, args, exception);
109 }
110
GetProperty(const pp::Var & name,pp::Var * exception)111 pp::Var GetProperty(const pp::Var& name, pp::Var* exception) override {
112 auto accessor = FindProperty(name);
113 if (accessor != properties_.end()) {
114 pp::Var value;
115 accessor->second.Run(false, &value);
116 return value;
117 }
118 return ScriptableObject::GetProperty(name, exception);
119 }
120
SetProperty(const pp::Var & name,const pp::Var & value,pp::Var * exception)121 void SetProperty(const pp::Var& name,
122 const pp::Var& value,
123 pp::Var* exception) override {
124 auto accessor = FindProperty(name);
125 if (accessor != properties_.end())
126 accessor->second.Run(true, const_cast<pp::Var*>(&value));
127 else
128 ScriptableObject::SetProperty(name, value, exception);
129 }
130
131 protected:
132 using MethodMap = std::map<
133 std::string,
134 base::RepeatingCallback<pp::Var(const std::vector<pp::Var>&, pp::Var*)>>;
135 using PropertyMap =
136 std::map<std::string, base::RepeatingCallback<void(bool, pp::Var*)>>;
137
FindMethod(const pp::Var & name)138 MethodMap::iterator FindMethod(const pp::Var& name) {
139 if (!name.is_string())
140 return methods_.end();
141 return methods_.find(name.AsString());
142 }
143
FindProperty(const pp::Var & name)144 PropertyMap::iterator FindProperty(const pp::Var& name) {
145 if (!name.is_string())
146 return properties_.end();
147 return properties_.find(name.AsString());
148 }
149
150 pp::InstancePrivate* const instance_;
151 MethodMap methods_;
152 PropertyMap properties_;
153 };
154
155 class TestObjectSO : public ScriptableBase {
156 public:
TestObjectSO(pp::InstancePrivate * instance)157 explicit TestObjectSO(pp::InstancePrivate* instance)
158 : ScriptableBase(instance) {
159 ++count_;
160 properties_.insert(std::make_pair(
161 "testObject", base::BindRepeating(&TestObjectSO::TestObjectAccessor,
162 base::Unretained(this))));
163 }
~TestObjectSO()164 ~TestObjectSO() override {
165 --count_;
166 }
167
count()168 static int32_t count() { return count_; }
169
170 private:
TestObjectAccessor(bool set,pp::Var * var)171 void TestObjectAccessor(bool set, pp::Var* var) {
172 if (set)
173 return;
174 if (test_object_.is_undefined())
175 test_object_ = pp::VarPrivate(instance_, new TestObjectSO(instance_));
176 *var = test_object_;
177 }
178
179 static int32_t count_;
180
181 pp::VarPrivate test_object_;
182 };
183
184 int32_t TestObjectSO::count_ = 0;
185
186 class InstanceSO : public ScriptableBase {
187 public:
InstanceSO(pp::InstancePrivate * instance)188 explicit InstanceSO(pp::InstancePrivate* instance)
189 : ScriptableBase(instance) {
190 methods_.insert(std::make_pair(
191 "normalize",
192 base::BindRepeating(&InstanceSO::Normalize, base::Unretained(this))));
193 methods_.insert(std::make_pair(
194 "remember",
195 base::BindRepeating(&InstanceSO::Remember, base::Unretained(this))));
196 methods_.insert(std::make_pair(
197 "testCloneObject", base::BindRepeating(&InstanceSO::TestCloneObject,
198 base::Unretained(this))));
199 methods_.insert(
200 std::make_pair("testCreateTestObject",
201 base::BindRepeating(&InstanceSO::TestCreateTestObject,
202 base::Unretained(this))));
203 methods_.insert(std::make_pair(
204 "testExecuteScript", base::BindRepeating(&InstanceSO::TestExecuteScript,
205 base::Unretained(this))));
206 methods_.insert(std::make_pair(
207 "testGetProperty", base::BindRepeating(&InstanceSO::TestGetProperty,
208 base::Unretained(this))));
209 methods_.insert(
210 std::make_pair("testPassTestObject",
211 base::BindRepeating(&InstanceSO::TestPassTestObject,
212 base::Unretained(this))));
213 // Note: the semantics of testScriptObjectInvoke are identical to the
214 // semantics of testPassTestObject: call args[0] with args[1] as a
215 // parameter.
216 methods_.insert(
217 std::make_pair("testScriptObjectInvoke",
218 base::BindRepeating(&InstanceSO::TestPassTestObject,
219 base::Unretained(this))));
220 properties_.insert(std::make_pair(
221 "testObject", base::BindRepeating(&InstanceSO::TestObjectAccessor,
222 base::Unretained(this))));
223 properties_.insert(
224 std::make_pair("testObjectCount",
225 base::BindRepeating(&InstanceSO::TestObjectCountAccessor,
226 base::Unretained(this))));
227 properties_.insert(std::make_pair(
228 "testGetUndefined",
229 base::BindRepeating(&InstanceSO::TestGetUndefinedAccessor,
230 base::Unretained(this))));
231 }
232 ~InstanceSO() override = default;
233
234 private:
235 // Requires no argument.
Normalize(const std::vector<pp::Var> & args,pp::Var * exception)236 pp::Var Normalize(const std::vector<pp::Var>& args, pp::Var* exception) {
237 pp::VarPrivate object = instance_->GetWindowObject();
238 return object.Call(pp::Var("pluginCallback"), exception);
239 }
240
241 // Requires 1 argument. The argument is retained into remembered_
Remember(const std::vector<pp::Var> & args,pp::Var * exception)242 pp::Var Remember(const std::vector<pp::Var>& args, pp::Var* exception) {
243 if (args.size() != 1) {
244 *exception = pp::Var("remember requires one argument");
245 return pp::Var();
246 }
247 remembered_ = args[0];
248 return pp::Var();
249 }
250
251 // Requires no argument.
TestCloneObject(const std::vector<pp::Var> & args,pp::Var * exception)252 pp::Var TestCloneObject(const std::vector<pp::Var>& args,
253 pp::Var* exception) {
254 return pp::VarPrivate(instance_, new InstanceSO(instance_));
255 }
256
257 // Requires no argument.
TestCreateTestObject(const std::vector<pp::Var> & args,pp::Var * exception)258 pp::Var TestCreateTestObject(const std::vector<pp::Var>& args,
259 pp::Var* exception) {
260 return pp::VarPrivate(instance_, new TestObjectSO(instance_));
261 }
262
263 // Requires one argument. The argument is passed through as-is to
264 // pp::InstancePrivate::ExecuteScript().
TestExecuteScript(const std::vector<pp::Var> & args,pp::Var * exception)265 pp::Var TestExecuteScript(const std::vector<pp::Var>& args,
266 pp::Var* exception) {
267 if (args.size() != 1) {
268 *exception = pp::Var("testExecuteScript requires one argument");
269 return pp::Var();
270 }
271 return instance_->ExecuteScript(args[0], exception);
272 }
273
274 // Requires one or more arguments. Roughly analogous to NPN_GetProperty.
275 // The arguments are the chain of properties to traverse, starting with the
276 // global context.
TestGetProperty(const std::vector<pp::Var> & args,pp::Var * exception)277 pp::Var TestGetProperty(const std::vector<pp::Var>& args,
278 pp::Var* exception) {
279 if (args.size() < 1) {
280 *exception = pp::Var("testGetProperty requires at least one argument");
281 return pp::Var();
282 }
283 pp::VarPrivate object = instance_->GetWindowObject();
284 for (const auto& arg : args) {
285 if (!object.HasProperty(arg, exception))
286 return pp::Var();
287 object = object.GetProperty(arg, exception);
288 }
289 return object;
290 }
291
292 // Requires 2 or more arguments. The first argument is the name of a function
293 // to invoke, and the second argument is a value to pass to that function.
TestPassTestObject(const std::vector<pp::Var> & args,pp::Var * exception)294 pp::Var TestPassTestObject(const std::vector<pp::Var>& args,
295 pp::Var* exception) {
296 if (args.size() < 2) {
297 *exception = pp::Var("testPassTestObject requires at least 2 arguments");
298 return pp::Var();
299 }
300 pp::VarPrivate object = instance_->GetWindowObject();
301 return object.Call(args[0], args[1], exception);
302 }
303
TestObjectAccessor(bool set,pp::Var * var)304 void TestObjectAccessor(bool set, pp::Var* var) {
305 if (set)
306 return;
307 if (test_object_.is_undefined())
308 test_object_ = pp::VarPrivate(instance_, new TestObjectSO(instance_));
309 *var = test_object_;
310 }
311
TestObjectCountAccessor(bool set,pp::Var * var)312 void TestObjectCountAccessor(bool set, pp::Var* var) {
313 if (set)
314 return;
315 *var = pp::Var(TestObjectSO::count());
316 }
317
TestGetUndefinedAccessor(bool set,pp::Var * var)318 void TestGetUndefinedAccessor(bool set, pp::Var* var) {
319 if (set)
320 return;
321 *var = pp::Var();
322 }
323
324 pp::VarPrivate test_object_;
325 pp::Var remembered_;
326 };
327
328 class BlinkDeprecatedTestInstance : public pp::InstancePrivate {
329 public:
BlinkDeprecatedTestInstance(PP_Instance instance)330 explicit BlinkDeprecatedTestInstance(PP_Instance instance)
331 : pp::InstancePrivate(instance) {}
~BlinkDeprecatedTestInstance()332 ~BlinkDeprecatedTestInstance() override {
333 LogMessage("%s", "Destroying");
334 }
335
336 // pp::Instance overrides
Init(uint32_t argc,const char * argn[],const char * argv[])337 bool Init(uint32_t argc, const char* argn[], const char* argv[]) override {
338 for (uint32_t i = 0; i < argc; ++i)
339 attributes_[argn[i]] = argv[i];
340
341 if (HasAttribute("testwindowopen"))
342 return TestWindowOpen();
343
344 if (HasAttribute("initscript"))
345 ExecuteScript(attributes_["initscript"]);
346
347 uint32_t event_classes = 0;
348 if (HasAttribute("keydownscript"))
349 event_classes |= PP_INPUTEVENT_CLASS_KEYBOARD;
350 if (HasAttribute("mousedownscript"))
351 event_classes |= PP_INPUTEVENT_CLASS_MOUSE;
352 RequestFilteringInputEvents(event_classes);
353
354 return true;
355 }
356
HandleInputEvent(const pp::InputEvent & event)357 virtual bool HandleInputEvent(const pp::InputEvent& event) override {
358 switch (event.GetType()) {
359 case PP_INPUTEVENT_TYPE_MOUSEDOWN:
360 if (HasAttribute("mousedownscript"))
361 ExecuteScript(attributes_["mousedownscript"]);
362 return true;
363 case PP_INPUTEVENT_TYPE_KEYDOWN:
364 if (HasAttribute("keydownscript"))
365 ExecuteScript(attributes_["keydownscript"]);
366 return true;
367 default:
368 return false;
369 }
370 }
371
372 // pp::InstancePrivate overrides:
GetInstanceObject()373 pp::Var GetInstanceObject() override {
374 if (instance_var_.is_undefined()) {
375 instance_so_ = new InstanceSO(this);
376 instance_var_ = pp::VarPrivate(this, instance_so_);
377 }
378 return instance_var_;
379 }
380
NotifyTestCompletion()381 void NotifyTestCompletion() {
382 ExecuteScript("window.testRunner.notifyDone()");
383 }
384
TestWindowOpen()385 bool TestWindowOpen() {
386 pp::Var result = GetWindowObject().Call(
387 pp::Var("open"), pp::Var("about:blank"), pp::Var("_blank"));
388 if (result.is_object())
389 LogMessage("PLUGIN: WINDOW OPEN SUCCESS");
390 NotifyTestCompletion();
391 return true;
392 }
393
LogMessage(const char * format...)394 void LogMessage(const char* format...) {
395 va_list args;
396 va_start(args, format);
397 LogToConsoleWithSource(PP_LOGLEVEL_LOG,
398 pp::Var("Blink Deprecated Test Plugin"),
399 pp::Var(base::StringPrintV(format, args)));
400 va_end(args);
401 }
402
403 private:
HasAttribute(const std::string & name)404 bool HasAttribute(const std::string& name) {
405 return attributes_.find(name) != attributes_.end();
406 }
407
408 std::unordered_map<std::string, std::string> attributes_;
409 pp::VarPrivate instance_var_;
410 // Owned by |instance_var_|.
411 InstanceSO* instance_so_;
412 };
413
414 class BlinkDeprecatedTestModule : public pp::Module {
415 public:
BlinkDeprecatedTestModule()416 BlinkDeprecatedTestModule() {}
~BlinkDeprecatedTestModule()417 ~BlinkDeprecatedTestModule() override {}
418
CreateInstance(PP_Instance instance)419 pp::Instance* CreateInstance(PP_Instance instance) override {
420 return new BlinkDeprecatedTestInstance(instance);
421 }
422 };
423
424 } // namespace
425
426 namespace pp {
427
CreateModule()428 Module* CreateModule() {
429 return new BlinkDeprecatedTestModule();
430 }
431
432 } // namespace pp
433