blob: 3ed465224544f5f865c0d7e4799ee11d0a42f4aa [file] [log] [blame]
// Copyright 2017 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 "extensions/renderer/messaging_util.h"
#include <memory>
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "extensions/common/api/messaging/message.h"
#include "extensions/common/extension_builder.h"
#include "extensions/renderer/bindings/api_binding_test.h"
#include "extensions/renderer/bindings/api_binding_test_util.h"
#include "extensions/renderer/native_extension_bindings_system_test_base.h"
#include "extensions/renderer/script_context.h"
#include "gin/converter.h"
#include "v8/include/v8.h"
namespace extensions {
using MessagingUtilTest = APIBindingTest;
TEST_F(MessagingUtilTest, TestMaximumMessageSize) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
constexpr char kMessageTooLongError[] =
"Message length exceeded maximum allowed length.";
{
v8::Local<v8::Value> long_message =
V8ValueFromScriptSource(context, "'a'.repeat(1024 *1024 * 65)");
std::string error;
std::unique_ptr<Message> message =
messaging_util::MessageFromV8(context, long_message, &error);
EXPECT_FALSE(message);
EXPECT_EQ(kMessageTooLongError, error);
}
{
v8::Local<v8::Value> long_json_message = V8ValueFromScriptSource(
context, "(JSON.stringify('a'.repeat(1024 *1024 * 65)))");
ASSERT_TRUE(long_json_message->IsString());
std::string error;
std::unique_ptr<Message> message = messaging_util::MessageFromV8(
context, long_json_message.As<v8::String>(), &error);
EXPECT_FALSE(message);
EXPECT_EQ(kMessageTooLongError, error);
}
}
TEST_F(MessagingUtilTest, TestParseMessageOptionsFrameId) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
struct {
int expected_frame_id;
const char* string_options;
} test_cases[] = {
{messaging_util::kNoFrameId, "({})"},
{messaging_util::kNoFrameId, "({frameId: undefined})"},
// Note: we don't test null here, because the argument parsing code
// ensures we would never pass undefined to ParseMessageOptions (and
// there's a DCHECK to validate it). The null case is tested in the tabs'
// API hooks delegate test.
{0, "({frameId: 0})"},
{2, "({frameId: 2})"},
};
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.string_options);
v8::Local<v8::Value> value =
V8ValueFromScriptSource(context, test_case.string_options);
ASSERT_FALSE(value.IsEmpty());
ASSERT_TRUE(value->IsObject());
messaging_util::MessageOptions options =
messaging_util::ParseMessageOptions(context, value.As<v8::Object>(),
messaging_util::PARSE_FRAME_ID);
EXPECT_EQ(test_case.expected_frame_id, options.frame_id);
}
}
using MessagingUtilWithSystemTest = NativeExtensionBindingsSystemUnittest;
TEST_F(MessagingUtilWithSystemTest, TestGetTargetIdFromExtensionContext) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
scoped_refptr<const Extension> extension = ExtensionBuilder("foo").Build();
RegisterExtension(extension);
ScriptContext* script_context = CreateScriptContext(
context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
script_context->set_url(extension->url());
std::string other_id(32, 'a');
struct {
v8::Local<v8::Value> passed_id;
base::StringPiece expected_id;
bool should_pass;
} test_cases[] = {
// If the extension ID is not provided, the bindings use the calling
// extension's.
{v8::Null(isolate()), extension->id(), true},
// We treat the empty string to be the same as null, even though it's
// somewhat unfortunate.
// See https://crbug.com/823577.
{gin::StringToV8(isolate(), ""), extension->id(), true},
{gin::StringToV8(isolate(), extension->id()), extension->id(), true},
{gin::StringToV8(isolate(), other_id), other_id, true},
{gin::StringToV8(isolate(), "invalid id"), base::StringPiece(), false},
};
for (size_t i = 0; i < base::size(test_cases); ++i) {
SCOPED_TRACE(base::StringPrintf("Test Case: %d", static_cast<int>(i)));
const auto& test_case = test_cases[i];
std::string target;
std::string error;
EXPECT_EQ(test_case.should_pass,
messaging_util::GetTargetExtensionId(
script_context, test_case.passed_id, "runtime.sendMessage",
&target, &error));
EXPECT_EQ(test_case.expected_id, target);
EXPECT_EQ(test_case.should_pass, error.empty()) << error;
}
}
TEST_F(MessagingUtilWithSystemTest, TestGetTargetIdFromWebContext) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
ScriptContext* script_context =
CreateScriptContext(context, nullptr, Feature::WEB_PAGE_CONTEXT);
script_context->set_url(GURL("https://example.com"));
std::string other_id(32, 'a');
struct {
v8::Local<v8::Value> passed_id;
base::StringPiece expected_id;
bool should_pass;
} test_cases[] = {
// A web page should always have to specify the extension id.
{gin::StringToV8(isolate(), other_id), other_id, true},
{v8::Null(isolate()), base::StringPiece(), false},
{gin::StringToV8(isolate(), ""), base::StringPiece(), false},
{gin::StringToV8(isolate(), "invalid id"), base::StringPiece(), false},
};
for (size_t i = 0; i < base::size(test_cases); ++i) {
SCOPED_TRACE(base::StringPrintf("Test Case: %d", static_cast<int>(i)));
const auto& test_case = test_cases[i];
std::string target;
std::string error;
EXPECT_EQ(test_case.should_pass,
messaging_util::GetTargetExtensionId(
script_context, test_case.passed_id, "runtime.sendMessage",
&target, &error));
EXPECT_EQ(test_case.expected_id, target);
EXPECT_EQ(test_case.should_pass, error.empty()) << error;
}
}
} // namespace extensions