// 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::createForScript( PassRefPtr element, TextPosition position, HTMLScriptRunnerHost* host) { return adoptPtr(new HTMLScriptRunner(element, position, host)); } HTMLScriptRunner::HTMLScriptRunner(PassRefPtr 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 module, RefPtr 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(), m_element.get()); } advanceTo(StateCompleted); // We may be deleted at this point. } }