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