Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 9 | #include "base/strings/utf_string_conversions.h" |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 10 | #include "content/public/common/url_constants.h" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 11 | #include "content/public/renderer/render_view_observer.h" |
| 12 | #include "content/public/test/render_view_test.h" |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 13 | #include "content/test/cpp_binding_example.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 14 | #include "third_party/WebKit/public/platform/WebURLRequest.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 15 | #include "third_party/WebKit/public/web/WebDocument.h" |
| 16 | #include "third_party/WebKit/public/web/WebElement.h" |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 17 | |
| 18 | using webkit_glue::CppArgumentList; |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 19 | using webkit_glue::CppVariant; |
| 20 | |
| 21 | namespace content { |
| 22 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 23 | namespace { |
| 24 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 25 | class 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 | |
| 36 | class 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 | |
| 58 | class TestObserver : public RenderViewObserver { |
| 59 | public: |
| 60 | explicit TestObserver(RenderView* render_view) |
| 61 | : RenderViewObserver(render_view) {} |
| 62 | virtual void DidClearWindowObject(WebKit::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 | |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 72 | } // namespace |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 73 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 74 | class 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 | WebKit::WebURLRequest url_request; |
| 84 | url_request.initialize(); |
Ben Murdoch | 2385ea3 | 2013-08-06 11:01:04 +0100 | [diff] [blame] | 85 | url_request.setURL(GURL(kAboutBlankURL)); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 86 | |
| 87 | GetMainFrame()->loadRequest(url_request); |
| 88 | ProcessPendingMessages(); |
| 89 | } |
| 90 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 91 | virtual void TearDown() OVERRIDE { |
| 92 | observer_.reset(); |
| 93 | RenderViewTest::TearDown(); |
| 94 | } |
| 95 | |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 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()); |
Torne (Richard Coles) | b2df76e | 2013-05-13 16:52:09 +0100 | [diff] [blame] | 100 | EXPECT_EQ( |
| 101 | "", |
| 102 | UTF16ToASCII(GetMainFrame()->document().documentElement().innerText())); |
Torne (Richard Coles) | c2e0dbd | 2013-05-09 18:35:53 +0100 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | void CheckTrue(const std::string& expression) { |
| 106 | int was_page_a = -1; |
| 107 | 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 | |
| 122 | class 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. |
| 130 | TEST_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 | |
| 137 | TEST_F(CppBoundClassTest, PropertiesAreInitialized) { |
| 138 | CheckTrue("example.my_value == 10"); |
| 139 | |
| 140 | CheckTrue("example.my_other_value == 'Reinitialized!'"); |
| 141 | } |
| 142 | |
| 143 | TEST_F(CppBoundClassTest, SubOject) { |
| 144 | CheckTrue("typeof window.example.sub_object == 'object'"); |
| 145 | |
| 146 | CheckTrue("example.sub_object.sub_value == 'sub!'"); |
| 147 | } |
| 148 | |
| 149 | TEST_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 | |
| 176 | TEST_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 | |
| 191 | TEST_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 |
| 228 | TEST_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 |
| 236 | TEST_F(CppBoundClassWithFallbackMethodTest, |
| 237 | InvokeExistentMethodsWithFallback) { |
| 238 | CheckTrue("example.echoValue(34) == 34"); |
| 239 | } |
| 240 | |
| 241 | } // namespace content |