Skip to content

Commit

Permalink
Use PendingScript in ScriptRunner
Browse files Browse the repository at this point in the history
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: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Daniel Vogelheim <vogelheim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#560874}
  • Loading branch information
hiroshige-g authored and Commit Bot committed May 23, 2018
1 parent be64717 commit aa3ce76
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 96 deletions.
15 changes: 12 additions & 3 deletions third_party/blink/renderer/core/script/script_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -979,4 +982,10 @@ PendingScript* ScriptLoader::GetPendingScriptIfControlledByScriptRunner() {
return pending_script_;
}

PendingScript*
ScriptLoader::GetPendingScriptIfControlledByScriptRunnerForCrossDocMove() {
DCHECK(!pending_script_ || pending_script_->IsControlledByScriptRunner());
return pending_script_;
}

} // namespace blink
5 changes: 5 additions & 0 deletions third_party/blink/renderer/core/script/script_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
121 changes: 69 additions & 52 deletions third_party/blink/renderer/core/script/script_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -99,29 +96,25 @@ 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());
PostTask(FROM_HERE);
}
}

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();
Expand All @@ -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);
Expand Down Expand Up @@ -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();
}
}
Expand All @@ -205,36 +210,50 @@ 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;
}

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();
DCHECK(pending_script);
return !pending_script->IsCurrentlyStreaming();
});
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();
});
if (it == async_scripts_to_execute_soon_.end()) {
return false;
}

// 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;
}
Expand Down Expand Up @@ -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;

Expand Down
22 changes: 12 additions & 10 deletions third_party/blink/renderer/core/script/script_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
namespace blink {

class Document;
class PendingScript;
class ScriptLoader;

class CORE_EXPORT ScriptRunner final
Expand All @@ -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*);
Expand All @@ -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&);
Expand All @@ -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_;

Expand Down
Loading

0 comments on commit aa3ce76

Please sign in to comment.