blob: 3b281bd8d249b88aa2e26726645e1dd5b5f6de6c [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/renderer/core/workers/dedicated_worker_test.h"
#include <bitset>
#include <memory>
#include "base/single_thread_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h"
#include "third_party/blink/public/mojom/worker/dedicated_worker_host.mojom-blink.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
#include "third_party/blink/renderer/core/events/message_event.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/inspector/console_message_storage.h"
#include "third_party/blink/renderer/core/inspector/thread_debugger.h"
#include "third_party/blink/renderer/core/script/script.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_thread.h"
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
#include "third_party/blink/renderer/core/workers/worker_backing_thread_startup_data.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
#include "third_party/blink/renderer/core/workers/worker_thread_test_helper.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
namespace blink {
class DedicatedWorkerThreadForTest final : public DedicatedWorkerThread {
public:
DedicatedWorkerThreadForTest(ExecutionContext* parent_execution_context,
DedicatedWorkerObjectProxy& worker_object_proxy)
: DedicatedWorkerThread(
parent_execution_context,
worker_object_proxy,
mojo::PendingRemote<mojom::blink::DedicatedWorkerHost>()) {
worker_backing_thread_ = std::make_unique<WorkerBackingThread>(
ThreadCreationParams(ThreadType::kTestThread));
}
WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
std::unique_ptr<GlobalScopeCreationParams> creation_params) override {
// Needed to avoid calling into an uninitialized broker.
if (!creation_params->browser_interface_broker) {
(void)creation_params->browser_interface_broker
.InitWithNewPipeAndPassReceiver();
}
auto* global_scope = DedicatedWorkerGlobalScope::Create(
std::move(creation_params), this, time_origin_,
mojo::PendingRemote<mojom::blink::DedicatedWorkerHost>());
// Initializing a global scope with a dummy creation params may emit warning
// messages (e.g., invalid CSP directives). Clear them here for tests that
// check console messages (i.e., UseCounter tests).
GetConsoleMessageStorage()->Clear();
return global_scope;
}
// Emulates API use on DedicatedWorkerGlobalScope.
void CountFeature(WebFeature feature) {
EXPECT_TRUE(IsCurrentThread());
GlobalScope()->CountUse(feature);
PostCrossThreadTask(*GetParentTaskRunnerForTesting(), FROM_HERE,
CrossThreadBindOnce(&test::ExitRunLoop));
}
// Emulates deprecated API use on DedicatedWorkerGlobalScope.
void CountDeprecation(WebFeature feature) {
EXPECT_TRUE(IsCurrentThread());
Deprecation::CountDeprecation(GlobalScope(), feature);
// CountDeprecation() should add a warning message.
EXPECT_EQ(1u, GetConsoleMessageStorage()->size());
String console_message = GetConsoleMessageStorage()->at(0)->Message();
EXPECT_TRUE(console_message.Contains("deprecated"));
PostCrossThreadTask(*GetParentTaskRunnerForTesting(), FROM_HERE,
CrossThreadBindOnce(&test::ExitRunLoop));
}
void TestTaskRunner() {
EXPECT_TRUE(IsCurrentThread());
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
GlobalScope()->GetTaskRunner(TaskType::kInternalTest);
EXPECT_TRUE(task_runner->RunsTasksInCurrentSequence());
PostCrossThreadTask(*GetParentTaskRunnerForTesting(), FROM_HERE,
CrossThreadBindOnce(&test::ExitRunLoop));
}
};
class DedicatedWorkerObjectProxyForTest final
: public DedicatedWorkerObjectProxy {
public:
DedicatedWorkerObjectProxyForTest(
DedicatedWorkerMessagingProxy* messaging_proxy,
ParentExecutionContextTaskRunners* parent_execution_context_task_runners)
: DedicatedWorkerObjectProxy(messaging_proxy,
parent_execution_context_task_runners,
DedicatedWorkerToken()) {}
void CountFeature(WebFeature feature) override {
// Any feature should be reported only one time.
EXPECT_FALSE(reported_features_[static_cast<size_t>(feature)]);
reported_features_.set(static_cast<size_t>(feature));
DedicatedWorkerObjectProxy::CountFeature(feature);
}
private:
std::bitset<static_cast<size_t>(WebFeature::kNumberOfFeatures)>
reported_features_;
};
class DedicatedWorkerMessagingProxyForTest
: public DedicatedWorkerMessagingProxy {
public:
DedicatedWorkerMessagingProxyForTest(ExecutionContext* execution_context)
: DedicatedWorkerMessagingProxy(execution_context,
nullptr /* worker_object */) {
// The |worker_object_proxy_| should not have been set in the
// DedicatedWorkerMessagingProxy constructor as |worker_object| is nullptr.
DCHECK(!worker_object_proxy_);
worker_object_proxy_ = std::make_unique<DedicatedWorkerObjectProxyForTest>(
this, GetParentExecutionContextTaskRunners());
}
~DedicatedWorkerMessagingProxyForTest() override = default;
void StartWithSourceCode(const String& source) {
KURL script_url("http://fake.url/");
security_origin_ = SecurityOrigin::Create(script_url);
auto worker_settings = std::make_unique<WorkerSettings>(
To<LocalDOMWindow>(GetExecutionContext())->GetFrame()->GetSettings());
auto params = std::make_unique<GlobalScopeCreationParams>(
script_url, mojom::blink::ScriptType::kClassic,
"fake global scope name", "fake user agent", UserAgentMetadata(),
nullptr /* web_worker_fetch_context */,
Vector<network::mojom::blink::ContentSecurityPolicyPtr>(),
network::mojom::ReferrerPolicy::kDefault, security_origin_.get(),
false /* starter_secure_context */,
CalculateHttpsState(security_origin_.get()),
nullptr /* worker_clients */, nullptr /* content_settings_client */,
network::mojom::IPAddressSpace::kLocal,
nullptr /* origin_trial_tokens */, base::UnguessableToken::Create(),
std::move(worker_settings), mojom::blink::V8CacheOptions::kDefault,
nullptr /* worklet_module_responses_map */);
params->parent_context_token =
GetExecutionContext()->GetExecutionContextToken();
InitializeWorkerThread(
std::move(params),
WorkerBackingThreadStartupData(
WorkerBackingThreadStartupData::HeapLimitMode::kDefault,
WorkerBackingThreadStartupData::AtomicsWaitMode::kAllow),
worker_object_proxy_->token());
GetWorkerThread()->EvaluateClassicScript(script_url, source,
nullptr /* cached_meta_data */,
v8_inspector::V8StackTraceId());
}
DedicatedWorkerThreadForTest* GetDedicatedWorkerThread() {
return static_cast<DedicatedWorkerThreadForTest*>(GetWorkerThread());
}
void Trace(Visitor* visitor) const override {
DedicatedWorkerMessagingProxy::Trace(visitor);
}
private:
std::unique_ptr<WorkerThread> CreateWorkerThread() override {
return std::make_unique<DedicatedWorkerThreadForTest>(GetExecutionContext(),
WorkerObjectProxy());
}
scoped_refptr<const SecurityOrigin> security_origin_;
};
void DedicatedWorkerTest::SetUp() {
PageTestBase::SetUp(IntSize());
worker_messaging_proxy_ =
MakeGarbageCollected<DedicatedWorkerMessagingProxyForTest>(
GetFrame().DomWindow());
}
void DedicatedWorkerTest::TearDown() {
GetWorkerThread()->TerminateForTesting();
GetWorkerThread()->WaitForShutdownForTesting();
}
void DedicatedWorkerTest::DispatchMessageEvent() {
BlinkTransferableMessage message;
WorkerMessagingProxy()->PostMessageToWorkerGlobalScope(std::move(message));
}
DedicatedWorkerMessagingProxyForTest*
DedicatedWorkerTest::WorkerMessagingProxy() {
return worker_messaging_proxy_.Get();
}
DedicatedWorkerThreadForTest* DedicatedWorkerTest::GetWorkerThread() {
return worker_messaging_proxy_->GetDedicatedWorkerThread();
}
void DedicatedWorkerTest::StartWorker(const String& source_code) {
WorkerMessagingProxy()->StartWithSourceCode(source_code);
}
namespace {
void PostExitRunLoopTaskOnParent(WorkerThread* worker_thread) {
PostCrossThreadTask(*worker_thread->GetParentTaskRunnerForTesting(),
FROM_HERE, CrossThreadBindOnce(&test::ExitRunLoop));
}
} // anonymous namespace
void DedicatedWorkerTest::WaitUntilWorkerIsRunning() {
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
CrossThreadBindOnce(&PostExitRunLoopTaskOnParent,
CrossThreadUnretained(GetWorkerThread())));
test::EnterRunLoop();
}
TEST_F(DedicatedWorkerTest, PendingActivity_NoActivityAfterContextDestroyed) {
const String source_code = "// Do nothing";
StartWorker(source_code);
EXPECT_TRUE(WorkerMessagingProxy()->HasPendingActivity());
// Destroying the context should result in no pending activities.
WorkerMessagingProxy()->TerminateGlobalScope();
EXPECT_FALSE(WorkerMessagingProxy()->HasPendingActivity());
}
TEST_F(DedicatedWorkerTest, UseCounter) {
Page::InsertOrdinaryPageForTesting(&GetPage());
const String source_code = "// Do nothing";
StartWorker(source_code);
// This feature is randomly selected.
const WebFeature kFeature1 = WebFeature::kRequestFileSystem;
// API use on the DedicatedWorkerGlobalScope should be recorded in UseCounter
// on the Document.
EXPECT_FALSE(GetDocument().IsUseCounted(kFeature1));
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
CrossThreadBindOnce(&DedicatedWorkerThreadForTest::CountFeature,
CrossThreadUnretained(GetWorkerThread()), kFeature1));
test::EnterRunLoop();
EXPECT_TRUE(GetDocument().IsUseCounted(kFeature1));
// API use should be reported to the Document only one time. See comments in
// DedicatedWorkerObjectProxyForTest::CountFeature.
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
CrossThreadBindOnce(&DedicatedWorkerThreadForTest::CountFeature,
CrossThreadUnretained(GetWorkerThread()), kFeature1));
test::EnterRunLoop();
// This feature is randomly selected from Deprecation::deprecationMessage().
const WebFeature kFeature2 = WebFeature::kPrefixedStorageInfo;
// Deprecated API use on the DedicatedWorkerGlobalScope should be recorded in
// UseCounter on the Document.
EXPECT_FALSE(GetDocument().IsUseCounted(kFeature2));
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
CrossThreadBindOnce(&DedicatedWorkerThreadForTest::CountDeprecation,
CrossThreadUnretained(GetWorkerThread()), kFeature2));
test::EnterRunLoop();
EXPECT_TRUE(GetDocument().IsUseCounted(kFeature2));
// API use should be reported to the Document only one time. See comments in
// DedicatedWorkerObjectProxyForTest::CountDeprecation.
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
CrossThreadBindOnce(&DedicatedWorkerThreadForTest::CountDeprecation,
CrossThreadUnretained(GetWorkerThread()), kFeature2));
test::EnterRunLoop();
}
TEST_F(DedicatedWorkerTest, TaskRunner) {
const String source_code = "// Do nothing";
StartWorker(source_code);
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
CrossThreadBindOnce(&DedicatedWorkerThreadForTest::TestTaskRunner,
CrossThreadUnretained(GetWorkerThread())));
test::EnterRunLoop();
}
} // namespace blink