• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2006-2008 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 <vector>
10 
11 #include "base/message_loop.h"
12 #include "base/string_util.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
17 #include "webkit/glue/cpp_binding_example.h"
18 #include "webkit/glue/webkit_glue.h"
19 #include "webkit/tools/test_shell/test_shell_test.h"
20 
21 using WebKit::WebFrame;
22 
23 namespace {
24 
25 class CppBindingExampleSubObject : public CppBindingExample {
26  public:
CppBindingExampleSubObject()27   CppBindingExampleSubObject() {
28     sub_value_.Set("sub!");
29     BindProperty("sub_value", &sub_value_);
30   }
31  private:
32   CppVariant sub_value_;
33 };
34 
35 
36 class CppBindingExampleWithOptionalFallback : public CppBindingExample {
37  public:
CppBindingExampleWithOptionalFallback()38   CppBindingExampleWithOptionalFallback() {
39     BindProperty("sub_object", sub_object_.GetAsCppVariant());
40   }
41 
set_fallback_method_enabled(bool state)42   void set_fallback_method_enabled(bool state) {
43     BindFallbackMethod(state ?
44         &CppBindingExampleWithOptionalFallback::fallbackMethod
45         : NULL);
46   }
47 
48   // The fallback method does nothing, but because of it the JavaScript keeps
49   // running when a nonexistent method is called on an object.
fallbackMethod(const CppArgumentList & args,CppVariant * result)50   void fallbackMethod(const CppArgumentList& args, CppVariant* result) {
51   }
52 
53  private:
54   CppBindingExampleSubObject sub_object_;
55 };
56 
57 class ExampleTestShell : public TestShell {
58  public:
59 
ExampleTestShell(bool use_fallback_method)60   ExampleTestShell(bool use_fallback_method) {
61     example_bound_class_.set_fallback_method_enabled(use_fallback_method);
62   }
63 
64   // When called by WebViewDelegate::WindowObjectCleared method, this binds a
65   // CppExampleObject to window.example.
BindJSObjectsToWindow(WebFrame * frame)66   virtual void BindJSObjectsToWindow(WebFrame* frame) {
67     example_bound_class_.BindToJavascript(frame, "example");
68     // We use the layoutTestController binding for notifyDone.
69     TestShell::BindJSObjectsToWindow(frame);
70   }
71 
72   // This is a public interface to TestShell's protected method, so it
73   // can be called by our CreateEmptyWindow.
PublicInitialize(const std::string & starting_url)74   bool PublicInitialize(const std::string& starting_url) {
75     return Initialize(GURL(starting_url));
76   }
77 
78   CppBindingExampleWithOptionalFallback example_bound_class_;
79 };
80 
81 class CppBoundClassTest : public TestShellTest {
82  protected:
83    // Adapted from TestShell::CreateNewWindow, this creates an
84    // ExampleTestShellWindow rather than a regular TestShell.
CreateEmptyWindow()85    virtual void CreateEmptyWindow() {
86      ExampleTestShell* host = new ExampleTestShell(useFallback());
87      ASSERT_TRUE(host != NULL);
88      bool rv = host->PublicInitialize("about:blank");
89      if (rv) {
90        test_shell_ = host;
91        TestShell::windowList()->push_back(host->mainWnd());
92        webframe_ = test_shell_->webView()->mainFrame();
93        ASSERT_TRUE(webframe_ != NULL);
94      } else {
95        delete host;
96      }
97    }
98 
99    // Wraps the given JavaScript snippet in <html><body><script> tags, then
100    // loads it into a webframe so it is executed.
ExecuteJavaScript(const std::string & javascript)101    void ExecuteJavaScript(const std::string& javascript) {
102      std::string html = "<html><body>";
103      html.append(TestShellTest::kJavascriptDelayExitScript);
104      html.append("<script>");
105      html.append(javascript);
106      html.append("</script></body></html>");
107      // The base URL doesn't matter.
108      webframe_->loadHTMLString(html, GURL("about:blank"));
109 
110      test_shell_->WaitTestFinished();
111     }
112 
113    // Executes the specified JavaScript and checks to be sure that the resulting
114    // document text is exactly "SUCCESS".
CheckJavaScriptSuccess(const std::string & javascript)115    void CheckJavaScriptSuccess(const std::string& javascript) {
116      ExecuteJavaScript(javascript);
117      EXPECT_EQ("SUCCESS",
118                UTF16ToASCII(webkit_glue::DumpDocumentText(webframe_)));
119    }
120 
121    // Executes the specified JavaScript and checks that the resulting document
122    // text is empty.
CheckJavaScriptFailure(const std::string & javascript)123    void CheckJavaScriptFailure(const std::string& javascript) {
124      ExecuteJavaScript(javascript);
125      EXPECT_EQ("", UTF16ToASCII(webkit_glue::DumpDocumentText(webframe_)));
126    }
127 
128    // Constructs a JavaScript snippet that evaluates and compares the left and
129    // right expressions, printing "SUCCESS" to the page if they are equal and
130    // printing their actual values if they are not.  Any strings in the
131    // expressions should be enclosed in single quotes, and no double quotes
132    // should appear in either expression (even if escaped). (If a test case
133    // is added that needs fancier quoting, Json::valueToQuotedString could be
134    // used here.  For now, it's not worth adding the dependency.)
BuildJSCondition(std::string left,std::string right)135    std::string BuildJSCondition(std::string left, std::string right) {
136      return "var leftval = " + left + ";" +
137             "var rightval = " + right + ";" +
138             "if (leftval == rightval) {" +
139             "  document.writeln('SUCCESS');" +
140             "} else {" +
141             "  document.writeln(\"" +
142                  left + " [\" + leftval + \"] != " +
143                  right + " [\" + rightval + \"]\");" +
144             "}";
145    }
146 
147 protected:
useFallback()148   virtual bool useFallback() {
149     return false;
150   }
151 
152 private:
153   WebFrame* webframe_;
154 };
155 
156 class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest {
157 protected:
useFallback()158   virtual bool useFallback() {
159     return true;
160   }
161 };
162 
163 // Ensures that the example object has been bound to JS.
TEST_F(CppBoundClassTest,ObjectExists)164 TEST_F(CppBoundClassTest, ObjectExists) {
165   std::string js = BuildJSCondition("typeof window.example", "'object'");
166   CheckJavaScriptSuccess(js);
167 
168   // An additional check to test our test.
169   js = BuildJSCondition("typeof window.invalid_object", "'undefined'");
170   CheckJavaScriptSuccess(js);
171 }
172 
TEST_F(CppBoundClassTest,PropertiesAreInitialized)173 TEST_F(CppBoundClassTest, PropertiesAreInitialized) {
174   std::string js = BuildJSCondition("example.my_value", "10");
175   CheckJavaScriptSuccess(js);
176 
177   js = BuildJSCondition("example.my_other_value", "'Reinitialized!'");
178   CheckJavaScriptSuccess(js);
179 }
180 
TEST_F(CppBoundClassTest,SubOject)181 TEST_F(CppBoundClassTest, SubOject) {
182   std::string js = BuildJSCondition("typeof window.example.sub_object",
183                                     "'object'");
184   CheckJavaScriptSuccess(js);
185 
186   js = BuildJSCondition("example.sub_object.sub_value", "'sub!'");
187   CheckJavaScriptSuccess(js);
188 }
189 
TEST_F(CppBoundClassTest,SetAndGetProperties)190 TEST_F(CppBoundClassTest, SetAndGetProperties) {
191   // The property on the left will be set to the value on the right, then
192   // checked to make sure it holds that same value.
193   static const std::string tests[] = {
194     "example.my_value", "7",
195     "example.my_value", "'test'",
196     "example.my_other_value", "3.14",
197     "example.my_other_value", "false",
198     "" // Array end marker: insert additional test pairs before this.
199   };
200 
201   for (int i = 0; tests[i] != ""; i += 2) {
202     std::string left = tests[i];
203     std::string right = tests[i + 1];
204     // left = right;
205     std::string js = left;
206     js.append(" = ");
207     js.append(right);
208     js.append(";");
209     js.append(BuildJSCondition(left, right));
210     CheckJavaScriptSuccess(js);
211   }
212 }
213 
TEST_F(CppBoundClassTest,SetAndGetPropertiesWithCallbacks)214 TEST_F(CppBoundClassTest, SetAndGetPropertiesWithCallbacks) {
215   // TODO(dglazkov): fix NPObject issues around failing property setters and
216   // getters and add tests for situations when GetProperty or SetProperty fail.
217   std::string js = "var result = 'SUCCESS';\n"
218     "example.my_value_with_callback = 10;\n"
219     "if (example.my_value_with_callback != 10)\n"
220     "  result = 'FAIL: unable to set property.';\n"
221     "example.my_value_with_callback = 11;\n"
222     "if (example.my_value_with_callback != 11)\n"
223     "  result = 'FAIL: unable to set property again';\n"
224     "if (example.same != 42)\n"
225     "  result = 'FAIL: same property should always be 42';\n"
226     "example.same = 24;\n"
227     "if (example.same != 42)\n"
228     "  result = 'FAIL: same property should always be 42';\n"
229     "document.writeln(result);\n";
230   CheckJavaScriptSuccess(js);
231 }
232 
TEST_F(CppBoundClassTest,InvokeMethods)233 TEST_F(CppBoundClassTest, InvokeMethods) {
234   // The expression on the left is expected to return the value on the right.
235   static const std::string tests[] = {
236     "example.echoValue(true)", "true",
237     "example.echoValue(13)", "13",
238     "example.echoValue(2.718)", "2.718",
239     "example.echoValue('yes')", "'yes'",
240     "example.echoValue()", "null",     // Too few arguments
241 
242     "example.echoType(false)", "true",
243     "example.echoType(19)", "3.14159",
244     "example.echoType(9.876)", "3.14159",
245     "example.echoType('test string')", "'Success!'",
246     "example.echoType()", "null",      // Too few arguments
247 
248     // Comparing floats that aren't integer-valued is usually problematic due
249     // to rounding, but exact powers of 2 should also be safe.
250     "example.plus(2.5, 18.0)", "20.5",
251     "example.plus(2, 3.25)", "5.25",
252     "example.plus(2, 3)", "5",
253     "example.plus()", "null",             // Too few arguments
254     "example.plus(1)", "null",            // Too few arguments
255     "example.plus(1, 'test')", "null",    // Wrong argument type
256     "example.plus('test', 2)", "null",    // Wrong argument type
257     "example.plus('one', 'two')", "null", // Wrong argument type
258     "" // Array end marker: insert additional test pairs before this.
259   };
260 
261   for (int i = 0; tests[i] != ""; i+= 2) {
262     std::string left = tests[i];
263     std::string right = tests[i + 1];
264     std::string js = BuildJSCondition(left, right);
265     CheckJavaScriptSuccess(js);
266   }
267 
268   std::string js = "example.my_value = 3.25; example.my_other_value = 1.25;";
269   js.append(BuildJSCondition(
270       "example.plus(example.my_value, example.my_other_value)", "4.5"));
271   CheckJavaScriptSuccess(js);
272 }
273 
274 // Tests that invoking a nonexistent method with no fallback method stops the
275 // script's execution
TEST_F(CppBoundClassTest,InvokeNonexistentMethodNoFallback)276 TEST_F(CppBoundClassTest,
277        InvokeNonexistentMethodNoFallback) {
278   std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');";
279   CheckJavaScriptFailure(js);
280 }
281 
282 // Ensures existent methods can be invoked successfully when the fallback method
283 // is used
TEST_F(CppBoundClassWithFallbackMethodTest,InvokeExistentMethodsWithFallback)284 TEST_F(CppBoundClassWithFallbackMethodTest,
285        InvokeExistentMethodsWithFallback) {
286   std::string js = BuildJSCondition("example.echoValue(34)", "34");
287   CheckJavaScriptSuccess(js);
288 }
289 
290 }  // namespace
291