// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Tests for CppBoundClass, in conjunction with CppBindingExample. Binds // a CppBindingExample class into JavaScript in a custom test shell and tests // the binding from the outside by loading JS into the shell. #include "base/strings/utf_string_conversions.h" #include "content/public/common/url_constants.h" #include "content/public/renderer/render_view_observer.h" #include "content/public/test/render_view_test.h" #include "content/test/cpp_binding_example.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" using webkit_glue::CppArgumentList; using webkit_glue::CppVariant; namespace content { namespace { class CppBindingExampleSubObject : public CppBindingExample { public: CppBindingExampleSubObject() { sub_value_.Set("sub!"); BindProperty("sub_value", &sub_value_); } private: CppVariant sub_value_; }; class CppBindingExampleWithOptionalFallback : public CppBindingExample { public: CppBindingExampleWithOptionalFallback() { BindProperty("sub_object", sub_object_.GetAsCppVariant()); } void set_fallback_method_enabled(bool state) { BindFallbackCallback(state ? base::Bind(&CppBindingExampleWithOptionalFallback::fallbackMethod, base::Unretained(this)) : CppBoundClass::Callback()); } // The fallback method does nothing, but because of it the JavaScript keeps // running when a nonexistent method is called on an object. void fallbackMethod(const CppArgumentList& args, CppVariant* result) { } private: CppBindingExampleSubObject sub_object_; }; class TestObserver : public RenderViewObserver { public: explicit TestObserver(RenderView* render_view) : RenderViewObserver(render_view) {} virtual void DidClearWindowObject(blink::WebFrame* frame) OVERRIDE { example_bound_class_.BindToJavascript(frame, "example"); } void set_fallback_method_enabled(bool use_fallback) { example_bound_class_.set_fallback_method_enabled(use_fallback); } private: CppBindingExampleWithOptionalFallback example_bound_class_; }; } // namespace class CppBoundClassTest : public RenderViewTest { public: CppBoundClassTest() {} virtual void SetUp() OVERRIDE { RenderViewTest::SetUp(); observer_.reset(new TestObserver(view_)); observer_->set_fallback_method_enabled(useFallback()); blink::WebURLRequest url_request; url_request.initialize(); url_request.setURL(GURL(kAboutBlankURL)); GetMainFrame()->loadRequest(url_request); ProcessPendingMessages(); } virtual void TearDown() OVERRIDE { observer_.reset(); RenderViewTest::TearDown(); } // Executes the specified JavaScript and checks that the resulting document // text is empty. void CheckJavaScriptFailure(const std::string& javascript) { ExecuteJavaScript(javascript.c_str()); EXPECT_EQ( "", UTF16ToASCII(GetMainFrame()->document().documentElement().innerText())); } void CheckTrue(const std::string& expression) { int was_page_a = -1; base::string16 check_page_a = ASCIIToUTF16(std::string("Number(") + expression + ")"); EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a)); EXPECT_EQ(1, was_page_a); } protected: virtual bool useFallback() { return false; } private: scoped_ptr<TestObserver> observer_; }; class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest { protected: virtual bool useFallback() OVERRIDE { return true; } }; // Ensures that the example object has been bound to JS. TEST_F(CppBoundClassTest, ObjectExists) { CheckTrue("typeof window.example == 'object'"); // An additional check to test our test. CheckTrue("typeof window.invalid_object == 'undefined'"); } TEST_F(CppBoundClassTest, PropertiesAreInitialized) { CheckTrue("example.my_value == 10"); CheckTrue("example.my_other_value == 'Reinitialized!'"); } TEST_F(CppBoundClassTest, SubOject) { CheckTrue("typeof window.example.sub_object == 'object'"); CheckTrue("example.sub_object.sub_value == 'sub!'"); } TEST_F(CppBoundClassTest, SetAndGetProperties) { // The property on the left will be set to the value on the right, then // checked to make sure it holds that same value. static const std::string tests[] = { "example.my_value", "7", "example.my_value", "'test'", "example.my_other_value", "3.14", "example.my_other_value", "false", "" // Array end marker: insert additional test pairs before this. }; for (int i = 0; tests[i] != ""; i += 2) { std::string left = tests[i]; std::string right = tests[i + 1]; // left = right; std::string js = left; js.append(" = "); js.append(right); js.append(";"); ExecuteJavaScript(js.c_str()); std::string expression = left; expression += " == "; expression += right; CheckTrue(expression); } } TEST_F(CppBoundClassTest, SetAndGetPropertiesWithCallbacks) { // TODO(dglazkov): fix NPObject issues around failing property setters and // getters and add tests for situations when GetProperty or SetProperty fail. ExecuteJavaScript("example.my_value_with_callback = 10;"); CheckTrue("example.my_value_with_callback == 10"); ExecuteJavaScript("example.my_value_with_callback = 11;"); CheckTrue("example.my_value_with_callback == 11"); CheckTrue("example.same == 42"); ExecuteJavaScript("example.same = 24;"); CheckTrue("example.same == 42"); } TEST_F(CppBoundClassTest, InvokeMethods) { // The expression on the left is expected to return the value on the right. static const std::string tests[] = { "example.echoValue(true) == true", "example.echoValue(13) == 13", "example.echoValue(2.718) == 2.718", "example.echoValue('yes') == 'yes'", "example.echoValue() == null", // Too few arguments "example.echoType(false) == true", "example.echoType(19) == 3.14159", "example.echoType(9.876) == 3.14159", "example.echoType('test string') == 'Success!'", "example.echoType() == null", // Too few arguments // Comparing floats that aren't integer-valued is usually problematic due // to rounding, but exact powers of 2 should also be safe. "example.plus(2.5, 18.0) == 20.5", "example.plus(2, 3.25) == 5.25", "example.plus(2, 3) == 5", "example.plus() == null", // Too few arguments "example.plus(1) == null", // Too few arguments "example.plus(1, 'test') == null", // Wrong argument type "example.plus('test', 2) == null", // Wrong argument type "example.plus('one', 'two') == null", // Wrong argument type "" // Array end marker: insert additional test pairs before this. }; for (int i = 0; tests[i] != ""; i++) CheckTrue(tests[i]); ExecuteJavaScript("example.my_value = 3.25; example.my_other_value = 1.25;"); CheckTrue("example.plus(example.my_value, example.my_other_value) == 4.5"); } // Tests that invoking a nonexistent method with no fallback method stops the // script's execution TEST_F(CppBoundClassTest, InvokeNonexistentMethodNoFallback) { std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');"; CheckJavaScriptFailure(js); } // Ensures existent methods can be invoked successfully when the fallback method // is used TEST_F(CppBoundClassWithFallbackMethodTest, InvokeExistentMethodsWithFallback) { CheckTrue("example.echoValue(34) == 34"); } } // namespace content