Index: Source/bindings/core/dart/DartController.cpp |
diff --git a/Source/bindings/core/dart/DartController.cpp b/Source/bindings/core/dart/DartController.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2c1fe454780a28b4c22d59b683216388a2840f3c |
--- /dev/null |
+++ b/Source/bindings/core/dart/DartController.cpp |
@@ -0,0 +1,988 @@ |
+// 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/DartController.h" |
+ |
+#if OS(ANDROID) |
+#include <sys/system_properties.h> |
+#endif |
+ |
+ |
+#include "core/HTMLNames.h" |
+#include "bindings/core/dart/DartApplicationLoader.h" |
+#include "bindings/core/dart/DartDOMData.h" |
+#include "bindings/core/dart/DartDOMWrapper.h" |
+#include "bindings/core/dart/DartInspectorTimeline.h" |
+#include "bindings/core/dart/DartIsolateDestructionObserver.h" |
+#include "bindings/core/dart/DartJsInterop.h" |
+#include "bindings/core/dart/DartNativeUtilities.h" |
+#include "bindings/core/dart/DartScriptDebugServer.h" |
+#include "bindings/core/dart/DartScriptState.h" |
+#include "bindings/core/dart/DartService.h" |
+#include "bindings/core/dart/DartUtilities.h" |
+#include "bindings/core/dart/ThreadSafeDartIsolateWrapper.h" |
+#include "bindings/core/v8/ScriptController.h" |
+#include "bindings/core/v8/V8Binding.h" |
+#include "core/dom/Document.h" |
+#include "core/dom/ExecutionContext.h" |
+#include "core/dom/ExecutionContextTask.h" |
+#include "core/dom/MutationObserver.h" |
+#include "core/dom/NodeList.h" |
+#include "core/dom/ScriptLoader.h" |
+#include "core/frame/DOMTimer.h" |
+#include "core/frame/LocalDOMWindow.h" |
+#include "core/frame/LocalFrame.h" |
+#include "core/frame/Settings.h" |
+#include "core/html/HTMLDocument.h" |
+#include "core/html/HTMLLinkElement.h" |
+#include "core/html/HTMLScriptElement.h" |
+#include "core/page/Page.h" |
+#include "core/storage/StorageNamespace.h" |
+#include "core/svg/SVGScriptElement.h" |
+#include "modules/indexeddb/IDBPendingTransactionMonitor.h" |
+#include "public/platform/Platform.h" |
+ |
+#include <ctype.h> |
+ |
+#include <dart_api.h> |
+#include <dart_tools_api.h> |
+ |
+namespace blink { |
+ |
+static void copyValue(Dart_Handle source, const char* fieldName, |
+ Dart_Handle targetLibrary, const char* targetClass, const char* targetField) |
+{ |
+ Dart_Handle value = Dart_GetField(source, Dart_NewStringFromCString(fieldName)); |
+ ASSERT(!Dart_IsError(value)); |
+ |
+ Dart_Handle target = targetClass ? Dart_GetType(targetLibrary, Dart_NewStringFromCString(targetClass), 0, 0) : targetLibrary; |
+ ASSERT(!Dart_IsError(target)); |
+ |
+ Dart_SetField(target, Dart_NewStringFromCString(targetField), value); |
+} |
+ |
+static void messageNotifyCallback(Dart_Isolate); |
+ |
+extern Dart_NativeFunction blinkSnapshotResolver(Dart_Handle name, int argumentCount, bool* autoSetupScope); |
+ |
+static void throwDomDisabled(Dart_NativeArguments) |
+{ |
+ DartApiScope apiScope; |
+ Dart_ThrowException(Dart_NewStringFromCString("DOM access is not enabled in this isolate")); |
+} |
+ |
+Dart_NativeFunction pureIsolateResolver(Dart_Handle name, int argumentCount, bool* autoSetupScope) |
+{ |
+ if (Dart_NativeFunction function = commonHtmlResolver(name, argumentCount, autoSetupScope)) |
+ return function; |
+ return throwDomDisabled; |
+} |
+ |
+const uint8_t* pureIsolateSymbolizer(Dart_NativeFunction nf) |
+{ |
+ return 0; |
+} |
+ |
+Dart_Isolate DartController::createIsolate(const char* scriptURL, const char* entryPoint, const char* packageRoot, Dart_IsolateFlags* flags, |
+ Document* document, bool isDOMEnabled, bool isDebuggerEnabled, char** errorMessage) |
+{ |
+ DART_START_TIMER(); |
+ const uint8_t* snapshot = DartUtilities::isolateSnapshot(); |
+ DartDOMData* domData = new DartDOMData(document, scriptURL, packageRoot, isDOMEnabled); |
+ Dart_Isolate isolate = Dart_CreateIsolate(scriptURL, entryPoint, snapshot, flags, domData, errorMessage); |
+ if (!isolate) { |
+ delete domData; |
+ return 0; |
+ } |
+ DART_RECORD_TIMER(" createIsolate after Dart_CreateIsolate call"); |
+ |
+ DartApiScope apiScope; |
+ |
+ domData->setThreadSafeIsolateWrapper(ThreadSafeDartIsolateWrapper::create()); |
+ |
+ Dart_Handle blink = Dart_LookupLibrary(Dart_NewStringFromCString("dart:_blink")); |
+ ASSERT(!Dart_IsError(blink)); |
+ // FIXME: this should be blinkSnapshotResolver |
+ Dart_SetNativeResolver(blink, isDOMEnabled ? domIsolateHtmlResolver : pureIsolateResolver, isDOMEnabled ? domIsolateHtmlSymbolizer : pureIsolateSymbolizer); |
+ domData->setBlinkLibrary(Dart_NewPersistentHandle(blink)); |
+ |
+ // Fix the html library. |
+ Dart_Handle html = Dart_LookupLibrary(Dart_NewStringFromCString("dart:html")); |
+ ASSERT(!Dart_IsError(html)); |
+ Dart_SetNativeResolver(html, isDOMEnabled ? domIsolateHtmlResolver : pureIsolateResolver, isDOMEnabled ? domIsolateHtmlSymbolizer : pureIsolateSymbolizer); |
+ domData->setHtmlLibrary(Dart_NewPersistentHandle(html)); |
+ |
+ Dart_Handle js = Dart_LookupLibrary(Dart_NewStringFromCString("dart:js")); |
+ ASSERT(!Dart_IsError(js)); |
+ Dart_SetNativeResolver(js, isDOMEnabled ? JsInterop::resolver : pureIsolateResolver, 0); |
+ domData->setJsLibrary(Dart_NewPersistentHandle(js)); |
+ |
+ Dart_Handle core = Dart_LookupLibrary(Dart_NewStringFromCString("dart:core")); |
+ ASSERT(!Dart_IsError(core)); |
+ |
+ domData->setSvgLibrary(0); |
+ |
+ Dart_Handle functionType = Dart_GetType(core, Dart_NewStringFromCString("Function"), 0, 0); |
+ ASSERT(!Dart_IsError(functionType)); |
+ domData->setFunctionType(Dart_NewPersistentHandle(functionType)); |
+ |
+ domData->setCurrentException(Dart_NewPersistentHandle(Dart_Null())); |
+ |
+ // Setup configuration closures |
+ char forwardingProp[DartUtilities::PROP_VALUE_MAX_LEN]; |
+ int propLen = DartUtilities::getProp("DART_FORWARDING_PRINT", |
+ forwardingProp, DartUtilities::PROP_VALUE_MAX_LEN); |
+ bool forwardPrint = propLen > 0; |
+ const char * const printClosure = forwardPrint ? |
+ "_forwardingPrintClosure" : |
+ (isDOMEnabled ? "_printClosure" : "_pureIsolatePrintClosure"); |
+ Dart_Handle asyncLib = Dart_LookupLibrary(Dart_NewStringFromCString("dart:async")); |
+ ASSERT(!Dart_IsError(asyncLib)); |
+ Dart_Handle internalLib = Dart_LookupLibrary(Dart_NewStringFromCString("dart:_internal")); |
+ ASSERT(!Dart_IsError(internalLib)); |
+ copyValue(html, printClosure, internalLib, 0, "_printClosure"); |
+ copyValue(html, isDOMEnabled ? "_timerFactoryClosure" : "_pureIsolateTimerFactoryClosure", asyncLib, "_TimerFactory", "_factory"); |
+ if (isDOMEnabled) { |
+ copyValue(html, "_scheduleImmediateClosure", asyncLib, "_ScheduleImmediate", "_closure"); |
+ } else { |
+ // Use the VM implementation (from dart:isolate) for scheduleImmediate. |
+ Dart_Handle isolateLibrary = Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate")); |
+ ASSERT(!Dart_IsError(isolateLibrary)); |
+ |
+ Dart_Handle value = Dart_Invoke(isolateLibrary, Dart_NewStringFromCString("_getIsolateScheduleImmediateClosure"), 0, 0); |
+ ASSERT(!Dart_IsError(value)); |
+ |
+ Dart_Handle target = Dart_GetType(asyncLib, Dart_NewStringFromCString("_ScheduleImmediate"), 0, 0); |
+ ASSERT(!Dart_IsError(target)); |
+ |
+ Dart_SetField(target, Dart_NewStringFromCString("_closure"), value); |
+ } |
+ copyValue(html, isDOMEnabled ? "_uriBaseClosure" : "_pureIsolateUriBaseClosure", core, 0, "_uriBaseClosure"); |
+ |
+ if (isDOMEnabled) { |
+ Dart_SetMessageNotifyCallback(&messageNotifyCallback); |
+ |
+ if (isDebuggerEnabled) { |
+ DART_RECORD_TIMER(" createIsolate before debug setup"); |
+ DartScriptDebugServer::shared().registerIsolate(isolate, document->page()); |
+ DART_RECORD_TIMER(" createIsolate after debug setup"); |
+ } |
+ } |
+ DART_RECORD_TIMER(" createIsolate done %.3f ms"); |
+ |
+ return isolate; |
+} |
+ |
+Dart_Isolate DartController::createDOMEnabledIsolate(const String& scriptURL, const String& entryPoint, const char* packageRoot, Document* document) |
+{ |
+ DART_START_TIMER(); |
+ ASSERT(!Dart_CurrentIsolate()); |
+ |
+ // FIXME: proper error reporting. |
+ char* errorMessage = 0; |
+ Dart_Isolate newIsolate = createIsolate(scriptURL.utf8().data(), entryPoint.utf8().data(), packageRoot, 0, document, true, true, &errorMessage); |
+ ASSERT(newIsolate); |
+ m_isolates.append(newIsolate); |
+ DART_RECORD_TIMER(" createDOMEnabledIsolate took"); |
+ return newIsolate; |
+} |
+ |
+/// Spawn a special DOM isolate to handle print and timer requests. Should be |
+/// unnecessary if we have isolates as workers. |
+void DartController::spawnHelperDomIsolate(const String& libraryUrl, const String& entryPoint, DartDOMData* parentDOMData, Dart_Port replyTo) |
+{ |
+ ASSERT(isMainThread()); |
+ createDOMEnabledIsolate(libraryUrl, entryPoint, parentDOMData->packageRoot(), m_frame->document()); |
+ |
+ { |
+ DartApiScope apiScope; |
+ Dart_Handle dummyUrl = DartUtilities::stringToDartString(*new String("dummy")); |
+ Dart_Handle dummySource = DartUtilities::stringToDartString( |
+ *new String("library dummy; main() {}")); |
+ Dart_Handle loadResult = Dart_LoadScript(dummyUrl, dummySource, 0, 0); |
+ if (Dart_IsError(loadResult)) { |
+ DartUtilities::reportProblem(m_frame->document(), loadResult); |
+ return; |
+ } |
+ |
+ Dart_Handle library = Dart_LookupLibrary(DartUtilities::stringToDartString(libraryUrl)); |
+ if (Dart_IsError(library)) { |
+ DartUtilities::reportProblem(m_frame->document(), library); |
+ return; |
+ } |
+ |
+ V8Scope v8scope(DartDOMData::current()); |
+ Dart_Handle sendPort = Dart_NewSendPort(replyTo); |
+ if (Dart_IsError(sendPort)) { |
+ DartUtilities::reportProblem(m_frame->document(), sendPort); |
+ return; |
+ } |
+ Dart_Handle result = Dart_Invoke(library, DartUtilities::stringToDartString(entryPoint), 1, &sendPort); |
+ if (Dart_IsError(result)) { |
+ DartUtilities::reportProblem(m_frame->document(), result); |
+ return; |
+ } |
+ } |
+ Dart_ExitIsolate(); |
+} |
+ |
+class SendIsolatePortCallback : public DartApplicationLoader::Callback { |
+public: |
+ explicit SendIsolatePortCallback(Dart_Port replyTo, Document* originDocument, Dart_Isolate isolate) |
+ : Callback(originDocument) |
+ , m_replyTo(replyTo) |
+ , m_isolate(isolate) { } |
+ |
+ void ready() |
+ { |
+ DartIsolateScope scope(m_isolate); |
+ DartApiScope apiScope; |
+ Dart_Handle sendPort = Dart_NewSendPort(Dart_GetMainPortId()); |
+ if (!Dart_IsError(sendPort)) |
+ Dart_Post(m_replyTo, sendPort); |
+ } |
+ |
+private: |
+ Dart_Port m_replyTo; |
+ Dart_Isolate m_isolate; |
+}; |
+ |
+class SpawnUriErrorEventDispatcher : public DartErrorEventDispatcher { |
+public: |
+ // TODO(antonm): this is used to dispatch DOM error event. Most probably we need |
+ // nothing like that for spawnDomUri, but need to double check. |
+ void dispatchErrorEvent() { } |
+}; |
+ |
+void DartController::shutdownIsolate(Dart_Isolate isolate) |
+{ |
+ DartDOMData* domData = DartDOMData::current(); |
+ ASSERT(domData->isDOMEnabled()); |
+ // If the following assert triggers, we have hit dartbug.com/14183 |
+ // FIXME: keep the isolate alive until the recursion level is 0. |
+ ASSERT(!*(domData->recursion())); |
+ DartScriptDebugServer::shared().unregisterIsolate(isolate, m_frame->page()); |
+ DartIsolateDestructionObservers* observers = domData->isolateDestructionObservers(); |
+ for (DartIsolateDestructionObservers::iterator it = observers->begin(); it != observers->end(); ++it) |
+ (*it)->isolateDestroyed(); |
+ Dart_ShutdownIsolate(); |
+ delete domData; |
+} |
+ |
+DartController::DartController(LocalFrame* frame) |
+ : m_frame(frame) |
+ , m_npObjectMap() |
+{ |
+ // The DartController's constructor must be called in the LocalFrame's |
+ // constructor, so it can properly maintain the unit of related |
+ // browsing contexts. |
+} |
+ |
+DartController::~DartController() |
+{ |
+ clearWindowShell(); |
+} |
+ |
+void DartController::clearWindowShell() |
+{ |
+ DART_START_TIMER(); |
+ initVMIfNeeded(); |
+ DART_RECORD_TIMER("clearWindowShell::initVM took"); |
+ m_loaders.clear(); |
+ m_usedNames.clear(); |
+ m_isolateNames.clear(); |
+ m_mainLoader.clear(); |
+ |
+ // Due to synchronous dispatch, we may be in an isolate corresponding to another frame. |
+ // If so, exit here but re-enter before returning. |
+ Dart_Isolate currentIsolate = Dart_CurrentIsolate(); |
+ if (currentIsolate) |
+ Dart_ExitIsolate(); |
+ |
+ Vector<Dart_Isolate>::iterator iterator; |
+ for (iterator = m_isolates.begin(); iterator != m_isolates.end(); ++iterator) { |
+ Dart_Isolate isolate = *iterator; |
+ Dart_EnterIsolate(isolate); |
+ shutdownIsolate(isolate); |
+ } |
+ m_isolates.clear(); |
+ |
+ DartScriptDebugServer::shared().clearWindowShell(m_frame->page()); |
+ |
+ for (ScriptStatesMap::iterator it = m_scriptStates.begin(); it != m_scriptStates.end(); ++it) { |
+ LibraryIdMap* libraryIdMap = it->value; |
+ delete libraryIdMap; |
+ } |
+ m_scriptStates.clear(); |
+ |
+ // Restore previous isolate. |
+ if (currentIsolate) |
+ Dart_EnterIsolate(currentIsolate); |
+} |
+ |
+void DartController::clearScriptObjects() |
+{ |
+ // FIXME(dartbug.com/18427): Clear plugin / NP objects. |
+} |
+ |
+class MessageNotifyTask : public ExecutionContextTask { |
+public: |
+ explicit MessageNotifyTask(PassRefPtr<ThreadSafeDartIsolateWrapper> destinationIsolate) |
+ : m_destinationIsolate(destinationIsolate) |
+ { } |
+ |
+ virtual void performTask(ExecutionContext* context) |
+ { |
+ if (!m_destinationIsolate->isIsolateAlive()) |
+ return; |
+ |
+ DartIsolateScope scope(m_destinationIsolate->isolate()); |
+ DartApiScope apiScope; |
+ |
+ DartDOMData* domData = DartDOMData::current(); |
+ |
+ V8Scope v8scope(domData); |
+ Dart_Handle result = Dart_HandleMessage(); |
+ if (Dart_IsError(result)) |
+ DartUtilities::reportProblem(context, result); |
+ } |
+ |
+private: |
+ RefPtr<ThreadSafeDartIsolateWrapper> m_destinationIsolate; |
+}; |
+ |
+static void messageNotifyCallback(Dart_Isolate destinationIsolate) |
+{ |
+ DartDOMData* domData = static_cast<DartDOMData*>(Dart_IsolateData(destinationIsolate)); |
+ ASSERT(domData->isDOMEnabled()); |
+ ExecutionContext* destinationContext = domData->scriptExecutionContext(); |
+ destinationContext->postTask(adoptPtr(new MessageNotifyTask(domData->threadSafeIsolateWrapper()))); |
+} |
+ |
+class DartSpawnUriCallback : public DartApplicationLoader::Callback { |
+public: |
+ DartSpawnUriCallback(Dart_Isolate isolate, PassRefPtr<DartApplicationLoader> loader, const String& url, Document* originDocument) |
+ : Callback(originDocument) |
+ , m_isolate(isolate) |
+ , m_loader(loader) |
+ , m_url(url) |
+ { |
+ } |
+ |
+ ~DartSpawnUriCallback() { } |
+ |
+ virtual void initialize() = 0; |
+ virtual bool domEnabled() = 0; |
+ |
+ void ready() |
+ { |
+ RefPtr<SpawnUriErrorEventDispatcher> errorEventDispatcher = adoptRef(new SpawnUriErrorEventDispatcher()); |
+ |
+ m_loader->load(errorEventDispatcher); |
+ initialize(); |
+ } |
+ |
+protected: |
+ Dart_Isolate m_isolate; |
+ RefPtr<DartApplicationLoader> m_loader; |
+ String m_url; |
+}; |
+ |
+class DartSpawnBackgroundUriCallback : public DartSpawnUriCallback { |
+public: |
+ DartSpawnBackgroundUriCallback(Dart_Isolate isolate, PassRefPtr<DartApplicationLoader> loader, const String& url, Document* originDocument) |
+ : DartSpawnUriCallback(isolate, loader, url, originDocument) |
+ { |
+ } |
+ |
+ void initialize() |
+ { |
+ // The VM initiates background isolates. |
+ Dart_IsolateMakeRunnable(m_isolate); |
+ } |
+ |
+ bool domEnabled() { return false; } |
+}; |
+ |
+class DartSpawnDomUriCallback : public DartSpawnUriCallback { |
+public: |
+ DartSpawnDomUriCallback(Dart_Isolate isolate, PassRefPtr<DartApplicationLoader> loader, const String& url, Document* originDocument) |
+ : DartSpawnUriCallback(isolate, loader, url, originDocument) |
+ { |
+ } |
+ |
+ void initialize() |
+ { |
+ // The browser initiates DOM isolates directly. |
+ } |
+ |
+ bool domEnabled() { return true; } |
+}; |
+ |
+ |
+Dart_Isolate DartController::createPureIsolateCallback(const char* scriptURL, const char* entryPoint, const char* packageRoot, Dart_IsolateFlags* flags, void* data, char** errorMsg) |
+{ |
+ bool isSpawnUri = scriptURL ? true : false; |
+ |
+ if (isSpawnUri && strcmp(scriptURL, DART_VM_SERVICE_ISOLATE_NAME) == 0) { |
+ return DartService::CreateIsolate(); |
+ } |
+ |
+ if (isSpawnUri && !WTF::isMainThread()) { |
+ // FIXME(14463): We need to forward this request to the main thread to fetch the URI. |
+ *errorMsg = strdup("spawnUri is not yet supported on background isolates."); |
+ return 0; |
+ } |
+ |
+ DartDOMData* parentDOMData = static_cast<DartDOMData*>(data); |
+ ExecutionContext* context = parentDOMData->scriptExecutionContext(); |
+ |
+ if (parentDOMData->isDOMEnabled() && !isSpawnUri) { |
+ // spawnFunction is not allowed from a DOM enabled isolate. |
+ // This triggers an exception in the caller. |
+ *errorMsg = strdup("spawnFunction is not supported from a dom-enabled isolate. Please use spawnUri instead."); |
+ return 0; |
+ } |
+ if (!isSpawnUri) { |
+ scriptURL = parentDOMData->scriptURL(); |
+ } |
+ if (!packageRoot) { |
+ packageRoot = parentDOMData->packageRoot(); |
+ } |
+ |
+ ASSERT(context->isDocument()); |
+ Document* document = static_cast<Document*>(context); |
+ |
+ Dart_Isolate isolate = createIsolate(scriptURL, entryPoint, packageRoot, flags, document, false, true, errorMsg); |
+ |
+ if (!isolate) { |
+ // This triggers an exception in the caller. |
+ *errorMsg = strdup("Isolate spawn failed."); |
+ return 0; |
+ } |
+ |
+ // FIXME: If a spawnFunction, we should not need to request resources again. But, it's not clear |
+ // we need this callback in the first place for spawnFunction. |
+ |
+ // We need to request the sources asynchronously. |
+ RefPtr<DartApplicationLoader> loader = DartApplicationLoader::create(document, false); |
+ RefPtr<DartSpawnUriCallback> callback = adoptRef(new DartSpawnBackgroundUriCallback(isolate, loader, scriptURL, document)); |
+ Dart_ExitIsolate(); |
+ loader->processSingleRequest(isolate, scriptURL, callback); |
+ |
+ return isolate; |
+} |
+ |
+static char* skipWhiteSpace(char* p) |
+{ |
+ for (; *p != '\0' && isspace(*p); p++) { } |
+ return p; |
+} |
+ |
+static char* skipBlackSpace(char* p) |
+{ |
+ for (; *p != '\0' && !isspace(*p); p++) { } |
+ return p; |
+} |
+ |
+static void setDartFlags(const char* str) |
+{ |
+ if (!str) { |
+ Dart_SetVMFlags(0, 0); |
+ return; |
+ } |
+ |
+ size_t length = strlen(str); |
+ char* copy = new char[length + 1]; |
+ memmove(copy, str, length); |
+ copy[length] = '\0'; |
+ |
+ // Strip leading white space. |
+ char* start = skipWhiteSpace(copy); |
+ |
+ // Count the number of 'arguments'. |
+ int argc = 0; |
+ for (char* p = start; *p != '\0'; argc++) { |
+ p = skipBlackSpace(p); |
+ p = skipWhiteSpace(p); |
+ } |
+ |
+ // Allocate argument array. |
+ const char** argv = new const char*[argc]; |
+ |
+ // Split the flags string into arguments. |
+ argc = 0; |
+ for (char* p = start; *p != '\0'; argc++) { |
+ argv[argc] = p; |
+ p = skipBlackSpace(p); |
+ if (*p != '\0') |
+ *p++ = '\0'; // 0-terminate argument |
+ p = skipWhiteSpace(p); |
+ } |
+ |
+ // Set the flags. |
+ Dart_SetVMFlags(argc, argv); |
+ |
+ delete[] argv; |
+ delete[] copy; |
+} |
+ |
+namespace { |
+ |
+#if OS(LINUX) |
+ |
+static void* openFileCallback(const char* name, bool write) |
+{ |
+ return fopen(name, write ? "w" : "r"); |
+} |
+ |
+static void readFileCallback(const uint8_t** data, intptr_t* fileLength, void* stream) |
+{ |
+ if (!stream) { |
+ *data = 0; |
+ *fileLength = 0; |
+ } else { |
+ FILE* file = reinterpret_cast<FILE*>(stream); |
+ |
+ // Get the file size. |
+ fseek(file, 0, SEEK_END); |
+ *fileLength = ftell(file); |
+ rewind(file); |
+ |
+ // Allocate data buffer. |
+ *data = new uint8_t[*fileLength]; |
+ *fileLength = fread(const_cast<uint8_t*>(*data), 1, *fileLength, file); |
+ } |
+} |
+ |
+static void writeFileCallback(const void* data, intptr_t length, void* file) |
+{ |
+ fwrite(data, 1, length, reinterpret_cast<FILE*>(file)); |
+} |
+ |
+static void closeFileCallback(void* file) |
+{ |
+ fclose(reinterpret_cast<FILE*>(file)); |
+} |
+ |
+#else |
+ |
+static Dart_FileOpenCallback openFileCallback = 0; |
+static Dart_FileReadCallback readFileCallback = 0; |
+static Dart_FileWriteCallback writeFileCallback = 0; |
+static Dart_FileCloseCallback closeFileCallback = 0; |
+ |
+#endif // OS(LINUX) |
+ |
+} |
+ |
+static bool generateEntropy(uint8_t* buffer, intptr_t length) |
+{ |
+ if (blink::Platform::current()) { |
+ blink::Platform::current()->cryptographicallyRandomValues(buffer, length); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void DartController::initVMIfNeeded() |
+{ |
+ static bool hasBeenInitialized = false; |
+ if (hasBeenInitialized) |
+ return; |
+ |
+ char flagsProp[DartUtilities::PROP_VALUE_MAX_LEN]; |
+ int propLen = DartUtilities::getProp( |
+ "DART_FLAGS", flagsProp, DartUtilities::PROP_VALUE_MAX_LEN); |
+ if (propLen > 0) { |
+ setDartFlags(flagsProp); |
+ } else { |
+ setDartFlags(0); |
+ } |
+ |
+ DartService::Bootstrap(); |
+ // FIXME(antonm): implement proper unhandled exception callback. |
+ Dart_Initialize(DartUtilities::vmIsolateSnapshot(), 0, &createPureIsolateCallback, 0, 0, 0, openFileCallback, readFileCallback, writeFileCallback, closeFileCallback, generateEntropy); |
+ hasBeenInitialized = true; |
+} |
+ |
+static bool checkForExpiration() |
+{ |
+ const time_t ExpirationTimeSecsSinceEpoch = |
+#include "bindings/dart/ExpirationTimeSecsSinceEpoch.time_t" |
+ ; |
+ const char* override = getenv("DARTIUM_EXPIRATION_TIME"); |
+ time_t expiration; |
+ if (override) { |
+ expiration = static_cast<time_t>(String(override).toInt64()); |
+ } else { |
+ expiration = ExpirationTimeSecsSinceEpoch; |
+ } |
+ const time_t now = time(0); |
+ double diff = difftime(now, expiration); |
+ if (diff > 0) { |
+ fprintf(stderr, "[dartToStderr]: Dartium build has expired\n"); |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+class DartDomLoadCallback : public DartApplicationLoader::Callback { |
+public: |
+ DartDomLoadCallback(DartController* controller, const String& url, Dart_Isolate domIsolate, Document* originDocument, PassRefPtr<DartScriptInfo> info) |
+ : Callback(originDocument, info) |
+ , m_controller(controller) |
+ , m_url(url) |
+ , m_isolate(domIsolate) |
+ { |
+ } |
+ |
+ void ready() |
+ { |
+ m_controller->scheduleScriptExecution(m_url, m_isolate, scriptInfo()); |
+ } |
+ |
+private: |
+ DartController* m_controller; |
+ String m_url; |
+ Dart_Isolate m_isolate; |
+}; |
+ |
+class DartScriptRunner : public EventListener { |
+public: |
+ static PassRefPtr<DartScriptRunner> create(const String& url, Dart_Isolate isolate, PassRefPtr<DartScriptInfo> info) |
+ { |
+ return adoptRef(new DartScriptRunner(url, isolate, info)); |
+ } |
+ |
+ virtual void handleEvent(ExecutionContext* context, Event*) |
+ { |
+ ASSERT(context->isDocument()); |
+ Document* document = static_cast<Document*>(context); |
+ |
+ // this gets removed below, so protect it while handler runs. |
+ RefPtr<DartScriptRunner> protect(this); |
+ document->domWindow()->removeEventListener(AtomicString("DOMContentLoaded"), this, false); |
+ |
+ DartController::retrieve(context)->loadAndRunScript(m_url, m_isolate, m_info); |
+ } |
+ |
+ virtual bool operator==(const EventListener& other) |
+ { |
+ return this == &other; |
+ } |
+ |
+private: |
+ DartScriptRunner(const String& url, Dart_Isolate isolate, PassRefPtr<DartScriptInfo> info) |
+ : EventListener(EventListener::NativeEventListenerType) |
+ , m_url(url) |
+ , m_isolate(isolate) |
+ , m_info(info) |
+ { |
+ } |
+ |
+ String m_url; |
+ Dart_Isolate m_isolate; |
+ RefPtr<DartScriptInfo> m_info; |
+}; |
+ |
+void DartController::scheduleScriptExecution(const String& url, Dart_Isolate isolate, PassRefPtr<DartScriptInfo> info) |
+{ |
+ Document* document = frame()->document(); |
+ if (document->readyState() == "loading") |
+ document->domWindow()->addEventListener(AtomicString("DOMContentLoaded"), DartScriptRunner::create(url, isolate, info), false); |
+ else |
+ loadAndRunScript(url, isolate, info); |
+} |
+ |
+void DartController::loadAndRunScript(const String& url, Dart_Isolate isolate, PassRefPtr<DartScriptInfo> info) |
+{ |
+ DART_START_TIMER(); |
+ RefPtr<DartScriptInfo> scriptInfo = info; |
+ Document* ALLOW_UNUSED document = frame()->document(); |
+ |
+ // Invoke only if this is the main document. |
+ ASSERT(scriptInfo->ownerDocument() == document); |
+ RefPtr<DartApplicationLoader> loader = m_loaders.get(isolate); |
+ ASSERT(loader); |
+ // Due to deferred loading we might already be running Dart code on |
+ // an isolate and the library tag handler callback could result in a call |
+ // to this code, we skip the Enter/Exit Isolate calls in that case. |
+ if (isolate == Dart_CurrentIsolate()) { |
+ loader->load(scriptInfo); |
+ } else { |
+ DartIsolateScope scope(isolate); |
+ loader->load(scriptInfo); |
+ } |
+ |
+ DART_RECORD_TIMER("DartController::loadAndRunScript took"); |
+} |
+ |
+void DartController::evaluate(const ScriptSourceCode& sourceCode, ScriptLoader* loader) |
+{ |
+ Document* document = frame()->document(); |
+ |
+ if (checkForExpiration()) { |
+ // Log to console. |
+ DartUtilities::reportProblem(document, ExpirationMessage); |
+ // Bring up alert message that Dartium has expired. |
+ document->domWindow()->alert(ExpirationMessage); |
+ return; |
+ } |
+ |
+ DART_START_TIMER(); |
+ initVMIfNeeded(); |
+ DART_RECORD_TIMER("evaluate::initVM took"); |
+ RefPtr<Element> element(loader->element()); |
+ |
+ RefPtr<DartScriptInfo> scriptInfo = DartScriptInfo::create(element); |
+ if (!scriptInfo) { |
+ DartUtilities::reportProblem(document, "Dart script must be in HTML or SVG document."); |
+ ASSERT_NOT_REACHED(); |
+ return; |
+ } |
+ |
+ RefPtr<DartApplicationLoader> currentLoader; |
+ |
+ // HTML Import case. |
+ if (m_mainLoader && document != scriptInfo->ownerDocument() && !m_mainLoader->uninitialized()) { |
+ int line = scriptInfo->startLineNumber().zeroBasedInt(); |
+ DartUtilities::reportProblem(document, "Inline Darts scripts not supported after main script is invoked.", line); |
+ return; |
+ } |
+ if (!m_mainLoader || (!m_mainLoader->uninitialized() && document == scriptInfo->ownerDocument())) { |
+ // FIXME: perhaps if loader->validateUrlLoaded(scriptURL) has been |
+ // called we are actually good to use the existing loader? |
+ currentLoader = DartApplicationLoader::create(document, true); |
+ if (!m_mainLoader) { |
+ m_mainLoader = currentLoader; |
+ } |
+ } else { |
+ currentLoader = m_mainLoader; |
+ } |
+ |
+ DART_RECORD_TIMER("evaluate::prep for loading took"); |
+ if (document == scriptInfo->ownerDocument()) { |
+ String url = scriptInfo->url(); |
+ DART_RECORD_TIMER("evaluate before createIsolate"); |
+ DEFINE_STATIC_LOCAL(const char*, packageRoot, (getenv("DART_PACKAGE_ROOT"))); |
+ Dart_Isolate isolate = createDOMEnabledIsolate(url, "main", packageRoot, document); |
+ m_loaders.add(isolate, currentLoader); |
+ DART_RECORD_TIMER("evaluate after createIsolate"); |
+ RefPtr<DartDomLoadCallback> callback = adoptRef(new DartDomLoadCallback(this, url, isolate, document, scriptInfo)); |
+ Dart_ExitIsolate(); |
+ currentLoader->processRequests(isolate, sourceCode, callback); |
+ DART_RECORD_TIMER("evaluate process request took"); |
+ } else { |
+ // Add all HTML import scripts into the first loader. |
+ m_mainLoader->addRequest(scriptInfo); |
+ } |
+ DART_RECORD_TIMER("evaluate took"); |
+} |
+ |
+void DartController::bindToWindowObject(LocalFrame* frame, const String& key, NPObject* object) |
+{ |
+ // FIXME: proper management of lifetime. |
+ m_npObjectMap.set(key, object); |
+} |
+ |
+NPObject* DartController::npObject(const String& key) |
+{ |
+ return m_npObjectMap.get(key); |
+} |
+ |
+Dart_Handle DartController::callFunction(Dart_Handle function, int argc, Dart_Handle* argv) |
+{ |
+ V8Scope v8scope(DartDOMData::current()); |
+ |
+ // FIXME: Introduce Dart variant of V8GCController::checkMemoryUsage(); |
+ |
+ if (V8RecursionScope::recursionLevel(v8::Isolate::GetCurrent()) >= kMaxRecursionDepth) |
+ return Dart_NewApiError("Maximum call stack size exceeded"); |
+ |
+ // FIXME: implement InspectorInstrumentationCookie stuff a la v8. |
+ Dart_Handle result = Dart_InvokeClosure(function, argc, argv); |
+ |
+ // Handle fatal error in Dart VM a la v8. |
+ |
+ return result; |
+} |
+ |
+DartController* DartController::retrieve(LocalFrame* frame) |
+{ |
+ if (!frame) |
+ return 0; |
+ return &frame->dart(); |
+} |
+ |
+DartController* DartController::retrieve(ExecutionContext* context) |
+{ |
+ if (!context || !context->isDocument()) |
+ return 0; |
+ return retrieve(static_cast<Document*>(context)->frame()); |
+} |
+ |
+void DartController::collectScriptStates(V8ScriptState* v8ScriptState, Vector<DartScriptState*>& result) |
+{ |
+ if (m_isolates.isEmpty()) |
+ return; |
+ |
+ v8::HandleScope handleScope(v8::Isolate::GetCurrent()); |
+ v8::Handle<v8::Context> v8Context = v8ScriptState->context(); |
+ |
+ Vector<Dart_Isolate>::iterator iterator; |
+ for (iterator = m_isolates.begin(); iterator != m_isolates.end(); ++iterator) { |
+ Dart_Isolate isolate = *iterator; |
+ collectScriptStatesForIsolate(isolate, v8Context, result); |
+ } |
+} |
+ |
+LibraryIdMap* DartController::libraryIdMapForIsolate(Dart_Isolate isolate) |
+{ |
+ LibraryIdMap* libraryIdMap; |
+ ScriptStatesMap::iterator it = m_scriptStates.find(isolate); |
+ if (it == m_scriptStates.end()) { |
+ libraryIdMap = new LibraryIdMap(); |
+ m_scriptStates.set(isolate, libraryIdMap); |
+ } else { |
+ libraryIdMap = it->value; |
+ } |
+ return libraryIdMap; |
+} |
+ |
+DartScriptState* DartController::lookupScriptState(Dart_Isolate isolate, v8::Handle<v8::Context> v8Context, intptr_t libraryId) |
+{ |
+ return lookupScriptStateFromLibraryIdMap(isolate, v8Context, libraryIdMapForIsolate(isolate), libraryId); |
+} |
+ |
+DartScriptState* DartController::lookupScriptStateFromLibraryIdMap(Dart_Isolate isolate, v8::Handle<v8::Context> v8Context, LibraryIdMap* libraryIdMap, intptr_t libraryId) |
+{ |
+ // -1 cannot be used as a HashMap key however library ids are |
+ // guaranteed to be non-negative so it is a non-issue. |
+ ASSERT(libraryId >= 0); |
+ // 0 cannot be used as a HashMap key so we add 1 to the library id to |
+ // create a valid key. |
+ intptr_t libraryIdKey = libraryId + 1; |
+ LibraryIdMap::iterator libraryIter = libraryIdMap->find(libraryIdKey); |
+ DartScriptState* scriptState; |
+ if (libraryIter == libraryIdMap->end()) { |
+ V8ScriptState* v8ScriptState = V8ScriptState::from(v8Context); |
+ RefPtr<DartScriptState> scriptStatePtr = DartScriptState::create(isolate, libraryId, v8ScriptState, isolateName(isolate)); |
+ libraryIdMap->set(libraryIdKey, scriptStatePtr); |
+ scriptState = scriptStatePtr.get(); |
+ } else { |
+ scriptState = libraryIter->value.get(); |
+ ASSERT(scriptState); |
+ } |
+ return scriptState; |
+} |
+ |
+String DartController::isolateName(Dart_Isolate isolate) |
+{ |
+ if (m_isolateNames.contains(isolate)) { |
+ return m_isolateNames.get(isolate); |
+ } |
+ String fileName(""); |
+ String name; |
+ if (m_loaders.contains(isolate)) { |
+ const KURL& scriptUrl = m_loaders.get(isolate)->scriptUrl(); |
+ if (scriptUrl.isValid()) { |
+ String lastPathComponent = scriptUrl.lastPathComponent(); |
+ if (!lastPathComponent.isEmpty()) { |
+ fileName = scriptUrl.lastPathComponent(); |
+ } |
+ } |
+ } |
+ name = fileName; |
+ int i = 2; |
+ while (m_usedNames.contains(name)) { |
+ name = String::format("%s[%d]", fileName.utf8().data(), i); |
+ i++; |
+ } |
+ m_isolateNames.add(isolate, name); |
+ m_usedNames.add(name); |
+ return name; |
+} |
+ |
+void DartController::collectScriptStatesForIsolate(Dart_Isolate isolate, v8::Handle<v8::Context> v8Context, Vector<DartScriptState*>& result) |
+{ |
+ if (!isolate) |
+ return; |
+ DartIsolateScope scope(isolate); |
+ DartApiScope apiScope; |
+ LibraryIdMap* libraryIdMap = libraryIdMapForIsolate(isolate); |
+ Dart_Handle libraryIdList = Dart_GetLibraryIds(); |
+ |
+ intptr_t length = 0; |
+ Dart_Handle ALLOW_UNUSED valid = Dart_ListLength(libraryIdList, &length); |
+ ASSERT(!Dart_IsError(valid)); |
+ |
+ |
+ for (intptr_t i = 0; i < length; i++) { |
+ Dart_Handle libraryIdHandle = Dart_ListGetAt(libraryIdList, i); |
+ Dart_Handle exception = 0; |
+ intptr_t libraryId = DartUtilities::toInteger(libraryIdHandle, exception); |
+ ASSERT(!exception); |
+ DartScriptState* scriptState = lookupScriptStateFromLibraryIdMap(isolate, v8Context, libraryIdMap, libraryId); |
+ result.append(scriptState); |
+ } |
+} |
+ |
+void DartController::spawnDomUri(const String& url) |
+{ |
+ // Save caller isolate. |
+ Dart_Isolate caller = Dart_CurrentIsolate(); |
+ ASSERT(caller); |
+ DEFINE_STATIC_LOCAL(const char*, packageRoot, (getenv("DART_PACKAGE_ROOT"))); |
+ if (!packageRoot) { |
+ DartDOMData* parentDOMData = static_cast<DartDOMData*>(Dart_CurrentIsolateData()); |
+ packageRoot = parentDOMData->packageRoot(); |
+ } |
+ Dart_ExitIsolate(); |
+ |
+ // Create DOM isolate. |
+ Document* document = frame()->document(); |
+ |
+ Dart_Isolate isolate = createDOMEnabledIsolate(url, "main", packageRoot, document); |
+ |
+ // Fetch and start. |
+ RefPtr<DartApplicationLoader> loader = DartApplicationLoader::create(document, true); |
+ RefPtr<DartSpawnUriCallback> callback = adoptRef(new DartSpawnDomUriCallback(isolate, loader, url, document)); |
+ Dart_ExitIsolate(); |
+ loader->processSingleRequest(isolate, url, callback); |
+ |
+ // Restore caller isolate. |
+ Dart_EnterIsolate(caller); |
+ |
+ // FIXME: We need some way to return a Dart_Handle to the isolate we just created. |
+} |
+ |
+} |