Index: Source/bindings/dart/DartAsyncLoader.cpp |
diff --git a/Source/bindings/dart/DartAsyncLoader.cpp b/Source/bindings/dart/DartAsyncLoader.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f9affeb6bdb2149fe8e2136e598368f356d64f95 |
--- /dev/null |
+++ b/Source/bindings/dart/DartAsyncLoader.cpp |
@@ -0,0 +1,263 @@ |
+// Copyright (c) 2013, 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/dart/DartAsyncLoader.h" |
+ |
+#include "FetchInitiatorTypeNames.h" |
+#include "bindings/dart/DartUtilities.h" |
+#include "bindings/v8/ScriptSourceCode.h" |
+#include "core/dom/Document.h" |
+#include "core/dom/ScriptLoader.h" |
+#include "core/fetch/FetchRequest.h" |
+#include "core/fetch/ResourceClient.h" |
+#include "core/fetch/ResourceFetcher.h" |
+#include "core/fetch/ScriptResource.h" |
+#include "core/inspector/ScriptCallStack.h" |
+ |
+#include <dart_api.h> |
+ |
+namespace WebCore { |
+ |
+static const unsigned dartTypeID = 0xDAADDAAD; |
+ |
+void DartAsyncLoader::LoadCallback::reportError(const String& error, const String& url) |
+{ |
+ m_originDocument->logExceptionToConsole(error + ": " + url, url, 0, 0, 0); |
+ if (m_scriptLoader) |
+ m_scriptLoader->dispatchErrorEvent(); |
+} |
+ |
+ResourcePtr<ScriptResource> DartAsyncLoader::LoadCallback::requestScript(FetchRequest& request) |
+{ |
+ return m_originDocument->fetcher()->requestScript(request); |
+} |
+ |
+ |
+DartAsyncLoader::DartAsyncLoader(const String& url, PassRefPtr<DartAsyncLoader::LoadCallback> loadCallback) |
+ : m_loadCallback(loadCallback) |
+ , m_mainScriptFetched(false) |
+{ |
+ Document* document = m_loadCallback->document(); |
+ char* error; |
+ |
+ const KURL canonical = KURL(document->baseURL(), url); |
+ m_topUrl = canonical.string(); |
+ |
+ DartDOMData* domData = new DartDOMData(document, false); |
+ domData->setAsyncLoader(adoptRef(this)); |
+ |
+ const uint8_t* snapshot = DartUtilities::fullSnapshot(document->frame()); |
+ m_parseIsolate = Dart_CreateIsolate("dartium.fetch", "", snapshot, domData, &error); |
+ if (!m_parseIsolate) |
+ m_loadCallback->reportError(error, url); |
+ |
+ Dart_Handle result = Dart_SetLibraryTagHandler(&libraryTagHandlerCallback); |
+ ASSERT(!Dart_IsError(result)); |
+ UNUSED_PARAM(result); |
+ |
+ m_pendingLibraries.add(m_topUrl); |
+} |
+ |
+DartAsyncLoader::~DartAsyncLoader() |
+{ |
+ m_loadCallback = 0; |
+} |
+ |
+void DartAsyncLoader::findDependences(const String& url, const String& source, intptr_t lineNumber) |
+{ |
+ ASSERT(m_pendingLibraries.contains(url) || m_pendingSource.contains(url)); |
+ |
+ DartIsolateScope isolateScope(m_parseIsolate); |
+ DartApiScope dartApiScope; |
+ |
+ if (!m_mainScriptFetched) { |
+ ASSERT(m_topUrl == url); |
+ m_topSource = source; |
+ m_lineNumber = lineNumber; |
+ m_mainScriptFetched = true; |
+ } |
+ |
+ if (m_pendingLibraries.contains(url)) { |
+ processLibrary(url, source); |
+ m_pendingLibraries.remove(url); |
+ } else { |
+ ASSERT(m_pendingSource.contains(url)); |
+ // No imports or parts, so no need to process further. |
+ m_pendingSource.remove(url); |
+ } |
+} |
+ |
+void DartAsyncLoader::reportError(Dart_Handle error, const String& url) |
+{ |
+ reportError(Dart_GetError(error), url); |
+} |
+ |
+void DartAsyncLoader::processLibrary(const String& url, const String& source) |
+{ |
+ ASSERT(m_pendingLibraries.contains(url)); |
+ |
+ Dart_Handle result = Dart_LoadLibrary(DartUtilities::stringToDartString(url), DartUtilities::convertSourceString(source)); |
+ if (Dart_IsError(result)) |
+ reportError(result, url); |
+} |
+ |
+void DartAsyncLoader::process(const String& url, const String& source, intptr_t lineNumber) |
+{ |
+ ASSERT(!m_urlSourceMap.contains(url)); |
+ m_urlSourceMap.add(url, source); |
+ |
+ findDependences(url, source, lineNumber); |
+ if (ready()) { |
+ // All dependences are in. |
+ RefPtr<DartAsyncLoader> guard(this); |
+ m_loadCallback->ready(guard); |
+ |
+ // Free this loader and its isolate. |
+ Dart_EnterIsolate(m_parseIsolate); |
+ DartDOMData::current()->setAsyncLoader(0); |
+ Dart_ShutdownIsolate(); |
+ } |
+} |
+ |
+Dart_Handle DartAsyncLoader::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 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); |
+ } |
+ |
+ RefPtr<DartAsyncLoader> loader = DartDOMData::current()->asyncLoader(); |
+ ASSERT(loader); |
+ |
+ if (!url.startsWith("dart:")) { |
+ // Fetch non-system URLs. |
+ if (tag == Dart_kImportTag) { |
+ loader->m_pendingLibraries.add(url); |
+ } else if (tag == Dart_kSourceTag) { |
+ loader->m_pendingSource.add(url); |
+ } else { |
+ ASSERT_NOT_REACHED(); |
+ } |
+ loader->fetchScriptResource(url); |
+ } |
+ return Dart_NewBoolean(true); |
+} |
+ |
+class ScriptLoadedCallback : public ResourceClient { |
+public: |
+ ScriptLoadedCallback(String url, PassRefPtr<DartAsyncLoader> 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()); |
+ // FIXME: Should we check for cached metadata? |
+ // CachedMetadata* cachedMetadata = m_scriptResource->cachedMetadata(dartTypeID); |
+ |
+ // 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<DartAsyncLoader> m_loader; |
+ ResourcePtr<ScriptResource> m_scriptResource; |
+}; |
+ |
+static String resolveUrl(String mainLibraryURL, const String& url) |
+{ |
+ if (!url.startsWith("package:") || url.startsWith("package://")) |
+ return url; |
+ |
+ String packageRoot; |
+ String packageUrl; |
+ if (const char* packageRootOverride = getenv("DART_PACKAGE_ROOT")) { |
+ // Resolve with respect to the override. Append a |
+ // slash to ensure that resolution is against this |
+ // path and not its parent. |
+ packageRoot = String(packageRootOverride) + "/"; |
+ // Strip the 'package:' prefix. |
+ packageUrl = url.substring(8); |
+ } 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 DartAsyncLoader::fetchScriptResource(const String& url) |
+{ |
+ // Request loading of script dependencies. |
+ FetchRequest request(m_loadCallback->completeURL(resolveUrl(m_topUrl, url)), |
+ 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); |
+} |
+ |
+} |