1// Copyright (c) 2012 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// Tests for CppBoundClass, in conjunction with CppBindingExample.  Binds
6// a CppBindingExample class into JavaScript in a custom test shell and tests
7// the binding from the outside by loading JS into the shell.
8
9#include "base/strings/utf_string_conversions.h"
10#include "content/public/common/url_constants.h"
11#include "content/public/renderer/render_view_observer.h"
12#include "content/public/test/render_view_test.h"
13#include "content/test/cpp_binding_example.h"
14#include "third_party/WebKit/public/platform/WebURLRequest.h"
15#include "third_party/WebKit/public/web/WebDocument.h"
16#include "third_party/WebKit/public/web/WebElement.h"
17
18using webkit_glue::CppArgumentList;
19using webkit_glue::CppVariant;
20
21namespace content {
22
23namespace {
24
25class CppBindingExampleSubObject : public CppBindingExample {
26 public:
27  CppBindingExampleSubObject() {
28    sub_value_.Set("sub!");
29    BindProperty("sub_value", &sub_value_);
30  }
31 private:
32  CppVariant sub_value_;
33};
34
35
36class CppBindingExampleWithOptionalFallback : public CppBindingExample {
37 public:
38  CppBindingExampleWithOptionalFallback() {
39    BindProperty("sub_object", sub_object_.GetAsCppVariant());
40  }
41
42  void set_fallback_method_enabled(bool state) {
43    BindFallbackCallback(state ?
44        base::Bind(&CppBindingExampleWithOptionalFallback::fallbackMethod,
45                   base::Unretained(this))
46        : CppBoundClass::Callback());
47  }
48
49  // The fallback method does nothing, but because of it the JavaScript keeps
50  // running when a nonexistent method is called on an object.
51  void fallbackMethod(const CppArgumentList& args, CppVariant* result) {
52  }
53
54 private:
55  CppBindingExampleSubObject sub_object_;
56};
57
58class TestObserver : public RenderViewObserver {
59 public:
60  explicit TestObserver(RenderView* render_view)
61      : RenderViewObserver(render_view) {}
62  virtual void DidClearWindowObject(blink::WebFrame* frame) OVERRIDE {
63    example_bound_class_.BindToJavascript(frame, "example");
64  }
65  void set_fallback_method_enabled(bool use_fallback) {
66    example_bound_class_.set_fallback_method_enabled(use_fallback);
67  }
68 private:
69  CppBindingExampleWithOptionalFallback example_bound_class_;
70};
71
72}  // namespace
73
74class CppBoundClassTest : public RenderViewTest {
75 public:
76  CppBoundClassTest() {}
77
78  virtual void SetUp() OVERRIDE {
79    RenderViewTest::SetUp();
80    observer_.reset(new TestObserver(view_));
81    observer_->set_fallback_method_enabled(useFallback());
82
83    blink::WebURLRequest url_request;
84    url_request.initialize();
85    url_request.setURL(GURL(kAboutBlankURL));
86
87    GetMainFrame()->loadRequest(url_request);
88    ProcessPendingMessages();
89  }
90
91  virtual void TearDown() OVERRIDE {
92    observer_.reset();
93    RenderViewTest::TearDown();
94  }
95
96  // Executes the specified JavaScript and checks that the resulting document
97  // text is empty.
98  void CheckJavaScriptFailure(const std::string& javascript) {
99    ExecuteJavaScript(javascript.c_str());
100    EXPECT_EQ(
101        "",
102        UTF16ToASCII(GetMainFrame()->document().documentElement().innerText()));
103  }
104
105  void CheckTrue(const std::string& expression) {
106    int was_page_a = -1;
107    base::string16 check_page_a =
108        ASCIIToUTF16(std::string("Number(") + expression + ")");
109    EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
110    EXPECT_EQ(1, was_page_a);
111  }
112
113 protected:
114  virtual bool useFallback() {
115    return false;
116  }
117
118 private:
119  scoped_ptr<TestObserver> observer_;
120};
121
122class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest {
123 protected:
124  virtual bool useFallback() OVERRIDE {
125    return true;
126  }
127};
128
129// Ensures that the example object has been bound to JS.
130TEST_F(CppBoundClassTest, ObjectExists) {
131  CheckTrue("typeof window.example == 'object'");
132
133  // An additional check to test our test.
134  CheckTrue("typeof window.invalid_object == 'undefined'");
135}
136
137TEST_F(CppBoundClassTest, PropertiesAreInitialized) {
138  CheckTrue("example.my_value == 10");
139
140  CheckTrue("example.my_other_value == 'Reinitialized!'");
141}
142
143TEST_F(CppBoundClassTest, SubOject) {
144  CheckTrue("typeof window.example.sub_object == 'object'");
145
146  CheckTrue("example.sub_object.sub_value == 'sub!'");
147}
148
149TEST_F(CppBoundClassTest, SetAndGetProperties) {
150  // The property on the left will be set to the value on the right, then
151  // checked to make sure it holds that same value.
152  static const std::string tests[] = {
153    "example.my_value", "7",
154    "example.my_value", "'test'",
155    "example.my_other_value", "3.14",
156    "example.my_other_value", "false",
157    "" // Array end marker: insert additional test pairs before this.
158  };
159
160  for (int i = 0; tests[i] != ""; i += 2) {
161    std::string left = tests[i];
162    std::string right = tests[i + 1];
163    // left = right;
164    std::string js = left;
165    js.append(" = ");
166    js.append(right);
167    js.append(";");
168    ExecuteJavaScript(js.c_str());
169    std::string expression = left;
170    expression += " == ";
171    expression += right;
172    CheckTrue(expression);
173  }
174}
175
176TEST_F(CppBoundClassTest, SetAndGetPropertiesWithCallbacks) {
177  // TODO(dglazkov): fix NPObject issues around failing property setters and
178  // getters and add tests for situations when GetProperty or SetProperty fail.
179  ExecuteJavaScript("example.my_value_with_callback = 10;");
180  CheckTrue("example.my_value_with_callback == 10");
181
182  ExecuteJavaScript("example.my_value_with_callback = 11;");
183  CheckTrue("example.my_value_with_callback == 11");
184
185  CheckTrue("example.same == 42");
186
187  ExecuteJavaScript("example.same = 24;");
188  CheckTrue("example.same == 42");
189}
190
191TEST_F(CppBoundClassTest, InvokeMethods) {
192  // The expression on the left is expected to return the value on the right.
193  static const std::string tests[] = {
194    "example.echoValue(true) == true",
195    "example.echoValue(13) == 13",
196    "example.echoValue(2.718) == 2.718",
197    "example.echoValue('yes') == 'yes'",
198    "example.echoValue() == null",     // Too few arguments
199
200    "example.echoType(false) == true",
201    "example.echoType(19) == 3.14159",
202    "example.echoType(9.876) == 3.14159",
203    "example.echoType('test string') == 'Success!'",
204    "example.echoType() == null",      // Too few arguments
205
206    // Comparing floats that aren't integer-valued is usually problematic due
207    // to rounding, but exact powers of 2 should also be safe.
208    "example.plus(2.5, 18.0) == 20.5",
209    "example.plus(2, 3.25) == 5.25",
210    "example.plus(2, 3) == 5",
211    "example.plus() == null",             // Too few arguments
212    "example.plus(1) == null",            // Too few arguments
213    "example.plus(1, 'test') == null",    // Wrong argument type
214    "example.plus('test', 2) == null",    // Wrong argument type
215    "example.plus('one', 'two') == null", // Wrong argument type
216    "" // Array end marker: insert additional test pairs before this.
217  };
218
219  for (int i = 0; tests[i] != ""; i++)
220    CheckTrue(tests[i]);
221
222  ExecuteJavaScript("example.my_value = 3.25; example.my_other_value = 1.25;");
223  CheckTrue("example.plus(example.my_value, example.my_other_value) == 4.5");
224}
225
226// Tests that invoking a nonexistent method with no fallback method stops the
227// script's execution
228TEST_F(CppBoundClassTest,
229       InvokeNonexistentMethodNoFallback) {
230  std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');";
231  CheckJavaScriptFailure(js);
232}
233
234// Ensures existent methods can be invoked successfully when the fallback method
235// is used
236TEST_F(CppBoundClassWithFallbackMethodTest,
237       InvokeExistentMethodsWithFallback) {
238  CheckTrue("example.echoValue(34) == 34");
239}
240
241}  // namespace content
242