diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-14 06:10:31 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-14 06:10:31 +0000 |
commit | ad23b99078f3db28bf5e88821cadc2d5effac52b (patch) | |
tree | 0df04a0908e71c383853e71f7586c381cbc4b9c8 /chrome/renderer/extensions | |
parent | 0c9366d5f357759e3bd968a267b6eb1100e5835c (diff) | |
download | chromium_src-ad23b99078f3db28bf5e88821cadc2d5effac52b.zip chromium_src-ad23b99078f3db28bf5e88821cadc2d5effac52b.tar.gz chromium_src-ad23b99078f3db28bf5e88821cadc2d5effac52b.tar.bz2 |
Add JsonSchema-based validation for the tab APIs.
Arv: can you take json_schema.js and json_schema_test.js.
Matt: you take the rest.
Review URL: http://codereview.chromium.org/66006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13649 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer/extensions')
4 files changed, 302 insertions, 28 deletions
diff --git a/chrome/renderer/extensions/extension_api_client_unittest.cc b/chrome/renderer/extensions/extension_api_client_unittest.cc new file mode 100755 index 0000000..1c3530f --- /dev/null +++ b/chrome/renderer/extensions/extension_api_client_unittest.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2009 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. + +#include "chrome/common/render_messages.h" +#include "chrome/renderer/extensions/extension_process_bindings.h" +#include "chrome/renderer/extensions/renderer_extension_bindings.h" +#include "chrome/test/render_view_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +class ExtensionAPIClientTest : public RenderViewTest { + protected: + virtual void SetUp() { + RenderViewTest::SetUp(); + + render_thread_.sink().ClearMessages(); + LoadHTML("<body></body>"); + } + + std::string GetConsoleMessage() { + const IPC::Message* message = + render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_AddMessageToConsole::ID); + ViewHostMsg_AddMessageToConsole::Param params; + if (message) { + ViewHostMsg_AddMessageToConsole::Read(message, ¶ms); + render_thread_.sink().ClearMessages(); + return WideToASCII(params.a); + } else { + return ""; + } + } + + void ExpectJsFail(const std::string& js, const std::string& message) { + ExecuteJavaScript(js.c_str()); + EXPECT_EQ(message, GetConsoleMessage()); + } +}; + +// Tests that callback dispatching works correctly and that JSON is properly +// deserialized before handing off to the extension code. We use the createTab +// API here, but we could use any of them since they all dispatch callbacks the +// same way. +TEST_F(ExtensionAPIClientTest, CallbackDispatching) { + ExecuteJavaScript( + "function assert(truth, message) {" + " if (!truth) {" + " throw new Error(message);" + " }" + "}" + "function callback(result) {" + " assert(typeof result == 'object', 'result not object');" + " assert(goog.json.serialize(result) == '{\"foo\":\"bar\"}', " + " 'incorrect result');" + " console.log('pass')" + "}" + "chromium.tabs.createTab({}, callback);" + ); + + // Ok, we should have gotten a message to create a tab, grab the callback ID. + const IPC::Message* request_msg = + render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_ExtensionRequest::ID); + ASSERT_TRUE(request_msg); + ViewHostMsg_ExtensionRequest::Param params; + ViewHostMsg_ExtensionRequest::Read(request_msg, ¶ms); + int callback_id = params.c; + ASSERT_TRUE(callback_id >= 0); + + // Now send the callback a response + ExtensionProcessBindings::ExecuteCallbackInFrame( + GetMainFrame(), callback_id, "{\"foo\":\"bar\"}"); + + // And verify that it worked + ASSERT_EQ("pass", GetConsoleMessage()); +} + +// The remainder of these tests exercise the client side of the various +// extension functions. We test both error and success conditions, but do not +// test errors exhaustively as json schema code is well tested by itself. + +TEST_F(ExtensionAPIClientTest, GetTabsForWindow) { + ExpectJsFail("chromium.tabs.getTabsForWindow(42, function(){});", + "Uncaught Error: Too many arguments."); + + ExecuteJavaScript("chromium.tabs.getTabsForWindow(function(){})"); + const IPC::Message* request_msg = + render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_ExtensionRequest::ID); + ASSERT_TRUE(request_msg); + ViewHostMsg_ExtensionRequest::Param params; + ViewHostMsg_ExtensionRequest::Read(request_msg, ¶ms); + ASSERT_EQ("GetTabsForWindow", params.a); + ASSERT_EQ("null", params.b); +} + +TEST_F(ExtensionAPIClientTest, GetTab) { + ExpectJsFail("chromium.tabs.getTab(null, function(){});", + "Uncaught Error: Argument 0 is required."); + + ExecuteJavaScript("chromium.tabs.getTab(42)"); + const IPC::Message* request_msg = + render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_ExtensionRequest::ID); + ASSERT_TRUE(request_msg); + ViewHostMsg_ExtensionRequest::Param params; + ViewHostMsg_ExtensionRequest::Read(request_msg, ¶ms); + ASSERT_EQ("GetTab", params.a); + ASSERT_EQ("42", params.b); +} + +TEST_F(ExtensionAPIClientTest, CreateTab) { + ExpectJsFail("chromium.tabs.createTab({windowId: 'foo'}, function(){});", + "Uncaught Error: Invalid value for argument 0. Property " + "'windowId': Expected 'integer' but got 'string'."); + ExpectJsFail("chromium.tabs.createTab({url: 42}, function(){});", + "Uncaught Error: Invalid value for argument 0. Property " + "'url': Expected 'string' but got 'integer'."); + ExpectJsFail("chromium.tabs.createTab({selected: null}, function(){});", + "Uncaught Error: Invalid value for argument 0. Property " + "'selected': Expected 'boolean' but got 'null'."); + + ExecuteJavaScript("chromium.tabs.createTab({" + " url:'http://www.google.com/'," + " selected:true," + " windowId:4" + "})"); + const IPC::Message* request_msg = + render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_ExtensionRequest::ID); + ASSERT_TRUE(request_msg); + ViewHostMsg_ExtensionRequest::Param params; + ViewHostMsg_ExtensionRequest::Read(request_msg, ¶ms); + ASSERT_EQ("CreateTab", params.a); + ASSERT_EQ("{\"url\":\"http://www.google.com/\"," + "\"selected\":true," + "\"windowId\":4}", params.b); +} + +TEST_F(ExtensionAPIClientTest, UpdateTab) { + ExpectJsFail("chromium.tabs.updateTab({id: null});", + "Uncaught Error: Invalid value for argument 0. Property " + "'id': Expected 'integer' but got 'null'."); + ExpectJsFail("chromium.tabs.updateTab({id: 42, windowId: 'foo'});", + "Uncaught Error: Invalid value for argument 0. Property " + "'windowId': Expected 'integer' but got 'string'."); + ExpectJsFail("chromium.tabs.updateTab({id: 42, url: 42});", + "Uncaught Error: Invalid value for argument 0. Property " + "'url': Expected 'string' but got 'integer'."); + ExpectJsFail("chromium.tabs.updateTab({id: 42, selected: null});", + "Uncaught Error: Invalid value for argument 0. Property " + "'selected': Expected 'boolean' but got 'null'."); + + ExecuteJavaScript("chromium.tabs.updateTab({" + " id:42," + " url:'http://www.google.com/'," + " selected:true," + " windowId:4" + "})"); + const IPC::Message* request_msg = + render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_ExtensionRequest::ID); + ASSERT_TRUE(request_msg); + ViewHostMsg_ExtensionRequest::Param params; + ViewHostMsg_ExtensionRequest::Read(request_msg, ¶ms); + ASSERT_EQ("UpdateTab", params.a); + ASSERT_EQ("{\"id\":42," + "\"url\":\"http://www.google.com/\"," + "\"selected\":true," + "\"windowId\":4}", params.b); +} + +TEST_F(ExtensionAPIClientTest, RemoveTab) { + ExpectJsFail("chromium.tabs.removeTab('foobar', function(){});", + "Uncaught Error: Too many arguments."); + + ExecuteJavaScript("chromium.tabs.removeTab(21)"); + const IPC::Message* request_msg = + render_thread_.sink().GetUniqueMessageMatching( + ViewHostMsg_ExtensionRequest::ID); + ASSERT_TRUE(request_msg); + ViewHostMsg_ExtensionRequest::Param params; + ViewHostMsg_ExtensionRequest::Read(request_msg, ¶ms); + ASSERT_EQ("RemoveTab", params.a); + ASSERT_EQ("21", params.b); +} diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 2d8f3db..4bc4409 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -18,14 +18,17 @@ using WebKit::WebString; namespace { const char kExtensionName[] = "chrome/ExtensionProcessBindings"; -const char* kExtensionDeps[] = { JsonJsV8Extension::kName }; +const char* kDeps[] = { + BaseJsV8Extension::kName, + JsonJsV8Extension::kName, + JsonSchemaJsV8Extension::kName +}; class ExtensionImpl : public v8::Extension { public: ExtensionImpl() : v8::Extension( kExtensionName, GetStringResource<IDR_EXTENSION_PROCESS_BINDINGS_JS>(), - arraysize(kExtensionDeps), kExtensionDeps) { - } + arraysize(kDeps), kDeps) {} static void SetFunctionNames(const std::vector<std::string>& names) { function_names_ = new std::set<std::string>(); diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h index 8246b4b..01c335b 100644 --- a/chrome/renderer/extensions/extension_process_bindings.h +++ b/chrome/renderer/extensions/extension_process_bindings.h @@ -1,25 +1,26 @@ -// Copyright (c) 2009 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.
-
-// Exposes extension APIs into the extension process.
-
-#ifndef CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
-#define CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
-
-#include <string>
-#include <vector>
-
-#include "v8/include/v8.h"
-
-class WebFrame;
-
-class ExtensionProcessBindings {
- public:
- static void SetFunctionNames(const std::vector<std::string>& names);
- static v8::Extension* Get();
- static void ExecuteCallbackInFrame(WebFrame* frame, int callback_id,
- const std::string& response);
-};
-
-#endif // CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
+// Copyright (c) 2009 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. + +// Exposes extension APIs into the extension process. + +#ifndef CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_ +#define CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_ + +#include <string> +#include <vector> + +#include "v8/include/v8.h" + +class RenderThreadBase; +class WebFrame; + +class ExtensionProcessBindings { + public: + static void SetFunctionNames(const std::vector<std::string>& names); + static v8::Extension* Get(); + static void ExecuteCallbackInFrame(WebFrame* frame, int callback_id, + const std::string& response); +}; + +#endif // CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_ diff --git a/chrome/renderer/extensions/json_schema_unittest.cc b/chrome/renderer/extensions/json_schema_unittest.cc new file mode 100755 index 0000000..7df624d --- /dev/null +++ b/chrome/renderer/extensions/json_schema_unittest.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2009 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. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/test/v8_unit_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "grit/renderer_resources.h" + +// TODO(port) +#if defined(OS_WIN) + +static const char kJsonSchema[] = "json_schema.js"; +static const char kJsonSchemaTest[] = "json_schema_test.js"; + +class JsonSchemaTest : public V8UnitTest { + public: + JsonSchemaTest() {} + + virtual void SetUp() { + V8UnitTest::SetUp(); + + // Add the json schema code to the context. + StringPiece js = ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_JSON_SCHEMA_JS); + ExecuteScriptInContext(js, kJsonSchema); + + // Add the test functions to the context. + std::wstring test_js_file_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_js_file_path)); + file_util::AppendToPath(&test_js_file_path, L"extensions"); + file_util::AppendToPath(&test_js_file_path, UTF8ToWide(kJsonSchemaTest)); + std::string test_js; + ASSERT_TRUE(file_util::ReadFileToString(test_js_file_path, &test_js)); + ExecuteScriptInContext(test_js, kJsonSchemaTest); + } +}; + +TEST_F(JsonSchemaTest, TestFormatError) { + TestFunction("testFormatError"); +} + +TEST_F(JsonSchemaTest, TestComplex) { + TestFunction("testComplex"); +} + +TEST_F(JsonSchemaTest, TestEnum) { + TestFunction("testEnum"); +} + +TEST_F(JsonSchemaTest, TestExtends) { + TestFunction("testExtends"); +} + +TEST_F(JsonSchemaTest, TestObject) { + TestFunction("testObject"); +} + +TEST_F(JsonSchemaTest, TestArrayTuple) { + TestFunction("testArrayTuple"); +} + +TEST_F(JsonSchemaTest, TestArrayNonTuple) { + TestFunction("testArrayNonTuple"); +} + +TEST_F(JsonSchemaTest, TestString) { + TestFunction("testString"); +} + +TEST_F(JsonSchemaTest, TestNumber) { + TestFunction("testNumber"); +} + +TEST_F(JsonSchemaTest, TestType) { + TestFunction("testType"); +} + +#endif // #if defined(OSWIN) |