Commit aa3ce761 authored by Hiroshige Hayashizaki's avatar Hiroshige Hayashizaki Committed by Commit Bot

Use PendingScript in ScriptRunner

This CL replaces the most of ScriptLoader references in
ScriptRunner with PendingScript.

ScriptLoader is still used
- When the script is evaluated,
  because ScriptLoader::Execute() is still in ScriptLoader.
  ScriptLoader is obtained by PendingScript::GetElement()->Loader()
  here, but this will be removed by [1] once
  ScriptLoader::Execute() is removed.
- When the script element is moved between documents, because
  we have to get PendingScript via the
  Element->ScriptLoader->PendingScript path.
  This is done by
  GetPendingScriptIfControlledByScriptRunnerForCrossDocMove()
  in MovePendingScript(), which will be the last use of
  GetPendingScriptIfControlledByScriptRunner()
  that will remain until [2] that is not planned to land soon.

No behavior changes.

[1] https://chromium-review.googlesource.com/1054553
[2] https://chromium-review.googlesource.com/1041143

Bug: 842349
Change-Id: Idc36b00c8c25fe8e24ff60e980608d8b324fec5e
Reviewed-on: https://chromium-review.googlesource.com/1053354
Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Reviewed-by: default avatarDaniel Vogelheim <vogelheim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#560874}
parent be64717b
......@@ -612,7 +612,8 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
pending_script_ = TakePendingScript(ScriptSchedulingType::kInOrder);
// TODO(hiroshige): Here |contextDocument| is used as "node document"
// while Step 14 uses |elementDocument| as "node document". Fix this.
context_document->GetScriptRunner()->QueueScriptForExecution(this);
context_document->GetScriptRunner()->QueueScriptForExecution(
pending_script_);
// Note that watchForLoad can immediately call pendingScriptFinished.
pending_script_->WatchForLoad(this);
// The part "When the script is ready..." is implemented in
......@@ -637,7 +638,8 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
pending_script_ = TakePendingScript(ScriptSchedulingType::kAsync);
// TODO(hiroshige): Here |contextDocument| is used as "node document"
// while Step 14 uses |elementDocument| as "node document". Fix this.
context_document->GetScriptRunner()->QueueScriptForExecution(this);
context_document->GetScriptRunner()->QueueScriptForExecution(
pending_script_);
// Note that watchForLoad can immediately call pendingScriptFinished.
pending_script_->WatchForLoad(this);
// The part "When the script is ready..." is implemented in
......@@ -936,7 +938,8 @@ void ScriptLoader::PendingScriptFinished(PendingScript* pending_script) {
return;
}
context_document->GetScriptRunner()->NotifyScriptReady(this);
DCHECK_EQ(pending_script->GetElement()->Loader(), this);
context_document->GetScriptRunner()->NotifyScriptReady(pending_script);
pending_script_->StopWatchingForLoad();
}
......@@ -979,4 +982,10 @@ PendingScript* ScriptLoader::GetPendingScriptIfControlledByScriptRunner() {
return pending_script_;
}
PendingScript*
ScriptLoader::GetPendingScriptIfControlledByScriptRunnerForCrossDocMove() {
DCHECK(!pending_script_ || pending_script_->IsControlledByScriptRunner());
return pending_script_;
}
} // namespace blink
......@@ -121,6 +121,11 @@ class CORE_EXPORT ScriptLoader : public GarbageCollectedFinalized<ScriptLoader>,
// please use with care. (Method is virtual to support testing.)
virtual PendingScript* GetPendingScriptIfControlledByScriptRunner();
// Return non-null if controlled by ScriptRunner, or null otherwise.
// Only for ScriptRunner::MovePendingScript() and should be removed once
// crbug.com/721914 is fixed.
PendingScript* GetPendingScriptIfControlledByScriptRunnerForCrossDocMove();
protected:
ScriptLoader(ScriptElementBase*, bool created_by_parser, bool is_evaluated);
......
......@@ -44,20 +44,17 @@ ScriptRunner::ScriptRunner(Document* document)
DCHECK(document);
}
void ScriptRunner::QueueScriptForExecution(ScriptLoader* script_loader) {
DCHECK(script_loader);
PendingScript* pending_script =
script_loader->GetPendingScriptIfControlledByScriptRunner();
void ScriptRunner::QueueScriptForExecution(PendingScript* pending_script) {
DCHECK(pending_script);
document_->IncrementLoadEventDelayCount();
switch (pending_script->GetSchedulingType()) {
case ScriptSchedulingType::kAsync:
pending_async_scripts_.insert(script_loader);
TryStream(script_loader);
pending_async_scripts_.insert(pending_script);
TryStream(pending_script);
break;
case ScriptSchedulingType::kInOrder:
pending_in_order_scripts_.push_back(script_loader);
pending_in_order_scripts_.push_back(pending_script);
number_of_in_order_scripts_with_pending_notification_++;
break;
......@@ -99,7 +96,6 @@ void ScriptRunner::Resume() {
void ScriptRunner::ScheduleReadyInOrderScripts() {
while (!pending_in_order_scripts_.IsEmpty() &&
pending_in_order_scripts_.front()
->GetPendingScriptIfControlledByScriptRunner()
->IsReady()) {
in_order_scripts_to_execute_soon_.push_back(
pending_in_order_scripts_.TakeFirst());
......@@ -107,21 +103,18 @@ void ScriptRunner::ScheduleReadyInOrderScripts() {
}
}
void ScriptRunner::NotifyScriptReady(ScriptLoader* script_loader) {
SECURITY_CHECK(script_loader);
PendingScript* pending_script =
script_loader->GetPendingScriptIfControlledByScriptRunner();
DCHECK(pending_script);
void ScriptRunner::NotifyScriptReady(PendingScript* pending_script) {
SECURITY_CHECK(pending_script);
switch (pending_script->GetSchedulingType()) {
case ScriptSchedulingType::kAsync:
// SECURITY_CHECK() makes us crash in a controlled way in error cases
// where the ScriptLoader is associated with the wrong ScriptRunner
// where the PendingScript is associated with the wrong ScriptRunner
// (otherwise we'd cause a use-after-free in ~ScriptRunner when it tries
// to detach).
SECURITY_CHECK(pending_async_scripts_.Contains(script_loader));
SECURITY_CHECK(pending_async_scripts_.Contains(pending_script));
pending_async_scripts_.erase(script_loader);
async_scripts_to_execute_soon_.push_back(script_loader);
pending_async_scripts_.erase(pending_script);
async_scripts_to_execute_soon_.push_back(pending_script);
PostTask(FROM_HERE);
TryStreamAny();
......@@ -141,9 +134,9 @@ void ScriptRunner::NotifyScriptReady(ScriptLoader* script_loader) {
}
}
bool ScriptRunner::RemovePendingInOrderScript(ScriptLoader* script_loader) {
bool ScriptRunner::RemovePendingInOrderScript(PendingScript* pending_script) {
auto it = std::find(pending_in_order_scripts_.begin(),
pending_in_order_scripts_.end(), script_loader);
pending_in_order_scripts_.end(), pending_script);
if (it == pending_in_order_scripts_.end())
return false;
pending_in_order_scripts_.erase(it);
......@@ -180,23 +173,35 @@ void ScriptRunner::MovePendingScript(Document& old_document,
DCHECK(!old_document.GetFrame());
old_context_document = &old_document;
}
if (old_context_document != new_context_document) {
old_context_document->GetScriptRunner()->MovePendingScript(
new_context_document->GetScriptRunner(), script_loader);
if (old_context_document == new_context_document)
return;
PendingScript* pending_script =
script_loader
->GetPendingScriptIfControlledByScriptRunnerForCrossDocMove();
if (!pending_script) {
// The ScriptLoader is not controlled by ScriptRunner. This can happen
// because MovePendingScript() is called for all <script> elements
// moved between Documents, not only for those controlled by ScriptRunner.
return;
}
old_context_document->GetScriptRunner()->MovePendingScript(
new_context_document->GetScriptRunner(), pending_script);
}
void ScriptRunner::MovePendingScript(ScriptRunner* new_runner,
ScriptLoader* script_loader) {
auto it = pending_async_scripts_.find(script_loader);
PendingScript* pending_script) {
auto it = pending_async_scripts_.find(pending_script);
if (it != pending_async_scripts_.end()) {
new_runner->QueueScriptForExecution(script_loader);
new_runner->QueueScriptForExecution(pending_script);
pending_async_scripts_.erase(it);
document_->DecrementLoadEventDelayCount();
return;
}
if (RemovePendingInOrderScript(script_loader)) {
new_runner->QueueScriptForExecution(script_loader);
if (RemovePendingInOrderScript(pending_script)) {
new_runner->QueueScriptForExecution(pending_script);
document_->DecrementLoadEventDelayCount();
}
}
......@@ -205,13 +210,18 @@ bool ScriptRunner::ExecuteInOrderTask() {
if (in_order_scripts_to_execute_soon_.IsEmpty())
return false;
DCHECK_EQ(in_order_scripts_to_execute_soon_.front()
->GetPendingScriptIfControlledByScriptRunner()
->GetSchedulingType(),
ScriptSchedulingType::kInOrder)
PendingScript* pending_script = in_order_scripts_to_execute_soon_.TakeFirst();
DCHECK(pending_script);
DCHECK_EQ(pending_script->GetSchedulingType(), ScriptSchedulingType::kInOrder)
<< "In-order scripts queue should not contain any async script.";
in_order_scripts_to_execute_soon_.TakeFirst()->Execute();
// TODO(hiroshige): Move ExecuteScriptBlock() to PendingScript and remove
// the use of ScriptLoader here.
ScriptLoader* script_loader = pending_script->GetElement()->Loader();
DCHECK(script_loader);
DCHECK_EQ(script_loader->GetPendingScriptIfControlledByScriptRunner(),
pending_script);
script_loader->Execute();
document_->DecrementLoadEventDelayCount();
return true;
......@@ -219,11 +229,9 @@ bool ScriptRunner::ExecuteInOrderTask() {
bool ScriptRunner::ExecuteAsyncTask() {
// Find an async script loader which is not currently streaming.
auto it = std::find_if(
async_scripts_to_execute_soon_.begin(),
async_scripts_to_execute_soon_.end(), [](ScriptLoader* loader) {
PendingScript* pending_script =
loader->GetPendingScriptIfControlledByScriptRunner();
auto it = std::find_if(async_scripts_to_execute_soon_.begin(),
async_scripts_to_execute_soon_.end(),
[](PendingScript* pending_script) {
DCHECK(pending_script);
return !pending_script->IsCurrentlyStreaming();
});
......@@ -232,9 +240,20 @@ bool ScriptRunner::ExecuteAsyncTask() {
}
// Remove the async script loader from the ready-to-exec set and execute.
ScriptLoader* async_script_to_execute = *it;
PendingScript* pending_script = *it;
async_scripts_to_execute_soon_.erase(it);
async_script_to_execute->Execute();
DCHECK_EQ(pending_script->GetSchedulingType(), ScriptSchedulingType::kAsync)
<< "Async scripts queue should not contain any in-order script.";
// TODO(hiroshige): Move ExecuteScriptBlock() to PendingScript and remove
// the use of ScriptLoader here.
ScriptLoader* script_loader = pending_script->GetElement()->Loader();
DCHECK(script_loader);
DCHECK_EQ(script_loader->GetPendingScriptIfControlledByScriptRunner(),
pending_script);
script_loader->Execute();
document_->DecrementLoadEventDelayCount();
return true;
}
......@@ -265,33 +284,31 @@ void ScriptRunner::TryStreamAny() {
return;
// Look through async_scripts_to_execute_soon_, and stream any one of them.
for (auto script_loader : async_scripts_to_execute_soon_) {
if (DoTryStream(script_loader))
for (auto pending_script : async_scripts_to_execute_soon_) {
if (DoTryStream(pending_script))
return;
}
}
void ScriptRunner::TryStream(ScriptLoader* script_loader) {
void ScriptRunner::TryStream(PendingScript* pending_script) {
if (!is_suspended_)
DoTryStream(script_loader);
DoTryStream(pending_script);
}
bool ScriptRunner::DoTryStream(ScriptLoader* script_loader) {
bool ScriptRunner::DoTryStream(PendingScript* pending_script) {
// Checks that all callers should have already done.
DCHECK(!is_suspended_);
DCHECK(script_loader);
DCHECK(pending_script);
// Currently, we stream only async scripts in this function.
// Note: HTMLParserScriptRunner kicks streaming for deferred or blocking
// scripts.
DCHECK(pending_async_scripts_.find(script_loader) !=
DCHECK(pending_async_scripts_.find(pending_script) !=
pending_async_scripts_.end() ||
std::find(async_scripts_to_execute_soon_.begin(),
async_scripts_to_execute_soon_.end(),
script_loader) != async_scripts_to_execute_soon_.end());
pending_script) != async_scripts_to_execute_soon_.end());
PendingScript* pending_script =
script_loader->GetPendingScriptIfControlledByScriptRunner();
if (!pending_script)
return false;
......
......@@ -39,6 +39,7 @@
namespace blink {
class Document;
class PendingScript;
class ScriptLoader;
class CORE_EXPORT ScriptRunner final
......@@ -49,14 +50,14 @@ class CORE_EXPORT ScriptRunner final
return new ScriptRunner(document);
}
void QueueScriptForExecution(ScriptLoader*);
void QueueScriptForExecution(PendingScript*);
bool HasPendingScripts() const {
return !pending_in_order_scripts_.IsEmpty() ||
!pending_async_scripts_.IsEmpty();
}
void Suspend();
void Resume();
void NotifyScriptReady(ScriptLoader*);
void NotifyScriptReady(PendingScript*);
void NotifyScriptStreamerFinished();
static void MovePendingScript(Document&, Document&, ScriptLoader*);
......@@ -70,8 +71,8 @@ class CORE_EXPORT ScriptRunner final
explicit ScriptRunner(Document*);
void MovePendingScript(ScriptRunner*, ScriptLoader*);
bool RemovePendingInOrderScript(ScriptLoader*);
void MovePendingScript(ScriptRunner*, PendingScript*);
bool RemovePendingInOrderScript(PendingScript*);
void ScheduleReadyInOrderScripts();
void PostTask(const base::Location&);
......@@ -87,18 +88,19 @@ class CORE_EXPORT ScriptRunner final
void ExecuteTask();
// Try to start streaming a specific script or any available script.
void TryStream(ScriptLoader*);
void TryStream(PendingScript*);
void TryStreamAny();
bool DoTryStream(ScriptLoader*); // Implementation for both Try* methods.
bool DoTryStream(PendingScript*); // Implementation for both Try* methods.
Member<Document> document_;
HeapDeque<TraceWrapperMember<ScriptLoader>> pending_in_order_scripts_;
HeapHashSet<TraceWrapperMember<ScriptLoader>> pending_async_scripts_;
HeapDeque<TraceWrapperMember<PendingScript>> pending_in_order_scripts_;
HeapHashSet<TraceWrapperMember<PendingScript>> pending_async_scripts_;
// http://www.whatwg.org/specs/web-apps/current-work/#set-of-scripts-that-will-execute-as-soon-as-possible
HeapDeque<TraceWrapperMember<ScriptLoader>> async_scripts_to_execute_soon_;
HeapDeque<TraceWrapperMember<ScriptLoader>> in_order_scripts_to_execute_soon_;
HeapDeque<TraceWrapperMember<PendingScript>> async_scripts_to_execute_soon_;
HeapDeque<TraceWrapperMember<PendingScript>>
in_order_scripts_to_execute_soon_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
......
......@@ -157,9 +157,16 @@ class ScriptRunnerTest : public testing::Test {
protected:
void NotifyScriptReady(MockScriptLoader* script_loader) {
script_loader->GetPendingScriptIfControlledByScriptRunner()->SetIsReady(
true);
script_runner_->NotifyScriptReady(script_loader);
MockPendingScript* pending_script =
script_loader->GetPendingScriptIfControlledByScriptRunner();
pending_script->SetIsReady(true);
script_runner_->NotifyScriptReady(pending_script);
}
void QueueScriptForExecution(MockScriptLoader* script_loader) {
MockPendingScript* pending_script =
script_loader->GetPendingScriptIfControlledByScriptRunner();
script_runner_->QueueScriptForExecution(pending_script);
}
Persistent<Document> document_;
......@@ -172,7 +179,7 @@ class ScriptRunnerTest : public testing::Test {
TEST_F(ScriptRunnerTest, QueueSingleScript_Async) {
auto* script_loader = MockScriptLoader::CreateAsync(document_);
script_runner_->QueueScriptForExecution(script_loader);
QueueScriptForExecution(script_loader);
NotifyScriptReady(script_loader);
EXPECT_CALL(*script_loader, Execute());
......@@ -181,7 +188,7 @@ TEST_F(ScriptRunnerTest, QueueSingleScript_Async) {
TEST_F(ScriptRunnerTest, QueueSingleScript_InOrder) {
auto* script_loader = MockScriptLoader::CreateInOrder(document_);
script_runner_->QueueScriptForExecution(script_loader);
QueueScriptForExecution(script_loader);
EXPECT_CALL(*script_loader, Execute());
......@@ -200,8 +207,8 @@ TEST_F(ScriptRunnerTest, QueueMultipleScripts_InOrder) {
script_loaders.push_back(script_loader2);
script_loaders.push_back(script_loader3);
for (ScriptLoader* script_loader : script_loaders) {
script_runner_->QueueScriptForExecution(script_loader);
for (MockScriptLoader* script_loader : script_loaders) {
QueueScriptForExecution(script_loader);
}
for (size_t i = 0; i < script_loaders.size(); ++i) {
......@@ -226,11 +233,11 @@ TEST_F(ScriptRunnerTest, QueueMixedScripts) {
auto* script_loader4 = MockScriptLoader::CreateAsync(document_);
auto* script_loader5 = MockScriptLoader::CreateAsync(document_);
script_runner_->QueueScriptForExecution(script_loader1);
script_runner_->QueueScriptForExecution(script_loader2);
script_runner_->QueueScriptForExecution(script_loader3);
script_runner_->QueueScriptForExecution(script_loader4);
script_runner_->QueueScriptForExecution(script_loader5);
QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader2);
QueueScriptForExecution(script_loader3);
QueueScriptForExecution(script_loader4);
QueueScriptForExecution(script_loader5);
NotifyScriptReady(script_loader1);
NotifyScriptReady(script_loader2);
......@@ -265,9 +272,9 @@ TEST_F(ScriptRunnerTest, QueueReentrantScript_Async) {
auto* script_loader2 = MockScriptLoader::CreateAsync(document_);
auto* script_loader3 = MockScriptLoader::CreateAsync(document_);
script_runner_->QueueScriptForExecution(script_loader1);
script_runner_->QueueScriptForExecution(script_loader2);
script_runner_->QueueScriptForExecution(script_loader3);
QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader2);
QueueScriptForExecution(script_loader3);
NotifyScriptReady(script_loader1);
auto* script_loader = script_loader2;
......@@ -305,14 +312,14 @@ TEST_F(ScriptRunnerTest, QueueReentrantScript_InOrder) {
auto* script_loader2 = MockScriptLoader::CreateInOrder(document_);
auto* script_loader3 = MockScriptLoader::CreateInOrder(document_);
script_runner_->QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader1);
NotifyScriptReady(script_loader1);
MockScriptLoader* script_loader = script_loader2;
EXPECT_CALL(*script_loader1, Execute())
.WillOnce(Invoke([script_loader, &script_loader2, this] {
order_.push_back(1);
script_runner_->QueueScriptForExecution(script_loader);
QueueScriptForExecution(script_loader);
NotifyScriptReady(script_loader2);
}));
......@@ -320,7 +327,7 @@ TEST_F(ScriptRunnerTest, QueueReentrantScript_InOrder) {
EXPECT_CALL(*script_loader2, Execute())
.WillOnce(Invoke([script_loader, &script_loader3, this] {
order_.push_back(2);
script_runner_->QueueScriptForExecution(script_loader);
QueueScriptForExecution(script_loader);
NotifyScriptReady(script_loader3);
}));
......@@ -348,7 +355,7 @@ TEST_F(ScriptRunnerTest, QueueReentrantScript_ManyAsyncScripts) {
for (int i = 0; i < 20; i++) {
script_loaders[i] = MockScriptLoader::CreateAsync(document_);
script_runner_->QueueScriptForExecution(script_loaders[i]);
QueueScriptForExecution(script_loaders[i]);
if (i > 0) {
EXPECT_CALL(*script_loaders[i], Execute()).WillOnce(Invoke([this, i] {
......@@ -381,9 +388,9 @@ TEST_F(ScriptRunnerTest, ResumeAndSuspend_InOrder) {
auto* script_loader2 = MockScriptLoader::CreateInOrder(document_);
auto* script_loader3 = MockScriptLoader::CreateInOrder(document_);
script_runner_->QueueScriptForExecution(script_loader1);
script_runner_->QueueScriptForExecution(script_loader2);
script_runner_->QueueScriptForExecution(script_loader3);
QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader2);
QueueScriptForExecution(script_loader3);
EXPECT_CALL(*script_loader1, Execute()).WillOnce(Invoke([this] {
order_.push_back(1);
......@@ -413,9 +420,9 @@ TEST_F(ScriptRunnerTest, ResumeAndSuspend_Async) {
auto* script_loader2 = MockScriptLoader::CreateAsync(document_);
auto* script_loader3 = MockScriptLoader::CreateAsync(document_);
script_runner_->QueueScriptForExecution(script_loader1);
script_runner_->QueueScriptForExecution(script_loader2);
script_runner_->QueueScriptForExecution(script_loader3);
QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader2);
QueueScriptForExecution(script_loader3);
NotifyScriptReady(script_loader1);
NotifyScriptReady(script_loader2);
......@@ -444,8 +451,8 @@ TEST_F(ScriptRunnerTest, LateNotifications) {
auto* script_loader1 = MockScriptLoader::CreateInOrder(document_);
auto* script_loader2 = MockScriptLoader::CreateInOrder(document_);
script_runner_->QueueScriptForExecution(script_loader1);
script_runner_->QueueScriptForExecution(script_loader2);
QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader2);
EXPECT_CALL(*script_loader1, Execute()).WillOnce(Invoke([this] {
order_.push_back(1);
......@@ -471,8 +478,8 @@ TEST_F(ScriptRunnerTest, TasksWithDeadScriptRunner) {
Persistent<MockScriptLoader> script_loader2 =
MockScriptLoader::CreateAsync(document_);
script_runner_->QueueScriptForExecution(script_loader1);
script_runner_->QueueScriptForExecution(script_loader2);
QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader2);
NotifyScriptReady(script_loader1);
NotifyScriptReady(script_loader2);
......@@ -493,14 +500,14 @@ TEST_F(ScriptRunnerTest, TryStreamWhenEnqueingScript) {
auto* script_loader1 = MockScriptLoader::CreateAsync(document_);
script_loader1->GetPendingScriptIfControlledByScriptRunner()->SetIsReady(
true);
script_runner_->QueueScriptForExecution(script_loader1);
QueueScriptForExecution(script_loader1);
}
TEST_F(ScriptRunnerTest, DontExecuteWhileStreaming) {
auto* script_loader = MockScriptLoader::CreateAsync(document_);
// Enqueue script.
script_runner_->QueueScriptForExecution(script_loader);
QueueScriptForExecution(script_loader);
// Simulate script load and mark the pending script as streaming ready.
script_loader->GetPendingScriptIfControlledByScriptRunner()->SetIsReady(true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment