Index: Source/bindings/core/dart/DartApplicationLoader.cpp |
diff --git a/Source/bindings/core/dart/DartApplicationLoader.cpp b/Source/bindings/core/dart/DartApplicationLoader.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5a2612e63c2a41f4648ef8357fcd41072dcc140f |
--- /dev/null |
+++ b/Source/bindings/core/dart/DartApplicationLoader.cpp |
@@ -0,0 +1,741 @@ |
+// Copyright (c) 2009, Google Inc. |
+// All rights reserved. |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+#include "config.h" |
+#include "bindings/core/dart/DartApplicationLoader.h" |
+ |
+#include "core/FetchInitiatorTypeNames.h" |
+#include "bindings/core/dart/DartDOMWrapper.h" |
+#include "bindings/core/dart/DartInspectorTimeline.h" |
+#include "bindings/core/dart/DartScriptDebugServer.h" |
+#include "bindings/core/dart/DartService.h" |
+#include "bindings/core/dart/DartUtilities.h" |
+#if defined(ENABLE_DART_NATIVE_EXTENSIONS) |
+#include "bindings/core/dart/shared_lib/DartNativeExtensions.h" |
+#endif |
+#include "bindings/core/v8/ScriptSourceCode.h" |
+#include "core/dom/Document.h" |
+#include "core/dom/Element.h" |
+#include "core/dom/ScriptLoader.h" |
+#include "core/dom/ScriptLoaderClient.h" |
+#include "core/fetch/CachedMetadata.h" |
+#include "core/fetch/FetchRequest.h" |
+#include "core/fetch/ResourceClient.h" |
+#include "core/fetch/ResourceFetcher.h" |
+#include "core/fetch/ScriptResource.h" |
+#include "core/frame/LocalDOMWindow.h" |
+#include "core/frame/LocalFrame.h" |
+#include "core/html/HTMLScriptElement.h" |
+#include "core/inspector/ScriptCallStack.h" |
+#include "core/svg/SVGScriptElement.h" |
+ |
+#include <dart_api.h> |
+#include <dart_mirrors_api.h> |
+ |
+namespace blink { |
+ |
+// FIXME(vsm): Define this in a central place. |
+static const unsigned dartTypeID = 0xDAADDAAD; |
+ |
+PassRefPtr<DartScriptInfo> DartScriptInfo::create(PassRefPtr<Element> scriptElement) |
+{ |
+ RefPtr<DartScriptInfo> scriptInfo = adoptRef(new DartScriptInfo(scriptElement)); |
+ if (scriptInfo->loader()) |
+ return scriptInfo; |
+ return nullptr; |
+} |
+ |
+String DartScriptInfo::sourceAttributeValue() const |
+{ |
+ return m_client->sourceAttributeValue(); |
+} |
+ |
+String DartScriptInfo::typeAttributeValue() const |
+{ |
+ return m_client->typeAttributeValue(); |
+} |
+ |
+String DartScriptInfo::scriptContent() |
+{ |
+ return m_loader->scriptContent(); |
+} |
+ |
+void DartScriptInfo::dispatchErrorEvent() |
+{ |
+ m_loader->dispatchErrorEvent(); |
+} |
+ |
+WTF::OrdinalNumber DartScriptInfo::startLineNumber() |
+{ |
+ return m_loader->startLineNumber(); |
+} |
+ |
+ScriptLoader* DartScriptInfo::loader() |
+{ |
+ return m_loader; |
+} |
+ |
+Document* DartScriptInfo::ownerDocument() |
+{ |
+ return m_element->ownerDocument(); |
+} |
+ |
+String DartScriptInfo::url() |
+{ |
+ String scriptURL = sourceAttributeValue(); |
+ KURL ownerURL = ownerDocument()->url(); |
+ return scriptURL.isEmpty() ? ownerURL : KURL(ownerURL, scriptURL); |
+} |
+ |
+DartScriptInfo::DartScriptInfo(PassRefPtr<Element> element) |
+{ |
+ m_element = element; |
+ if (m_element->document().isHTMLDocument()) { |
+ HTMLScriptElement* scriptElement = static_cast<HTMLScriptElement*>(m_element.get()); |
+ m_loader = scriptElement->loader(); |
+ m_client = static_cast<ScriptLoaderClient*>(scriptElement); |
+ } else if (m_element->document().isSVGDocument()) { |
+ SVGScriptElement* scriptElement = static_cast<SVGScriptElement*>(m_element.get()); |
+ m_loader = scriptElement->loader(); |
+ m_client = static_cast<ScriptLoaderClient*>(scriptElement); |
+ } else { |
+ // Invalid script element. |
+ m_loader = 0; |
+ m_client = 0; |
+ m_element = nullptr; |
+ } |
+} |
+ |
+void DartApplicationLoader::Callback::reportError(const String& error, const String& url) |
+{ |
+ m_originDocument->logExceptionToConsole(error + ": " + url, 0, url, 0, 0, nullptr); |
+ if (m_scriptInfo) |
+ m_scriptInfo->loader()->dispatchErrorEvent(); |
+} |
+ |
+ResourcePtr<ScriptResource> DartApplicationLoader::Callback::requestScript(FetchRequest& request) |
+{ |
+ return m_originDocument->fetcher()->fetchScript(request); |
+} |
+ |
+Document* DartApplicationLoader::Callback::document() |
+{ |
+ return m_scriptInfo ? m_scriptInfo->ownerDocument() : m_originDocument; |
+} |
+ |
+DartApplicationLoader::DartApplicationLoader( |
+ Document* document, |
+ bool domEnabled) |
+ : m_isolate(0) |
+ , m_originDocument(document) |
+ , m_loadCallback(nullptr) |
+ , m_domEnabled(domEnabled) |
+ , m_cacheable(false) |
+ , m_state(Uninitialized) |
+ , m_snapshotMode(SnapshotOff) |
+{ |
+ ASSERT(m_originDocument); |
+ |
+ DEFINE_STATIC_LOCAL(String, setting, (getenv("DART_SNAPSHOT_MODE"))); |
+ if (!setting.isNull()) { |
+ if (setting == "all") |
+ m_snapshotMode = SnapshotAll; |
+ else if (setting == "off") |
+ m_snapshotMode = SnapshotOff; |
+ else if (setting == "single") |
+ m_snapshotMode = SnapshotSingle; |
+ else |
+ DartUtilities::reportProblem(m_originDocument, String("Invalid DART_SNAPSHOT_MODE: ") + setting); |
+ } |
+} |
+ |
+void DartApplicationLoader::addRequest(PassRefPtr<DartScriptInfo> scriptInfo) |
+{ |
+ // FIXME(vsm): Temporary support to load extra Dart libraries via HTML imports before we encounter the main |
+ // script. In the future, this should map to deferred loading. |
+ RELEASE_ASSERT(m_state == Uninitialized); |
+ m_htmlImportedScripts.append(scriptInfo); |
+} |
+ |
+void DartApplicationLoader::initialize(Dart_Isolate isolate, const String& scriptUrl, PassRefPtr<Callback> loadCallback) |
+{ |
+ RELEASE_ASSERT(m_state == Uninitialized && !m_isolate); |
+ m_isolate = isolate; |
+ m_loadCallback = loadCallback; |
+ |
+ Document* document = m_loadCallback->document(); |
+ |
+ m_scriptUrl = KURL(document->baseURL(), scriptUrl); |
+ m_scriptUrlString = m_scriptUrl.string(); |
+ |
+ RELEASE_ASSERT(m_isolate == isolate); |
+ Dart_EnterIsolate(isolate); |
+ |
+ DartDOMData* domData = DartDOMData::current(); |
+ if (!domData->packageRoot()) { |
+ m_packageRoot = String(); |
+ } else { |
+ m_packageRoot = String(domData->packageRoot()); |
+ } |
+ // Associate application loader with current isolate, so we can retrieve it in libraryTagHandlerCallback. |
+ domData->setApplicationLoader(this); |
+ Dart_Handle ALLOW_UNUSED result = Dart_SetLibraryTagHandler(&libraryTagHandlerCallback); |
+ ASSERT(!Dart_IsError(result)); |
+ |
+ // FIXME: Stay in isolate for processing below. |
+ Dart_ExitIsolate(); |
+ m_state = Initialized; |
+} |
+ |
+void DartApplicationLoader::processRequests(Dart_Isolate isolate, const ScriptSourceCode& sourceCode, PassRefPtr<Callback> loadCallback) |
+{ |
+ DART_START_TIMER(); |
+ const String& scriptUrl = sourceCode.url(); |
+ initialize(isolate, scriptUrl, loadCallback); |
+ DART_RECORD_TIMER(" DartApplicationLoader::initialize took"); |
+ |
+ m_state = Fetching; |
+ |
+ // Check for cached snapshot. |
+ ScriptResource* scriptResource = sourceCode.resource(); |
+ if (scriptResource && m_snapshotMode != SnapshotOff) { |
+ // This is cacheable if there is a resource we can cache the snapshot on. |
+ m_cacheable = true; |
+ CachedMetadata* cachedMetadata = scriptResource->cachedMetadata(dartTypeID); |
+ if (cachedMetadata) { |
+ loadScriptFromSnapshot(sourceCode.url(), reinterpret_cast<const uint8_t*>(cachedMetadata->data()), cachedMetadata->size()); |
+ return; |
+ } |
+ } |
+ |
+ // Set up root library for main DOM isolate. |
+ { |
+ DartIsolateScope isolateScope(m_isolate); |
+ DartApiScope apiScope; |
+ |
+ ASSERT(Dart_IsNull(Dart_RootLibrary())); |
+ |
+ Document* document = m_loadCallback->document(); |
+ const String& source = sourceCode.source(); |
+ int lineNumber = sourceCode.startLine(); |
+ |
+ // Use zero-based counting instead of one-based. |
+ lineNumber = (lineNumber <= 0) ? 0 : lineNumber - 1; |
+ |
+ String canonical = KURL(document->url(), scriptUrl).string(); |
+ m_pendingLibraries.add(canonical); |
+ process(canonical, source, lineNumber); |
+ |
+ // Any problem loading main script we're done. |
+ if (m_state == Error) |
+ return; |
+ |
+ RELEASE_ASSERT(m_state >= Loading); |
+ } |
+ |
+ // FIXME(vsm): This will go away. |
+ // Load HTML imported dart scripts. |
+ while (!m_htmlImportedScripts.isEmpty()) { |
+ RefPtr<DartScriptInfo> script = m_htmlImportedScripts.takeFirst(); |
+ const String& src = script->sourceAttributeValue(); |
+ const String& url = script->url(); |
+ if (src.isEmpty()) { |
+ // Inline script. |
+ ASSERT(!m_pendingLibraries.contains(url)); |
+ m_pendingLibraries.add(url); |
+ intptr_t lineOffset = script->startLineNumber().zeroBasedInt(); |
+ // Blink gives generated script tags an invalid start line. |
+ if (lineOffset < 0) |
+ lineOffset = 0; |
+ process(script->url(), script->scriptContent(), lineOffset); |
+ } else { |
+ // Canonicalize the src attribute url. |
+ String canonical = KURL(script->ownerDocument()->url(), src).string(); |
+ |
+ // Check if this was loaded by an earlier script. |
+ { |
+ DartIsolateScope isolateScope(m_isolate); |
+ DartApiScope apiScope; |
+ Dart_Handle library = Dart_LookupLibrary(DartUtilities::safeStringToDartString(canonical)); |
+ if (!Dart_IsError(library)) |
+ continue; |
+ } |
+ |
+ // Request if we don't have the script or haven't already requested it. |
+ if (!m_pendingLibraries.contains(canonical)) { |
+ m_pendingLibraries.add(canonical); |
+ fetchScriptResource(canonical); |
+ } |
+ } |
+ } |
+ |
+ // FIXME(vsm): Once m_htmlImportedScripts goes away, we should be able to delete this. |
+ // Processing HTML imports may be made the app ready without running. |
+ RELEASE_ASSERT(m_state == Error || m_state >= Loading); |
+ if (ready() && m_state == Loading) { |
+ m_state = Ready; |
+ // All dependences are in. |
+ m_loadCallback->ready(); |
+ } |
+ |
+ DART_RECORD_TIMER(" DartApplicationLoader::processRequests took"); |
+} |
+ |
+void DartApplicationLoader::processSingleRequest(Dart_Isolate isolate, const String& scriptUrl, PassRefPtr<Callback> loadCallback) |
+{ |
+ initialize(isolate, scriptUrl, loadCallback); |
+ // Use the resolved scriptUrl. |
+ m_pendingLibraries.add(m_scriptUrlString); |
+ m_state = Fetching; |
+ fetchScriptResource(m_scriptUrlString); |
+} |
+ |
+void DartApplicationLoader::load(PassRefPtr<DartErrorEventDispatcher> errorEventDispatcher) |
+{ |
+ RELEASE_ASSERT(m_state == Ready || m_state == DeferredReady); |
+ |
+ m_errorEventDispatcher = errorEventDispatcher; |
+ |
+ DartIsolateScope isolateScope(m_isolate); |
+ DartApiScope dartApiScope; |
+ bool completeFutures = false; |
+ |
+ // Finalize classes and complete futures if there are any deferred loads. |
+ if (m_state == DeferredReady) { |
+ // We have already invoked the entry point on the main script URL at |
+ // this point we will start running dart code again. |
+ m_state = Running; |
+ completeFutures = true; |
+ } else { |
+ RELEASE_ASSERT(m_state == Ready); |
+ } |
+ |
+ { |
+ V8Scope v8scope(DartDOMData::current()); |
+ Dart_Handle result = Dart_FinalizeLoading(completeFutures); |
+ |
+ if (completeFutures && m_domEnabled) { |
+ // Call this after Dart_FinalizeLoading so that everything is ready. |
+ Timeline timeline(m_originDocument->frame(), String("notifyDebugServerDeferredReady@") + m_scriptUrlString); |
+ DartScriptDebugServer::shared().deferredReady(); |
+ } |
+ |
+ if (Dart_IsError(result)) { |
+ reportDartError(result); |
+ return; |
+ } |
+ } |
+ // Invoke the entry point on the main script URL if it has not yet been |
+ // invoked. |
+ if (m_state == Ready) { |
+ // Call the entry point on the main script URL. |
+ callEntryPoint(); |
+ } |
+} |
+ |
+void DartApplicationLoader::loadScriptFromSnapshot(const String& url, const uint8_t* snapshot, intptr_t snapshotSize) |
+{ |
+ DART_START_TIMER(); |
+ RELEASE_ASSERT(m_state == Fetching); |
+ |
+ Timeline timeline(m_originDocument->frame(), String("loadSnapshot@") + m_scriptUrlString); |
+ m_scriptUrlString = url; |
+ DartIsolateScope isolateScope(m_isolate); |
+ DartApiScope apiScope; |
+ Dart_Handle result = Dart_LoadScriptFromSnapshot(snapshot, snapshotSize); |
+ if (Dart_IsError(result)) { |
+ reportDartError(result); |
+ return; |
+ } |
+ |
+ m_state = Ready; |
+ m_loadCallback->ready(); |
+ DART_RECORD_TIMER(" DartApplicationLoader::loadScriptFromSnapshot took"); |
+} |
+ |
+void DartApplicationLoader::callEntryPoint() |
+{ |
+ RELEASE_ASSERT(m_state == Ready); |
+ |
+ Timeline timeline(m_originDocument->frame(), String("callEntryPoint@") + m_scriptUrlString); |
+ |
+ if (m_cacheable) { |
+ // Snapshot single-script applications. |
+ ResourceFetcher* loader = m_originDocument->fetcher(); |
+ FetchRequest request(m_originDocument->completeURL(m_scriptUrlString), FetchInitiatorTypeNames::document); |
+ ResourcePtr<ScriptResource> scriptResource = loader->fetchScript(request); |
+ |
+ // Check if already snapshotted. |
+ if (scriptResource && !scriptResource->cachedMetadata(dartTypeID)) { |
+ uint8_t* buffer; |
+ intptr_t size; |
+ Dart_Handle result = Dart_CreateScriptSnapshot(&buffer, &size); |
+ if (Dart_IsError(result)) { |
+ reportDartError(result); |
+ // FIXME: exiting early might be not the best option if error is due to snapshot |
+ // creation proper (and not due to compilation), even though it's unlikely. |
+ // Consider other options like Dart_CompileAll. |
+ return; |
+ } |
+ |
+ Vector<uint8_t>* snapshot = DartDOMData::current()->applicationSnapshot(); |
+ ASSERT(snapshot->isEmpty()); |
+ snapshot->append(buffer, size); |
+ |
+ // Write the snapshot. |
+ scriptResource->setCachedMetadata(dartTypeID, reinterpret_cast<const char*>(buffer), size); |
+ } |
+ } |
+ |
+ m_state = Running; |
+ |
+ if (m_domEnabled) { |
+ Timeline timeline(m_originDocument->frame(), String("notifyDebugServer@") + m_scriptUrlString); |
+ DartScriptDebugServer::shared().isolateLoaded(); |
+ } |
+ |
+ if (m_domEnabled) { |
+ V8Scope v8scope(DartDOMData::current()); |
+ Dart_Handle mainLibrary = topLevelLibrary(); |
+ |
+ logObservatoryInformation(); |
+ |
+ // Trampoline to invoke main. |
+ // FIXME: Use the page library instead. To do this, we need to import each script tag's library into the page |
+ // with a unique prefix to ensure a secondary script doesn't define a main. |
+ String trampolineUrl = m_scriptUrlString + "$trampoline"; |
+ Dart_Handle trampoline = Dart_LoadLibrary(DartUtilities::safeStringToDartString(trampolineUrl), Dart_NewStringFromCString(""), 0, 0); |
+ Dart_LibraryImportLibrary(trampoline, mainLibrary, Dart_Null()); |
+ |
+ Dart_Handle result = Dart_FinalizeLoading(false); |
+ if (Dart_IsError(result)) { |
+ DartUtilities::reportProblem(m_originDocument, result, m_scriptUrlString); |
+ } |
+ |
+ // FIXME: Settle on proper behavior here. We have not determined exactly when |
+ // or how often we'll call the entry point. |
+ Dart_Handle entryPoint = Dart_NewStringFromCString("main"); |
+ Dart_Handle main = Dart_LookupFunction(trampoline, entryPoint); |
+ if (!Dart_IsNull(main)) { |
+ // FIXME: Avoid relooking up main. |
+ Dart_Handle result = Dart_Invoke(trampoline, entryPoint, 0, 0); |
+ if (Dart_IsError(result)) { |
+ DartUtilities::reportProblem(m_originDocument, result, m_scriptUrlString); |
+ } |
+ } |
+ |
+ } |
+} |
+ |
+ |
+void DartApplicationLoader::logObservatoryInformation() |
+{ |
+ const char* serverIp = DartService::GetServerIP(); |
+ const int serverPort = DartService::GetServerPort(); |
+ String message = String::format("Observatory listening at http://%s:%d/", serverIp, serverPort); |
+ m_originDocument->addConsoleMessage( |
+ ConsoleMessage::create(JSMessageSource, InfoMessageLevel, message)); |
+} |
+ |
+ |
+void DartApplicationLoader::validateUrlLoaded(const String& url) |
+{ |
+ RELEASE_ASSERT(m_state == Running); |
+ |
+ DartIsolateScope isolateScope(m_isolate); |
+ DartApiScope apiScope; |
+ |
+ Dart_Handle library = Dart_LookupLibrary(DartUtilities::safeStringToDartString(url)); |
+ if (!Dart_IsNull(library)) { |
+ DartUtilities::reportProblem(m_originDocument, "URL must be imported by main Dart script: " + url); |
+ } |
+} |
+ |
+Dart_Handle DartApplicationLoader::topLevelLibrary() |
+{ |
+ Dart_Handle library = Dart_LookupLibrary(DartUtilities::safeStringToDartString(m_scriptUrlString)); |
+ ASSERT(!Dart_IsError(library)); |
+ return library; |
+} |
+ |
+void DartApplicationLoader::findDependences(const String& url, const String& source, intptr_t lineNumber) |
+{ |
+ ASSERT(m_pendingLibraries.contains(url) || m_pendingSource.contains(url)); |
+ |
+ DartIsolateScope isolateScope(m_isolate); |
+ DartApiScope dartApiScope; |
+ |
+ if (m_pendingLibraries.contains(url)) { |
+ processLibrary(url, source, lineNumber); |
+ } else { |
+ ASSERT(m_pendingSource.contains(url)); |
+ processSource(url, source, lineNumber); |
+ } |
+} |
+ |
+void DartApplicationLoader::processLibrary(const String& url, const String& source, intptr_t lineNumber) |
+{ |
+ ASSERT(m_pendingLibraries.contains(url)); |
+ |
+ Dart_Handle result; |
+ if (m_state == Fetching) { |
+ // A spawned isolate. |
+ m_state = Loading; |
+ result = Dart_LoadScript(DartUtilities::safeStringToDartString(url), DartUtilities::convertSourceString(source), lineNumber, 0); |
+ } else { |
+ result = Dart_LoadLibrary(DartUtilities::safeStringToDartString(url), DartUtilities::convertSourceString(source), lineNumber, 0); |
+ } |
+ if (Dart_IsError(result)) |
+ reportError(result, url); |
+ |
+ m_pendingLibraries.remove(url); |
+} |
+ |
+void DartApplicationLoader::processSource(const String& url, const String& source, intptr_t lineNumber) |
+{ |
+ ASSERT(m_pendingSource.contains(url)); |
+ HandleSet* importers = m_pendingSource.take(url); |
+ for (HandleSet::iterator i = importers->begin(); i != importers->end(); ++i) { |
+ Dart_Handle persistent = *i; |
+ Dart_Handle library = Dart_HandleFromPersistent(persistent); |
+ Dart_DeletePersistentHandle(persistent); |
+ |
+ Dart_Handle result = Dart_LoadSource(library, DartUtilities::safeStringToDartString(url), DartUtilities::convertSourceString(source), lineNumber, 0); |
+ if (Dart_IsError(result)) |
+ reportError(result, url); |
+ } |
+ delete importers; |
+} |
+ |
+void DartApplicationLoader::process(const String& url, const String& source, intptr_t lineNumber) |
+{ |
+ if (m_state == Error) |
+ return; |
+ |
+ if (url != m_scriptUrlString && m_snapshotMode != SnapshotAll) |
+ m_cacheable = false; |
+ |
+ RELEASE_ASSERT(m_state == Fetching || m_state == Loading || m_state == DeferredLoading); |
+ findDependences(url, source, lineNumber); |
+ RELEASE_ASSERT(m_state == Error || m_state == Loading || m_state == DeferredLoading); |
+ |
+ if (ready()) { |
+ m_state = (m_state == Loading) ? Ready : DeferredReady; |
+ // All dependences are in. |
+ m_loadCallback->ready(); |
+ } |
+} |
+ |
+Dart_Handle DartApplicationLoader::libraryTagHandlerCallback(Dart_LibraryTag tag, Dart_Handle library, Dart_Handle urlHandle) |
+{ |
+ ASSERT(Dart_CurrentIsolate()); |
+ ASSERT(Dart_IsLibrary(library)); |
+ |
+ const String url = DartUtilities::toString(urlHandle); |
+ |
+ if (tag == Dart_kCanonicalizeUrl) { |
+ if (url.startsWith("dart:")) { |
+ if (url == "dart:io" || url == "dart:_builtin" || url == "dart:vmserviceio") { |
+ // We do not allow Dartium isolates to import 'dart:io' and 'dart:_builtin'. |
+ return Dart_NewApiError("The requested built-in library is not available on Dartium."); |
+ } |
+ } |
+ // If a dart application calls spawnUri, the DartVM will call this |
+ // libraryTagHandler to canonicalize the url. |
+ // DartDOMData::current()->scriptLoader() may be 0 at this point. |
+ return DartUtilities::canonicalizeUrl(library, urlHandle, url); |
+ } |
+ |
+ if (url.startsWith("dart:")) { |
+ // All supported system URLs are already built-in and shouldn't get to this point. |
+ return Dart_NewApiError("The requested built-in library is not available on Dartium."); |
+ } |
+ |
+ RefPtr<DartApplicationLoader> loader = DartDOMData::current()->applicationLoader(); |
+ ASSERT(loader); |
+ // We can only handle requests in one of the following states. |
+ if (loader->m_state == Error) |
+ return Dart_NewApiError("The isolate is in an invalid state."); |
+ RELEASE_ASSERT(loader->m_state == Fetching || loader->m_state == Loading || loader->m_state == Running || loader->m_state == DeferredLoading); |
+ |
+ |
+ // Fetch non-system URLs. |
+ if (tag == Dart_kImportTag) { |
+ if (loader->m_pendingLibraries.contains(url)) |
+ return Dart_True(); |
+#if defined(ENABLE_DART_NATIVE_EXTENSIONS) |
+ if (url.startsWith("dart-ext:")) { |
+ return DartNativeExtensions::loadExtension(url, library); |
+ } |
+#endif |
+ loader->m_pendingLibraries.add(url); |
+ } else if (tag == Dart_kSourceTag) { |
+ Dart_PersistentHandle importer = Dart_NewPersistentHandle(library); |
+ HandleSet* importers; |
+ if (loader->m_pendingSource.contains(url)) { |
+ // We have already requested this url. It is a part of more than one library. |
+ importers = loader->m_pendingSource.get(url); |
+ importers->append(importer); |
+ return Dart_True(); |
+ } |
+ importers = new HandleSet(); |
+ loader->m_pendingSource.add(url, importers); |
+ importers->append(importer); |
+ } else { |
+ ASSERT_NOT_REACHED(); |
+ } |
+ |
+ // If the isolate is running, this is part of a deferred load request. |
+ if (loader->m_state == Running) |
+ loader->m_state = DeferredLoading; |
+ loader->fetchScriptResource(url); |
+ return Dart_True(); |
+} |
+ |
+class ScriptLoadedCallback : public ScriptResourceClient { |
+public: |
+ ScriptLoadedCallback(String url, PassRefPtr<DartApplicationLoader> loader, ResourcePtr<ScriptResource> scriptResource) |
+ : m_url(url) |
+ , m_loader(loader) |
+ , m_scriptResource(scriptResource) |
+ { |
+ } |
+ |
+ virtual void notifyFinished(Resource* cachedResource) |
+ { |
+ ASSERT(cachedResource->type() == Resource::Script); |
+ ASSERT(cachedResource == m_scriptResource.get()); |
+ ASSERT(WTF::isMainThread()); |
+ |
+ if (cachedResource->errorOccurred()) { |
+ m_loader->reportError(String("An error occurred loading file"), m_url); |
+ } else if (cachedResource->wasCanceled()) { |
+ // FIXME: shall we let VM know, so it can inform application some of its |
+ // resources cannot be loaded? |
+ m_loader->reportError(String("File request cancelled"), m_url); |
+ } else { |
+ ScriptSourceCode sourceCode(m_scriptResource.get()); |
+ |
+ // Use the original url associated with the Script so that |
+ // redirects do not break the DartScriptLoader. |
+ // FIXME: is this the correct behavior? This functionality is |
+ // very convenient when you want the source file to act as if |
+ // it was from the original location but that isn't always |
+ // what the user expects. |
+ m_loader->process(m_url, sourceCode.source(), 0); |
+ } |
+ |
+ m_scriptResource->removeClient(this); |
+ delete this; |
+ } |
+ |
+private: |
+ String m_url; |
+ RefPtr<DartApplicationLoader> m_loader; |
+ ResourcePtr<ScriptResource> m_scriptResource; |
+}; |
+ |
+static String resolveUrl(String mainLibraryURL, const String& url, const String& packageRootOverride) |
+{ |
+ if (!url.startsWith("package:") || url.startsWith("package://")) |
+ return url; |
+ |
+ String packageRoot; |
+ String packageUrl; |
+ if (!packageRootOverride.isNull()) { |
+ // Resolve with respect to the override. Append a |
+ // slash to ensure that resolution is against this |
+ // path and not its parent. |
+ packageRoot = packageRootOverride + "/"; |
+ // Strip the 'package:' prefix. |
+ packageUrl = url.substring(8); |
+ if (!(packageRoot.startsWith("file:") || packageRoot.startsWith("http:") || packageRoot.startsWith("https:"))) { |
+ return packageRoot + packageUrl; |
+ } |
+ } else { |
+ // Resolve with respect to the entry point's URL. Note, the |
+ // trailing file name in the entry point URL (e.g., |
+ // 'rootpath/mainapp.dart') is stripped by the KURL |
+ // constructor below. |
+ packageRoot = mainLibraryURL; |
+ packageUrl = String("packages/") + url.substring(8); |
+ } |
+ return KURL(KURL(KURL(), packageRoot), packageUrl).string(); |
+} |
+ |
+void DartApplicationLoader::fetchScriptResource(const String& url) |
+{ |
+ // Request loading of script dependencies. |
+ FetchRequest request(m_loadCallback->completeURL(resolveUrl(m_scriptUrl, url, m_packageRoot)), |
+ FetchInitiatorTypeNames::document, "utf8"); |
+ // FIXME: what about charset for this script, maybe use charset of initial script tag? |
+ ResourcePtr<ScriptResource> scriptResource = m_loadCallback->requestScript(request); |
+ if (scriptResource) { |
+ scriptResource->addClient(new ScriptLoadedCallback(m_loadCallback->completeURL(url), this, scriptResource)); |
+ } else { |
+ m_loadCallback->reportError(String("File request error"), url); |
+ } |
+} |
+ |
+// FIXME(vsm): Merge these functions below. We need to be careful about where the error is dispatched. |
+void DartApplicationLoader::scriptLoadError(String failedUrl) |
+{ |
+ if (m_state < Running) |
+ m_state = Error; |
+ // FIXME: try to dig out line number, -1 for now. |
+ if (failedUrl.startsWith(String("dart:"))) { |
+ m_originDocument->logExceptionToConsole(String("The built-in library '") + failedUrl + String("' is not available on Dartium."), 0, m_scriptUrlString, -1, 0, nullptr); |
+ } else { |
+ m_originDocument->logExceptionToConsole(String("Failed to load a file ") + failedUrl, 0, m_scriptUrlString, -1, 0, nullptr); |
+ } |
+ RELEASE_ASSERT(m_errorEventDispatcher); |
+ m_errorEventDispatcher->dispatchErrorEvent(); |
+} |
+ |
+void DartApplicationLoader::reportDartError(Dart_Handle error) |
+{ |
+ if (m_state < Running) |
+ m_state = Error; |
+ DartUtilities::reportProblem(m_originDocument, error, m_scriptUrlString); |
+} |
+ |
+void DartApplicationLoader::reportError(Dart_Handle error, const String& url) |
+{ |
+ reportError(Dart_GetError(error), url); |
+} |
+ |
+void DartApplicationLoader::reportError(const String& error, const String& url) |
+{ |
+ if (m_state < Running) |
+ m_state = Error; |
+ m_loadCallback->reportError(error, url); |
+} |
+ |
+ |
+} |