storage_area_unittest.cc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. // Copyright 2018 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. #include "extensions/renderer/storage_area.h"
  5. #include "base/strings/stringprintf.h"
  6. #include "base/test/values_test_util.h"
  7. #include "components/version_info/channel.h"
  8. #include "extensions/common/extension_builder.h"
  9. #include "extensions/common/features/feature_channel.h"
  10. #include "extensions/common/mojom/event_dispatcher.mojom.h"
  11. #include "extensions/common/mojom/frame.mojom.h"
  12. #include "extensions/renderer/bindings/api_binding_test_util.h"
  13. #include "extensions/renderer/bindings/api_binding_util.h"
  14. #include "extensions/renderer/bindings/api_invocation_errors.h"
  15. #include "extensions/renderer/native_extension_bindings_system.h"
  16. #include "extensions/renderer/native_extension_bindings_system_test_base.h"
  17. #include "extensions/renderer/script_context.h"
  18. #include "testing/gmock/include/gmock/gmock.h"
  19. namespace extensions {
  20. using StorageAreaTest = NativeExtensionBindingsSystemUnittest;
  21. // Test that trying to use StorageArea.get without a StorageArea `this` fails
  22. // (with a helpful error message).
  23. TEST_F(StorageAreaTest, TestUnboundedUse) {
  24. scoped_refptr<const Extension> extension =
  25. ExtensionBuilder("foo").AddPermission("storage").Build();
  26. RegisterExtension(extension);
  27. v8::HandleScope handle_scope(isolate());
  28. v8::Local<v8::Context> context = MainContext();
  29. ScriptContext* script_context = CreateScriptContext(
  30. context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
  31. script_context->set_url(extension->url());
  32. bindings_system()->UpdateBindingsForContext(script_context);
  33. v8::Local<v8::Value> storage_get =
  34. V8ValueFromScriptSource(context, "chrome.storage.local.get");
  35. ASSERT_TRUE(storage_get->IsFunction());
  36. constexpr char kRunStorageGet[] =
  37. "(function(get) { get('foo', function() {}); })";
  38. v8::Local<v8::Function> run_storage_get =
  39. FunctionFromString(context, kRunStorageGet);
  40. v8::Local<v8::Value> args[] = {storage_get};
  41. RunFunctionAndExpectError(
  42. run_storage_get, context, std::size(args), args,
  43. "Uncaught TypeError: Illegal invocation: Function must be called on "
  44. "an object of type StorageArea");
  45. }
  46. TEST_F(StorageAreaTest, TestUseAfterInvalidation) {
  47. scoped_refptr<const Extension> extension =
  48. ExtensionBuilder("foo").AddPermission("storage").Build();
  49. RegisterExtension(extension);
  50. v8::HandleScope handle_scope(isolate());
  51. v8::Local<v8::Context> context = MainContext();
  52. ScriptContext* script_context = CreateScriptContext(
  53. context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
  54. script_context->set_url(extension->url());
  55. bindings_system()->UpdateBindingsForContext(script_context);
  56. v8::Local<v8::Value> storage =
  57. V8ValueFromScriptSource(context, "chrome.storage.local");
  58. ASSERT_TRUE(storage->IsObject());
  59. constexpr char kRunStorageGet[] =
  60. "(function(storage) { storage.get('foo', function() {}); })";
  61. v8::Local<v8::Function> run_storage_get =
  62. FunctionFromString(context, kRunStorageGet);
  63. v8::Local<v8::Value> args[] = {storage};
  64. RunFunction(run_storage_get, context, std::size(args), args);
  65. DisposeContext(context);
  66. EXPECT_FALSE(binding::IsContextValid(context));
  67. RunFunctionAndExpectError(run_storage_get, context, std::size(args), args,
  68. "Uncaught Error: Extension context invalidated.");
  69. }
  70. TEST_F(StorageAreaTest, InvalidInvocationError) {
  71. scoped_refptr<const Extension> extension =
  72. ExtensionBuilder("foo").AddPermission("storage").Build();
  73. RegisterExtension(extension);
  74. v8::HandleScope handle_scope(isolate());
  75. v8::Local<v8::Context> context = MainContext();
  76. ScriptContext* script_context = CreateScriptContext(
  77. context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
  78. script_context->set_url(extension->url());
  79. bindings_system()->UpdateBindingsForContext(script_context);
  80. v8::Local<v8::Value> storage =
  81. V8ValueFromScriptSource(context, "chrome.storage.local");
  82. ASSERT_TRUE(storage->IsObject());
  83. constexpr char kRunStorageGet[] =
  84. "(function(storage) { storage.get(1, function() {}); })";
  85. v8::Local<v8::Function> run_storage_get =
  86. FunctionFromString(context, kRunStorageGet);
  87. v8::Local<v8::Value> args[] = {storage};
  88. RunFunctionAndExpectError(
  89. run_storage_get, context, std::size(args), args,
  90. "Uncaught TypeError: " +
  91. api_errors::InvocationError(
  92. "storage.get",
  93. "optional [string|array|object] keys, function callback",
  94. "No matching signature."));
  95. }
  96. TEST_F(StorageAreaTest, HasOnChanged) {
  97. scoped_refptr<const Extension> extension = ExtensionBuilder("foo")
  98. .SetManifestVersion(3)
  99. .AddPermission("storage")
  100. .Build();
  101. RegisterExtension(extension);
  102. v8::HandleScope handle_scope(isolate());
  103. v8::Local<v8::Context> context = MainContext();
  104. ScriptContext* script_context = CreateScriptContext(
  105. context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
  106. script_context->set_url(extension->url());
  107. bindings_system()->UpdateBindingsForContext(script_context);
  108. const char* kStorages[] = {"sync", "local", "managed", "session"};
  109. for (auto* kStorage : kStorages) {
  110. const std::string kRegisterListener = base::StringPrintf(
  111. R"((function() {
  112. chrome.storage.%s.onChanged.addListener(
  113. function(change) {
  114. this.change = change;
  115. });
  116. }))",
  117. kStorage);
  118. v8::Local<v8::Function> add_listener =
  119. FunctionFromString(context, kRegisterListener);
  120. RunFunctionOnGlobal(add_listener, context, 0, nullptr);
  121. base::Value::List value = ListValueFromString("['foo']");
  122. bindings_system()->DispatchEventInContext(
  123. base::StringPrintf("storage.%s.onChanged", kStorage).c_str(), value,
  124. nullptr, script_context);
  125. EXPECT_EQ("\"foo\"", GetStringPropertyFromObject(context->Global(), context,
  126. "change"));
  127. }
  128. }
  129. TEST_F(StorageAreaTest, PromiseBasedFunctionsForManifestV3) {
  130. scoped_refptr<const Extension> extension = ExtensionBuilder("foo")
  131. .SetManifestVersion(3)
  132. .AddPermission("storage")
  133. .Build();
  134. RegisterExtension(extension);
  135. v8::HandleScope handle_scope(isolate());
  136. v8::Local<v8::Context> context = MainContext();
  137. ScriptContext* script_context = CreateScriptContext(
  138. context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
  139. script_context->set_url(extension->url());
  140. bindings_system()->UpdateBindingsForContext(script_context);
  141. v8::Local<v8::Value> storage =
  142. V8ValueFromScriptSource(context, "chrome.storage.local");
  143. ASSERT_TRUE(storage->IsObject());
  144. constexpr char kRunStorageGet[] =
  145. "(function(storage) { return storage.get('foo'); });";
  146. v8::Local<v8::Function> run_storage_get =
  147. FunctionFromString(context, kRunStorageGet);
  148. v8::Local<v8::Value> args[] = {storage};
  149. v8::Local<v8::Value> return_value =
  150. RunFunctionOnGlobal(run_storage_get, context, std::size(args), args);
  151. ASSERT_TRUE(return_value->IsPromise());
  152. v8::Local<v8::Promise> promise = return_value.As<v8::Promise>();
  153. EXPECT_EQ(v8::Promise::kPending, promise->State());
  154. EXPECT_EQ(extension->id(), last_params().extension_id);
  155. EXPECT_EQ("storage.get", last_params().name);
  156. EXPECT_EQ(extension->url(), last_params().source_url);
  157. // We treat returning a promise as having a callback in the request params.
  158. EXPECT_TRUE(last_params().has_callback);
  159. EXPECT_THAT(last_params().arguments,
  160. base::test::IsJson(R"(["local", "foo"])"));
  161. bindings_system()->HandleResponse(last_params().request_id, /*success=*/true,
  162. ListValueFromString(R"([{"foo": 42}])"),
  163. /*error=*/std::string());
  164. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  165. EXPECT_EQ(R"({"foo":42})", V8ToString(promise->Result(), context));
  166. }
  167. TEST_F(StorageAreaTest, PromiseBasedFunctionsDisallowedForManifestV2) {
  168. scoped_refptr<const Extension> extension = ExtensionBuilder("foo")
  169. .SetManifestVersion(2)
  170. .AddPermission("storage")
  171. .Build();
  172. RegisterExtension(extension);
  173. v8::HandleScope handle_scope(isolate());
  174. v8::Local<v8::Context> context = MainContext();
  175. ScriptContext* script_context = CreateScriptContext(
  176. context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
  177. script_context->set_url(extension->url());
  178. bindings_system()->UpdateBindingsForContext(script_context);
  179. v8::Local<v8::Value> storage =
  180. V8ValueFromScriptSource(context, "chrome.storage.local");
  181. ASSERT_TRUE(storage->IsObject());
  182. constexpr char kRunStorageGet[] =
  183. "(function(storage) { this.returnValue = storage.get('foo'); });";
  184. v8::Local<v8::Function> run_storage_get =
  185. FunctionFromString(context, kRunStorageGet);
  186. v8::Local<v8::Value> args[] = {storage};
  187. auto expected_error =
  188. "Uncaught TypeError: " +
  189. api_errors::InvocationError(
  190. "storage.get",
  191. "optional [string|array|object] keys, function callback",
  192. api_errors::NoMatchingSignature());
  193. RunFunctionAndExpectError(run_storage_get, context, std::size(args), args,
  194. expected_error);
  195. }
  196. } // namespace extensions