webengine/osswebengine/WebKitTools/Drosera/DebuggerDocument.cpp
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebKitTools/Drosera/DebuggerDocument.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2007 Apple 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:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer. 
+ * 2.  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. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "DebuggerDocument.h"
+
+#include "DebuggerClient.h"
+
+#include <JavaScriptCore/JSContextRef.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <JavaScriptCore/JSStringRef.h>
+#include <JavaScriptCore/JSStringRefCF.h>
+#include <JavaScriptCore/RetainPtr.h>
+#include <JavaScriptCore/Vector.h>
+
+DebuggerDocument::DebuggerDocument(DebuggerClient* debugger)
+    : m_debuggerClient(debugger)
+{
+    ASSERT(m_debuggerClient);
+}
+
+//-- Callbacks
+
+JSValueRef DebuggerDocument::breakpointEditorHTMLCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef /*thisObject*/, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* /*exception*/)
+{
+    RetainPtr<CFURLRef> htmlURLRef(AdoptCF, ::CFBundleCopyResourceURL(::CFBundleGetBundleWithIdentifier(CFSTR("org.webkit.drosera")), CFSTR("breakpointEditor"), CFSTR("html"), 0));
+    if (!htmlURLRef)
+        return JSValueMakeUndefined(context);
+
+    // FIXME: I'm open to a better way to do this.  We convert from UInt8 to CFString to JSString (3 string types!)
+    RetainPtr<CFReadStreamRef> readStreamRef(AdoptCF, CFReadStreamCreateWithFile(0, htmlURLRef.get()));
+    CFReadStreamRef readStream = readStreamRef.get();
+
+    if (!CFReadStreamOpen(readStream))
+        return JSValueMakeUndefined(context);
+
+    // Large enough for current BreakPointEditor.html but won't need to be changed if that file changes 
+    // because we loop over the entire file and read it in bufferLength pieces at a time
+    const CFIndex bufferLength = 740;
+    UInt8 buffer[bufferLength];
+    Vector<UInt8, bufferLength> charBuffer;
+    CFIndex readResult = bufferLength;
+    while (readResult == bufferLength) {
+        readResult = CFReadStreamRead(readStream, buffer, bufferLength);
+
+        // Error condition (-1) will not copy any data
+        for (int i = 0; i < readResult; i++)
+            charBuffer.append(buffer[i]);
+    }
+
+    CFReadStreamClose(readStream);
+    if (readResult == -1)
+        return JSValueMakeUndefined(context);
+
+    // FIXME: Is there a way to determine the encoding?
+    RetainPtr<CFStringRef> fileContents(AdoptCF, CFStringCreateWithBytes(0, charBuffer.data(), charBuffer.size(), kCFStringEncodingUTF8, true));
+    JSRetainPtr<JSStringRef> fileContentsJS(Adopt, JSStringCreateWithCFString(fileContents.get()));
+    JSValueRef ret = JSValueMakeString(context, fileContentsJS.get());
+
+    return ret;
+}
+
+JSValueRef DebuggerDocument::pauseCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* /*exception*/)
+{
+    DebuggerDocument* debuggerDocument = reinterpret_cast<DebuggerDocument*>(JSObjectGetPrivate(thisObject));
+
+    debuggerDocument->platformPause();
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef DebuggerDocument::resumeCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* /*exception*/)
+{
+    DebuggerDocument* debuggerDocument = reinterpret_cast<DebuggerDocument*>(JSObjectGetPrivate(thisObject));
+    debuggerDocument->platformResume();
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef DebuggerDocument::stepIntoCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* /*exception*/)
+{
+    DebuggerDocument* debuggerDocument = reinterpret_cast<DebuggerDocument*>(JSObjectGetPrivate(thisObject));
+    debuggerDocument->platformStepInto();
+    return JSValueMakeUndefined(context);
+}
+
+JSValueRef DebuggerDocument::evaluateScriptCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    DebuggerDocument* debuggerDocument = reinterpret_cast<DebuggerDocument*>(JSObjectGetPrivate(thisObject));
+    if (argumentCount < 2)
+        return JSValueMakeUndefined(context);
+
+    if (!JSValueIsNumber(context, arguments[1]))
+        return JSValueMakeUndefined(context);
+    
+    double callFrame = JSValueToNumber(context, arguments[1], exception);
+    ASSERT(!*exception);
+
+    JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+    ASSERT(!*exception);
+
+    JSValueRef ret = debuggerDocument->platformEvaluateScript(context, script.get(), (int)callFrame);
+
+    return ret;
+}
+
+JSValueRef DebuggerDocument::currentFunctionStackCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef /*arguments*/[], JSValueRef* exception)
+{
+    DebuggerDocument* debuggerDocument = reinterpret_cast<DebuggerDocument*>(JSObjectGetPrivate(thisObject));
+    Vector<JSValueRef> stack;
+    debuggerDocument->getPlatformCurrentFunctionStack(context, stack);
+    return DebuggerDocument::toJSArray(context, stack, exception);
+}
+
+JSValueRef DebuggerDocument::localScopeVariableNamesForCallFrameCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    DebuggerDocument* debuggerDocument = reinterpret_cast<DebuggerDocument*>(JSObjectGetPrivate(thisObject));
+    if (argumentCount < 1)
+        return JSValueMakeUndefined(context);
+
+    if (!JSValueIsNumber(context, arguments[0]))
+        return JSValueMakeUndefined(context);    
+
+    double callFrame = JSValueToNumber(context, arguments[0], exception);
+    ASSERT(!*exception);
+
+    // Get the variable names
+    Vector<JSValueRef> localVariableNames;
+
+    debuggerDocument->getPlatformLocalScopeVariableNamesForCallFrame(context, static_cast<int>(callFrame), localVariableNames);
+    return DebuggerDocument::toJSArray(context, localVariableNames, exception);
+}
+
+JSValueRef DebuggerDocument::valueForScopeVariableNamedCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    DebuggerDocument* debuggerDocument = reinterpret_cast<DebuggerDocument*>(JSObjectGetPrivate(thisObject));
+
+    if (argumentCount < 2)
+        return JSValueMakeUndefined(context);
+
+    if (!JSValueIsString(context, arguments[0]))
+        return JSValueMakeUndefined(context);
+
+    JSRetainPtr<JSStringRef> key(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+    ASSERT(!*exception);
+
+    if (!JSValueIsNumber(context, arguments[1]))
+        return JSValueMakeUndefined(context);
+
+    double callFrame = JSValueToNumber(context, arguments[1], exception);
+    ASSERT(!*exception);
+
+    return debuggerDocument->platformValueForScopeVariableNamed(context, key.get(), (int)callFrame);
+}
+
+JSValueRef DebuggerDocument::logCallback(JSContextRef context, JSObjectRef /*function*/, JSObjectRef /*thisObject*/, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    if (argumentCount < 1)
+        return JSValueMakeUndefined(context);
+
+    if (!JSValueIsString(context, arguments[0]))
+        return JSValueMakeUndefined(context);
+
+    JSRetainPtr<JSStringRef> msg(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+    ASSERT(!*exception);
+
+    DebuggerDocument::platformLog(msg.get());
+    return JSValueMakeUndefined(context);
+}
+
+//-- These are the calls into the JS. --//    
+
+bool DebuggerDocument::isPaused(JSContextRef context) const
+{
+    JSObjectRef globalObject = JSContextGetGlobalObject(context);
+    JSRetainPtr<JSStringRef> string(Adopt, JSStringCreateWithUTF8CString("isPaused"));
+    JSValueRef objectProperty = JSObjectGetProperty(context, globalObject, string.get(), 0);
+    return JSValueToBoolean(context, objectProperty);
+}
+
+void DebuggerDocument::updateFileSource(JSContextRef context, JSStringRef documentSource, JSStringRef url)
+{
+    JSValueRef documentSourceValue = JSValueMakeString(context, documentSource);
+    JSValueRef urlValue = JSValueMakeString(context, url);
+    JSValueRef forceValue = JSValueMakeBoolean(context, false);
+
+    JSValueRef arguments[] = { documentSourceValue, urlValue, forceValue };
+    int argumentsSize = sizeof(arguments)/sizeof(arguments[0]);
+
+    DebuggerDocument::callGlobalFunction(context, "updateFileSource", argumentsSize, arguments);
+}
+
+void DebuggerDocument::didParseScript(JSContextRef context, JSStringRef source, JSStringRef documentSource, JSStringRef url, JSValueRef sourceId, JSValueRef baseLine)
+{
+    JSValueRef sourceValue = JSValueMakeString(context, source);
+    JSValueRef documentSourceValue = JSValueMakeString(context, documentSource);
+    JSValueRef urlValue = JSValueMakeString(context, url);
+
+    JSValueRef arguments[] = { sourceValue, documentSourceValue, urlValue, sourceId, baseLine };
+    int argumentsSize = sizeof(arguments)/sizeof(arguments[0]);
+
+    DebuggerDocument::callGlobalFunction(context, "didParseScript", argumentsSize, arguments);
+}
+
+void DebuggerDocument::willExecuteStatement(JSContextRef context, JSValueRef sourceId, JSValueRef lineno, JSValueRef* exception)
+{
+    JSValueRef arguments[] = { sourceId, lineno };
+    int argumentsSize = sizeof(arguments)/sizeof(arguments[0]);
+
+    DebuggerDocument::callGlobalFunction(context, "willExecuteStatement", argumentsSize, arguments, exception);
+    if (exception)
+        logException(context, *exception);
+}
+
+void DebuggerDocument::didEnterCallFrame(JSContextRef context, JSValueRef sourceId, JSValueRef lineno, JSValueRef* exception)
+{
+    JSValueRef arguments[] = { sourceId, lineno };
+    int argumentsSize = sizeof(arguments)/sizeof(arguments[0]);
+
+    DebuggerDocument::callGlobalFunction(context, "didEnterCallFrame", argumentsSize, arguments, exception);
+    if (exception)
+        logException(context, *exception);
+}
+
+void DebuggerDocument::willLeaveCallFrame(JSContextRef context, JSValueRef sourceId, JSValueRef lineno, JSValueRef* exception)
+{
+    JSValueRef arguments[] = { sourceId, lineno };
+    int argumentsSize = sizeof(arguments)/sizeof(arguments[0]);
+
+    DebuggerDocument::callGlobalFunction(context, "willLeaveCallFrame", argumentsSize, arguments, exception);
+    if (exception)
+        logException(context, *exception);
+}
+
+void DebuggerDocument::exceptionWasRaised(JSContextRef context, JSValueRef sourceId, JSValueRef lineno, JSValueRef* exception)
+{
+    JSValueRef arguments[] = { sourceId, lineno };
+    int argumentsSize = sizeof(arguments)/sizeof(arguments[0]);
+
+    DebuggerDocument::callGlobalFunction(context, "exceptionWasRaised", argumentsSize, arguments, exception);
+}
+
+void DebuggerDocument::windowScriptObjectAvailable(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
+{
+    JSRetainPtr<JSStringRef> droseraStr(Adopt, JSStringCreateWithUTF8CString("DebuggerDocument"));
+    JSValueRef droseraObject = JSObjectMake(context, getDroseraJSClass(), this);
+
+    JSObjectSetProperty(context, windowObject, droseraStr.get(), droseraObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
+    if (exception)
+        logException(context, *exception);
+}
+
+JSValueRef DebuggerDocument::toJSArray(JSContextRef context, Vector<JSValueRef>& vectorValues, JSValueRef* exception)
+{
+    JSObjectRef globalObject = JSContextGetGlobalObject(context);
+    JSRetainPtr<JSStringRef> constructorString(Adopt, JSStringCreateWithUTF8CString("Array"));
+    JSValueRef constructorProperty = JSObjectGetProperty(context, globalObject, constructorString.get(), exception);
+    ASSERT(!*exception);
+
+    JSObjectRef arrayConstructor = JSValueToObject(context, constructorProperty, exception);
+    ASSERT(!*exception);
+
+    JSObjectRef array = JSObjectCallAsConstructor(context, arrayConstructor, 0, 0, exception);
+    ASSERT(!*exception);
+
+    JSRetainPtr<JSStringRef> pushString(Adopt, JSStringCreateWithUTF8CString("push"));
+    JSValueRef pushValue = JSObjectGetProperty(context, array, pushString.get(), exception);
+    ASSERT(!*exception);
+
+    JSObjectRef push = JSValueToObject(context, pushValue, exception);
+    ASSERT(!*exception);
+
+    for (Vector<JSValueRef>::iterator it = vectorValues.begin(); it != vectorValues.end(); ++it) {
+        JSObjectCallAsFunction(context, push, array, 1, it, exception);
+        ASSERT(!*exception);
+    }
+    
+    return array;
+}
+
+// Private
+JSValueRef DebuggerDocument::callGlobalFunction(JSContextRef context, const char* functionName, int argumentCount, JSValueRef arguments[], JSValueRef* exception)
+{
+    JSObjectRef globalObject = JSContextGetGlobalObject(context);
+    return callFunctionOnObject(context, globalObject, functionName, argumentCount, arguments, exception);
+}
+
+JSValueRef DebuggerDocument::callFunctionOnObject(JSContextRef context, JSObjectRef object, const char* functionName, int argumentCount, JSValueRef arguments[], JSValueRef* exception)
+{
+    JSRetainPtr<JSStringRef> string(Adopt, JSStringCreateWithUTF8CString(functionName));
+    JSValueRef objectProperty = JSObjectGetProperty(context, object, string.get(), exception);
+
+    JSObjectRef function = JSValueToObject(context, objectProperty, exception);
+    ASSERT(JSObjectIsFunction(context, function));
+ 
+    JSValueRef returnValue = JSObjectCallAsFunction(context, function, 0, argumentCount, arguments, exception);
+    if (exception)
+        logException(context, *exception);
+
+    return returnValue;
+}
+
+JSClassRef DebuggerDocument::getDroseraJSClass()
+{
+    static JSClassRef droseraClass = 0;
+
+    if (!droseraClass) {
+        JSClassDefinition classDefinition = {0};
+        classDefinition.staticFunctions = DebuggerDocument::staticFunctions();
+
+        droseraClass = JSClassCreate(&classDefinition);
+    }
+
+    return droseraClass;
+}
+
+JSStaticFunction* DebuggerDocument::staticFunctions()
+{
+    static JSStaticFunction staticFunctions[] = {
+        { "breakpointEditorHTML", DebuggerDocument::breakpointEditorHTMLCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "currentFunctionStack", DebuggerDocument::currentFunctionStackCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "evaluateScript", DebuggerDocument::evaluateScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "localScopeVariableNamesForCallFrame", DebuggerDocument::localScopeVariableNamesForCallFrameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "pause", DebuggerDocument::pauseCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "resume", DebuggerDocument::resumeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "stepInto", DebuggerDocument::stepIntoCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "valueForScopeVariableNamed", DebuggerDocument::valueForScopeVariableNamedCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "log", DebuggerDocument::logCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { 0, 0, 0 }
+    };
+
+    return staticFunctions;
+}
+
+void DebuggerDocument::logException(JSContextRef context, JSValueRef exception)
+{
+    if (!exception)
+        return;
+    
+    JSRetainPtr<JSStringRef> msg(Adopt, JSValueToStringCopy(context, exception, 0));
+    DebuggerDocument::platformLog(msg.get());
+}
+