flutter_flutter/engine/core/html/parser/HTMLScriptRunner.cpp
Eric Seidel c78cea7e54 Allow multiple dart <script> tags in .sky files
This does several things:
1.  Teaches sky about asynchronous script execution. Previously once all imports
were loaded and the script text was available, we executed a script and assumed
it completed synchronously.  We left the parser loop to do so, but that was fine
as the next chunk from the background thread would resume the parser.  In this
change scripts now load and execute separately.  The "load" step may trigger
further dart import loads which may cause the execution to happen asynchronously
which required teaching both the DartController and the HTMLScriptRunner to
take callbacks to allow HTMLDocumentParser to know to continue parsing after
the Dart script has resolved its imports and executed.

This required re-working some of how the parser executes scripts and I
re-purposed isWaitingForScripts to include "is the parser blocked" where
as before it was limited only to "does the treebuilder have a script", even
though the imports system may have had pending scripts as well.

I made HTMLScriptRunner live only as long as the script it was executing
since it only contained per-script state at this point.

2.  Fixed an error reporting bug whereby we would not show errors when "init"
failed to execute, only "main".  This required using the dart_mirrors_api.h
which required adding an include path to the core build. :(

3.  Made it possible for a single sky file to contain multiple dart <script>
tags.  Each <script> is a separate library and executes as
soon as </script> is seen.  main or init is called for each.  This required
mangling "urls" for these script blocks since Dart unique's libraries by urls.
Before this change it may have been possible to do <import 'foo.sky'> and then
<script>import 'foo.sky'</script> and have it work!?

R=abarth@chromium.org
BUG=

Review URL: https://codereview.chromium.org/938623005
2015-02-20 11:08:28 -08:00

126 lines
3.7 KiB
C++

// Copyright 2014 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 "sky/engine/config.h"
#include "sky/engine/core/html/parser/HTMLScriptRunner.h"
#include "base/bind.h"
#include "sky/engine/core/app/AbstractModule.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/dom/Microtask.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/html/HTMLScriptElement.h"
#include "sky/engine/core/script/dart_controller.h"
namespace blink {
PassOwnPtr<HTMLScriptRunner> HTMLScriptRunner::createForScript(
PassRefPtr<HTMLScriptElement> element,
TextPosition position,
HTMLScriptRunnerHost* host) {
return adoptPtr(new HTMLScriptRunner(element, position, host));
}
HTMLScriptRunner::HTMLScriptRunner(PassRefPtr<HTMLScriptElement> element,
TextPosition position,
HTMLScriptRunnerHost* host)
: m_host(host),
m_element(element),
m_position(position),
m_state(StateInitial),
m_weakFactory(this) {
}
HTMLScriptRunner::~HTMLScriptRunner()
{
// If we hit this ASSERT we failed to notify the ScriptRunnerHost!
ASSERT(m_state == StateCompleted);
}
bool HTMLScriptRunner::isExecutingScript() const {
return m_state == StateExecuting;
}
void HTMLScriptRunner::advanceTo(State state, AdvanceType advanceType) {
if (advanceType == ExecutionNormal) {
switch (m_state) {
case StateInitial:
ASSERT(state == StateLoading);
break;
case StateLoading:
ASSERT(state == StateExecuting);
break;
case StateExecuting:
ASSERT(state == StateCompleted);
break;
case StateCompleted:
ASSERT_NOT_REACHED();
}
}
m_state = state;
if (m_state == StateCompleted)
m_host->scriptExecutionCompleted();
// We may be deleted by scriptExecutionCompleted().
}
static LocalFrame* contextFrame(Element* element) {
Document* contextDocument = element->document().contextDocument().get();
if (!contextDocument)
return nullptr;
LocalFrame* frame = contextDocument->frame();
if (!frame)
return nullptr;
return frame;
}
void HTMLScriptRunner::scriptFailed() {
advanceTo(StateCompleted, ExecutionFailure);
}
void HTMLScriptRunner::start() {
ASSERT(m_state == StateInitial);
ASSERT(m_element->document().haveImportsLoaded());
Document& sourceDocument = m_element->document();
String source = m_element->textContent();
LocalFrame* frame = contextFrame(m_element.get());
if (!frame)
return scriptFailed();
advanceTo(StateLoading);
ASSERT(sourceDocument.module());
DartController::LoadFinishedCallback loadFinished = base::Bind(
&HTMLScriptRunner::executeLibrary, m_weakFactory.GetWeakPtr());
frame->dart().LoadScriptInModule(sourceDocument.module(), source,
m_position, loadFinished);
}
// Enforce that the caller holds refs using RefPtr.
// FIXME: Neither of these should need refs, the Script should hold onto the
// library the document should keep the Module alive.
void HTMLScriptRunner::executeLibrary(RefPtr<AbstractModule> module,
RefPtr<DartValue> library) {
if (!module)
return scriptFailed();
advanceTo(StateExecuting);
// Ian says we'll remove microtasks, but for now execute them right before
// we "run" the script (call 'init'), not at dependency resolution
// or script failures, etc.
Microtask::performCheckpoint();
if (LocalFrame* frame = contextFrame(m_element.get()))
frame->dart().ExecuteLibraryInModule(module.get(), library->dart_value());
advanceTo(StateCompleted);
// We may be deleted at this point.
}
}