| // Copyright 2021 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 <tuple> |
| |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "content/public/test/browser_test.h" |
| #include "extensions/common/extension_features.h" |
| #include "extensions/common/value_builder.h" |
| #include "extensions/test/test_extension_dir.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace extensions { |
| |
| constexpr char kWorkerJS[] = R"( |
| function verifyData(data) { |
| if (data.byteLength != 16) |
| return `Improper byteLength: ${data.byteLength}`; |
| |
| const bufView = new Uint8Array(data); |
| for (let i = 0; i < 16; i++) { |
| if (bufView[i] != i % 2) { |
| return `Data mismatch at index ${i}: Expected: ${i % 2}, got: ${ |
| bufView[i]}`; |
| } |
| } |
| |
| return 'PASS'; |
| } |
| |
| self.addEventListener('message', e => { |
| try { |
| postMessage(verifyData(e.data)); |
| } catch (e) { |
| postMessage(e.message); |
| } |
| }); |
| )"; |
| |
| constexpr char kBackgroundJS_SabDisallowed[] = R"( |
| chrome.test.runTests([ |
| function sendSharedArrayBufferToWorker() { |
| try { |
| let sab = new SharedArrayBuffer(16); |
| chrome.test.fail('SAB construction succeeded unexpectedly') |
| } catch (e) { |
| chrome.test.succeed(); |
| } |
| } |
| ]); |
| )"; |
| |
| constexpr char kBackgroundJS_SabAllowed[] = R"( |
| chrome.test.runTests([ |
| function sendSharedArrayBufferToWorker() { |
| let sab = new SharedArrayBuffer(16); |
| let bufView = new Uint8Array(sab); |
| for (let i = 0; i < 16; i++) |
| bufView[i] = (i % 2); |
| |
| const workerUrl = chrome.runtime.getURL('worker.js'); |
| let worker = new Worker(workerUrl); |
| |
| worker.onmessage = e => { |
| chrome.test.assertEq('PASS', e.data); |
| chrome.test.succeed(); |
| }; |
| |
| worker.postMessage(sab); |
| chrome.test.assertEq(16, sab.byteLength); |
| |
| // The worker will ack on receiving the SharedArrayBuffer causing the test |
| // to terminate. |
| } |
| ]); |
| )"; |
| |
| // Parameterized on tuple of |
| // <is_sab_allowed_unconditionally, is_cross_origin_isolated, is_platform_app>. |
| class SharedArrayBufferTest |
| : public ExtensionApiTest, |
| public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> { |
| public: |
| SharedArrayBufferTest() { |
| std::vector<base::Feature> enabled_features, disabled_features; |
| const bool is_sab_allowed_unconditionally = std::get<0>(GetParam()); |
| if (is_sab_allowed_unconditionally) { |
| enabled_features.push_back( |
| extensions_features::kAllowSharedArrayBuffersUnconditionally); |
| } else { |
| disabled_features.push_back( |
| extensions_features::kAllowSharedArrayBuffersUnconditionally); |
| } |
| feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| } |
| |
| TestExtensionDir& test_dir() { return test_dir_; } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| TestExtensionDir test_dir_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(SharedArrayBufferTest, TransferToWorker) { |
| ASSERT_TRUE(StartEmbeddedTestServer()); |
| |
| bool is_sab_allowed_unconditionally; |
| bool is_cross_origin_isolated; |
| bool is_platform_app; |
| std::tie(is_sab_allowed_unconditionally, is_cross_origin_isolated, |
| is_platform_app) = GetParam(); |
| |
| DictionaryBuilder builder; |
| builder.Set("manifest_version", 2) |
| .Set("name", "SharedArrayBuffer") |
| .Set("version", "1.1"); |
| |
| if (is_cross_origin_isolated) { |
| builder |
| .Set("cross_origin_opener_policy", |
| DictionaryBuilder().Set("value", "same-origin").Build()) |
| .Set("cross_origin_embedder_policy", |
| DictionaryBuilder().Set("value", "require-corp").Build()); |
| } |
| |
| DictionaryBuilder background_builder; |
| background_builder.Set("scripts", |
| ListBuilder().Append("background.js").Build()); |
| |
| if (is_platform_app) { |
| builder.Set("app", DictionaryBuilder() |
| .Set("background", background_builder.Build()) |
| .Build()); |
| } else { |
| builder.Set("background", background_builder.Build()); |
| } |
| |
| test_dir().WriteManifest(builder.ToJSON()); |
| |
| const bool expect_sab_allowed = |
| is_cross_origin_isolated || is_sab_allowed_unconditionally; |
| if (expect_sab_allowed) { |
| test_dir().WriteFile(FILE_PATH_LITERAL("background.js"), |
| kBackgroundJS_SabAllowed); |
| test_dir().WriteFile(FILE_PATH_LITERAL("worker.js"), kWorkerJS); |
| } else { |
| test_dir().WriteFile(FILE_PATH_LITERAL("background.js"), |
| kBackgroundJS_SabDisallowed); |
| } |
| |
| ASSERT_TRUE(RunExtensionTest(test_dir().Pack(), |
| {.launch_as_platform_app = is_platform_app}, |
| {} /* load_options */)) |
| << message_; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| SharedArrayBufferTest, |
| ::testing::Combine(::testing::Bool(), ::testing::Bool(), ::testing::Bool()), |
| [](const testing::TestParamInfo<std::tuple<bool, bool, bool>>& info) { |
| bool is_sab_allowed_unconditionally; |
| bool is_cross_origin_isolated; |
| bool is_platform_app; |
| std::tie(is_sab_allowed_unconditionally, is_cross_origin_isolated, |
| is_platform_app) = info.param; |
| return base::StringPrintf("%s_%s_%s", |
| is_sab_allowed_unconditionally |
| ? "SabAllowedEnabled" |
| : "SabAllowedDisabled", |
| is_cross_origin_isolated ? "COI" : "NonCOI", |
| is_platform_app ? "App" : "Extension"); |
| }); |
| |
| } // namespace extensions |