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