| 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.
|
| +}
|
| +
|
| +}
|
|
|